[prim] Allow disabling SVAs ensuring REQ is held until ACK at run time

Previously, a parameter was used to disable these checks statically.
While we need to be able to disable these checks during abnormal
operating conditions (e.g. LC escalation), violating such assertions
in normal operation can be very critical and can lock up the system
(e.g. CDC). We must be able to catch such issues.

Thus, this commit replaces the parameter by a new input signal to gate
the corresponding assertion at run time.

This resolves lowRISC/OpenTitan#7308.

Signed-off-by: Pirmin Vogel <vogelpi@lowrisc.org>
diff --git a/hw/ip/aes/rtl/aes.sv b/hw/ip/aes/rtl/aes.sv
index 15582c5..b8239c7 100644
--- a/hw/ip/aes/rtl/aes.sv
+++ b/hw/ip/aes/rtl/aes.sv
@@ -109,16 +109,17 @@
     .DataSrc2Dst(1'b0),
     .DataReg(1'b0)
   ) u_prim_sync_reqack_data (
-    .clk_src_i  ( clk_i         ),
-    .rst_src_ni ( rst_ni        ),
-    .clk_dst_i  ( clk_edn_i     ),
-    .rst_dst_ni ( rst_edn_ni    ),
-    .src_req_i  ( edn_req       ),
-    .src_ack_o  ( edn_ack       ),
-    .dst_req_o  ( edn_o.edn_req ),
-    .dst_ack_i  ( edn_i.edn_ack ),
-    .data_i     ( edn_i.edn_bus ),
-    .data_o     ( edn_data      )
+    .clk_src_i  ( clk_i                              ),
+    .rst_src_ni ( rst_ni                             ),
+    .clk_dst_i  ( clk_edn_i                          ),
+    .rst_dst_ni ( rst_edn_ni                         ),
+    .req_chk_i  ( lc_escalate_en == lc_ctrl_pkg::Off ),
+    .src_req_i  ( edn_req                            ),
+    .src_ack_o  ( edn_ack                            ),
+    .dst_req_o  ( edn_o.edn_req                      ),
+    .dst_ack_i  ( edn_i.edn_ack                      ),
+    .data_i     ( edn_i.edn_bus                      ),
+    .data_o     ( edn_data                           )
   );
   // We don't track whether the entropy is pre-FIPS or not inside AES.
   assign unused_edn_fips = edn_i.edn_fips;
diff --git a/hw/ip/alert_handler/rtl/alert_handler.sv b/hw/ip/alert_handler/rtl/alert_handler.sv
index 9887c18..b5aa524 100644
--- a/hw/ip/alert_handler/rtl/alert_handler.sv
+++ b/hw/ip/alert_handler/rtl/alert_handler.sv
@@ -91,6 +91,7 @@
     // Alert handler side
     .clk_i,
     .rst_ni,
+    .req_chk_i   ( 1'b1     ),
     .req_i       ( edn_req  ),
     .ack_o       ( edn_ack  ),
     .data_o      ( edn_data ),
diff --git a/hw/ip/csrng/rtl/csrng_core.sv b/hw/ip/csrng/rtl/csrng_core.sv
index f1a2a64..bf21637 100644
--- a/hw/ip/csrng/rtl/csrng_core.sv
+++ b/hw/ip/csrng/rtl/csrng_core.sv
@@ -805,6 +805,7 @@
   ) u_prim_arbiter_ppc_acmd (
     .clk_i(clk_i),
     .rst_ni(rst_ni),
+    .req_chk_i(1'b1),
     .req_i(cmd_arb_req),
     .data_i(cmd_arb_bus),
     .gnt_o(cmd_arb_gnt),
@@ -1175,6 +1176,7 @@
   ) u_prim_arbiter_ppc_updblk_arb (
     .clk_i(clk_i),
     .rst_ni(rst_ni),
+    .req_chk_i(1'b1),
     .req_i({genblk_updblk_arb_req,cmdblk_updblk_arb_req}),
     .data_i(updblk_arb_din),
     .gnt_o({updblk_genblk_arb_req_rdy,updblk_cmdblk_arb_req_rdy}),
@@ -1268,6 +1270,7 @@
   ) u_prim_arbiter_ppc_benblk_arb (
     .clk_i(clk_i),
     .rst_ni(rst_ni),
+    .req_chk_i(1'b1),
     .req_i({genblk_benblk_arb_req,updblk_benblk_arb_req}),
     .data_i(benblk_arb_din),
     .gnt_o({genblk_benblk_arb_req_rdy,updblk_benblk_arb_req_rdy}),
diff --git a/hw/ip/edn/rtl/edn_core.sv b/hw/ip/edn/rtl/edn_core.sv
index 72ebe83..68551bc 100644
--- a/hw/ip/edn/rtl/edn_core.sv
+++ b/hw/ip/edn/rtl/edn_core.sv
@@ -516,6 +516,7 @@
   ) u_prim_arbiter_ppc_packer_arb (
     .clk_i(clk_i),
     .rst_ni(rst_ni),
+    .req_chk_i(1'b1),
     .req_i(packer_arb_req), // N number of reqs
     .data_i('{default: 1'b0}),
     .gnt_o(packer_arb_gnt), // N number of gnts
diff --git a/hw/ip/flash_ctrl/rtl/flash_ctrl_lcmgr.sv b/hw/ip/flash_ctrl/rtl/flash_ctrl_lcmgr.sv
index 5fca6c6..fd5931b 100644
--- a/hw/ip/flash_ctrl/rtl/flash_ctrl_lcmgr.sv
+++ b/hw/ip/flash_ctrl/rtl/flash_ctrl_lcmgr.sv
@@ -225,6 +225,7 @@
     .rst_src_ni(rst_ni),
     .clk_dst_i(clk_otp_i),
     .rst_dst_ni(rst_otp_ni),
+    .req_chk_i(1'b1),
     .src_req_i(addr_key_req_d),
     .src_ack_o(addr_key_ack_q),
     .dst_req_o(otp_key_req_o.addr_req),
@@ -237,6 +238,7 @@
     .rst_src_ni(rst_ni),
     .clk_dst_i(clk_otp_i),
     .rst_dst_ni(rst_otp_ni),
+    .req_chk_i(1'b1),
     .src_req_i(data_key_req_d),
     .src_ack_o(data_key_ack_q),
     .dst_req_o(otp_key_req_o.data_req),
diff --git a/hw/ip/flash_ctrl/rtl/flash_phy_rd.sv b/hw/ip/flash_ctrl/rtl/flash_phy_rd.sv
index 30babf6..18a0498 100644
--- a/hw/ip/flash_ctrl/rtl/flash_phy_rd.sv
+++ b/hw/ip/flash_ctrl/rtl/flash_phy_rd.sv
@@ -140,12 +140,11 @@
 
   prim_arbiter_tree #(
     .N(NumBuf),
-    // disable request stability assertion
-    .EnReqStabA(0),
     .DW(2)
   ) i_valid_random (
     .clk_i,
     .rst_ni,
+    .req_chk_i(1'b0), // Valid is allowed to drop without ready.
     .req_i(buf_valid),
     .data_i(dummy_data),
     .gnt_o(buf_valid_alloc),
diff --git a/hw/ip/keymgr/rtl/keymgr_reseed_ctrl.sv b/hw/ip/keymgr/rtl/keymgr_reseed_ctrl.sv
index b715b47..bba83cc 100644
--- a/hw/ip/keymgr/rtl/keymgr_reseed_ctrl.sv
+++ b/hw/ip/keymgr/rtl/keymgr_reseed_ctrl.sv
@@ -112,6 +112,7 @@
     .rst_src_ni(rst_ni),
     .clk_dst_i(clk_edn_i),
     .rst_dst_ni(rst_edn_ni),
+    .req_chk_i(1'b1),
     .src_req_i(edn_req),
     .src_ack_o(edn_ack),
     .dst_req_o(edn_o.edn_req),
diff --git a/hw/ip/kmac/rtl/kmac.sv b/hw/ip/kmac/rtl/kmac.sv
index 18e15dd..7753346 100644
--- a/hw/ip/kmac/rtl/kmac.sv
+++ b/hw/ip/kmac/rtl/kmac.sv
@@ -880,10 +880,11 @@
       // Design side
       .clk_i,
       .rst_ni,
-      .req_i (entropy_req),
-      .ack_o (entropy_ack),
-      .data_o(entropy_data),
-      .fips_o(entropy_fips),
+      .req_chk_i (1'b1),
+      .req_i     (entropy_req),
+      .ack_o     (entropy_ack),
+      .data_o    (entropy_data),
+      .fips_o    (entropy_fips),
       // EDN side
       .clk_edn_i,
       .rst_edn_ni,
diff --git a/hw/ip/lc_ctrl/rtl/lc_ctrl.sv b/hw/ip/lc_ctrl/rtl/lc_ctrl.sv
index 45728fd..6c568c9 100644
--- a/hw/ip/lc_ctrl/rtl/lc_ctrl.sv
+++ b/hw/ip/lc_ctrl/rtl/lc_ctrl.sv
@@ -536,7 +536,7 @@
   // KMAC Interface //
   ////////////////////
 
-  logic token_hash_req, token_hash_ack, token_hash_err;
+  logic token_hash_req, token_hash_req_chk, token_hash_ack, token_hash_err;
   lc_token_t hashed_token;
   lc_ctrl_kmac_if u_lc_ctrl_kmac_if (
     .clk_i,
@@ -545,11 +545,12 @@
     .rst_kmac_ni,
     .kmac_data_i,
     .kmac_data_o,
-    .transition_token_i ( transition_token_q ),
-    .token_hash_req_i   ( token_hash_req     ),
-    .token_hash_ack_o   ( token_hash_ack     ),
-    .token_hash_err_o   ( token_hash_err     ),
-    .hashed_token_o     ( hashed_token       )
+    .transition_token_i   ( transition_token_q ),
+    .token_hash_req_i     ( token_hash_req     ),
+    .token_hash_req_chk_i ( token_hash_req_chk ),
+    .token_hash_ack_o     ( token_hash_ack     ),
+    .token_hash_err_o     ( token_hash_err     ),
+    .hashed_token_o       ( hashed_token       )
   );
 
   ////////////
@@ -584,6 +585,7 @@
     .dec_lc_cnt_o           ( dec_lc_cnt                      ),
     .dec_lc_id_state_o      ( dec_lc_id_state                 ),
     .token_hash_req_o       ( token_hash_req                  ),
+    .token_hash_req_chk_o   ( token_hash_req_chk              ),
     .token_hash_ack_i       ( token_hash_ack                  ),
     .token_hash_err_i       ( token_hash_err                  ),
     .hashed_token_i         ( hashed_token                    ),
diff --git a/hw/ip/lc_ctrl/rtl/lc_ctrl_fsm.sv b/hw/ip/lc_ctrl/rtl/lc_ctrl_fsm.sv
index 17c85f1..3a226f9 100644
--- a/hw/ip/lc_ctrl/rtl/lc_ctrl_fsm.sv
+++ b/hw/ip/lc_ctrl/rtl/lc_ctrl_fsm.sv
@@ -45,6 +45,7 @@
   output dec_lc_id_state_e      dec_lc_id_state_o,
   // Token hashing interface
   output logic                  token_hash_req_o,
+  output logic                  token_hash_req_chk_o,
   input                         token_hash_ack_i,
   input                         token_hash_err_i,
   input  lc_token_t             hashed_token_i,
@@ -149,7 +150,8 @@
     lc_cnt_d      = lc_cnt_q;
 
     // Token hashing.
-    token_hash_req_o = 1'b0;
+    token_hash_req_o     = 1'b0;
+    token_hash_req_chk_o = 1'b1;
 
     // OTP Interface
     otp_prog_req_o = 1'b0;
@@ -380,9 +382,13 @@
       ///////////////////////////////////////////////////////////////////
       // Terminal states.
       ScrapSt,
-      PostTransSt,
+      PostTransSt: ;
+
       EscalateSt,
-      InvalidSt: ;
+      InvalidSt: begin
+        // During an escalation it is okay to de-assert token_hash_req without receivng ACK.
+        token_hash_req_chk_o = 1'b0;
+      end
       ///////////////////////////////////////////////////////////////////
       // Go to terminal error state if we get here.
       default: fsm_state_d = InvalidSt;
diff --git a/hw/ip/lc_ctrl/rtl/lc_ctrl_kmac_if.sv b/hw/ip/lc_ctrl/rtl/lc_ctrl_kmac_if.sv
index a8f6f33..6949d55 100644
--- a/hw/ip/lc_ctrl/rtl/lc_ctrl_kmac_if.sv
+++ b/hw/ip/lc_ctrl/rtl/lc_ctrl_kmac_if.sv
@@ -24,6 +24,8 @@
   // Token hashing interface to LC FSM'
   input lc_token_t              transition_token_i,
   input                         token_hash_req_i,
+  // Used for gating assertions inside CDC primitives.
+  input                         token_hash_req_chk_i,
   output                        token_hash_ack_o,
   output                        token_hash_err_o,
   output lc_token_t             hashed_token_o
@@ -44,15 +46,13 @@
   // make sure the CDC paths go through a prim_sync_reqack_data instance).
   prim_sync_reqack_data #(
     .Width(LcTokenWidth),
-    .DataSrc2Dst(1'b1),
-    // Source domain requests may drop out
-    // when the LC FSM goes into error state.
-    .EnReqStabA(1'b0)
+    .DataSrc2Dst(1'b1)
   ) u_prim_sync_reqack_data_out (
     .clk_src_i  ( clk_i                 ),
     .rst_src_ni ( rst_ni                ),
     .clk_dst_i  ( clk_kmac_i            ),
     .rst_dst_ni ( rst_kmac_ni           ),
+    .req_chk_i  ( token_hash_req_chk_i  ),
     .src_req_i  ( token_hash_req        ),
     .src_ack_o  (                       ), // not connected
     .dst_req_o  (                       ), // not connected
@@ -71,25 +71,23 @@
     .DataSrc2Dst(1'b0),
     // This instantiates a data register
     // on the destination side.
-    .DataReg    (1'b1),
-    // Source domain requests may drop out
-    // when the LC FSM goes into error state.
-    .EnReqStabA(1'b0)
+    .DataReg    (1'b1)
   ) u_prim_sync_reqack_data_in (
-    .clk_src_i  ( clk_i            ),
-    .rst_src_ni ( rst_ni           ),
-    .clk_dst_i  ( clk_kmac_i       ),
-    .rst_dst_ni ( rst_kmac_ni      ),
-    .src_req_i  ( token_hash_req   ),
-    .src_ack_o  ( token_hash_ack_d ),
-    .dst_req_o  ( kmac_req         ),
-    .dst_ack_i  ( kmac_ack         ),
+    .clk_src_i  ( clk_i                ),
+    .rst_src_ni ( rst_ni               ),
+    .clk_dst_i  ( clk_kmac_i           ),
+    .rst_dst_ni ( rst_kmac_ni          ),
+    .req_chk_i  ( token_hash_req_chk_i ),
+    .src_req_i  ( token_hash_req       ),
+    .src_ack_o  ( token_hash_ack_d     ),
+    .dst_req_o  ( kmac_req             ),
+    .dst_ack_i  ( kmac_ack             ),
     // Truncate hash to 128bit and remove masking (not required here).
     .data_i     ( {kmac_data_i.error,
                    kmac_data_i.digest_share0[LcTokenWidth-1:0] ^
                    kmac_data_i.digest_share1[LcTokenWidth-1:0]} ),
     .data_o     ( {token_hash_err_d,
-                   hashed_token_d} )
+                   hashed_token_d}     )
   );
 
   logic unused_sigs;
diff --git a/hw/ip/otbn/rtl/otbn.sv b/hw/ip/otbn/rtl/otbn.sv
index 881db26..eafd3ab 100644
--- a/hw/ip/otbn/rtl/otbn.sv
+++ b/hw/ip/otbn/rtl/otbn.sv
@@ -653,6 +653,7 @@
   ) u_prim_edn_rnd_req (
     .clk_i,
     .rst_ni     ( rst_n        ),
+    .req_chk_i  ( 1'b1         ),
     .req_i      ( edn_rnd_req  ),
     .ack_o      ( edn_rnd_ack  ),
     .data_o     ( edn_rnd_data ),
@@ -668,6 +669,7 @@
   ) u_prim_edn_urnd_req (
     .clk_i,
     .rst_ni     ( rst_n         ),
+    .req_chk_i  ( 1'b1          ),
     .req_i      ( edn_urnd_req  ),
     .ack_o      ( edn_urnd_ack  ),
     .data_o     ( edn_urnd_data ),
diff --git a/hw/ip/otbn/rtl/otbn_scramble_ctrl.sv b/hw/ip/otbn/rtl/otbn_scramble_ctrl.sv
index 0bcd259..79a9a1d 100644
--- a/hw/ip/otbn/rtl/otbn_scramble_ctrl.sv
+++ b/hw/ip/otbn/rtl/otbn_scramble_ctrl.sv
@@ -166,6 +166,7 @@
     .rst_src_ni(rst_ni),
     .clk_dst_i (clk_otp_i),
     .rst_dst_ni(rst_otp_ni),
+    .req_chk_i (1'b1),
     .src_req_i (otp_key_req),
     .src_ack_o (otp_key_ack),
     .dst_req_o (otbn_otp_key_o.req),
diff --git a/hw/ip/otp_ctrl/rtl/otp_ctrl.sv b/hw/ip/otp_ctrl/rtl/otp_ctrl.sv
index 662d1c0..daef571 100644
--- a/hw/ip/otp_ctrl/rtl/otp_ctrl.sv
+++ b/hw/ip/otp_ctrl/rtl/otp_ctrl.sv
@@ -130,6 +130,8 @@
   lc_ctrl_pkg::lc_tx_t [1:0] lc_dft_en;
   // NumAgents + lfsr timer and scrambling datapath.
   lc_ctrl_pkg::lc_tx_t [NumAgentsIdx+1:0] lc_escalate_en, lc_escalate_en_synced;
+  // Single wire for gating assertions in arbitration and CDC primitives.
+  logic lc_escalate_en_any;
 
   prim_lc_sync #(
     .NumCopies(NumAgentsIdx+2)
@@ -375,6 +377,8 @@
     fatal_bus_integ_error_d = fatal_bus_integ_error_q | (|intg_error);
     // These are the per-partition buffered escalation inputs
     lc_escalate_en = lc_escalate_en_synced;
+    // Need a single wire for gating assertions in arbitration and CDC primitives.
+    lc_escalate_en_any = 1'b0;
     // Aggregate all the errors from the partitions and the DAI/LCI
     for (int k = 0; k < NumPart+2; k++) begin
       // Set the error bit if the error status of the corresponding partition is nonzero.
@@ -396,6 +400,9 @@
       if (fatal_macro_error_q || fatal_check_error_q) begin
         lc_escalate_en[k] = lc_escalate_en_synced[k] | lc_ctrl_pkg::On;
       end
+      if (lc_escalate_en[k] == lc_ctrl_pkg::On) begin
+        lc_escalate_en_any = 1'b1;
+      end
     end
   end
 
@@ -568,34 +575,32 @@
   logic key_edn_req, key_edn_ack;
   prim_arbiter_tree #(
     .N(2),
-    .EnDataPort(0),
-    // Disable req stable assertion check due to lc_escalate_en_i
-    .EnReqStabA(0)
+    .EnDataPort(0)
   ) u_edn_arb (
     .clk_i,
     .rst_ni,
-    .req_i   ( {lfsr_edn_req, key_edn_req} ),
-    .data_i  ( '{default: '0}              ),
-    .gnt_o   ( {lfsr_edn_ack, key_edn_ack} ),
-    .idx_o   (                             ), // unused
-    .valid_o ( edn_req                     ),
-    .data_o  (                             ), // unused
-    .ready_i ( edn_ack                     )
+    .req_chk_i ( ~lc_escalate_en_any         ),
+    .req_i     ( {lfsr_edn_req, key_edn_req} ),
+    .data_i    ( '{default: '0}              ),
+    .gnt_o     ( {lfsr_edn_ack, key_edn_ack} ),
+    .idx_o     (                             ), // unused
+    .valid_o   ( edn_req                     ),
+    .data_o    (                             ), // unused
+    .ready_i   ( edn_ack                     )
   );
 
   // This synchronizes the data coming from EDN and stacks the
   // 32bit EDN words to achieve an internal entropy width of 64bit.
   prim_edn_req #(
-    .OutWidth(EdnDataWidth),
-    // Disable req stable assertion check due to lc_escalate_en_i
-    .EnReqStabA(0)
+    .OutWidth(EdnDataWidth)
   ) u_prim_edn_req (
     .clk_i,
     .rst_ni,
-    .req_i      ( edn_req  ),
-    .ack_o      ( edn_ack  ),
-    .data_o     ( edn_data ),
-    .fips_o     (          ), // unused
+    .req_chk_i ( ~lc_escalate_en_any ),
+    .req_i     ( edn_req             ),
+    .ack_o     ( edn_ack             ),
+    .data_o    ( edn_data            ),
+    .fips_o    (                     ), // unused
     .clk_edn_i,
     .rst_edn_ni,
     .edn_o,
@@ -623,19 +628,18 @@
   // transactions can be completely independent.
   prim_arbiter_tree #(
     .N(NumAgents),
-    .DW($bits(otp_bundle_t)),
-    // Disable req stable assertion check due to lc_escalate_en_i
-    .EnReqStabA(0)
+    .DW($bits(otp_bundle_t))
   ) u_otp_arb (
     .clk_i,
     .rst_ni,
-    .req_i   ( part_otp_arb_req    ),
-    .data_i  ( part_otp_arb_bundle ),
-    .gnt_o   ( part_otp_arb_gnt    ),
-    .idx_o   ( otp_arb_idx         ),
-    .valid_o ( otp_arb_valid       ),
-    .data_o  ( otp_arb_bundle      ),
-    .ready_i ( otp_arb_ready       )
+    .req_chk_i ( ~lc_escalate_en_any ),
+    .req_i     ( part_otp_arb_req    ),
+    .data_i    ( part_otp_arb_bundle ),
+    .gnt_o     ( part_otp_arb_gnt    ),
+    .idx_o     ( otp_arb_idx         ),
+    .valid_o   ( otp_arb_valid       ),
+    .data_o    ( otp_arb_bundle      ),
+    .ready_i   ( otp_arb_ready       )
   );
 
   prim_otp_pkg::err_e          part_otp_err;
@@ -751,18 +755,18 @@
   // Hence, the idx_o signal is guaranteed to remain stable until ack'ed.
   prim_arbiter_tree #(
     .N(NumAgents),
-    .DW($bits(scrmbl_bundle_t)),
-    .EnReqStabA(0)
+    .DW($bits(scrmbl_bundle_t))
   ) u_scrmbl_mtx (
     .clk_i,
     .rst_ni,
-    .req_i   ( part_scrmbl_mtx_req  ),
-    .data_i  ( part_scrmbl_req_bundle ),
-    .gnt_o   (                        ),
-    .idx_o   ( scrmbl_mtx_idx         ),
-    .valid_o ( scrmbl_mtx_valid       ),
-    .data_o  ( scrmbl_req_bundle      ),
-    .ready_i ( 1'b0                   )
+    .req_chk_i ( ~lc_escalate_en_any    ),
+    .req_i     ( part_scrmbl_mtx_req    ),
+    .data_i    ( part_scrmbl_req_bundle ),
+    .gnt_o     (                        ),
+    .idx_o     ( scrmbl_mtx_idx         ),
+    .valid_o   ( scrmbl_mtx_valid       ),
+    .data_o    ( scrmbl_req_bundle      ),
+    .ready_i   ( 1'b0                   )
   );
 
   // Since the ready_i signal of the arbiter is statically set to 1'b0 above, we are always in a
diff --git a/hw/ip/otp_ctrl/rtl/otp_ctrl_kdi.sv b/hw/ip/otp_ctrl/rtl/otp_ctrl_kdi.sv
index 3be8c8d..125fbed 100644
--- a/hw/ip/otp_ctrl/rtl/otp_ctrl_kdi.sv
+++ b/hw/ip/otp_ctrl/rtl/otp_ctrl_kdi.sv
@@ -174,13 +174,14 @@
   u_req_arb (
     .clk_i,
     .rst_ni,
-    .req_i   ( req         ),
-    .data_i  ( req_bundles ),
-    .gnt_o   ( gnt         ),
-    .idx_o   (             ),
-    .valid_o ( req_valid   ),
-    .data_o  ( req_bundle  ),
-    .ready_i ( req_ready   )
+    .req_chk_i ( 1'b1        ),
+    .req_i     ( req         ),
+    .data_i    ( req_bundles ),
+    .gnt_o     ( gnt         ),
+    .idx_o     (             ),
+    .valid_o   ( req_valid   ),
+    .data_o    ( req_bundle  ),
+    .ready_i   ( req_ready   )
   );
 
   //////////////////////////////
diff --git a/hw/ip/prim/fpv/tb/prim_arbiter_ppc_fpv.sv b/hw/ip/prim/fpv/tb/prim_arbiter_ppc_fpv.sv
index 7e7e38a..a571992 100644
--- a/hw/ip/prim/fpv/tb/prim_arbiter_ppc_fpv.sv
+++ b/hw/ip/prim/fpv/tb/prim_arbiter_ppc_fpv.sv
@@ -9,12 +9,13 @@
   parameter int unsigned N = 8,
   parameter int unsigned DW = 32,
   parameter bit EnDataPort = 1,
-  parameter bit EnReqStabA = 1,
   localparam int IdxW = $clog2(N)
 ) (
   input clk_i,
   input rst_ni,
 
+  input                    req_chk_i,
+
   input        [ N-1:0]    req_i,
   input        [DW-1:0]    data_i [N],
   output logic [ N-1:0]    gnt_o,
@@ -29,11 +30,11 @@
   prim_arbiter_ppc #(
     .N(N),
     .DW(DW),
-    .EnDataPort(EnDataPort),
-    .EnReqStabA(EnReqStabA)
+    .EnDataPort(EnDataPort)
   ) i_prim_arbiter_ppc (
     .clk_i,
     .rst_ni,
+    .req_chk_i,
     .req_i,
     .data_i,
     .gnt_o,
diff --git a/hw/ip/prim/fpv/tb/prim_arbiter_tree_fpv.sv b/hw/ip/prim/fpv/tb/prim_arbiter_tree_fpv.sv
index aa8c299..b5a61c2 100644
--- a/hw/ip/prim/fpv/tb/prim_arbiter_tree_fpv.sv
+++ b/hw/ip/prim/fpv/tb/prim_arbiter_tree_fpv.sv
@@ -9,12 +9,13 @@
   parameter int N = 8,
   parameter int DW = 32,
   parameter bit EnDataPort = 1,
-  parameter bit EnReqStabA = 1,
   localparam int IdxW = $clog2(N)
 ) (
   input clk_i,
   input rst_ni,
 
+  input                    req_chk_i,
+
   input        [ N-1:0]    req_i,
   input        [DW-1:0]    data_i [N],
   output logic [ N-1:0]    gnt_o,
@@ -29,11 +30,11 @@
   prim_arbiter_tree #(
     .N(N),
     .DW(DW),
-    .EnDataPort(EnDataPort),
-    .EnReqStabA(EnReqStabA)
+    .EnDataPort(EnDataPort)
   ) i_prim_arbiter_tree (
     .clk_i,
     .rst_ni,
+    .req_chk_i,
     .req_i,
     .data_i,
     .gnt_o,
diff --git a/hw/ip/prim/pre_dv/prim_sync_reqack/rtl/prim_sync_reqack_tb.sv b/hw/ip/prim/pre_dv/prim_sync_reqack/rtl/prim_sync_reqack_tb.sv
index 354e648..81f86d2 100644
--- a/hw/ip/prim/pre_dv/prim_sync_reqack/rtl/prim_sync_reqack_tb.sv
+++ b/hw/ip/prim/pre_dv/prim_sync_reqack/rtl/prim_sync_reqack_tb.sv
@@ -69,6 +69,8 @@
     .clk_dst_i  (clk_dst),
     .rst_dst_ni (rst_slow_n),
 
+    .req_chk_i  (1'b1),
+
     .src_req_i  (src_req),
     .src_ack_o  (src_ack),
     .dst_req_o  (dst_req),
diff --git a/hw/ip/prim/rtl/prim_arbiter_ppc.sv b/hw/ip/prim/rtl/prim_arbiter_ppc.sv
index 86bef1e..17f3e19 100644
--- a/hw/ip/prim/rtl/prim_arbiter_ppc.sv
+++ b/hw/ip/prim/rtl/prim_arbiter_ppc.sv
@@ -8,7 +8,6 @@
 //   N:           Number of request ports
 //   DW:          Data width
 //   DataPort:    Set to 1 to enable the data port. Otherwise that port will be ignored.
-//   EnReqStabA:  Checks whether requests remain asserted until granted
 //
 // This is the original implementation of the arbiter which relies on parallel prefix computing
 // optimization to optimize the request / arbiter tree. Not all synthesis tools may support this.
@@ -18,8 +17,8 @@
 // this behavior.
 //
 // Also, this module contains a request stability assertion that checks that requests stay asserted
-// until they have been served. This assertion can be optionally disabled by setting EnReqStabA to
-// zero. This is a non-functional parameter and does not affect the designs behavior.
+// until they have been served. This assertion can be gated by driving the req_chk_i low. This is
+// a non-functional input and does not affect the designs behavior.
 //
 // See also: prim_arbiter_tree
 
@@ -33,15 +32,14 @@
   // EnDataPort: {0, 1}, if 0, input data will be ignored
   parameter bit EnDataPort = 1,
 
-  // Non-functional parameter to switch on the request stability assertion
-  parameter bit EnReqStabA = 1,
-
   // Derived parameters
   localparam int IdxW = $clog2(N)
 ) (
   input clk_i,
   input rst_ni,
 
+  input                    req_chk_i, // Used for gating assertions. Drive to 1 during normal
+                                      // operation.
   input        [ N-1:0]    req_i,
   input        [DW-1:0]    data_i [N],
   output logic [ N-1:0]    gnt_o,
@@ -52,6 +50,10 @@
   input                    ready_i
 );
 
+  // req_chk_i is used for gating assertions only.
+  logic unused_req_chk;
+  assign unused_req_chk = req_chk_i;
+
   `ASSERT_INIT(CheckNGreaterZero_A, N > 0)
 
   // this case is basically just a bypass
@@ -165,13 +167,12 @@
   `ASSERT(DataFlow_A, ready_i && valid_o |-> data_o == data_i[idx_o])
 end
 
-if (EnReqStabA) begin : gen_lock_assertion
   // requests must stay asserted until they have been granted
-  `ASSUME(ReqStaysHighUntilGranted0_M, (|req_i) && !ready_i |=>
-      (req_i & $past(req_i)) == $past(req_i))
+  `ASSUME(ReqStaysHighUntilGranted0_M, |req_i && !ready_i |=>
+      (req_i & $past(req_i)) == $past(req_i), clk_i, !rst_ni || !req_chk_i)
   // check that the arbitration decision is held if the sink is not ready
-  `ASSERT(LockArbDecision_A, |req_i && !ready_i |=> idx_o == $past(idx_o))
-end
+  `ASSERT(LockArbDecision_A, |req_i && !ready_i |=> idx_o == $past(idx_o),
+      clk_i, !rst_ni || !req_chk_i)
 
 // FPV-only assertions with symbolic variables
 `ifdef FPV_ON
@@ -216,11 +217,9 @@
     end
   end
 
-  if (EnReqStabA) begin : gen_lock_assertion_fpv
-    // requests must stay asserted until they have been granted
-    `ASSUME(ReqStaysHighUntilGranted1_M, req_i[k] & !gnt_o[k] |=>
-        req_i[k], clk_i, !rst_ni)
-  end
+  // requests must stay asserted until they have been granted
+  `ASSUME(ReqStaysHighUntilGranted1_M, req_i[k] && !gnt_o[k] |=>
+      req_i[k], clk_i, !rst_ni || !req_chk_i)
 `endif
 
 endmodule : prim_arbiter_ppc
diff --git a/hw/ip/prim/rtl/prim_arbiter_tree.sv b/hw/ip/prim/rtl/prim_arbiter_tree.sv
index 7b6be73..93d809e 100644
--- a/hw/ip/prim/rtl/prim_arbiter_tree.sv
+++ b/hw/ip/prim/rtl/prim_arbiter_tree.sv
@@ -8,7 +8,6 @@
 //   N:           Number of request ports
 //   DW:          Data width
 //   DataPort:    Set to 1 to enable the data port. Otherwise that port will be ignored.
-//   EnReqStabA:  Checks whether requests remain asserted until granted
 //
 // This is a tree implementation of a round robin arbiter. It has the same behavior as the PPC
 // implementation in prim_arbiter_ppc, and also uses a prefix summing approach to determine the next
@@ -25,8 +24,8 @@
 // this behavior.
 //
 // Also, this module contains a request stability assertion that checks that requests stay asserted
-// until they have been served. This assertion can be optionally disabled by setting EnReqStabA to
-// zero. This is a non-functional parameter and does not affect the designs behavior.
+// until they have been served. This assertion can be gated by driving the req_chk_i low. This is
+// a non-functional input and does not affect the designs behavior.
 //
 // See also: prim_arbiter_ppc
 
@@ -40,15 +39,14 @@
   // EnDataPort: {0, 1}, if 0, input data will be ignored
   parameter bit EnDataPort = 1,
 
-  // Non-functional parameter to switch on the request stability assertion
-  parameter bit EnReqStabA = 1,
-
   // Derived parameters
   localparam int IdxW = $clog2(N)
 ) (
   input clk_i,
   input rst_ni,
 
+  input                    req_chk_i, // Used for gating assertions. Drive to 1 during normal
+                                      // operation.
   input        [ N-1:0]    req_i,
   input        [DW-1:0]    data_i [N],
   output logic [ N-1:0]    gnt_o,
@@ -59,6 +57,10 @@
   input                    ready_i
 );
 
+  // req_chk_i is used for gating assertions only.
+  logic unused_req_chk;
+  assign unused_req_chk = req_chk_i;
+
   `ASSERT_INIT(CheckNGreaterZero_A, N > 0)
 
   // this case is basically just a bypass
@@ -231,13 +233,12 @@
   `ASSERT(DataFlow_A, ready_i && valid_o |-> data_o == data_i[idx_o])
 end
 
-if (EnReqStabA) begin : gen_lock_assertion
   // requests must stay asserted until they have been granted
-  `ASSUME(ReqStaysHighUntilGranted0_M, (|req_i) && !ready_i |=>
-      (req_i & $past(req_i)) == $past(req_i))
+  `ASSUME(ReqStaysHighUntilGranted0_M, |req_i && !ready_i |=>
+      (req_i & $past(req_i)) == $past(req_i), clk_i, !rst_ni || !req_chk_i)
   // check that the arbitration decision is held if the sink is not ready
-  `ASSERT(LockArbDecision_A, |req_i && !ready_i |=> idx_o == $past(idx_o))
-end
+  `ASSERT(LockArbDecision_A, |req_i && !ready_i |=> idx_o == $past(idx_o),
+      clk_i, !rst_ni || !req_chk_i)
 
 // FPV-only assertions with symbolic variables
 `ifdef FPV_ON
@@ -282,11 +283,9 @@
     end
   end
 
-  if (EnReqStabA) begin : gen_lock_assertion_fpv
-    // requests must stay asserted until they have been granted
-    `ASSUME(ReqStaysHighUntilGranted1_M, req_i[k] & !gnt_o[k] |=>
-        req_i[k], clk_i, !rst_ni)
-  end
+  // requests must stay asserted until they have been granted
+  `ASSUME(ReqStaysHighUntilGranted1_M, req_i[k] && !gnt_o[k] |=>
+      req_i[k], clk_i, !rst_ni || !req_chk_i)
 `endif
 
 endmodule : prim_arbiter_tree
diff --git a/hw/ip/prim/rtl/prim_edn_req.sv b/hw/ip/prim/rtl/prim_edn_req.sv
index e973fcc..ad5b2ec 100644
--- a/hw/ip/prim/rtl/prim_edn_req.sv
+++ b/hw/ip/prim/rtl/prim_edn_req.sv
@@ -16,15 +16,13 @@
 module prim_edn_req
   import prim_alert_pkg::*;
 #(
-  parameter int OutWidth = 32,
-
-  // Non-functional parameter to switch on the request stability assertion.
-  // Used in submodule `prim_sync_reqack`.
-  parameter bit EnReqStabA = 1
+  parameter int OutWidth = 32
 ) (
   // Design side
   input                       clk_i,
   input                       rst_ni,
+  input                       req_chk_i, // Used for gating assertions. Drive to 1 during normal
+                                         // operation.
   input                       req_i,
   output logic                ack_o,
   output logic [OutWidth-1:0] data_o,
@@ -46,13 +44,13 @@
   prim_sync_reqack_data #(
     .Width(SyncWidth),
     .DataSrc2Dst(1'b0),
-    .DataReg(1'b0),
-    .EnReqStabA(EnReqStabA)
+    .DataReg(1'b0)
   ) u_prim_sync_reqack_data (
     .clk_src_i  ( clk_i                           ),
     .rst_src_ni ( rst_ni                          ),
     .clk_dst_i  ( clk_edn_i                       ),
     .rst_dst_ni ( rst_edn_ni                      ),
+    .req_chk_i  ( req_chk_i                       ),
     .src_req_i  ( word_req                        ),
     .src_ack_o  ( word_ack                        ),
     .dst_req_o  ( edn_o.edn_req                   ),
diff --git a/hw/ip/prim/rtl/prim_sram_arbiter.sv b/hw/ip/prim/rtl/prim_sram_arbiter.sv
index 2df22ea..50d2762 100644
--- a/hw/ip/prim/rtl/prim_sram_arbiter.sv
+++ b/hw/ip/prim/rtl/prim_sram_arbiter.sv
@@ -67,13 +67,14 @@
     ) u_reqarb (
       .clk_i,
       .rst_ni,
+      .req_chk_i ( 1'b1        ),
       .req_i,
-      .data_i  ( req_packed  ),
+      .data_i    ( req_packed  ),
       .gnt_o,
-      .idx_o   (             ),
-      .valid_o ( sram_req_o  ),
-      .data_o  ( sram_packed ),
-      .ready_i ( 1'b1        )
+      .idx_o     (             ),
+      .valid_o   ( sram_req_o  ),
+      .data_o    ( sram_packed ),
+      .ready_i   ( 1'b1        )
     );
   end else if (ArbiterImpl == "BINTREE") begin : gen_tree_arb
     prim_arbiter_tree #(
@@ -82,13 +83,14 @@
     ) u_reqarb (
       .clk_i,
       .rst_ni,
+      .req_chk_i ( 1'b1        ),
       .req_i,
-      .data_i  ( req_packed  ),
+      .data_i    ( req_packed  ),
       .gnt_o,
-      .idx_o   (             ),
-      .valid_o ( sram_req_o  ),
-      .data_o  ( sram_packed ),
-      .ready_i ( 1'b1        )
+      .idx_o     (             ),
+      .valid_o   ( sram_req_o  ),
+      .data_o    ( sram_packed ),
+      .ready_i   ( 1'b1        )
     );
   end else begin : gen_unknown
     `ASSERT_INIT(UnknownArbImpl_A, 0)
diff --git a/hw/ip/prim/rtl/prim_sync_reqack.sv b/hw/ip/prim/rtl/prim_sync_reqack.sv
index 45d0c59..b2d036a 100644
--- a/hw/ip/prim/rtl/prim_sync_reqack.sv
+++ b/hw/ip/prim/rtl/prim_sync_reqack.sv
@@ -23,21 +23,24 @@
 
 `include "prim_assert.sv"
 
-module prim_sync_reqack #(
-  // Non-functional parameter to switch on the request stability assertion
-  parameter bit EnReqStabA = 1
-) (
+module prim_sync_reqack (
   input  clk_src_i,       // REQ side, SRC domain
   input  rst_src_ni,      // REQ side, SRC domain
   input  clk_dst_i,       // ACK side, DST domain
   input  rst_dst_ni,      // ACK side, DST domain
 
+  input  logic req_chk_i, // Used for gating assertions. Drive to 1 during normal operation.
+
   input  logic src_req_i, // REQ side, SRC domain
   output logic src_ack_o, // REQ side, SRC domain
   output logic dst_req_o, // ACK side, DST domain
   input  logic dst_ack_i  // ACK side, DST domain
 );
 
+  // req_chk_i is used for gating assertions only.
+  logic unused_req_chk;
+  assign unused_req_chk = req_chk_i;
+
   // Types
   typedef enum logic {
     EVEN, ODD
@@ -167,10 +170,9 @@
     end
   end
 
-  if (EnReqStabA) begin : gen_lock_assertion
-    // SRC domain can only de-assert REQ after receiving ACK.
-    `ASSERT(SyncReqAckHoldReq, $fell(src_req_i) |-> $fell(src_ack_o), clk_src_i, !rst_src_ni)
-  end
+  // SRC domain can only de-assert REQ after receiving ACK.
+  `ASSERT(SyncReqAckHoldReq, $fell(src_req_i) |->
+      $fell(src_ack_o), clk_src_i, !rst_src_ni || !req_chk_i)
 
   // DST domain cannot assert ACK without REQ.
   `ASSERT(SyncReqAckAckNeedsReq, dst_ack_i |-> dst_req_o, clk_dst_i, !rst_dst_ni)
diff --git a/hw/ip/prim/rtl/prim_sync_reqack_data.sv b/hw/ip/prim/rtl/prim_sync_reqack_data.sv
index 82e7fa0..42727e4 100644
--- a/hw/ip/prim/rtl/prim_sync_reqack_data.sv
+++ b/hw/ip/prim/rtl/prim_sync_reqack_data.sv
@@ -20,15 +20,16 @@
   parameter int unsigned Width       = 1,
   parameter bit          DataSrc2Dst = 1'b1, // Direction of data flow: 1'b1 = SRC to DST,
                                              //                         1'b0 = DST to SRC
-  parameter bit          DataReg     = 1'b0, // Enable optional register stage for data,
+  parameter bit          DataReg     = 1'b0  // Enable optional register stage for data,
                                              // only usable with DataSrc2Dst == 1'b0.
-  parameter bit EnReqStabA = 1               // Used in submodule `prim_sync_reqack`.
 ) (
   input  clk_src_i,       // REQ side, SRC domain
   input  rst_src_ni,      // REQ side, SRC domain
   input  clk_dst_i,       // ACK side, DST domain
   input  rst_dst_ni,      // ACK side, DST domain
 
+  input  logic req_chk_i, // Used for gating assertions. Drive to 1 during normal operation.
+
   input  logic src_req_i, // REQ side, SRC domain
   output logic src_ack_o, // REQ side, SRC domain
   output logic dst_req_o, // ACK side, DST domain
@@ -41,14 +42,14 @@
   ////////////////////////////////////
   // REQ/ACK synchronizer primitive //
   ////////////////////////////////////
-  prim_sync_reqack #(
-    .EnReqStabA(EnReqStabA)
-  ) u_prim_sync_reqack (
+  prim_sync_reqack u_prim_sync_reqack (
     .clk_src_i,
     .rst_src_ni,
     .clk_dst_i,
     .rst_dst_ni,
 
+    .req_chk_i,
+
     .src_req_i,
     .src_ack_o,
     .dst_req_o,
diff --git a/hw/ip/spi_host/rtl/spi_host_command_cdc.sv b/hw/ip/spi_host/rtl/spi_host_command_cdc.sv
index 132d028..5062ea2 100644
--- a/hw/ip/spi_host/rtl/spi_host_command_cdc.sv
+++ b/hw/ip/spi_host/rtl/spi_host_command_cdc.sv
@@ -74,6 +74,7 @@
     .rst_src_ni (rst_ni),
     .clk_dst_i  (clk_core_i),
     .rst_dst_ni (rst_core_ni),
+    .req_chk_i  (1'b1),
     .src_req_i  (cdc_req_q),
     .src_ack_o  (cdc_ack),
     .dst_req_o  (core_cdc_req),
diff --git a/hw/ip/sram_ctrl/rtl/sram_ctrl.sv b/hw/ip/sram_ctrl/rtl/sram_ctrl.sv
index 7296063..683a1f4 100644
--- a/hw/ip/sram_ctrl/rtl/sram_ctrl.sv
+++ b/hw/ip/sram_ctrl/rtl/sram_ctrl.sv
@@ -240,6 +240,7 @@
     .rst_src_ni ( rst_ni             ),
     .clk_dst_i  ( clk_otp_i          ),
     .rst_dst_ni ( rst_otp_ni         ),
+    .req_chk_i  ( 1'b1               ),
     .src_req_i  ( key_req_pending_q  ),
     .src_ack_o  ( key_ack            ),
     .dst_req_o  ( sram_otp_key_o.req ),
diff --git a/hw/ip/tlul/rtl/tlul_socket_m1.sv b/hw/ip/tlul/rtl/tlul_socket_m1.sv
index b8762ed..52258f5 100644
--- a/hw/ip/tlul/rtl/tlul_socket_m1.sv
+++ b/hw/ip/tlul/rtl/tlul_socket_m1.sv
@@ -168,34 +168,34 @@
   if (tlul_pkg::ArbiterImpl == "PPC") begin : gen_arb_ppc
     prim_arbiter_ppc #(
       .N          (M),
-      .DW         ($bits(tlul_pkg::tl_h2d_t)),
-      .EnReqStabA (0)
+      .DW         ($bits(tlul_pkg::tl_h2d_t))
     ) u_reqarb (
       .clk_i,
       .rst_ni,
-      .req_i   ( hrequest    ),
-      .data_i  ( hreq_fifo_o ),
-      .gnt_o   ( hgrant      ),
-      .idx_o   (             ),
-      .valid_o ( arb_valid   ),
-      .data_o  ( arb_data    ),
-      .ready_i ( arb_ready   )
+      .req_chk_i ( 1'b0        ), // TL-UL allows dropping valid without ready. See #3354.
+      .req_i     ( hrequest    ),
+      .data_i    ( hreq_fifo_o ),
+      .gnt_o     ( hgrant      ),
+      .idx_o     (             ),
+      .valid_o   ( arb_valid   ),
+      .data_o    ( arb_data    ),
+      .ready_i   ( arb_ready   )
     );
   end else if (tlul_pkg::ArbiterImpl == "BINTREE") begin : gen_tree_arb
     prim_arbiter_tree #(
       .N          (M),
-      .DW         ($bits(tlul_pkg::tl_h2d_t)),
-      .EnReqStabA (0)
+      .DW         ($bits(tlul_pkg::tl_h2d_t))
     ) u_reqarb (
       .clk_i,
       .rst_ni,
-      .req_i   ( hrequest    ),
-      .data_i  ( hreq_fifo_o ),
-      .gnt_o   ( hgrant      ),
-      .idx_o   (             ),
-      .valid_o ( arb_valid   ),
-      .data_o  ( arb_data    ),
-      .ready_i ( arb_ready   )
+      .req_chk_i ( 1'b0        ), // TL-UL allows dropping valid without ready. See #3354.
+      .req_i     ( hrequest    ),
+      .data_i    ( hreq_fifo_o ),
+      .gnt_o     ( hgrant      ),
+      .idx_o     (             ),
+      .valid_o   ( arb_valid   ),
+      .data_o    ( arb_data    ),
+      .ready_i   ( arb_ready   )
     );
   end else begin : gen_unknown
     `ASSERT_INIT(UnknownArbImpl_A, 0)