-
Notifications
You must be signed in to change notification settings - Fork 43
/
Copy pathArbiter_Round_Robin.v
238 lines (189 loc) · 9.03 KB
/
Arbiter_Round_Robin.v
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
//# A Round-Robin Arbiter
// Returns a one-hot grant bitmask selected from one of the raised request
// bits in a word, in round-robin order, going from least-significant bit
// (highest priority) to most-significant bit (lowest priority), and back
// around. *A grant is held until the request is released.*
// Unset request bits are skipped, which avoids wasting time. Requests can be
// raised or dropped before their turn comes, but this must be done
// synchronously to the clock. *Grants are calculated combinationally from the
// requests*, so pipeline as necessary. If the `requests_mask` bit
// corresponding to a `requests` bit is zero, then that request cannot be
// granted that cycle. The round-robin always continues from the last granted
// request, even after an idle period of no requests, unless `clear` is
// asserted.
//## Usage
// A common use-case for an arbiter is to drive a [one-hot
// multiplexer](./Multiplexer_One_Hot.html) to select one of multiple senders
// requesting for one receiver, or one of multiple receivers requesting from
// one sender. This arrangement requires that the requestors can raise and
// hold a `requests` bit, wait until they receive the correspondig `grant` bit
// to begin their transaction, and to drop their `requests` bit only once they
// are done. This is very similar to a ready/valid handshake, except that the
// transaction cannot be interrupted, else the granted access is lost.
//## Fairness
// Here, we implement a "mask method" round-robin arbiter, as described in
// Section 4.2.4, Figure 12 of Matt Weber's [Arbiters: Design Ideas and Coding
// Styles](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.86.550&rep=rep1&type=pdf).
// A round-robin arbiter is commonly used to fairly divide a resource amongst
// multiple requestors, *in proportion to each requestor's activity*, since
// each requestor holds a grant until they lower their request. Idle
// requestors don't use up any of the arbitrated resource. A frequent
// requestor will not obstruct other requestors perpetually.
// However, this round-robin arbiter does not deal with some subtleties of
// fairness. For example, it's possible that under normal operation, one
// requestor ends up placing a request always just before the previous
// requestor finishes. Thus, that new request waits less than the others. One
// solution periodically takes a snapshot of the current pending requests, and
// services all of these requests before refreshing the snapshot.
//### Customizations
// To enable the creation of custom fairness adjustments, the `requests_mask`
// input can be used to exclude one or more requests from being granted in the
// current cycle, and must be updated synchronously to the `clock`. The
// `requests_mask` is arbitrary, and if desired can be calculated from the
// current `requests`, the `grant_previous` one-hot output which always holds
// the last given grant even if the requests are currently all idle, and the
// current one-hot `grant` output.
// * Since a grant typically lasts longer than one cycle and won't get granted
// again for several cycles, taking multiple cycles to compute the next
// `requests_mask` is a valid option.
// * The `requests_mask` is applied combinationally to the `requests` input
// and to the internal `round_robin_mask`, both of which have a combinational
// path to `grant`, so pipeline as necessary.
// If unused, leave `requests_mask` set to all-ones, and the masking logic
// will optimize away.
`default_nettype none
module Arbiter_Round_Robin
#(
parameter INPUT_COUNT = 0
)
(
input wire clock,
input wire clear,
input wire [INPUT_COUNT-1:0] requests,
input wire [INPUT_COUNT-1:0] requests_mask, // Set to all-ones if unused.
output wire [INPUT_COUNT-1:0] grant_previous,
output reg [INPUT_COUNT-1:0] grant
);
localparam ZERO = {INPUT_COUNT{1'b0}};
initial begin
grant = ZERO;
end
// We need to detect if any requests are active: *when all requests go idle,
// we want to remember the last granted request.* Otherwise, that lost state
// means the round-robin restarts from the highest priority request rather
// than the next lower priority request after the last granted request.
// Always granting the highest priority first after an idle period allows
// a pathological request pattern to cause starvation: all but the highest
// priority request would starve if they keep asserting and de-asserting in
// lock-step after an idle period.
reg any_requests_active = 1'b0;
always @(*) begin
any_requests_active = (requests != ZERO);
end
// For the same reasons, we need to detect when we leave an idle request
// period so we can, for that cycle, refuse to grant again the last granted
// request, which would otherwise cause starvation of other requests happening
// in lock-step.
wire out_from_idle;
Pulse_Generator
requests_restart
(
.clock (clock),
.level_in (any_requests_active),
.pulse_posedge_out (out_from_idle),
// verilator lint_off PINCONNECTEMPTY
.pulse_negedge_out (),
.pulse_anyedge_out ()
// verilator lint_on PINCONNECTEMPTY
);
// Grant a request in priority order (LSB has higest priority) after applying
// `requests_mask`. This is the base case which starts the round-robin.
reg [INPUT_COUNT-1:0] requests_masked_priority = ZERO;
always @(*) begin
requests_masked_priority = requests & requests_mask;
end
wire [INPUT_COUNT-1:0] grant_priority;
Bitmask_Isolate_Rightmost_1_Bit
#(
.WORD_WIDTH (INPUT_COUNT)
)
priority_grants
(
.word_in (requests_masked_priority),
.word_out (grant_priority)
);
// Mask-off all requests *of equal and higher priority* than the request
// currently granted, from the previous cycle. This masking makes the
// round-robin progress towards lower priority requests as the previously
// granted higher priority requests are released. The `thermometer_mask` must
// be inverted before use.
wire [INPUT_COUNT-1:0] thermometer_mask;
Bitmask_Thermometer_to_Rightmost_1_Bit
#(
.WORD_WIDTH (INPUT_COUNT)
)
grant_mask
(
.word_in (grant_previous),
.word_out (thermometer_mask)
);
reg [INPUT_COUNT-1:0] round_robin_mask = ZERO;
always @(*) begin
round_robin_mask = ~thermometer_mask;
end
// The `round_robin_mask` excludes the currently granted request, which we
// don't want to interrupt, so we OR the currently granted request bit into
// the `round_robin_mask` so we only mask-off all *higher* priority requests,
// leaving the currently granted request as highest priority.
// **EXCEPTION:** If we are returning from an all-idle request phase, we
// instead zero-out that granted request bit, so the round-robin can resume to
// the next lower priority request based on the `round_robin_mask`, instead of
// possibly re-granting the same request again.
// The `round_robin_mask` is further masked, in the same manner, by the
// `requests_mask` input, which may prevent the granting of some `requests` of
// lower priority after the currently granted request is released.
reg [INPUT_COUNT-1:0] requests_masked_round_robin = ZERO;
reg [INPUT_COUNT-1:0] grant_previous_gated = ZERO;
always @(*) begin
grant_previous_gated = (out_from_idle == 1'b1) ? ZERO : grant_previous;
requests_masked_round_robin = requests & (round_robin_mask | grant_previous_gated) & (requests_mask | grant_previous_gated);
end
// Grant a request in priority order, but from the round-robin masked
// requests, which only contain requests of equal or lower priority to the
// currently granted request, minus any requests further masked by the
// `requests_mask` input.
wire [INPUT_COUNT-1:0] grant_round_robin;
Bitmask_Isolate_Rightmost_1_Bit
#(
.WORD_WIDTH (INPUT_COUNT)
)
round_robin_grants
(
.word_in (requests_masked_round_robin),
.word_out (grant_round_robin)
);
// If no round-robin granted requests remain, then instead grant from the
// priority requests, which starts over the round-robin at the highest
// priority (LSB) request. This also resets `round_robin_mask`.
always @(*) begin
grant = (grant_round_robin == ZERO) ? grant_priority : grant_round_robin;
end
// Remember the last granted request so we can compute `round_robin_mask` to
// exclude higher priority requests than the last granted request. If all
// requests go idle, don't update, so we can continue the round-robin from
// where we left off when requests appear again, to avoid starvation of lower
// priority requests.
Register
#(
.WORD_WIDTH (INPUT_COUNT),
.RESET_VALUE (ZERO)
)
previously_granted_request
(
.clock (clock),
.clock_enable (any_requests_active),
.clear (clear),
.data_in (grant),
.data_out (grant_previous)
);
endmodule