前言
仲裁器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波形图
![](https://i-blog.csdnimg.cn/blog_migrate/9577346eae4da4d6f1254155968624a3.png)
在仿真中,我们先给模块输入了一个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。