round robin arbiter 轮询仲裁器设计

前言

仲裁器Arbiter是数字设计中非常常见的模块,应用也非常广泛。定义就是当有两个或两个以上的模块需要占用同一个资源的时候,我们需要由仲裁器arbiter来决定哪一个模块来占有这个资源。一般来说,提出占有资源的模块要产生一个请求(request),所有的请求送给仲裁器之后,仲裁器要返回一个许可(grant)。

轮询仲裁器规则

轮询仲裁的规则是当0、1、2、、、N-1个data模块同时向仲裁器发出请求 (request) 时,初始情况下data_req_0的优先级最高,当仲裁器响应了data_req_0后,data_req_1的优先级最高,存在规律:当仲裁器响应了data_req_i后,就令data_req_(i+1)的优先级最高(假设i不是最高位)。

轮询仲裁器实现

轮询仲裁器的实现分为检测仲裁器输入口data模块的request,根据当前仲裁器的优先级响应相应的request,仲裁器grant输出data端的请求,更新仲裁器的优先级。

假设模块的请求为8bit的request,每1bit代表第1个模块发起的request,当request中有多个bit位同时为1时,代表有多个模块同时申请总线,这时候我们就需要根据轮询仲裁规则,给优先级最高的模块一个grant信号,因此我们设置grant信号也为一个8bit的信号,当总线把控制权给第i个模块时,就把grant信号的第i位拉高,其他位为0。

例如:request = 8'b1101_0011时,共有五个模块同时申请总线,此时优先级最高的为第0个模块,所以输出grant = 8'b0000_0001。并把第0个信号的优先级调到最低,第1个信号的优先级调到最高。此时优先级顺序为1>2>3>4>5>6>7>0,若此时又来一个request = request = 8'b1101_0101,由于bit 0位置的优先级最低,所以继续往后搜索,搜索到bit 2位置,所以输出grant = 8'b0000_0100,第2个信号优先级调到最低,第3个信号优先级调到最高,即输出后优先级顺序变为:3>4>5>6>7>0>1>2,以此类推,这就是轮询仲裁。

1.1verilog代码

module rr_arbiter(
    input                clk         ,
    input                rstn        ,
    input        [7:0]   req         ,

    output  reg  [7:0]   grant       
);

reg  [7:0] shift_req;
wire [7:0] prio_grant;
wire [2:0] shift_length;

// 根据上一周期的grant,修改request,使得最低bit位优先级最高。
always @(*)begin
    case(grant)     //this grant is pre-cycle request's result
        8'b0000_0001:shift_req = {req[0:0],req[7:1]};
        8'b0000_0010:shift_req = {req[1:0],req[7:2]};
        8'b0000_0100:shift_req = {req[2:0],req[7:3]};
        8'b0000_1000:shift_req = {req[3:0],req[7:4]};
        8'b0001_0000:shift_req = {req[4:0],req[7:5]};
        8'b0010_0000:shift_req = {req[5:0],req[7:6]};
        8'b0100_0000:shift_req = {req[6:0],req[7:7]};
        default:shift_req = req;
    endcase
end

// 找到修改后最低位的one-hot码(参考fixed_arbiter设计)
assign prio_grant = shift_req & (~(shift_req-1));  

// 如果grant信号是1,那么移动长度计算需要+1,如果grant信号是0则不+1.
// 这是因为$clog2函数,有$clog2(0)=$clog2(1)=0的缘故,所以我们需要区分grant是不是0.
assign shift_length = grant?($clog2(prio_grant) + $clog2(grant)+1):($clog2(prio_grant) + $clog2(grant));

always @(posedge clk)begin
    if(!rstn)begin
        grant <= 8'd0;
    end
    else if(req==0) // 如果输入为0,那么grant信号直接给0
        grant <= 8'd0;
    else
        case(shift_length)
            3'd0:grant <= 8'b0000_0001;
            3'd1:grant <= 8'b0000_0010;
            3'd2:grant <= 8'b0000_0100;
            3'd3:grant <= 8'b0000_1000;
            3'd4:grant <= 8'b0001_0000;
            3'd5:grant <= 8'b0010_0000;
            3'd6:grant <= 8'b0100_0000;
            3'd7:grant <= 8'b1000_0000;
        endcase
end

endmodule

1.2testbench

module rr_arbiter_tb();
reg clk,rstn;
reg [7:0] req;

wire [7:0] grant;

initial begin
    forever #5 clk = ~clk;
end

initial begin
    clk = 0;
    rstn = 0;
    req = 8'd0;
    #10
    rstn = 1;
    #5
    req = #1 8'b1011_1110;
    #10
    req = #1 8'b0101_0010;
    #10
    req = #1 8'b1010_1000;
    #10
    req = #1 8'b1100_1000;
    #10
    req = #1 8'd0;
    #50
    $finish();
end

rr_arbiter u_rr_arbiter(
    .clk    (clk)  ,
    .rstn   (rstn) ,
    .req    (req)  ,
    .grant  (grant)
);

initial begin
    $fsdbDumpfile("rr_arbiter.fsdb");
    $fsdbDumpvars(0);
end

endmodule

1.3波形图

  • 在仿真中,我们先给模块输入了一个8'b0000_0000的request,模块回我了我们一个8'b0000_0000的grant。

  • 接着发送8'b1011_1110,模块返回grant=8'b0000_0010,此时优先级为:2>3>4>5>6>7>0>1。

  • 下一周期发送request = 8'b0101_0010,由于上一周期已经把request[1]的优先级拉低了,所以匹配到request[4],输出grant = 8'b0001_0000.此时优先级为:5>6>7>0>1>2>3>4。

  • 下一周期输入request = 8'b1010_1000,注意这时候grant不是匹配request最低的bit位,因为根据上一轮的优先级顺序,request[3]的优先级是小于request[5]的,所以grant输出8'b0010_0000。

猜你喜欢

转载自blog.csdn.net/qq_57502075/article/details/129764974
今日推荐