[otbn,rtl] Make sure to latch "non-core" error bits I think I probably broke this in 75885e6. If you look at e.g. otbn_core.sv, you'll see that we have err_bits_q and err_bits_d and we latch errors until the next start signal. I thought that I'd done the same thing in otbn.sv for errors that don't come from the core, but I really hadn't! There, err_bits, err_bits_d and err_bits_q are used to make sure that we only update the (hwext) ERR_BITS register when a command finishes. That's fine for signals coming out of the core because (as mentioned above) we latch them there. But it isn't fine for signals generated at top-level. Note that, unlike the other "error bits" registers, we don't bother including a provision for clearing them. This is because all of the signals are fatal so we should never be able to start another operation (clearing the error bits) anyway. Signed-off-by: Rupert Swarbrick <rswarbrick@lowrisc.org>
diff --git a/hw/ip/otbn/rtl/otbn.sv b/hw/ip/otbn/rtl/otbn.sv index af6f26e..15f6ec6 100644 --- a/hw/ip/otbn/rtl/otbn.sv +++ b/hw/ip/otbn/rtl/otbn.sv
@@ -104,9 +104,11 @@ logic core_recoverable_err, recoverable_err_d, recoverable_err_q; mubi4_t core_escalate_en; - core_err_bits_t core_err_bits; - err_bits_t err_bits, err_bits_d, err_bits_q; - logic err_bits_en; + + core_err_bits_t core_err_bits; + non_core_err_bits_t non_core_err_bits, non_core_err_bits_d, non_core_err_bits_q; + err_bits_t err_bits, err_bits_d, err_bits_q; + logic err_bits_en; // ERR_BITS register should be cleared due to a write request from the host processor // when OTBN is not running. @@ -963,14 +965,32 @@ .sideload_key_shares_valid_i ({2{keymgr_key_i.valid}}) ); + // Collect up the error bits that don't come from the core itself and latch them so that they'll + // be available when an operation finishes. + assign non_core_err_bits = '{ + lifecycle_escalation: lc_escalate_en[0] != lc_ctrl_pkg::Off, + illegal_bus_access: illegal_bus_access_q, + bad_internal_state: otbn_scramble_state_error, + bus_intg_violation: bus_intg_violation + }; + + assign non_core_err_bits_d = non_core_err_bits_q | non_core_err_bits; + always @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + non_core_err_bits_q <= '0; + end else begin + non_core_err_bits_q <= non_core_err_bits_d; + end + end + // Construct a full set of error bits from the core output assign err_bits = '{ fatal_software: core_err_bits.fatal_software, - lifecycle_escalation: lc_escalate_en[0] != lc_ctrl_pkg::Off, - illegal_bus_access: illegal_bus_access_q, + lifecycle_escalation: non_core_err_bits_d.lifecycle_escalation, + illegal_bus_access: non_core_err_bits_d.illegal_bus_access, bad_internal_state: |{core_err_bits.bad_internal_state, - otbn_scramble_state_error}, - bus_intg_violation: bus_intg_violation, + non_core_err_bits_d.bad_internal_state}, + bus_intg_violation: non_core_err_bits_d.bus_intg_violation, reg_intg_violation: core_err_bits.reg_intg_violation, dmem_intg_violation: core_err_bits.dmem_intg_violation, imem_intg_violation: core_err_bits.imem_intg_violation, @@ -984,7 +1004,9 @@ // An error signal going down into the core to show that it should locally escalate assign core_escalate_en = mubi4_or_hi( - mubi4_bool_to_mubi(|{illegal_bus_access_q, otbn_scramble_state_error, bus_intg_violation}), + mubi4_bool_to_mubi(|{non_core_err_bits.illegal_bus_access, + non_core_err_bits.bad_internal_state, + non_core_err_bits.bus_intg_violation}), lc_ctrl_pkg::lc_to_mubi4(lc_escalate_en[1]) ); @@ -1021,8 +1043,9 @@ (hw2reg.status.d != StatusIdle) & dmem_rvalid_bus |-> dmem_rdata_bus == 'd0) // Error handling: if we pass an error signal down to the core then we should also be setting an - // error flag. - `ASSERT(ErrBitIfEscalate_A, mubi4_test_true_loose(core_escalate_en) |=> |err_bits_q) + // error flag. Note that this uses err_bits, not err_bits_q, because the latter signal only gets + // asserted when an operation finishes. + `ASSERT(ErrBitIfEscalate_A, mubi4_test_true_loose(core_escalate_en) |=> |err_bits) // Constraint from package, check here as we cannot have `ASSERT_INIT in package `ASSERT_INIT(WsrESizeMatchesParameter_A, $bits(wsr_e) == WsrNumWidth)
diff --git a/hw/ip/otbn/rtl/otbn_pkg.sv b/hw/ip/otbn/rtl/otbn_pkg.sv index 0ea4dde..017aae0 100644 --- a/hw/ip/otbn/rtl/otbn_pkg.sv +++ b/hw/ip/otbn/rtl/otbn_pkg.sv
@@ -122,6 +122,14 @@ logic bad_data_addr; } core_err_bits_t; + // The error signals that are generated outside of otbn_core + typedef struct packed { + logic lifecycle_escalation; + logic illegal_bus_access; + logic bad_internal_state; + logic bus_intg_violation; + } non_core_err_bits_t; + // Constants ===================================================================================== typedef enum logic {