5ddc7255aece4565a90a7b85613a5ceaa9d3fe64
[soc.git] / src / unused / TLB / ariane / miss_handler.py
1 # Copyright 2018 ETH Zurich and University of Bologna.
2 # Copyright and related rights are licensed under the Solderpad Hardware
3 # License, Version 0.51 (the "License"); you may not use this file except in
4 # compliance with the License. You may obtain a copy of the License at
5 # http:#solderpad.org/licenses/SHL-0.51. Unless required by applicable law
6 # or agreed to in writing, software, hardware and materials distributed under
7 # this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
8 # CONDITIONS OF ANY KIND, either express or implied. See the License for the
9 # specific language governing permissions and limitations under the License.
10 #
11 # Author: Florian Zaruba, ETH Zurich
12 # Date: 12.11.2017
13 # Description: Handles cache misses.
14 from nmigen.lib.coding import Encoder, PriorityEncoder
15
16
17 # --------------
18 # MISS Handler
19 # --------------
20 import ariane_pkg::*;
21 import std_cache_pkg::*;
22
23 unsigned NR_PORTS = 3
24
25 class MissReq(RecordObject):
26 def __init__(self, name=None):
27 Record.__init__(self, name)
28 self.valid = Signal()
29 self.addr = Signal(64)
30 self.be = Signal(8)
31 self.size = Signal(2)
32 self.we = Signal()
33 self.wdata = Signal(64)
34 bypass = Signal()
35
36 class CacheLine:
37 def __init__(self):
38 self.tag = Signal(DCACHE_TAG_WIDTH) # tag array
39 self.data = Signal(DCACHE_LINE_WIDTH) # data array
40 self.valid = Signal() # state array
41 self.dirty = Signal() # state array
42
43 # cache line byte enable
44 class CLBE:
45 def __init__(self):
46 self.tag = Signal(DCACHE_TAG_WIDTH+7)//8) # byte enable into tag array
47 self.data = Signal(DCACHE_LINE_WIDTH+7)//8) # byte enable data array
48 # bit enable into state array (valid for a pair of dirty/valid bits)
49 self.vldrty = Signal(DCACHE_SET_ASSOC)
50 } cl_be_t;
51
52
53
54 # FSM states
55 """
56 enum logic [3:0] {
57 IDLE, # 0
58 FLUSHING, # 1
59 FLUSH, # 2
60 WB_CACHELINE_FLUSH, # 3
61 FLUSH_REQ_STATUS, # 4
62 WB_CACHELINE_MISS, # 5
63 WAIT_GNT_SRAM, # 6
64 MISS, # 7
65 REQ_CACHELINE, # 8
66 MISS_REPL, # 9
67 SAVE_CACHELINE, # A
68 INIT, # B
69 AMO_LOAD, # C
70 AMO_SAVE_LOAD, # D
71 AMO_STORE # E
72 } state_d, state_q;
73 """
74
75 class MissHandler(Elaboratable):
76 def __init__(self, NR_PORTS):
77 self.NR_PORTS = NR_PORTS
78 self.pwid = pwid = ceil(log(NR_PORTS) / log(2))
79 self.flush_i = Signal() # flush request
80 self.flush_ack_o = Signal() # acknowledge successful flush
81 self.miss_o = Signal()
82 self.busy_i = Signal() # dcache is busy with something
83
84 # Bypass or miss
85 self.miss_req_i = Array(MissReq(name="missreq") for i in range(NR_PORTS))
86 # Bypass handling
87 self.bypass_gnt_o = Signal(NR_PORTS)
88 self.bypass_valid_o = Signal(NR_PORTS)
89 self.bypass_data_o = Array(Signal(name="bdata_o", 64) \
90 for i in range(NR_PORTS))
91
92 # AXI port
93 output ariane_axi::req_t axi_bypass_o,
94 input ariane_axi::resp_t axi_bypass_i,
95
96 # Miss handling (~> cacheline refill)
97 self.miss_gnt_o = Signal(NR_PORTS)
98 self.active_serving_o = Signal(NR_PORTS)
99
100 self.critical_word_o = Signal(64)
101 self.critical_word_valid_o = Signal()
102 output ariane_axi::req_t axi_data_o,
103 input ariane_axi::resp_t axi_data_i,
104
105 self.mshr_addr_i = Array(Signal(name="bdata_o", 56) \
106 for i in range(NR_PORTS))
107 self.mshr_addr_matches_o = Signal(NR_PORTS)
108 self.mshr_index_matches_o = Signal(NR_PORTS)
109
110 # AMO
111 self.amo_req_i = AMOReq()
112 self.amo_resp_o = AMOResp()
113 # Port to SRAMs, for refill and eviction
114 self.req_o = Signal(DCACHE_SET_ASSOC)
115 self.addr_o = Signal(DCACHE_INDEX_WIDTH) # address into cache array
116 self.data_o = CacheLine()
117 self.be_o = CLBE()
118 self.data_i = Array(CacheLine() \
119 for i in range(DCACHE_SET_ASSOC))
120 self.we_o = Signal()
121
122 def elaborate(self, platform):
123 # Registers
124 mshr_t mshr_d, mshr_q;
125 logic [DCACHE_INDEX_WIDTH-1:0] cnt_d, cnt_q;
126 logic [DCACHE_SET_ASSOC-1:0] evict_way_d, evict_way_q;
127 # cache line to evict
128 cache_line_t evict_cl_d, evict_cl_q;
129
130 logic serve_amo_d, serve_amo_q;
131 # Request from one FSM
132 miss_req_valid = Signal(self.NR_PORTS)
133 miss_req_bypass = Signal(self.NR_PORTS)
134 miss_req_addr = Array(Signal(name="miss_req_addr", 64) \
135 for i in range(NR_PORTS))
136 miss_req_wdata = Array(Signal(name="miss_req_wdata", 64) \
137 for i in range(NR_PORTS))
138 miss_req_we = Signal(self.NR_PORTS)
139 miss_req_be = Array(Signal(name="miss_req_be", 8) \
140 for i in range(NR_PORTS))
141 miss_req_size = Array(Signal(name="miss_req_size", 2) \
142 for i in range(NR_PORTS))
143
144 # Cache Line Refill <-> AXI
145 req_fsm_miss_valid = Signal()
146 req_fsm_miss_addr = Signal(64)
147 req_fsm_miss_wdata = Signal(DCACHE_LINE_WIDTH)
148 req_fsm_miss_we = Signal()
149 req_fsm_miss_be = Signal(DCACHE_LINE_WIDTH//8)
150 ariane_axi::ad_req_t req_fsm_miss_req;
151 req_fsm_miss_size = Signal(2)
152
153 gnt_miss_fsm = Signal()
154 valid_miss_fsm = Signal()
155 nmiss = DCACHE_LINE_WIDTH//64
156 data_miss_fsm = Array(Signal(name="data_miss_fsm", 64) \
157 for i in range(nmiss))
158
159 # Cache Management <-> LFSR
160 lfsr_enable = Signal()
161 lfsr_oh = Signal(DCACHE_SET_ASSOC)
162 lfsr_bin = Signal($clog2(DCACHE_SET_ASSOC-1))
163 # AMOs
164 ariane_pkg::amo_t amo_op;
165 amo_operand_a = Signal(64)
166 amo_operand_b = Signal(64)
167 amo_result_o = Signal(64)
168
169 struct packed {
170 logic [63:3] address;
171 logic valid;
172 } reservation_d, reservation_q;
173
174 # ------------------------------
175 # Cache Management
176 # ------------------------------
177 evict_way = Signal(DCACHE_SET_ASSOC)
178 valid_way = Signal(DCACHE_SET_ASSOC)
179
180 for (i in range(DCACHE_SET_ASSOC):
181 comb += evict_way[i].eq(data_i[i].valid & data_i[i].dirty)
182 comb += valid_way[i].eq(data_i[i].valid)
183
184 # ----------------------
185 # Default Assignments
186 # ----------------------
187 # to AXI refill
188 req_fsm_miss_req = ariane_axi::CACHE_LINE_REQ;
189 req_fsm_miss_size = Const(0b11, 2)
190 # core
191 serve_amo_d = serve_amo_q;
192 # --------------------------------
193 # Flush and Miss operation
194 # --------------------------------
195 state_d = state_q;
196 cnt_d = cnt_q;
197 evict_way_d = evict_way_q;
198 evict_cl_d = evict_cl_q;
199 mshr_d = mshr_q;
200 # communicate to the requester which unit we are currently serving
201 active_serving_o[mshr_q.id] = mshr_q.valid;
202 # AMOs
203 # silence the unit when not used
204 amo_op = amo_req_i.amo_op;
205
206 reservation_d = reservation_q;
207 with m.FSM() as state_q:
208
209 with m.Case("IDLE"):
210 # lowest priority are AMOs, wait until everything else
211 # is served before going for the AMOs
212 with m.If (amo_req_i.req & ~busy_i):
213 # 1. Flush the cache
214 with m.If(~serve_amo_q):
215 m.next = "FLUSH_REQ_STATUS"
216 serve_amo_d.eq(0b1
217 cnt_d.eq(0
218 # 2. Do the AMO
219 with m.Else():
220 m.next = "AMO_LOAD"
221 serve_amo_d.eq(0b0
222
223 # check if we want to flush and can flush
224 # e.g.: we are not busy anymore
225 # TODO: Check that the busy flag is indeed needed
226 with m.If (flush_i & ~busy_i):
227 m.next = "FLUSH_REQ_STATUS"
228 cnt_d = 0
229
230 # check if one of the state machines missed
231 for i in range(NR_PORTS):
232 # here comes the refill portion of code
233 with m.If (miss_req_valid[i] & ~miss_req_bypass[i]):
234 m.next = "MISS"
235 # we are taking another request so don't
236 # take the AMO
237 serve_amo_d = 0b0;
238 # save to MSHR
239 wid = DCACHE_TAG_WIDTH+DCACHE_INDEX_WIDTH
240 comb += [ mshr_d.valid.eq(0b1),
241 mshr_d.we.eq(miss_req_we[i]),
242 mshr_d.id.eq(i),
243 mshr_d.addr.eq(miss_req_addr[i][0:wid]),
244 mshr_d.wdata.eq(miss_req_wdata[i]),
245 mshr_d.be.eq(miss_req_be[i]),
246 ]
247 break
248
249 # ~> we missed on the cache
250 with m.Case("MISS"):
251 # 1. Check if there is an empty cache-line
252 # 2. If not -> evict one
253 comb += req_o.eq(1)
254 sync += addr_o.eq(mshr_q.addr[:DCACHE_INDEX_WIDTH]
255 m.next = "MISS_REPL"
256 comb += miss_o.eq(1)
257
258 # ~> second miss cycle
259 with m.Case("MISS_REPL"):
260 # if all are valid we need to evict one,
261 # pseudo random from LFSR
262 with m.If(~(~valid_way).bool()):
263 comb += lfsr_enable.eq(0b1)
264 comb += evict_way_d.eq(lfsr_oh)
265 # do we need to write back the cache line?
266 with m.If(data_i[lfsr_bin].dirty):
267 state_d = WB_CACHELINE_MISS;
268 comb += evict_cl_d.tag.eq(data_i[lfsr_bin].tag)
269 comb += evict_cl_d.data.eq(data_i[lfsr_bin].data)
270 comb += cnt_d.eq(mshr_q.addr[:DCACHE_INDEX_WIDTH])
271 # no - we can request a cache line now
272 with m.Else():
273 m.next = "REQ_CACHELINE"
274 # we have at least one free way
275 with m.Else():
276 # get victim cache-line by looking for the
277 # first non-valid bit
278 comb += evict_way_d.eq(get_victim_cl(~valid_way)
279 m.next = "REQ_CACHELINE"
280
281 # ~> we can just load the cache-line,
282 # the way is store in evict_way_q
283 with m.Case("REQ_CACHELINE"):
284 comb += req_fsm_miss_valid .eq(1)
285 sync += req_fsm_miss_addr .eq(mshr_q.addr)
286
287 with m.If (gnt_miss_fsm):
288 m.next = "SAVE_CACHELINE"
289 comb += miss_gnt_o[mshr_q.id].eq(1)
290
291 # ~> replace the cacheline
292 with m.Case("SAVE_CACHELINE"):
293 # calculate cacheline offset
294 automatic logic [$clog2(DCACHE_LINE_WIDTH)-1:0] cl_offset;
295 sync += cl_offset.eq(mshr_q.addr[3:DCACHE_BYTE_OFFSET] << 6)
296 # we've got a valid response from refill unit
297 with m.If (valid_miss_fsm):
298 wid = DCACHE_TAG_WIDTH+DCACHE_INDEX_WIDTH
299 sync += addr_o .eq(mshr_q.addr[:DCACHE_INDEX_WIDTH])
300 sync += req_o .eq(evict_way_q)
301 comb += we_o .eq(1)
302 comb += be_o .eq(1)
303 sync += be_o.vldrty .eq(evict_way_q)
304 sync += data_o.tag .eq(mshr_q.addr[DCACHE_INDEX_WIDTH:wid]
305 comb += data_o.data .eq(data_miss_fsm)
306 comb += data_o.valid.eq(1)
307 comb += data_o.dirty.eq(0)
308
309 # is this a write?
310 with m.If (mshr_q.we):
311 # Yes, so safe the updated data now
312 for i in range(8):
313 # check if we really want to write
314 # the corresponding byte
315 with m.If (mshr_q.be[i]):
316 sync += data_o.data[(cl_offset + i*8) +: 8].eq(mshr_q.wdata[i];
317 # it's immediately dirty if we write
318 comb += data_o.dirty.eq(1)
319
320 # reset MSHR
321 comb += mshr_d.valid.eq(0)
322 # go back to idle
323 m.next = 'IDLE'
324
325 # ------------------------------
326 # Write Back Operation
327 # ------------------------------
328 # ~> evict a cache line from way saved in evict_way_q
329 with m.Case("WB_CACHELINE_FLUSH"):
330 with m.Case("WB_CACHELINE_MISS"):
331
332 comb += req_fsm_miss_valid .eq(0b1)
333 sync += req_fsm_miss_addr .eq({evict_cl_q.tag, cnt_q[DCACHE_INDEX_WIDTH-1:DCACHE_BYTE_OFFSET], {{DCACHE_BYTE_OFFSET}{0b0}}};
334 comb += req_fsm_miss_be .eq(1)
335 comb += req_fsm_miss_we .eq(0b1)
336 sync += req_fsm_miss_wdata .eq(evict_cl_q.data;
337
338 # we've got a grant --> this is timing critical, think about it
339 if (gnt_miss_fsm) begin
340 # write status array
341 sync += addr_o .eq(cnt_q)
342 comb += req_o .eq(0b1)
343 comb += we_o .eq(0b1)
344 comb += data_o.valid.eq(INVALIDATE_ON_FLUSH ? 0b0 : 0b1)
345 # invalidate
346 sync += be_o.vldrty.eq(evict_way_q)
347 # go back to handling the miss or flushing,
348 # depending on where we came from
349 with m.If(state_q == WB_CACHELINE_MISS):
350 m.next = "MISS"
351 with m.Else():
352 m.next = "FLUSH_REQ_STATUS"
353
354 # ------------------------------
355 # Flushing & Initialization
356 # ------------------------------
357 # ~> make another request to check the same
358 # cache-line if there are still some valid entries
359 with m.Case("FLUSH_REQ_STATUS"):
360 comb += req_o .eq(1)
361 sync += addr_o .eq(cnt_q)
362 m.next = "FLUSHING"
363
364 with m.Case("FLUSHING"):
365 # this has priority
366 # at least one of the cache lines is dirty
367 with m.If(~evict_way):
368 # evict cache line, look for the first
369 # cache-line which is dirty
370 comb += evict_way_d.eq(get_victim_cl(evict_way))
371 comb += evict_cl_d .eq(data_i[one_hot_to_bin(evict_way)])
372 state_d = WB_CACHELINE_FLUSH;
373 # not dirty ~> increment and continue
374 with m.Else():
375 # increment and re-request
376 sync += cnt_d.eq(cnt_q + (1 << DCACHE_BYTE_OFFSET))
377 m.next = "FLUSH_REQ_STATUS"
378 sync += addr_o .eq(cnt_q)
379 comb += req_o .eq(1)
380 comb += be_o.vldrty.eq(INVALIDATE_ON_FLUSH ? 1 : 0)
381 comb += we_o .eq(1)
382 # finished with flushing operation, go back to idle
383 with m.If (cnt_q[DCACHE_BYTE_OFFSET:DCACHE_INDEX_WIDTH] \
384 == DCACHE_NUM_WORDS-1):
385 # only acknowledge if the flush wasn't
386 # triggered by an atomic
387 sync += flush_ack_o.eq(~serve_amo_q)
388 m.next = "IDLE"
389
390 # ~> only called after reset
391 with m.Case("INIT"):
392 # initialize status array
393 sync += addr_o.eq(cnt_q)
394 comb += req_o .eq(1)
395 comb += we_o .eq(1)
396 # only write the dirty array
397 comb += be_o.vldrty.eq(1)
398 sync += cnt_d .eq(cnt_q + (1 << DCACHE_BYTE_OFFSET))
399 # finished initialization
400 with m.If (cnt_q[DCACHE_BYTE_OFFSET:DCACHE_INDEX_WIDTH] \
401 == DCACHE_NUM_WORDS-1)
402 m.next = "IDLE"
403
404 # ----------------------
405 # AMOs
406 # ----------------------
407 # TODO(zarubaf) Move this closer to memory
408 # ~> we are here because we need to do the AMO,
409 # the cache is clean at this point
410 # start by executing the load
411 with m.Case("AMO_LOAD"):
412 comb += req_fsm_miss_valid.eq(1)
413 # address is in operand a
414 comb += req_fsm_miss_addr.eq(amo_req_i.operand_a)
415 comb += req_fsm_miss_req.eq(ariane_axi::SINGLE_REQ)
416 comb += req_fsm_miss_size.eq(amo_req_i.size)
417 # the request has been granted
418 with m.If(gnt_miss_fsm):
419 m.next = "AMO_SAVE_LOAD"
420 # save the load value
421 with m.Case("AMO_SAVE_LOAD"):
422 with m.If (valid_miss_fsm):
423 # we are only concerned about the lower 64-bit
424 comb += mshr_d.wdata.eq(data_miss_fsm[0])
425 m.next = "AMO_STORE"
426 # and do the store
427 with m.Case("AMO_STORE"):
428 load_data = Signal(64)
429 # re-align load data
430 comb += load_data.eq(data_align(amo_req_i.operand_a[:3],
431 mshr_q.wdata))
432 # Sign-extend for word operation
433 with m.If (amo_req_i.size == 0b10):
434 comb += amo_operand_a.eq(sext32(load_data[:32]))
435 comb += amo_operand_b.eq(sext32(amo_req_i.operand_b[:32]))
436 with m.Else():
437 comb += amo_operand_a.eq(load_data)
438 comb += amo_operand_b.eq(amo_req_i.operand_b)
439
440 # we do not need a store request for load reserved
441 # or a failing store conditional
442 # we can bail-out without making any further requests
443 with m.If ((amo_req_i.amo_op == AMO_LR) | \
444 ((amo_req_i.amo_op == AMO_SC) & \
445 ((reservation_q.valid & \
446 (reservation_q.address != \
447 amo_req_i.operand_a[3:64])) | \
448 ~reservation_q.valid))):
449 comb += req_fsm_miss_valid.eq(0)
450 m.next = "IDLE"
451 comb += amo_resp_o.ack.eq(1)
452 # write-back the result
453 comb += amo_resp_o.result.eq(amo_operand_a)
454 # we know that the SC failed
455 with m.If (amo_req_i.amo_op == AMO_SC):
456 comb += amo_resp_o.result.eq(1)
457 # also clear the reservation
458 comb += reservation_d.valid.eq(0)
459 with m.Else():
460 comb += req_fsm_miss_valid.eq(1)
461
462 comb += req_fsm_miss_we .eq(1)
463 comb += req_fsm_miss_req .eq(ariane_axi::SINGLE_REQ)
464 comb += req_fsm_miss_size.eq(amo_req_i.size)
465 comb += req_fsm_miss_addr.eq(amo_req_i.operand_a)
466
467 comb += req_fsm_miss_wdata.eq(
468 data_align(amo_req_i.operand_a[0:3], amo_result_o))
469 comb += req_fsm_miss_be.eq(
470 be_gen(amo_req_i.operand_a[0:3], amo_req_i.size))
471
472 # place a reservation on the memory
473 with m.If (amo_req_i.amo_op == AMO_LR):
474 comb += reservation_d.address.eq(amo_req_i.operand_a[3:64])
475 comb += reservation_d.valid.eq(1)
476
477 # the request is valid or we didn't need to go for another store
478 with m.If (valid_miss_fsm):
479 m.next = "IDLE"
480 comb += amo_resp_o.ack.eq(1)
481 # write-back the result
482 comb += amo_resp_o.result.eq(amo_operand_a;
483
484 if (amo_req_i.amo_op == AMO_SC) begin
485 comb += amo_resp_o.result.eq(0)
486 # An SC must fail if there is another SC
487 # (to any address) between the LR and the SC in
488 # program order (even to the same address).
489 # in any case destroy the reservation
490 comb += reservation_d.valid.eq(0)
491
492 # check MSHR for aliasing
493
494 comb += mshr_addr_matches_o .eq(0)
495 comb += mshr_index_matches_o.eq()
496
497 for i in range(NR_PORTS):
498 # check mshr for potential matching of other units,
499 # exclude the unit currently being served
500 with m.If (mshr_q.valid & \
501 (mshr_addr_i[i][DCACHE_BYTE_OFFSET:56] == \
502 mshr_q.addr[DCACHE_BYTE_OFFSET:56])):
503 comb += mshr_addr_matches_o[i].eq(1)
504
505 # same as previous, but checking only the index
506 with m.If (mshr_q.valid & \
507 (mshr_addr_i[i][DCACHE_BYTE_OFFSET:DCACHE_INDEX_WIDTH] == \
508 mshr_q.addr[DCACHE_BYTE_OFFSET:DCACHE_INDEX_WIDTH])):
509 mshr_index_matches_o[i].eq(1)
510
511 # --------------------
512 # Sequential Process
513 # --------------------
514
515 """
516 #pragma translate_off
517 `ifndef VERILATOR
518 # assert that cache only hits on one way
519 assert property (
520 @(posedge clk_i) $onehot0(evict_way_q)) else $warning("Evict-way should be one-hot encoded");
521 `endif
522 #pragma translate_on
523 """
524
525 # ----------------------
526 # Bypass Arbiter
527 # ----------------------
528 # Connection Arbiter <-> AXI
529 req_fsm_bypass_valid = Signal()
530 req_fsm_bypass_addr = Signal(64)
531 req_fsm_bypass_wdata = Signal(64)
532 req_fsm_bypass_we = Signal()
533 req_fsm_bypass_be = Signal(8)
534 req_fsm_bypass_size = Signal(2)
535 gnt_bypass_fsm = Signal()
536 valid_bypass_fsm = Signal()
537 data_bypass_fsm = Signal(64)
538 logic [$clog2(NR_PORTS)-1:0] id_fsm_bypass;
539 logic [3:0] id_bypass_fsm;
540 logic [3:0] gnt_id_bypass_fsm;
541
542 i_bypass_arbiter = ib = AXIArbiter( NR_PORTS, 64)
543 comb += [
544 # Master Side
545 ib.data_req_i .eq( miss_req_valid & miss_req_bypass ),
546 ib.address_i .eq( miss_req_addr ),
547 ib.data_wdata_i .eq( miss_req_wdata ),
548 ib.data_we_i .eq( miss_req_we ),
549 ib.data_be_i .eq( miss_req_be ),
550 ib.data_size_i .eq( miss_req_size ),
551 ib.data_gnt_o .eq( bypass_gnt_o ),
552 ib.data_rvalid_o .eq( bypass_valid_o ),
553 ib.data_rdata_o .eq( bypass_data_o ),
554 # Slave Sid
555 ib.id_i .eq( id_bypass_fsm[$clog2(NR_PORTS)-1:0] ),
556 ib.id_o .eq( id_fsm_bypass ),
557 ib.gnt_id_i .eq( gnt_id_bypass_fsm[$clog2(NR_PORTS)-1:0] ),
558 ib.address_o .eq( req_fsm_bypass_addr ),
559 ib.data_wdata_o .eq( req_fsm_bypass_wdata ),
560 ib.data_req_o .eq( req_fsm_bypass_valid ),
561 ib.data_we_o .eq( req_fsm_bypass_we ),
562 ib.data_be_o .eq( req_fsm_bypass_be ),
563 ib.data_size_o .eq( req_fsm_bypass_size ),
564 ib.data_gnt_i .eq( gnt_bypass_fsm ),
565 ib.data_rvalid_i .eq( valid_bypass_fsm ),
566 ib.data_rdata_i .eq( data_bypass_fsm ),
567 ]
568
569 axi_adapter #(
570 .DATA_WIDTH ( 64 ),
571 .AXI_ID_WIDTH ( 4 ),
572 .CACHELINE_BYTE_OFFSET ( DCACHE_BYTE_OFFSET )
573 ) i_bypass_axi_adapter (
574 .clk_i,
575 .rst_ni,
576 .req_i ( req_fsm_bypass_valid ),
577 .type_i ( ariane_axi::SINGLE_REQ ),
578 .gnt_o ( gnt_bypass_fsm ),
579 .addr_i ( req_fsm_bypass_addr ),
580 .we_i ( req_fsm_bypass_we ),
581 .wdata_i ( req_fsm_bypass_wdata ),
582 .be_i ( req_fsm_bypass_be ),
583 .size_i ( req_fsm_bypass_size ),
584 .id_i ( Cat(id_fsm_bypass, 0, 0) ),
585 .valid_o ( valid_bypass_fsm ),
586 .rdata_o ( data_bypass_fsm ),
587 .gnt_id_o ( gnt_id_bypass_fsm ),
588 .id_o ( id_bypass_fsm ),
589 .critical_word_o ( ), # not used for single requests
590 .critical_word_valid_o ( ), # not used for single requests
591 .axi_req_o ( axi_bypass_o ),
592 .axi_resp_i ( axi_bypass_i )
593 );
594
595 # ----------------------
596 # Cache Line AXI Refill
597 # ----------------------
598 axi_adapter #(
599 .DATA_WIDTH ( DCACHE_LINE_WIDTH ),
600 .AXI_ID_WIDTH ( 4 ),
601 .CACHELINE_BYTE_OFFSET ( DCACHE_BYTE_OFFSET )
602 ) i_miss_axi_adapter (
603 .clk_i,
604 .rst_ni,
605 .req_i ( req_fsm_miss_valid ),
606 .type_i ( req_fsm_miss_req ),
607 .gnt_o ( gnt_miss_fsm ),
608 .addr_i ( req_fsm_miss_addr ),
609 .we_i ( req_fsm_miss_we ),
610 .wdata_i ( req_fsm_miss_wdata ),
611 .be_i ( req_fsm_miss_be ),
612 .size_i ( req_fsm_miss_size ),
613 .id_i ( Const(0b1100, 4) ),
614 .gnt_id_o ( ), # open
615 .valid_o ( valid_miss_fsm ),
616 .rdata_o ( data_miss_fsm ),
617 .id_o ( ),
618 .critical_word_o,
619 .critical_word_valid_o,
620 .axi_req_o ( axi_data_o ),
621 .axi_resp_i ( axi_data_i )
622 );
623
624 # -----------------
625 # Replacement LFSR
626 # -----------------
627 lfsr_8bit #(.WIDTH (DCACHE_SET_ASSOC)) i_lfsr (
628 .en_i ( lfsr_enable ),
629 .refill_way_oh ( lfsr_oh ),
630 .refill_way_bin ( lfsr_bin ),
631 .*
632 );
633
634 # -----------------
635 # AMO ALU
636 # -----------------
637 amo_alu i_amo_alu (
638 .amo_op_i ( amo_op ),
639 .amo_operand_a_i ( amo_operand_a ),
640 .amo_operand_b_i ( amo_operand_b ),
641 .amo_result_o ( amo_result_o )
642 );
643
644 # -----------------
645 # Struct Split
646 # -----------------
647
648 for i in range(NR_PORTS):
649 miss_req = MissReq()
650 comb += miss_req.eq(miss_req_i[i]);
651 comb += miss_req_valid [i] .eq(miss_req.valid)
652 comb += miss_req_bypass [i] .eq(miss_req.bypass)
653 comb += miss_req_addr [i] .eq(miss_req.addr)
654 comb += miss_req_wdata [i] .eq(miss_req.wdata)
655 comb += miss_req_we [i] .eq(miss_req.we)
656 comb += miss_req_be [i] .eq(miss_req.be)
657 comb += miss_req_size [i] .eq(miss_req.size)
658
659 # --------------
660 # AXI Arbiter
661 # --------------s
662 #
663 # Description: Arbitrates access to AXI refill/bypass
664 #
665 class AXIArbiter:
666 def __init__(self, NR_PORTS = 3, DATA_WIDTH = 64):
667 self.NR_PORTS = NR_PORTS
668 self.DATA_WIDTH = DATA_WIDTH
669 self.pwid = pwid = ceil(log(NR_PORTS) / log(2))
670 rst_ni = ResetSignal() # Asynchronous reset active low
671 # master ports
672 self.data_req_i = Signal(NR_PORTS)
673 self.address_i = Array(Signal(name="address_i", 64) \
674 for i in range(NR_PORTS))
675 self.data_wdata_i = Array(Signal(name="data_wdata_i", 64) \
676 for i in range(NR_PORTS))
677 self.data_we_i = Signal(NR_PORTS)
678 self.data_be_i = Array(Signal(name="data_wdata_i", DATA_WIDTH/8) \
679 for i in range(NR_PORTS))
680 self.data_size_i = Array(Signal(name="data_size_i", 2) \
681 for i in range(NR_PORTS))
682 self.data_gnt_o = Signal(NR_PORTS)
683 self.data_rvalid_o = Signal(NR_PORTS)
684 self.data_rdata_o = Array(Signal(name="data_rdata_o", 64) \
685 for i in range(NR_PORTS))
686
687 # slave port
688 self.id_i = Signal(pwid)
689 self.id_o = Signal(pwid)
690 self.gnt_id_i = Signal(pwid)
691 self.data_req_o = Signal()
692 self.address_o = Signal(64)
693 self.data_wdata_o = Signal(DATA_WIDTH)
694 self.data_we_o = Signal()
695 self.data_be_o = Signal(DATA_WIDTH/8)
696 self.data_size_o = Signal(2)
697 self.data_gnt_i = Signal()
698 self.data_rvalid_i = Signal()
699 self.data_rdata_i = Signal(DATA_WIDTH)
700
701 def elaborate(self, platform):
702 #enum logic [1:0] { IDLE, REQ, SERVING } state_d, state_q;
703
704 class Packet:
705 def __init__(self, pwid, DATA_WIDTH):
706 self.id = Signal(pwid)
707 self.address = Signal(64)
708 self.data = Signal(64)
709 self.size = Signal(2)
710 self.be = Signal(DATA_WIDTH/8)
711 self.we = Signal()
712
713 request_index = Signal(self.pwid)
714 req_q = Packet(self.pwid, self.DATA_WIDTH)
715 req_d = Packet(self.pwid, self.DATA_WIDTH)
716
717 # request register
718 sync += req_q.eq(req_d)
719
720 # request port
721 comb += self.address_o .eq(req_q.address)
722 comb += self.data_wdata_o .eq(req_q.data)
723 comb += self.data_be_o .eq(req_q.be)
724 comb += self.data_size_o .eq(req_q.size)
725 comb += self.data_we_o .eq(req_q.we)
726 comb += self.id_o .eq(req_q.id)
727 comb += self.data_gnt_o .eq(0)
728 # read port
729 comb += self.data_rvalid_o .eq(0)
730 comb += self.data_rdata_o .eq(0)
731 comb += self.data_rdata_o[req_q.id].eq(data_rdata_i)
732
733 m.submodules.pp = pp = PriorityEncoder(self.NR_PORTS)
734 comb += pp.i.eq(self.data_req_i) # select one request (priority-based)
735 comb += request_index.eq(pp.o)
736
737 with m.Switch("state") as s:
738
739 with m.Case("IDLE"):
740 # wait for incoming requests (priority encoder data_req_i)
741 with m.If(~pp.n): # one output valid from encoder
742 comb += self.data_req_o .eq(self.data_req_i[i])
743 comb += self.data_gnt_o[i].eq(self.data_req_i[i])
744 # save the request
745 comb += req_d.address.eq(self.address_i[i])
746 comb += req_d.id.eq(request_index)
747 comb += req_d.data.eq(self.data_wdata_i[i])
748 comb += req_d.size.eq(self.data_size_i[i])
749 comb += req_d.be.eq(self.data_be_i[i])
750 comb += req_d.we.eq(self.data_we_i[i])
751 m.next = "SERVING"
752
753 comb += self.address_o .eq(self.address_i[request_index])
754 comb += self.data_wdata_o .eq(self.data_wdata_i[request_index])
755 comb += self.data_be_o .eq(self.data_be_i[request_index])
756 comb += self.data_size_o .eq(self.data_size_i[request_index])
757 comb += self.data_we_o .eq(self.data_we_i[request_index])
758 comb += self.id_o .eq(request_index)
759
760 with m.Case("SERVING"):
761 comb += self.data_req_o.eq(1)
762 with m.If (self.data_rvalid_i):
763 comb += self.data_rvalid_o[req_q.id].eq(1)
764 m.next = "IDLE"
765
766 # ------------
767 # Assertions
768 # ------------
769
770 """
771 #pragma translate_off
772 `ifndef VERILATOR
773 # make sure that we eventually get an rvalid after we received a grant
774 assert property (@(posedge clk_i) data_gnt_i |-> ##[1:$] data_rvalid_i )
775 else begin $error("There was a grant without a rvalid"); $stop(); end
776 # assert that there is no grant without a request
777 assert property (@(negedge clk_i) data_gnt_i |-> data_req_o)
778 else begin $error("There was a grant without a request."); $stop(); end
779 # assert that the address does not contain X when request is sent
780 assert property ( @(posedge clk_i) (data_req_o) |-> (!$isunknown(address_o)) )
781 else begin $error("address contains X when request is set"); $stop(); end
782
783 `endif
784 #pragma translate_on
785 """
786