Skip to content

Commit 6e8e68b

Browse files
committed
[cosim] Add write suppress support
When Ibex does a load that receives data with bad integrity it suppresses the write to the destination register. The implements matching functionality for cosim.
1 parent 64c2bec commit 6e8e68b

16 files changed

+157
-16
lines changed

dv/cosim/cosim.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ class Cosim {
7272
//
7373
// Returns false if there are any errors; use `get_errors` to obtain details
7474
virtual bool step(uint32_t write_reg, uint32_t write_reg_data, uint32_t pc,
75-
bool sync_trap) = 0;
75+
bool sync_trap, bool suppress_reg_write) = 0;
7676

7777
// When more than one of `set_mip`, `set_nmi` or `set_debug_req` is called
7878
// before `step` which one takes effect is chosen by the co-simulator. Which

dv/cosim/cosim_dpi.cc

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,13 @@
1010

1111
int riscv_cosim_step(Cosim *cosim, const svBitVecVal *write_reg,
1212
const svBitVecVal *write_reg_data, const svBitVecVal *pc,
13-
svBit sync_trap) {
13+
svBit sync_trap, svBit suppress_reg_write) {
1414
assert(cosim);
1515

16-
return cosim->step(write_reg[0], write_reg_data[0], pc[0], sync_trap) ? 1 : 0;
16+
return cosim->step(write_reg[0], write_reg_data[0], pc[0], sync_trap,
17+
suppress_reg_write)
18+
? 1
19+
: 0;
1720
}
1821

1922
void riscv_cosim_set_mip(Cosim *cosim, const svBitVecVal *mip) {

dv/cosim/cosim_dpi.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
extern "C" {
1515
int riscv_cosim_step(Cosim *cosim, const svBitVecVal *write_reg,
1616
const svBitVecVal *write_reg_data, const svBitVecVal *pc,
17-
svBit sync_trap);
17+
svBit sync_trap, svBit suppress_reg_write);
1818
void riscv_cosim_set_mip(Cosim *cosim, const svBitVecVal *mip);
1919
void riscv_cosim_set_nmi(Cosim *cosim, svBit nmi);
2020
void riscv_cosim_set_nmi_int(Cosim *cosim, svBit nmi_int);

dv/cosim/cosim_dpi.svh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
`define COSIM_DPI_SVH
1212

1313
import "DPI-C" function int riscv_cosim_step(chandle cosim_handle, bit [4:0] write_reg,
14-
bit [31:0] write_reg_data, bit [31:0] pc, bit sync_trap);
14+
bit [31:0] write_reg_data, bit [31:0] pc, bit sync_trap, bit suppress_reg_write);
1515
import "DPI-C" function void riscv_cosim_set_mip(chandle cosim_handle, bit [31:0] mip);
1616
import "DPI-C" function void riscv_cosim_set_nmi(chandle cosim_handle, bit nmi);
1717
import "DPI-C" function void riscv_cosim_set_nmi_int(chandle cosim_handle, bit nmi_int);

dv/cosim/spike_cosim.cc

Lines changed: 99 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -160,9 +160,8 @@ bool SpikeCosim::backdoor_read_mem(uint32_t addr, size_t len,
160160
// - If we catch a trap_t&, then the take_trap() fn updates the state of the
161161
// processor, and when we call step() again we start executing in the new
162162
// context of the trap (trap andler, new MSTATUS, debug rom, etc. etc.)
163-
bool SpikeCosim::step(uint32_t write_reg, uint32_t write_reg_data,
164-
uint32_t pc,
165-
bool sync_trap) {
163+
bool SpikeCosim::step(uint32_t write_reg, uint32_t write_reg_data, uint32_t pc,
164+
bool sync_trap, bool suppress_reg_write) {
166165
assert(write_reg < 32);
167166

168167
// The DUT has just produced an RVFI item
@@ -180,8 +179,28 @@ bool SpikeCosim::step(uint32_t write_reg, uint32_t write_reg_data,
180179
}
181180

182181
uint32_t initial_spike_pc;
182+
uint32_t suppressed_write_reg;
183+
uint32_t suppressed_write_reg_data;
183184
bool pending_sync_exception = false;
184185

186+
if (suppress_reg_write) {
187+
// If Ibex suppressed a register write (which occurs when a load gets data
188+
// with bad integrity) record the state of the destination register before
189+
// we do the stop, so we can restore it after the step (as spike won't
190+
// suppressed the register write).
191+
//
192+
// First check retired instruciton to ensure load suppression is correct
193+
if (!check_suppress_reg_write(write_reg, pc, suppressed_write_reg)) {
194+
return false;
195+
}
196+
197+
// The check gives us the destination register the instruction would have
198+
// written to (write_reg will be 0 to indicate to write). Record the
199+
// contents of that register.
200+
suppressed_write_reg_data =
201+
processor->get_state()->XPR[suppressed_write_reg];
202+
}
203+
185204
// Before stepping Spike, record the current spike pc.
186205
// (If the current step causes a synchronous trap, it will be
187206
// recorded against the current pc)
@@ -276,7 +295,13 @@ bool SpikeCosim::step(uint32_t write_reg, uint32_t write_reg_data,
276295
}
277296
pending_iside_error = false;
278297

279-
if (!check_retired_instr(write_reg, write_reg_data, pc)) {
298+
if (suppress_reg_write) {
299+
// If we suppressed a register write restore the old register state now
300+
processor->get_state()->XPR.write(suppressed_write_reg,
301+
suppressed_write_reg_data);
302+
}
303+
304+
if (!check_retired_instr(write_reg, write_reg_data, pc, suppress_reg_write)) {
280305
return false;
281306
}
282307

@@ -285,8 +310,9 @@ bool SpikeCosim::step(uint32_t write_reg, uint32_t write_reg_data,
285310
return true;
286311
}
287312

288-
bool SpikeCosim::check_retired_instr(uint32_t write_reg, uint32_t write_reg_data,
289-
uint32_t dut_pc) {
313+
bool SpikeCosim::check_retired_instr(uint32_t write_reg,
314+
uint32_t write_reg_data, uint32_t dut_pc,
315+
bool suppress_reg_write) {
290316
// Check the retired instruction and all of its side-effects match those from
291317
// the DUT
292318

@@ -320,7 +346,8 @@ bool SpikeCosim::check_retired_instr(uint32_t write_reg, uint32_t write_reg_data
320346
// should never see more than one GPR write per step
321347
assert(!gpr_write_seen);
322348

323-
if (!check_gpr_write(reg_change, write_reg, write_reg_data)) {
349+
if (!suppress_reg_write &&
350+
!check_gpr_write(reg_change, write_reg, write_reg_data)) {
324351
return false;
325352
}
326353

@@ -432,6 +459,31 @@ bool SpikeCosim::check_gpr_write(const commit_log_reg_t::value_type &reg_change,
432459
return true;
433460
}
434461

462+
bool SpikeCosim::check_suppress_reg_write(uint32_t write_reg, uint32_t pc,
463+
uint32_t &suppressed_write_reg) {
464+
if (write_reg != 0) {
465+
std::stringstream err_str;
466+
err_str << "Instruction at " << std::hex << pc
467+
<< " indicated a suppressed register write but wrote to x"
468+
<< std::dec << write_reg;
469+
errors.emplace_back(err_str.str());
470+
471+
return false;
472+
}
473+
474+
if (!pc_is_load(pc, suppressed_write_reg)) {
475+
std::stringstream err_str;
476+
err_str << "Instruction at " << std::hex << pc
477+
<< " indicated a suppressed register write is it not a load"
478+
" only loads can suppress register writes";
479+
errors.emplace_back(err_str.str());
480+
481+
return false;
482+
}
483+
484+
return true;
485+
}
486+
435487
void SpikeCosim::on_csr_write(const commit_log_reg_t::value_type &reg_change) {
436488
int cosim_write_csr = (reg_change.first >> 4) & 0xfff;
437489

@@ -913,4 +965,44 @@ bool SpikeCosim::check_debug_ebreak(uint32_t write_reg, uint32_t pc,
913965
return true;
914966
}
915967

968+
bool SpikeCosim::pc_is_load(uint32_t pc, uint32_t &rd_out) {
969+
uint16_t insn_16;
970+
971+
if (!backdoor_read_mem(pc, 2, reinterpret_cast<uint8_t *>(&insn_16))) {
972+
return false;
973+
}
974+
975+
// C.LW
976+
if ((insn_16 & 0xE003) == 0x4000) {
977+
rd_out = ((insn_16 >> 2) & 0x7) + 8;
978+
return true;
979+
}
980+
981+
// C.LWSP
982+
if ((insn_16 & 0xE003) == 0x4002) {
983+
rd_out = (insn_16 >> 7) & 0x1F;
984+
return rd_out != 0;
985+
}
986+
987+
uint16_t insn_32;
988+
989+
if (!backdoor_read_mem(pc, 4, reinterpret_cast<uint8_t *>(&insn_32))) {
990+
return false;
991+
}
992+
993+
// LB/LH/LW/LBU/LHU
994+
if ((insn_32 & 0x7F) == 0x3) {
995+
uint32_t func = (insn_32 >> 12) & 0x7;
996+
if ((func == 0x3) || (func == 0x6) || (func == 0x7)) {
997+
// Not valid load encodings
998+
return false;
999+
}
1000+
1001+
rd_out = (insn_32 >> 7) & 0x1F;
1002+
return true;
1003+
}
1004+
1005+
return false;
1006+
}
1007+
9161008
unsigned int SpikeCosim::get_insn_cnt() { return insn_cnt; }

dv/cosim/spike_cosim.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,17 @@ class SpikeCosim : public simif_t, public Cosim {
6161
const uint8_t *bytes);
6262

6363
bool pc_is_mret(uint32_t pc);
64+
bool pc_is_load(uint32_t pc, uint32_t &rd_out);
6465

6566
bool pc_is_debug_ebreak(uint32_t pc);
6667
bool check_debug_ebreak(uint32_t write_reg, uint32_t pc, bool sync_trap);
6768

6869
bool check_gpr_write(const commit_log_reg_t::value_type &reg_change,
6970
uint32_t write_reg, uint32_t write_reg_data);
7071

72+
bool check_suppress_reg_write(uint32_t write_reg, uint32_t pc,
73+
uint32_t &suppressed_write_reg);
74+
7175
void on_csr_write(const commit_log_reg_t::value_type &reg_change);
7276

7377
void leave_nmi_mode();
@@ -101,10 +105,10 @@ class SpikeCosim : public simif_t, public Cosim {
101105
const uint8_t *data_in) override;
102106
bool backdoor_read_mem(uint32_t addr, size_t len, uint8_t *data_out) override;
103107
bool step(uint32_t write_reg, uint32_t write_reg_data, uint32_t pc,
104-
bool sync_trap) override;
108+
bool sync_trap, bool suppress_reg_write) override;
105109

106110
bool check_retired_instr(uint32_t write_reg, uint32_t write_reg_data,
107-
uint32_t pc);
111+
uint32_t dut_pc, bool suppress_reg_write);
108112
bool check_sync_trap(uint32_t write_reg, uint32_t pc,
109113
uint32_t initial_spike_pc);
110114
void set_mip(uint32_t mip) override;

dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_cosim_scoreboard.sv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ class ibex_cosim_scoreboard extends uvm_scoreboard;
135135
riscv_cosim_set_ic_scr_key_valid(cosim_handle, rvfi_instr.ic_scr_key_valid);
136136

137137
if (!riscv_cosim_step(cosim_handle, rvfi_instr.rd_addr, rvfi_instr.rd_wdata, rvfi_instr.pc,
138-
rvfi_instr.trap)) begin
138+
rvfi_instr.trap, rvfi_instr.rf_wr_suppress)) begin
139139
// cosim instruction step doesn't match rvfi captured instruction, report a fatal error
140140
// with the details
141141
if (cfg.relax_cosim_check) begin

dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_rvfi_monitor.sv

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ class ibex_rvfi_monitor extends uvm_monitor;
4040
trans_collected.nmi = vif.monitor_cb.ext_nmi;
4141
trans_collected.nmi_int = vif.monitor_cb.ext_nmi_int;
4242
trans_collected.debug_req = vif.monitor_cb.ext_debug_req;
43+
trans_collected.rf_wr_suppress = vif.monitor_cb.ext_rf_wr_suppress;
4344
trans_collected.mcycle = vif.monitor_cb.ext_mcycle;
4445
trans_collected.ic_scr_key_valid = vif.monitor_cb.ext_ic_scr_key_valid;
4546

dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_rvfi_seq_item.sv

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ class ibex_rvfi_seq_item extends uvm_sequence_item;
1212
bit nmi;
1313
bit nmi_int;
1414
bit debug_req;
15+
bit rf_wr_suppress;
1516
bit [63:0] mcycle;
1617

1718
bit [31:0] mhpmcounters [10];
@@ -28,6 +29,7 @@ class ibex_rvfi_seq_item extends uvm_sequence_item;
2829
`uvm_field_int (nmi, UVM_DEFAULT)
2930
`uvm_field_int (nmi_int, UVM_DEFAULT)
3031
`uvm_field_int (debug_req, UVM_DEFAULT)
32+
`uvm_field_int (rf_wr_suppress, UVM_DEFAULT)
3133
`uvm_field_int (mcycle, UVM_DEFAULT)
3234
`uvm_field_sarray_int (mhpmcounters, UVM_DEFAULT)
3335
`uvm_field_sarray_int (mhpmcountersh, UVM_DEFAULT)

dv/uvm/core_ibex/env/core_ibex_rvfi_if.sv

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ interface core_ibex_rvfi_if(input logic clk);
3030
logic ext_nmi;
3131
logic ext_nmi_int;
3232
logic [31:0] ext_debug_req;
33+
logic [31:0] ext_rf_wr_suppress;
3334
logic [63:0] ext_mcycle;
3435

3536
logic [31:0] ext_mhpmcounters [10];
@@ -64,6 +65,7 @@ interface core_ibex_rvfi_if(input logic clk);
6465
input ext_nmi;
6566
input ext_nmi_int;
6667
input ext_debug_req;
68+
input ext_rf_wr_suppress;
6769
input ext_mcycle;
6870
input ext_mhpmcounters;
6971
input ext_mhpmcountersh;

dv/uvm/core_ibex/tb/core_ibex_tb_top.sv

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ module core_ibex_tb_top;
194194
assign rvfi_if.ext_nmi = dut.rvfi_ext_nmi;
195195
assign rvfi_if.ext_nmi_int = dut.rvfi_ext_nmi_int;
196196
assign rvfi_if.ext_debug_req = dut.rvfi_ext_debug_req;
197+
assign rvfi_if.ext_rf_wr_suppress = dut.rvfi_ext_rf_wr_suppress;
197198
assign rvfi_if.ext_mcycle = dut.rvfi_ext_mcycle;
198199
assign rvfi_if.ext_mhpmcounters = dut.rvfi_ext_mhpmcounters;
199200
assign rvfi_if.ext_mhpmcountersh = dut.rvfi_ext_mhpmcountersh;

dv/verilator/simple_system_cosim/ibex_simple_system_cosim_checker.sv

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ module ibex_simple_system_cosim_checker #(
5656
riscv_cosim_set_ic_scr_key_valid(cosim_handle, u_top.rvfi_ext_ic_scr_key_valid);
5757

5858
if (riscv_cosim_step(cosim_handle, u_top.rvfi_rd_addr, u_top.rvfi_rd_wdata,
59-
u_top.rvfi_pc_rdata, u_top.rvfi_trap) == 0)
59+
u_top.rvfi_pc_rdata, u_top.rvfi_trap,
60+
u_top.rvfi_ext_rf_wr_suppress) == 0)
6061
begin
6162
$display("FAILURE: Co-simulation mismatch at time %t", $time());
6263
for (int i = 0;i < riscv_cosim_get_num_errors(cosim_handle); ++i) begin

rtl/ibex_core.sv

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ module ibex_core import ibex_pkg::*; #(
141141
output logic rvfi_ext_nmi,
142142
output logic rvfi_ext_nmi_int,
143143
output logic rvfi_ext_debug_req,
144+
output logic rvfi_ext_rf_wr_suppress,
144145
output logic [63:0] rvfi_ext_mcycle,
145146
output logic [31:0] rvfi_ext_mhpmcounters [10],
146147
output logic [31:0] rvfi_ext_mhpmcountersh [10],
@@ -1348,6 +1349,12 @@ module ibex_core import ibex_pkg::*; #(
13481349
assign rvfi_trap_wb = id_stage_i.controller_i.exc_req_lsu;
13491350
// WB is instantly done in the tracking pipeline when a trap is progress through the pipeline
13501351
assign rvfi_wb_done = rvfi_stage_valid[0] & (instr_done_wb | rvfi_stage_trap[0]);
1352+
1353+
1354+
logic rvfi_stage_rf_wr_suppress_wb_q;
1355+
always_ff @(posedge clk_i or negedge rst_ni) begin
1356+
1357+
end
13511358
end else begin : gen_rvfi_no_wb_stage
13521359
// Without writeback stage first RVFI stage is output stage so simply valid the cycle after
13531360
// instruction leaves ID/EX (and so has retired)
@@ -1676,6 +1683,27 @@ module ibex_core import ibex_pkg::*; #(
16761683
end
16771684
end
16781685

1686+
if (WritebackStage) begin : g_rvfi_rf_wr_suppress_wb
1687+
logic rvfi_stage_rf_wr_suppress_wb;
1688+
logic rvfi_rf_wr_suppress_wb;
1689+
1690+
// Set when RF write from load data is suppressed due to an integrity error
1691+
assign rvfi_rf_wr_suppress_wb =
1692+
instr_done_wb & ~rf_we_wb_o & outstanding_load_wb & lsu_load_resp_intg_err;
1693+
1694+
always@(posedge clk_i or negedge rst_ni) begin
1695+
if (!rst_ni) begin
1696+
rvfi_stage_rf_wr_suppress_wb <= 1'b0;
1697+
end else if (rvfi_wb_done) begin
1698+
rvfi_stage_rf_wr_suppress_wb <= rvfi_rf_wr_suppress_wb;
1699+
end
1700+
end
1701+
1702+
assign rvfi_ext_rf_wr_suppress = rvfi_stage_rf_wr_suppress_wb;
1703+
end else begin : g_rvfi_no_rf_wr_suppress_wb
1704+
assign rvfi_ext_rf_wr_suppress = 1'b0;
1705+
end
1706+
16791707
// rvfi_intr must be set for first instruction that is part of a trap handler.
16801708
// On the first cycle of a new instruction see if a trap PC was set by the previous instruction,
16811709
// otherwise maintain value.

rtl/ibex_lockstep.sv

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,7 @@ module ibex_lockstep import ibex_pkg::*; #(
431431
.rvfi_ext_nmi (),
432432
.rvfi_ext_nmi_int (),
433433
.rvfi_ext_debug_req (),
434+
.rvfi_ext_rf_wr_suppress (),
434435
.rvfi_ext_mcycle (),
435436
.rvfi_ext_mhpmcounters (),
436437
.rvfi_ext_mhpmcountersh (),

rtl/ibex_top.sv

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ module ibex_top import ibex_pkg::*; #(
120120
output logic rvfi_ext_nmi,
121121
output logic rvfi_ext_nmi_int,
122122
output logic rvfi_ext_debug_req,
123+
output logic rvfi_ext_rf_wr_suppress,
123124
output logic [63:0] rvfi_ext_mcycle,
124125
output logic [31:0] rvfi_ext_mhpmcounters [10],
125126
output logic [31:0] rvfi_ext_mhpmcountersh [10],
@@ -390,6 +391,7 @@ module ibex_top import ibex_pkg::*; #(
390391
.rvfi_ext_nmi,
391392
.rvfi_ext_nmi_int,
392393
.rvfi_ext_debug_req,
394+
.rvfi_ext_rf_wr_suppress,
393395
.rvfi_ext_mcycle,
394396
.rvfi_ext_mhpmcounters,
395397
.rvfi_ext_mhpmcountersh,

0 commit comments

Comments
 (0)