|
14 | 14 | // queue preserves FIFO order. |
15 | 15 | // |
16 | 16 | // 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%. |
22 | 28 | // |
23 | 29 | // This ID queue additionally provides the `exists_` port, which searches for an element anywhere in |
24 | 30 | // the queue. The comparison performed during the search can be masked: for every bit that is |
|
42 | 48 | module id_queue #( |
43 | 49 | parameter int ID_WIDTH = 0, |
44 | 50 | parameter int CAPACITY = 0, |
| 51 | + parameter bit FULL_BW = 0, |
45 | 52 | parameter type data_t = logic, |
46 | 53 | // Dependent parameters, DO NOT OVERRIDE! |
47 | 54 | localparam type id_t = logic[ID_WIDTH-1:0], |
@@ -102,115 +109,248 @@ module id_queue #( |
102 | 109 | linked_data_t [CAPACITY-1:0] linked_data_d, linked_data_q; |
103 | 110 |
|
104 | 111 | 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; |
107 | 116 |
|
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; |
110 | 120 |
|
111 | 121 | logic [CAPACITY-1:0] exists_match, |
112 | 122 | linked_data_free; |
113 | 123 |
|
114 | | - id_t match_id; |
| 124 | + id_t match_in_id, match_out_id; |
115 | 125 |
|
116 | 126 | 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; |
118 | 132 |
|
119 | | - ld_idx_t linked_data_free_idx; |
| 133 | + logic oup_data_popped, |
| 134 | + oup_ht_popped; |
120 | 135 |
|
121 | 136 | // Find the index in the head-tail table that matches a given ID. |
122 | 137 | 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) && |
124 | 141 | !head_tail_q[i].free; |
125 | 142 | 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 | + ); |
127 | 151 | 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 ) |
132 | 156 | ); |
133 | 157 |
|
134 | 158 | // Find the first free index in the head-tail table. |
135 | 159 | for (genvar i = 0; i < HtCapacity; i++) begin: gen_head_tail_free |
136 | 160 | assign head_tail_free[i] = head_tail_q[i].free; |
137 | 161 | end |
138 | 162 | lzc #( |
139 | | - .WIDTH (HtCapacity), |
140 | | - .MODE (0) // Start at index 0. |
| 163 | + .WIDTH ( HtCapacity ), |
| 164 | + .MODE ( 0 ) // Start at index 0. |
141 | 165 | ) 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 ( ) |
145 | 169 | ); |
146 | 170 |
|
147 | 171 | // Find the first free index in the linked data table. |
148 | 172 | for (genvar i = 0; i < CAPACITY; i++) begin: gen_linked_data_free |
149 | 173 | assign linked_data_free[i] = linked_data_q[i].free; |
150 | 174 | end |
151 | 175 | lzc #( |
152 | | - .WIDTH (CAPACITY), |
153 | | - .MODE (0) // Start at index 0. |
| 176 | + .WIDTH ( CAPACITY ), |
| 177 | + .MODE ( 0 ) // Start at index 0. |
154 | 178 | ) 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 ( ) |
158 | 182 | ); |
159 | 183 |
|
160 | 184 | // The queue is full if and only if there are no free items in the linked data structure. |
161 | 185 | 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; |
162 | 188 |
|
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); |
164 | 191 | 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; |
167 | 196 | head_tail_d = head_tail_q; |
168 | 197 | linked_data_d = linked_data_q; |
169 | 198 | oup_gnt_o = 1'b0; |
170 | 199 | oup_data_o = data_t'('0); |
171 | 200 | 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, |
181 | 224 | free: 1'b0 |
182 | 225 | }; |
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; |
187 | 273 | 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 | + }; |
205 | 343 | 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 | + }; |
208 | 351 | end |
209 | 352 | end |
210 | 353 | 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; |
214 | 354 | end |
215 | 355 | end |
216 | 356 |
|
|
0 commit comments