本节主要介绍了固定优先级仲裁器、RR轮询调度仲裁器、WRR仲裁器的调度规则及其Verilog的实现方式。
固定优先级仲裁器
- 固定优先级仲裁器即请求的优先级顺序是永远固定的,若规定优先级req[0]>req[1]>req[2]>req[3]…,那么则永远遵循这个优先级顺序。
1.case/if写法
Verilog代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31module fixed_prio_arb(
input [2 : 0] req,
output reg [2 : 0] grant
);
// always @(*) begin
// grant = 3'b000;
// case(1'b1)
// req[0]: grant = 3'b001;
// req[1]: grant = 3'b010;
// req[2]: grant = 3'b100;
// default: grant = 3'b000;
// endcase
// end
always @(*) begin
if(req[0]) begin
grant = 3'b001;
end
else if(req[1]) begin
grant = 3'b010;
end
else if(req[2]) begin
grant = 3'b100;
end
else begin
grant = 3'b000;
end
end
endmodule上述两种方法等效,它们综合后的结果都如下:
2.for/assign循环写法*
从低位到高位依次去判断,借助一个pre_req来记录低位是否已经有了request, 如果第i位有了request,那么第i+1位一直到最高位的pre_req都是1。
值得一提的是如下掩码写法:
1
assign pre_req[REQ_WIDTH - 1 : 1] = req[REQ_WIDTH - 2 : 0] | pre_req[REQ_WIDTH - 2 : 0];
其实它等效于(以REQ_WIDTH = 4为例):
1
2
3pre_req[1] = req[0] | pre_req[0];
pre_req[2] = req[1] | pre_req[1];
pre_req[3] = req[2] | pre_req[2];Verilog代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28module prior_arb #(
parameter REQ_WIDTH = 16
)(
input [REQ_WIDTH - 1 : 0] req,
output reg [REQ_WIDTH - 1 : 0] grant
);
integer i;
// reg [REQ_WIDTH - 1 : 0] pre_req; //记录低位是否已经有了request, 如果第i位有了request,那么第i+1位一直到最高位的pre_req都是1。
// always @(*) begin
// pre_req[0] = req[0];
// grant[0] = req[0];
// for(i = 1; i < REQ_WIDTH; i = i + 1) begin
// grant[i] = req[i] & !pre_req[i - 1]; // current req & no higher priority request
// pre_req[i] = req[i] | pre_req[i - 1]; // or all higher priority requests
// end
// end
wire [REQ_WIDTH - 1 : 0] pre_req;
assign pre_req[0] = 1'b0;
assign pre_req[REQ_WIDTH - 1 : 1] = req[REQ_WIDTH - 2 : 0] | pre_req[REQ_WIDTH - 2 : 0];//很神奇的写法,从未见过
assign grant = req & ~pre_req;
endmodule综合后的电路均为如下:
3.利用补码特性写法*
本质上,我们要做的是找req这个信号里从低到高第一个出现的1,那么我们给req减去1会得到什么?假设req的第i位是1,第0到第i-1位都是0,那么减去1之后我们知道低位不够减,得要向高位借位,直到哪一位可以借到呢?就是第一次出现1的位,即从第i位借位,第0到i-1位都变成了1,而第i位变为了0,更高位不变。
然后我们再给减1之后的结果取反,然后把结果再和req本身按位与,可以得出,只有第i位在取反之后又变成了1,而其余位都是和req本身相反的,按位与之后是0,这样就提取出来了第一个为1的那一位,也就是我们需要的grant。
再考虑一下特殊情况req全0,很明显,按位与之后gnt依然都是全0,没有任何问题。
对二进制数来说,先减1后取反和先取反后加1得到的结果是一样的
一个数和它的补码相与,得到的结果是一个独热码,独热码为1的那一位是这个数最低的1。
Verilog代码:
1
2
3
4
5
6
7
8
9
10module prior_arb2 #(
parameter REQ_WIDTH = 16
)(
input [REQ_WIDTH-1 : 0] req,
output [REQ_WIDTH-1 : 0] gnt
);
assign gnt = req & (~(req - 1));
endmodule综合后的结果如下:
RR轮询仲裁器
req每一位轮流当最高优先级,详细解释见Reference中的第二个。
循环优先级的简单示意表格:(0~3优先级依次减小)
reg[3] reg[2] reg[1] reg[0] 3 2 1 0 2 1 0 3 1 0 3 2 0 3 2 1
1.基于输入给定优先级的固定优先级仲裁器(优先级改变,req不变)的RR写法
输入给定优先级的固定优先级仲裁器:Verilog代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17module arbiter_base #(
parameter NUM_REQ = 4
)(
input [NUM_REQ-1:0] req,
input [NUM_REQ-1:0] base,
output [NUM_REQ-1:0] gnt
);
wire [2 * NUM_REQ - 1 : 0] double_req;
assign dounle_req = {req, req};
wire [2 * NUM_REQ -1 : 0] double_gnt;
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在这个模块中,base是一个onehot的信号,它为1的那一位表示这一位的优先级最高,然后其次是它的高位即左边的位,直到最高位后回到第0位绕回来,优先级依次降低,直到为1那一位右边的这位为最低。咱们以4位为例,如果base = 4’b0100, 那么优先级是bit[2] > bit[3] > bit[0] > bit[1]。
这个设计的思路和前面补码特性写法很像,里面double_req & ~(double_req-base)其实就是利用减法的借位去找出base以上第一个为1的那一位,只不过由于base值可能比req值要大,不够减,所以要扩展为{req, req}来去减。当base=4‘b0001的时候就是上一节里面的最后的算法(那一节的-1相当于是-base等于4’b0001的情况)。当然base=4’b0001的时候不存在req不够减的问题,所以不用扩展。
那么好了,既然有了可以根据输入给定优先级的固定优先级仲裁器,那么接下来的任务就简单了,每次grant之后,把优先级调整一下就可以了呗。而且这个设计妙就妙在,base要求是一个onehot signal,而且为1的那一位优先级最高。我们前面说过,grant一定是onehot,grant之后被grant的那一路优先级变为最低,它的高1位优先级变为最高,
所以,只需要一个history_reg,来去记录之前最后grant的值,然后只需要将grant的值左移一下就变成了下一个周期的base。比如说,假设上一个周期grant为4’b0010,那么bit[2]要变为最高优先级,那只需要base是grant的左移即可(妙啊)
最终,RTL代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31module round_robin_arbiter #(
parameter NUM_REQ = 4
)(
input clk,
input rstn,
input [NUM_REQ-1:0] req,
output [NUM_REQ-1:0] gnt
);
reg [NUM_REQ-1:0] hist_q, hist_d;
always @(posedge clk) begin
if(!rstn) begin
hist_q <= {{NUM_REQ-1{1'b0}}, 1'b1};
end
else begin
if(|req) begin
hist_q <= {gnt[NUM_REQ-2:0], gnt[NUM_REQ-1]};
end
end
end
arbiter_base #(
.NUM_REQ(NUM_REQ)
) arbiter(
.req (req),
.gnt (gnt),
.base (hist_q)
);
endmoduleRTL原理图如下:
2.基于req改变,优先级不变的RR写法
当某一路request已经grant之后,我们人为地把进入fixed priority arbiter的这一路req给屏蔽掉,这样相当于只允许之前没有grant的那些路去参与仲裁,grant一路之后就屏蔽一路,等到剩余的request都依次处理完了再把屏蔽放开,重新来过。这就是利用屏蔽mask的办法来实现round robin的思路。
Verilog代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56module round_robin_arbiter2 #(
parameter N = 16
)(
input clk,
input rst,
input [N - 1 : 0] req,
output [N - 1 : 0] grant
);
wire [N - 1 : 0] req_masked;
wire [N - 1 : 0] mask_higher_pri_reqs;
wire [N - 1 : 0] grant_masked;
wire [N - 1 : 0] unmask_higher_pri_reqs;
wire [N - 1 : 0] grant_unmasked;
wire no_req_masked;
reg [N - 1 : 0] pointer_reg;
// Simple priority arbitration for masked portion
assign req_masked = req & pointer_reg;
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[N - 1 : 0] = req_masked[N - 1 : 0] & ~mask_higher_pri_reqs[N - 1 : 0];
// Simple priority arbitration for unmasked portion
assign unmask_higher_pri_reqs[N - 1 : 1] = unmask_higher_pri_reqs[N - 2 : 0] | req[N - 2 : 0];
assign unmask_higher_pri_reqs[0] = 1'b0;
assign grant_unmasked[N - 1 : 0] = req[N - 1 : 0] & ~unmask_higher_pri_reqs[N - 1 : 0];
// Use grant_masked if there is any there, otherwise use grant_unmasked.
assign no_req_masked = ~(|req_masked);
assign grant = ({N{no_req_masked}} & grant_unmasked) | grant_masked;
// Pointer update
always @ (posedge clk) begin
if (rst) begin
pointer_reg <= {N{1'b1}};
end
else begin
if (|req_masked) begin // Which arbiter was used?
pointer_reg <= mask_higher_pri_reqs;
end
else begin
if (|req) begin // Only update if there's a req
pointer_reg <= unmask_higher_pri_reqs;
end
else begin
pointer_reg <= pointer_reg ;
end
end
end
end
endmoduleRTL原理图:
上述代码中,mask_higher_pri_reqs(grant_masked)这一路是用来屏蔽上一次grant右边的req,并让左边req通过,对左侧的req进行grant授予(与固定优先级那方法一致,即找左侧req中第一个1的位置)
grant_unmasked这一路是用来执行当此时req有请求的bit全部被屏蔽,没有被屏蔽的bit又无请求的情况,这时就按正常的固定优先级的方式去处理req
WRR权重轮询仲裁器
WRR是基于RR的,每个req都有权重,当每个req的grant次数计数到权重时,则按照RR的规则切换优先级顺序,具体解释见reference的3、4
weight round robin保证的是你有连续的request的时候,grant可以持续给你(但得小于grant weight),但是一旦你有一个周期没有request,那么你就被跳过了,得再等一圈
1.基于grant的WRR
Verilog代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91module weight_rr_arbiter #(
parameter REQ_CNT = 4,
GRANT_WIDTH = 5,
localparam WEIGHT_WIDTH = GRANT_WIDTH * REQ_CNT
)(
input clk ,
input rst_n ,
input [REQ_CNT - 1 : 0] req ,
input [WEIGHT_WIDTH - 1 : 0] weight ,
input switch_to_next ,
output [REQ_CNT - 1 : 0] grant ,
output grant_vld ,
output reg [REQ_CNT - 1 : 0] grant_ff ,
output reg grant_ff_vld
);
parameter INIT_GRANT = {{REQ_CNT-1{1'b0}}, 1'b1};
reg [REQ_CNT-1:0] grant_base;
wire [REQ_CNT*2-1:0] double_req;
wire [REQ_CNT*2-1:0] double_grant;
reg [GRANT_WIDTH-1:0] priority_cnt [REQ_CNT-1:0];
wire [REQ_CNT-1:0] cnt_over;
wire [REQ_CNT-1:0] round_en;
wire req_change;
wire no_req;
wire no_grant;
wire first_grant;
wire arb_trig;
assign no_req = ~(|req);
assign no_grant = ~(|grant_ff);
assign first_grant = no_grant && ~no_req;
assign arb_trig = first_grant || switch_to_next;
assign req_change = ~(|(grant_ff & req)) && |req;
//priority_cnt逻辑
generate
genvar i;
for(i=0;i<REQ_CNT;i=i+1)begin
assign cnt_over[i] = priority_cnt[i] == weight[GRANT_WIDTH*(i+1)-1-:GRANT_WIDTH];
assign round_en[i] = cnt_over[i] || (priority_cnt[i]!=0 && req_change);
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
priority_cnt[i] <= {GRANT_WIDTH{1'b0}};
else if(cnt_over[i] && arb_trig)
priority_cnt[i] <= {GRANT_WIDTH{1'b0}};
else if(grant[i])
priority_cnt[i] <= priority_cnt[i] + 1'b1;
end
end
endgenerate
//---------基于grant的RR---------
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
grant_base[REQ_CNT-1:0] <= INIT_GRANT;
else if(|round_en && ~no_grant)
grant_base[REQ_CNT-1:0] <= {grant_ff[REQ_CNT-2:0], grant_ff[REQ_CNT-1]};
end
assign double_req[REQ_CNT*2-1:0] = {req,req};
assign double_grant[REQ_CNT*2-1:0] = double_req & (~(double_req - grant_base));
assign grant = arb_trig? (double_grant[REQ_CNT*2-1:REQ_CNT] | double_grant[REQ_CNT-1:0]) : {REQ_CNT{1'b0}};
assign grant_vld = (arb_trig && ~no_req)? 1'b1 : 1'b0;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
grant_ff <= {REQ_CNT{1'b0}};
else if(arb_trig)
grant_ff <= grant;
else
grant_ff <= grant_ff;
end
//------------------------------
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
grant_ff_vld <= 1'b0;
else if(arb_trig)
grant_ff_vld <= no_req? 1'b0 : 1'b1;
end
endmodule
2.基于request的WRR
Verilog代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134module weight_rr_arbiter2(
clk,
rst_n,
req,
weight,
grant,
grant_vld,
grant_ff,
grant_ff_vld,
switch_to_next
);
parameter REQ_CNT = 4;
parameter GRANT_WIDTH = 5;
parameter INIT_GRANT = {{REQ_CNT-1{1'b0}}, 1'b1};
parameter WEIGHT_WIDTH = GRANT_WIDTH * REQ_CNT;
input clk;
input rst_n;
input [REQ_CNT-1:0] req;
input [WEIGHT_WIDTH-1:0] weight;
input switch_to_next;
output [REQ_CNT-1:0] grant;
output grant_vld;
output [REQ_CNT-1:0] grant_ff;
output grant_ff_vld;
wire [REQ_CNT-1:0] grant;
wire grant_vld;
reg [REQ_CNT-1:0] grant_ff;
reg grant_ff_vld;
wire no_req;
wire no_grant;
wire first_grant;
wire arb_trig;
reg [GRANT_WIDTH-1:0] priority_cnt [REQ_CNT-1:0];
wire [REQ_CNT-1:0] cnt_over;
wire round_en;
assign no_req = ~(|req);
assign no_grant = ~(|grant_ff);
assign first_grant = ~no_req && no_grant;
assign arb_trig = first_grant || switch_to_next;
assign round_en = |cnt_over[REQ_CNT-1:0];
wire [REQ_CNT-1:0] req_masked;
wire [REQ_CNT-1:0] mask_higher_pri_reqs;
wire [REQ_CNT-1:0] grant_masked;
wire [REQ_CNT-1:0] unmask_higher_pri_reqs;
wire [REQ_CNT-1:0] grant_unmasked;
wire no_req_masked;
reg [REQ_CNT-1:0] mask_next;
//Simple priority arbitration for masked portion
assign req_masked[REQ_CNT-1:0] = req & mask_next;
assign mask_higher_pri_reqs[0] = 1'b0;
assign mask_higher_pri_reqs[REQ_CNT-1:1] = req_masked[REQ_CNT-2:0] | mask_higher_pri_reqs[REQ_CNT-2:0];
assign grant_masked[REQ_CNT-1:0] = req_masked[REQ_CNT-1:0] & ~mask_higher_pri_reqs[REQ_CNT-1:0];
//Simple priority arbitration for unmasked portion
assign unmask_higher_pri_reqs[0] = 1'b0;
assign unmask_higher_pri_reqs[REQ_CNT-1:1] = req[REQ_CNT-2:0] | unmask_higher_pri_reqs[REQ_CNT-2:0];
assign grant_unmasked[REQ_CNT-1:0] = req[REQ_CNT-1:0] & ~unmask_higher_pri_reqs[REQ_CNT-1:0];
//Use grant_masked if there is any there, otherwise use grant_unmasked
assign no_req_masked = ~(|req_masked);
assign grant = ({REQ_CNT{no_req_masked}} & grant_unmasked) | grant_masked;
assign grant_vld = (arb_trig && |req)? 1'b1 : 1'b0;
//round cnt
generate
genvar i;
for(i=0;i<REQ_CNT;i=i+1)begin
assign cnt_over[i] = (priority_cnt[i] == weight[GRANT_WIDTH*(i+1)-1-:GRANT_WIDTH]-1'b1);
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
priority_cnt[i] <= {GRANT_WIDTH{1'b0}};
else if(cnt_over[i])
priority_cnt[i] <= {GRANT_WIDTH{1'b0}};
else if(grant[i] && grant_vld)
priority_cnt[i] <= priority_cnt[i] + 1'b1;
end
end
endgenerate
//pointer update
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
mask_next <= {REQ_CNT{1'b1}};
else begin
case({first_grant,round_en})
2'b10:begin
if(|req_masked)
mask_next <= req_masked | mask_higher_pri_reqs;
else
mask_next <= req | unmask_higher_pri_reqs;
end
2'b01,2'b11:begin
if(|req_masked)
mask_next <= mask_higher_pri_reqs;
else begin
if(|req)
mask_next <= unmask_higher_pri_reqs;
else
mask_next <= mask_next;
end
end
default:mask_next <= mask_next;
endcase
end
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
grant_ff <= {REQ_CNT{1'b0}};
grant_ff_vld <= 1'b0;
end
else if(arb_trig)begin
grant_ff <= grant;
grant_ff_vld <= no_req? 1'b0 : 1'b1;
end
else begin
grant_ff <= grant_ff;
grant_ff_vld <= grant_ff_vld;
end
end
endmodule