[otbn] Implement illegal bus access error & alert

Any time the bus tries to access the OTBN memories whilst OTBN is active
a fatal alert is triggered and OTBN halts immediately indicating an
error.

Fixes #7470
Fixes #2696

Signed-off-by: Greg Chadwick <gac@lowrisc.org>
diff --git a/hw/ip/otbn/dv/verilator/otbn_top_sim.sv b/hw/ip/otbn/dv/verilator/otbn_top_sim.sv
index e5d9e51..9abd46f 100644
--- a/hw/ip/otbn/dv/verilator/otbn_top_sim.sv
+++ b/hw/ip/otbn/dv/verilator/otbn_top_sim.sv
@@ -61,42 +61,43 @@
     .ImemSizeByte ( ImemSizeByte ),
     .DmemSizeByte ( DmemSizeByte )
   ) u_otbn_core (
-    .clk_i           ( IO_CLK           ),
-    .rst_ni          ( IO_RST_N         ),
+    .clk_i                ( IO_CLK           ),
+    .rst_ni               ( IO_RST_N         ),
 
-    .start_i         ( otbn_start       ),
-    .done_o          ( otbn_done_d      ),
+    .start_i              ( otbn_start       ),
+    .done_o               ( otbn_done_d      ),
 
-    .err_bits_o      ( otbn_err_bits_d  ),
+    .err_bits_o           ( otbn_err_bits_d  ),
 
-    .start_addr_i    ( ImemStartAddr    ),
+    .start_addr_i         ( ImemStartAddr    ),
 
-    .imem_req_o      ( imem_req         ),
-    .imem_addr_o     ( imem_addr        ),
-    .imem_wdata_o    (                  ),
-    .imem_rdata_i    ( imem_rdata[31:0] ),
-    .imem_rvalid_i   ( imem_rvalid      ),
-    .imem_rerror_i   ( imem_rerror      ),
+    .imem_req_o           ( imem_req         ),
+    .imem_addr_o          ( imem_addr        ),
+    .imem_wdata_o         (                  ),
+    .imem_rdata_i         ( imem_rdata[31:0] ),
+    .imem_rvalid_i        ( imem_rvalid      ),
+    .imem_rerror_i        ( imem_rerror      ),
 
-    .dmem_req_o      ( dmem_req         ),
-    .dmem_write_o    ( dmem_write       ),
-    .dmem_addr_o     ( dmem_addr        ),
-    .dmem_wdata_o    ( dmem_wdata       ),
-    .dmem_wmask_o    ( dmem_wmask       ),
-    .dmem_rmask_o    ( ),
-    .dmem_rdata_i    ( dmem_rdata       ),
-    .dmem_rvalid_i   ( dmem_rvalid      ),
-    .dmem_rerror_i   ( dmem_rerror      ),
+    .dmem_req_o           ( dmem_req         ),
+    .dmem_write_o         ( dmem_write       ),
+    .dmem_addr_o          ( dmem_addr        ),
+    .dmem_wdata_o         ( dmem_wdata       ),
+    .dmem_wmask_o         ( dmem_wmask       ),
+    .dmem_rmask_o         ( ),
+    .dmem_rdata_i         ( dmem_rdata       ),
+    .dmem_rvalid_i        ( dmem_rvalid      ),
+    .dmem_rerror_i        ( dmem_rerror      ),
 
-    .edn_rnd_req_o   ( edn_rnd_req      ),
-    .edn_rnd_ack_i   ( edn_rnd_ack      ),
-    .edn_rnd_data_i  ( edn_rnd_data     ),
+    .edn_rnd_req_o        ( edn_rnd_req      ),
+    .edn_rnd_ack_i        ( edn_rnd_ack      ),
+    .edn_rnd_data_i       ( edn_rnd_data     ),
 
-    .edn_urnd_req_o  ( edn_urnd_req     ),
-    .edn_urnd_ack_i  ( edn_urnd_ack     ),
-    .edn_urnd_data_i ( edn_urnd_data    ),
+    .edn_urnd_req_o       ( edn_urnd_req     ),
+    .edn_urnd_ack_i       ( edn_urnd_ack     ),
+    .edn_urnd_data_i      ( edn_urnd_data    ),
 
-    .insn_cnt_o      ( insn_cnt )
+    .insn_cnt_o           ( insn_cnt         ),
+    .illegal_bus_access_i ( 1'b0             )
   );
 
   // The top bits of IMEM rdata aren't currently used (they will eventually be used for integrity
diff --git a/hw/ip/otbn/rtl/otbn.sv b/hw/ip/otbn/rtl/otbn.sv
index 5400561..af486cb 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_d, busy_q;
   logic done;
+  logic illegal_bus_access_d, illegal_bus_access_q;
 
   err_bits_t err_bits;
 
@@ -170,6 +171,7 @@
   logic imem_rvalid;
   logic [1:0] imem_rerror_vec;
   logic imem_rerror;
+  logic imem_illegal_bus_access;
 
   logic imem_req_core;
   logic imem_write_core;
@@ -180,6 +182,7 @@
   logic imem_rerror_core;
 
   logic imem_req_bus;
+  logic imem_dummy_response_q, imem_dummy_response_d;
   logic imem_write_bus;
   logic [ImemIndexWidth-1:0] imem_index_bus;
   logic [38:0] imem_wdata_bus;
@@ -283,7 +286,8 @@
 
   // IMEM access from main TL-UL bus
   logic imem_gnt_bus;
-  assign imem_gnt_bus = imem_req_bus & ~imem_access_core;
+  // Always grant to bus accesses, when OTBN is running a dummy response is returned
+  assign imem_gnt_bus = imem_req_bus;
 
   tlul_adapter_sram #(
     .SramAw      (ImemIndexWidth),
@@ -311,6 +315,7 @@
     .rerror_i    (imem_rerror_bus         )
   );
 
+
   // Mux core and bus access into IMEM
   assign imem_access_core = busy_q | start_q;
 
@@ -319,6 +324,17 @@
   assign imem_index = imem_access_core ? imem_index_core      : imem_index_bus;
   assign imem_wdata = imem_access_core ? 39'(imem_wdata_core) : imem_wdata_bus;
 
+  assign imem_illegal_bus_access = imem_req_bus & imem_access_core;
+
+  assign imem_dummy_response_d = imem_illegal_bus_access;
+  always_ff @(posedge clk_i or negedge rst_ni) begin
+    if (!rst_ni) begin
+      imem_dummy_response_q <= 1'b0;
+    end else begin
+      imem_dummy_response_q <= imem_dummy_response_d;
+    end
+  end
+
   // The instruction memory only supports 32b word writes, so we hardcode its
   // wmask here.
   //
@@ -334,10 +350,11 @@
   // Explicitly tie off bus interface during core operation to avoid leaking
   // the currently executed instruction from IMEM through the bus
   // unintentionally.
-  assign imem_rdata_bus  = !imem_access_core ? imem_rdata : 39'b0;
+  assign imem_rdata_bus  = !imem_access_core && !illegal_bus_access_q ? imem_rdata : 39'b0;
   assign imem_rdata_core = imem_rdata[31:0];
 
-  assign imem_rvalid_bus  = !imem_access_core ? imem_rvalid : 1'b0;
+  // When an illegal bus access is seen, always return a dummy response the follow cycle.
+  assign imem_rvalid_bus  = (~imem_access_core & imem_rvalid) | imem_dummy_response_q;
   assign imem_rvalid_core = imem_access_core ? imem_rvalid : 1'b0;
 
   // imem_rerror_bus is passed to a TLUL adapter to report read errors back to the TL interface.
@@ -368,6 +385,7 @@
   logic dmem_rvalid;
   logic [BaseWordsPerWLEN*2-1:0] dmem_rerror_vec;
   logic dmem_rerror;
+  logic dmem_illegal_bus_access;
 
   logic dmem_req_core;
   logic dmem_write_core;
@@ -380,6 +398,7 @@
   logic dmem_rerror_core;
 
   logic dmem_req_bus;
+  logic dmem_dummy_response_q, dmem_dummy_response_d;
   logic dmem_write_bus;
   logic [DmemIndexWidth-1:0] dmem_index_bus;
   logic [ExtWLEN-1:0] dmem_wdata_bus;
@@ -465,7 +484,8 @@
 
   // DMEM access from main TL-UL bus
   logic dmem_gnt_bus;
-  assign dmem_gnt_bus = dmem_req_bus & ~dmem_access_core;
+  // Always grant to bus accesses, when OTBN is running a dummy response is returned
+  assign dmem_gnt_bus = dmem_req_bus;
 
   tlul_adapter_sram #(
     .SramAw      (DmemIndexWidth),
@@ -502,13 +522,26 @@
   assign dmem_index = dmem_access_core ? dmem_index_core : dmem_index_bus;
   assign dmem_wdata = dmem_access_core ? dmem_wdata_core : dmem_wdata_bus;
 
+  assign dmem_illegal_bus_access = dmem_req_bus & dmem_access_core;
+
+  assign dmem_dummy_response_d = dmem_illegal_bus_access;
+  always_ff @(posedge clk_i or negedge rst_ni) begin
+    if (!rst_ni) begin
+      dmem_dummy_response_q <= 1'b0;
+    end else begin
+      dmem_dummy_response_q <= dmem_dummy_response_d;
+    end
+  end
+
   // Explicitly tie off bus interface during core operation to avoid leaking
-  // DMEM data through the bus unintentionally.
-  assign dmem_rdata_bus  = !dmem_access_core ? dmem_rdata : '0;
+  // DMEM data through the bus unintentionally. Once an illegal bus access is seen always return
+  // 0 data.
+  assign dmem_rdata_bus  = !dmem_access_core && !illegal_bus_access_q ? dmem_rdata : '0;
   assign dmem_rdata_core = dmem_rdata;
 
-  assign dmem_rvalid_bus  = !dmem_access_core ? dmem_rvalid : 1'b0;
-  assign dmem_rvalid_core = dmem_access_core  ? dmem_rvalid : 1'b0;
+  // When an illegal bus access is seen, always return a dummy response the follow cycle.
+  assign dmem_rvalid_bus  = (~dmem_access_core & dmem_rvalid) | dmem_dummy_response_q;
+  assign dmem_rvalid_core = dmem_access_core ? dmem_rvalid : 1'b0;
 
   // Expand the error signal to 2 bits and mask when the core has access. See note above
   // imem_rerror_bus for details.
@@ -547,12 +580,17 @@
   // CMD.start ("start" is omitted by reggen since it is the only field).
   // start is flopped to avoid long timing paths from the TL fabric into OTBN internals.
   assign start_d = reg2hw.cmd.qe & reg2hw.cmd.q;
+  assign illegal_bus_access_d = dmem_illegal_bus_access | imem_illegal_bus_access;
 
+  // Flop `illegal_bus_access_q` so we know an illegal bus access has happened and to break a timing
+  // path from the TL interface into the OTBN core.
   always_ff @(posedge clk_i or negedge rst_ni) begin
     if (!rst_ni) begin
-      start_q <= 1'b0;
+      start_q              <= 1'b0;
+      illegal_bus_access_q <= 1'b0;
     end else begin
-      start_q <= start_d;
+      start_q              <= start_d;
+      illegal_bus_access_q <= illegal_bus_access_d;
     end
   end
 
@@ -606,8 +644,8 @@
   // TODO: Register file errors
   assign hw2reg.fatal_alert_cause.reg_error.de = 0;
   assign hw2reg.fatal_alert_cause.reg_error.d  = 0;
-  assign hw2reg.fatal_alert_cause.illegal_bus_access.de = 1'b0; // TODO: implement
-  assign hw2reg.fatal_alert_cause.illegal_bus_access.d  = 1'b0; // TODO: implement
+  assign hw2reg.fatal_alert_cause.illegal_bus_access.de = illegal_bus_access_d;
+  assign hw2reg.fatal_alert_cause.illegal_bus_access.d  = illegal_bus_access_d;
 
   // INSN_CNT register
   logic [31:0] insn_cnt;
@@ -622,7 +660,10 @@
                                   reg2hw.alert_test.recov.qe;
 
   logic [NumAlerts-1:0] alerts;
-  assign alerts[AlertFatal] = bus_integrity_error | imem_rerror | dmem_rerror;
+  assign alerts[AlertFatal] = illegal_bus_access_d |
+                              bus_integrity_error  |
+                              imem_rerror          |
+                              dmem_rerror;
   assign alerts[AlertRecov] = 1'b0; // TODO: Implement
 
   for (genvar i = 0; i < NumAlerts; i++) begin: gen_alert_tx
@@ -765,41 +806,42 @@
       .RndCnstUrndChunkLfsrPerm(RndCnstUrndChunkLfsrPerm)
     ) u_otbn_core (
       .clk_i,
-      .rst_ni (rst_n),
+      .rst_ni               (rst_n),
 
-      .start_i (start_rtl),
-      .done_o  (done_rtl),
+      .start_i              (start_rtl),
+      .done_o               (done_rtl),
 
-      .err_bits_o (err_bits_rtl),
+      .err_bits_o           (err_bits_rtl),
 
-      .start_addr_i  (start_addr),
+      .start_addr_i         (start_addr),
 
-      .imem_req_o    (imem_req_core),
-      .imem_addr_o   (imem_addr_core),
-      .imem_wdata_o  (imem_wdata_core),
-      .imem_rdata_i  (imem_rdata_core),
-      .imem_rvalid_i (imem_rvalid_core),
-      .imem_rerror_i (imem_rerror_core),
+      .imem_req_o           (imem_req_core),
+      .imem_addr_o          (imem_addr_core),
+      .imem_wdata_o         (imem_wdata_core),
+      .imem_rdata_i         (imem_rdata_core),
+      .imem_rvalid_i        (imem_rvalid_core),
+      .imem_rerror_i        (imem_rerror_core),
 
-      .dmem_req_o    (dmem_req_core),
-      .dmem_write_o  (dmem_write_core),
-      .dmem_addr_o   (dmem_addr_core),
-      .dmem_wdata_o  (dmem_wdata_core),
-      .dmem_wmask_o  (dmem_wmask_core),
-      .dmem_rmask_o  (dmem_rmask_core_d),
-      .dmem_rdata_i  (dmem_rdata_core),
-      .dmem_rvalid_i (dmem_rvalid_core),
-      .dmem_rerror_i (dmem_rerror_core),
+      .dmem_req_o           (dmem_req_core),
+      .dmem_write_o         (dmem_write_core),
+      .dmem_addr_o          (dmem_addr_core),
+      .dmem_wdata_o         (dmem_wdata_core),
+      .dmem_wmask_o         (dmem_wmask_core),
+      .dmem_rmask_o         (dmem_rmask_core_d),
+      .dmem_rdata_i         (dmem_rdata_core),
+      .dmem_rvalid_i        (dmem_rvalid_core),
+      .dmem_rerror_i        (dmem_rerror_core),
 
-      .edn_rnd_req_o   (edn_rnd_req),
-      .edn_rnd_ack_i   (edn_rnd_ack),
-      .edn_rnd_data_i  (edn_rnd_data),
+      .edn_rnd_req_o        (edn_rnd_req),
+      .edn_rnd_ack_i        (edn_rnd_ack),
+      .edn_rnd_data_i       (edn_rnd_data),
 
-      .edn_urnd_req_o  (edn_urnd_req),
-      .edn_urnd_ack_i  (edn_urnd_ack),
-      .edn_urnd_data_i (edn_urnd_data),
+      .edn_urnd_req_o       (edn_urnd_req),
+      .edn_urnd_ack_i       (edn_urnd_ack),
+      .edn_urnd_data_i      (edn_urnd_data),
 
-      .insn_cnt_o      (insn_cnt_rtl)
+      .insn_cnt_o           (insn_cnt_rtl),
+      .illegal_bus_access_i (illegal_bus_access_q)
     );
   `else
     otbn_core #(
@@ -810,41 +852,42 @@
       .RndCnstUrndChunkLfsrPerm(RndCnstUrndChunkLfsrPerm)
     ) u_otbn_core (
       .clk_i,
-      .rst_ni          (rst_n),
+      .rst_ni               (rst_n),
 
-      .start_i         (start_q),
-      .done_o          (done),
+      .start_i              (start_q),
+      .done_o               (done),
 
-      .err_bits_o      (err_bits),
+      .err_bits_o           (err_bits),
 
-      .start_addr_i    (start_addr),
+      .start_addr_i         (start_addr),
 
-      .imem_req_o      (imem_req_core),
-      .imem_addr_o     (imem_addr_core),
-      .imem_wdata_o    (imem_wdata_core),
-      .imem_rdata_i    (imem_rdata_core),
-      .imem_rvalid_i   (imem_rvalid_core),
-      .imem_rerror_i   (imem_rerror_core),
+      .imem_req_o           (imem_req_core),
+      .imem_addr_o          (imem_addr_core),
+      .imem_wdata_o         (imem_wdata_core),
+      .imem_rdata_i         (imem_rdata_core),
+      .imem_rvalid_i        (imem_rvalid_core),
+      .imem_rerror_i        (imem_rerror_core),
 
-      .dmem_req_o      (dmem_req_core),
-      .dmem_write_o    (dmem_write_core),
-      .dmem_addr_o     (dmem_addr_core),
-      .dmem_wdata_o    (dmem_wdata_core),
-      .dmem_wmask_o    (dmem_wmask_core),
-      .dmem_rmask_o    (dmem_rmask_core_d),
-      .dmem_rdata_i    (dmem_rdata_core),
-      .dmem_rvalid_i   (dmem_rvalid_core),
-      .dmem_rerror_i   (dmem_rerror_core),
+      .dmem_req_o           (dmem_req_core),
+      .dmem_write_o         (dmem_write_core),
+      .dmem_addr_o          (dmem_addr_core),
+      .dmem_wdata_o         (dmem_wdata_core),
+      .dmem_wmask_o         (dmem_wmask_core),
+      .dmem_rmask_o         (dmem_rmask_core_d),
+      .dmem_rdata_i         (dmem_rdata_core),
+      .dmem_rvalid_i        (dmem_rvalid_core),
+      .dmem_rerror_i        (dmem_rerror_core),
 
-      .edn_rnd_req_o   (edn_rnd_req),
-      .edn_rnd_ack_i   (edn_rnd_ack),
-      .edn_rnd_data_i  (edn_rnd_data),
+      .edn_rnd_req_o        (edn_rnd_req),
+      .edn_rnd_ack_i        (edn_rnd_ack),
+      .edn_rnd_data_i       (edn_rnd_data),
 
-      .edn_urnd_req_o  (edn_urnd_req),
-      .edn_urnd_ack_i  (edn_urnd_ack),
-      .edn_urnd_data_i (edn_urnd_data),
+      .edn_urnd_req_o       (edn_urnd_req),
+      .edn_urnd_ack_i       (edn_urnd_ack),
+      .edn_urnd_data_i      (edn_urnd_data),
 
-      .insn_cnt_o      (insn_cnt)
+      .insn_cnt_o           (insn_cnt),
+      .illegal_bus_access_i (illegal_bus_access_q)
     );
   `endif
 
diff --git a/hw/ip/otbn/rtl/otbn_controller.sv b/hw/ip/otbn/rtl/otbn_controller.sv
index 4a82992..f5ee02a 100644
--- a/hw/ip/otbn/rtl/otbn_controller.sv
+++ b/hw/ip/otbn/rtl/otbn_controller.sv
@@ -124,7 +124,8 @@
   input  logic rnd_valid_i,
 
   input  logic        state_reset_i,
-  output logic [31:0] insn_cnt_o
+  output logic [31:0] insn_cnt_o,
+  input  logic        illegal_bus_access_i
 );
   otbn_state_e state_q, state_d, state_raw;
 
@@ -326,7 +327,7 @@
     end
   end
 
-  assign err_bits_o.fatal_illegal_bus_access = 1'b0; // TODO: implement
+  assign err_bits_o.fatal_illegal_bus_access = illegal_bus_access_i;
   assign err_bits_o.fatal_reg                = rf_base_rd_data_err_i | rf_bignum_rd_data_err_i;
   assign err_bits_o.fatal_imem               = insn_fetch_err_i;
   assign err_bits_o.fatal_dmem               = lsu_rdata_err_i;
diff --git a/hw/ip/otbn/rtl/otbn_core.sv b/hw/ip/otbn/rtl/otbn_core.sv
index 35bf291..76a18f6 100644
--- a/hw/ip/otbn/rtl/otbn_core.sv
+++ b/hw/ip/otbn/rtl/otbn_core.sv
@@ -66,7 +66,11 @@
   input  logic                    edn_urnd_ack_i,
   input  logic [EdnDataWidth-1:0] edn_urnd_data_i,
 
-  output logic [31:0]             insn_cnt_o
+  output logic [31:0]             insn_cnt_o,
+
+  // Asserted by system when bus tries to access OTBN memories whilst OTBN is active. Results in an
+  // fatal error.
+  input  logic                    illegal_bus_access_i
 );
   // Fetch request (the next instruction)
   logic [ImemAddrWidth-1:0] insn_fetch_req_addr;
@@ -348,7 +352,8 @@
     .rnd_valid_i        (rnd_valid),
 
     .state_reset_i      (state_reset),
-    .insn_cnt_o         (insn_cnt)
+    .insn_cnt_o         (insn_cnt),
+    .illegal_bus_access_i
   );
 
   assign insn_cnt_o = insn_cnt;