summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavit Grigoryan <[email protected]>2026-04-14 03:06:42 +0000
committerDavit Grigoryan <[email protected]>2026-04-14 03:06:42 +0000
commit305c5ea8fed5bca224377c1bdd8c726fac0f926f (patch)
tree549f408023103f33d6b074293e7f25f467590fc1
parent2c3524751b9b175514970c768637266bbed08767 (diff)
create separate scoreboard for I-Buf's second slot; fix other minor issues
-rw-r--r--src/gpgpu-sim/scoreboard.cc31
-rw-r--r--src/gpgpu-sim/scoreboard.h9
-rw-r--r--src/gpgpu-sim/shader.cc70
3 files changed, 102 insertions, 8 deletions
diff --git a/src/gpgpu-sim/scoreboard.cc b/src/gpgpu-sim/scoreboard.cc
index 44a9980..de71807 100644
--- a/src/gpgpu-sim/scoreboard.cc
+++ b/src/gpgpu-sim/scoreboard.cc
@@ -38,6 +38,7 @@ Scoreboard::Scoreboard(unsigned sid, unsigned n_warps, class gpgpu_t* gpu)
// Initialize size of table
reg_table.resize(n_warps);
longopregs.resize(n_warps);
+ sec_reg_table.resize(n_warps);
m_gpu = gpu;
}
@@ -152,3 +153,33 @@ bool Scoreboard::checkCollision(unsigned wid, const class inst_t* inst) const {
bool Scoreboard::pendingWrites(unsigned wid) const {
return !reg_table[wid].empty();
}
+
+// Secondary scoreboard methods for intra-warp co-issue
+
+void Scoreboard::reserveRegisterSecondary(unsigned wid, unsigned regnum) {
+ // No abort on duplicate — secondary may share register names with primary
+ sec_reg_table[wid].insert(regnum);
+}
+
+void Scoreboard::releaseRegisterSecondary(unsigned wid, unsigned regnum) {
+ sec_reg_table[wid].erase(regnum);
+}
+
+bool Scoreboard::checkCollisionSecondary(unsigned wid,
+ const inst_t *inst) const {
+ std::set<int> inst_regs;
+ for (unsigned i = 0; i < inst->outcount; i++) inst_regs.insert(inst->out[i]);
+ for (unsigned j = 0; j < inst->incount; j++) inst_regs.insert(inst->in[j]);
+ if (inst->pred > 0) inst_regs.insert(inst->pred);
+ if (inst->ar1 > 0) inst_regs.insert(inst->ar1);
+ if (inst->ar2 > 0) inst_regs.insert(inst->ar2);
+
+ for (auto it = inst_regs.begin(); it != inst_regs.end(); it++)
+ if (sec_reg_table[wid].find(*it) != sec_reg_table[wid].end())
+ return true;
+ return false;
+}
+
+void Scoreboard::clearSecondary(unsigned wid) {
+ sec_reg_table[wid].clear();
+}
diff --git a/src/gpgpu-sim/scoreboard.h b/src/gpgpu-sim/scoreboard.h
index 1fe0923..9bf51e6 100644
--- a/src/gpgpu-sim/scoreboard.h
+++ b/src/gpgpu-sim/scoreboard.h
@@ -50,6 +50,12 @@ class Scoreboard {
void printContents() const;
const bool islongop(unsigned warp_id, unsigned regnum);
+ // Secondary scoreboard for intra-warp co-issue (split-level tracking)
+ void reserveRegisterSecondary(unsigned wid, unsigned regnum);
+ void releaseRegisterSecondary(unsigned wid, unsigned regnum);
+ bool checkCollisionSecondary(unsigned wid, const inst_t *inst) const;
+ void clearSecondary(unsigned wid);
+
private:
void reserveRegister(unsigned wid, unsigned regnum);
int get_sid() const { return m_sid; }
@@ -62,6 +68,9 @@ class Scoreboard {
// Register that depend on a long operation (global, local or tex memory)
std::vector<std::set<unsigned> > longopregs;
+ // Secondary path register tracking (for I-Buffer half 1, intra-warp co-issue)
+ std::vector<std::set<unsigned> > sec_reg_table;
+
class gpgpu_t *m_gpu;
};
diff --git a/src/gpgpu-sim/shader.cc b/src/gpgpu-sim/shader.cc
index 43e86b6..bf6c9c0 100644
--- a/src/gpgpu-sim/shader.cc
+++ b/src/gpgpu-sim/shader.cc
@@ -1350,10 +1350,16 @@ void shader_core_ctx::co_issue_warp(warp_inst_t *composite,
if (split_id == (unsigned)-1) {
// Inter-warp co-issue: different warp_id, always safe to reserve
m_scoreboard->reserveRegisters(&temp_inst);
+ } else {
+ // Intra-warp co-issue: reserve on secondary scoreboard to track
+ // dependencies within the secondary split's instruction stream.
+ // Cannot use primary scoreboard (would abort on duplicate register names).
+ for (unsigned r = 0; r < MAX_OUTPUT_VALUES; r++) {
+ if (temp_inst.out[r] > 0) {
+ m_scoreboard->reserveRegisterSecondary(warp_id, temp_inst.out[r]);
+ }
+ }
}
- // For intra-warp: we skip reservation. The primary's reservation covers
- // shared registers, and non-shared registers will still be released at
- // writeback (the release tolerates releasing non-reserved registers).
// Set next PC for the co-issued warp
m_warp[warp_id]->set_next_pc(next_inst->pc + next_inst->isize);
@@ -1363,6 +1369,11 @@ void shader_core_ctx::co_issue_warp(warp_inst_t *composite,
// Merge the co-issued instruction's valid sets into the composite
composite->merge_simd_sets(temp_inst.get_simd_sets());
+
+ // Use max latency across all co-issued instructions for result bus reservation
+ if (temp_inst.latency > composite->latency) {
+ composite->latency = temp_inst.latency;
+ }
}
void shader_core_ctx::issue() {
@@ -1584,6 +1595,7 @@ void scheduler_unit::cycle() {
// control hazard
warp(warp_id).set_next_pc(pc);
warp(warp_id).ibuffer_flush();
+ m_scoreboard->clearSecondary(warp_id);
} else {
valid_inst = true;
if (!m_scoreboard->checkCollision(warp_id, pI)) {
@@ -1800,6 +1812,7 @@ void scheduler_unit::cycle() {
(*iter)->get_warp_id(), (*iter)->get_dynamic_warp_id());
warp(warp_id).set_next_pc(pc);
warp(warp_id).ibuffer_flush();
+ m_scoreboard->clearSecondary(warp_id);
}
if (warp_inst_issued) {
SCHED_DPRINTF(
@@ -2008,6 +2021,7 @@ void scheduler_unit::cycle() {
// Verify the split is still valid in the splits table
if (!m_shader->is_split_valid(primary_warp_id, sec_split_id)) {
warp(primary_warp_id).ibuffer_flush_half(1);
+ m_scoreboard->clearSecondary(primary_warp_id);
break;
}
@@ -2018,6 +2032,7 @@ void scheduler_unit::cycle() {
&split_mask);
if (split_pc != sec_inst->pc) {
warp(primary_warp_id).ibuffer_flush_half(1);
+ m_scoreboard->clearSecondary(primary_warp_id);
break;
}
@@ -2052,6 +2067,10 @@ void scheduler_unit::cycle() {
}
if (sec_fu_type != co_issue_fu_type) continue;
+ // Secondary scoreboard check: dependencies within secondary stream
+ if (m_scoreboard->checkCollisionSecondary(primary_warp_id, sec_inst))
+ continue;
+
// Compute sets needed
unsigned sec_active = sec_mask.count();
unsigned sec_sets_needed = (sec_active + set_width - 1) / set_width;
@@ -2080,6 +2099,7 @@ void scheduler_unit::cycle() {
if (!m_shader->is_split_valid(primary_warp_id, sec_split_id)) {
// Split was invalidated by the update — flush the entire secondary half
warp(primary_warp_id).ibuffer_flush_half(1);
+ m_scoreboard->clearSecondary(primary_warp_id);
break;
}
}
@@ -2541,8 +2561,14 @@ void shader_core_ctx::writeback() {
co_issued_warps_decremented.insert(sets[s].warp_id);
}
} else {
- // Intra-warp: dec once for the co-issued split (same warp_id)
+ // Intra-warp: release from secondary scoreboard and dec once
if (!intra_warp_decremented) {
+ for (unsigned r = 0; r < MAX_OUTPUT_VALUES; r++) {
+ if (sets[s].source_inst->out[r] > 0) {
+ m_scoreboard->releaseRegisterSecondary(
+ sets[s].warp_id, sets[s].source_inst->out[r]);
+ }
+ }
m_warp[warp_id]->dec_inst_in_pipeline();
intra_warp_decremented = true;
}
@@ -2554,6 +2580,35 @@ void shader_core_ctx::writeback() {
}
warp_inst_complete(*pipe_reg);
+
+ // Count additional co-issued instructions (not counted by warp_inst_complete)
+ if (m_config->gpgpu_simd_partitioning && pipe_reg->has_simd_sets()) {
+ const std::vector<simd_set_info> &sets = pipe_reg->get_simd_sets();
+ // Accumulate active threads per unique co-issued warp/split
+ std::map<unsigned, unsigned> inter_warp_threads; // warp_id -> thread count
+ unsigned intra_threads = 0;
+ for (unsigned s = 0; s < sets.size(); s++) {
+ if (!sets[s].valid || sets[s].source_inst == NULL) continue;
+ if (sets[s].warp_id != warp_id) {
+ inter_warp_threads[sets[s].warp_id] += sets[s].num_active_threads;
+ } else {
+ intra_threads += sets[s].num_active_threads;
+ }
+ }
+ // Inter-warp: one warp-instruction per unique co-issued warp
+ for (auto &entry : inter_warp_threads) {
+ m_stats->m_num_sim_winsn[m_sid]++;
+ m_gpu->gpu_sim_insn += entry.second;
+ m_stats->m_num_sim_insn[m_sid] += entry.second;
+ }
+ // Intra-warp: one warp-instruction for the co-issued split
+ if (intra_threads > 0) {
+ m_stats->m_num_sim_winsn[m_sid]++;
+ m_gpu->gpu_sim_insn += intra_threads;
+ m_stats->m_num_sim_insn[m_sid] += intra_threads;
+ }
+ }
+
m_gpu->gpu_sim_insn_last_update_sid = m_sid;
m_gpu->gpu_sim_insn_last_update = m_gpu->gpu_sim_cycle;
m_last_inst_gpu_sim_cycle = m_gpu->gpu_sim_cycle;
@@ -4985,8 +5040,7 @@ bool opndcoll_rfu_t::writeback(warp_inst_t &inst) {
const std::vector<simd_set_info> &sets = inst.get_simd_sets();
for (unsigned s = 0; s < sets.size(); s++) {
if (!sets[s].valid) continue;
- if (sets[s].warp_id == inst.warp_id()) continue;
- if (sets[s].source_inst == NULL) continue;
+ if (sets[s].source_inst == NULL) continue; // primary sets have NULL source_inst
for (unsigned op = 0; op < MAX_REG_OPERANDS; op++) {
int reg_num = sets[s].source_inst->arch_reg.dst[op];
@@ -5213,8 +5267,7 @@ bool opndcoll_rfu_t::collector_unit_t::allocate(register_set *pipeline_reg_set,
(*pipeline_reg)->get_simd_sets();
for (unsigned s = 0; s < sets.size(); s++) {
if (!sets[s].valid) continue;
- if (sets[s].warp_id == m_warp_id) continue;
- if (sets[s].source_inst == NULL) continue;
+ if (sets[s].source_inst == NULL) continue; // primary sets have NULL source_inst
std::vector<int> set_prev_regs;
for (unsigned op = 0; op < MAX_REG_OPERANDS && op_idx < MAX_REG_OPERANDS * 2; op++) {
@@ -5702,6 +5755,7 @@ void exec_shader_core_ctx::checkExecutionStatusAndUpdate(warp_inst_t &inst,
if (ptx_thread_done(tid)) {
m_warp[inst.warp_id()]->set_completed(t);
m_warp[inst.warp_id()]->ibuffer_flush();
+ m_scoreboard->clearSecondary(inst.warp_id());
}
// PC-Histogram Update