Skip to content

Commit 7a6c591

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 4320827 commit 7a6c591

File tree

2 files changed

+110
-4
lines changed

2 files changed

+110
-4
lines changed

dv/cosim/spike_cosim.cc

Lines changed: 102 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,10 @@ 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),
43+
pending_iside_error(false),
44+
insn_cnt(0),
45+
pending_expanded_insn(0) {
4346
FILE *log_file = nullptr;
4447
if (trace_log_path.length() != 0) {
4548
log = std::make_unique<log_file_t>(trace_log_path.c_str());
@@ -215,7 +218,13 @@ bool SpikeCosim::step(uint32_t write_reg, uint32_t write_reg_data, uint32_t pc,
215218
// (If the current step causes a synchronous trap, it will be
216219
// recorded against the current pc)
217220
initial_spike_pc = (processor->get_state()->pc & 0xffffffff);
218-
processor->step(1);
221+
// Only step Spike if the current instruction is not an expanded one. Spike
222+
// does not expand instructions, so we have to consume a single Spike step
223+
// over multiple steps of Ibex. In the last Ibex step of the expanded
224+
// instruction, we step Spike.
225+
if (pending_expanded_insn == 0) {
226+
processor->step(1);
227+
}
219228

220229
// ISS
221230
// - If encountered an async trap,
@@ -310,15 +319,99 @@ bool SpikeCosim::step(uint32_t write_reg, uint32_t write_reg_data, uint32_t pc,
310319
suppressed_write_reg_data);
311320
}
312321

313-
if (!check_retired_instr(write_reg, write_reg_data, pc, suppress_reg_write)) {
314-
return false;
322+
if (expanded_insn) {
323+
if (!check_expanded_instr(write_reg, write_reg_data, pc, suppress_reg_write,
324+
expanded_insn)) {
325+
return false;
326+
}
327+
} else {
328+
if (!check_retired_instr(write_reg, write_reg_data, pc, suppress_reg_write,
329+
expanded_insn)) {
330+
return false;
331+
}
315332
}
316333

317334
// Only increment insn_cnt and return true if there are no errors
318335
insn_cnt++;
319336
return true;
320337
}
321338

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

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)