@@ -128,30 +128,79 @@ struct set_t {
128128 }
129129 }
130130
131+ // Tag lookup for a core/replay access. Updates LRU counters.
132+ // - Returns hit line id or -1.
133+ // - free_line_id: first invalid way (if any), else -1.
134+ // - repl_line_id: LRU victim candidate (always a valid index when lines is non-empty).
131135 int tag_lookup (uint64_t tag, int * free_line_id, int * repl_line_id) {
132- uint32_t max_cnt = 0 ;
133136 int hit_line_id = -1 ;
134137 *free_line_id = -1 ;
135- *repl_line_id = -1 ;
138+
139+ // Select replacement among valid lines using max LRU counter.
140+ uint32_t max_cnt = 0 ;
141+ bool repl_valid = false ;
142+ *repl_line_id = 0 ;
143+
136144 for (uint32_t i = 0 , n = lines.size (); i < n; ++i) {
137145 auto & line = lines.at (i);
138- if (max_cnt < line.lru_ctr ) {
146+
147+ if (!line.valid ) {
148+ if (*free_line_id == -1 )
149+ *free_line_id = i;
150+ continue ;
151+ }
152+
153+ if (!repl_valid || line.lru_ctr >= max_cnt) {
139154 max_cnt = line.lru_ctr ;
140155 *repl_line_id = i;
156+ repl_valid = true ;
141157 }
142- if (line.valid ) {
143- if (line.tag == tag) {
144- hit_line_id = i;
145- line.lru_ctr = 0 ;
146- } else {
147- ++line.lru_ctr ;
148- }
158+
159+ if (line.tag == tag) {
160+ hit_line_id = i;
161+ line.lru_ctr = 0 ;
149162 } else {
150- *free_line_id = i ;
163+ ++line. lru_ctr ;
151164 }
152165 }
166+
167+ // If no valid lines exist, pick way 0 (or the free way if present).
168+ if (!repl_valid) {
169+ *repl_line_id = (*free_line_id != -1 ) ? *free_line_id : 0 ;
170+ }
171+
153172 return hit_line_id;
154173 }
174+
175+ // Choose a victim line for installing a fill. Does NOT mutate LRU.
176+ // Returns the selected line id.
177+ int select_victim (int * free_line_id, int * repl_line_id) const {
178+ *free_line_id = -1 ;
179+ *repl_line_id = 0 ;
180+
181+ uint32_t max_cnt = 0 ;
182+ bool repl_valid = false ;
183+
184+ for (uint32_t i = 0 , n = lines.size (); i < n; ++i) {
185+ const auto & line = lines.at (i);
186+ if (!line.valid ) {
187+ if (*free_line_id == -1 )
188+ *free_line_id = i;
189+ continue ;
190+ }
191+ if (!repl_valid || line.lru_ctr >= max_cnt) {
192+ max_cnt = line.lru_ctr ;
193+ *repl_line_id = i;
194+ repl_valid = true ;
195+ }
196+ }
197+
198+ if (!repl_valid) {
199+ *repl_line_id = (*free_line_id != -1 ) ? *free_line_id : 0 ;
200+ }
201+
202+ return (*free_line_id != -1 ) ? *free_line_id : *repl_line_id;
203+ }
155204};
156205
157206struct bank_req_t {
@@ -202,6 +251,7 @@ struct mshr_entry_t {
202251 bank_req_t bank_req;
203252 uint32_t set_id;
204253 uint64_t addr_tag;
254+ uint32_t line_id;
205255
206256 mshr_entry_t () {
207257 this ->reset ();
@@ -211,6 +261,7 @@ struct mshr_entry_t {
211261 bank_req.reset ();
212262 set_id = 0 ;
213263 addr_tag = 0 ;
264+ line_id = 0 ;
214265 }
215266};
216267
@@ -270,6 +321,7 @@ class MSHR {
270321 entry.bank_req = bank_req;
271322 entry.set_id = set_id;
272323 entry.addr_tag = addr_tag;
324+ entry.line_id = 0 ; // victim is selected at Fill time
273325 ++size_;
274326 return i;
275327 }
@@ -357,6 +409,7 @@ class CacheBank : public SimObject<CacheBank> {
357409 pending_read_reqs_ = 0 ;
358410 pending_write_reqs_ = 0 ;
359411 pending_fill_reqs_ = 0 ;
412+ inflight_fills_ = 0 ;
360413 }
361414
362415 void tick () {
@@ -390,14 +443,19 @@ class CacheBank : public SimObject<CacheBank> {
390443 return ;
391444 }
392445
393- // second: schedule memory fill
394- if (!this ->mem_rsp_in .empty ()) {
446+ // second: schedule memory fill
447+ // IMPORTANT: When the bank has latency > 1, we must not enqueue multiple Fill ops
448+ // into the pipeline before the first Fill reaches the head and schedules replays.
449+ // Otherwise, later Fill ops would sit ahead of Replay ops in the FIFO, breaking the
450+ // required ordering (Fill -> Replay) and causing replay misses / MSHR assertions.
451+ if (!this ->mem_rsp_in .empty () && inflight_fills_ == 0 ) {
395452 auto & mem_rsp = this ->mem_rsp_in .peek ();
396453 bank_req_t bank_req;
397454 bank_req.reset ();
398455 bank_req.type = bank_req_t ::Fill;
399456 bank_req.mshr_id = mem_rsp.tag ;
400457 pipe_req_->push (bank_req);
458+ ++inflight_fills_;
401459 this ->mem_rsp_in .pop ();
402460 --pending_fill_reqs_;
403461 DT (3 , this ->name () << " -fill-rsp: " << mem_rsp);
@@ -448,27 +506,17 @@ class CacheBank : public SimObject<CacheBank> {
448506 break ;
449507
450508 case bank_req_t ::Fill: {
509+ // Victim selection is deferred until Fill, so the cache remains fully hit-under-miss.
451510 // Peek root entry first so we can do backpressure checks *before* mutating the MSHR.
452511 const auto & root_peek = mshr_.peek (bank_req.mshr_id );
453512 auto & set = sets_.at (root_peek.set_id );
454513
455- // Select victim logic moved here
456514 int32_t free_line_id = -1 ;
457515 int32_t repl_line_id = 0 ;
458- int hit_line_id = set.tag_lookup (root_peek.addr_tag , &free_line_id, &repl_line_id);
516+ int32_t victim_line_id = set.select_victim (&free_line_id, &repl_line_id);
517+ auto & victim_line = set.lines .at (victim_line_id);
459518
460- uint32_t victim_line_id;
461- if (hit_line_id != -1 ) {
462- // We found the line!
463- victim_line_id = hit_line_id;
464- } else {
465- // Select a victim: Prioritize free lines, then LRU
466- victim_line_id = (free_line_id != -1 ) ? free_line_id : repl_line_id;
467- }
468-
469- auto & line = set.lines .at (victim_line_id);
470-
471- bool need_wb = config_.write_back && line.valid && line.dirty ;
519+ bool need_wb = config_.write_back && victim_line.valid && victim_line.dirty ;
472520 if (need_wb && this ->mem_req_out .full ())
473521 return ; // stall
474522
@@ -478,7 +526,7 @@ class CacheBank : public SimObject<CacheBank> {
478526 // Writeback victim if needed (writeback happens in the fill pipeline stage, RTL-style).
479527 if (need_wb) {
480528 MemReq mem_req;
481- mem_req.addr = params_.mem_addr (bank_id_, root_entry.set_id , line .tag );
529+ mem_req.addr = params_.mem_addr (bank_id_, root_entry.set_id , victim_line .tag );
482530 mem_req.write = true ;
483531 mem_req.cid = root_entry.bank_req .cid ;
484532 mem_req.uuid = root_entry.bank_req .uuid ;
@@ -488,10 +536,10 @@ class CacheBank : public SimObject<CacheBank> {
488536 }
489537
490538 // Install the filled line (Replay will apply request semantics such as dirtying for write-miss).
491- line .valid = true ;
492- line .tag = root_entry.addr_tag ;
493- line .lru_ctr = 0 ;
494- line .dirty = false ;
539+ victim_line .valid = true ;
540+ victim_line .tag = root_entry.addr_tag ;
541+ victim_line .lru_ctr = 0 ;
542+ victim_line .dirty = false ;
495543 } break ;
496544
497545 case bank_req_t ::Replay: {
@@ -507,8 +555,6 @@ class CacheBank : public SimObject<CacheBank> {
507555 int32_t free_line_id = -1 ;
508556 int32_t repl_line_id = 0 ;
509557 int hit_line_id = set.tag_lookup (addr_tag, &free_line_id, &repl_line_id);
510-
511- // The line must be there because Fill just installed it
512558 assert (hit_line_id != -1 );
513559
514560 if (bank_req.write && config_.write_back ) {
@@ -605,17 +651,14 @@ class CacheBank : public SimObject<CacheBank> {
605651
606652 // MSHR-backed miss (read miss, or write-back write miss).
607653 uint32_t root_id = 0 ;
608- // [UPDATED] No need for root_line_id anymore
609654 bool mshr_pending = mshr_.lookup (set_id, addr_tag, &root_id);
610655
611- // [UPDATED] LATE BINDING: We do NOT select the victim here.
612- // We only check if we can send a Fill request if needed.
656+ // If we are the first miss for this block, we must be able to send the fill request this cycle.
613657 if (!mshr_pending && this ->mem_req_out .full ())
614658 return ; // stall
615659
616660 // Allocate an MSHR entry for this request.
617661 assert (!mshr_.full ());
618- // [UPDATED] Enqueue without alloc_line_id
619662 int mshr_id = mshr_.enqueue (bank_req, set_id, addr_tag);
620663 DT (3 , this ->name () << " -mshr-enqueue: " << bank_req);
621664
@@ -636,7 +679,15 @@ class CacheBank : public SimObject<CacheBank> {
636679 std::abort ();
637680 }
638681
639- pipe_req_->pop ();
682+ // Pop the request that we just processed.
683+ // Keep track of in-flight Fill ops so we never enqueue multiple fills ahead of replays
684+ // when bank latency > 1.
685+ const bool popped_fill = (bank_req.type == bank_req_t ::Fill);
686+ pipe_req_->pop ();
687+ if (popped_fill) {
688+ assert (inflight_fills_ > 0 );
689+ --inflight_fills_;
690+ }
640691 }
641692
642693 CacheSim::Config config_;
@@ -653,6 +704,7 @@ class CacheBank : public SimObject<CacheBank> {
653704 uint64_t pending_read_reqs_;
654705 uint64_t pending_write_reqs_;
655706 uint64_t pending_fill_reqs_;
707+ uint32_t inflight_fills_;
656708};
657709
658710// /////////////////////////////////////////////////////////////////////////////
0 commit comments