[tlul] Add memory transmission integrity checks

- Add optional integrity generation / checks into tlul_adapter_sram
- Update sram_scr to make use of the integrity error information

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

[top] Fix for englishbreakfast

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

[all] update for intg_error_o port

- Create alert from sram controllers
- Unused for all other modules

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

[top] Auto generate files

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

[sram dv] Add extra alert

Signed-off-by: Timothy Chen <timothytim@google.com>
diff --git a/hw/ip/flash_ctrl/data/flash_ctrl.sv.tpl b/hw/ip/flash_ctrl/data/flash_ctrl.sv.tpl
index 1e18d37..198c57a 100644
--- a/hw/ip/flash_ctrl/data/flash_ctrl.sv.tpl
+++ b/hw/ip/flash_ctrl/data/flash_ctrl.sv.tpl
@@ -445,6 +445,7 @@
     .we_o        (sw_wen),
     .addr_o      (),
     .wmask_o     (),
+    .intg_error_o(),
     .wdata_o     (sw_wdata),
     .rdata_i     (BusWidth'(0)),
     .rvalid_i    (1'b0),
@@ -529,6 +530,7 @@
     .addr_o      (),
     .wmask_o     (),
     .wdata_o     (),
+    .intg_error_o(),
     .rdata_i     (rd_fifo_rdata),
     .rvalid_i    (adapter_rvalid),
     .rerror_i    (2'b0)
diff --git a/hw/ip/flash_ctrl/rtl/flash_ctrl.sv b/hw/ip/flash_ctrl/rtl/flash_ctrl.sv
index 30bc0e3..a8ba13a 100644
--- a/hw/ip/flash_ctrl/rtl/flash_ctrl.sv
+++ b/hw/ip/flash_ctrl/rtl/flash_ctrl.sv
@@ -445,6 +445,7 @@
     .we_o        (sw_wen),
     .addr_o      (),
     .wmask_o     (),
+    .intg_error_o(),
     .wdata_o     (sw_wdata),
     .rdata_i     (BusWidth'(0)),
     .rvalid_i    (1'b0),
@@ -529,6 +530,7 @@
     .addr_o      (),
     .wmask_o     (),
     .wdata_o     (),
+    .intg_error_o(),
     .rdata_i     (rd_fifo_rdata),
     .rvalid_i    (adapter_rvalid),
     .rerror_i    (2'b0)
diff --git a/hw/ip/hmac/rtl/hmac.sv b/hw/ip/hmac/rtl/hmac.sv
index 26dda29..9925090 100644
--- a/hw/ip/hmac/rtl/hmac.sv
+++ b/hw/ip/hmac/rtl/hmac.sv
@@ -300,6 +300,7 @@
     .addr_o      (msg_fifo_addr  ), // Doesn't care the address other than sub-word
     .wdata_o     (msg_fifo_wdata ),
     .wmask_o     (msg_fifo_wmask ),
+    .intg_error_o(               ),
     .rdata_i     (msg_fifo_rdata ),
     .rvalid_i    (msg_fifo_rvalid),
     .rerror_i    (msg_fifo_rerror)
diff --git a/hw/ip/kmac/rtl/kmac.sv b/hw/ip/kmac/rtl/kmac.sv
index aa9452b..ea75505 100644
--- a/hw/ip/kmac/rtl/kmac.sv
+++ b/hw/ip/kmac/rtl/kmac.sv
@@ -649,6 +649,7 @@
     .addr_o      (tlram_addr),
     .wdata_o     (tlram_wdata),
     .wmask_o     (tlram_wmask),
+    .intg_error_o(           ),
     .rdata_i     (tlram_rdata),
     .rvalid_i    (tlram_rvalid),
     .rerror_i    (tlram_rerror)
diff --git a/hw/ip/kmac/rtl/kmac_staterd.sv b/hw/ip/kmac/rtl/kmac_staterd.sv
index a31ae99..e31c8a8 100644
--- a/hw/ip/kmac/rtl/kmac_staterd.sv
+++ b/hw/ip/kmac/rtl/kmac_staterd.sv
@@ -71,6 +71,7 @@
     .addr_o      (tlram_addr),
     .wdata_o     (unused_tlram_wdata),
     .wmask_o     (unused_tlram_wmask),
+    .intg_error_o(),
     .rdata_i     (tlram_rdata),
     .rvalid_i    (tlram_rvalid),
     .rerror_i    (tlram_rerror)
diff --git a/hw/ip/otbn/rtl/otbn.sv b/hw/ip/otbn/rtl/otbn.sv
index 2576efd..521e015 100644
--- a/hw/ip/otbn/rtl/otbn.sv
+++ b/hw/ip/otbn/rtl/otbn.sv
@@ -183,6 +183,7 @@
     .addr_o      (imem_index_bus ),
     .wdata_o     (imem_wdata_bus ),
     .wmask_o     (imem_wmask_bus ),
+    .intg_error_o(               ),
     .rdata_i     (imem_rdata_bus ),
     .rvalid_i    (imem_rvalid_bus),
     .rerror_i    (imem_rerror_bus)
@@ -317,6 +318,7 @@
     .addr_o      (dmem_index_bus ),
     .wdata_o     (dmem_wdata_bus ),
     .wmask_o     (dmem_wmask_bus ),
+    .intg_error_o(               ),
     .rdata_i     (dmem_rdata_bus ),
     .rvalid_i    (dmem_rvalid_bus),
     .rerror_i    (dmem_rerror_bus)
diff --git a/hw/ip/otp_ctrl/rtl/otp_ctrl.sv b/hw/ip/otp_ctrl/rtl/otp_ctrl.sv
index 594dee8..6d98bec 100644
--- a/hw/ip/otp_ctrl/rtl/otp_ctrl.sv
+++ b/hw/ip/otp_ctrl/rtl/otp_ctrl.sv
@@ -194,6 +194,7 @@
     .addr_o      (  tlul_addr         ),
     .wdata_o     (                    ), // unused
     .wmask_o     (                    ), // unused
+    .intg_error_o(                    ),
     .rdata_i     (  tlul_rdata        ),
     .rvalid_i    (  tlul_rvalid       ),
     .rerror_i    (  tlul_rerror       )
diff --git a/hw/ip/prim/rtl/prim_ram_1p_scr.sv b/hw/ip/prim/rtl/prim_ram_1p_scr.sv
index faaa307..f3132a6 100644
--- a/hw/ip/prim/rtl/prim_ram_1p_scr.sv
+++ b/hw/ip/prim/rtl/prim_ram_1p_scr.sv
@@ -75,10 +75,16 @@
   input        [AddrWidth-1:0]      addr_i,
   input        [Width-1:0]          wdata_i,
   input        [Width-1:0]          wmask_i,  // Needs to be byte-aligned for parity
+  // The incoming transaction contains an integrity error and the module should alter
+  // its behavior appropriately.
+  // On integrity errors, the primitive reverses the bit-order of the nonce and surpresses
+  // any real transaction to the memory.
+  input                             intg_error_i,
   output logic [Width-1:0]          rdata_o,
   output logic                      rvalid_o, // Read response (rdata_o) is valid
   output logic [1:0]                rerror_o, // Bit1: Uncorrectable, Bit0: Correctable
   output logic [31:0]               raddr_o,  // Read address for error reporting.
+  output logic                      intg_error_o,
 
   // config
   input [CfgWidth-1:0]              cfg_i
@@ -93,6 +99,24 @@
   `ASSERT_INIT(DiffWidthAligned_A, (DataBitsPerMask % DiffWidth) == 0)
   `ASSERT_INIT(DiffWidthWithParity_A, EnableParity && (DiffWidth == 8) || !EnableParity)
 
+  //////////////////////////////
+  // Integrity error latching //
+  //////////////////////////////
+
+  logic intg_err_q;
+  always_ff @(posedge clk_i or negedge rst_ni) begin
+    if (!rst_ni) begin
+      intg_err_q <= '0;
+    end else if (intg_error_i) begin
+      intg_err_q <= 1'b1;
+    end
+  end
+
+  prim_buf u_intg_err_out (
+    .in_i(intg_error_i | intg_err_q),
+    .out_o(intg_error_o)
+  );
+
   /////////////////////////////////////////
   // Pending Write and Address Registers //
   /////////////////////////////////////////
@@ -118,8 +142,14 @@
   assign addr_collision_d = read_en & (write_en_q | write_pending_q) & (addr_i == waddr_q);
 
   // Macro requests and write strobe
+  // The macro operation is silenced if an integrity error is seen
   logic macro_req;
-  assign macro_req   = read_en | write_en_q | write_pending_q;
+  logic intg_err_macro_req;
+  prim_buf u_intg_err_macro_req (
+    .in_i(intg_error_i | intg_err_q),
+    .out_o(intg_err_macro_req)
+  );
+  assign macro_req   = ~intg_err_macro_req & (read_en | write_en_q | write_pending_q);
   // We are allowed to write a pending write transaction to the memory if there is no incoming read
   logic macro_write;
   assign macro_write = (write_en_q | write_pending_q) & ~read_en;
@@ -138,18 +168,34 @@
   // This creates a bijective address mapping using a substitution / permutation network.
   logic [AddrWidth-1:0] addr_scr;
   if (NumAddrScrRounds > 0) begin : gen_addr_scr
+
+    // TODO, expand this into copies with another primitive
+    logic intg_err_addr_scr;
+    prim_buf u_intg_err_addr_scr (
+      .in_i(intg_error_i | intg_err_q),
+      .out_o(intg_err_addr_scr)
+    );
+
+    // If there is an intergirty error, the nonce used is reversed
+    logic [AddrWidth-1:0] addr_scr_nonce;
+    for (genvar j = 0; j < AddrWidth; j++) begin : gen_addr_scr_nonce
+      assign addr_scr_nonce[j] = intg_err_addr_scr ?
+                                 nonce_i[NonceWidth - 1 - j] :
+                                 nonce_i[NonceWidth - AddrWidth + j];
+    end
+
     prim_subst_perm #(
       .DataWidth ( AddrWidth        ),
       .NumRounds ( NumAddrScrRounds ),
       .Decrypt   ( 0                )
     ) u_prim_subst_perm (
-      .data_i ( addr_mux ),
+      .data_i ( addr_mux       ),
       // Since the counter mode concatenates {nonce_i[NonceWidth-1-AddrWidth:0], addr_i} to form
       // the IV, the upper AddrWidth bits of the nonce are not used and can be used for address
       // scrambling. In cases where N parallel PRINCE blocks are used due to a data
       // width > 64bit, N*AddrWidth nonce bits are left dangling.
-      .key_i  ( nonce_i[NonceWidth - 1 : NonceWidth - AddrWidth] ),
-      .data_o ( addr_scr )
+      .key_i  ( addr_scr_nonce ),
+      .data_o ( addr_scr       )
     );
   end else begin : gen_no_addr_scr
     assign addr_scr = addr_mux;
@@ -166,8 +212,26 @@
   // This encrypts the IV consisting of the nonce and address using the key provided in order to
   // generate the keystream for the data. Note that we instantiate a register halfway within this
   // primitive to balance the delay between request and response side.
+  localparam int DataNonceWidth = 64 - AddrWidth;
   logic [NumParScr*64-1:0] keystream;
+  logic [NumParScr-1:0][DataNonceWidth-1:0] data_scr_nonce;
+
+  // TODO, expand this into copies with another primitive
+  logic intg_err_data_scr;
+  prim_buf u_intg_err_data_scr (
+    .in_i(intg_error_i | intg_err_q),
+    .out_o(intg_err_data_scr)
+  );
+
   for (genvar k = 0; k < NumParScr; k++) begin : gen_par_scr
+
+    for (genvar j = 0; j < DataNonceWidth; j++) begin : gen_data_nonce
+      assign data_scr_nonce[k][j] = intg_err_data_scr ?
+                                    nonce_i[(k + 1) * DataNonceWidth - j] :
+                                    nonce_i[k * DataNonceWidth + j];
+    end
+
+
     prim_prince #(
       .DataWidth      (64),
       .KeyWidth       (128),
@@ -180,7 +244,8 @@
       .rst_ni,
       .valid_i ( gnt_o ),
       // The IV is composed of a nonce and the row address
-      .data_i  ( {nonce_i[k * (64 - AddrWidth) +: (64 - AddrWidth)], addr_i} ),
+      //.data_i  ( {nonce_i[k * (64 - AddrWidth) +: (64 - AddrWidth)], addr_i} ),
+      .data_i  ( {data_scr_nonce[k], addr_i} ),
       // All parallel scramblers use the same key
       .key_i,
       // Since we operate in counter mode, this can always be set to encryption mode
diff --git a/hw/ip/prim_generic/rtl/prim_generic_flash.sv b/hw/ip/prim_generic/rtl/prim_generic_flash.sv
index 8483fbd..154f7fa 100644
--- a/hw/ip/prim_generic/rtl/prim_generic_flash.sv
+++ b/hw/ip/prim_generic/rtl/prim_generic_flash.sv
@@ -151,6 +151,7 @@
     .addr_o(cfg_addr),
     .wdata_o(cfg_wdata),
     .wmask_o(),
+    .intg_error_o(),
     .rdata_i(cfg_rdata),
     .rvalid_i(cfg_rvalid),
     .rerror_i('0)
diff --git a/hw/ip/prim_generic/rtl/prim_generic_otp.sv b/hw/ip/prim_generic/rtl/prim_generic_otp.sv
index 149d6e7..05f9d58 100644
--- a/hw/ip/prim_generic/rtl/prim_generic_otp.sv
+++ b/hw/ip/prim_generic/rtl/prim_generic_otp.sv
@@ -78,6 +78,7 @@
     .addr_o      ( tlul_addr          ),
     .wdata_o     ( tlul_wdata         ),
     .wmask_o     (                    ),
+    .intg_error_o(                    ),
     .rdata_i     ( tlul_rdata_q       ),
     .rvalid_i    ( tlul_rvalid_q      ),
     .rerror_i    ( '0                 )
diff --git a/hw/ip/rv_dm/rtl/rv_dm.sv b/hw/ip/rv_dm/rtl/rv_dm.sv
index 113db6b..40e13d5 100644
--- a/hw/ip/rv_dm/rtl/rv_dm.sv
+++ b/hw/ip/rv_dm/rtl/rv_dm.sv
@@ -348,6 +348,7 @@
     .addr_o      (addr_w),
     .wdata_o     (wdata),
     .wmask_o     (),
+    .intg_error_o(),
     .rdata_i     (rdata),
     .rvalid_i    (rvalid),
     .rerror_i    (2'b00),
diff --git a/hw/ip/spi_device/rtl/spi_device.sv b/hw/ip/spi_device/rtl/spi_device.sv
index bb97bc0..e4e25be 100644
--- a/hw/ip/spi_device/rtl/spi_device.sv
+++ b/hw/ip/spi_device/rtl/spi_device.sv
@@ -656,6 +656,7 @@
     .addr_o      (mem_a_addr),
     .wdata_o     (mem_a_wdata),
     .wmask_o     (),           // Not used
+    .intg_error_o(),
     .rdata_i     (mem_a_rdata),
     .rvalid_i    (mem_a_rvalid),
     .rerror_i    (mem_a_rerror)
diff --git a/hw/ip/sram_ctrl/data/sram_ctrl.hjson b/hw/ip/sram_ctrl/data/sram_ctrl.hjson
index 4127c2c..6224e05 100644
--- a/hw/ip/sram_ctrl/data/sram_ctrl.hjson
+++ b/hw/ip/sram_ctrl/data/sram_ctrl.hjson
@@ -19,9 +19,12 @@
   ///////////////////////////
 
   alert_list: [
+    { name: "fatal_intg_error",
+      desc: "An integrity error has occurred in the SRAM."
+    },
     { name: "fatal_parity_error",
-      desc: "An uncorrectable parity error has occurred in the SRAM."
-    }
+      desc: "An parity error has occurred in the SRAM."
+    },
   ],
 
   ////////////////
@@ -97,6 +100,12 @@
       act:     "req",
       package: "tlul_pkg",
     },
+    { struct:  "logic",
+      type:    "uni",
+      name:    "intg_error",
+      act:     "rcv",
+      package: ""
+    },
   ] // inter_signal_list
 
   regwidth: "32",
diff --git a/hw/ip/sram_ctrl/doc/_index.md b/hw/ip/sram_ctrl/doc/_index.md
index adf0fe5..d95c275 100644
--- a/hw/ip/sram_ctrl/doc/_index.md
+++ b/hw/ip/sram_ctrl/doc/_index.md
@@ -15,6 +15,7 @@
 
 - Key request logic for the lightweight memory and address scrambling device.
 - Reporting CSRs and alert trigger for SRAM integrity errors.
+- Security hardening when integrity error has been detected.
 
 # Theory of Operations
 
@@ -121,6 +122,15 @@
 The same two-layer S&P network that is used for byte diffusion is leveraged to non-linearly remap the SRAM address as shown in the block diagram above.
 As opposed to the byte diffusion S&P networks, this particular address scrambling network additionally XOR's in a nonce that has the same width as the address.
 
+### Integrity Error Handling
+
+The `prim_ram_1p_scr` primitive contains an input to indicate whether upstream logic has detected an integrity error (The generation of the integrity error is determined by system integration).
+When an integrity error is encountered, the prim memory module does the following:
+*  Latch the error condition until reset.
+*  Reverse the nonce used during the address and CTR scrambling.
+*  Disallow any transaction (read or write) on the actual memory macro.
+
+This behavior, combined with other top level defenses, form a multi-layered defense when integrity errors are seen in the system.
 
 ### Read and Write Sequencing
 
@@ -221,7 +231,7 @@
 
 Signal                     | Direction        | Type                               | Description
 ---------------------------|------------------|------------------------------------|---------------
-`key_valid_i`              | `input`          | `logic `                           | Indicates whether the key and nonce are considered valid. New memory requests are blocked if this is set to 0.
+`key_valid_i`              | `input`          | `logic`                            | Indicates whether the key and nonce are considered valid. New memory requests are blocked if this is set to 0.
 `key_i`                    | `input`          | `logic [127:0]`                    | Scrambling key.
 `nonce_i`                  | `input`          | `logic [NonceWidth-1:0]`           | Scrambling nonce.
 `req_i`                    | `input`          | `logic`                            | Memory request indication signal (from TL-UL SRAM adapter).
@@ -230,9 +240,11 @@
 `addr_i`                   | `input`          | `logic [AddrWidth-1:0]`            | Address for memory op (from TL-UL SRAM adapter).
 `wdata_i`                  | `input`          | `logic [Width-1:0]`                | Write data (from TL-UL SRAM adapter).
 `wmask_i`                  | `input`          | `logic [Width-1:0]`                | Write mask (from TL-UL SRAM adapter).
+`intg_error_i`             | `input`          | `logic`                            | Indicates whether the incoming transaction has an integrity error
 `rdata_o`                  | `output`         | `logic [Width-1:0]`                | Read data output (to TL-UL SRAM adapter).
 `rvalid_o`                 | `output`         | `logic`                            | Read data valid indication (to TL-UL SRAM adapter).
 `rerror_o`                 | `output`         | `logic [1:0]`                      | Error indication (to TL-UL SRAM adapter). Bit 0 indicates a correctable and bit 1 an uncorrectable error. Note that at this time, only uncorrectable errors are reported, since the scrambling device only supports byte parity.
+`intg_error_o`             | `output`         | `logic`                            | Forwards latched integrity error indication to the sram controller.
 `raddr_o`                  | `output`         | `logic [31:0]`                     | Address of the faulty read operation.
 `cfg_i`                    | `input`          | `logic [CfgWidth-1:0]`             | Attributes for physical memory macro.
 
@@ -262,7 +274,7 @@
 ## Error Handling
 
 Data in the SRAM is integrity protected with byte parity.
-In case an integrity failure is detected, the SRAM controller sets the {{< regref "STATUS.ERROR" >}} bit in the CSRs and continuously sends out a `fatal_parity_error` alert.
+In case an integrity failure is detected, the SRAM controller sets the {{< regref "STATUS.ERROR" >}} bit in the CSRs and continuously sends out a `fatal_parity_error` or `fatal_intg_error` alert.
 At the same time, the affected TL-UL transaction will error out.
 
 SRAM integrity failures are considered unrecoverable and cannot be cleared.
diff --git a/hw/ip/sram_ctrl/dv/env/sram_ctrl_env_pkg.sv b/hw/ip/sram_ctrl/dv/env/sram_ctrl_env_pkg.sv
index ef837d8..3b5171d 100644
--- a/hw/ip/sram_ctrl/dv/env/sram_ctrl_env_pkg.sv
+++ b/hw/ip/sram_ctrl/dv/env/sram_ctrl_env_pkg.sv
@@ -23,8 +23,8 @@
   `include "dv_macros.svh"
 
   // parameters
-  parameter string LIST_OF_ALERTS[] = { "fatal_parity_error" };
-  parameter uint   NUM_ALERTS = 1;
+  parameter string LIST_OF_ALERTS[] = { "fatal_intg_error", "fatal_parity_error"};
+  parameter uint   NUM_ALERTS = 2;
 
   // Number of bits in the otp_ctrl_pkg::sram_otp_key_rsp_t struct:
   // 1 bit for valid, SramKeyWidth bits for the key, SramNonceWidth bits for the nonce.
diff --git a/hw/ip/sram_ctrl/dv/sram_ctrl_wrapper.sv b/hw/ip/sram_ctrl/dv/sram_ctrl_wrapper.sv
index a6aa441..c28e7e7 100644
--- a/hw/ip/sram_ctrl/dv/sram_ctrl_wrapper.sv
+++ b/hw/ip/sram_ctrl/dv/sram_ctrl_wrapper.sv
@@ -50,6 +50,7 @@
   wire [DataWidth-1:0]  wmask;
   wire [DataWidth-1:0]  rdata;
   wire                  rvalid;
+  wire                  intg_error;
 
   // SRAM Controller
   sram_ctrl u_sram_ctrl (
@@ -72,7 +73,9 @@
     .sram_otp_key_i   (sram_otp_key_i   ),
     // Interface with SRAM memory scrambling
     .sram_scr_o       (scr_req          ),
-    .sram_scr_i       (scr_rsp          )
+    .sram_scr_i       (scr_rsp          ),
+    // Integrity error
+    .intg_error_i     (intg_error)
   );
 
   // TLUL Adapter SRAM
@@ -102,24 +105,26 @@
     .Width(DataWidth),
     .Depth(2 ** AddrWidth)
   ) u_ram1p_sram (
-    .clk_i      (clk_i          ),
-    .rst_ni     (rst_ni         ),
+    .clk_i        (clk_i          ),
+    .rst_ni       (rst_ni         ),
     // Key interface
-    .key_valid_i(scr_req.valid  ),
-    .key_i      (scr_req.key    ),
-    .nonce_i    (scr_req.nonce  ),
+    .key_valid_i  (scr_req.valid  ),
+    .key_i        (scr_req.key    ),
+    .nonce_i      (scr_req.nonce  ),
     // SRAM response interface to TLUL adapter
-    .req_i      (req            ),
-    .gnt_o      (gnt            ),
-    .write_i    (we             ),
-    .addr_i     (addr           ),
-    .wdata_i    (wdata          ),
-    .wmask_i    (wmask          ),
-    .rdata_o    (rdata          ),
-    .rvalid_o   (rvalid         ),
-    .rerror_o   (scr_rsp.rerror ),
-    .raddr_o    (scr_rsp.raddr  ),
-    .cfg_i      ('0             )
+    .req_i        (req            ),
+    .gnt_o        (gnt            ),
+    .write_i      (we             ),
+    .addr_i       (addr           ),
+    .wdata_i      (wdata          ),
+    .wmask_i      (wmask          ),
+    .intg_error_i ('0             ),
+    .rdata_o      (rdata          ),
+    .rvalid_o     (rvalid         ),
+    .rerror_o     (scr_rsp.rerror ),
+    .intg_error_o (intg_error     ),
+    .raddr_o      (scr_rsp.raddr  ),
+    .cfg_i        ('0             )
   );
 
 endmodule
diff --git a/hw/ip/sram_ctrl/rtl/sram_ctrl.sv b/hw/ip/sram_ctrl/rtl/sram_ctrl.sv
index dc7260f..7f31165 100644
--- a/hw/ip/sram_ctrl/rtl/sram_ctrl.sv
+++ b/hw/ip/sram_ctrl/rtl/sram_ctrl.sv
@@ -39,6 +39,8 @@
   // Key request to OTP (running on clk_fixed)
   output otp_ctrl_pkg::sram_otp_key_req_t            sram_otp_key_o,
   input  otp_ctrl_pkg::sram_otp_key_rsp_t            sram_otp_key_i,
+  // Integrity error detection on corresponding sram
+  input                                              intg_error_i,
   // Interface with SRAM scrambling wrapper
   output sram_scr_req_t                              sram_scr_o,
   input  sram_scr_rsp_t                              sram_scr_i,
@@ -101,25 +103,37 @@
   // Alert Sender //
   //////////////////
 
-  logic alert;
-  logic alert_test;
-  assign alert = parity_error_q;
-  assign alert_test = reg2hw.alert_test.q &
-                      reg2hw.alert_test.qe;
+  logic [NumAlerts-1:0] alert, alert_test;
 
-  prim_alert_sender #(
-    .AsyncOn(AlertAsyncOn[0]),
-    .IsFatal(1)
-  ) u_prim_alert_sender (
-    .clk_i,
-    .rst_ni,
-    .alert_test_i  ( alert_test    ),
-    .alert_req_i   ( alert         ),
-    .alert_ack_o   (               ),
-    .alert_state_o (               ),
-    .alert_rx_i    ( alert_rx_i[0] ),
-    .alert_tx_o    ( alert_tx_o[0] )
-  );
+  assign alert = {
+                   parity_error_q,
+                   intg_error_i
+                 };
+
+
+  assign alert_test = {
+                        reg2hw.alert_test.fatal_parity_error.q &
+                        reg2hw.alert_test.fatal_parity_error.qe,
+                        reg2hw.alert_test.fatal_intg_error.q &
+                        reg2hw.alert_test.fatal_intg_error.qe
+                      };
+
+  for (genvar i=0; i < NumAlerts; i++) begin : gen_alerts
+    prim_alert_sender #(
+      .AsyncOn(AlertAsyncOn[i]),
+      .IsFatal(1)
+    ) u_prim_alert_sender_parity (
+      .clk_i,
+      .rst_ni,
+      .alert_test_i  ( alert_test[i] ),
+      .alert_req_i   ( alert[i]      ),
+      .alert_ack_o   (               ),
+      .alert_state_o (               ),
+      .alert_rx_i    ( alert_rx_i[i] ),
+      .alert_tx_o    ( alert_tx_o[i] )
+    );
+  end
+
 
   //////////////////////////////////////////
   // Lifecycle Escalation Synchronization //
diff --git a/hw/ip/sram_ctrl/rtl/sram_ctrl_reg_pkg.sv b/hw/ip/sram_ctrl/rtl/sram_ctrl_reg_pkg.sv
index c52ea0b..d92a230 100644
--- a/hw/ip/sram_ctrl/rtl/sram_ctrl_reg_pkg.sv
+++ b/hw/ip/sram_ctrl/rtl/sram_ctrl_reg_pkg.sv
@@ -7,7 +7,7 @@
 package sram_ctrl_reg_pkg;
 
   // Param list
-  parameter int NumAlerts = 1;
+  parameter int NumAlerts = 2;
 
   // Address width within the block
   parameter int BlockAw = 5;
@@ -16,8 +16,14 @@
   // Typedefs for registers //
   ////////////////////////////
   typedef struct packed {
-    logic        q;
-    logic        qe;
+    struct packed {
+      logic        q;
+      logic        qe;
+    } fatal_intg_error;
+    struct packed {
+      logic        q;
+      logic        qe;
+    } fatal_parity_error;
   } sram_ctrl_reg2hw_alert_test_reg_t;
 
   typedef struct packed {
@@ -55,7 +61,7 @@
   // Register to internal design logic //
   ///////////////////////////////////////
   typedef struct packed {
-    sram_ctrl_reg2hw_alert_test_reg_t alert_test; // [6:5]
+    sram_ctrl_reg2hw_alert_test_reg_t alert_test; // [8:5]
     sram_ctrl_reg2hw_exec_reg_t exec; // [4:2]
     sram_ctrl_reg2hw_ctrl_reg_t ctrl; // [1:0]
   } sram_ctrl_reg2hw_t;
@@ -78,7 +84,8 @@
   parameter logic [BlockAw-1:0] SRAM_CTRL_ERROR_ADDRESS_OFFSET = 5'h 18;
 
   // Reset values for hwext registers and their fields
-  parameter logic [0:0] SRAM_CTRL_ALERT_TEST_RESVAL = 1'h 0;
+  parameter logic [1:0] SRAM_CTRL_ALERT_TEST_RESVAL = 2'h 0;
+  parameter logic [0:0] SRAM_CTRL_ALERT_TEST_FATAL_INTG_ERROR_RESVAL = 1'h 0;
   parameter logic [0:0] SRAM_CTRL_ALERT_TEST_FATAL_PARITY_ERROR_RESVAL = 1'h 0;
   parameter logic [3:0] SRAM_CTRL_STATUS_RESVAL = 4'h 0;
   parameter logic [0:0] SRAM_CTRL_CTRL_RESVAL = 1'h 0;
diff --git a/hw/ip/sram_ctrl/rtl/sram_ctrl_reg_top.sv b/hw/ip/sram_ctrl/rtl/sram_ctrl_reg_top.sv
index fd2cae0..9a679d1 100644
--- a/hw/ip/sram_ctrl/rtl/sram_ctrl_reg_top.sv
+++ b/hw/ip/sram_ctrl/rtl/sram_ctrl_reg_top.sv
@@ -99,8 +99,10 @@
   // Define SW related signals
   // Format: <reg>_<field>_{wd|we|qs}
   //        or <reg>_{wd|we|qs} if field == 1 or 0
-  logic alert_test_wd;
-  logic alert_test_we;
+  logic alert_test_fatal_intg_error_wd;
+  logic alert_test_fatal_intg_error_we;
+  logic alert_test_fatal_parity_error_wd;
+  logic alert_test_fatal_parity_error_we;
   logic status_error_qs;
   logic status_error_re;
   logic status_escalated_qs;
@@ -125,16 +127,32 @@
   // Register instances
   // R[alert_test]: V(True)
 
+  //   F[fatal_intg_error]: 0:0
   prim_subreg_ext #(
     .DW    (1)
-  ) u_alert_test (
+  ) u_alert_test_fatal_intg_error (
     .re     (1'b0),
-    .we     (alert_test_we),
-    .wd     (alert_test_wd),
+    .we     (alert_test_fatal_intg_error_we),
+    .wd     (alert_test_fatal_intg_error_wd),
     .d      ('0),
     .qre    (),
-    .qe     (reg2hw.alert_test.qe),
-    .q      (reg2hw.alert_test.q ),
+    .qe     (reg2hw.alert_test.fatal_intg_error.qe),
+    .q      (reg2hw.alert_test.fatal_intg_error.q ),
+    .qs     ()
+  );
+
+
+  //   F[fatal_parity_error]: 1:1
+  prim_subreg_ext #(
+    .DW    (1)
+  ) u_alert_test_fatal_parity_error (
+    .re     (1'b0),
+    .we     (alert_test_fatal_parity_error_we),
+    .wd     (alert_test_fatal_parity_error_wd),
+    .d      ('0),
+    .qre    (),
+    .qe     (reg2hw.alert_test.fatal_parity_error.qe),
+    .q      (reg2hw.alert_test.fatal_parity_error.q ),
     .qs     ()
   );
 
@@ -353,8 +371,11 @@
     if (addr_hit[6] && reg_we && (SRAM_CTRL_PERMIT[6] != (SRAM_CTRL_PERMIT[6] & reg_be))) wr_err = 1'b1 ;
   end
 
-  assign alert_test_we = addr_hit[0] & reg_we & !reg_error;
-  assign alert_test_wd = reg_wdata[0];
+  assign alert_test_fatal_intg_error_we = addr_hit[0] & reg_we & !reg_error;
+  assign alert_test_fatal_intg_error_wd = reg_wdata[0];
+
+  assign alert_test_fatal_parity_error_we = addr_hit[0] & reg_we & !reg_error;
+  assign alert_test_fatal_parity_error_wd = reg_wdata[1];
 
   assign status_error_re = addr_hit[1] & reg_re & !reg_error;
 
@@ -383,6 +404,7 @@
     unique case (1'b1)
       addr_hit[0]: begin
         reg_rdata_next[0] = '0;
+        reg_rdata_next[1] = '0;
       end
 
       addr_hit[1]: begin
diff --git a/hw/ip/tlul/rtl/tlul_adapter_sram.sv b/hw/ip/tlul/rtl/tlul_adapter_sram.sv
index 666e556..4d6b150 100644
--- a/hw/ip/tlul/rtl/tlul_adapter_sram.sv
+++ b/hw/ip/tlul/rtl/tlul_adapter_sram.sv
@@ -12,12 +12,15 @@
  *   than SRAM size
  */
 module tlul_adapter_sram import tlul_pkg::*; #(
-  parameter int SramAw      = 12,
-  parameter int SramDw      = 32, // Must be multiple of the TL width
-  parameter int Outstanding = 1,  // Only one request is accepted
-  parameter bit ByteAccess  = 1,  // 1: true, 0: false
-  parameter bit ErrOnWrite  = 0,  // 1: Writes not allowed, automatically error
-  parameter bit ErrOnRead   = 0   // 1: Reads not allowed, automatically error
+  parameter int SramAw            = 12,
+  parameter int SramDw            = 32, // Must be multiple of the TL width
+  parameter int Outstanding       = 1,  // Only one request is accepted
+  parameter bit ByteAccess        = 1,  // 1: true, 0: false
+  parameter bit ErrOnWrite        = 0,  // 1: Writes not allowed, automatically error
+  parameter bit ErrOnRead         = 0,  // 1: Reads not allowed, automatically error
+  parameter bit CmdIntgCheck      = 0,  // 1: Enable command integrity check
+  parameter bit EnableRspIntgGen  = 0,  // 1: Generate response integrity
+  parameter bit EnableDataIntgGen = 0   // 1: Generate data integrity
 ) (
   input   clk_i,
   input   rst_ni,
@@ -36,6 +39,7 @@
   output logic [SramAw-1:0] addr_o,
   output logic [SramDw-1:0] wdata_o,
   output logic [SramDw-1:0] wmask_o,
+  output logic              intg_error_o,
   input        [SramDw-1:0] rdata_i,
   input                     rvalid_i,
   input        [1:0]        rerror_i // 2 bit error [1]: Uncorrectable, [0]: Correctable
@@ -91,6 +95,7 @@
   rsp_t rspfifo_wdata,  rspfifo_rdata;
 
   logic error_internal; // Internal protocol error checker
+  logic intg_error;
   logic wr_attr_error;
   logic instr_error;
   logic wr_vld_error;
@@ -136,7 +141,9 @@
     end
   end
 
-  assign tl_o = '{
+
+  tl_d2h_t tl_out;
+  assign tl_out = '{
       d_valid  : d_valid ,
       d_opcode : (d_valid && reqfifo_rdata.op != OpRead) ? AccessAck : AccessAckData,
       d_param  : '0,
@@ -151,6 +158,15 @@
       a_ready  : (gnt_i | error_internal) & reqfifo_wready & sramreqfifo_wready
   };
 
+
+  tlul_rsp_intg_gen #(
+    .EnableRspIntgGen(EnableRspIntgGen),
+    .EnableDataIntgGen(EnableDataIntgGen)
+  ) u_rsp_gen (
+    .tl_i(tl_out),
+    .tl_o
+  );
+
   // a_ready depends on the FIFO full condition and grant from SRAM (or SRAM arbiter)
   // assemble response, including read response, write response, and error for unsupported stuff
 
@@ -216,6 +232,30 @@
     assign rd_vld_error = 1'b0;
   end
 
+  if (CmdIntgCheck) begin : gen_cmd_intg_check
+    tlul_cmd_intg_chk u_cmd_intg_chk (
+      .tl_i,
+      .err_o ()
+    );
+
+    // TODO, hook up err_o once memory initialization is done
+    assign intg_error = '0;
+  end else begin : gen_no_cmd_intg_check
+    assign intg_error = '0;
+  end
+
+
+  // permanently latch integrity error until reset
+  logic intg_error_q;
+  always_ff @(posedge clk_i or negedge rst_ni) begin
+    if (!rst_ni) begin
+      intg_error_q <= '0;
+    end else if (intg_error) begin
+      intg_error_q <= 1'b1;
+    end
+  end
+  assign intg_error_o = intg_error_q;
+
   tlul_err u_err (
     .clk_i,
     .rst_ni,
@@ -223,7 +263,8 @@
     .err_o (tlul_error)
   );
 
-  assign error_internal = wr_attr_error | wr_vld_error | rd_vld_error | instr_error | tlul_error;
+  assign error_internal = wr_attr_error | wr_vld_error | rd_vld_error | instr_error |
+                          tlul_error    | intg_error   | intg_error_q;
   // End: Request Error Detection
 
   assign reqfifo_wvalid = a_ack ; // Push to FIFO only when granted
diff --git a/hw/ip/tlul/rtl/tlul_rsp_intg_gen.sv b/hw/ip/tlul/rtl/tlul_rsp_intg_gen.sv
index 6ee680e..6c58916 100644
--- a/hw/ip/tlul/rtl/tlul_rsp_intg_gen.sv
+++ b/hw/ip/tlul/rtl/tlul_rsp_intg_gen.sv
@@ -9,6 +9,7 @@
  */
 
 module tlul_rsp_intg_gen import tlul_pkg::*; #(
+  parameter bit EnableRspIntgGen = 1'b1,
   parameter bit EnableDataIntgGen = 1'b1
 ) (
   // TL-UL interface
@@ -16,15 +17,20 @@
   output tl_d2h_t tl_o
 );
 
-  tl_d2h_rsp_intg_t rsp;
-  logic [D2HRspMaxWidth-1:0] unused_payload;
   logic [D2HRspIntgWidth-1:0] rsp_intg;
-  assign rsp = extract_d2h_rsp_intg(tl_i);
+  if (EnableRspIntgGen) begin : gen_rsp_intg
+    tl_d2h_rsp_intg_t rsp;
+    logic [D2HRspMaxWidth-1:0] unused_payload;
 
-  prim_secded_64_57_enc u_rsp_gen (
-    .in(D2HRspMaxWidth'(rsp)),
-    .out({rsp_intg, unused_payload})
-  );
+    assign rsp = extract_d2h_rsp_intg(tl_i);
+
+    prim_secded_64_57_enc u_rsp_gen (
+      .in(D2HRspMaxWidth'(rsp)),
+      .out({rsp_intg, unused_payload})
+    );
+  end else begin : gen_passthrough_rsp_intg
+    assign rsp_intg = tl_i.d_user.rsp_intg;
+  end
 
   logic [DataIntgWidth-1:0] data_intg;
   if (EnableDataIntgGen) begin : gen_data_intg
@@ -38,7 +44,6 @@
     assign data_intg = tl_i.d_user.data_intg;
   end
 
-
   always_comb begin
     tl_o = tl_i;
     tl_o.d_user.rsp_intg = rsp_intg;
diff --git a/hw/ip/usbdev/rtl/usbdev.sv b/hw/ip/usbdev/rtl/usbdev.sv
index 008ecf6..db93e6e 100644
--- a/hw/ip/usbdev/rtl/usbdev.sv
+++ b/hw/ip/usbdev/rtl/usbdev.sv
@@ -677,6 +677,7 @@
     .addr_o      (mem_a_addr),
     .wdata_o     (mem_a_wdata),
     .wmask_o     (),           // Not used
+    .intg_error_o(),
     .rdata_i     (mem_a_rdata),
     .rvalid_i    (mem_a_rvalid),
     .rerror_i    (mem_a_rerror)