[otbn,dv] Properly mirror STATUS reg from ISS to SystemVerilog

Also, mirror all the other registers in a slightly less ad-hoc fashion
in ISSWrapper.

There's a bit of churn in the model connection, because we were using
a signal called "status" to mean "the current state of the model".
This signal is now called model_state.

The status_o signal isn't actually connected in the UVM or Verilator
testbenches yet (so doesn't really do anything): this will be
connected in a subsequent commit.

Signed-off-by: Rupert Swarbrick <rswarbrick@lowrisc.org>
diff --git a/hw/ip/otbn/dv/memutil/sv_utils.h b/hw/ip/otbn/dv/memutil/sv_utils.h
index 5e453a6..2b480b9 100644
--- a/hw/ip/otbn/dv/memutil/sv_utils.h
+++ b/hw/ip/otbn/dv/memutil/sv_utils.h
@@ -6,6 +6,15 @@
 
 #include <svdpi.h>
 
+// Utility function that packs a uint8_t into a SystemVerilog bit vector that
+// represents a "bit [7:0]"
+inline void set_sv_u8(svBitVecVal *dst, uint8_t src) {
+  for (int i = 0; i < 8; ++i) {
+    svBit bit = (src >> i) & 1;
+    svPutBitselBit(dst, i, bit);
+  }
+}
+
 // Utility function that packs a uint32_t into a SystemVerilog bit vector that
 // represents a "bit [31:0]"
 inline void set_sv_u32(svBitVecVal *dst, uint32_t src) {
diff --git a/hw/ip/otbn/dv/model/iss_wrapper.cc b/hw/ip/otbn/dv/model/iss_wrapper.cc
index e8cec8c..9a70d9d 100644
--- a/hw/ip/otbn/dv/model/iss_wrapper.cc
+++ b/hw/ip/otbn/dv/model/iss_wrapper.cc
@@ -193,10 +193,13 @@
 }
 
 // Read through trace output (in the lines argument) to pick up any write to
-// the named CSR register. If there is no such write, return default_val.
-static uint32_t read_ext_reg(const std::string &reg_name,
-                             const std::vector<std::string> &lines,
-                             uint32_t default_val) {
+// the named CSR register, updating *dest. If there is no such write and
+// required is true, returns false. Otherwise returns true.
+static bool read_ext_reg(const std::string &reg_name,
+                         const std::vector<std::string> &lines, uint32_t *dest,
+                         bool required) {
+  assert(dest);
+
   // We're interested in lines that show an update to the external register
   // called reg_name. These look something like this:
   //
@@ -204,7 +207,8 @@
   std::regex re("! otbn\\." + reg_name + ": 0x([0-9a-f]{8})");
   std::smatch match;
 
-  uint32_t val = default_val;
+  uint32_t val = 0;
+  bool found = false;
 
   for (const auto &line : lines) {
     if (std::regex_match(line, match, re)) {
@@ -212,11 +216,17 @@
       // that we can safely parse them to a uint32_t without risking a parse
       // failure or overflow.
       assert(match.size() == 2);
-      val = (uint32_t)strtoul(match[1].str().c_str(), nullptr, 16);
+      *dest = (uint32_t)strtoul(match[1].str().c_str(), nullptr, 16);
+      found = true;
     }
   }
 
-  return val;
+  if (required && !found) {
+    std::cerr << "ERROR: Expected register `" << reg_name
+              << "' not found in output.";
+    return false;
+  }
+  return true;
 }
 
 static std::string wlen_val_to_hex_str(uint32_t val[8]) {
@@ -230,8 +240,7 @@
   return oss.str();
 }
 
-ISSWrapper::ISSWrapper()
-    : tmpdir(new TmpDir()), insn_cnt_(0), err_bits_(0), stop_pc_(0) {
+ISSWrapper::ISSWrapper() : tmpdir(new TmpDir()) {
   std::string model_path(find_otbn_model());
 
   // We want two pipes: one for writing to the child process, and the other for
@@ -347,13 +356,16 @@
       << "\n";
   run_command(oss.str(), nullptr);
 
-  // "Reset" our mirror of INSN_CNT. This gets zeroed on this cycle in the
-  // Python model, but the text-based interface doesn't expose the change (and
-  // doing so would require some complicated rejigging). We'll get valid
-  // numbers as soon as an instruction executes, but zeroing here avoids having
-  // an old number for the stall cycles at the start of the second and
-  // subsequent runs.
-  insn_cnt_ = 0;
+  // Zero our mirror of INSN_CNT. This gets zeroed on this cycle in the Python
+  // model, but the text-based interface doesn't expose the change (and doing
+  // so would require some complicated rejigging). We'll get valid numbers as
+  // soon as an instruction executes, but zeroing here avoids having an old
+  // number for the stall cycles at the start of the second and subsequent
+  // runs.
+  mirrored_.insn_cnt = 0;
+
+  // Set our mirror of STATUS to BUSY_EXECUTE (= 1).
+  mirrored_.status = 1;
 }
 
 void ISSWrapper::edn_rnd_data(uint32_t edn_rnd_data[8]) {
@@ -368,30 +380,30 @@
 
 int ISSWrapper::step(bool gen_trace) {
   std::vector<std::string> lines;
-  bool mismatch = false;
+  bool error = false;
 
   run_command("step\n", &lines);
   if (gen_trace) {
-    mismatch = !OtbnTraceChecker::get().OnIssTrace(lines);
+    error = !OtbnTraceChecker::get().OnIssTrace(lines);
   }
 
-  // The busy flag is bit 0 of the STATUS register, so is cleared on this cycle
-  // if we see a write that sets the value to an even number.
-  bool done = (read_ext_reg("STATUS", lines, 1) & 1) == 0;
+  // Try to read STATUS, which is written when execution ends. Execution has
+  // finished if status_ is either 0 (IDLE) or 0xff (LOCKED)
+  read_ext_reg("STATUS", lines, &mirrored_.status, false);
+  bool done = (mirrored_.status == 0) || (mirrored_.status == 0xff);
 
-  // Always try to read INSN_CNT. On failure, don't update it (this
-  // happens on stall cycles)
-  insn_cnt_ = read_ext_reg("INSN_CNT", lines, insn_cnt_);
+  // Always try to read INSN_CNT
+  read_ext_reg("INSN_CNT", lines, &mirrored_.insn_cnt, false);
 
   // If we've just finished, try to read ERR_BITS and STOP_PC, storing
   // them into fields on this structure. The caller will retrieve them
   // after we've returned.
   if (done) {
-    err_bits_ = read_ext_reg("ERR_BITS", lines, 0);
-    stop_pc_ = read_ext_reg("STOP_PC", lines, 0);
+    error |= !read_ext_reg("ERR_BITS", lines, &mirrored_.err_bits, true);
+    error |= !read_ext_reg("STOP_PC", lines, &mirrored_.stop_pc, true);
   }
 
-  return mismatch ? -1 : (done ? 1 : 0);
+  return error ? -1 : (done ? 1 : 0);
 }
 
 void ISSWrapper::reset(bool gen_trace) {
diff --git a/hw/ip/otbn/dv/model/iss_wrapper.h b/hw/ip/otbn/dv/model/iss_wrapper.h
index 01965b9..713863c 100644
--- a/hw/ip/otbn/dv/model/iss_wrapper.h
+++ b/hw/ip/otbn/dv/model/iss_wrapper.h
@@ -15,6 +15,21 @@
 // Forward declaration (the implementation is private in iss_wrapper.cc)
 struct TmpDir;
 
+// OTBN has some externally visible CSRs that can be updated by hardware
+// (without explicit writes from software). The ISSWrapper mirrors the ISS's
+// versions of these registers in this structure.
+class MirroredRegs {
+ public:
+  MirroredRegs() : status(0), insn_cnt(0), err_bits(0), stop_pc(0) {}
+
+  uint32_t status;
+  uint32_t insn_cnt;
+  uint32_t err_bits;
+
+  // The final PC from the most recent run
+  uint32_t stop_pc;
+};
+
 // An object wrapping the ISS subprocess.
 struct ISSWrapper {
   // A 256-bit unsigned integer value, stored in "LSB order". Thus, words[0]
@@ -49,37 +64,29 @@
   // Signal URND reseed at beginning of execution is complete
   void edn_urnd_reseed_complete();
 
-  // Run simulation for a single cycle. Returns a pair (ret_code, err_bits).
+  // Run simulation for a single cycle.
   //
-  // If gen_trace is true, pass trace data to the (singleton)
-  // OtbnTraceChecker object.
+  // If gen_trace is true, pass trace data to the (singleton) OtbnTraceChecker
+  // object.
   //
-  // ret_code describes the state of the simulation. It is 1 if the simulation
-  // just stopped (on ECALL or an architectural error); it is 0 if the
-  // simulation is still running. It is -1 if something went wrong (such as a
-  // trace mismatch).
+  // The return code describes the state of the simulation. It is 1 if the
+  // simulation just stopped (on ECALL or an architectural error); it is 0 if
+  // the simulation is still running. It is -1 if something went wrong (such as
+  // a trace mismatch).
   //
-  // err_bits is zero unless the simulation just came to a halt, in which case
-  // it's the value of the ERR_BITS register.
+  // Updates mirrored versions of STATUS and INSN_CNT registers. If execution
+  // finishes (so we return 1), also updates mirrored versions of ERR_BITS and
+  // the final PC (see get_stop_pc()).
   int step(bool gen_trace);
 
   // Reset simulation
   //
-  // This doesn't actually send anything to the ISS, but instead tells
-  // the OtbnTraceChecker to clear out any partial instructions
+  // This doesn't actually send anything to the ISS, but instead tells the
+  // OtbnTraceChecker to clear out any partial instructions. It also resets
+  // mirrored registers to their initial states.
   void reset(bool gen_trace);
 
-  // Get the current value of otbn.INSN_CNT. This should be called just after
-  // step (but doesn't necessarily need to wait until the run has finished).
-  uint32_t get_insn_cnt() const { return insn_cnt_; }
-
-  // Get the err_bits from a run that's just finished. This should be
-  // called just after step() returns 1.
-  uint32_t get_err_bits() const { return err_bits_; }
-
-  // Get the final PC from a run that's just finished. This should be
-  // called just after step() returns 1.
-  uint32_t get_stop_pc() const { return stop_pc_; }
+  const MirroredRegs &get_mirrored() const { return mirrored_; }
 
   // Read contents of the register file
   void get_regs(std::array<uint32_t, 32> *gprs, std::array<u256_t, 32> *wdrs);
@@ -109,13 +116,8 @@
   // A temporary directory for communicating with the child process
   std::unique_ptr<TmpDir> tmpdir;
 
-  // INSN_CNT for the current run if there is one, or the previous run if
-  // there's no current one.
-  uint32_t insn_cnt_;
-
-  // ERR_BITS and STOP_PC values from a run that's just finished.
-  uint32_t err_bits_;
-  uint32_t stop_pc_;
+  // Mirrored copies of registers
+  MirroredRegs mirrored_;
 };
 
 #endif  // OPENTITAN_HW_IP_OTBN_DV_MODEL_ISS_WRAPPER_H_
diff --git a/hw/ip/otbn/dv/model/otbn_core_model.sv b/hw/ip/otbn/dv/model/otbn_core_model.sv
index 55ac0e9..222e751 100644
--- a/hw/ip/otbn/dv/model/otbn_core_model.sv
+++ b/hw/ip/otbn/dv/model/otbn_core_model.sv
@@ -40,6 +40,7 @@
   input logic [WLEN-1:0] edn_rnd_data_i,
   input logic            edn_urnd_data_valid_i, // URND reseed from EDN is valid
 
+  output bit [7:0]       status_o,   // STATUS register
   output bit [31:0]      insn_cnt_o, // INSN_CNT register
 
   output bit             err_o // something went wrong
@@ -58,13 +59,13 @@
     otbn_model_destroy(model_handle);
   end
 
-  // A packed "status" value. This gets assigned by DPI function calls that need to update both
-  // whether we're running and also error flags at the same time. The contents are magic simulation
-  // values, so get initialized before reset (to avoid stopping the simulation before it even
-  // starts).
-  int unsigned status = 0;
+  // A packed set of bits representing the state of the model. This gets assigned by DPI function
+  // calls that need to update both whether we're running and also error flags at the same time. The
+  // contents are magic simulation values, so get initialized before reset (to avoid stopping the
+  // simulation before it even starts).
+  int unsigned model_state = 0;
 
-  // Extract particular bits of the status value.
+  // Extract particular bits of the model_state value.
   //
   //   [0]     running:      The ISS is currently running
   //   [1]     check_due:    The ISS just finished but still needs to check results
@@ -72,45 +73,51 @@
   //   [3]     failed_cmp:   The consistency check at the end of run failed.
 
   bit failed_cmp, failed_step, check_due, running;
-  assign {failed_cmp, failed_step, check_due, running} = status[3:0];
+  assign {failed_cmp, failed_step, check_due, running} = model_state[3:0];
 
-  bit [31:0] raw_err_bits_d, raw_err_bits_q;
-  bit unused_raw_err_bits;
-
-  bit [31:0] stop_pc_d, stop_pc_q;
+  bit [7:0] status_d, status_q;
   bit [31:0] insn_cnt_d, insn_cnt_q;
+  bit [31:0] raw_err_bits_d, raw_err_bits_q;
+  bit [31:0] stop_pc_d, stop_pc_q;
+
+  bit unused_raw_err_bits;
 
   always_ff @(posedge clk_i or negedge rst_ni) begin
     if (!rst_ni) begin
-      if (status != 0) begin
+      if (model_state != 0) begin
         otbn_model_reset(model_handle);
       end
-      status <= 0;
+      model_state <= 0;
+      status_q <= 0;
+      insn_cnt_q <= 0;
       raw_err_bits_q <= 0;
       stop_pc_q <= 0;
-      insn_cnt_q <= 0;
     end else begin
       if (start_i | running | check_due) begin
-        status <= otbn_model_step(model_handle,
-                                  start_i,
-                                  status,
-                                  edn_rnd_data_valid_i, edn_rnd_data_i,
-                                  edn_urnd_data_valid_i,
-                                  insn_cnt_d,
-                                  raw_err_bits_d,
-                                  stop_pc_d);
+        model_state <= otbn_model_step(model_handle,
+                                       start_i,
+                                       model_state,
+                                       edn_rnd_data_valid_i, edn_rnd_data_i,
+                                       edn_urnd_data_valid_i,
+                                       status_d,
+                                       insn_cnt_d,
+                                       raw_err_bits_d,
+                                       stop_pc_d);
+        status_q <= status_d;
+        insn_cnt_q <= insn_cnt_d;
         raw_err_bits_q <= raw_err_bits_d;
         stop_pc_q <= stop_pc_d;
-        insn_cnt_q <= insn_cnt_d;
       end else begin
         // If we're not running and we're not being told to start, there's nothing to do.
       end
     end
   end
 
-  assign err_bits_o = raw_err_bits_q[$bits(err_bits_t)-1:0];
   assign unused_raw_err_bits = ^raw_err_bits_q[31:$bits(err_bits_t)];
 
+  assign err_bits_o = raw_err_bits_q[$bits(err_bits_t)-1:0];
+
+  assign status_o = status_q;
   assign insn_cnt_o = insn_cnt_q;
 
   // Track negedges of running_q and expose that as a "done" output.
diff --git a/hw/ip/otbn/dv/model/otbn_model.cc b/hw/ip/otbn/dv/model/otbn_model.cc
index 581b8e7..9fb7347 100644
--- a/hw/ip/otbn/dv/model/otbn_model.cc
+++ b/hw/ip/otbn/dv/model/otbn_model.cc
@@ -252,10 +252,11 @@
 int OtbnModel::step(svLogic edn_rnd_data_valid,
                     svLogicVecVal *edn_rnd_data, /* logic [255:0] */
                     svLogic edn_urnd_data_valid,
+                    svBitVecVal *status /* bit [7:0] */,
                     svBitVecVal *insn_cnt /* bit [31:0] */,
                     svBitVecVal *err_bits /* bit [31:0] */,
                     svBitVecVal *stop_pc /* bit [31:0] */) {
-  assert(err_bits);
+  assert(edn_rnd_data && err_bits && insn_cnt && err_bits && stop_pc);
 
   ISSWrapper *iss = ensure_wrapper();
   if (!iss)
@@ -282,15 +283,25 @@
         return -1;
 
       case 1:
-        // The simulation has stopped. Fill in insn_cnt, err_bits and stop_pc.
-        set_sv_u32(insn_cnt, iss->get_insn_cnt());
-        set_sv_u32(err_bits, iss->get_err_bits());
-        set_sv_u32(stop_pc, iss->get_stop_pc());
+        // The simulation has stopped. Fill in status, insn_cnt, err_bits and
+        // stop_pc. Note that status should never have anything in its top 24
+        // bits.
+        if (iss->get_mirrored().status >> 8) {
+          throw std::runtime_error("STATUS register had non-empty top bits.");
+        }
+        set_sv_u8(status, iss->get_mirrored().status);
+        set_sv_u32(insn_cnt, iss->get_mirrored().insn_cnt);
+        set_sv_u32(err_bits, iss->get_mirrored().err_bits);
+        set_sv_u32(stop_pc, iss->get_mirrored().stop_pc);
         return 1;
 
       case 0:
-        // The simulation is still running. Update insn_cnt.
-        set_sv_u32(insn_cnt, iss->get_insn_cnt());
+        // The simulation is still running. Update status and insn_cnt.
+        if (iss->get_mirrored().status >> 8) {
+          throw std::runtime_error("STATUS register had non-empty top bits.");
+        }
+        set_sv_u8(status, iss->get_mirrored().status);
+        set_sv_u32(insn_cnt, iss->get_mirrored().insn_cnt);
         return 0;
 
       default:
@@ -537,18 +548,19 @@
 
 void otbn_model_destroy(OtbnModel *model) { delete model; }
 
-unsigned otbn_model_step(OtbnModel *model, svLogic start, unsigned status,
+unsigned otbn_model_step(OtbnModel *model, svLogic start, unsigned model_state,
                          svLogic edn_rnd_data_valid,
                          svLogicVecVal *edn_rnd_data, /* logic [255:0] */
                          svLogic edn_urnd_data_valid,
+                         svBitVecVal *status /* bit [7:0] */,
                          svBitVecVal *insn_cnt /* bit [31:0] */,
                          svBitVecVal *err_bits /* bit [31:0] */,
                          svBitVecVal *stop_pc /* bit [31:0] */) {
-  assert(model && insn_cnt && err_bits && stop_pc);
+  assert(model && status && insn_cnt && err_bits && stop_pc);
 
   // Run model checks if needed. This usually happens just after an operation
   // has finished.
-  if (model->has_rtl() && (status & CHECK_DUE_BIT)) {
+  if (model->has_rtl() && (model_state & CHECK_DUE_BIT)) {
     switch (model->check()) {
       case 1:
         // Match (success)
@@ -556,14 +568,14 @@
 
       case 0:
         // Mismatch
-        status |= FAILED_CMP_BIT;
+        model_state |= FAILED_CMP_BIT;
         break;
 
       default:
         // Something went wrong
-        return (status & ~RUNNING_BIT) | FAILED_STEP_BIT;
+        return (model_state & ~RUNNING_BIT) | FAILED_STEP_BIT;
     }
-    status &= ~CHECK_DUE_BIT;
+    model_state &= ~CHECK_DUE_BIT;
   }
 
   assert(!is_xz(start));
@@ -573,39 +585,39 @@
     switch (model->start()) {
       case 0:
         // All good
-        status |= RUNNING_BIT;
+        model_state |= RUNNING_BIT;
         break;
 
       default:
         // Something went wrong.
-        return (status & ~RUNNING_BIT) | FAILED_STEP_BIT;
+        return (model_state & ~RUNNING_BIT) | FAILED_STEP_BIT;
     }
   }
 
   // If the model isn't running, there's nothing more to do.
-  if (!(status & RUNNING_BIT))
-    return status;
+  if (!(model_state & RUNNING_BIT))
+    return model_state;
 
   // Step the model once
   switch (model->step(edn_rnd_data_valid, edn_rnd_data, edn_urnd_data_valid,
-                      insn_cnt, err_bits, stop_pc)) {
+                      status, insn_cnt, err_bits, stop_pc)) {
     case 0:
       // Still running: no change
       break;
 
     case 1:
       // Finished
-      status = (status & ~RUNNING_BIT) | CHECK_DUE_BIT;
+      model_state = (model_state & ~RUNNING_BIT) | CHECK_DUE_BIT;
       break;
 
     default:
       // Something went wrong
-      return (status & ~RUNNING_BIT) | FAILED_STEP_BIT;
+      return (model_state & ~RUNNING_BIT) | FAILED_STEP_BIT;
   }
 
   // If we're still running, there's nothing more to do.
-  if (status & RUNNING_BIT)
-    return status;
+  if (model_state & RUNNING_BIT)
+    return model_state;
 
   // If we've just stopped running and there's no RTL, load the contents of
   // DMEM back from the ISS
@@ -617,11 +629,11 @@
 
       default:
         // Failed to load DMEM
-        return (status & ~RUNNING_BIT) | FAILED_STEP_BIT;
+        return (model_state & ~RUNNING_BIT) | FAILED_STEP_BIT;
     }
   }
 
-  return status;
+  return model_state;
 }
 
 void otbn_model_reset(OtbnModel *model) {
diff --git a/hw/ip/otbn/dv/model/otbn_model.h b/hw/ip/otbn/dv/model/otbn_model.h
index 889e871..c3d6c88 100644
--- a/hw/ip/otbn/dv/model/otbn_model.h
+++ b/hw/ip/otbn/dv/model/otbn_model.h
@@ -38,7 +38,8 @@
   // checker. If the model has finished, writes otbn.ERR_BITS to *err_bits.
   int step(svLogic edn_rnd_data_valid,
            svLogicVecVal *edn_rnd_data, /* logic [255:0] */
-           svLogic edn_urnd_data_valid, svBitVecVal *insn_cnt /* bit [31:0] */,
+           svLogic edn_urnd_data_valid, svBitVecVal *status /* bit [7:0] */,
+           svBitVecVal *insn_cnt /* bit [31:0] */,
            svBitVecVal *err_bits /* bit [31:0] */,
            svBitVecVal *stop_pc /* bit [31:0] */);
 
diff --git a/hw/ip/otbn/dv/model/otbn_model_dpi.h b/hw/ip/otbn/dv/model/otbn_model_dpi.h
index 3031b28..33b7d45 100644
--- a/hw/ip/otbn/dv/model/otbn_model_dpi.h
+++ b/hw/ip/otbn/dv/model/otbn_model_dpi.h
@@ -25,7 +25,7 @@
 // The main entry point to the OTBN model, exported from here and used in
 // otbn_core_model.sv.
 //
-// This communicates state with otbn_core_model.sv through the status
+// This communicates state with otbn_core_model.sv through the model_state
 // parameter, which has the following bits:
 //
 //    Bit 0:      running       True if the model is currently running
@@ -34,9 +34,9 @@
 //    Bit 3:      failed_cmp    Consistency check at end of run failed
 //
 // The otbn_model_step function should only be called when either the model is
-// running (bit 0 of status), has a check due (bit 1 of status), or when start
-// is asserted. At other times, it will return immediately (but wastes a DPI
-// call).
+// running (bit 0 of model_state), has a check due (bit 1 of model_state), or
+// when start is asserted. At other times, it will return immediately (but
+// wastes a DPI call).
 //
 // If the model is running and start is false, otbn_model_step steps the ISS by
 // a single cycle. If something goes wrong, it will set failed_step to true and
@@ -54,10 +54,11 @@
 //
 // If start is true, we start the model and then step once (as described
 // above).
-unsigned otbn_model_step(OtbnModel *model, svLogic start, unsigned status,
+unsigned otbn_model_step(OtbnModel *model, svLogic start, unsigned model_state,
                          svLogic edn_rnd_data_valid,
                          svLogicVecVal *edn_rnd_data, /* logic [255:0] */
                          svLogic edn_urnd_data_valid,
+                         svBitVecVal *status /* bit [7:0] */,
                          svBitVecVal *insn_cnt /* bit [31:0] */,
                          svBitVecVal *err_bits /* bit [31:0] */,
                          svBitVecVal *stop_pc /* bit [31:0] */);
diff --git a/hw/ip/otbn/dv/model/otbn_model_pkg.sv b/hw/ip/otbn/dv/model/otbn_model_pkg.sv
index 028d03e..eb927d4 100644
--- a/hw/ip/otbn/dv/model/otbn_model_pkg.sv
+++ b/hw/ip/otbn/dv/model/otbn_model_pkg.sv
@@ -19,10 +19,11 @@
   import "DPI-C" context function
     int unsigned otbn_model_step(chandle          model,
                                  logic            start,
-                                 int unsigned     status,
+                                 int unsigned     model_state,
                                  logic            edn_rnd_data_valid,
                                  logic [WLEN-1:0] edn_rnd_data,
                                  logic            edn_urnd_data_valid,
+                                 inout bit [7:0]  status,
                                  inout bit [31:0] insn_cnt,
                                  inout bit [31:0] err_bits,
                                  inout bit [31:0] stop_pc);
diff --git a/hw/ip/otbn/dv/uvm/tb.sv b/hw/ip/otbn/dv/uvm/tb.sv
index ea63270..3ba5021 100644
--- a/hw/ip/otbn/dv/uvm/tb.sv
+++ b/hw/ip/otbn/dv/uvm/tb.sv
@@ -191,7 +191,9 @@
     .edn_rnd_data_i        (dut.edn_rnd_data),
     .edn_urnd_data_valid_i (edn_urnd_data_valid),
 
+    .status_o     (),
     .insn_cnt_o   (model_insn_cnt),
+
     .err_o        (model_if.err)
   );
 
diff --git a/hw/ip/otbn/dv/verilator/otbn_top_sim.sv b/hw/ip/otbn/dv/verilator/otbn_top_sim.sv
index 8f803ba..f230473 100644
--- a/hw/ip/otbn/dv/verilator/otbn_top_sim.sv
+++ b/hw/ip/otbn/dv/verilator/otbn_top_sim.sv
@@ -290,6 +290,7 @@
     .edn_rnd_data_i        ( edn_rnd_data ),
     .edn_urnd_data_valid_i ( edn_urnd_data_valid ),
 
+    .status_o              (),
     .insn_cnt_o            ( otbn_model_insn_cnt ),
 
     .err_o                 ( otbn_model_err )