-
Notifications
You must be signed in to change notification settings - Fork 630
Mem intg testing #1907
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Mem intg testing #1907
Changes from all commits
ec0b034
931b358
3c12971
ec36d5d
cd641e2
85d3072
69fea40
cf7b1a1
64c2bec
70638e7
53ed7bd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -160,9 +160,8 @@ bool SpikeCosim::backdoor_read_mem(uint32_t addr, size_t len, | |
// - If we catch a trap_t&, then the take_trap() fn updates the state of the | ||
// processor, and when we call step() again we start executing in the new | ||
// context of the trap (trap andler, new MSTATUS, debug rom, etc. etc.) | ||
bool SpikeCosim::step(uint32_t write_reg, uint32_t write_reg_data, | ||
uint32_t pc, | ||
bool sync_trap) { | ||
bool SpikeCosim::step(uint32_t write_reg, uint32_t write_reg_data, uint32_t pc, | ||
bool sync_trap, bool suppress_reg_write) { | ||
assert(write_reg < 32); | ||
|
||
// The DUT has just produced an RVFI item | ||
|
@@ -180,8 +179,28 @@ bool SpikeCosim::step(uint32_t write_reg, uint32_t write_reg_data, | |
} | ||
|
||
uint32_t initial_spike_pc; | ||
uint32_t suppressed_write_reg; | ||
uint32_t suppressed_write_reg_data; | ||
bool pending_sync_exception = false; | ||
|
||
if (suppress_reg_write) { | ||
// If Ibex suppressed a register write (which occurs when a load gets data | ||
// with bad integrity) record the state of the destination register before | ||
// we do the stop, so we can restore it after the step (as spike won't | ||
// suppressed the register write). | ||
// | ||
// First check retired instruciton to ensure load suppression is correct | ||
if (!check_suppress_reg_write(write_reg, pc, suppressed_write_reg)) { | ||
return false; | ||
} | ||
|
||
// The check gives us the destination register the instruction would have | ||
// written to (write_reg will be 0 to indicate to write). Record the | ||
// contents of that register. | ||
suppressed_write_reg_data = | ||
processor->get_state()->XPR[suppressed_write_reg]; | ||
} | ||
|
||
// Before stepping Spike, record the current spike pc. | ||
// (If the current step causes a synchronous trap, it will be | ||
// recorded against the current pc) | ||
|
@@ -211,7 +230,8 @@ bool SpikeCosim::step(uint32_t write_reg, uint32_t write_reg_data, | |
|
||
if (processor->get_state()->last_inst_pc == PC_INVALID) { | ||
if (!(processor->get_state()->mcause->read() & 0x80000000) || | ||
processor->get_state()->debug_mode) { // (Async-Traps are disabled in debug mode) | ||
processor->get_state() | ||
->debug_mode) { // (Async-Traps are disabled in debug mode) | ||
// Spike encountered a synchronous trap | ||
pending_sync_exception = true; | ||
|
||
|
@@ -275,7 +295,13 @@ bool SpikeCosim::step(uint32_t write_reg, uint32_t write_reg_data, | |
} | ||
pending_iside_error = false; | ||
|
||
if (!check_retired_instr(write_reg, write_reg_data, pc)) { | ||
if (suppress_reg_write) { | ||
// If we suppressed a register write restore the old register state now | ||
processor->get_state()->XPR.write(suppressed_write_reg, | ||
suppressed_write_reg_data); | ||
} | ||
|
||
if (!check_retired_instr(write_reg, write_reg_data, pc, suppress_reg_write)) { | ||
return false; | ||
} | ||
|
||
|
@@ -284,8 +310,9 @@ bool SpikeCosim::step(uint32_t write_reg, uint32_t write_reg_data, | |
return true; | ||
} | ||
|
||
bool SpikeCosim::check_retired_instr(uint32_t write_reg, uint32_t write_reg_data, | ||
uint32_t dut_pc) { | ||
bool SpikeCosim::check_retired_instr(uint32_t write_reg, | ||
uint32_t write_reg_data, uint32_t dut_pc, | ||
bool suppress_reg_write) { | ||
// Check the retired instruction and all of its side-effects match those from | ||
// the DUT | ||
|
||
|
@@ -319,7 +346,8 @@ bool SpikeCosim::check_retired_instr(uint32_t write_reg, uint32_t write_reg_data | |
// should never see more than one GPR write per step | ||
assert(!gpr_write_seen); | ||
|
||
if (!check_gpr_write(reg_change, write_reg, write_reg_data)) { | ||
if (!suppress_reg_write && | ||
!check_gpr_write(reg_change, write_reg, write_reg_data)) { | ||
return false; | ||
} | ||
|
||
|
@@ -376,6 +404,12 @@ bool SpikeCosim::check_sync_trap(uint32_t write_reg, | |
return false; | ||
} | ||
|
||
// If we see an internal NMI, that means we receive an extra memory intf item. | ||
// Deleting that is necessary since next Load/Store would fail otherwise. | ||
if (processor->get_state()->mcause->read() == 0xFFFFFFE0) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why don't we check the |
||
pending_dside_accesses.erase(pending_dside_accesses.begin()); | ||
} | ||
|
||
// Errors may have been generated outside of step() (e.g. in | ||
// check_mem_access()), return false if there are any. | ||
if (errors.size() != 0) { | ||
|
@@ -425,6 +459,31 @@ bool SpikeCosim::check_gpr_write(const commit_log_reg_t::value_type ®_change, | |
return true; | ||
} | ||
|
||
bool SpikeCosim::check_suppress_reg_write(uint32_t write_reg, uint32_t pc, | ||
uint32_t &suppressed_write_reg) { | ||
if (write_reg != 0) { | ||
std::stringstream err_str; | ||
err_str << "Instruction at " << std::hex << pc | ||
<< " indicated a suppressed register write but wrote to x" | ||
<< std::dec << write_reg; | ||
errors.emplace_back(err_str.str()); | ||
|
||
return false; | ||
} | ||
|
||
if (!pc_is_load(pc, suppressed_write_reg)) { | ||
std::stringstream err_str; | ||
err_str << "Instruction at " << std::hex << pc | ||
<< " indicated a suppressed register write is it not a load" | ||
" only loads can suppress register writes"; | ||
errors.emplace_back(err_str.str()); | ||
|
||
return false; | ||
} | ||
|
||
return true; | ||
} | ||
|
||
void SpikeCosim::on_csr_write(const commit_log_reg_t::value_type ®_change) { | ||
int cosim_write_csr = (reg_change.first >> 4) & 0xfff; | ||
|
||
|
@@ -528,6 +587,20 @@ void SpikeCosim::set_nmi(bool nmi) { | |
} | ||
} | ||
|
||
void SpikeCosim::set_nmi_int(bool nmi_int) { | ||
if (nmi_int && !nmi_mode && !processor->get_state()->debug_mode) { | ||
processor->get_state()->nmi_int = true; | ||
nmi_mode = true; | ||
|
||
// When NMI is set it is guaranteed NMI trap will be taken at the next step | ||
// so save CSR state for recoverable NMI to mstack now. | ||
mstack.mpp = get_field(processor->get_csr(CSR_MSTATUS), MSTATUS_MPP); | ||
mstack.mpie = get_field(processor->get_csr(CSR_MSTATUS), MSTATUS_MPIE); | ||
mstack.epc = processor->get_csr(CSR_MEPC); | ||
mstack.cause = processor->get_csr(CSR_MCAUSE); | ||
} | ||
} | ||
|
||
void SpikeCosim::set_debug_req(bool debug_req) { | ||
processor->halt_request = | ||
debug_req ? processor_t::HR_REGULAR : processor_t::HR_NONE; | ||
|
@@ -892,4 +965,44 @@ bool SpikeCosim::check_debug_ebreak(uint32_t write_reg, uint32_t pc, | |
return true; | ||
} | ||
|
||
bool SpikeCosim::pc_is_load(uint32_t pc, uint32_t &rd_out) { | ||
uint16_t insn_16; | ||
|
||
if (!backdoor_read_mem(pc, 2, reinterpret_cast<uint8_t *>(&insn_16))) { | ||
return false; | ||
} | ||
|
||
// C.LW | ||
if ((insn_16 & 0xE003) == 0x4000) { | ||
rd_out = ((insn_16 >> 2) & 0x7) + 8; | ||
return true; | ||
} | ||
|
||
// C.LWSP | ||
if ((insn_16 & 0xE003) == 0x4002) { | ||
rd_out = (insn_16 >> 7) & 0x1F; | ||
return rd_out != 0; | ||
} | ||
|
||
uint16_t insn_32; | ||
|
||
if (!backdoor_read_mem(pc, 4, reinterpret_cast<uint8_t *>(&insn_32))) { | ||
return false; | ||
} | ||
|
||
// LB/LH/LW/LBU/LHU | ||
if ((insn_32 & 0x7F) == 0x3) { | ||
uint32_t func = (insn_32 >> 12) & 0x7; | ||
if ((func == 0x3) || (func == 0x6) || (func == 0x7)) { | ||
// Not valid load encodings | ||
return false; | ||
} | ||
|
||
rd_out = (insn_32 >> 7) & 0x1F; | ||
return true; | ||
} | ||
|
||
return false; | ||
} | ||
Comment on lines
+968
to
+1006
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't like that this replicates decode logic, although I don't have a concrete alternative in mind. |
||
|
||
unsigned int SpikeCosim::get_insn_cnt() { return insn_cnt; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't there a way to stop the write in the first place instead of undoing it?