[rtl,doc] Add double fault detection.

Fixes #1117
This commit is contained in:
Greg Chadwick 2022-01-24 18:46:46 +00:00 committed by Greg Chadwick
parent 97fa5cf280
commit c0f67946f2
11 changed files with 156 additions and 86 deletions

View file

@ -203,6 +203,8 @@ Interfaces
| ``debug_*`` | Debug interface, see :ref:`debug-support` |
+-------------------------+------------------------------------------------------------------------+
| ``crash_dump_o`` | A set of signals that can be captured on reset to aid crash debugging. |
+-------------------------+------------------------------------------------------------------------+
| ``double_fault_seen_o`` | A double fault was observed, see :ref:`double-fault-detect` |
+-------------------------+-------------------------+-----+----------------------------------------+
| ``fetch_enable_i`` | 1 | in | Allow the core to fetch instructions. |
| | | | If this bit is set low, the core will |

View file

@ -539,6 +539,14 @@ Other bit fields read as zero.
+-------+------+------------------------------------------------------------------+
| Bit# | R/W | Description |
+-------+------+------------------------------------------------------------------+
| 7 | RW | **double_fault_seen:** A synchronous exception was observed when |
| | | the ``sync_exc_seen`` field was set. This field must be manually |
| | | cleared, hardware only sets it (see :ref:`double-fault-detect`). |
+-------+------+------------------------------------------------------------------+
| 6 | RW | **sync_exc_seen:** A synchronous exception has been observed. |
| | | This flag is cleared when ``mret`` is executed. |
| | | (see :ref:`double-fault-detect`). |
+-------+------+------------------------------------------------------------------+
| 5:3 | WARL | **dummy_instr_mask:** Mask to control frequency of dummy |
| | | instruction insertion. If the core has not been configured with |
| | | security features (SecureIbex parameter == 0), this field will |

View file

@ -148,3 +148,19 @@ The purpose of the nonstandard ``mstack`` CSRs in Ibex is only to support recove
These CSRs are not accessible by software.
While handling an NMI, all interrupts are ignored independent of ``mstatus``.MIE.
Nested NMIs are not supported.
.. _double-fault-detect:
Double Fault Detection
----------------------
Ibex has a mechanism to detect when a double fault has occurred.
A double fault is defined as a synchronous exception occurring whilst handling a previous synchronous exception.
The ``cpuctrl`` custom CSR has fields to provide software visibility and access to this mechanism.
When a synchronous exception occurs, Ibex sets ``cpuctrl``.sync_exception_seen.
Ibex clears ``cpuctrl``.sync_exception_seen when ``mret`` is executed.
If a synchronous exception occurs whilst ``cpuctrl``.sync_exception_seen is set, a double fault has been detected.
When a double fault is detected, the ``double_fault_seen_o`` output is asserted for one cycle and ``cpuctrl``.double_fault_seen is set.
Note that writing the ``cpuctrl``.double_fault_seen field has no effect on the ``double_fault_seen_o`` output.

View file

@ -175,6 +175,7 @@ module ibex_riscv_compliance (
.debug_req_i ('b0 ),
.crash_dump_o ( ),
.double_fault_seen_o ( ),
.fetch_enable_i ('b1 ),
.alert_minor_o ( ),

View file

@ -127,6 +127,7 @@ module core_ibex_tb_top;
.debug_req_i (dut_if.debug_req ),
.crash_dump_o ( ),
.double_fault_seen_o ( ),
.fetch_enable_i (dut_if.fetch_enable ),
.alert_minor_o (dut_if.alert_minor ),

View file

@ -225,6 +225,7 @@ module ibex_simple_system (
.debug_req_i ('b0),
.crash_dump_o (),
.double_fault_seen_o (),
.fetch_enable_i ('b1),
.alert_minor_o (),

View file

@ -101,6 +101,7 @@ module ibex_core import ibex_pkg::*; #(
// Debug Interface
input logic debug_req_i,
output crash_dump_t crash_dump_o,
output logic double_fault_seen_o,
// RISC-V Formal Interface
// Does not comply with the coding standards of _i/_o suffixes, but follows
@ -973,6 +974,8 @@ module ibex_core import ibex_pkg::*; #(
.csr_mtval_i (csr_mtval),
.illegal_csr_insn_o(illegal_csr_insn_id),
.double_fault_seen_o,
// performance counter related signals
.instr_ret_i (perf_instr_ret_wb),
.instr_ret_compressed_i (perf_instr_ret_compressed_wb),

View file

@ -104,6 +104,7 @@ module ibex_cs_registers #(
output logic illegal_csr_insn_o, // access to non-existent CSR,
// with wrong priviledge level, or
// missing write permissions
output logic double_fault_seen_o,
// Performance Counters
input logic instr_ret_i, // instr retired in ID/EX stage
input logic instr_ret_compressed_i, // compressed instr retired
@ -175,6 +176,8 @@ module ibex_cs_registers #(
// CPU control register fields
typedef struct packed {
logic double_fault_seen;
logic sync_exc_seen;
logic [2:0] dummy_instr_mask;
logic dummy_instr_en;
logic data_ind_timing;
@ -251,7 +254,7 @@ module ibex_cs_registers #(
logic [31:0] tmatch_value_rdata;
// CPU control bits
cpu_ctrl_t cpuctrl_q, cpuctrl_d, cpuctrl_wdata;
cpu_ctrl_t cpuctrl_q, cpuctrl_d, cpuctrl_wdata_raw, cpuctrl_wdata;
logic cpuctrl_we;
logic cpuctrl_err;
@ -537,6 +540,9 @@ module ibex_cs_registers #(
mhpmcounterh_we = '0;
cpuctrl_we = 1'b0;
cpuctrl_d = cpuctrl_q;
double_fault_seen_o = 1'b0;
if (csr_we_int) begin
unique case (csr_addr_i)
@ -635,7 +641,10 @@ module ibex_cs_registers #(
mhpmcounterh_we[mhpmcounter_idx] = 1'b1;
end
CSR_CPUCTRL: cpuctrl_we = 1'b1;
CSR_CPUCTRL: begin
cpuctrl_d = cpuctrl_wdata;
cpuctrl_we = 1'b1;
end
default:;
endcase
@ -685,6 +694,16 @@ module ibex_cs_registers #(
mcause_d = {csr_mcause_i};
// save previous status for recoverable NMI
mstack_en = 1'b1;
if (!csr_mcause_i[5]) begin
cpuctrl_we = 1'b1;
cpuctrl_d.sync_exc_seen = 1'b1;
if (cpuctrl_q.sync_exc_seen) begin
double_fault_seen_o = 1'b1;
cpuctrl_d.double_fault_seen = 1'b1;
end
end
end
end // csr_save_cause_i
@ -697,6 +716,9 @@ module ibex_cs_registers #(
mstatus_en = 1'b1;
mstatus_d.mie = mstatus_q.mpie; // re-enable interrupts
cpuctrl_we = 1'b1;
cpuctrl_d.sync_exc_seen = 1'b0;
if (nmi_mode_i) begin
// when returning from an NMI restore state from mstack CSR
mstatus_d.mpie = mstack_q.mpie;
@ -1476,27 +1498,27 @@ module ibex_cs_registers #(
//////////////////////////
// Cast register write data
assign cpuctrl_wdata = cpu_ctrl_t'(csr_wdata_int[$bits(cpu_ctrl_t)-1:0]);
assign cpuctrl_wdata_raw = cpu_ctrl_t'(csr_wdata_int[$bits(cpu_ctrl_t)-1:0]);
// Generate fixed time execution bit
if (DataIndTiming) begin : gen_dit
assign cpuctrl_d.data_ind_timing = cpuctrl_wdata.data_ind_timing;
assign cpuctrl_wdata.data_ind_timing = cpuctrl_wdata_raw.data_ind_timing;
end else begin : gen_no_dit
// tieoff for the unused bit
logic unused_dit;
assign unused_dit = cpuctrl_wdata.data_ind_timing;
assign unused_dit = cpuctrl_wdata_raw.data_ind_timing;
// field will always read as zero if not configured
assign cpuctrl_d.data_ind_timing = 1'b0;
assign cpuctrl_wdata.data_ind_timing = 1'b0;
end
assign data_ind_timing_o = cpuctrl_q.data_ind_timing;
// Generate dummy instruction signals
if (DummyInstructions) begin : gen_dummy
assign cpuctrl_d.dummy_instr_en = cpuctrl_wdata.dummy_instr_en;
assign cpuctrl_d.dummy_instr_mask = cpuctrl_wdata.dummy_instr_mask;
assign cpuctrl_wdata.dummy_instr_en = cpuctrl_wdata_raw.dummy_instr_en;
assign cpuctrl_wdata.dummy_instr_mask = cpuctrl_wdata_raw.dummy_instr_mask;
// Signal a write to the seed register
assign dummy_instr_seed_en_o = csr_we_int && (csr_addr == CSR_SECURESEED);
@ -1506,12 +1528,12 @@ module ibex_cs_registers #(
// tieoff for the unused bit
logic unused_dummy_en;
logic [2:0] unused_dummy_mask;
assign unused_dummy_en = cpuctrl_wdata.dummy_instr_en;
assign unused_dummy_mask = cpuctrl_wdata.dummy_instr_mask;
assign unused_dummy_en = cpuctrl_wdata_raw.dummy_instr_en;
assign unused_dummy_mask = cpuctrl_wdata_raw.dummy_instr_mask;
// field will always read as zero if not configured
assign cpuctrl_d.dummy_instr_en = 1'b0;
assign cpuctrl_d.dummy_instr_mask = 3'b000;
assign cpuctrl_wdata.dummy_instr_en = 1'b0;
assign cpuctrl_wdata.dummy_instr_mask = 3'b000;
assign dummy_instr_seed_en_o = 1'b0;
assign dummy_instr_seed_o = '0;
end
@ -1521,16 +1543,19 @@ module ibex_cs_registers #(
// Generate icache enable bit
if (ICache) begin : gen_icache_enable
assign cpuctrl_d.icache_enable = cpuctrl_wdata.icache_enable;
assign cpuctrl_wdata.icache_enable = cpuctrl_wdata_raw.icache_enable;
end else begin : gen_no_icache
// tieoff for the unused icen bit
logic unused_icen;
assign unused_icen = cpuctrl_wdata.icache_enable;
assign unused_icen = cpuctrl_wdata_raw.icache_enable;
// icen field will always read as zero if ICache not configured
assign cpuctrl_d.icache_enable = 1'b0;
assign cpuctrl_wdata.icache_enable = 1'b0;
end
assign cpuctrl_wdata.double_fault_seen = cpuctrl_wdata_raw.double_fault_seen;
assign cpuctrl_wdata.sync_exc_seen = cpuctrl_wdata_raw.sync_exc_seen;
assign icache_enable_o = cpuctrl_q.icache_enable;
ibex_csr #(

View file

@ -92,6 +92,7 @@ module ibex_lockstep import ibex_pkg::*; #(
input logic debug_req_i,
input crash_dump_t crash_dump_i,
input logic double_fault_seen_i,
input logic fetch_enable_i,
output logic alert_minor_o,
@ -294,6 +295,7 @@ module ibex_lockstep import ibex_pkg::*; #(
logic [LineSizeECC-1:0] ic_data_wdata;
logic irq_pending;
crash_dump_t crash_dump;
logic double_fault_seen;
logic icache_inval;
logic core_busy;
} delayed_outputs_t;
@ -303,31 +305,32 @@ module ibex_lockstep import ibex_pkg::*; #(
delayed_outputs_t shadow_outputs_d, shadow_outputs_q;
// Assign core outputs to the structure
assign core_outputs_in.instr_req = instr_req_i;
assign core_outputs_in.instr_addr = instr_addr_i;
assign core_outputs_in.data_req = data_req_i;
assign core_outputs_in.data_we = data_we_i;
assign core_outputs_in.data_be = data_be_i;
assign core_outputs_in.data_addr = data_addr_i;
assign core_outputs_in.data_wdata = data_wdata_i;
assign core_outputs_in.dummy_instr_id = dummy_instr_id_i;
assign core_outputs_in.rf_raddr_a = rf_raddr_a_i;
assign core_outputs_in.rf_raddr_b = rf_raddr_b_i;
assign core_outputs_in.rf_waddr_wb = rf_waddr_wb_i;
assign core_outputs_in.rf_we_wb = rf_we_wb_i;
assign core_outputs_in.rf_wdata_wb_ecc = rf_wdata_wb_ecc_i;
assign core_outputs_in.ic_tag_req = ic_tag_req_i;
assign core_outputs_in.ic_tag_write = ic_tag_write_i;
assign core_outputs_in.ic_tag_addr = ic_tag_addr_i;
assign core_outputs_in.ic_tag_wdata = ic_tag_wdata_i;
assign core_outputs_in.ic_data_req = ic_data_req_i;
assign core_outputs_in.ic_data_write = ic_data_write_i;
assign core_outputs_in.ic_data_addr = ic_data_addr_i;
assign core_outputs_in.ic_data_wdata = ic_data_wdata_i;
assign core_outputs_in.irq_pending = irq_pending_i;
assign core_outputs_in.crash_dump = crash_dump_i;
assign core_outputs_in.icache_inval = icache_inval_i;
assign core_outputs_in.core_busy = core_busy_i;
assign core_outputs_in.instr_req = instr_req_i;
assign core_outputs_in.instr_addr = instr_addr_i;
assign core_outputs_in.data_req = data_req_i;
assign core_outputs_in.data_we = data_we_i;
assign core_outputs_in.data_be = data_be_i;
assign core_outputs_in.data_addr = data_addr_i;
assign core_outputs_in.data_wdata = data_wdata_i;
assign core_outputs_in.dummy_instr_id = dummy_instr_id_i;
assign core_outputs_in.rf_raddr_a = rf_raddr_a_i;
assign core_outputs_in.rf_raddr_b = rf_raddr_b_i;
assign core_outputs_in.rf_waddr_wb = rf_waddr_wb_i;
assign core_outputs_in.rf_we_wb = rf_we_wb_i;
assign core_outputs_in.rf_wdata_wb_ecc = rf_wdata_wb_ecc_i;
assign core_outputs_in.ic_tag_req = ic_tag_req_i;
assign core_outputs_in.ic_tag_write = ic_tag_write_i;
assign core_outputs_in.ic_tag_addr = ic_tag_addr_i;
assign core_outputs_in.ic_tag_wdata = ic_tag_wdata_i;
assign core_outputs_in.ic_data_req = ic_data_req_i;
assign core_outputs_in.ic_data_write = ic_data_write_i;
assign core_outputs_in.ic_data_addr = ic_data_addr_i;
assign core_outputs_in.ic_data_wdata = ic_data_wdata_i;
assign core_outputs_in.irq_pending = irq_pending_i;
assign core_outputs_in.crash_dump = crash_dump_i;
assign core_outputs_in.double_fault_seen = double_fault_seen_i;
assign core_outputs_in.icache_inval = icache_inval_i;
assign core_outputs_in.core_busy = core_busy_i;
// Delay the outputs
always_ff @(posedge clk_i) begin
@ -372,59 +375,60 @@ module ibex_lockstep import ibex_pkg::*; #(
.DmHaltAddr ( DmHaltAddr ),
.DmExceptionAddr ( DmExceptionAddr )
) u_shadow_core (
.clk_i (clk_i),
.rst_ni (rst_shadow_n),
.clk_i (clk_i),
.rst_ni (rst_shadow_n),
.hart_id_i (hart_id_i),
.boot_addr_i (boot_addr_i),
.hart_id_i (hart_id_i),
.boot_addr_i (boot_addr_i),
.instr_req_o (shadow_outputs_d.instr_req),
.instr_gnt_i (shadow_inputs_q[0].instr_gnt),
.instr_rvalid_i (shadow_inputs_q[0].instr_rvalid),
.instr_addr_o (shadow_outputs_d.instr_addr),
.instr_rdata_i (shadow_inputs_q[0].instr_rdata),
.instr_err_i (shadow_inputs_q[0].instr_err),
.instr_req_o (shadow_outputs_d.instr_req),
.instr_gnt_i (shadow_inputs_q[0].instr_gnt),
.instr_rvalid_i (shadow_inputs_q[0].instr_rvalid),
.instr_addr_o (shadow_outputs_d.instr_addr),
.instr_rdata_i (shadow_inputs_q[0].instr_rdata),
.instr_err_i (shadow_inputs_q[0].instr_err),
.data_req_o (shadow_outputs_d.data_req),
.data_gnt_i (shadow_inputs_q[0].data_gnt),
.data_rvalid_i (shadow_inputs_q[0].data_rvalid),
.data_we_o (shadow_outputs_d.data_we),
.data_be_o (shadow_outputs_d.data_be),
.data_addr_o (shadow_outputs_d.data_addr),
.data_wdata_o (shadow_outputs_d.data_wdata),
.data_rdata_i (shadow_inputs_q[0].data_rdata),
.data_err_i (shadow_inputs_q[0].data_err),
.data_req_o (shadow_outputs_d.data_req),
.data_gnt_i (shadow_inputs_q[0].data_gnt),
.data_rvalid_i (shadow_inputs_q[0].data_rvalid),
.data_we_o (shadow_outputs_d.data_we),
.data_be_o (shadow_outputs_d.data_be),
.data_addr_o (shadow_outputs_d.data_addr),
.data_wdata_o (shadow_outputs_d.data_wdata),
.data_rdata_i (shadow_inputs_q[0].data_rdata),
.data_err_i (shadow_inputs_q[0].data_err),
.dummy_instr_id_o (shadow_outputs_d.dummy_instr_id),
.rf_raddr_a_o (shadow_outputs_d.rf_raddr_a),
.rf_raddr_b_o (shadow_outputs_d.rf_raddr_b),
.rf_waddr_wb_o (shadow_outputs_d.rf_waddr_wb),
.rf_we_wb_o (shadow_outputs_d.rf_we_wb),
.rf_wdata_wb_ecc_o (shadow_outputs_d.rf_wdata_wb_ecc),
.rf_rdata_a_ecc_i (shadow_inputs_q[0].rf_rdata_a_ecc),
.rf_rdata_b_ecc_i (shadow_inputs_q[0].rf_rdata_b_ecc),
.dummy_instr_id_o (shadow_outputs_d.dummy_instr_id),
.rf_raddr_a_o (shadow_outputs_d.rf_raddr_a),
.rf_raddr_b_o (shadow_outputs_d.rf_raddr_b),
.rf_waddr_wb_o (shadow_outputs_d.rf_waddr_wb),
.rf_we_wb_o (shadow_outputs_d.rf_we_wb),
.rf_wdata_wb_ecc_o (shadow_outputs_d.rf_wdata_wb_ecc),
.rf_rdata_a_ecc_i (shadow_inputs_q[0].rf_rdata_a_ecc),
.rf_rdata_b_ecc_i (shadow_inputs_q[0].rf_rdata_b_ecc),
.ic_tag_req_o (shadow_outputs_d.ic_tag_req),
.ic_tag_write_o (shadow_outputs_d.ic_tag_write),
.ic_tag_addr_o (shadow_outputs_d.ic_tag_addr),
.ic_tag_wdata_o (shadow_outputs_d.ic_tag_wdata),
.ic_tag_rdata_i (shadow_tag_rdata_q[0]),
.ic_data_req_o (shadow_outputs_d.ic_data_req),
.ic_data_write_o (shadow_outputs_d.ic_data_write),
.ic_data_addr_o (shadow_outputs_d.ic_data_addr),
.ic_data_wdata_o (shadow_outputs_d.ic_data_wdata),
.ic_data_rdata_i (shadow_data_rdata_q[0]),
.ic_scr_key_valid_i (shadow_inputs_q[0].ic_scr_key_valid),
.ic_tag_req_o (shadow_outputs_d.ic_tag_req),
.ic_tag_write_o (shadow_outputs_d.ic_tag_write),
.ic_tag_addr_o (shadow_outputs_d.ic_tag_addr),
.ic_tag_wdata_o (shadow_outputs_d.ic_tag_wdata),
.ic_tag_rdata_i (shadow_tag_rdata_q[0]),
.ic_data_req_o (shadow_outputs_d.ic_data_req),
.ic_data_write_o (shadow_outputs_d.ic_data_write),
.ic_data_addr_o (shadow_outputs_d.ic_data_addr),
.ic_data_wdata_o (shadow_outputs_d.ic_data_wdata),
.ic_data_rdata_i (shadow_data_rdata_q[0]),
.ic_scr_key_valid_i (shadow_inputs_q[0].ic_scr_key_valid),
.irq_software_i (shadow_inputs_q[0].irq_software),
.irq_timer_i (shadow_inputs_q[0].irq_timer),
.irq_external_i (shadow_inputs_q[0].irq_external),
.irq_fast_i (shadow_inputs_q[0].irq_fast),
.irq_nm_i (shadow_inputs_q[0].irq_nm),
.irq_pending_o (shadow_outputs_d.irq_pending),
.irq_software_i (shadow_inputs_q[0].irq_software),
.irq_timer_i (shadow_inputs_q[0].irq_timer),
.irq_external_i (shadow_inputs_q[0].irq_external),
.irq_fast_i (shadow_inputs_q[0].irq_fast),
.irq_nm_i (shadow_inputs_q[0].irq_nm),
.irq_pending_o (shadow_outputs_d.irq_pending),
.debug_req_i (shadow_inputs_q[0].debug_req),
.crash_dump_o (shadow_outputs_d.crash_dump),
.debug_req_i (shadow_inputs_q[0].debug_req),
.crash_dump_o (shadow_outputs_d.crash_dump),
.double_fault_seen_o (shadow_outputs_d.double_fault_seen),
`ifdef RVFI
.rvfi_valid (),

View file

@ -87,6 +87,7 @@ module ibex_top import ibex_pkg::*; #(
// Debug Interface
input logic debug_req_i,
output crash_dump_t crash_dump_o,
output logic double_fault_seen_o,
// RISC-V Formal Interface
// Does not comply with the coding standards of _i/_o suffixes, but follows
@ -287,6 +288,7 @@ module ibex_top import ibex_pkg::*; #(
.debug_req_i,
.crash_dump_o,
.double_fault_seen_o,
`ifdef RVFI
.rvfi_valid,
@ -592,6 +594,7 @@ module ibex_top import ibex_pkg::*; #(
irq_pending,
debug_req_i,
crash_dump_o,
double_fault_seen_o,
fetch_enable_i,
icache_inval,
core_busy_d
@ -650,6 +653,7 @@ module ibex_top import ibex_pkg::*; #(
logic debug_req_local;
crash_dump_t crash_dump_local;
logic double_fault_seen_local;
logic fetch_enable_local;
logic icache_inval_local;
@ -700,6 +704,7 @@ module ibex_top import ibex_pkg::*; #(
irq_pending,
debug_req_i,
crash_dump_o,
double_fault_seen_o,
fetch_enable_i,
icache_inval,
core_busy_d
@ -750,6 +755,7 @@ module ibex_top import ibex_pkg::*; #(
irq_pending_local,
debug_req_local,
crash_dump_local,
double_fault_seen_local,
fetch_enable_local,
icache_inval_local,
core_busy_local
@ -860,6 +866,7 @@ module ibex_top import ibex_pkg::*; #(
.debug_req_i (debug_req_local),
.crash_dump_i (crash_dump_local),
.double_fault_seen_i(double_fault_seen_local),
.fetch_enable_i (fetch_enable_local),
.alert_minor_o (lockstep_alert_minor_local),

View file

@ -80,6 +80,7 @@ module ibex_top_tracing import ibex_pkg::*; #(
// Debug Interface
input logic debug_req_i,
output crash_dump_t crash_dump_o,
output logic double_fault_seen_o,
// CPU Control Signals
input logic fetch_enable_i,
@ -201,6 +202,7 @@ module ibex_top_tracing import ibex_pkg::*; #(
.debug_req_i,
.crash_dump_o,
.double_fault_seen_o,
.rvfi_valid,
.rvfi_order,