Skip to content

Commit 6cbaded

Browse files
committed
[dv] Support expanded instructions in cosim
Add a function and state to process multiple Ibex instructions that are modeled as a single instruction in riscv-isa-sim. The current function checks that the same registers are written overall.
1 parent 77fe555 commit 6cbaded

File tree

2 files changed

+106
-4
lines changed

2 files changed

+106
-4
lines changed

dv/cosim/spike_cosim.cc

Lines changed: 98 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ SpikeCosim::SpikeCosim(const std::string &isa_string, uint32_t start_pc,
3939
uint32_t pmp_num_regions, uint32_t pmp_granularity,
4040
uint32_t mhpm_counter_num, uint32_t dm_start_addr,
4141
uint32_t dm_end_addr)
42-
: nmi_mode(false), pending_iside_error(false), insn_cnt(0) {
42+
: nmi_mode(false), pending_iside_error(false), insn_cnt(0),
43+
pending_expanded_insn(0) {
4344
FILE *log_file = nullptr;
4445
if (trace_log_path.length() != 0) {
4546
log = std::make_unique<log_file_t>(trace_log_path.c_str());
@@ -215,7 +216,12 @@ bool SpikeCosim::step(uint32_t write_reg, uint32_t write_reg_data, uint32_t pc,
215216
// (If the current step causes a synchronous trap, it will be
216217
// recorded against the current pc)
217218
initial_spike_pc = (processor->get_state()->pc & 0xffffffff);
218-
processor->step(1);
219+
// Only step Spike if the current instruction is not an expanded one. Spike
220+
// does not expand instructions, so we have to consume a single Spike step
221+
// over multiple steps of Ibex.
222+
if (pending_expanded_insn == 0) {
223+
processor->step(1);
224+
}
219225

220226
// ISS
221227
// - If encountered an async trap,
@@ -310,15 +316,98 @@ bool SpikeCosim::step(uint32_t write_reg, uint32_t write_reg_data, uint32_t pc,
310316
suppressed_write_reg_data);
311317
}
312318

313-
if (!check_retired_instr(write_reg, write_reg_data, pc, suppress_reg_write)) {
314-
return false;
319+
if (expanded_insn) {
320+
if (!check_expanded_instr(write_reg, write_reg_data, pc, suppress_reg_write,
321+
expanded_insn)) {
322+
return false;
323+
}
324+
} else {
325+
if (!check_retired_instr(write_reg, write_reg_data, pc, suppress_reg_write,
326+
expanded_insn)) {
327+
return false;
328+
}
315329
}
316330

317331
// Only increment insn_cnt and return true if there are no errors
318332
insn_cnt++;
319333
return true;
320334
}
321335

336+
bool SpikeCosim::check_expanded_instr(uint32_t write_reg,
337+
uint32_t write_reg_data, uint32_t dut_pc,
338+
bool suppress_reg_write,
339+
uint32_t expanded_insn) {
340+
// If this is the first step of an expanded instruction, set up our expectations.
341+
if (!pending_expanded_insn) {
342+
pending_expanded_insn = expanded_insn;
343+
expanded_insn_pc = dut_pc;
344+
expanded_reg_changes.clear();
345+
346+
// The ISS has just stepped once for the entire expanded instruction.
347+
// We now collect all the GPR changes it produced and store them in a map
348+
auto &reg_changes = processor->get_state()->log_reg_write;
349+
for (auto reg_change : reg_changes) {
350+
// Check if it's a GPR write (type 0)
351+
if ((reg_change.first & 0xf) == 0) {
352+
uint32_t iss_write_reg = (reg_change.first >> 4) & 0x1f;
353+
// Ignore writes to x0
354+
if (iss_write_reg == 0) {
355+
continue;
356+
}
357+
uint32_t iss_write_data = reg_change.second.v[0];
358+
expanded_reg_changes[iss_write_reg] = iss_write_data;
359+
}
360+
}
361+
}
362+
363+
// TODO: We have to deal with suppressed writes here too
364+
if (suppress_reg_write) {
365+
return true;
366+
}
367+
368+
// If the DUT did not write a register, this is a valid micro-op (e.g., a
369+
// memory access or intermediate calculation). There is nothing to check.
370+
if (write_reg == 0) {
371+
return true;
372+
}
373+
374+
// The DUT wrote a register. Check if it matches one of our expectations.
375+
auto reg_change = expanded_reg_changes.find(write_reg);
376+
377+
// DUT wrote, but ISS didn't
378+
if (reg_change == expanded_reg_changes.end()) {
379+
std::stringstream err_str;
380+
err_str << "Expanded instruction at PC 0x" << std::hex << expanded_insn_pc
381+
<< ": DUT wrote to x" << std::dec << write_reg
382+
<< ", which was not an expected register write for this sequence.";
383+
errors.emplace_back(err_str.str());
384+
return false;
385+
}
386+
387+
// The register index matches. Now check if the data matches.
388+
uint32_t expected_data = reg_change->second;
389+
if (write_reg_data != expected_data) {
390+
std::stringstream err_str;
391+
err_str << "Expanded instruction at PC 0x" << std::hex << expanded_insn_pc
392+
<< ": Data mismatch for register x" << std::dec << write_reg
393+
<< ". DUT wrote: 0x" << std::hex << write_reg_data
394+
<< ", but ISS expected: 0x" << expected_data;
395+
errors.emplace_back(err_str.str());
396+
return false;
397+
}
398+
399+
// Match is perfect. Remove this from our set of expectations.
400+
expanded_reg_changes.erase(reg_change);
401+
402+
if(expanded_reg_changes.empty()) {
403+
// All expected register writes have been matched. Clear the pending state.
404+
pending_expanded_insn = 0;
405+
expanded_insn_pc = 0;
406+
}
407+
408+
return true;
409+
}
410+
322411
bool SpikeCosim::check_retired_instr(uint32_t write_reg,
323412
uint32_t write_reg_data, uint32_t dut_pc,
324413
bool suppress_reg_write,
@@ -335,6 +424,11 @@ bool SpikeCosim::check_retired_instr(uint32_t write_reg,
335424
<< " , but the ISS retired: " << std::hex
336425
<< (processor->get_state()->last_inst_pc & 0xffffffff);
337426
errors.emplace_back(err_str.str());
427+
if (pending_expanded_insn) {
428+
err_str << " (while processing expanded instruction at PC 0x"
429+
<< std::hex << expanded_insn_pc << ")";
430+
errors.emplace_back(err_str.str());
431+
}
338432
return false;
339433
}
340434

dv/cosim/spike_cosim.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,11 @@ class SpikeCosim : public simif_t, public Cosim {
100100

101101
unsigned int insn_cnt;
102102

103+
// Handle expanded insn
104+
uint32_t pending_expanded_insn;
105+
uint32_t expanded_insn_pc;
106+
std::map<uint32_t, uint32_t> expanded_reg_changes;
107+
103108
public:
104109
SpikeCosim(const std::string &isa_string, uint32_t start_pc,
105110
uint32_t start_mtvec, const std::string &trace_log_path,
@@ -127,6 +132,9 @@ class SpikeCosim : public simif_t, public Cosim {
127132
bool check_retired_instr(uint32_t write_reg, uint32_t write_reg_data,
128133
uint32_t dut_pc, bool suppress_reg_write,
129134
uint32_t expanded_insn);
135+
bool check_expanded_instr(uint32_t write_reg, uint32_t write_reg_data,
136+
uint32_t dut_pc, bool suppress_reg_write,
137+
uint32_t expanded_insn);
130138
bool check_sync_trap(uint32_t write_reg, uint32_t pc,
131139
uint32_t initial_spike_pc);
132140
void set_mip(uint32_t pre_mip, uint32_t post_mip) override;

0 commit comments

Comments
 (0)