一、問題
實現輪詢仲裁(roundrobin arbiter)
二、實現思路
每次訪問結束后更新優先級(開始上電默認的優先級是0,1,2......),然后依次從優先級最低的開始檢測request直至出現最終的request為1,則表示該request獲得grant(例如:ARBITER_NUM=4,目前優先級由高到低依次為[1,2,3,0],那么使用for循環先從后向前進行檢測,檢查到最終request為1的情況,即表示其獲得grant)。
三、代碼如下
module arbiter_roundrobin
#(
parameter ARBITER_NUM = 4
)
(
input clk_i ,
input rst_n_i ,
input [ARBITER_NUM-1:0] req_i ,
input [ARBITER_NUM-1:0] end_access_i ,
output logic [ARBITER_num-1:0] grant_o
);
genvar i;
logic [ARBITER_NUM-1:0][$clog2(ARBITER_NUM)-1:0] record_initial_priority_s ; //[0,1,2,3]
logic [ARBITER_NUM-1:0][$clog2(ARBITER_NUM]-1:0] record_priority_r ; //record the changed priority.
logic [ARBITER_NUM-1:0][$clog2(ARBITER_NUM)-1:0] record_initial_priority_splice_s; //[0,1,2,3,0,1,2,3]
logic start_s ;
logic end_s ;
logic [$clog2(ARBITER_NUM)-1:0] index_s ; //record which request is 1 start with the highest priority
logic [$clog2(ARBITER_NUM)-1:0] index_r ; //when start_s = 1'b1, keep the request index
enum logic {
IDLE = 1'b0,
GRANT = 1'b1
} cur_state_r, next_state_s;
generate
for(i=0;i<ARBITER_NUM;i=i+1) begin : RECORD_INITIAL_PRIORITY
assign record_initial_priority_s[i] = i[$clog2(ARBITER_NUM)-1:0]; //存儲初始的優先級,如果ARBITER_NUM=4,優先級由高到底是[0,1,2,3]
end
endgenerate
assign record_initial_priority_splice_s = {record_initial_priority_s,record_initial_priority_s}; //將優先級重復兩遍,如果ARBITER_NUM=4,則該信號的值依次為[0,1,2,3,0,1,2,3]
always_ff @(posedge clk_i or negedge rst_n_i) begin
if(!rst_n_i) begin
cur_state_r <= IDLE;
end
else begin
cur_state_r <= next_state_s;
end
end
always_comb begin : INDEX_RECORD_PRIORITY_UPDATE_FLAG //狀態機,為了產生start_s和end_s,start_s表示開始獲得GRANT,end_s表示req已請求完,可以輪詢。
next_state = cur_state_r;
case(cur_state_r)
IDLE : begin
if(|req_i) begin
next_state_s = GRANT;
end
end
GRANT : begin
if(grant_o[record_priority_r[index_r]] && end_access_i[record_priority_r[index_r]]) begin
next_state_s = IDLE;
end
end
default : next_state_s = IDLE;
endcase
end
assign start_s = (cur_state_r == IDLE && next_state_s ==GRANT);
assign end_s = (cur_state_r == GRANT && next_state_s == IDLE);
always_comb begin : INDEX_S //檢測出request為1的最高優先級的索引
index_s = {$clog2(ARBITER_NUM){1'b0}};
for(int j=ARBITER_NUM;j>=0;j=j-1) begin
if(req_i[record_priority_r[j]]) begin
index_s = j[$clog2(ARBITER_NUM)-1:0];
end
end
end
always_ff @(posedge clk_i or negedge rst_n_i) begin //保持檢查出的request為1的最高優先級的索引
if(!rst_n_i) begin
index_r <= {$clog2(ARBITER_NUM){1'b0}};
end
else if(start_s) begin
index_r <= index_s;
end
end
generate
for(i=0;i<ARBITER_NUM;i=i+1) begin RECORD_PRIORITY_R //在req結束后更新優先級,record_priority_r[0]存儲最高的優先級,依次往下
always_ff @(posedge clk_i or negedge rst_n_i) begin
if(!rst_n_i) begin
record_priority_r[i] <= i[$clog2(ARBITER_NUM)-1:0];
end
else if(end_s) begin
for(int j=0;j<ARBITER_NUM;j=j+1) begin
if(j == record_priority_r[index_r]) begin
record_priority_r[i] <= record_priority_r[i+j+1];
end
end
end
end
end
endgenerate
alwasy_comb begin : GRANT_O //grant輸出
grant_o <= {ARBITER_NUM{1'b0}};
for(int j=0;j<ARBITER_NUM;j=j+1) begin
if(j == record_priority_r[index_r]) begin
grant_o[j] = (cur_state_r == GRANT);
end
end
end
property check_out; //斷言,檢查grant的輸出是否為one-hot
@(posedge clk_i)
disbale iff(!rst_n_i)
$onehot0(grant_o);
endproperty
a_check_out : assert property(check_out());
c_check_out : cover property(check_out());
property check_record_priority(j); //斷言,檢查req結束后優先級的輪換
//int temp = record_priority_r[index_r];
int temp;
@(posedge clk_i)
disable iff(!rst_n_i)
//$rose(end_s) |=> (record_priority_r[j] == record_initial_priority_splice_s[temp+j+1]);
($rose(end_s),temp = record_priority_r[index_r]) |=> (record_priority_r[j] == record_initial_priority_splice_s[temp+j+1]);
endproperty
generate
for(i=0;i<ARBITER_NUM;i=i+1) begin : ASSERT_CHECK_RECORD_PRIORITY
a_check_record_priority : assert property(check_record_priority(i));
c_check_record_priority : cover property(check_record_priority(i));
end
endgenerate
endmodule
四、testbench如下
module arbiter_roundrobin_tb;
parameter ARBITER_NUM = 4 ;
logic clk_i ;
logic rst_n_i ;
logic [ARBITER_NUM-1:0] req_i ;
logic [ARBITER_NUM-1:0] end_access_i;
logic [ARBITER_NUM-1:0] grant_o ;
arbiter_roundrobin
#(
.ARBITER_NUM(ARBITER_NUM)
)
arbiter_roundrobin_inst
(
.clk_i (clk_i ),
.rst_n_i (rst_n_i ),
.req_i (req_i ),
.end_access_i (end_access_i),
.grant_o (grant_o )
);
always #10 clk_i = ~clk_i;
task rst;
begin
rst_n_i = 1'b0;
#101;
rst_n_i = 1'b1;
end
endtask
task automatic request;
input [3:0] i;
begin
@(posedge clk_i);
req_i[i] <= 1'b0;
repeat($urandom()%10) begin
@(posedge clk_i);
end
req_i[i] <= 1'b1;
while(~grant_o[i]) begin
@(posedge clk_i);
end
repeat($urandom()%20) begin
req_i[i] <= $urandom_range(0,1);
@(posedge clk_i);
end
end_access_i[i] <= 1'b1;
@(posedge clk_i);
end_access_i[i] <= 1'b0;
req_i[i] <= 1'b0;
end
endtask
initial
begin
clk_i = 1'b0;
req_i = 'h0 ;
end_access_i = 'h0 ;
rst();
repeat(10) begin
fork
request(0);
request(1);
request(2);
request(3);
join
end
repeat(10) begin
@(posedge clk_i);
end
$stop();
end
endmodule
五、simulation結果如下