这里写目录标题
一级目录
二级目录
三级目录
1、仲裁Arbiter简介
多个信号同时请求一个处理时,需要一个名为仲裁器的模块来选择优先级高的信号处理。通常输入一个多信号优先级请求i_req[:0],经过处理后输出一个处理结果信号o_result[:0],来表明谁被许可处理。常见有固定优先级仲裁器和循环优先级仲裁器。
2、固定优先级仲裁器原理实现
顾名思义,即优先级固定,每一次都以同一优先级判断,前后仲裁不影响。代码用if-else/case语句就可简单实现。即:
module fixed_arbiter4(
input clk,
input rst_n,
input [3:0] i_req,
output reg [3:0] o_result
);
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
o_result <= 4'b0001; //默认i_req第0位优先
/*
//一次只允许一位请求,不太合理
else begin
case(i_req)
4'b0001 : o_result <= 4'b0001;
4'b0010 : o_result <= 4'b0010;
4'b0100 : o_result <= 4'b0100;
4'b1000 : o_result <= 4'b1000;
default :o_result <= 4'b0001;
end
*/
//一次允许多位请求,优先顺序位i_req[0]>i_req[1]i_req[2]>i_req[3]
else begin
case(1'b1)
i_req[0] :o_result <= 4'b0001;
i_req[1] :o_result <= 4'b0010;
i_req[2] :o_result <= 4'b0100;
i_req[3] :o_result <= 4'b1000;
default:o_result <= 4'b0001;
endcase
end
end
endmodule
测试代码也很简单,这里不赘述。
3、循环优先级仲裁器原理
每一次仲裁的优先级都是根据上一次仲裁结果而定的,即本次的仲裁结果会影响下一次仲裁的优先级。
因为这样仲裁的优先级会改变,所以理论上每个请求的几率基本平等。实现上可用一个优先级状态寄存器来完成。
基本原理思想:
1、请求信号i_req[3:0],则默认规定最低位优先级最高,往左依次递减。
2、一次仲裁中,某一位获得请求后,则下一次仲裁时,其左侧优先级变为最高,依次递减,循环回到自己优先级最低。
例如:
请求信号i_req[3:0],优先级 i_req[0]>i_req[1]>i_req[2]>i_req[3]。
第一次发起仲裁请求的输入为i_req[3:0] = 4’b 0 1 1 0,
根据规则判断i_req[1]获得允许,输出o_result[4:0] = 4’b 0 0 1 0。
与此同时,下一次仲裁的优先级即变成了:
i_req[2]>i_req[3]>i_req[0]>i_req[1],即上一次的优先在本级最后。
4、实现方法
一个仲裁器通常包含两部分组成,即一个优先级状态寄存器阵列和一个请求计算模块。
4.1、优先级状态寄存器阵列
优先级状态寄存器阵列,用于保存每个请求和其他请求之间的关系。如果一个仲裁电路有N个请求,那么需要一个(N-1)*N的寄存器阵列,阵列设置不同则代表含义不同,下面以其4个请求仲裁的一种阵列说明。
总共有4个请求,则每个请求需要3个寄存器来表示其他外部请求优先级是否高于本请求优先级。本例中,优先级关系规定为:本请求比另一个高则赋0,低则赋1。
。
其中req0_1,req0_2,req0_3就表示R[0]相对于R[1]、R[2]、R[3]的优先级。
假设初始优先级顺序:i_req[0]>i_req[1]>i_req[2]>i_req[3]
初始矩阵如下左图,此时R[0]获得允许,下一次R[1]优先级最高,往左依次递减,R[0]优先级最低,
此时优先级顺序为:i_req[0]>i_req[1]>i_req[2]>i_req[3],矩阵变为下右图
上述的改变过程可用状态机实现,值得注意的是,状态机是根据本次输出的结果o_result[4:0]来修改阵列的值,而不是输入的i_req[0],这里怎么理解呢?
因为输入可能有两位同时有效,即输入i_req[3:0] = 4’b 0 1 0 1,这样不符合状态机运行规则,而经过计算模块o_result[4:0]每次只会有1位有效(下个部分介绍怎样计算才能保证无论输入几位有效,输出有且只有一位有效),这样才可以用状态机来实现。至于第一次输出,第一次输出不过状态机,根据初始矩阵数据和输入实现第一次输出,然后过状态机修改矩阵。
4.2、请求计算模块
根据上述优先状态寄存器的描述可知,本次输出的结果和两个值有关,分别是上一次仲裁后修改的阵列和本次输入来判断本次输出。
即某个请求响应的条件是:1、本请求对应的输入有效;2、本次有效的请求优先级顺位最高。
计算包含两部分:第一级“与非”,三次与非依据优先级状态寄存器阵列中保存的请求优先级别仲裁;第二级“与”,四次与完成当前请求是否有效。
例如:o_result[0] =
i_req[0] & //
(~(req0_1&i_req[1]))&
(~(req0_2&i_req[2]))&
(~(req0_3&i_req[3]));
o_result[0]输出有效的条件是:
1、请求输入0位有效,即:i_req[0];第0位优先级比第1位高,比第2位高,比第3位高。
5、Verilog HDL代码
模块代码:
module arbiter4(
input clk,
input rst_n,
input [3:0] i_req,
output reg [3:0] o_result
);
//优先级状态寄存器
reg req0_1,req0_2,req0_3;//第0位3个优先级状态
reg req1_0,req1_2,req1_3;//第1位3个优先级状态
reg req2_0,req2_1,req2_3;//第2位3个优先级状态
reg req3_0,req3_1,req3_2;//第3位3个优先级状态
//优先级状态阵列
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin//默认情况下req[0]优先级最高,三位分别000;优先级往左依次递减,req[4]优先级最低,三位分别111
req0_1 <= 1'b0; req1_0 <= 1'b1; req2_0 <= 1'b1; req3_0 <= 1'b1;
req0_2 <= 1'b0; req1_2 <= 1'b0; req2_1 <= 1'b1; req3_1 <= 1'b1;
req0_3 <= 1'b0; req1_3 <= 1'b0; req2_3 <= 1'b0; req3_2 <= 1'b1;
end
else begin //本次输出决定下次优先级
case(o_result)
4'b0001:begin //上次req[0]优先,本次最后
req0_1 <= 1'b1;
req0_2 <= 1'b1;
req0_3 <= 1'b1; //req[0]本次最后
req1_0 <= 1'b0;
req2_0 <= 1'b0;
req3_0 <= 1'b0; //修改其余3位优先级
end
4'b0010:begin //上次req[1]优先,本次最后
req1_0 <= 1'b1;
req1_2 <= 1'b1;
req1_3 <= 1'b1; //req[1]本次最后
req0_1 <= 1'b0;
req2_1 <= 1'b0;
req3_1 <= 1'b0; //修改其余3位优先级
end
4'b0100:begin //上次req[2]优先,本次最后
req2_0 <= 1'b1;
req2_1 <= 1'b1;
req2_3 <= 1'b1; //req[2]本次最后
req0_2 <= 1'b0;
req1_2 <= 1'b0;
req3_2 <= 1'b0; //修改其余3位优先级
end
4'b1000:begin //上次req[3]优先,本次最后
req3_0 <= 1'b1;
req3_1 <= 1'b1;
req3_2 <= 1'b1; //req[3]本次最后
req0_3 <= 1'b0;
req1_3 <= 1'b0;
req2_3 <= 1'b0; //修改其余3位优先级
end
endcase
end
end
//请求响应计算
always@(*)
begin
o_result[0] = i_req[0] &
(~(req0_1 & i_req[1])) &
(~(req0_2 & i_req[2])) &
(~(req0_3 & i_req[3]));
o_result[1] = i_req[1] &
(~(req1_0 & i_req[0])) &
(~(req1_2 & i_req[2])) &
(~(req1_3 & i_req[3]));
o_result[2] = i_req[2] &
(~(req2_0 & i_req[0])) &
(~(req2_1 & i_req[1])) &
(~(req2_3 & i_req[3]));
o_result[3] = i_req[3] &
(~(req3_0 & i_req[0])) &
(~(req3_1 & i_req[1])) &
(~(req3_2 & i_req[2]));
end
endmodule
Testbench:
`timescale 1ns/1ns
module tb_arbiter4;
reg clk;
reg rst_n;
reg [3:0] i_req;
wire [3:0] o_result;
arbiter4 arbiter4_u0(
.clk (clk),
.rst_n (rst_n),
.i_req (i_req),
.o_result (o_result)
);
initial begin
clk = 1'b0;
rst_n = 1'b0;
i_req = 4'b0000;
#20 rst_n = 1'b1;
#10 i_req = 4'b0101;
#20 i_req = 4'b1111;
#20 i_req = 4'b0110;
#20 i_req = 4'b1100;
end
always #10 clk = ~clk;
endmodule
6、仿真分析
在第一次写测试时是这样写的,导致的波形有一个输入数据4’b1111无效,
以为是哪里有问题,反复检查才发现问题,代码没错。
只是这个数据恰好处在时钟下降沿,所以没处理。简单修改testbench即可。
错误数据输入:错误仿真波形:,中间有一个数据被漏掉了。
正确数据输入:
正确波形:,每个输入数据都处理。