輪詢仲裁(roundrobin arbiter)


一、問題
實現輪詢仲裁(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結果如下


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM