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.
11 # Author: Florian Zaruba, ETH Zurich
13 # Description: Handles cache misses.
14 from nmigen
.lib
.coding
import Encoder
, PriorityEncoder
21 import std_cache_pkg
::*;
25 class MissReq(RecordObject
):
26 def __init__(self
, name
=None):
27 Record
.__init
__(self
, name
)
29 self
.addr
= Signal(64)
33 self
.wdata
= Signal(64)
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
43 # cache line byte enable
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
)
60 WB_CACHELINE_FLUSH, # 3
62 WB_CACHELINE_MISS, # 5
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
85 self
.miss_req_i
= Array(MissReq(name
="missreq") for i
in range(NR_PORTS
))
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
))
93 output ariane_axi
::req_t axi_bypass_o
,
94 input ariane_axi
::resp_t axi_bypass_i
,
96 # Miss handling (~> cacheline refill)
97 self
.miss_gnt_o
= Signal(NR_PORTS
)
98 self
.active_serving_o
= Signal(NR_PORTS
)
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
,
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
)
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()
118 self
.data_i
= Array(CacheLine() \
119 for i
in range(DCACHE_SET_ASSOC
))
122 def elaborate(self
, platform
):
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
;
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
))
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)
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
))
159 # Cache Management <-> LFSR
160 lfsr_enable
= Signal()
161 lfsr_oh
= Signal(DCACHE_SET_ASSOC
)
162 lfsr_bin
= Signal($
clog2(DCACHE_SET_ASSOC
-1))
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)
170 logic
[63:3] address
;
172 } reservation_d
, reservation_q
;
174 # ------------------------------
176 # ------------------------------
177 evict_way
= Signal(DCACHE_SET_ASSOC
)
178 valid_way
= Signal(DCACHE_SET_ASSOC
)
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
)
184 # ----------------------
185 # Default Assignments
186 # ----------------------
188 req_fsm_miss_req
= ariane_axi
::CACHE_LINE_REQ
;
189 req_fsm_miss_size
= Const(0b11, 2)
191 serve_amo_d
= serve_amo_q
;
192 # --------------------------------
193 # Flush and Miss operation
194 # --------------------------------
197 evict_way_d
= evict_way_q
;
198 evict_cl_d
= evict_cl_q
;
200 # communicate to the requester which unit we are currently serving
201 active_serving_o
[mshr_q
.id] = mshr_q
.valid
;
203 # silence the unit when not used
204 amo_op
= amo_req_i
.amo_op
;
206 reservation_d
= reservation_q
;
207 with m
.FSM() as state_q
:
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
):
214 with m
.If(~serve_amo_q
):
215 m
.next
= "FLUSH_REQ_STATUS"
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"
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
]):
235 # we are taking another request so don't
239 wid
= DCACHE_TAG_WIDTH
+DCACHE_INDEX_WIDTH
240 comb
+= [ mshr_d
.valid
.eq(0b1),
241 mshr_d
.we
.eq(miss_req_we
[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
]),
249 # ~> we missed on the cache
251 # 1. Check if there is an empty cache-line
252 # 2. If not -> evict one
254 sync
+= addr_o
.eq(mshr_q
.addr
[:DCACHE_INDEX_WIDTH
]
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
273 m
.next
= "REQ_CACHELINE"
274 # we have at least one free way
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"
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
)
287 with m
.If (gnt_miss_fsm
):
288 m
.next
= "SAVE_CACHELINE"
289 comb
+= miss_gnt_o
[mshr_q
.id].eq(1)
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
)
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)
310 with m
.If (mshr_q
.we
):
311 # Yes, so safe the updated data now
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)
321 comb
+= mshr_d
.valid
.eq(0)
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"):
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
;
338 # we've got a grant --> this is timing critical, think about it
339 if (gnt_miss_fsm
) begin
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)
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
):
352 m
.next
= "FLUSH_REQ_STATUS"
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"):
361 sync
+= addr_o
.eq(cnt_q
)
364 with m
.Case("FLUSHING"):
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
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
)
380 comb
+= be_o
.vldrty
.eq(INVALIDATE_ON_FLUSH ?
1 : 0)
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
)
390 # ~> only called after reset
392 # initialize status array
393 sync
+= addr_o
.eq(cnt_q
)
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)
404 # ----------------------
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])
427 with m
.Case("AMO_STORE"):
428 load_data
= Signal(64)
430 comb
+= load_data
.eq(data_align(amo_req_i
.operand_a
[:3],
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]))
437 comb
+= amo_operand_a
.eq(load_data
)
438 comb
+= amo_operand_b
.eq(amo_req_i
.operand_b
)
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)
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)
460 comb
+= req_fsm_miss_valid
.eq(1)
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
)
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
))
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)
477 # the request is valid or we didn't need to go for another store
478 with m
.If (valid_miss_fsm
):
480 comb
+= amo_resp_o
.ack
.eq(1)
481 # write-back the result
482 comb
+= amo_resp_o
.result
.eq(amo_operand_a
;
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)
492 # check MSHR for aliasing
494 comb
+= mshr_addr_matches_o
.eq(0)
495 comb
+= mshr_index_matches_o
.eq()
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)
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)
511 # --------------------
513 # --------------------
516 #pragma translate_off
518 # assert that cache only hits on one way
520 @(posedge clk_i) $onehot0(evict_way_q)) else $warning("Evict-way should be one-hot encoded");
525 # ----------------------
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
;
542 i_bypass_arbiter
= ib
= AXIArbiter( NR_PORTS
, 64)
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
),
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
),
572 .CACHELINE_BYTE_OFFSET ( DCACHE_BYTE_OFFSET
)
573 ) i_bypass_axi_adapter (
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
)
595 # ----------------------
596 # Cache Line AXI Refill
597 # ----------------------
599 .DATA_WIDTH ( DCACHE_LINE_WIDTH
),
601 .CACHELINE_BYTE_OFFSET ( DCACHE_BYTE_OFFSET
)
602 ) i_miss_axi_adapter (
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
),
619 .critical_word_valid_o
,
620 .axi_req_o ( axi_data_o
),
621 .axi_resp_i ( axi_data_i
)
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
),
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
)
648 for i
in range(NR_PORTS
):
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
)
663 # Description: Arbitrates access to AXI refill/bypass
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
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
))
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
)
701 def elaborate(self
, platform
):
702 #enum logic [1:0] { IDLE, REQ, SERVING } state_d, state_q;
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)
713 request_index
= Signal(self
.pwid
)
714 req_q
= Packet(self
.pwid
, self
.DATA_WIDTH
)
715 req_d
= Packet(self
.pwid
, self
.DATA_WIDTH
)
718 sync
+= req_q
.eq(req_d
)
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)
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
)
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
)
737 with m
.Switch("state") as s
:
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
])
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
])
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
)
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)
771 #pragma translate_off
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