[otbn,fcov] Extend coverage for Bad Internal error
Signed-off-by: Canberk Topal <ctopal@lowrisc.org>
diff --git a/hw/ip/otbn/dv/tracer/rtl/otbn_trace_if.sv b/hw/ip/otbn/dv/tracer/rtl/otbn_trace_if.sv
index 501d7ba..f84092b 100644
--- a/hw/ip/otbn/dv/tracer/rtl/otbn_trace_if.sv
+++ b/hw/ip/otbn/dv/tracer/rtl/otbn_trace_if.sv
@@ -83,9 +83,20 @@
input logic [1:0][otbn_pkg::SideloadKeyWidth-1:0] sideload_key_shares_i,
input logic secure_wipe_req,
- input logic secure_wipe_ack
+ input logic secure_wipe_ack,
+
+ input logic locking_o,
+ input logic urnd_all_zero,
+ input logic insn_addr_err,
+ input logic alu_bignum_predec_error,
+ input logic mac_bignum_predec_error,
+ input logic ispr_predec_error,
+ input logic controller_predec_error,
+ input logic rf_bignum_predec_error,
+ input logic rd_predec_error
);
import otbn_pkg::*;
+ import prim_mubi_pkg::*;
localparam int DmemSubWordAddrWidth = prim_util_pkg::vbits(WLEN/8);
@@ -324,6 +335,75 @@
end
end
+ // Bad Internal State Probes
+ // We need to capture them until we are actually locking to sample them in the correct instance.
+ predec_err_t predec_err_i, predec_err_d, predec_err_q;
+ start_stop_bad_int_t start_stop_bad_int_i, start_stop_bad_int_d, start_stop_bad_int_q;
+ controller_bad_int_t controller_bad_int_i, controller_bad_int_d, controller_bad_int_q;
+ missed_gnt_t missed_gnt_i, missed_gnt_d, missed_gnt_q;
+ logic scramble_state_err_i, scramble_state_err_d, scramble_state_err_q;
+ logic urnd_all_zero_d, urnd_all_zero_q;
+ logic insn_addr_err_d, insn_addr_err_q;
+
+ assign missed_gnt_d = (locking_o) ? '0 : (missed_gnt_q | missed_gnt_i);
+ assign predec_err_d = (locking_o) ? '0 : (predec_err_q | predec_err_i);
+ assign urnd_all_zero_d = (locking_o) ? '0 : (urnd_all_zero_q | urnd_all_zero);
+ assign insn_addr_err_d = (locking_o) ? '0 : (insn_addr_err_q | insn_addr_err);
+ assign start_stop_bad_int_d = (locking_o) ? '0 : (start_stop_bad_int_q | start_stop_bad_int_i);
+ assign controller_bad_int_d = (locking_o) ? '0 : (controller_bad_int_q | controller_bad_int_i);
+ assign scramble_state_err_d = (locking_o) ? '0 : (scramble_state_err_q | scramble_state_err_i);
+
+ always_ff @(posedge clk_i or negedge rst_ni) begin
+ if (!rst_ni) begin
+ missed_gnt_q <= '0;
+ predec_err_q <= '0;
+ urnd_all_zero_q <= '0;
+ insn_addr_err_q <= '0;
+ start_stop_bad_int_q <= '0;
+ controller_bad_int_q <= '0;
+ scramble_state_err_q <= '0;
+ end else begin
+ missed_gnt_q <= missed_gnt_d;
+ predec_err_q <= predec_err_d;
+ urnd_all_zero_q <= urnd_all_zero_d;
+ insn_addr_err_q <= insn_addr_err_d;
+ scramble_state_err_q <= scramble_state_err_d;
+ start_stop_bad_int_q <= start_stop_bad_int_d;
+ controller_bad_int_q <= controller_bad_int_d;
+ end
+ end
+
+ assign predec_err_i.alu_bignum_err = alu_bignum_predec_error && insn_fetch_resp_valid;
+ assign predec_err_i.mac_bignum_err = mac_bignum_predec_error && insn_fetch_resp_valid;
+ assign predec_err_i.ispr_bignum_err = ispr_predec_error;
+ assign predec_err_i.controller_err = controller_predec_error;
+ assign predec_err_i.rf_err = rf_bignum_predec_error;
+ assign predec_err_i.rd_err = rd_predec_error;
+
+ assign start_stop_bad_int_i.state_err = u_otbn_start_stop_control.state_error;
+ assign start_stop_bad_int_i.spr_urnd_acks = u_otbn_start_stop_control.spurious_urnd_ack_error;
+ assign start_stop_bad_int_i.spr_secwipe_reqs = u_otbn_start_stop_control.secure_wipe_error_q;
+
+ assign start_stop_bad_int_i.mubi_rma_err = u_otbn_start_stop_control.mubi_err_d &&
+ prim_mubi_pkg::mubi4_test_invalid(u_otbn_start_stop_control.rma_ack_q);
+ assign start_stop_bad_int_i.mubi_urnd_err = u_otbn_start_stop_control.mubi_err_d &&
+ prim_mubi_pkg::mubi4_test_invalid(u_otbn_start_stop_control.wipe_after_urnd_refresh_q);
+
+ assign controller_bad_int_i.loop_hw_cnt_err =
+ (|u_otbn_controller.u_otbn_loop_controller.loop_counter_err);
+
+ assign controller_bad_int_i.loop_hw_stack_cnt_err =
+ u_otbn_controller.u_otbn_loop_controller.loop_stack_cnt_err;
+
+ assign controller_bad_int_i.loop_hw_intg_err =
+ ((|u_otbn_controller.u_otbn_loop_controller.current_loop_intg_err) &&
+ u_otbn_controller.u_otbn_loop_controller.current_loop_valid);
+
+ assign controller_bad_int_i.rf_base_call_stack_err =
+ u_otbn_controller.rf_base_call_stack_hw_err_i;
+ assign controller_bad_int_i.spr_secwipe_acks = u_otbn_controller.spurious_secure_wipe_ack_q;
+ assign controller_bad_int_i.state_err = u_otbn_controller.state_error;
+
endinterface
`endif // SYNTHESIS
diff --git a/hw/ip/otbn/dv/uvm/env/otbn_env_cov.sv b/hw/ip/otbn/dv/uvm/env/otbn_env_cov.sv
index 31172a8..dd1d7fa 100644
--- a/hw/ip/otbn/dv/uvm/env/otbn_env_cov.sv
+++ b/hw/ip/otbn/dv/uvm/env/otbn_env_cov.sv
@@ -622,10 +622,53 @@
clr_C_cross: cross fg_cp, clr_C_cp;
endgroup
+ // This covergroup tracks each possible "Bad Internal State" fatal error
+ // cause using probes from RTL.
+ covergroup bad_internal_state_cg
+ with function sample(logic urnd_all_zero,
+ logic insn_addr_err,
+ logic scramble_state_err,
+ otbn_pkg::predec_err_t predec_err,
+ otbn_pkg::missed_gnt_t missed_gnt,
+ otbn_pkg::controller_bad_int_t controller_bad_int,
+ otbn_pkg::start_stop_bad_int_t start_stop_bad_int);
+
+ `DEF_SEEN_CP(alu_bignum_predec_err, predec_err.alu_bignum_err)
+ `DEF_SEEN_CP(mac_bignum_predec_err, predec_err.mac_bignum_err)
+ `DEF_SEEN_CP(ispr_bignum_predec_err, predec_err.ispr_bignum_err)
+ `DEF_SEEN_CP(controller_predec_err, predec_err.controller_err)
+ `DEF_SEEN_CP(rf_predec_err, predec_err.rf_err)
+ `DEF_SEEN_CP(rd_predec_err, predec_err.rd_err)
+
+ `DEF_SEEN_CP(spr_urnd_acks_cp, start_stop_bad_int.spr_urnd_acks)
+ `DEF_SEEN_CP(spr_secwipe_reqs_cp, start_stop_bad_int.spr_secwipe_reqs)
+ `DEF_SEEN_CP(mubi_rma_err_cp, start_stop_bad_int.mubi_rma_err)
+ `DEF_SEEN_CP(mubi_urnd_err_cp, start_stop_bad_int.mubi_urnd_err)
+ `DEF_SEEN_CP(start_stop_state_err_cp, start_stop_bad_int.state_err)
+
+ `DEF_SEEN_CP(loop_hw_cnt_err_cp, controller_bad_int.loop_hw_cnt_err)
+ `DEF_SEEN_CP(loop_hw_stack_cnt_err_cp, controller_bad_int.loop_hw_stack_cnt_err)
+ `DEF_SEEN_CP(loop_hw_intg_err_cp, controller_bad_int.loop_hw_intg_err)
+ `DEF_SEEN_CP(rf_base_call_stack_err_cp, controller_bad_int.rf_base_call_stack_err)
+ `DEF_SEEN_CP(spr_secwipe_acks_cp, controller_bad_int.spr_secwipe_acks)
+ `DEF_SEEN_CP(controller_state_err_cp, controller_bad_int.state_err)
+
+ `DEF_SEEN_CP(imem_gnt_missed_err_cp, missed_gnt.imem_gnt_missed_err)
+ `DEF_SEEN_CP(dmem_gnt_missed_err_cp, missed_gnt.dmem_gnt_missed_err)
+
+ `DEF_SEEN_CP(urnd_all_zero_cp, urnd_all_zero)
+ `DEF_SEEN_CP(insn_addr_err_cp, insn_addr_err)
+ `DEF_SEEN_CP(scramble_state_err_cp, scramble_state_err)
+
+ endgroup
+
+ // This covergroup tracks straight-line instructions (i.e., instructions that do not
+ // branch or jump) at the top of instruction memory (i.e., at the highest possible
+ // address).
covergroup insn_addr_cg with function sample(mnem_str_t mnemonic, logic [31:0] insn_addr);
`DEF_SEEN_CP(str_insn_at_top_cp, (insn_addr == ImemSizeByte - 4) &&
- !(mnemonic inside {mnem_beq, mnem_bne, mnem_jal, mnem_jalr,
- mnem_ecall, mnem_loop, mnem_loopi}))
+ !(mnemonic inside {mnem_beq, mnem_bne, mnem_jal, mnem_jalr,
+ mnem_ecall, mnem_loop, mnem_loopi}))
endgroup
@@ -2050,6 +2093,7 @@
promoted_err_cg = new;
scratchpad_writes_cg = new;
+ bad_internal_state_cg = new;
insn_addr_cg = new;
call_stack_cg = new;
flag_write_cg = new;
@@ -2176,6 +2220,15 @@
function void on_state_change(operational_state_e new_state);
last_err_bits = 0;
last_mnem = '0;
+ // Sample bad internal state related signals here because otherwise they tend to not get
+ // captured.
+ bad_internal_state_cg.sample(cfg.trace_vif.urnd_all_zero_q,
+ cfg.trace_vif.insn_addr_err_q,
+ cfg.trace_vif.scramble_state_err_q,
+ cfg.trace_vif.predec_err_q,
+ cfg.trace_vif.missed_gnt_q,
+ cfg.trace_vif.controller_bad_int_q,
+ cfg.trace_vif.start_stop_bad_int_q);
endfunction
function void on_write_to_wr_csr(uvm_reg csr, logic [31:0] data, operational_state_e state);
diff --git a/hw/ip/otbn/dv/uvm/tb.sv b/hw/ip/otbn/dv/uvm/tb.sv
index 5fdfb79..098444b 100644
--- a/hw/ip/otbn/dv/uvm/tb.sv
+++ b/hw/ip/otbn/dv/uvm/tb.sv
@@ -157,6 +157,10 @@
.DmemAddrWidth (DmemAddrWidth)
) i_otbn_trace_if (.*);
+ assign dut.u_otbn_core.i_otbn_trace_if.scramble_state_err_i = dut.otbn_scramble_state_error;
+ assign dut.u_otbn_core.i_otbn_trace_if.missed_gnt_i.imem_gnt_missed_err = dut.imem_missed_gnt;
+ assign dut.u_otbn_core.i_otbn_trace_if.missed_gnt_i.dmem_gnt_missed_err = dut.dmem_missed_gnt;
+
bind dut.u_otbn_core otbn_tracer u_otbn_tracer(.*, .otbn_trace(i_otbn_trace_if));
bind dut.u_otbn_core.u_otbn_controller.u_otbn_loop_controller
diff --git a/hw/ip/otbn/dv/verilator/otbn_top_sim.sv b/hw/ip/otbn/dv/verilator/otbn_top_sim.sv
index e173e12..25be925 100644
--- a/hw/ip/otbn/dv/verilator/otbn_top_sim.sv
+++ b/hw/ip/otbn/dv/verilator/otbn_top_sim.sv
@@ -173,6 +173,9 @@
bind otbn_core otbn_trace_if #(.ImemAddrWidth, .DmemAddrWidth) i_otbn_trace_if (.*);
bind otbn_core otbn_tracer u_otbn_tracer(.*, .otbn_trace(i_otbn_trace_if));
+ assign u_otbn_core.i_otbn_trace_if.scramble_state_err_i = '0;
+ assign u_otbn_core.i_otbn_trace_if.missed_gnt_i = '0;
+
// Convert from core_err_bits_t to err_bits_t
assign otbn_err_bits = '{
fatal_software: core_err_bits.fatal_software,
diff --git a/hw/ip/otbn/rtl/otbn_pkg.sv b/hw/ip/otbn/rtl/otbn_pkg.sv
index 6fdd62f..5eb11a4 100644
--- a/hw/ip/otbn/rtl/otbn_pkg.sv
+++ b/hw/ip/otbn/rtl/otbn_pkg.sv
@@ -110,6 +110,38 @@
logic bad_data_addr;
} err_bits_t;
+ // Wrappers for classifying bad internal states
+ typedef struct packed {
+ logic alu_bignum_err;
+ logic mac_bignum_err;
+ logic ispr_bignum_err;
+ logic controller_err;
+ logic rf_err;
+ logic rd_err;
+ } predec_err_t;
+
+ typedef struct packed {
+ logic spr_urnd_acks;
+ logic spr_secwipe_reqs;
+ logic mubi_rma_err;
+ logic mubi_urnd_err;
+ logic state_err;
+ } start_stop_bad_int_t;
+
+ typedef struct packed {
+ logic loop_hw_cnt_err;
+ logic loop_hw_stack_cnt_err;
+ logic loop_hw_intg_err;
+ logic rf_base_call_stack_err;
+ logic spr_secwipe_acks;
+ logic state_err;
+ } controller_bad_int_t;
+
+ typedef struct packed {
+ logic imem_gnt_missed_err;
+ logic dmem_gnt_missed_err;
+ } missed_gnt_t;
+
// All the error signals that can be generated directly from the controller. Note that this is
// organised to include every software error (including 'call_stack', which actually gets fed in
// from the base register file)