轮询仲裁(roundrobin arbiter)

时间:2024-02-25 17:01:56

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