Loading

Chapter3. 仲裁器专题

Chapter3. 仲裁器专题

导图

本专题内容总结自李虹江老师的IC加油站公众号,李老师的讲的内容十分精彩,除了仲裁器还包括异步FIFO、跨时钟域处理,讲的十分透彻,受益匪浅。

Fixed Priority Arbiter

  • 默认req中最低位的优先级最高

先来最优写法,req & (~(req - 1))直接得到req中最低为1的位置,就是gnt

为什么会有这个性质呢?

~(req - 1)其实就是req的补码,一个数的原码与补码相与。得到的就是一个独热码,onehot code中为1的位置就是这个数最低的1的位置。精彩!

module priority_arbiter
#(
    parameter REQ_WIDTH = 16
)
(
    input [REQ_WIDTH-1:0] req,
    output [REQ_WIDTH-1:0] gnt
);

    // best solution

    assign gnt = req & (~(req - 1));

endmodule

`timescale 1ns/1ps
module tb;
    reg clk, rst_n;

    initial begin 
    clk = 1'b0; 
    #5 clk = 1'b1; 
    forever #5 clk = ~clk;
    end

    reg [15:0] req;
    initial begin
        while(1)begin
            #20
            req = $random;
        end
    end
    wire [15:0] gnt;

    priority_arbiter inst
    (req, gnt);

    initial begin 
    $dumpfile("showmebug.vcd"); 
    $dumpvars(0, tb); 
    #600

    end
endmodule

Fixed Priority Arbiter的另外两种写法

  • 思路就是用pre_req保存req当前位和前一位是否为1(被req过),如果第i位有了request,那么第i+1位到最高位的pre_req都是1
module priority_arbiter
#(
    parameter REQ_WIDTH = 4
)
(
    input [REQ_WIDTH-1:0] req,
    output reg [REQ_WIDTH-1:0] gnt
);
    reg [REQ_WIDTH-1:0] pre_req;
    integer i;
    always @(*)begin
        // pre_req记录低位是否有req
        pre_req[0] = req[0];
        gnt[0] = req[0];

        for(i = 1; i < REQ_WIDTH; i = i + 1)begin
            gnt[i] = req[i] & !pre_req[i-1];
            pre_req[i] = req[i] | pre_req[i-1];
        end
    end
endmodule

// 另一种写法,跟上面的思路一样,就是把上面for用assign来实现。
module priority_arbiter_m2
#(
    parameter REQ_WIDTH = 4
)
(
    input [REQ_WIDTH-1:0] req,
    output [REQ_WIDTH-1:0] gnt
);
    wire [REQ_WIDTH-1:0] pre_req;

    assign pre_req = req[0];
    assign pre_req[REQ_WIDTH-1:1] = req[REQ_WIDTH-2:0] | pre_req[REQ_WIDTH-2:0];
    assign gnt = req & !pre_req; 
endmodule

noob写法

为什么这种case的写法是可以的?因为在verilog中,case的执行只执行其中一个条件,执行完该条件就跳出case,不像C中的case,如果不加continue就会继续判断下面的条件。

module fixed_arbiter
(
	input [3:0] req,
	output reg [3:0] gnt
);
	always @(*)begin
    case(1'b1)
      req[0]: gnt = 4'b0001;
      req[1]: gnt = 4'b0010;
      req[2]: gnt = 4'b0100;
      req[3]: gnt = 4'b1000;
    endcase
	end
endmodule

Round Robin Arbiter

RR仲裁器的仲裁逻辑

  • priority是变化的
  • 变化的规则为,初始priority是3210(数字越小优先级越高),如果某位被grant了,那么那一位的优先级变为最低(3),同时那一位左边的优先级从低到高排列到最高位,然后“绕回”到被gnt到那位的右边。
  • 这种绕圈也因此被称为_round robin_
  • 比如说初始priority是3210,倒数第二位被gnt了,那么优先级将会变化为1032

RR仲裁器的实现思路

更换base,req不变思路

  • 实现一个固定优先级的fixed arbiter,它的输入除了有req,还有base,表示输入的优先级
  • 那么RR仲裁器实现只需要每次gnt之后更新base,即可实现
  • base的格式是什么呢?优先级最高的那一位为1
  • 那么base是如何更新的呢?由于base是one hot code,gnt也是one hot,根据rr仲裁器的逻辑,只需要将gnt循环左移一位就是下一个cycle的base了
module round_robin_arbiter
#(
    parameter NUM_REQ = 4
)
(
    input clk,
    input rst_n,
    input [NUM_REQ-1:0] req,
    output [NUM_REQ-1:0] gnt
);

    reg [NUM_REQ-1:0] hist_q;

    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)
            hist_q <= 1'b1;
        else begin
            if(|req)
                hist_q <= {gnt[NUM_REQ-2:0, gnt[NUM_REQ-1]]}    // 循环左移更新base
        end
    end

endmodule

module arbiter_base
#(
    parameter NUM_REQ = 4
)
(
    input [NUM_REQ-1:0] req,
    input [NUM_REQ-1:0] base,
    output [NUM_REQ-1:0] gnt
);

    // 位宽扩展,防止-base不够减
    wire [2*NUM_REQ-1:0] double_req;
    wire [2*NUM_REQ-1:0] double_gnt;

    assign double_req = {req, req};
    assign double_gnt = double_req | (~(double_req - base));

    assign gnt = double_gnt[NUM_REQ-1:0] | double_gnt[2*NUM_REQ-1:NUM_REQ];

endmodule

mask掉req,不改变base

module round_robin_arbiter
#(
    parameter N = 16
)
(
    input clk,
    input rst,
    input [N-1:0] req,
    output [N-1:0] grant
);

    reg [N-1:0] req_masked;
    reg [N-1:0] mask_higher_pri_reqs;
    reg [N-1:0] unmask_higher_prt_reqs;
    reg [N-1:0] grant_unmasked;
    reg [N-1:0] grant_masked;

    reg no_req_masked;
    reg [N-1:0] pointer_reg;

    assign req_masked = req & pointer_reg;  // req 跟 pre_req相与,从req最低位1位置开始,到最高位都是1.
    assign mask_higher_pri_reqs[N-1:1] = mask_higher_pri_reqs[N-2:0] | req_masked[N-2:0];
    assign mask_higher_pri_reqs[0] = 1'b0;
    assign grant_masked = req_masked[N-1:0] & ~mask_higher_pri_reqs[N-1:0];

    assign unmask_higher_prt_reqs[N-1:1] = unmask_higher_prt_reqs[N-2:0] | req[N-2:0];
    assign unmask_higher_prt_reqs[0] = 1'b0;
    assign grant_unmasked[N-1:0] = req[N-1:0] & ~unmask_higher_prt_reqs[N-1:0];

    assign no_req_masked = ~(|req_masked);
    assign grant = ({N{no_req_masked}} & grant_unmasked) | grant_masked;

    always @(posedge clk)begin
        if(rst_n)
            pointer_reg <= {N{1'b1}};
        else begin
            if(|req_masked)
                pointer_reg <= mask_higher_pri_reqs;
            else begin
                if(|req)
                    pointer_reg <= unmask_higher_prt_reqs;
                else
                    pointer_reg <= pointer_reg;
            end
        end
    end

endmodule
posted @ 2022-08-23 22:07  pu1se  阅读(615)  评论(0)    收藏  举报