[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)