summaryrefslogtreecommitdiff
path: root/src/gpgpu-sim/shader.cc
diff options
context:
space:
mode:
authorDavit Grigoryan <[email protected]>2026-04-29 00:02:46 +0000
committerDavit Grigoryan <[email protected]>2026-04-29 00:02:46 +0000
commit314fb652d8a27a38599eeeec121d45327a405bb0 (patch)
treed3fa263f05e8b40c766b68eb1771b619b847802f /src/gpgpu-sim/shader.cc
parentfbb90ef3a76cd6c469782c84668d25c5f3bfdd22 (diff)
impl option w/ only 2 SBs; stall so SBs are cleared before div or reconv
Diffstat (limited to 'src/gpgpu-sim/shader.cc')
-rw-r--r--src/gpgpu-sim/shader.cc150
1 files changed, 131 insertions, 19 deletions
diff --git a/src/gpgpu-sim/shader.cc b/src/gpgpu-sim/shader.cc
index ec764aa..9a47996 100644
--- a/src/gpgpu-sim/shader.cc
+++ b/src/gpgpu-sim/shader.cc
@@ -1063,6 +1063,15 @@ void shader_core_ctx::fetch() {
m_simt_tables[wid]->get_active_split_id();
m_inst_fetch_buffer.m_split_mask =
m_simt_tables[wid]->get_active_mask();
+ // Mode 2: record half 0's owner.
+ if (m_config->gpgpu_scoreboard_mode == 2) {
+ active_mask_t mask_aw;
+ const simt_mask_t &m = m_simt_tables[wid]->get_active_mask();
+ for (unsigned t = 0; t < MAX_WARP_SIZE; t++)
+ if (m.test(t)) mask_aw.set(t);
+ m_warp[wid]->ibuffer_assign_half(0, m_inst_fetch_buffer.m_split_id,
+ mask_aw);
+ }
}
m_inst_fetch_buffer.m_ibuffer_half = 0;
m_warp[mf->get_wid()]->set_last_fetch(m_gpu->gpu_sim_cycle);
@@ -1110,6 +1119,20 @@ void shader_core_ctx::fetch() {
if (simt_conditions && !m_warp[warp_id]->functional_done() &&
!m_warp[warp_id]->imiss_pending() &&
m_warp[warp_id]->ibuffer_half_empty(0)) {
+ // Mode 2 (slot-pinned): if the next split to fetch from differs
+ // from half 0's previously assigned owner, wait for the slot's
+ // scoreboard to drain before reassigning. This is the "wait
+ // for clean at divergence/reconvergence/split-rotation" gate.
+ if (m_config->gpgpu_scoreboard_mode == 2 &&
+ m_config->model == AWARE_RECONVERGENCE) {
+ unsigned new_split_id =
+ m_simt_tables[warp_id]->get_active_split_id();
+ if (m_warp[warp_id]->ibuffer_half_assigned(0) &&
+ m_warp[warp_id]->ibuffer_half_split_id(0) != new_split_id &&
+ !m_scoreboard->slotClean(warp_id, 0)) {
+ continue; // drain pending — try next warp
+ }
+ }
address_type pc;
pc = m_warp[warp_id]->get_pc();
address_type ppc = pc + PROGRAM_MEM_START;
@@ -1147,6 +1170,16 @@ void shader_core_ctx::fetch() {
m_simt_tables[warp_id]->get_active_split_id();
m_inst_fetch_buffer.m_split_mask =
m_simt_tables[warp_id]->get_active_mask();
+ // Mode 2: record half 0's owner for the wait-for-clean gate.
+ if (m_config->gpgpu_scoreboard_mode == 2) {
+ active_mask_t mask_aw;
+ const simt_mask_t &m =
+ m_simt_tables[warp_id]->get_active_mask();
+ for (unsigned t = 0; t < MAX_WARP_SIZE; t++)
+ if (m.test(t)) mask_aw.set(t);
+ m_warp[warp_id]->ibuffer_assign_half(
+ 0, m_inst_fetch_buffer.m_split_id, mask_aw);
+ }
}
m_inst_fetch_buffer.m_ibuffer_half = 0;
m_warp[warp_id]->set_last_fetch(m_gpu->gpu_sim_cycle);
@@ -1192,6 +1225,15 @@ void shader_core_ctx::fetch() {
if ((sec_mask & half0_mask).any()) continue; // overlapping, skip
}
+ // Mode 2: if half 1's previous owner differs from this candidate,
+ // wait for SB[1] to drain before reassigning.
+ if (m_config->gpgpu_scoreboard_mode == 2 &&
+ m_warp[warp_id]->ibuffer_half_assigned(1) &&
+ m_warp[warp_id]->ibuffer_half_split_id(1) != sec_split_id &&
+ !m_scoreboard->slotClean(warp_id, 1)) {
+ continue; // drain pending — try next warp
+ }
+
// Attempt I-Cache access (HIT-only for secondary)
address_type ppc = sec_pc + PROGRAM_MEM_START;
unsigned nbytes = 16;
@@ -1297,6 +1339,8 @@ warp_inst_t *shader_core_ctx::issue_warp(register_set &pipe_reg_set,
(*pipe_reg)->set_dbg_path(0);
(*pipe_reg)->set_dbg_split_id(-1);
(*pipe_reg)->set_dbg_source_inst(next_inst);
+ // Mode 2: primary issue always comes through ibuffer half 0.
+ (*pipe_reg)->set_ibuffer_half_id(0);
static const bool dbg_pc_enabled_pri = (getenv("MEMCO_DBG_PC") != NULL);
if (dbg_pc_enabled_pri && warp_id == 0 &&
m_config->model == AWARE_RECONVERGENCE) {
@@ -1449,6 +1493,10 @@ void shader_core_ctx::co_issue_warp(warp_inst_t *composite,
temp_inst.set_dbg_path(split_id != (unsigned)-1 ? 1u : 2u);
temp_inst.set_dbg_split_id(split_id != (unsigned)-1 ? (int)split_id : -1);
temp_inst.set_dbg_source_inst(next_inst);
+ // Mode 2: stamp the source's owning ibuffer half. Intra-warp co-issue
+ // came from primary's slot 1 (secondary half); inter-warp from the
+ // donor's slot 0 (donor's own primary half).
+ temp_inst.set_ibuffer_half_id(split_id != (unsigned)-1 ? 1u : 0u);
// Debug: for intra co-issue, compare cached active_mask (from
// ibuffer slot) against current splits-table mask + warp's
@@ -1529,11 +1577,13 @@ void shader_core_ctx::co_issue_warp(warp_inst_t *composite,
}
// Reserve scoreboard for the co-issued warp.
- if (m_config->gpgpu_scoreboard_mode == 1) {
- // Mode 1 (mask-aware): both inter-warp and intra-warp use the unified
- // mask-aware scoreboard. The active_mask carried in temp_inst already
- // differentiates the two splits' reservations — disjoint-lane writes
- // get distinct (reg, mask) entries even when they share warp_id.
+ if (m_config->gpgpu_scoreboard_mode == 1 ||
+ m_config->gpgpu_scoreboard_mode == 2) {
+ // Mode 1 (mask-aware): both inter and intra use unified mask-aware
+ // scoreboard; mask differentiates entries.
+ // Mode 2 (slot-pinned): both inter and intra reserve on their
+ // respective slot's scoreboard via temp_inst's set ibuffer_half_id
+ // (set above to 0 for inter, 1 for intra).
m_scoreboard->reserveRegisters(&temp_inst);
} else {
// Mode 0 (legacy):
@@ -1570,6 +1620,10 @@ void shader_core_ctx::co_issue_warp(warp_inst_t *composite,
// release the matching mask-aware reservation. Cheap and harmless in
// mode 0 (field is unused there).
temp_inst.set_source_mask_on_sets(active_mask);
+ // Mode 2: stamp the source's owning ibuffer half (0 for inter-warp
+ // donor's primary half, 1 for intra-warp's secondary half) so per-set
+ // writeback can pick the right slot scoreboard.
+ temp_inst.set_source_slot_on_sets(split_id != (unsigned)-1 ? 1u : 0u);
// Mixed-space MEM co-issue (v2): for SHARED coissuers, capture per-lane
// memreqaddr + temp_inst.cycles into each valid set BEFORE merge. The
@@ -1963,8 +2017,8 @@ void scheduler_unit::try_inter_warp_coissue(
}
// Scoreboard check.
- // Mode 1 needs the candidate's active mask for hazard intersection;
- // hoist its lookup before the legacy/mode-1 dispatch.
+ // Mode 1 needs the candidate's active mask for hazard intersection.
+ // Mode 2 checks the donor warp's slot 0 (its primary half).
bool sb_collision_inter;
if (m_shader->m_config->gpgpu_scoreboard_mode == 1) {
const active_mask_t &cand_mask_pre =
@@ -1972,6 +2026,9 @@ void scheduler_unit::try_inter_warp_coissue(
sb_collision_inter = m_scoreboard->checkCollisionMask(cand_warp_id,
cand_inst,
cand_mask_pre);
+ } else if (m_shader->m_config->gpgpu_scoreboard_mode == 2) {
+ sb_collision_inter = m_scoreboard->checkCollisionSlot(
+ cand_warp_id, /*slot=*/0, cand_inst);
} else {
sb_collision_inter = m_scoreboard->checkCollision(cand_warp_id,
cand_inst);
@@ -2135,6 +2192,10 @@ void scheduler_unit::try_intra_warp_coissue(
if (m_shader->m_config->gpgpu_scoreboard_mode == 1) {
sb_collision = m_scoreboard->checkCollisionMask(primary_warp_id,
sec_inst, sec_mask_eff);
+ } else if (m_shader->m_config->gpgpu_scoreboard_mode == 2) {
+ // Intra-warp candidates check primary warp's slot 1 (secondary half).
+ sb_collision = m_scoreboard->checkCollisionSlot(primary_warp_id,
+ /*slot=*/1, sec_inst);
} else {
sb_collision = m_scoreboard->checkCollisionSecondary(primary_warp_id,
sec_inst);
@@ -2234,6 +2295,9 @@ void scheduler_unit::try_utilization_max_coissue(
sb_collision_inter2 = m_scoreboard->checkCollisionMask(cand_warp_id,
cand_inst,
cand_mask_pre);
+ } else if (m_shader->m_config->gpgpu_scoreboard_mode == 2) {
+ sb_collision_inter2 = m_scoreboard->checkCollisionSlot(
+ cand_warp_id, /*slot=*/0, cand_inst);
} else {
sb_collision_inter2 = m_scoreboard->checkCollision(cand_warp_id,
cand_inst);
@@ -2400,6 +2464,10 @@ void scheduler_unit::try_utilization_max_coissue(
sb_collision = m_scoreboard->checkCollisionMask(cand_warp_id,
sec_inst,
sec_mask_eff);
+ } else if (m_shader->m_config->gpgpu_scoreboard_mode == 2) {
+ sb_collision = m_scoreboard->checkCollisionSlot(cand_warp_id,
+ /*slot=*/1,
+ sec_inst);
} else {
sb_collision = m_scoreboard->checkCollisionSecondary(cand_warp_id,
sec_inst);
@@ -2614,6 +2682,9 @@ void scheduler_unit::cycle() {
if (m_shader->m_config->gpgpu_scoreboard_mode == 1) {
sb_collision_primary =
m_scoreboard->checkCollisionMask(warp_id, pI, active_mask);
+ } else if (m_shader->m_config->gpgpu_scoreboard_mode == 2) {
+ sb_collision_primary = m_scoreboard->checkCollisionSlot(
+ warp_id, /*slot=*/0, pI);
} else {
sb_collision_primary = m_scoreboard->checkCollision(warp_id, pI);
}
@@ -3403,12 +3474,15 @@ void shader_core_ctx::writeback() {
for (unsigned s = 0; s < sets.size(); s++) {
if (!sets[s].valid || sets[s].source_inst == NULL) continue;
unsigned set_wid = sets[s].warp_id;
+ unsigned set_slot =
+ (sets[s].source_slot_id != (unsigned)-1) ? sets[s].source_slot_id
+ : 0u;
for (unsigned r = 0; r < MAX_OUTPUT_VALUES; r++) {
if (sets[s].source_inst->out[r] > 0) {
m_scoreboard->releaseSetReg(set_wid,
sets[s].source_inst->out[r],
sets[s].source_mask,
- /*is_intra_legacy=*/false);
+ /*is_intra_legacy=*/false, set_slot);
}
}
}
@@ -3433,11 +3507,15 @@ void shader_core_ctx::writeback() {
// Intra-warp: release from secondary scoreboard (mode 0) or
// mask-aware (mode 1) and dec once.
if (!intra_warp_decremented) {
+ unsigned set_slot =
+ (sets[s].source_slot_id != (unsigned)-1)
+ ? sets[s].source_slot_id
+ : 1u; // intra defaults to slot 1
for (unsigned r = 0; r < MAX_OUTPUT_VALUES; r++) {
if (sets[s].source_inst->out[r] > 0) {
m_scoreboard->releaseSetReg(
sets[s].warp_id, sets[s].source_inst->out[r],
- sets[s].source_mask, /*is_intra_legacy=*/true);
+ sets[s].source_mask, /*is_intra_legacy=*/true, set_slot);
}
}
m_warp[warp_id]->dec_inst_in_pipeline();
@@ -3774,7 +3852,8 @@ void ldst_unit::L1_latency_queue_cycle() {
m_pending_writes_secondary[src.wid][src_split_mf].erase(
reg);
m_scoreboard->releaseSetReg(src.wid, reg, src.source_mask,
- /*is_intra_legacy=*/true);
+ /*is_intra_legacy=*/true,
+ src.source_slot_id);
any_completed = true;
}
} else {
@@ -3783,7 +3862,8 @@ void ldst_unit::L1_latency_queue_cycle() {
if (!still_pending) {
m_pending_writes[src.wid].erase(reg);
m_scoreboard->releaseSetReg(src.wid, reg, src.source_mask,
- /*is_intra_legacy=*/false);
+ /*is_intra_legacy=*/false,
+ src.source_slot_id);
any_completed = true;
}
}
@@ -4370,6 +4450,7 @@ ldst_unit::mem_src_t ldst_unit::resolve_source(
r.wid = (access_src_wid == (unsigned)-1) ? inst.warp_id() : access_src_wid;
r.out_inst = &inst;
r.source_mask.reset();
+ r.source_slot_id = 0; // primary's own slot
// Primary access: (wid == primary AND split_id == -1). Return early with
// out_inst = composite (primary's out[]). Note: split_id is the
// discriminator — a coissuer can have the same warp_id as primary when
@@ -4377,6 +4458,8 @@ ldst_unit::mem_src_t ldst_unit::resolve_source(
if (r.wid == inst.warp_id() && access_src_split_id == (unsigned)-1) {
// Primary's own mask is on the composite itself.
r.source_mask = inst.get_active_mask();
+ r.source_slot_id = inst.get_ibuffer_half_id();
+ if (r.source_slot_id == (unsigned)-1) r.source_slot_id = 0;
return r;
}
if (!inst.has_simd_sets()) return r;
@@ -4388,6 +4471,9 @@ ldst_unit::mem_src_t ldst_unit::resolve_source(
sets[s].split_id == access_src_split_id) {
r.out_inst = sets[s].source_inst;
r.source_mask = sets[s].source_mask;
+ if (sets[s].source_slot_id != (unsigned)-1) {
+ r.source_slot_id = sets[s].source_slot_id;
+ }
break;
}
}
@@ -4769,7 +4855,8 @@ void ldst_unit::writeback() {
.erase(reg);
m_scoreboard->releaseSetReg(src_i.wid, reg,
src_i.source_mask,
- /*is_intra_legacy=*/true);
+ /*is_intra_legacy=*/true,
+ src_i.source_slot_id);
insn_completed = true;
}
} else {
@@ -4780,7 +4867,8 @@ void ldst_unit::writeback() {
m_pending_writes[src_i.wid].erase(reg);
m_scoreboard->releaseSetReg(src_i.wid, reg,
src_i.source_mask,
- /*is_intra_legacy=*/false);
+ /*is_intra_legacy=*/false,
+ src_i.source_slot_id);
insn_completed = true;
}
}
@@ -4807,7 +4895,8 @@ void ldst_unit::writeback() {
if (src.out_inst->out[r] > 0) {
m_scoreboard->releaseSetReg(src.wid, src.out_inst->out[r],
src.source_mask,
- /*is_intra_legacy=*/false);
+ /*is_intra_legacy=*/false,
+ src.source_slot_id);
insn_completed = true;
}
}
@@ -4828,11 +4917,16 @@ void ldst_unit::writeback() {
released.insert(key);
// Secondary slot coissuer → secondary scoreboard map (mode 0).
bool coissued_intra = (sets[s].split_id != (unsigned)-1);
+ unsigned set_slot =
+ (sets[s].source_slot_id != (unsigned)-1)
+ ? sets[s].source_slot_id
+ : (coissued_intra ? 1u : 0u);
for (unsigned r = 0; r < MAX_OUTPUT_VALUES; r++) {
if (sets[s].source_inst->out[r] > 0) {
m_scoreboard->releaseSetReg(
sets[s].warp_id, sets[s].source_inst->out[r],
- sets[s].source_mask, /*is_intra_legacy=*/coissued_intra);
+ sets[s].source_mask, /*is_intra_legacy=*/coissued_intra,
+ set_slot);
insn_completed = true;
}
}
@@ -5151,12 +5245,16 @@ void ldst_unit::cycle() {
if (released.count(key)) continue;
released.insert(key);
bool is_intra = (sets[s].split_id != (unsigned)-1);
+ unsigned set_slot =
+ (sets[s].source_slot_id != (unsigned)-1)
+ ? sets[s].source_slot_id
+ : (is_intra ? 1u : 0u);
for (unsigned r = 0; r < MAX_OUTPUT_VALUES; r++) {
unsigned reg = sets[s].source_inst->out[r];
if (reg == 0) continue;
m_scoreboard->releaseSetReg(sets[s].warp_id, reg,
sets[s].source_mask,
- /*is_intra_legacy=*/is_intra);
+ /*is_intra_legacy=*/is_intra, set_slot);
}
}
pipe_reg.set_shared_side_released(true);
@@ -5325,12 +5423,16 @@ void ldst_unit::cycle() {
if (released_shared.count(key)) continue;
released_shared.insert(key);
bool is_intra = (sets[s].split_id != (unsigned)-1);
+ unsigned set_slot =
+ (sets[s].source_slot_id != (unsigned)-1)
+ ? sets[s].source_slot_id
+ : (is_intra ? 1u : 0u);
for (unsigned r = 0; r < MAX_OUTPUT_VALUES; r++) {
unsigned reg_id = sets[s].source_inst->out[r];
if (reg_id == 0) continue;
m_scoreboard->releaseSetReg(sets[s].warp_id, reg_id,
sets[s].source_mask,
- /*is_intra_legacy=*/is_intra);
+ /*is_intra_legacy=*/is_intra, set_slot);
}
}
}
@@ -5472,12 +5574,17 @@ void ldst_unit::cycle() {
released.insert(key);
// Secondary slot → secondary scoreboard map (mode 0).
bool is_intra = (sets[s].split_id != (unsigned)-1);
+ unsigned set_slot =
+ (sets[s].source_slot_id != (unsigned)-1)
+ ? sets[s].source_slot_id
+ : (is_intra ? 1u : 0u);
for (unsigned r = 0; r < MAX_OUTPUT_VALUES; r++) {
unsigned reg_id = sets[s].source_inst->out[r];
if (reg_id == 0) continue;
m_scoreboard->releaseSetReg(sets[s].warp_id, reg_id,
sets[s].source_mask,
- /*is_intra_legacy=*/is_intra);
+ /*is_intra_legacy=*/is_intra,
+ set_slot);
}
}
}
@@ -5557,12 +5664,17 @@ void ldst_unit::cycle() {
if (released.count(key)) continue;
released.insert(key);
bool is_intra = (sets[s].split_id != (unsigned)-1);
+ unsigned set_slot =
+ (sets[s].source_slot_id != (unsigned)-1)
+ ? sets[s].source_slot_id
+ : (is_intra ? 1u : 0u);
for (unsigned r = 0; r < MAX_OUTPUT_VALUES; r++) {
unsigned reg_id = sets[s].source_inst->out[r];
if (reg_id == 0) continue;
m_scoreboard->releaseSetReg(sets[s].warp_id, reg_id,
sets[s].source_mask,
- /*is_intra_legacy=*/is_intra);
+ /*is_intra_legacy=*/is_intra,
+ set_slot);
}
}
}