Skip to content

Commit ea29614

Browse files
committed
bug fix
1 parent 9c24951 commit ea29614

File tree

1 file changed

+91
-39
lines changed

1 file changed

+91
-39
lines changed

sim/simx/cache_sim.cpp

Lines changed: 91 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -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

157206
struct 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

Comments
 (0)