一、问题
实现轮询仲裁(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结果如下