目录
当多个Master访问一个Slave时,该Slave就需要对这几个Master进行仲裁,实际上有很多仲裁算法,此处就作如下讲解。
仲裁器设计(一) – Fixed Priority Arbiter
仲裁器设计(二)-- Round Robin Arbiter
仲裁器设计(三)-- Weighted Round Robin
负载均衡算法–轮询法(Round Robin)
1. Fixed_Priority_Arbiter
这种仲裁算法很简单,这几个Master的优先级是固定的,永远是优先级最高的Master可以访问Slave。
1.1. 参数描述
Signal | Direction | Width(bits) | Description |
---|---|---|---|
req | input | REQ_WIDTH | Master请求信号,优先级由0到REQ_WIDTH-1依次递减 |
grant | input | REQ_WIDTH | 表明被赋予访问权的Master索引,该信号只有1bit为1 |
Parameter | Units | Description |
---|---|---|
REQ_WIDTH | bit | 参与仲裁的Master个数 |
1.2. 逻辑设计
首先可以发现这是一个组合逻辑,即当前的输出只与当前的输入有关。
涉及的数学关系是,req所有为1的位中最低的那一位就是grant为1的那一位。我们可以用真值表表达这层关系
例如3bit
req | grant |
---|---|
xx1 | 001 |
x10 | 010 |
100 | 100 |
由此可以直接写出逻辑表达式
grant[0] = req[0];
grant[1] = req[1] && (~req[0]);
grant[2] = req[2] && (~req[1])&& (~req[0])
= req[2] && (~(req[1] || req[0]));
grant[3] = req[3] && (~req[2])&& (~req[1])&& (~req[0])
= req[3] && (~(req[2]||req[1]||req[0]));
...
grant[i] = req[i] && (~(|req[i-1:0]));
由这个逻辑表达式直接得出代码
module fix_prio_arb#(
parameter REQ_WIDTH = 15
)(
input [REQ_WIDTH-1:0] req,
output [REQ_WIDTH-1:0] grant
);
genvar i;
generate
for(i=0;i<REQ_WIDTH;i=i+1) begin
if(i==0)
assign grant[0] = req[0];
else
assign grant[i] = req[i] && (~(|req[i-1:0]));
end
endgenerate
endmodule
当然还有更简单的解法,你不是req从右向左找到第一个1嘛,那就求req的补码。如果req可以表示为'bxxx...xx100...00
,那么其补码就是req_offset='b(!x)(!x)(!x)...(!x)(!x)100...000
,再令grant = req&req_offset;
即可
1.3. 测试
测试也比较简单,此处直接上图
2. Round_Robin_Arbiter
优先级固定的弊端就是最高优先级的Master可以永远与这个Slave保持通信,其他Master可能永远都没有机会。
于是就有了这个轮询调度算法,这个算法的意思是当所有Master都请求访问时,Slave轮流赋予访问权力
● req == {REQ_WIDTH{1'b1}};
永远成立
即所有Master都一直请求对Slave进行访问,那么Slave会在每一拍对这几个Master轮流访问。
即访问权限grant信号变成4’b0001→4’b0010→4’b0100→4’b1000→4’b0001→…
也就等价于访问权重变成3210→2103→1032→0321→3210→…(0权重最大、3最小)
● req == {REQ_WIDTH{1'b1}};
不永远成立
即req信号不是每拍都全bit为1、不是所有Master都一直要对Slave进行访问。那么访问权重还是上述那个顺序,只不过会根据req的取值,访问权重跳跃取值
如上图,黑色部分是Master轮流访问,红色部分是因为req非全1而导致的权重跳跃现象。
例如Prio==3210;req==4'b1010;
时,根据Prio显然grant[1]被置1,那么下一拍它的权重Prio[1]就要为最低值3,其他bit的权重则按照轮询的顺序依次赋值,即Prio==1032
。下面的过程同理。
2.1. 参数描述
Signal | Direction | Width(bits) | Description |
---|---|---|---|
rstn | input | 1 | 复位 |
clk | input | 1 | 时钟 |
req | input | REQ_WIDTH | Master请求信号,优先级由0到REQ_WIDTH-1依次递减 |
grant | input | REQ_WIDTH | 表明被赋予访问权的Master索引,该信号只有1bit为1 |
Parameter | Units | Description |
---|---|---|
REQ_WIDTH | bit | 参与仲裁的Master个数 |
MAX_PRIO_INITIAL_INDEX | 初始状态下,权重最高Master的索引,次高的索引是该值+1,并在0~REQ_WIDTH-1之间回环 |
2.2. 逻辑设计
这不是典型的状态机嘛,状态就是权重最高的bit,所以状态机的状态个数就是REQ_WIDTH
但由于req的取值不确定可能导致权重跳跃,所以每个状态直接都会可以相互转换,这是麻烦的地方。
MAX_PRIO_BITx
表示bit x权重最大时的状态,想一想该状态下要干什么事情、以及状态转换条件是什么
● 根据req输出grant
根据MAX_PRIO_INDEX的描述,bit x权重最高时,bit x+1的权重就是次高,则有:
若prio[x]
表示bit x的权重那么在MAX_PRIO_BITx状态下一定满足prio[x]>prio[x+1]>...>prio[REQ_WIDTH-1]>prio[0]...>prio[x-1];
,按照这个顺序,req中全部为1中权重最高的那一位,grant置1
哎,这个与Fixed_Priority_Arbiter的区别在哪?Fixed_Priority_Arbiter的权重顺序是prio[0]>prio[1]>...>prio[REQ_WIDTH-1];
,区别只是权重顺序不同,那么权重顺序不同在计算grant上有和区别?
想一下Fixed_Priority_Arbiter中是assign grant = req & (~req + 1'b1);
,那么权重顺序发生变化时,对req按位取反不变,只不过加的不是’b1,而是权重最高的那一位加1。而权重最高的那一位加1可能会导致结果溢出,而溢出的那个1应该加在bit 0上。
● 根据grant作状态转换
上面的那个图已经给出状态转换成啥了,这是基于grant信号进行转换的,那么什么时候进行状态转换呢?
从功能的角度来说,应该是输入一个req之后输出grant,但是状态不发生转换,直到req发生变化再发生转换。如下图所示。
根据req和当前cur_state可以得到grant,有了grant就可以计算出下一个状态是什么,那么计算出来就要立即转换到吗?不能,因为到了新状态有会有新的grant,有了新grant又会有再新的状态…这样grant就会反复横跳。
要想知道req什么时候变化就需要对req进行值变化检测,req_diff为高时表示req已经发生了变化。此时cur_state要发生变化,那变成几呢?
如上图所示,由于grant与req之间是组合逻辑,所以req变化而cur_state未变化会导致grant可能出现中间值(图中grant为4’b0001那一拍),这个值显然是不能作为cur_state变化的参考(如绿线所示),而且也可看作grant变化过程中的噪声。
这下cur_state转换的问题解决了,grant出现中间值如何解决?
注意到这一拍的噪声与req_diff为高是时序对齐的,所以只需令输出grant在req_diff为高时保持原值即可,这样nxt_state可以计算出下一个状态。
如何保持原值呢?很简单,只需在req_diff为高时,在grant组合运算逻辑中将req替换为req_d1即可。
注意按照上面的时序,req变化与grant变化差一拍!
你以为这就完了?并不是,在实际仿真中,会发生如下的情况
就是说,刚开始req为0,那么grant也是0,在req发生变化后req_diff拉高,但grant依旧为0,nxt_state根据grant得到的结果也是0,那么下一拍cur_state也是0,都是无效数据。
这一切都源自于req非0,但实际情况下其实没有请求发来是非常正常的,所以此处可以改变req_diff拉高的条件,当req为0时不拉高。
代码
然后开始写代码,需要注意的问题就是系统状态的个数也是REQ_WIDTH,所以如何写localparam?
我的处理方法是,不写localparam,将MAX_PRIO_BITx用位宽为REQ_WIDTH且只有bit x为1的常量表示。
module round_robin_arb#(
parameter REQ_WIDTH = 15,
parameter MAX_PRIO_INITIAL_INDEX = 7
)(
input clk,
input rstn,
input [REQ_WIDTH-1:0] req,
output [REQ_WIDTH-1:0] grant
);
reg [REQ_WIDTH-1:0] cur_state;
reg [REQ_WIDTH-1:0] nxt_state;
reg [REQ_WIDTH-1:0] req_d1;
wire [REQ_WIDTH-1:0] inital_state;
wire req_diff;
reg [REQ_WIDTH:0] res1;
wire [REQ_WIDTH:0] res2;
assign inital_state = {{(REQ_WIDTH-MAX_PRIO_INITIAL_INDEX-1){1'b0}},1'b1,{MAX_PRIO_INITIAL_INDEX{1'b0}}};
always@(posedge clk or negedge rstn) begin
if(!rstn)
req_d1 <= 'd0;
else
req_d1 <= req;
end
assign req_diff = (req != req_d1) && (|req_d1);
always@(posedge clk or negedge rstn) begin
if(!rstn)
cur_state <= inital_state;
else
cur_state <= nxt_state;
end
always@(*) begin
if(!req_diff)
nxt_state = cur_state;
else if(grant[REQ_WIDTH-1])
nxt_state = 'b1;
else
nxt_state = grant << 1;
end
assign res2 = res1[REQ_WIDTH] + res1;
assign grant = (!req_diff)?(req & res2):(req_d1 & res2);
always@(*) begin
if(!req_diff)
res1 = {1'b0,~req} + {1'b0,cur_state};
else
res1 = {1'b0,~req_d1} + {1'b0,cur_state};
end
endmodule
2.3. 测试
先上一个tb
`timescale 1ns/1ps
module arb_tb();
logic clk;
logic rstn;
logic [7:0] req;
logic [7:0] grant;
initial begin
clk = 1'b0;
forever #5 clk = ~clk;
end
initial begin
rstn = 1'b1;
#20;
rstn = 1'b0;
#20;
rstn = 1'b1;
end
initial begin
req = 8'h0;
#100;
req = 8'hCD;
#30;
req = 8'h02;
#30;
req = 8'hA0;
#30;
req = 8'hB0;
#30;
req = 8'h30;
#30;
end
round_robin_arb#(
.REQ_WIDTH (8),
.MAX_PRIO_INITIAL_INDEX (3)
)dut(
.clk (clk),
.rstn (rstn),
.req (req),
.grant (grant)
);
endmodule
波形图如下,可以看出grant的结果符合预期