[keymgr] Fix for error capture

- Fixes #7998
- Break keymgr errors into 4 explicit categories
  - sync errors
  - async errors
  - sync faults
  - async faults

Signed-off-by: Timothy Chen <timothytim@google.com>

[keymgr, dv] Accompanying DV updates

Signed-off-by: Timothy Chen <timothytim@google.com>

[keymgr, sw] Accompanying software updates

Signed-off-by: Timothy Chen <timothytim@google.com>

[keymgr] Accompanying documentation update

Signed-off-by: Timothy Chen <timothytim@google.com>
diff --git a/hw/ip/keymgr/data/keymgr.hjson b/hw/ip/keymgr/data/keymgr.hjson
index a329f6b..f00e2df 100644
--- a/hw/ip/keymgr/data/keymgr.hjson
+++ b/hw/ip/keymgr/data/keymgr.hjson
@@ -847,37 +847,41 @@
       desc: '''
         Key manager error code.
         This register must be explicitly cleared by software.
-        Software clears by writing back whatever it reads.
+
+        This register represents both synchronous and asynchronous recoverable
+        errors.
+
+        Synchronous errors refer to those that only happen when a keymgr operation is
+        invoked, while asynchronous refers to errors that can happen at any time.
       ''',
       swaccess: "rw1c",
-      hwaccess: "hrw",
+      hwaccess: "hwo",
       fields: [
         { bits: "0",
           name: "INVALID_OP",
           resval: "0x0"
-          desc: "Invalid operation issued to key manager",
+          desc: "Invalid operation issued to key manager, synchronous error",
         },
         { bits: "1",
           name: "INVALID_KMAC_INPUT",
           resval: "0x0"
-          desc: "Invalid data issued to kmac interface",
+          desc: "Invalid data issued to kmac interface, synchronous error",
         },
         { bits: "2",
           name: "INVALID_SHADOW_UPDATE",
           resval: "0x0"
-          desc: "An error observed during shadow register updates.",
-        },
-        { bits: "3",
-          name: "INVALID_STATES",
-          resval: "0x0"
-          desc: "Invalid states has occurred in keymgr. These are suspected faults.  Please see !!FAULT_STATUS for a detailed breakdown",
+          desc: "An error observed during shadow register updates, asynchronous error",
         },
       ]
     },
 
     { name: "FAULT_STATUS",
       desc: '''
-        Detailed per bit breakdown of the INVALID_STATES field in !!ERR_CODE
+        This register represents both synchronous and asynchronous fatal faults.
+
+        Synchronous faults refer to those that only happen when a keymgr operation is
+        invoked, while asynchronous refers to faults that can happen at any time.
+
       ''',
       swaccess: "ro",
       hwaccess: "hrw",
@@ -885,42 +889,42 @@
         { bits: "0",
           name: "CMD",
           resval: "0x0"
-          desc: "A non-onehot command was sent from keymgr to kmac",
+          desc: "A non-onehot command was seen in kmac, asynchronous fault.",
         },
         { bits: "1",
           name: "KMAC_FSM",
           resval: "0x0"
-          desc: "The kmac transfer interface is in an error state",
+          desc: "The kmac transfer interface is in an error state, asynchronous fault.",
         },
         { bits: "2",
           name: "KMAC_OP",
           resval: "0x0"
-          desc: "KMAC reported an error during keymgr usage, this should never happen",
+          desc: "KMAC reported an error during keymgr usage, this should never happen - synchronous fault.",
         },
         { bits: "3",
           name: "KMAC_OUT",
           resval: "0x0"
-          desc: "KMAC data returned as all 0's or all 1's",
+          desc: "KMAC data returned as all 0's or all 1's - synchronous fault",
         },
         { bits: "4",
           name: "REGFILE_INTG",
           resval: "0x0"
-          desc: "Register file integrity error.",
+          desc: "Register file integrity error, asynchronous fault",
         },
         { bits: "5",
           name: "SHADOW",
           resval: "0x0"
-          desc: "Shadow copy storage error.",
+          desc: "Shadow copy storage error, asynchronous fault",
         },
         { bits: "6",
           name: "CTRL_FSM_INTG",
           resval: "0x0"
-          desc: "Control FSM integrity error.",
+          desc: "Control FSM integrity error, asynchronous fault",
         },
         { bits: "7",
           name: "CTRL_FSM_CNT",
           resval: "0x0"
-          desc: "Control FSM counter integrity error.",
+          desc: "Control FSM counter integrity error, asynchronous fault",
         },
       ]
     },
diff --git a/hw/ip/keymgr/doc/_index.md b/hw/ip/keymgr/doc/_index.md
index bbc8a70..6cf2075 100644
--- a/hw/ip/keymgr/doc/_index.md
+++ b/hw/ip/keymgr/doc/_index.md
@@ -201,55 +201,104 @@
 For advance-state and `generate-output` commands, the KMAC emitted output are also in 2-shares.
 Software is responsible for determining if the key should be preserved in shares or combined, depending on the use case.
 
-## Errors, Interrupts and Alerts
-An error code register is maintained {{< regref ERR_CODE >}} to check issues that might rise while using the key manager.
-There are two categories of errors
-*  Hardware fault errors - These errors indicate something fundamental has gone wrong and are errors that could not have been caused by software.
-   *  Invalid states - There are invalid / impossible states observed in the keymgr.  These are likely fault errors.
-   *  Invalid fsm state - The fsm reached an invalid state.  This is not possible by software and indicates a hardware fault.
-   *  Invalid kmac operation - The KMAC module itself reported an error.  This is not possible given the set of KMAC data interface inputs.
-   *  Invalid output - The data return from KMAC is all 0's or all 1's.  This is not possible given the set of KMAC data interface inputs.
+## Errors, Faults and Alerts
 
-*  Software operation errors - These errors could have been caused by user errors and is a sign that software should examine its usage of key manager.
-   *  Invalid operation - An invalid operation (for example `generate` while in Reset) was invoked.
-   *  Invalid input - Invalid software and hardware inputs were supplied (for example a greater key version than allowed in {{< regref MAX_OWNER_KEY_VER >}}, or a root key or seed that has never been initialized.
+The key manager has two overall categories of errors:
+* Recoverable errors
+* Fatal errors
 
-Two separate alerts are generated, one corresponding to each category above.
+Recoverable errors are those likely to have been introduced by software and not fatal to the key manager or the system.
+Fatal errors are logically impossible errors that have a high likelihood of being a fault and thus fatal.
+
+Each category of error can be further divided into two:
+* Synchronous errors
+* Asynchronous errors
+
+Synchronous errors happen only during a key manager transaction.
+Asynchronous errors can happen at any time.
+
+Given the above, we have 4 total categories of errors:
+* Synchronous recoverable errors
+* Asynchronous recoverable errors
+* Synchronous fatal errors
+* Asynchronous fatal errors
+
+All recoverable errors (synchronous and asynchronous) are captured in {{< regref ERR_CODE >}}.
+All fatal errors (synchronous and asynchronous) are captured in {{< regref FAULT_STATUS >}}.
+
+Recoverable errors cause a recoverable alert to be sent from the key manager.
+Fatal errors cause a fatal alert to be sent from the key manager.
+
+Below, the behavior of each category and its constituent errors are described in detail.
+
+### Synchronous Recoverable Errors
+
+These errors can only happen when a key manager transaction is invoked and are typically associated with incorrect software programming.
+At the end of the transaction, key manager reports whether there was an error in {{< regref ERR_CODE >}} and sends a recoverable alert.
+
+* {{< regref ERR_CODE.INVALID_OP >}} Software issued an invalid operation given the current key manager state.
+* {{< regref ERR_CODE.INVALID_KMAC_INPUT >}} Software supplied invalid input (for example a key greater than the max version) for a key manager operation.
+
+### Asynchronous Recoverable Errors
+
+These errors can happen at any time regardless of whether there is a key manager operation.
+The error is reported in {{< regref ERR_CODE >}} and the key manager sends a recoverable alert.
+
+* {{< regref ERR_CODE.INVALID_SHADOW_UPDATE >}} Software performed an invalid sequence while trying to update a key manager shadow register.
+
+### Synchronous Fatal Errors
+
+These errors can only happen when a key manager transaction is invoked and receives malformed transaction results that are not logically possible.
+At the end of the transaction, key manager reports whether there was an error in {{< regref FAULT_STATUS >}} and continuously sends fatal alerts .
+
+* {{< regref ERR_CODE.KMAC_OP >}} KMAC reports a transaction error, this is not possible given current definitions.
+* {{< regref ERR_CODE.KMAC_OUT >}} KMAC returns all 0's or all 1's as a result, this is not possible given current definitions.
+
+Note, these errors are synchronous from the perspective of the key manager, but they may be asynchronous from the perspective of another module.
+
+### Asynchronous Fatal Errors
+
+These errors can happen at any time regardless of whether there is a key manager operation.
+The error is reported in {{< regref FAULT_STATUS >}} and the key manager continuously sends fatal alerts.
+
+* {{< regref ERR_CODE.CMD >}} KMAC control's command lines are displaying non-one hot values.
+* {{< regref ERR_CODE.KMAC_FSM >}} KMAC control's FSM is in an invalid state.
+* {{< regref ERR_CODE.REGFILE_INTG >}} The key manager's regfile reports an integrity error.
+* {{< regref ERR_CODE.SHADOW >}} The key manager's regfile reports a shadow storage error.
+* {{< regref ERR_CODE.CTRL_FSM_INTG >}} The key manager's main control FSM is in an invalid state.
+* {{< regref ERR_CODE.CTRL_FSM_CNT >}} The key manager's main control count exhibits an incorrect value.
 
 ### Faults and Operational Faults
 
-The {{< regref FAULT_STATUS >}} register captures all faults that can occur within the key manager.
-Some of these faults can occur only when there is a key manager operation ongoing, other faults can happen at any time (for example register integrity faults or shadow storage faults).
-{{< regref FAULT_STATUS >}} captures all faults regardless of when they happen.
+Since fatal errors (faults) can happen at any time, their impact on the key manager depends on transaction timing.
 
-The {{< regref ERR_CODE.INVALID_STATES >}} field represents the presence of **any** fault during a key manager operation.
-This means if a fault happens before or during an operation, it will be recognized as an operational fault and result in keymgr's transition to `Invalid` state.
+If the fault happens while a transaction is ongoing, key manager transitions to the `Invalid` [state](#invalid-entry-wiping).
+
+If the fault happens while there is no transaction, an alert is first sent to the alert handler.
+If before the alert handler escalates an operation is run, the key manager again transitions to `Invalid` [state](#invalid-entry-wiping).
+If the alert handler escalates and disables the key manager, then the key manager will also transition to `Invalid` state if it is not already there.
 
 #### Example 1: Fault During Operation
 The key manager is running a generate operation and a non-onehot command was observed by the kmac interface.
 Since the non-onehot condition is a fault, it will be reflected in {{< regref FAULT_STATUS >}}.
-Since an operation was ongoing when this fault was seen, it will also be reflected in {{< regref ERR_CODE.INVALID_STATES >}}.
-This is considered an operational fault and begin transition to the `Invalid` [state](#invalid-entry-wiping).
+Since an operation was ongoing when this fault was seen, it will also be reflected in {{< regref ERR_CODE.INVALID_OP >}}.
+This is considered an operational fault and begins transition to `Invalid`.
 
 #### Example 2: Fault During Idle
 The key manager is NOT running an operation and is idle.
 During this time, a fault was observed on the regfile (shadow storage error) and FSM (control FSM integrity error).
 The faults will be reflected in {{< regref FAULT_STATUS >}}.
-However, since there was no ongoing key manager operation, the error is **not** reflected in {{< regref ERR_CODE.INVALID_STATES >}}.
-This is **not** considered an operational fault and the key manager will remain in its current state.
+
+This is **not** considered an operational fault and the key manager will remain in its current state until an operation is invoked or the alert handler escalates.
 
 #### Example 3: Operation after Fault Detection
 Continuing from the example above, assume now the key manager begins an operation.
-Since the key manager has previous encountered a fault, any operation now is considered an operational fault and will be reflected in {{< regref ERR_CODE.INVALID_STATES >}}.
-This is considered an operational fault and begin transition to the `Invalid` [state](#invalid-entry-wiping).
+Since the key manager has previous encountered a fault, any operation now is considered an operational fault and begins transition to the `Invalid` [state](#invalid-entry-wiping).
 
 
-### Invalid Output
-When these errors occur, a fault alert is generated.
+#### Additional Details on Invalid Input
 
-### Invalid Input
-When these errors occur, an operation alert is generated
-What is considered invalid input depends on the current state and the operation called.
+What is considered invalid input changes based on current state and operation.
 
 When an advance operation is invoked:
 - The working state key is checked for all 0's and all 1's.
@@ -264,8 +313,7 @@
 When a generate output identity is invoked:
 - The working state key is checked for all 0's and all 1's.
 
-### Invalid Operation
-When these errors occur, an operation alert is generated.
+#### Invalid Operation
 
 The table below enumerates the legal operations in a given state.
 When an illegal operation is supplied, the error code is updated and the operation is flagged as `done with error`.
diff --git a/hw/ip/keymgr/dv/env/keymgr_env_cfg.sv b/hw/ip/keymgr/dv/env/keymgr_env_cfg.sv
index 2b10c08..ce6973f 100644
--- a/hw/ip/keymgr/dv/env/keymgr_env_cfg.sv
+++ b/hw/ip/keymgr/dv/env/keymgr_env_cfg.sv
@@ -19,11 +19,11 @@
     tl_intg_alert_name = "fatal_fault_err";
     has_edn = 1;
     super.initialize(csr_base_addr);
-    tl_intg_alert_fields[ral.err_code.invalid_states] = 1;
+    //tl_intg_alert_fields[ral.err_code.invalid_states] = 1;
     tl_intg_alert_fields[ral.fault_status.regfile_intg] = 1;
     shadow_update_err_status_fields[ral.err_code.invalid_shadow_update] = 1;
     shadow_storage_err_status_fields[ral.fault_status.shadow] = 1;
-    shadow_storage_err_status_fields[ral.err_code.invalid_states] = 1;
+    //shadow_storage_err_status_fields[ral.err_code.invalid_states] = 1;
 
     m_keymgr_kmac_agent_cfg = kmac_app_agent_cfg::type_id::create("m_keymgr_kmac_agent_cfg");
     m_keymgr_kmac_agent_cfg.if_mode = dv_utils_pkg::Device;
diff --git a/hw/ip/keymgr/dv/env/keymgr_scoreboard.sv b/hw/ip/keymgr/dv/env/keymgr_scoreboard.sv
index d25c8f8..63f8390 100644
--- a/hw/ip/keymgr/dv/env/keymgr_scoreboard.sv
+++ b/hw/ip/keymgr/dv/env/keymgr_scoreboard.sv
@@ -56,7 +56,7 @@
   keymgr_pkg::keymgr_op_status_e     current_op_status;
   bit                                is_kmac_rsp_err;
   bit                                is_kmac_invalid_data;
-  bit                                invalid_status_err;
+  bit                                is_fault_err;
   bit                                is_sw_share_corrupted;
 
   // HW internal key, used for OP in current state
@@ -135,7 +135,7 @@
     case (op)
       keymgr_pkg::OpAdvance: begin
         bit is_err = get_hw_invalid_input() || get_fault_err();
-
+        `uvm_info(`gfn, $sformatf("What is is_err: %d", is_err), UVM_MEDIUM)
         case (current_state)
           keymgr_pkg::StInit: begin
             compare_adv_creator_data(.cdi_type(current_cdi),
@@ -259,7 +259,7 @@
       begin
         cfg.clk_rst_vif.wait_n_clks(1);
         if (is_final_kdf) begin
-          if (get_err_code()) current_op_status = keymgr_pkg::OpDoneFail;
+          if (get_err_code() || get_fault_err()) current_op_status = keymgr_pkg::OpDoneFail;
           else                current_op_status = keymgr_pkg::OpDoneSuccess;
         end
       end
@@ -439,8 +439,6 @@
           `DV_CHECK_EQ(item.d_data[keymgr_pkg::ErrShadowUpdate],
                        err_code[keymgr_pkg::ErrShadowUpdate])
 
-          `DV_CHECK_EQ(item.d_data[keymgr_pkg::ErrInvalidStates],
-                       err_code[keymgr_pkg::ErrInvalidStates])
           // when op error occurs with keymgr_en = 0, input is meaningless. Design may or may not
           // assert ErrInvalidIn,  which doesn't matter
           if (!err_code[keymgr_pkg::ErrInvalidOp] || cfg.keymgr_vif.get_keymgr_en()) begin
@@ -608,8 +606,10 @@
           if (addr_phase_read) begin
             addr_phase_is_sw_share_corrupted = is_sw_share_corrupted;
           end else if (data_phase_read && addr_phase_is_sw_share_corrupted) begin
+            // disable read check outside of the item compare.
+            // it is possible for the returned data when corrupted, to be 0
+            do_read_check = 1'b0;
             if (item.d_data != 0) begin
-              do_read_check = 1'b0;
               `DV_CHECK_NE(item.d_data, `gmv(csr))
             end
           end
@@ -645,40 +645,51 @@
 
   virtual function void process_error_n_alert();
     bit [TL_DW-1:0] err = get_err_code();
+
+    // A detected fault will cause us to transition to invalid where
+    // operations are always error'd
+    if (get_fault_err()) begin
+      err[keymgr_pkg::ErrInvalidOp] = 1;
+    end
     void'(ral.err_code.predict(err));
 
-    if (get_fault_err()) begin
-      set_exp_alert("fatal_fault_err", .is_fatal(1));
-    end
     if (get_fault_err() || !cfg.keymgr_vif.get_keymgr_en()) begin
       is_sw_share_corrupted = 1;
       cfg.keymgr_vif.wipe_sideload_keys();
     end
+
+    if (get_fault_err()) begin
+      set_exp_alert("fatal_fault_err", .is_fatal(1));
+    end
+
     if (get_op_err()) set_exp_alert("recov_operation_err");
 
-    `uvm_info(`gfn, $sformatf("at %s, %s is issued and error code is 'b%0b",
+    `uvm_info(`gfn, $sformatf("at %s, %s is issued and error 'b%0b",
               current_state, get_operation(), err), UVM_MEDIUM)
   endfunction
 
   virtual function bit [TL_DW-1:0] get_fault_err();
-    bit [TL_DW-1:0] err = get_err_code();
-    return err[keymgr_pkg::ErrInvalidStates];
+
+    // faults are sticky, and will remain until reset
+    is_fault_err |= is_kmac_rsp_err | is_kmac_invalid_data;
+    return is_fault_err;
+
   endfunction
 
   virtual function bit [TL_DW-1:0] get_op_err();
     bit [TL_DW-1:0] err = get_err_code();
-    return err[keymgr_pkg::ErrInvalidOp] || err[keymgr_pkg::ErrInvalidIn];
+    bit fault = get_fault_err();
+
+    // A detected fault causes the operation to transition into invalid,
+    // which will report an invalid operation
+    return err[keymgr_pkg::ErrInvalidOp] || err[keymgr_pkg::ErrInvalidIn] ||
+      fault;
   endfunction
 
   virtual function bit [TL_DW-1:0] get_err_code();
     bit [TL_DW-1:0] err_code;
 
-    err_code[keymgr_pkg::ErrInvalidOp]  = get_op_error();
-
-    // this fault error is sticky, should preserve the value until reset
-    if (!invalid_status_err) invalid_status_err = is_kmac_rsp_err | is_kmac_invalid_data;
-    err_code[keymgr_pkg::ErrInvalidStates] = invalid_status_err;
-
+    err_code[keymgr_pkg::ErrInvalidOp] = get_op_error() | get_fault_err();
     err_code[keymgr_pkg::ErrInvalidIn] = get_hw_invalid_input() | get_sw_invalid_input();
 
     `uvm_info(`gfn, $sformatf({"op_err = %0d, rsp_err = %0d, hw_invalid = %0d, sw_invalid = %0d, ",
@@ -792,7 +803,7 @@
 
     if (current_state inside {keymgr_pkg::StCreatorRootKey, keymgr_pkg::StOwnerIntKey,
                               keymgr_pkg::StOwnerKey}) begin
-      return !(err_code[keymgr_pkg::ErrInvalidStates] |
+      return !(get_fault_err() |
                err_code[keymgr_pkg::ErrInvalidIn]  |
                !cfg.keymgr_vif.get_keymgr_en());
     end else begin
@@ -894,7 +905,7 @@
       `DV_CHECK_NE(act, exp, str)
     end
 
-    adv_data_a_array[Sealing][keymgr_pkg::StOwnerKey] = act;
+    if (exp_match) adv_data_a_array[Sealing][keymgr_pkg::StOwnerKey] = act;
   endfunction
 
   // for invalid OP, should not output any meaningful data to KMAC. Check the outputs aren't
@@ -1061,7 +1072,7 @@
     current_op_status     = keymgr_pkg::OpIdle;
     is_kmac_rsp_err       = 0;
     is_kmac_invalid_data  = 0;
-    invalid_status_err    = 0;
+    is_fault_err          = 0;
     is_sw_share_corrupted = 0;
     req_fifo.flush();
     rsp_fifo.flush();
diff --git a/hw/ip/keymgr/rtl/keymgr.sv b/hw/ip/keymgr/rtl/keymgr.sv
index 829400d..c2da1d7 100644
--- a/hw/ip/keymgr/rtl/keymgr.sv
+++ b/hw/ip/keymgr/rtl/keymgr.sv
@@ -242,7 +242,6 @@
     .sw_binding_unlock_o(sw_binding_unlock),
     .status_o(hw2reg.op_status.d),
     .fault_o(fault_code),
-    .fault_i(|reg2hw.fault_status),
     .error_o(err_code),
     .data_en_o(data_en),
     .data_valid_o(data_valid),
@@ -517,25 +516,19 @@
     .intr_o                 (intr_op_done_o)
   );
 
-  assign hw2reg.err_code.invalid_op.d             = reg2hw.err_code.invalid_op.q  |
-                                                    err_code[ErrInvalidOp];
-  assign hw2reg.err_code.invalid_kmac_input.d     = reg2hw.err_code.invalid_kmac_input.q |
-                                                    err_code[ErrInvalidIn];
-  assign hw2reg.err_code.invalid_shadow_update.d  = reg2hw.err_code.invalid_shadow_update.q |
-                                                    err_code[ErrShadowUpdate];
-  assign hw2reg.err_code.invalid_states.d         = reg2hw.err_code.invalid_states.q |
-                                                    err_code[ErrInvalidStates];
-  assign hw2reg.err_code.invalid_op.de            = 1'b1;
-  assign hw2reg.err_code.invalid_kmac_input.de    = 1'b1;
-  assign hw2reg.err_code.invalid_shadow_update.de = 1'b1;
-  assign hw2reg.err_code.invalid_states.de        = 1'b1;
+  assign hw2reg.err_code.invalid_op.d             = 1'b1;
+  assign hw2reg.err_code.invalid_kmac_input.d     = 1'b1;
+  assign hw2reg.err_code.invalid_shadow_update.d  = 1'b1;
+  assign hw2reg.err_code.invalid_op.de            = err_code[ErrInvalidOp];
+  assign hw2reg.err_code.invalid_kmac_input.de    = err_code[ErrInvalidIn];
+  assign hw2reg.err_code.invalid_shadow_update.de = err_code[ErrShadowUpdate];
 
   // detailed breakdown of the invalid_states field above
-  assign hw2reg.fault_status.cmd.de           = fault_code[FaultCmd];
+  assign hw2reg.fault_status.cmd.de           = fault_code[FaultKmacCmd];
   assign hw2reg.fault_status.kmac_fsm.de      = fault_code[FaultKmacFsm];
   assign hw2reg.fault_status.kmac_op.de       = fault_code[FaultKmacOp];
   assign hw2reg.fault_status.kmac_out.de      = fault_code[FaultKmacOut];
-  assign hw2reg.fault_status.regfile_intg.de  = fault_code[FaultRegFileIntg];
+  assign hw2reg.fault_status.regfile_intg.de  = fault_code[FaultRegIntg];
   assign hw2reg.fault_status.shadow.de        = fault_code[FaultShadow];
   assign hw2reg.fault_status.ctrl_fsm_intg.de = fault_code[FaultCtrlFsm];
   assign hw2reg.fault_status.ctrl_fsm_cnt.de  = fault_code[FaultCtrlCnt];
@@ -555,15 +548,13 @@
   logic fault_errs, fault_err_req_q, fault_err_req_d, fault_err_ack;
   logic op_errs, op_err_req_q, op_err_req_d, op_err_ack;
 
-  // Error code fatal faults occur only when keymgr operation is actually invoked.
   // Fault status can happen independently of any operation
-  assign fault_errs = |reg2hw.fault_status |
-                      err_code[ErrInvalidStates];
+  assign fault_errs = |reg2hw.fault_status;
 
   assign fault_err_req_d = fault_errs    ? 1'b1 :
                            fault_err_ack ? 1'b0 : fault_err_req_q;
 
-  assign op_errs = err_code[ErrInvalidOp] | err_code[ErrInvalidIn] | err_code[ErrShadowUpdate];
+  assign op_errs = |err_code;
   assign op_err_req_d = op_errs    ? 1'b1 :
                         op_err_ack ? 1'b0 : op_err_req_q;
 
diff --git a/hw/ip/keymgr/rtl/keymgr_ctrl.sv b/hw/ip/keymgr/rtl/keymgr_ctrl.sv
index 4f04ed4..d5731a3 100644
--- a/hw/ip/keymgr/rtl/keymgr_ctrl.sv
+++ b/hw/ip/keymgr/rtl/keymgr_ctrl.sv
@@ -35,7 +35,6 @@
   output keymgr_working_state_e working_state_o,
   output logic sw_binding_unlock_o,
   output logic init_o,
-  input fault_i,
 
   // Data input
   input  otp_ctrl_pkg::otp_keymgr_key_t root_key_i,
@@ -125,7 +124,7 @@
   logic [CdiWidth-1:0] cdi_cnt;
 
   // error conditions
-  logic kmac_out_valid;
+  logic invalid_kmac_out;
   logic invalid_op;
   logic cnt_err;
   // states fall out of sparsely encoded range
@@ -178,22 +177,30 @@
   ///////////////////////////
   //  interaction between operation fsm and software
   ///////////////////////////
+  // categories of keymgr errors
+  logic [SyncErrLastIdx-1:0] sync_err;
+  logic [AsyncErrLastIdx-1:0] async_err;
+  logic [SyncFaultLastIdx-1:0] sync_fault;
+  logic [AsyncFaultLastIdx-1:0] async_fault;
 
   logic op_err;
   logic op_fault_err;
 
   // unlock sw binding configuration whenever an advance call is made without errors
-  assign sw_binding_unlock_o = adv_req & op_ack & ~|error_o;
+  assign sw_binding_unlock_o = adv_req & op_ack & ~(op_err | op_fault_err);
 
   // error definition
   // check incoming kmac data validity
-  assign kmac_out_valid = valid_data_chk(kmac_data_i[0]) &
-                          (~KmacEnMasking | valid_data_chk(kmac_data_i[1]));
+  // Only check during the periods when there is actual kmac output
+  assign invalid_kmac_out = (op_update | op_ack) &
+                            (~valid_data_chk(kmac_data_i[0]) |
+                            (~valid_data_chk(kmac_data_i[1]) & KmacEnMasking));
 
-  assign op_err = error_o[ErrInvalidOp] |
-                  error_o[ErrInvalidIn];
+  assign op_err = sync_err[SyncErrInvalidOp] |
+                  sync_err[SyncErrInvalidIn];
 
-  assign op_fault_err = error_o[ErrInvalidStates];
+  assign op_fault_err = |sync_fault |
+                        |async_fault;
 
 
   ///////////////////////////
@@ -358,7 +365,7 @@
   logic adv_state;
   logic dis_state;
   logic inv_state;
-  assign adv_state = op_ack & adv_req;
+  assign adv_state = op_ack & adv_req & ~op_err;
   assign dis_state = op_ack & dis_req;
   assign inv_state = op_fault_err;
 
@@ -686,6 +693,7 @@
     endcase // unique case (adv_state_q)
   end
 
+
   // operations fsm update precedence
   // when in disabled state, always update.
   assign op_update_sel = (op_ack | op_update) & disabled ? KeyUpdateKmac :
@@ -693,26 +701,75 @@
                          op_err                          ? KeyUpdateIdle :
                          (op_ack | op_update)            ? KeyUpdateKmac : KeyUpdateIdle;
 
-  assign error_o[ErrInvalidOp]     = op_done_o & (invalid_op | disabled);
-  assign error_o[ErrInvalidIn]     = op_ack & kmac_input_invalid_i;
-  assign error_o[ErrShadowUpdate]  = shadowed_update_err_i;
-  assign error_o[ErrInvalidStates] = (op_done_o | op_update) & fault_i;
 
-  assign fault_o[FaultCmd]         = kmac_cmd_err_i;
-  assign fault_o[FaultKmacFsm]     = kmac_fsm_err_i;
-  assign fault_o[FaultKmacOp]      = kmac_op_err_i;
-  // Kmac output is only checked on operation complete.  Invalid
-  // values are legal otherwise
-  assign fault_o[FaultKmacOut]     = op_ack & ~kmac_out_valid;
-  assign fault_o[FaultRegFileIntg] = regfile_intg_err_i;
-  assign fault_o[FaultShadow]      = shadowed_storage_err_i;
-  assign fault_o[FaultCtrlFsm]     = state_intg_err_q;
-  assign fault_o[FaultCtrlCnt]     = cnt_err;
+  // Advance calls are made up of multiple rounds of kmac operations.
+  // Any sync error that occurs is treated as an error of the entire call.
+  // Therefore sync errors that happen before the end of the call must be
+  // latched.
+  logic[SyncErrLastIdx-1:0] sync_err_q, sync_err_d;
+  logic[SyncFaultLastIdx-1:0] sync_fault_q, sync_fault_d;
+
+  logic err_vld;
+  assign err_vld = op_update | op_done_o;
+
+  // sync errors
+  // When an operation encounters a fault, the operation is always rejected as the FSM
+  // transitions to wipe
+  assign sync_err_d[SyncErrInvalidOp] = err_vld & (invalid_op | disabled | op_fault_err);
+  assign sync_err_d[SyncErrInvalidIn] = err_vld & kmac_input_invalid_i;
+  always_ff @(posedge clk_i or negedge rst_ni) begin
+    if (!rst_ni) begin
+      sync_err_q <= '0;
+    end else if (op_done_o) begin
+      sync_err_q <= '0;
+    end else if (op_update) begin
+      sync_err_q <= sync_err_d;
+    end
+  end
+  assign sync_err = sync_err_q | sync_err_d;
+
+  // async errors
+  assign async_err[AsyncErrShadowUpdate] = shadowed_update_err_i;
+
+  // sync faults
+  assign sync_fault_d[SyncFaultKmacOp] = err_vld & kmac_op_err_i;
+  assign sync_fault_d[SyncFaultKmacOut] = err_vld & invalid_kmac_out;
+  always_ff @(posedge clk_i or negedge rst_ni) begin
+    if (!rst_ni) begin
+      sync_fault_q <= '0;
+    end else if (op_update) begin
+      sync_fault_q <= sync_fault_d;
+    end
+  end
+  assign sync_fault = sync_fault_q | sync_fault_d;
+
+  // async faults
+  assign async_fault[AsyncFaultKmacCmd] = kmac_cmd_err_i;
+  assign async_fault[AsyncFaultKmacFsm] = kmac_fsm_err_i;
+  assign async_fault[AsyncFaultRegIntg] = regfile_intg_err_i;
+  assign async_fault[AsyncFaultShadow ] = shadowed_storage_err_i;
+  assign async_fault[AsyncFaultFsmIntg] = state_intg_err_q;
+  assign async_fault[AsyncFaultCntErr ] = cnt_err;
+
+  // output to error code register
+  assign error_o[ErrInvalidOp]    = op_done_o & sync_err[SyncErrInvalidOp];
+  assign error_o[ErrInvalidIn]    = op_done_o & sync_err[SyncErrInvalidIn];
+  assign error_o[ErrShadowUpdate] = async_err[AsyncErrShadowUpdate];
+
+  // output to fault code register
+  assign fault_o[FaultKmacOp]  = op_done_o & sync_fault[SyncFaultKmacOp];
+  assign fault_o[FaultKmacOut] = op_done_o & sync_fault[SyncFaultKmacOut];
+  assign fault_o[FaultKmacCmd] = async_fault[AsyncFaultKmacCmd];
+  assign fault_o[FaultKmacFsm] = async_fault[AsyncFaultKmacFsm];
+  assign fault_o[FaultRegIntg] = async_fault[AsyncFaultRegIntg];
+  assign fault_o[FaultShadow]  = async_fault[AsyncFaultShadow];
+  assign fault_o[FaultCtrlFsm] = async_fault[AsyncFaultFsmIntg];
+  assign fault_o[FaultCtrlCnt] = async_fault[AsyncFaultCntErr];
 
   always_comb begin
     status_o = OpIdle;
     if (op_done_o) begin
-      status_o = |error_o ? OpDoneFail : OpDoneSuccess;
+      status_o = |error_o | |fault_o ? OpDoneFail : OpDoneSuccess;
     end else if (op_start_i) begin
       status_o = OpWip;
     end
diff --git a/hw/ip/keymgr/rtl/keymgr_pkg.sv b/hw/ip/keymgr/rtl/keymgr_pkg.sv
index aee9699..43c8651 100644
--- a/hw/ip/keymgr/rtl/keymgr_pkg.sv
+++ b/hw/ip/keymgr/rtl/keymgr_pkg.sv
@@ -140,23 +140,56 @@
     OpDoneFail = 3
   } keymgr_op_status_e;
 
+  // keymgr has 4 categories of errors
+  // sync errors  - recoverable errors that happen during keymgr operation
+  // async errors - recoverable errors that happen asynchronously
+  // sync faults  - fatal errors that happen during keymgr operation
+  // async faults - fatal errors that happen asynchronously
+
+  typedef enum logic [1:0] {
+    SyncErrInvalidOp,
+    SyncErrInvalidIn,
+    SyncErrLastIdx
+  } keymgr_sync_error_e;
+
+  typedef enum logic [1:0] {
+    AsyncErrShadowUpdate,
+    AsyncErrLastIdx
+  } keymgr_async_error_e;
+
+  typedef enum logic [1:0] {
+    SyncFaultKmacOp,
+    SyncFaultKmacOut,
+    SyncFaultLastIdx
+  } keymgr_sync_fault_e;
+
+  typedef enum logic [2:0] {
+    AsyncFaultKmacCmd,
+    AsyncFaultKmacFsm,
+    AsyncFaultRegIntg,
+    AsyncFaultShadow,
+    AsyncFaultFsmIntg,
+    AsyncFaultCntErr,
+    AsyncFaultLastIdx
+  } keymgr_async_fault_e;
+
+
   // Bit position of error code
   // Error is encoded as 1 error per bit
   typedef enum logic [2:0] {
     ErrInvalidOp,
     ErrInvalidIn,
     ErrShadowUpdate,
-    ErrInvalidStates,
     ErrLastPos
   } keymgr_err_pos_e;
 
   // Bit position of fault status
   typedef enum logic [3:0] {
-    FaultCmd,
+    FaultKmacCmd,
     FaultKmacFsm,
     FaultKmacOp,
     FaultKmacOut,
-    FaultRegFileIntg,
+    FaultRegIntg,
     FaultShadow,
     FaultCtrlFsm,
     FaultCtrlCnt,
diff --git a/hw/ip/keymgr/rtl/keymgr_reg_pkg.sv b/hw/ip/keymgr/rtl/keymgr_reg_pkg.sv
index 21ffbb0..67a2976 100644
--- a/hw/ip/keymgr/rtl/keymgr_reg_pkg.sv
+++ b/hw/ip/keymgr/rtl/keymgr_reg_pkg.sv
@@ -111,21 +111,6 @@
   typedef struct packed {
     struct packed {
       logic        q;
-    } invalid_op;
-    struct packed {
-      logic        q;
-    } invalid_kmac_input;
-    struct packed {
-      logic        q;
-    } invalid_shadow_update;
-    struct packed {
-      logic        q;
-    } invalid_states;
-  } keymgr_reg2hw_err_code_reg_t;
-
-  typedef struct packed {
-    struct packed {
-      logic        q;
     } cmd;
     struct packed {
       logic        q;
@@ -203,10 +188,6 @@
       logic        d;
       logic        de;
     } invalid_shadow_update;
-    struct packed {
-      logic        d;
-      logic        de;
-    } invalid_states;
   } keymgr_hw2reg_err_code_reg_t;
 
   typedef struct packed {
@@ -246,36 +227,35 @@
 
   // Register -> HW type
   typedef struct packed {
-    keymgr_reg2hw_intr_state_reg_t intr_state; // [944:944]
-    keymgr_reg2hw_intr_enable_reg_t intr_enable; // [943:943]
-    keymgr_reg2hw_intr_test_reg_t intr_test; // [942:941]
-    keymgr_reg2hw_alert_test_reg_t alert_test; // [940:937]
-    keymgr_reg2hw_control_reg_t control; // [936:929]
-    keymgr_reg2hw_sideload_clear_reg_t sideload_clear; // [928:926]
-    keymgr_reg2hw_reseed_interval_shadowed_reg_t reseed_interval_shadowed; // [925:910]
-    keymgr_reg2hw_sw_binding_regwen_reg_t sw_binding_regwen; // [909:908]
-    keymgr_reg2hw_sealing_sw_binding_mreg_t [7:0] sealing_sw_binding; // [907:652]
-    keymgr_reg2hw_attest_sw_binding_mreg_t [7:0] attest_sw_binding; // [651:396]
-    keymgr_reg2hw_salt_mreg_t [7:0] salt; // [395:140]
-    keymgr_reg2hw_key_version_mreg_t [0:0] key_version; // [139:108]
-    keymgr_reg2hw_max_creator_key_ver_shadowed_reg_t max_creator_key_ver_shadowed; // [107:76]
-    keymgr_reg2hw_max_owner_int_key_ver_shadowed_reg_t max_owner_int_key_ver_shadowed; // [75:44]
-    keymgr_reg2hw_max_owner_key_ver_shadowed_reg_t max_owner_key_ver_shadowed; // [43:12]
-    keymgr_reg2hw_err_code_reg_t err_code; // [11:8]
+    keymgr_reg2hw_intr_state_reg_t intr_state; // [940:940]
+    keymgr_reg2hw_intr_enable_reg_t intr_enable; // [939:939]
+    keymgr_reg2hw_intr_test_reg_t intr_test; // [938:937]
+    keymgr_reg2hw_alert_test_reg_t alert_test; // [936:933]
+    keymgr_reg2hw_control_reg_t control; // [932:925]
+    keymgr_reg2hw_sideload_clear_reg_t sideload_clear; // [924:922]
+    keymgr_reg2hw_reseed_interval_shadowed_reg_t reseed_interval_shadowed; // [921:906]
+    keymgr_reg2hw_sw_binding_regwen_reg_t sw_binding_regwen; // [905:904]
+    keymgr_reg2hw_sealing_sw_binding_mreg_t [7:0] sealing_sw_binding; // [903:648]
+    keymgr_reg2hw_attest_sw_binding_mreg_t [7:0] attest_sw_binding; // [647:392]
+    keymgr_reg2hw_salt_mreg_t [7:0] salt; // [391:136]
+    keymgr_reg2hw_key_version_mreg_t [0:0] key_version; // [135:104]
+    keymgr_reg2hw_max_creator_key_ver_shadowed_reg_t max_creator_key_ver_shadowed; // [103:72]
+    keymgr_reg2hw_max_owner_int_key_ver_shadowed_reg_t max_owner_int_key_ver_shadowed; // [71:40]
+    keymgr_reg2hw_max_owner_key_ver_shadowed_reg_t max_owner_key_ver_shadowed; // [39:8]
     keymgr_reg2hw_fault_status_reg_t fault_status; // [7:0]
   } keymgr_reg2hw_t;
 
   // HW -> register type
   typedef struct packed {
-    keymgr_hw2reg_intr_state_reg_t intr_state; // [564:563]
-    keymgr_hw2reg_cfg_regwen_reg_t cfg_regwen; // [562:562]
-    keymgr_hw2reg_control_reg_t control; // [561:560]
-    keymgr_hw2reg_sw_binding_regwen_reg_t sw_binding_regwen; // [559:559]
-    keymgr_hw2reg_sw_share0_output_mreg_t [7:0] sw_share0_output; // [558:295]
-    keymgr_hw2reg_sw_share1_output_mreg_t [7:0] sw_share1_output; // [294:31]
-    keymgr_hw2reg_working_state_reg_t working_state; // [30:27]
-    keymgr_hw2reg_op_status_reg_t op_status; // [26:24]
-    keymgr_hw2reg_err_code_reg_t err_code; // [23:16]
+    keymgr_hw2reg_intr_state_reg_t intr_state; // [562:561]
+    keymgr_hw2reg_cfg_regwen_reg_t cfg_regwen; // [560:560]
+    keymgr_hw2reg_control_reg_t control; // [559:558]
+    keymgr_hw2reg_sw_binding_regwen_reg_t sw_binding_regwen; // [557:557]
+    keymgr_hw2reg_sw_share0_output_mreg_t [7:0] sw_share0_output; // [556:293]
+    keymgr_hw2reg_sw_share1_output_mreg_t [7:0] sw_share1_output; // [292:29]
+    keymgr_hw2reg_working_state_reg_t working_state; // [28:25]
+    keymgr_hw2reg_op_status_reg_t op_status; // [24:22]
+    keymgr_hw2reg_err_code_reg_t err_code; // [21:16]
     keymgr_hw2reg_fault_status_reg_t fault_status; // [15:0]
   } keymgr_hw2reg_t;
 
diff --git a/hw/ip/keymgr/rtl/keymgr_reg_top.sv b/hw/ip/keymgr/rtl/keymgr_reg_top.sv
index fd3a6d9..d02dd53 100644
--- a/hw/ip/keymgr/rtl/keymgr_reg_top.sv
+++ b/hw/ip/keymgr/rtl/keymgr_reg_top.sv
@@ -297,8 +297,6 @@
   logic err_code_invalid_kmac_input_wd;
   logic err_code_invalid_shadow_update_qs;
   logic err_code_invalid_shadow_update_wd;
-  logic err_code_invalid_states_qs;
-  logic err_code_invalid_states_wd;
   logic fault_status_cmd_qs;
   logic fault_status_kmac_fsm_qs;
   logic fault_status_kmac_op_qs;
@@ -1983,7 +1981,7 @@
 
     // to internal hardware
     .qe     (),
-    .q      (reg2hw.err_code.invalid_op.q),
+    .q      (),
 
     // to register interface (read)
     .qs     (err_code_invalid_op_qs)
@@ -2009,7 +2007,7 @@
 
     // to internal hardware
     .qe     (),
-    .q      (reg2hw.err_code.invalid_kmac_input.q),
+    .q      (),
 
     // to register interface (read)
     .qs     (err_code_invalid_kmac_input_qs)
@@ -2035,39 +2033,13 @@
 
     // to internal hardware
     .qe     (),
-    .q      (reg2hw.err_code.invalid_shadow_update.q),
+    .q      (),
 
     // to register interface (read)
     .qs     (err_code_invalid_shadow_update_qs)
   );
 
 
-  //   F[invalid_states]: 3:3
-  prim_subreg #(
-    .DW      (1),
-    .SwAccess(prim_subreg_pkg::SwAccessW1C),
-    .RESVAL  (1'h0)
-  ) u_err_code_invalid_states (
-    .clk_i   (clk_i),
-    .rst_ni  (rst_ni),
-
-    // from register interface
-    .we     (err_code_we),
-    .wd     (err_code_invalid_states_wd),
-
-    // from internal hardware
-    .de     (hw2reg.err_code.invalid_states.de),
-    .d      (hw2reg.err_code.invalid_states.d),
-
-    // to internal hardware
-    .qe     (),
-    .q      (reg2hw.err_code.invalid_states.q),
-
-    // to register interface (read)
-    .qs     (err_code_invalid_states_qs)
-  );
-
-
   // R[fault_status]: V(False)
 
   //   F[cmd]: 0:0
@@ -2601,8 +2573,6 @@
 
   assign err_code_invalid_shadow_update_wd = reg_wdata[2];
 
-  assign err_code_invalid_states_wd = reg_wdata[3];
-
   // Read data return
   always_comb begin
     reg_rdata_next = '0;
@@ -2847,7 +2817,6 @@
         reg_rdata_next[0] = err_code_invalid_op_qs;
         reg_rdata_next[1] = err_code_invalid_kmac_input_qs;
         reg_rdata_next[2] = err_code_invalid_shadow_update_qs;
-        reg_rdata_next[3] = err_code_invalid_states_qs;
       end
 
       addr_hit[59]: begin
diff --git a/sw/device/lib/dif/dif_keymgr.c b/sw/device/lib/dif/dif_keymgr.c
index 3ac3415..e956207 100644
--- a/sw/device/lib/dif/dif_keymgr.c
+++ b/sw/device/lib/dif/dif_keymgr.c
@@ -108,9 +108,6 @@
 static_assert(kDifKeymgrStatusCodeInvalidOperation >> 1 ==
                   1 << KEYMGR_ERR_CODE_INVALID_OP_BIT,
               "Layout of ERR_CODE register changed.");
-static_assert(kDifKeymgrStatusCodeInvalidState >> 1 ==
-                  1 << KEYMGR_ERR_CODE_INVALID_STATES_BIT,
-              "Layout of ERR_CODE register changed.");
 static_assert(kDifKeymgrStatusCodeInvalidKmacInput >> 1 ==
                   1 << KEYMGR_ERR_CODE_INVALID_KMAC_INPUT_BIT,
               "Layout of ERR_CODE register changed.");
diff --git a/sw/device/lib/dif/dif_keymgr_unittest.cc b/sw/device/lib/dif/dif_keymgr_unittest.cc
index ba0fd9c..aafbcf5 100644
--- a/sw/device/lib/dif/dif_keymgr_unittest.cc
+++ b/sw/device/lib/dif/dif_keymgr_unittest.cc
@@ -529,14 +529,6 @@
         },
         GetStatusCodesTestCase{
             .reg_val = {{
-                .offset = KEYMGR_ERR_CODE_INVALID_STATES_BIT,
-                .value = 1,
-            }},
-            .exp_val = kDifKeymgrStatusCodeIdle |
-                       kDifKeymgrStatusCodeInvalidState,
-        },
-        GetStatusCodesTestCase{
-            .reg_val = {{
                 .offset = KEYMGR_ERR_CODE_INVALID_KMAC_INPUT_BIT,
                 .value = 1,
             }},
@@ -555,13 +547,7 @@
             .exp_val = kDifKeymgrStatusCodeIdle |
                        kDifKeymgrStatusCodeInvalidOperation |
                        kDifKeymgrStatusCodeInvalidKmacInput,
-        },
-        GetStatusCodesTestCase{.reg_val = {{
-                                   .offset = KEYMGR_ERR_CODE_INVALID_STATES_BIT,
-                                   .value = 1,
-                               }},
-                               .exp_val = kDifKeymgrStatusCodeIdle |
-                                          kDifKeymgrStatusCodeInvalidState}));
+        }));
 
 class GetStateTest : public DifKeymgrInitialized {};