Skip to content

Commit b736680

Browse files
committed
[dv] Add assertions checking double_fault_seen_o
1 parent ddf56b3 commit b736680

File tree

1 file changed

+77
-0
lines changed

1 file changed

+77
-0
lines changed

rtl/ibex_top.sv

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1166,6 +1166,83 @@ module ibex_top import ibex_pkg::*; #(
11661166

11671167
// data_err_i relevant to both reads and writes. Check it isn't X on any response.
11681168
`ASSERT_KNOWN_IF(IbexDataRErrPayloadX, data_err_i, data_rvalid_i)
1169+
1170+
// Tracking logic and predictor for double_fault_seen_o output
1171+
1172+
// Returns 1'b1 if the provided instruction decodes to one that would write the sync_exc_bit of
1173+
// the CPUCTRLSTS CSR
1174+
function automatic logic insn_write_sync_exc_seen(logic [31:0] insn_bits);
1175+
return (insn_bits[6:0] == OPCODE_SYSTEM) &&
1176+
(insn_bits[14:12] inside {3'b001, 3'b010, 3'b011, 3'b101}) &&
1177+
(insn_bits[31:20] == CSR_CPUCTRLSTS);
1178+
endfunction
1179+
1180+
// Given an instruction that writes the sync_exc_bit of the CPUCTRLSTS CSR along with the value
1181+
// of the rs1 register read for that instruction and the current predicted sync_exc_bit bit
1182+
// return the new value of the sync_exc_bit after the instruction is executed.
1183+
function automatic logic new_sync_exc_bit(logic [31:0] insn_bits, logic [31:0] rs1,
1184+
logic cur_bit);
1185+
logic sync_exc_update_bit;
1186+
1187+
sync_exc_update_bit = insn_bits[14] ? 1'b0 : rs1[6];
1188+
1189+
case (insn_bits[13:12])
1190+
2'b01: return sync_exc_update_bit;
1191+
2'b10: return cur_bit | sync_exc_update_bit;
1192+
2'b11: return cur_bit & ~sync_exc_update_bit;
1193+
default: return 1'bx;
1194+
endcase
1195+
endfunction
1196+
1197+
localparam int DoubleFaultSeenLatency = 3;
1198+
logic [DoubleFaultSeenLatency-1:0] double_fault_seen_delay_buffer;
1199+
logic [DoubleFaultSeenLatency-2:0] double_fault_seen_delay_buffer_q;
1200+
logic sync_exc_seen;
1201+
logic new_sync_exc;
1202+
logic double_fault_seen_predicted;
1203+
1204+
assign new_sync_exc = rvfi_valid & rvfi_trap & ~rvfi_ext_debug_mode;
1205+
assign double_fault_seen_predicted = sync_exc_seen & new_sync_exc;
1206+
1207+
// Depending on whether the exception comes from the WB or ID/EX stage the precise timing of the
1208+
// double_fault_seen_o output vs the double fault instruction being visible on RVFI differs. At
1209+
// the earliest extreme it can be asserted the same cycle the instruction is visible on the
1210+
// RVFI. Buffer the last few cycles of double_fault_seen_o output for checking. We can
1211+
// guarantee the minimum spacing between double_fault_seen_o assertions (occurring when the
1212+
// first instruction of an exception handler continuously double faults with a single cycle
1213+
// memory access time) is sufficient that we'll only see a single bit set in the delay buffer.
1214+
assign double_fault_seen_delay_buffer = {double_fault_seen_delay_buffer_q, double_fault_seen_o};
1215+
1216+
always @(posedge clk_i or negedge rst_ni) begin
1217+
if (!rst_ni) begin
1218+
double_fault_seen_delay_buffer_q <= '0;
1219+
sync_exc_seen <= 1'b0;
1220+
end else begin
1221+
double_fault_seen_delay_buffer_q <=
1222+
double_fault_seen_delay_buffer[DoubleFaultSeenLatency-2:0];
1223+
1224+
if (new_sync_exc) begin
1225+
// Set flag when we see a new synchronous exception
1226+
sync_exc_seen <= 1'b1;
1227+
end else if (rvfi_valid && rvfi_insn == 32'h30200073) begin
1228+
// Clear flag when we see an MRET
1229+
sync_exc_seen <= 1'b0;
1230+
end else if (rvfi_valid && insn_write_sync_exc_seen(rvfi_insn)) begin
1231+
// Update predicted sync_exc_seen when the instruction modifies the relevant CPUCTRLSTS CSR
1232+
// bit.
1233+
sync_exc_seen <= new_sync_exc_bit(rvfi_insn, rvfi_rs1_rdata, sync_exc_seen);
1234+
end
1235+
end
1236+
end
1237+
1238+
// We should only have a single assertion of double_fault_seen in the delay buffer
1239+
`ASSERT(DoubleFaultSinglePulse, $onehot0(double_fault_seen_delay_buffer))
1240+
// If we predict a double_fault_seen_o we should see one in the delay buffer
1241+
`ASSERT(DoubleFaultPulseSeenOnDoubleFault,
1242+
double_fault_seen_predicted |-> |double_fault_seen_delay_buffer)
1243+
// If double_fault_seen_o is asserted we should see predict one occurring within a bounded time
1244+
`ASSERT(DoubleFaultPulseOnlyOnDoubleFault,
1245+
double_fault_seen_o |-> ##[0:DoubleFaultSeenLatency] double_fault_seen_predicted)
11691246
`endif
11701247

11711248
`ASSERT_KNOWN(IbexIrqX, {irq_software_i, irq_timer_i, irq_external_i, irq_fast_i, irq_nm_i})

0 commit comments

Comments
 (0)