Skip to content

Commit 2d35892

Browse files
authored
src/id_queue.sv: Enables simultaneous input and output (#106)
Co-authored-by: Flavien Solt <[email protected]>
1 parent 29a88ad commit 2d35892

File tree

1 file changed

+206
-66
lines changed

1 file changed

+206
-66
lines changed

src/id_queue.sv

Lines changed: 206 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,17 @@
1414
// queue preserves FIFO order.
1515
//
1616
// This ID queue implementation allows to either push (through the `inp_*` signals) or pop (through
17-
// the `oup_*` signals) one element per clock cycle. The `inp_` port has priority and grants a
18-
// request iff the queue is not full. The `oup_` port dequeues an element iff `oup_pop_i` is
19-
// asserted during an `oup_` handshake; otherwise, it performs a non-destructive read. `oup_data_o`
20-
// is valid iff `oup_data_valid_o` is asserted during an `oup_` handshake. If `oup_data_valid_o` is
21-
// not asserted, the queue did not contain an element with the provided ID.
17+
// the `oup_*` signals) one element per clock cycle (depending on the _FULL_BW_ operating mode
18+
// descibed below). The `inp_` port has priority and grants a request iff the queue is not full. The
19+
// `oup_` port dequeues an element iff `oup_pop_i` is asserted during an `oup_` handshake;
20+
// otherwise, it performs a non-destructive read. `oup_data_o` is valid iff `oup_data_valid_o` is
21+
// asserted during an `oup_` handshake. If `oup_data_valid_o` is not asserted, the queue did not
22+
// contain an element with the provided ID.
23+
//
24+
// The queue can work in two bandwidth modes:
25+
// * !FULL_BW: Input and output cannot be performed simultaneously (max bandwidth: 50%).
26+
// * FULL_BW: Input and output can be performed simultaneously and a popped cell can be reused
27+
// immediately in the same clock cycle. Area increase typically 5-10%.
2228
//
2329
// This ID queue additionally provides the `exists_` port, which searches for an element anywhere in
2430
// the queue. The comparison performed during the search can be masked: for every bit that is
@@ -42,6 +48,7 @@
4248
module id_queue #(
4349
parameter int ID_WIDTH = 0,
4450
parameter int CAPACITY = 0,
51+
parameter bit FULL_BW = 0,
4552
parameter type data_t = logic,
4653
// Dependent parameters, DO NOT OVERRIDE!
4754
localparam type id_t = logic[ID_WIDTH-1:0],
@@ -102,115 +109,248 @@ module id_queue #(
102109
linked_data_t [CAPACITY-1:0] linked_data_d, linked_data_q;
103110

104111
logic full,
105-
match_id_valid,
106-
no_id_match;
112+
match_in_id_valid,
113+
match_out_id_valid,
114+
no_in_id_match,
115+
no_out_id_match;
107116

108-
logic [HtCapacity-1:0] head_tail_free,
109-
idx_matches_id;
117+
logic [HtCapacity-1:0] head_tail_free,
118+
idx_matches_in_id,
119+
idx_matches_out_id;
110120

111121
logic [CAPACITY-1:0] exists_match,
112122
linked_data_free;
113123

114-
id_t match_id;
124+
id_t match_in_id, match_out_id;
115125

116126
ht_idx_t head_tail_free_idx,
117-
match_idx;
127+
match_in_idx,
128+
match_out_idx;
129+
130+
ld_idx_t linked_data_free_idx,
131+
oup_data_free_idx;
118132

119-
ld_idx_t linked_data_free_idx;
133+
logic oup_data_popped,
134+
oup_ht_popped;
120135

121136
// Find the index in the head-tail table that matches a given ID.
122137
for (genvar i = 0; i < HtCapacity; i++) begin: gen_idx_match
123-
assign idx_matches_id[i] = match_id_valid && (head_tail_q[i].id == match_id) &&
138+
assign idx_matches_in_id[i] = match_in_id_valid && (head_tail_q[i].id == match_in_id) &&
139+
!head_tail_q[i].free;
140+
assign idx_matches_out_id[i] = match_out_id_valid && (head_tail_q[i].id == match_out_id) &&
124141
!head_tail_q[i].free;
125142
end
126-
assign no_id_match = !(|idx_matches_id);
143+
assign no_in_id_match = !(|idx_matches_in_id);
144+
assign no_out_id_match = !(|idx_matches_out_id);
145+
onehot_to_bin #(
146+
.ONEHOT_WIDTH ( HtCapacity )
147+
) i_id_ohb_in (
148+
.onehot ( idx_matches_in_id ),
149+
.bin ( match_in_idx )
150+
);
127151
onehot_to_bin #(
128-
.ONEHOT_WIDTH (HtCapacity)
129-
) i_id_ohb (
130-
.onehot (idx_matches_id),
131-
.bin (match_idx)
152+
.ONEHOT_WIDTH ( HtCapacity )
153+
) i_id_ohb_out (
154+
.onehot ( idx_matches_out_id ),
155+
.bin ( match_out_idx )
132156
);
133157

134158
// Find the first free index in the head-tail table.
135159
for (genvar i = 0; i < HtCapacity; i++) begin: gen_head_tail_free
136160
assign head_tail_free[i] = head_tail_q[i].free;
137161
end
138162
lzc #(
139-
.WIDTH (HtCapacity),
140-
.MODE (0) // Start at index 0.
163+
.WIDTH ( HtCapacity ),
164+
.MODE ( 0 ) // Start at index 0.
141165
) i_ht_free_lzc (
142-
.in_i (head_tail_free),
143-
.cnt_o (head_tail_free_idx),
144-
.empty_o ()
166+
.in_i ( head_tail_free ),
167+
.cnt_o ( head_tail_free_idx ),
168+
.empty_o ( )
145169
);
146170

147171
// Find the first free index in the linked data table.
148172
for (genvar i = 0; i < CAPACITY; i++) begin: gen_linked_data_free
149173
assign linked_data_free[i] = linked_data_q[i].free;
150174
end
151175
lzc #(
152-
.WIDTH (CAPACITY),
153-
.MODE (0) // Start at index 0.
176+
.WIDTH ( CAPACITY ),
177+
.MODE ( 0 ) // Start at index 0.
154178
) i_ld_free_lzc (
155-
.in_i (linked_data_free),
156-
.cnt_o (linked_data_free_idx),
157-
.empty_o ()
179+
.in_i ( linked_data_free ),
180+
.cnt_o ( linked_data_free_idx ),
181+
.empty_o ( )
158182
);
159183

160184
// The queue is full if and only if there are no free items in the linked data structure.
161185
assign full = !(|linked_data_free);
186+
// Data potentially freed by the output.
187+
assign oup_data_free_idx = head_tail_q[match_out_idx].head;
162188

163-
assign inp_gnt_o = ~full;
189+
// Data can be accepted if the linked list pool is not full, or some data is simultaneously.
190+
assign inp_gnt_o = ~full || (oup_data_popped && FULL_BW);
164191
always_comb begin
165-
match_id = '0;
166-
match_id_valid = 1'b0;
192+
match_in_id = '0;
193+
match_out_id = '0;
194+
match_in_id_valid = 1'b0;
195+
match_out_id_valid = 1'b0;
167196
head_tail_d = head_tail_q;
168197
linked_data_d = linked_data_q;
169198
oup_gnt_o = 1'b0;
170199
oup_data_o = data_t'('0);
171200
oup_data_valid_o = 1'b0;
172-
if (inp_req_i && !full) begin
173-
match_id = inp_id_i;
174-
match_id_valid = 1'b1;
175-
// If the ID does not yet exist in the queue, add a new ID entry.
176-
if (no_id_match) begin
177-
head_tail_d[head_tail_free_idx] = '{
178-
id: inp_id_i,
179-
head: linked_data_free_idx,
180-
tail: linked_data_free_idx,
201+
oup_data_popped = 1'b0;
202+
oup_ht_popped = 1'b0;
203+
204+
if (!FULL_BW) begin
205+
if (inp_req_i && !full) begin
206+
match_in_id = inp_id_i;
207+
match_in_id_valid = 1'b1;
208+
// If the ID does not yet exist in the queue, add a new ID entry.
209+
if (no_in_id_match) begin
210+
head_tail_d[head_tail_free_idx] = '{
211+
id: inp_id_i,
212+
head: linked_data_free_idx,
213+
tail: linked_data_free_idx,
214+
free: 1'b0
215+
};
216+
// Otherwise append it to the existing ID subqueue.
217+
end else begin
218+
linked_data_d[head_tail_q[match_in_idx].tail].next = linked_data_free_idx;
219+
head_tail_d[match_in_idx].tail = linked_data_free_idx;
220+
end
221+
linked_data_d[linked_data_free_idx] = '{
222+
data: inp_data_i,
223+
next: '0,
181224
free: 1'b0
182225
};
183-
// Otherwise append it to the existing ID subqueue.
184-
end else begin
185-
linked_data_d[head_tail_q[match_idx].tail].next = linked_data_free_idx;
186-
head_tail_d[match_idx].tail = linked_data_free_idx;
226+
end else if (oup_req_i) begin
227+
match_in_id = oup_id_i;
228+
match_in_id_valid = 1'b1;
229+
if (!no_in_id_match) begin
230+
oup_data_o = data_t'(linked_data_q[head_tail_q[match_in_idx].head].data);
231+
oup_data_valid_o = 1'b1;
232+
if (oup_pop_i) begin
233+
// Set free bit of linked data entry, all other bits are don't care.
234+
linked_data_d[head_tail_q[match_in_idx].head] = '0;
235+
linked_data_d[head_tail_q[match_in_idx].head][0] = 1'b1;
236+
if (head_tail_q[match_in_idx].head == head_tail_q[match_in_idx].tail) begin
237+
head_tail_d[match_in_idx] = '{free: 1'b1, default: '0};
238+
end else begin
239+
head_tail_d[match_in_idx].head =
240+
linked_data_q[head_tail_q[match_in_idx].head].next;
241+
end
242+
end
243+
end
244+
// Always grant the output request. If there was no match, the default, invalid entry
245+
// will be returned.
246+
oup_gnt_o = 1'b1;
247+
end
248+
end else begin
249+
// FULL_BW
250+
if (oup_req_i) begin
251+
match_out_id = oup_id_i;
252+
match_out_id_valid = 1'b1;
253+
if (!no_out_id_match) begin
254+
oup_data_o = data_t'(linked_data_q[head_tail_q[match_out_idx].head].data);
255+
oup_data_valid_o = 1'b1;
256+
if (oup_pop_i) begin
257+
oup_data_popped = 1'b1;
258+
// Set free bit of linked data entry, all other bits are don't care.
259+
linked_data_d[head_tail_q[match_out_idx].head] = '0;
260+
linked_data_d[head_tail_q[match_out_idx].head][0] = 1'b1;
261+
if (head_tail_q[match_out_idx].head == head_tail_q[match_out_idx].tail) begin
262+
oup_ht_popped = 1'b1;
263+
head_tail_d[match_out_idx] = '{free: 1'b1, default: '0};
264+
end else begin
265+
head_tail_d[match_out_idx].head =
266+
linked_data_q[head_tail_q[match_out_idx].head].next;
267+
end
268+
end
269+
end
270+
// Always grant the output request. If there was no match, the default, invalid entry
271+
// will be returned.
272+
oup_gnt_o = 1'b1;
187273
end
188-
linked_data_d[linked_data_free_idx] = '{
189-
data: inp_data_i,
190-
next: '0,
191-
free: 1'b0
192-
};
193-
end else if (oup_req_i) begin
194-
match_id = oup_id_i;
195-
match_id_valid = 1'b1;
196-
if (!no_id_match) begin
197-
oup_data_o = data_t'(linked_data_q[head_tail_q[match_idx].head].data);
198-
oup_data_valid_o = 1'b1;
199-
if (oup_pop_i) begin
200-
// Set free bit of linked data entry, all other bits are don't care.
201-
linked_data_d[head_tail_q[match_idx].head] = '0;
202-
linked_data_d[head_tail_q[match_idx].head][0] = 1'b1;
203-
if (head_tail_q[match_idx].head == head_tail_q[match_idx].tail) begin
204-
head_tail_d[match_idx] = '{free: 1'b1, default: '0};
274+
if (inp_req_i && inp_gnt_o) begin
275+
match_in_id = inp_id_i;
276+
match_in_id_valid = 1'b1;
277+
// If the ID does not yet exist in the queue or was just popped, add a new ID entry.
278+
if (oup_ht_popped && (oup_id_i==inp_id_i)) begin
279+
// If output data was popped for this ID, which lead the head_tail to be popped,
280+
// then repopulate this head_tail immediately.
281+
head_tail_d[match_out_idx] = '{
282+
id: inp_id_i,
283+
head: oup_data_free_idx,
284+
tail: oup_data_free_idx,
285+
free: 1'b0
286+
};
287+
linked_data_d[oup_data_free_idx] = '{
288+
data: inp_data_i,
289+
next: '0,
290+
free: 1'b0
291+
};
292+
end else if (no_in_id_match) begin
293+
// Else, if no head_tail corresponds to the input id.
294+
if (oup_ht_popped) begin
295+
head_tail_d[match_out_idx] = '{
296+
id: inp_id_i,
297+
head: oup_data_free_idx,
298+
tail: oup_data_free_idx,
299+
free: 1'b0
300+
};
301+
linked_data_d[oup_data_free_idx] = '{
302+
data: inp_data_i,
303+
next: '0,
304+
free: 1'b0
305+
};
306+
end else begin
307+
if (oup_data_popped) begin
308+
head_tail_d[head_tail_free_idx] = '{
309+
id: inp_id_i,
310+
head: oup_data_free_idx,
311+
tail: oup_data_free_idx,
312+
free: 1'b0
313+
};
314+
linked_data_d[oup_data_free_idx] = '{
315+
data: inp_data_i,
316+
next: '0,
317+
free: 1'b0
318+
};
319+
end else begin
320+
head_tail_d[head_tail_free_idx] = '{
321+
id: inp_id_i,
322+
head: linked_data_free_idx,
323+
tail: linked_data_free_idx,
324+
free: 1'b0
325+
};
326+
linked_data_d[linked_data_free_idx] = '{
327+
data: inp_data_i,
328+
next: '0,
329+
free: 1'b0
330+
};
331+
end
332+
end
333+
end else begin
334+
// Otherwise append it to the existing ID subqueue.
335+
if (oup_data_popped) begin
336+
linked_data_d[head_tail_q[match_in_idx].tail].next = oup_data_free_idx;
337+
head_tail_d[match_in_idx].tail = oup_data_free_idx;
338+
linked_data_d[oup_data_free_idx] = '{
339+
data: inp_data_i,
340+
next: '0,
341+
free: 1'b0
342+
};
205343
end else begin
206-
head_tail_d[match_idx].head =
207-
linked_data_q[head_tail_q[match_idx].head].next;
344+
linked_data_d[head_tail_q[match_in_idx].tail].next = linked_data_free_idx;
345+
head_tail_d[match_in_idx].tail = linked_data_free_idx;
346+
linked_data_d[linked_data_free_idx] = '{
347+
data: inp_data_i,
348+
next: '0,
349+
free: 1'b0
350+
};
208351
end
209352
end
210353
end
211-
// Always grant the output request. If there was no match, the default, invalid entry
212-
// will be returned.
213-
oup_gnt_o = 1'b1;
214354
end
215355
end
216356

0 commit comments

Comments
 (0)