[otbn, rtl] Implemented LOCKED state

Fixes #8023

Signed-off-by: Greg Chadwick <gac@lowrisc.org>
diff --git a/hw/ip/otbn/doc/_index.md b/hw/ip/otbn/doc/_index.md
index 14bd57a..23e8e36 100644
--- a/hw/ip/otbn/doc/_index.md
+++ b/hw/ip/otbn/doc/_index.md
@@ -921,6 +921,7 @@
 * To poll for a completed operation, software should repeatedly read the {{< regref "STATUS" >}} register.
   While the operation is in progress, {{< regref "STATUS" >}} is non-zero.
   The operation is complete if {{< regref "STATUS" >}} is `IDLE`.
+  If a fatal error occurs {{< regref "STATUS" >}} will become `LOCKED` and the operation will never complete.
 * Alternatively, software can listen for the `done` interrupt to determine if the operation has completed.
   The standard sequence of working with interrupts has to be followed, i.e. the interrupt has to be enabled, an interrupt service routine has to be registered, etc.
   The [DIF]({{<relref "#dif" >}}) contains helpers to do so conveniently.
diff --git a/hw/ip/otbn/dv/verilator/otbn_top_sim.sv b/hw/ip/otbn/dv/verilator/otbn_top_sim.sv
index 6b0d012..7a4bf5a 100644
--- a/hw/ip/otbn/dv/verilator/otbn_top_sim.sv
+++ b/hw/ip/otbn/dv/verilator/otbn_top_sim.sv
@@ -66,6 +66,7 @@
 
     .start_i                ( otbn_start       ),
     .done_o                 ( otbn_done_d      ),
+    .locked_o               (                  ),
 
     .err_bits_o             ( otbn_err_bits_d  ),
 
diff --git a/hw/ip/otbn/rtl/otbn.sv b/hw/ip/otbn/rtl/otbn.sv
index 9d0f0b0..edcd78c 100644
--- a/hw/ip/otbn/rtl/otbn.sv
+++ b/hw/ip/otbn/rtl/otbn.sv
@@ -88,6 +88,7 @@
   logic start_d, start_q;
   logic busy_execute_d, busy_execute_q;
   logic done;
+  logic locked;
   logic illegal_bus_access_d, illegal_bus_access_q;
 
   err_bits_t err_bits;
@@ -592,11 +593,14 @@
   always_comb begin
     unique case (1'b1)
       busy_execute_q: hw2reg.status.d = StatusBusyExecute;
+      locked:         hw2reg.status.d = StatusLocked;
       // TODO: Add other busy flags, and assert onehot encoding.
-      default: hw2reg.status.d = StatusIdle;
+      default:        hw2reg.status.d = StatusIdle;
     endcase
   end
 
+  `ASSERT(OtbnStatesOneHot, $onehot0({busy_execute_q, locked}))
+
   // 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.
@@ -756,6 +760,7 @@
 
     // Mux between model and RTL implementation at runtime.
     logic         done_model, done_rtl;
+    logic         locked_model, locked_rtl;
     logic         start_model, start_rtl;
     err_bits_t    err_bits_model, err_bits_rtl;
     logic [31:0]  insn_cnt_model, insn_cnt_rtl;
@@ -764,6 +769,7 @@
     logic [255:0] edn_rnd_data_model;
 
     assign done = otbn_use_model ? done_model : done_rtl;
+    assign locked = otbn_use_model ? locked_model : locked_rtl;
     assign err_bits = otbn_use_model ? err_bits_model : err_bits_rtl;
     assign insn_cnt = otbn_use_model ? insn_cnt_model : insn_cnt_rtl;
     assign start_model = start_q & otbn_use_model;
@@ -804,6 +810,8 @@
       .err_o ()
     );
 
+    assign locked_model = 1'b0;
+
     // RTL implementation
     otbn_core #(
       .RegFile(RegFile),
@@ -817,6 +825,7 @@
 
       .start_i                (start_rtl),
       .done_o                 (done_rtl),
+      .locked_o               (locked_rtl),
 
       .err_bits_o             (err_bits_rtl),
 
@@ -866,6 +875,7 @@
 
       .start_i                (start_q),
       .done_o                 (done),
+      .locked_o               (locked),
 
       .err_bits_o             (err_bits),
 
diff --git a/hw/ip/otbn/rtl/otbn_controller.sv b/hw/ip/otbn/rtl/otbn_controller.sv
index 101ccb8..c2e1af0 100644
--- a/hw/ip/otbn/rtl/otbn_controller.sv
+++ b/hw/ip/otbn/rtl/otbn_controller.sv
@@ -23,6 +23,7 @@
 
   input  logic  start_i, // start the processing at start_addr_i
   output logic  done_o,  // processing done, signaled by ECALL or error occurring
+  output logic  locked_o, // OTBN in locked state and must be reset to perform any further actions
 
   output err_bits_t err_bits_o, // valid when done_o is asserted
 
@@ -132,6 +133,7 @@
   otbn_state_e state_q, state_d, state_raw;
 
   logic err;
+  logic fatal_err;
   logic done_complete;
 
   logic insn_fetch_req_valid_raw;
@@ -233,6 +235,7 @@
   // error handling logic.
   assign done_complete = (insn_valid_i && insn_dec_shared_i.ecall_insn);
   assign done_o = done_complete | err;
+  assign locked_o = state_q == OtbnStateLocked;
 
   assign jump_or_branch = (insn_valid_i &
                            (insn_dec_shared_i.branch_insn | insn_dec_shared_i.jump_insn));
@@ -306,6 +309,10 @@
           state_raw = OtbnStateRun;
         end
       end
+      OtbnStateLocked: begin
+        insn_fetch_req_valid_raw = 1'b0;
+        state_raw = OtbnStateLocked;
+      end
       default: ;
     endcase
   end
@@ -314,7 +321,10 @@
   `ASSERT(StallIfNextStateStall, insn_valid_i & (state_d == OtbnStateStall) |-> stall)
 
   // On any error immediately halt and suppress any Imem request.
-  assign state_d = err ? OtbnStateHalt : state_raw;
+  assign state_d = fatal_err ? OtbnStateLocked :
+                   err       ? OtbnStateHalt   :
+                               state_raw;
+
   assign insn_fetch_req_valid_o = err ? 1'b0 : insn_fetch_req_valid_raw;
 
   // Determine if there are any errors related to the Imem fetch address.
@@ -350,11 +360,18 @@
   assign err_bits_o.bad_insn_addr        = imem_addr_err;
 
   assign err = |err_bits_o;
+  assign fatal_err = |{err_bits_o.lifecycle_escalation,
+                       err_bits_o.illegal_bus_access,
+                       err_bits_o.bus_intg_violation,
+                       err_bits_o.reg_intg_violation,
+                       err_bits_o.dmem_intg_violation,
+                       err_bits_o.imem_intg_violation};
 
   // Instructions must not execute if there is an error
   assign insn_executing = insn_valid_i & ~err;
 
   `ASSERT(ErrBitSetOnErr, err |-> |err_bits_o)
+  `ASSERT(ErrSetOnFatalErr, fatal_err |-> err)
 
   `ASSERT(ControllerStateValid, state_q inside {OtbnStateHalt, OtbnStateRun,
                                                 OtbnStateStall})
diff --git a/hw/ip/otbn/rtl/otbn_core.sv b/hw/ip/otbn/rtl/otbn_core.sv
index 2aab4d7..29387ee 100644
--- a/hw/ip/otbn/rtl/otbn_core.sv
+++ b/hw/ip/otbn/rtl/otbn_core.sv
@@ -32,6 +32,7 @@
 
   input  logic  start_i, // start the operation
   output logic  done_o,  // operation done
+  output logic  locked_o, // otbn locked, reset required to perform further commands
 
   output err_bits_t err_bits_o, // valid when done_o is asserted
 
@@ -264,6 +265,7 @@
 
     .start_i (controller_start),
     .done_o,
+    .locked_o,
 
     .err_bits_o,
 
diff --git a/hw/ip/otbn/rtl/otbn_pkg.sv b/hw/ip/otbn/rtl/otbn_pkg.sv
index ac6d303..1e45e21 100644
--- a/hw/ip/otbn/rtl/otbn_pkg.sv
+++ b/hw/ip/otbn/rtl/otbn_pkg.sv
@@ -379,11 +379,12 @@
   } mac_bignum_operation_t;
 
   // States for controller state machine
-  typedef enum logic [1:0] {
+  typedef enum logic [2:0] {
     OtbnStateHalt,
     OtbnStateUrndRefresh,
     OtbnStateRun,
-    OtbnStateStall
+    OtbnStateStall,
+    OtbnStateLocked
   } otbn_state_e;
 
   typedef enum logic [1:0] {
diff --git a/sw/device/lib/runtime/otbn.c b/sw/device/lib/runtime/otbn.c
index 8fa4246..b1de75b 100644
--- a/sw/device/lib/runtime/otbn.c
+++ b/sw/device/lib/runtime/otbn.c
@@ -47,7 +47,7 @@
     if (dif_otbn_get_status(&ctx->dif, &status) != kDifOtbnOk) {
       return kOtbnError;
     }
-    busy = status != kDifOtbnStatusIdle;
+    busy = status != kDifOtbnStatusIdle && status != kDifOtbnStatusLocked;
   }
 
   dif_otbn_err_bits_t err_bits;
diff --git a/sw/device/silicon_creator/lib/drivers/otbn.c b/sw/device/silicon_creator/lib/drivers/otbn.c
index 46c89cb..c467e2b 100644
--- a/sw/device/silicon_creator/lib/drivers/otbn.c
+++ b/sw/device/silicon_creator/lib/drivers/otbn.c
@@ -74,7 +74,7 @@
 
 bool otbn_is_busy() {
   uint32_t status = abs_mmio_read32(kBase + OTBN_STATUS_REG_OFFSET);
-  return status != kOtbnStatusIdle;
+  return status != kOtbnStatusIdle && status != kOtbnStatusLocked;
 }
 
 void otbn_get_err_bits(otbn_err_bits_t *err_bits) {