It seems that the top block keeps track of the last grant, and activates the 'p' signal for the next block to give it the highest priority.
However, any block can make a request.
The priority logic is combinatorial and the outputs can have glitches, so the request inputs and the grant outputs should go to synchronous logic in the same clock domain.
r = request
g = grant
p = priority
c = chain/carry for priority
module arbiter2 #(parameter WIDTH =4)(clk, reset, req, grant);input clk, reset;input[WIDTH-1:0] req;output[WIDTH-1:0] grant;// 'grant' is one-hot vector, which means only one client request is granted/given green light to proceed// note that 'base' is one-hot vector, // 'base' signal helps round-robin arbiter to decide which 'req' to start servicingreg[WIDTH-1:0] base;always@(posedge clk)beginif(reset) base <=1;else base <=(base[WIDTH-1])?1:(grant ==0)?1:(grant <<1);endwire[WIDTH-1:0] priority_in;reg[WIDTH-1:0] priority_out;genvar index;generatefor(index =0; index < WIDTH; index = index +1)beginif(index >0)assign priority_in[index]= base[index]| priority_out[index-1];elseassign priority_in[index]= base[index]| priority_out[WIDTH-1];endendgenerateassign grant =(reset)?1: req & priority_in;always@(posedge clk) priority_out <=(~req)& priority_in;`ifdef FORMAL
initialassume(reset);reg first_clock_had_passed;initial first_clock_had_passed =0;always@(posedge clk) first_clock_had_passed <=1;always@(posedge clk)if(first_clock_had_passed && $past(first_clock_had_passed)&& $past(first_clock_had_passed,2)&& $past(first_clock_had_passed,3))assume(&req);// ALL requests ON// covers the ability to handle requests properly even with ALL requests ONalways@(posedge clk)cover(first_clock_had_passed);`endifendmodule