[otbn] Clear External CSRs RTL

Signed-off-by: Vladimir Rozic <vrozic@lowrisc.org>
diff --git a/hw/ip/otbn/data/otbn.hjson b/hw/ip/otbn/data/otbn.hjson
index f8b94b6..0e0e86a 100644
--- a/hw/ip/otbn/data/otbn.hjson
+++ b/hw/ip/otbn/data/otbn.hjson
@@ -301,9 +301,18 @@
 
         Refer to the "List of Errors" section for a detailed description of the
         errors.
+
+        The host CPU can clear this register when OTBN is not running,
+        by writing any value. Write attempts while OTBN is running are ignored.
       ''',
-      swaccess: "ro",
-      hwaccess: "hwo",
+      swaccess: "rw",
+      hwaccess: "hrw",
+      hwext: "true",
+      hwqe:  "true",
+      tags: [
+            // Don't write this register in the automated CSR tests;
+            "excl:CsrAllTests:CsrExclWrite"
+      ],
       fields: [
         // Software errors
         { bits: "0",
@@ -450,10 +459,14 @@
         count towards the total.
 
         Always reads as 0 if OTBN is locked.
+
+        The host CPU can clear this register when OTBN is not running,
+        by writing any value. Write attempts while OTBN is running are ignored.
       ''',
+      swaccess: "rw",
+      hwaccess: "hrw",
       hwext: "true",
-      swaccess: "ro",
-      hwaccess: "hwo",
+      hwqe:  "true",
       fields: [
         { bits: "31:0",
           name: "insn_cnt",
@@ -461,6 +474,10 @@
           desc: '''
             The number of executed instructions.
           '''
+          tags: [
+            // Don't write this field in the automated CSR tests;
+            "excl:CsrAllTests:CsrExclWrite"
+          ]
         }
       ]
     }
diff --git a/hw/ip/otbn/dv/verilator/otbn_top_sim.sv b/hw/ip/otbn/dv/verilator/otbn_top_sim.sv
index 1e49939..aa11074 100644
--- a/hw/ip/otbn/dv/verilator/otbn_top_sim.sv
+++ b/hw/ip/otbn/dv/verilator/otbn_top_sim.sv
@@ -101,6 +101,7 @@
     .edn_urnd_data_i             ( edn_urnd_data       ),
 
     .insn_cnt_o                  ( insn_cnt            ),
+    .insn_cnt_clear_i            ( 1'b0                ),
 
     .bus_intg_violation_i        ( 1'b0                ),
     .illegal_bus_access_i        ( 1'b0                ),
diff --git a/hw/ip/otbn/rtl/otbn.sv b/hw/ip/otbn/rtl/otbn.sv
index 84b43d4..4da29b5 100644
--- a/hw/ip/otbn/rtl/otbn.sv
+++ b/hw/ip/otbn/rtl/otbn.sv
@@ -95,9 +95,14 @@
   logic locked;
   logic illegal_bus_access_d, illegal_bus_access_q;
 
-  err_bits_t err_bits;
   logic recoverable_err;
   logic reg_intg_violation;
+  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.
+  logic err_bits_clear;
 
   logic software_errs_fatal_q, software_errs_fatal_d;
 
@@ -641,48 +646,34 @@
   // ERR_BITS register
   // The error bits for an OTBN operation get stored on the cycle that done is
   // asserted. Software is expected to read them out before starting the next operation.
-  assign hw2reg.err_bits.bad_data_addr.de = done;
-  assign hw2reg.err_bits.bad_data_addr.d = err_bits.bad_data_addr;
 
-  assign hw2reg.err_bits.bad_insn_addr.de = done;
-  assign hw2reg.err_bits.bad_insn_addr.d = err_bits.bad_insn_addr;
-
-  assign hw2reg.err_bits.call_stack.de = done;
-  assign hw2reg.err_bits.call_stack.d = err_bits.call_stack;
-
-  assign hw2reg.err_bits.illegal_insn.de = done;
-  assign hw2reg.err_bits.illegal_insn.d = err_bits.illegal_insn;
-
-  assign hw2reg.err_bits.loop.de = done;
-  assign hw2reg.err_bits.loop.d = err_bits.loop;
-
-  assign hw2reg.err_bits.key_invalid.de = done;
-  assign hw2reg.err_bits.key_invalid.d = err_bits.key_invalid;
-
-  assign hw2reg.err_bits.imem_intg_violation.de = done;
-  assign hw2reg.err_bits.imem_intg_violation.d = err_bits.imem_intg_violation;
-
-  assign hw2reg.err_bits.dmem_intg_violation.de = done;
-  assign hw2reg.err_bits.dmem_intg_violation.d = err_bits.dmem_intg_violation;
-
-  assign hw2reg.err_bits.reg_intg_violation.de = done;
-  assign hw2reg.err_bits.reg_intg_violation.d = err_bits.reg_intg_violation;
-
-  assign hw2reg.err_bits.bus_intg_violation.de = done;
-  assign hw2reg.err_bits.bus_intg_violation.d = err_bits.bus_intg_violation;
-
-  assign hw2reg.err_bits.bad_internal_state.de = done;
-  assign hw2reg.err_bits.bad_internal_state.d = err_bits.bad_internal_state;
-
-  assign hw2reg.err_bits.illegal_bus_access.de = done;
-  assign hw2reg.err_bits.illegal_bus_access.d = err_bits.illegal_bus_access;
-
-  assign hw2reg.err_bits.lifecycle_escalation.de = done;
-  assign hw2reg.err_bits.lifecycle_escalation.d = err_bits.lifecycle_escalation;
-
-  assign hw2reg.err_bits.fatal_software.de = done;
+  assign hw2reg.err_bits.bad_data_addr.d = err_bits_q.bad_data_addr;
+  assign hw2reg.err_bits.bad_insn_addr.d = err_bits_q.bad_insn_addr;
+  assign hw2reg.err_bits.call_stack.d = err_bits_q.call_stack;
+  assign hw2reg.err_bits.illegal_insn.d = err_bits_q.illegal_insn;
+  assign hw2reg.err_bits.loop.d = err_bits_q.loop;
+  assign hw2reg.err_bits.key_invalid.d = err_bits_q.key_invalid;
+  assign hw2reg.err_bits.imem_intg_violation.d = err_bits_q.imem_intg_violation;
+  assign hw2reg.err_bits.dmem_intg_violation.d = err_bits_q.dmem_intg_violation;
+  assign hw2reg.err_bits.reg_intg_violation.d = err_bits_q.reg_intg_violation;
+  assign hw2reg.err_bits.bus_intg_violation.d = err_bits_q.bus_intg_violation;
+  assign hw2reg.err_bits.bad_internal_state.d = err_bits_q.bad_internal_state;
+  assign hw2reg.err_bits.illegal_bus_access.d = err_bits_q.illegal_bus_access;
+  assign hw2reg.err_bits.lifecycle_escalation.d = err_bits_q.lifecycle_escalation;
   assign hw2reg.err_bits.fatal_software.d = err_bits.fatal_software;
 
+  assign err_bits_clear = reg2hw.err_bits.bad_data_addr.qe & ~busy_execute_q;
+  assign err_bits_d = err_bits_clear ? '0 : err_bits;
+  assign err_bits_en = err_bits_clear | done;
+
+  always_ff @(posedge clk_i or negedge rst_ni) begin
+    if (!rst_ni) begin
+      err_bits_q <= '0;
+    end else if (err_bits_en) begin
+      err_bits_q <= err_bits_d;
+    end
+  end
+
   // FATAL_ALERT_CAUSE register. The .de and .d values are equal for each bit, so that it can only
   // be set, not cleared.
   assign hw2reg.fatal_alert_cause.imem_intg_violation.de = insn_fetch_err;
@@ -700,11 +691,13 @@
   assign hw2reg.fatal_alert_cause.lifecycle_escalation.de = lifecycle_escalation;
   assign hw2reg.fatal_alert_cause.lifecycle_escalation.d  = lifecycle_escalation;
   assign hw2reg.fatal_alert_cause.fatal_software.de = done;
-  assign hw2reg.fatal_alert_cause.fatal_software.d  = err_bits.fatal_software;
+  assign hw2reg.fatal_alert_cause.fatal_software.d  = err_bits_d.fatal_software;
 
   // INSN_CNT register
   logic [31:0] insn_cnt;
+  logic        insn_cnt_clear;
   assign hw2reg.insn_cnt.d = insn_cnt;
+  assign insn_cnt_clear = reg2hw.insn_cnt.qe & ~busy_execute_q;
 
   // Alerts ====================================================================
 
@@ -927,6 +920,7 @@
       .edn_urnd_data_i             (edn_urnd_data),
 
       .insn_cnt_o                  (insn_cnt_rtl),
+      .insn_cnt_clear_i            (insn_cnt_clear),
 
       .bus_intg_violation_i        (bus_intg_violation),
       .illegal_bus_access_i        (illegal_bus_access_q),
@@ -938,6 +932,7 @@
       .sideload_key_shares_valid_i ({2{keymgr_key_i.valid}})
     );
   `else
+
     otbn_core #(
       .RegFile(RegFile),
       .DmemSizeByte(DmemSizeByte),
@@ -981,6 +976,7 @@
       .edn_urnd_data_i             (edn_urnd_data),
 
       .insn_cnt_o                  (insn_cnt),
+      .insn_cnt_clear_i            (insn_cnt_clear),
 
       .bus_intg_violation_i        (bus_intg_violation),
       .illegal_bus_access_i        (illegal_bus_access_q),
diff --git a/hw/ip/otbn/rtl/otbn_controller.sv b/hw/ip/otbn/rtl/otbn_controller.sv
index ad80403..f5b6fac 100644
--- a/hw/ip/otbn/rtl/otbn_controller.sv
+++ b/hw/ip/otbn/rtl/otbn_controller.sv
@@ -133,6 +133,7 @@
 
   input  logic        state_reset_i,
   output logic [31:0] insn_cnt_o,
+  input  logic        insn_cnt_clear_i,
   input  logic        bus_intg_violation_i,
   input  logic        illegal_bus_access_i,
   input  logic        lifecycle_escalation_i,
@@ -239,6 +240,7 @@
   logic rf_a_indirect_err, rf_b_indirect_err, rf_d_indirect_err, rf_indirect_err;
 
   logic [31:0] insn_cnt_d, insn_cnt_q;
+  logic        insn_cnt_clear;
 
   logic [4:0] ld_insn_bignum_wr_addr_q;
   err_bits_t err_bits;
@@ -476,19 +478,8 @@
 
     always_ff @(posedge clk_i or negedge rst_ni) begin
       if (!rst_ni) begin
-        err_bits_q.fatal_software <= 1'b0;
-        err_bits_q.lifecycle_escalation <= 1'b0;
-        err_bits_q.illegal_bus_access  <= 1'b0;
-        err_bits_q.bus_intg_violation <= 1'b0;
-        err_bits_q.reg_intg_violation  <= 1'b0;
-        err_bits_q.dmem_intg_violation <= 1'b0;
-        err_bits_q.imem_intg_violation <= 1'b0;
-        err_bits_q.illegal_insn <= 1'b0;
-        err_bits_q.bad_data_addr <= 1'b0;
-        err_bits_q.loop   <= 1'b0;
-        err_bits_q.call_stack <= 1'b0;
-        err_bits_q.bad_insn_addr <= 1'b0;
         recoverable_err_q <= 1'b0;
+        err_bits_q <= '0;
       end else if (err_bits_en) begin
         err_bits_q <= err_bits_d;
         recoverable_err_q <= recoverable_err_d;
@@ -528,8 +519,10 @@
     end
   end
 
+  assign insn_cnt_clear = state_reset_i | (state_q == OtbnStateLocked) | insn_cnt_clear_i;
+
   always_comb begin
-    if (state_reset_i || state_q == OtbnStateLocked) begin
+    if (insn_cnt_clear) begin
       insn_cnt_d = 32'd0;
     end else if (insn_executing & ~stall & (insn_cnt_q != 32'hffffffff)) begin
       insn_cnt_d = insn_cnt_q + 32'd1;
diff --git a/hw/ip/otbn/rtl/otbn_core.sv b/hw/ip/otbn/rtl/otbn_core.sv
index 655a41d..42107bd 100644
--- a/hw/ip/otbn/rtl/otbn_core.sv
+++ b/hw/ip/otbn/rtl/otbn_core.sv
@@ -69,6 +69,7 @@
   input  logic [EdnDataWidth-1:0] edn_urnd_data_i,
 
   output logic [31:0]             insn_cnt_o,
+  input  logic                    insn_cnt_clear_i,
 
   // An integrity check on an incoming bus transaction failed. Results in a fatal error.
   input  logic                    bus_intg_violation_i,
@@ -417,6 +418,7 @@
 
     .state_reset_i      (state_reset),
     .insn_cnt_o         (insn_cnt),
+    .insn_cnt_clear_i,
     .bus_intg_violation_i,
     .illegal_bus_access_i,
     .lifecycle_escalation_i,
diff --git a/hw/ip/otbn/rtl/otbn_reg_pkg.sv b/hw/ip/otbn/rtl/otbn_reg_pkg.sv
index dfa40ec..1c523a7 100644
--- a/hw/ip/otbn/rtl/otbn_reg_pkg.sv
+++ b/hw/ip/otbn/rtl/otbn_reg_pkg.sv
@@ -51,6 +51,70 @@
   } otbn_reg2hw_ctrl_reg_t;
 
   typedef struct packed {
+    struct packed {
+      logic        q;
+      logic        qe;
+    } bad_data_addr;
+    struct packed {
+      logic        q;
+      logic        qe;
+    } bad_insn_addr;
+    struct packed {
+      logic        q;
+      logic        qe;
+    } call_stack;
+    struct packed {
+      logic        q;
+      logic        qe;
+    } illegal_insn;
+    struct packed {
+      logic        q;
+      logic        qe;
+    } loop;
+    struct packed {
+      logic        q;
+      logic        qe;
+    } key_invalid;
+    struct packed {
+      logic        q;
+      logic        qe;
+    } imem_intg_violation;
+    struct packed {
+      logic        q;
+      logic        qe;
+    } dmem_intg_violation;
+    struct packed {
+      logic        q;
+      logic        qe;
+    } reg_intg_violation;
+    struct packed {
+      logic        q;
+      logic        qe;
+    } bus_intg_violation;
+    struct packed {
+      logic        q;
+      logic        qe;
+    } bad_internal_state;
+    struct packed {
+      logic        q;
+      logic        qe;
+    } illegal_bus_access;
+    struct packed {
+      logic        q;
+      logic        qe;
+    } lifecycle_escalation;
+    struct packed {
+      logic        q;
+      logic        qe;
+    } fatal_software;
+  } otbn_reg2hw_err_bits_reg_t;
+
+  typedef struct packed {
+    logic [31:0] q;
+    logic        qe;
+  } otbn_reg2hw_insn_cnt_reg_t;
+
+  typedef struct packed {
     logic [31:0] q;
     logic        qe;
   } otbn_reg2hw_load_checksum_reg_t;
@@ -72,59 +136,45 @@
   typedef struct packed {
     struct packed {
       logic        d;
-      logic        de;
     } bad_data_addr;
     struct packed {
       logic        d;
-      logic        de;
     } bad_insn_addr;
     struct packed {
       logic        d;
-      logic        de;
     } call_stack;
     struct packed {
       logic        d;
-      logic        de;
     } illegal_insn;
     struct packed {
       logic        d;
-      logic        de;
     } loop;
     struct packed {
       logic        d;
-      logic        de;
     } key_invalid;
     struct packed {
       logic        d;
-      logic        de;
     } imem_intg_violation;
     struct packed {
       logic        d;
-      logic        de;
     } dmem_intg_violation;
     struct packed {
       logic        d;
-      logic        de;
     } reg_intg_violation;
     struct packed {
       logic        d;
-      logic        de;
     } bus_intg_violation;
     struct packed {
       logic        d;
-      logic        de;
     } bad_internal_state;
     struct packed {
       logic        d;
-      logic        de;
     } illegal_bus_access;
     struct packed {
       logic        d;
-      logic        de;
     } lifecycle_escalation;
     struct packed {
       logic        d;
-      logic        de;
     } fatal_software;
   } otbn_hw2reg_err_bits_reg_t;
 
@@ -173,21 +223,23 @@
 
   // Register -> HW type
   typedef struct packed {
-    otbn_reg2hw_intr_state_reg_t intr_state; // [51:51]
-    otbn_reg2hw_intr_enable_reg_t intr_enable; // [50:50]
-    otbn_reg2hw_intr_test_reg_t intr_test; // [49:48]
-    otbn_reg2hw_alert_test_reg_t alert_test; // [47:44]
-    otbn_reg2hw_cmd_reg_t cmd; // [43:35]
-    otbn_reg2hw_ctrl_reg_t ctrl; // [34:33]
+    otbn_reg2hw_intr_state_reg_t intr_state; // [112:112]
+    otbn_reg2hw_intr_enable_reg_t intr_enable; // [111:111]
+    otbn_reg2hw_intr_test_reg_t intr_test; // [110:109]
+    otbn_reg2hw_alert_test_reg_t alert_test; // [108:105]
+    otbn_reg2hw_cmd_reg_t cmd; // [104:96]
+    otbn_reg2hw_ctrl_reg_t ctrl; // [95:94]
+    otbn_reg2hw_err_bits_reg_t err_bits; // [93:66]
+    otbn_reg2hw_insn_cnt_reg_t insn_cnt; // [65:33]
     otbn_reg2hw_load_checksum_reg_t load_checksum; // [32:0]
   } otbn_reg2hw_t;
 
   // HW -> register type
   typedef struct packed {
-    otbn_hw2reg_intr_state_reg_t intr_state; // [119:118]
-    otbn_hw2reg_ctrl_reg_t ctrl; // [117:117]
-    otbn_hw2reg_status_reg_t status; // [116:108]
-    otbn_hw2reg_err_bits_reg_t err_bits; // [107:80]
+    otbn_hw2reg_intr_state_reg_t intr_state; // [105:104]
+    otbn_hw2reg_ctrl_reg_t ctrl; // [103:103]
+    otbn_hw2reg_status_reg_t status; // [102:94]
+    otbn_hw2reg_err_bits_reg_t err_bits; // [93:80]
     otbn_hw2reg_fatal_alert_cause_reg_t fatal_alert_cause; // [79:64]
     otbn_hw2reg_insn_cnt_reg_t insn_cnt; // [63:32]
     otbn_hw2reg_load_checksum_reg_t load_checksum; // [31:0]
@@ -216,6 +268,21 @@
   parameter logic [7:0] OTBN_CMD_CMD_RESVAL = 8'h 0;
   parameter logic [0:0] OTBN_CTRL_RESVAL = 1'h 0;
   parameter logic [0:0] OTBN_CTRL_SOFTWARE_ERRS_FATAL_RESVAL = 1'h 0;
+  parameter logic [23:0] OTBN_ERR_BITS_RESVAL = 24'h 0;
+  parameter logic [0:0] OTBN_ERR_BITS_BAD_DATA_ADDR_RESVAL = 1'h 0;
+  parameter logic [0:0] OTBN_ERR_BITS_BAD_INSN_ADDR_RESVAL = 1'h 0;
+  parameter logic [0:0] OTBN_ERR_BITS_CALL_STACK_RESVAL = 1'h 0;
+  parameter logic [0:0] OTBN_ERR_BITS_ILLEGAL_INSN_RESVAL = 1'h 0;
+  parameter logic [0:0] OTBN_ERR_BITS_LOOP_RESVAL = 1'h 0;
+  parameter logic [0:0] OTBN_ERR_BITS_KEY_INVALID_RESVAL = 1'h 0;
+  parameter logic [0:0] OTBN_ERR_BITS_IMEM_INTG_VIOLATION_RESVAL = 1'h 0;
+  parameter logic [0:0] OTBN_ERR_BITS_DMEM_INTG_VIOLATION_RESVAL = 1'h 0;
+  parameter logic [0:0] OTBN_ERR_BITS_REG_INTG_VIOLATION_RESVAL = 1'h 0;
+  parameter logic [0:0] OTBN_ERR_BITS_BUS_INTG_VIOLATION_RESVAL = 1'h 0;
+  parameter logic [0:0] OTBN_ERR_BITS_BAD_INTERNAL_STATE_RESVAL = 1'h 0;
+  parameter logic [0:0] OTBN_ERR_BITS_ILLEGAL_BUS_ACCESS_RESVAL = 1'h 0;
+  parameter logic [0:0] OTBN_ERR_BITS_LIFECYCLE_ESCALATION_RESVAL = 1'h 0;
+  parameter logic [0:0] OTBN_ERR_BITS_FATAL_SOFTWARE_RESVAL = 1'h 0;
   parameter logic [31:0] OTBN_INSN_CNT_RESVAL = 32'h 0;
   parameter logic [31:0] OTBN_INSN_CNT_INSN_CNT_RESVAL = 32'h 0;
   parameter logic [31:0] OTBN_LOAD_CHECKSUM_RESVAL = 32'h 0;
diff --git a/hw/ip/otbn/rtl/otbn_reg_top.sv b/hw/ip/otbn/rtl/otbn_reg_top.sv
index 1101867..cd15371 100644
--- a/hw/ip/otbn/rtl/otbn_reg_top.sv
+++ b/hw/ip/otbn/rtl/otbn_reg_top.sv
@@ -180,20 +180,36 @@
   logic ctrl_qs;
   logic ctrl_wd;
   logic [7:0] status_qs;
+  logic err_bits_re;
+  logic err_bits_we;
   logic err_bits_bad_data_addr_qs;
+  logic err_bits_bad_data_addr_wd;
   logic err_bits_bad_insn_addr_qs;
+  logic err_bits_bad_insn_addr_wd;
   logic err_bits_call_stack_qs;
+  logic err_bits_call_stack_wd;
   logic err_bits_illegal_insn_qs;
+  logic err_bits_illegal_insn_wd;
   logic err_bits_loop_qs;
+  logic err_bits_loop_wd;
   logic err_bits_key_invalid_qs;
+  logic err_bits_key_invalid_wd;
   logic err_bits_imem_intg_violation_qs;
+  logic err_bits_imem_intg_violation_wd;
   logic err_bits_dmem_intg_violation_qs;
+  logic err_bits_dmem_intg_violation_wd;
   logic err_bits_reg_intg_violation_qs;
+  logic err_bits_reg_intg_violation_wd;
   logic err_bits_bus_intg_violation_qs;
+  logic err_bits_bus_intg_violation_wd;
   logic err_bits_bad_internal_state_qs;
+  logic err_bits_bad_internal_state_wd;
   logic err_bits_illegal_bus_access_qs;
+  logic err_bits_illegal_bus_access_wd;
   logic err_bits_lifecycle_escalation_qs;
+  logic err_bits_lifecycle_escalation_wd;
   logic err_bits_fatal_software_qs;
+  logic err_bits_fatal_software_wd;
   logic fatal_alert_cause_imem_intg_violation_qs;
   logic fatal_alert_cause_dmem_intg_violation_qs;
   logic fatal_alert_cause_reg_intg_violation_qs;
@@ -203,7 +219,9 @@
   logic fatal_alert_cause_lifecycle_escalation_qs;
   logic fatal_alert_cause_fatal_software_qs;
   logic insn_cnt_re;
+  logic insn_cnt_we;
   logic [31:0] insn_cnt_qs;
+  logic [31:0] insn_cnt_wd;
   logic load_checksum_re;
   logic load_checksum_we;
   logic [31:0] load_checksum_qs;
@@ -363,354 +381,200 @@
   );
 
 
-  // R[err_bits]: V(False)
+  // R[err_bits]: V(True)
   //   F[bad_data_addr]: 0:0
-  prim_subreg #(
-    .DW      (1),
-    .SwAccess(prim_subreg_pkg::SwAccessRO),
-    .RESVAL  (1'h0)
+  prim_subreg_ext #(
+    .DW    (1)
   ) u_err_bits_bad_data_addr (
-    .clk_i   (clk_i),
-    .rst_ni  (rst_ni),
-
-    // from register interface
-    .we     (1'b0),
-    .wd     ('0),
-
-    // from internal hardware
-    .de     (hw2reg.err_bits.bad_data_addr.de),
+    .re     (err_bits_re),
+    .we     (err_bits_we),
+    .wd     (err_bits_bad_data_addr_wd),
     .d      (hw2reg.err_bits.bad_data_addr.d),
-
-    // to internal hardware
-    .qe     (),
-    .q      (),
-
-    // to register interface (read)
+    .qre    (),
+    .qe     (reg2hw.err_bits.bad_data_addr.qe),
+    .q      (reg2hw.err_bits.bad_data_addr.q),
     .qs     (err_bits_bad_data_addr_qs)
   );
 
   //   F[bad_insn_addr]: 1:1
-  prim_subreg #(
-    .DW      (1),
-    .SwAccess(prim_subreg_pkg::SwAccessRO),
-    .RESVAL  (1'h0)
+  prim_subreg_ext #(
+    .DW    (1)
   ) u_err_bits_bad_insn_addr (
-    .clk_i   (clk_i),
-    .rst_ni  (rst_ni),
-
-    // from register interface
-    .we     (1'b0),
-    .wd     ('0),
-
-    // from internal hardware
-    .de     (hw2reg.err_bits.bad_insn_addr.de),
+    .re     (err_bits_re),
+    .we     (err_bits_we),
+    .wd     (err_bits_bad_insn_addr_wd),
     .d      (hw2reg.err_bits.bad_insn_addr.d),
-
-    // to internal hardware
-    .qe     (),
-    .q      (),
-
-    // to register interface (read)
+    .qre    (),
+    .qe     (reg2hw.err_bits.bad_insn_addr.qe),
+    .q      (reg2hw.err_bits.bad_insn_addr.q),
     .qs     (err_bits_bad_insn_addr_qs)
   );
 
   //   F[call_stack]: 2:2
-  prim_subreg #(
-    .DW      (1),
-    .SwAccess(prim_subreg_pkg::SwAccessRO),
-    .RESVAL  (1'h0)
+  prim_subreg_ext #(
+    .DW    (1)
   ) u_err_bits_call_stack (
-    .clk_i   (clk_i),
-    .rst_ni  (rst_ni),
-
-    // from register interface
-    .we     (1'b0),
-    .wd     ('0),
-
-    // from internal hardware
-    .de     (hw2reg.err_bits.call_stack.de),
+    .re     (err_bits_re),
+    .we     (err_bits_we),
+    .wd     (err_bits_call_stack_wd),
     .d      (hw2reg.err_bits.call_stack.d),
-
-    // to internal hardware
-    .qe     (),
-    .q      (),
-
-    // to register interface (read)
+    .qre    (),
+    .qe     (reg2hw.err_bits.call_stack.qe),
+    .q      (reg2hw.err_bits.call_stack.q),
     .qs     (err_bits_call_stack_qs)
   );
 
   //   F[illegal_insn]: 3:3
-  prim_subreg #(
-    .DW      (1),
-    .SwAccess(prim_subreg_pkg::SwAccessRO),
-    .RESVAL  (1'h0)
+  prim_subreg_ext #(
+    .DW    (1)
   ) u_err_bits_illegal_insn (
-    .clk_i   (clk_i),
-    .rst_ni  (rst_ni),
-
-    // from register interface
-    .we     (1'b0),
-    .wd     ('0),
-
-    // from internal hardware
-    .de     (hw2reg.err_bits.illegal_insn.de),
+    .re     (err_bits_re),
+    .we     (err_bits_we),
+    .wd     (err_bits_illegal_insn_wd),
     .d      (hw2reg.err_bits.illegal_insn.d),
-
-    // to internal hardware
-    .qe     (),
-    .q      (),
-
-    // to register interface (read)
+    .qre    (),
+    .qe     (reg2hw.err_bits.illegal_insn.qe),
+    .q      (reg2hw.err_bits.illegal_insn.q),
     .qs     (err_bits_illegal_insn_qs)
   );
 
   //   F[loop]: 4:4
-  prim_subreg #(
-    .DW      (1),
-    .SwAccess(prim_subreg_pkg::SwAccessRO),
-    .RESVAL  (1'h0)
+  prim_subreg_ext #(
+    .DW    (1)
   ) u_err_bits_loop (
-    .clk_i   (clk_i),
-    .rst_ni  (rst_ni),
-
-    // from register interface
-    .we     (1'b0),
-    .wd     ('0),
-
-    // from internal hardware
-    .de     (hw2reg.err_bits.loop.de),
+    .re     (err_bits_re),
+    .we     (err_bits_we),
+    .wd     (err_bits_loop_wd),
     .d      (hw2reg.err_bits.loop.d),
-
-    // to internal hardware
-    .qe     (),
-    .q      (),
-
-    // to register interface (read)
+    .qre    (),
+    .qe     (reg2hw.err_bits.loop.qe),
+    .q      (reg2hw.err_bits.loop.q),
     .qs     (err_bits_loop_qs)
   );
 
   //   F[key_invalid]: 5:5
-  prim_subreg #(
-    .DW      (1),
-    .SwAccess(prim_subreg_pkg::SwAccessRO),
-    .RESVAL  (1'h0)
+  prim_subreg_ext #(
+    .DW    (1)
   ) u_err_bits_key_invalid (
-    .clk_i   (clk_i),
-    .rst_ni  (rst_ni),
-
-    // from register interface
-    .we     (1'b0),
-    .wd     ('0),
-
-    // from internal hardware
-    .de     (hw2reg.err_bits.key_invalid.de),
+    .re     (err_bits_re),
+    .we     (err_bits_we),
+    .wd     (err_bits_key_invalid_wd),
     .d      (hw2reg.err_bits.key_invalid.d),
-
-    // to internal hardware
-    .qe     (),
-    .q      (),
-
-    // to register interface (read)
+    .qre    (),
+    .qe     (reg2hw.err_bits.key_invalid.qe),
+    .q      (reg2hw.err_bits.key_invalid.q),
     .qs     (err_bits_key_invalid_qs)
   );
 
   //   F[imem_intg_violation]: 16:16
-  prim_subreg #(
-    .DW      (1),
-    .SwAccess(prim_subreg_pkg::SwAccessRO),
-    .RESVAL  (1'h0)
+  prim_subreg_ext #(
+    .DW    (1)
   ) u_err_bits_imem_intg_violation (
-    .clk_i   (clk_i),
-    .rst_ni  (rst_ni),
-
-    // from register interface
-    .we     (1'b0),
-    .wd     ('0),
-
-    // from internal hardware
-    .de     (hw2reg.err_bits.imem_intg_violation.de),
+    .re     (err_bits_re),
+    .we     (err_bits_we),
+    .wd     (err_bits_imem_intg_violation_wd),
     .d      (hw2reg.err_bits.imem_intg_violation.d),
-
-    // to internal hardware
-    .qe     (),
-    .q      (),
-
-    // to register interface (read)
+    .qre    (),
+    .qe     (reg2hw.err_bits.imem_intg_violation.qe),
+    .q      (reg2hw.err_bits.imem_intg_violation.q),
     .qs     (err_bits_imem_intg_violation_qs)
   );
 
   //   F[dmem_intg_violation]: 17:17
-  prim_subreg #(
-    .DW      (1),
-    .SwAccess(prim_subreg_pkg::SwAccessRO),
-    .RESVAL  (1'h0)
+  prim_subreg_ext #(
+    .DW    (1)
   ) u_err_bits_dmem_intg_violation (
-    .clk_i   (clk_i),
-    .rst_ni  (rst_ni),
-
-    // from register interface
-    .we     (1'b0),
-    .wd     ('0),
-
-    // from internal hardware
-    .de     (hw2reg.err_bits.dmem_intg_violation.de),
+    .re     (err_bits_re),
+    .we     (err_bits_we),
+    .wd     (err_bits_dmem_intg_violation_wd),
     .d      (hw2reg.err_bits.dmem_intg_violation.d),
-
-    // to internal hardware
-    .qe     (),
-    .q      (),
-
-    // to register interface (read)
+    .qre    (),
+    .qe     (reg2hw.err_bits.dmem_intg_violation.qe),
+    .q      (reg2hw.err_bits.dmem_intg_violation.q),
     .qs     (err_bits_dmem_intg_violation_qs)
   );
 
   //   F[reg_intg_violation]: 18:18
-  prim_subreg #(
-    .DW      (1),
-    .SwAccess(prim_subreg_pkg::SwAccessRO),
-    .RESVAL  (1'h0)
+  prim_subreg_ext #(
+    .DW    (1)
   ) u_err_bits_reg_intg_violation (
-    .clk_i   (clk_i),
-    .rst_ni  (rst_ni),
-
-    // from register interface
-    .we     (1'b0),
-    .wd     ('0),
-
-    // from internal hardware
-    .de     (hw2reg.err_bits.reg_intg_violation.de),
+    .re     (err_bits_re),
+    .we     (err_bits_we),
+    .wd     (err_bits_reg_intg_violation_wd),
     .d      (hw2reg.err_bits.reg_intg_violation.d),
-
-    // to internal hardware
-    .qe     (),
-    .q      (),
-
-    // to register interface (read)
+    .qre    (),
+    .qe     (reg2hw.err_bits.reg_intg_violation.qe),
+    .q      (reg2hw.err_bits.reg_intg_violation.q),
     .qs     (err_bits_reg_intg_violation_qs)
   );
 
   //   F[bus_intg_violation]: 19:19
-  prim_subreg #(
-    .DW      (1),
-    .SwAccess(prim_subreg_pkg::SwAccessRO),
-    .RESVAL  (1'h0)
+  prim_subreg_ext #(
+    .DW    (1)
   ) u_err_bits_bus_intg_violation (
-    .clk_i   (clk_i),
-    .rst_ni  (rst_ni),
-
-    // from register interface
-    .we     (1'b0),
-    .wd     ('0),
-
-    // from internal hardware
-    .de     (hw2reg.err_bits.bus_intg_violation.de),
+    .re     (err_bits_re),
+    .we     (err_bits_we),
+    .wd     (err_bits_bus_intg_violation_wd),
     .d      (hw2reg.err_bits.bus_intg_violation.d),
-
-    // to internal hardware
-    .qe     (),
-    .q      (),
-
-    // to register interface (read)
+    .qre    (),
+    .qe     (reg2hw.err_bits.bus_intg_violation.qe),
+    .q      (reg2hw.err_bits.bus_intg_violation.q),
     .qs     (err_bits_bus_intg_violation_qs)
   );
 
   //   F[bad_internal_state]: 20:20
-  prim_subreg #(
-    .DW      (1),
-    .SwAccess(prim_subreg_pkg::SwAccessRO),
-    .RESVAL  (1'h0)
+  prim_subreg_ext #(
+    .DW    (1)
   ) u_err_bits_bad_internal_state (
-    .clk_i   (clk_i),
-    .rst_ni  (rst_ni),
-
-    // from register interface
-    .we     (1'b0),
-    .wd     ('0),
-
-    // from internal hardware
-    .de     (hw2reg.err_bits.bad_internal_state.de),
+    .re     (err_bits_re),
+    .we     (err_bits_we),
+    .wd     (err_bits_bad_internal_state_wd),
     .d      (hw2reg.err_bits.bad_internal_state.d),
-
-    // to internal hardware
-    .qe     (),
-    .q      (),
-
-    // to register interface (read)
+    .qre    (),
+    .qe     (reg2hw.err_bits.bad_internal_state.qe),
+    .q      (reg2hw.err_bits.bad_internal_state.q),
     .qs     (err_bits_bad_internal_state_qs)
   );
 
   //   F[illegal_bus_access]: 21:21
-  prim_subreg #(
-    .DW      (1),
-    .SwAccess(prim_subreg_pkg::SwAccessRO),
-    .RESVAL  (1'h0)
+  prim_subreg_ext #(
+    .DW    (1)
   ) u_err_bits_illegal_bus_access (
-    .clk_i   (clk_i),
-    .rst_ni  (rst_ni),
-
-    // from register interface
-    .we     (1'b0),
-    .wd     ('0),
-
-    // from internal hardware
-    .de     (hw2reg.err_bits.illegal_bus_access.de),
+    .re     (err_bits_re),
+    .we     (err_bits_we),
+    .wd     (err_bits_illegal_bus_access_wd),
     .d      (hw2reg.err_bits.illegal_bus_access.d),
-
-    // to internal hardware
-    .qe     (),
-    .q      (),
-
-    // to register interface (read)
+    .qre    (),
+    .qe     (reg2hw.err_bits.illegal_bus_access.qe),
+    .q      (reg2hw.err_bits.illegal_bus_access.q),
     .qs     (err_bits_illegal_bus_access_qs)
   );
 
   //   F[lifecycle_escalation]: 22:22
-  prim_subreg #(
-    .DW      (1),
-    .SwAccess(prim_subreg_pkg::SwAccessRO),
-    .RESVAL  (1'h0)
+  prim_subreg_ext #(
+    .DW    (1)
   ) u_err_bits_lifecycle_escalation (
-    .clk_i   (clk_i),
-    .rst_ni  (rst_ni),
-
-    // from register interface
-    .we     (1'b0),
-    .wd     ('0),
-
-    // from internal hardware
-    .de     (hw2reg.err_bits.lifecycle_escalation.de),
+    .re     (err_bits_re),
+    .we     (err_bits_we),
+    .wd     (err_bits_lifecycle_escalation_wd),
     .d      (hw2reg.err_bits.lifecycle_escalation.d),
-
-    // to internal hardware
-    .qe     (),
-    .q      (),
-
-    // to register interface (read)
+    .qre    (),
+    .qe     (reg2hw.err_bits.lifecycle_escalation.qe),
+    .q      (reg2hw.err_bits.lifecycle_escalation.q),
     .qs     (err_bits_lifecycle_escalation_qs)
   );
 
   //   F[fatal_software]: 23:23
-  prim_subreg #(
-    .DW      (1),
-    .SwAccess(prim_subreg_pkg::SwAccessRO),
-    .RESVAL  (1'h0)
+  prim_subreg_ext #(
+    .DW    (1)
   ) u_err_bits_fatal_software (
-    .clk_i   (clk_i),
-    .rst_ni  (rst_ni),
-
-    // from register interface
-    .we     (1'b0),
-    .wd     ('0),
-
-    // from internal hardware
-    .de     (hw2reg.err_bits.fatal_software.de),
+    .re     (err_bits_re),
+    .we     (err_bits_we),
+    .wd     (err_bits_fatal_software_wd),
     .d      (hw2reg.err_bits.fatal_software.d),
-
-    // to internal hardware
-    .qe     (),
-    .q      (),
-
-    // to register interface (read)
+    .qre    (),
+    .qe     (reg2hw.err_bits.fatal_software.qe),
+    .q      (reg2hw.err_bits.fatal_software.q),
     .qs     (err_bits_fatal_software_qs)
   );
 
@@ -922,12 +786,12 @@
     .DW    (32)
   ) u_insn_cnt (
     .re     (insn_cnt_re),
-    .we     (1'b0),
-    .wd     ('0),
+    .we     (insn_cnt_we),
+    .wd     (insn_cnt_wd),
     .d      (hw2reg.insn_cnt.d),
     .qre    (),
-    .qe     (),
-    .q      (),
+    .qe     (reg2hw.insn_cnt.qe),
+    .q      (reg2hw.insn_cnt.q),
     .qs     (insn_cnt_qs)
   );
 
@@ -1002,7 +866,40 @@
   assign ctrl_we = addr_hit[5] & reg_we & !reg_error;
 
   assign ctrl_wd = reg_wdata[0];
+  assign err_bits_re = addr_hit[7] & reg_re & !reg_error;
+  assign err_bits_we = addr_hit[7] & reg_we & !reg_error;
+
+  assign err_bits_bad_data_addr_wd = reg_wdata[0];
+
+  assign err_bits_bad_insn_addr_wd = reg_wdata[1];
+
+  assign err_bits_call_stack_wd = reg_wdata[2];
+
+  assign err_bits_illegal_insn_wd = reg_wdata[3];
+
+  assign err_bits_loop_wd = reg_wdata[4];
+
+  assign err_bits_key_invalid_wd = reg_wdata[5];
+
+  assign err_bits_imem_intg_violation_wd = reg_wdata[16];
+
+  assign err_bits_dmem_intg_violation_wd = reg_wdata[17];
+
+  assign err_bits_reg_intg_violation_wd = reg_wdata[18];
+
+  assign err_bits_bus_intg_violation_wd = reg_wdata[19];
+
+  assign err_bits_bad_internal_state_wd = reg_wdata[20];
+
+  assign err_bits_illegal_bus_access_wd = reg_wdata[21];
+
+  assign err_bits_lifecycle_escalation_wd = reg_wdata[22];
+
+  assign err_bits_fatal_software_wd = reg_wdata[23];
   assign insn_cnt_re = addr_hit[9] & reg_re & !reg_error;
+  assign insn_cnt_we = addr_hit[9] & reg_we & !reg_error;
+
+  assign insn_cnt_wd = reg_wdata[31:0];
   assign load_checksum_re = addr_hit[10] & reg_re & !reg_error;
   assign load_checksum_we = addr_hit[10] & reg_we & !reg_error;