[dv] Add integrity test for passthru mem

Addressed #9546
For detail sequences, please refer to the added testplan

Signed-off-by: Weicai Yang <weicai@google.com>
diff --git a/hw/dv/sv/cip_lib/seq_lib/cip_base_vseq.sv b/hw/dv/sv/cip_lib/seq_lib/cip_base_vseq.sv
index 49fe26c..e96d17a 100644
--- a/hw/dv/sv/cip_lib/seq_lib/cip_base_vseq.sv
+++ b/hw/dv/sv/cip_lib/seq_lib/cip_base_vseq.sv
@@ -35,6 +35,9 @@
   // knobs to lock shadow register write access if fatal storage error occurred
   bit do_lock_shadow_reg = 1'b1;
 
+  // knob to enable/disable running csr_vseq with passthru_mem_tl_intg_err
+  bit en_csr_vseq_w_passthru_mem_intg = 1;
+
   // csr queues
   dv_base_reg all_csrs[$];
   dv_base_reg intr_state_csrs[$];
@@ -212,15 +215,16 @@
       input tl_intg_err_e     tl_intg_err_type = TlIntgErrNone,
       input int               req_abort_pct = 0);
 
+    cip_tl_seq_item rsp;
     if (blocking) begin
-      tl_access_sub(addr, write, data, completed, saw_err, mask, check_rsp, exp_err_rsp, exp_data,
-                    compare_mask, check_exp_data, req_abort_pct, instr_type, tl_sequencer_h,
-                    tl_intg_err_type);
+      tl_access_sub(addr, write, data, completed, saw_err, rsp, mask, check_rsp,
+                    exp_err_rsp, exp_data, compare_mask, check_exp_data, req_abort_pct,
+                    instr_type, tl_sequencer_h, tl_intg_err_type);
     end else begin
       fork
-        tl_access_sub(addr, write, data, completed, saw_err, mask, check_rsp, exp_err_rsp, exp_data,
-                      compare_mask, check_exp_data, req_abort_pct, instr_type, tl_sequencer_h,
-                      tl_intg_err_type);
+        tl_access_sub(addr, write, data, completed, saw_err, rsp, mask, check_rsp,
+                      exp_err_rsp, exp_data, compare_mask, check_exp_data, req_abort_pct,
+                      instr_type, tl_sequencer_h, tl_intg_err_type);
       join_none
       // Add #0 to ensure that this thread starts executing before any subsequent call
       #0;
@@ -232,6 +236,7 @@
                              inout bit [BUS_DW-1:0]  data,
                              output bit              completed,
                              output bit              saw_err,
+                             output cip_tl_seq_item  rsp,
                              input bit [BUS_DBW-1:0] mask = '1,
                              input bit               check_rsp = 1'b1,
                              input bit               exp_err_rsp = 1'b0,
@@ -260,8 +265,10 @@
             mask  == local::mask;
             data  == local::data;)
         `uvm_send_pri(tl_seq, 100)
+        rsp = tl_seq.rsp;
+
         if (!write) begin
-          data = tl_seq.rsp.d_data;
+          data = rsp.d_data;
           if (check_exp_data && !cfg.under_reset) begin
             bit [BUS_DW-1:0] masked_data = data & compare_mask;
             exp_data &= compare_mask;
@@ -269,13 +276,14 @@
           end
         end
         if (check_rsp && !cfg.under_reset && tl_intg_err_type == TlIntgErrNone) begin
-          `DV_CHECK_EQ(tl_seq.rsp.d_error, exp_err_rsp, "unexpected error response")
+          `DV_CHECK_EQ(rsp.d_error, exp_err_rsp, "unexpected error response")
         end
 
+
         // Expose whether the transaction ran and whether it generated an error. Note that we
         // probably only want to do a RAL update if it ran and caused no error.
-        completed = tl_seq.rsp.rsp_completed;
-        saw_err = tl_seq.rsp.d_error;
+        completed = rsp.rsp_completed;
+        saw_err = rsp.d_error;
 
         csr_utils_pkg::decrement_outstanding_access();,
         // thread to check timeout
@@ -393,6 +401,7 @@
       // Each iteration only sends 1 item with TL integrity error. Increase to send at least
       // 10 x num_times integrity errors
       "tl_intg_err":                   run_tl_intg_err_vseq(10 * num_times);
+      "passthru_mem_tl_intg_err":      run_passthru_mem_tl_intg_err_vseq(10 * num_times);
       "stress_all_with_rand_reset":    run_plusarg_vseq_with_rand_reset(num_times);
       "same_csr_outstanding":          run_same_csr_outstanding_vseq(num_times);
       "shadow_reg_errors":             run_shadow_reg_errors(num_times);
diff --git a/hw/dv/sv/cip_lib/seq_lib/cip_base_vseq__tl_errors.svh b/hw/dv/sv/cip_lib/seq_lib/cip_base_vseq__tl_errors.svh
index d1c7090..d11467a 100644
--- a/hw/dv/sv/cip_lib/seq_lib/cip_base_vseq__tl_errors.svh
+++ b/hw/dv/sv/cip_lib/seq_lib/cip_base_vseq__tl_errors.svh
@@ -266,6 +266,17 @@
 endtask
 
 virtual task run_tl_intg_err_vseq_sub(int num_times = 1, string ral_name);
+  uvm_mem mems[$];
+  dv_base_mem passthru_mems[$];
+
+  cfg.ral_models[ral_name].get_memories(mems);
+
+  foreach (mems[i]) begin
+    dv_base_mem dv_mem;
+    `downcast(dv_mem, mems[i])
+    if (dv_mem.get_data_intg_passthru()) passthru_mems.push_back(dv_mem);
+  end
+
   fork
     // run csr_rw seq to send some normal CSR accesses in parallel
     begin
@@ -334,4 +345,89 @@
   join
 endtask
 
+virtual task run_passthru_mem_tl_intg_err_vseq(int num_times = 1);
+  for (int trans = 1; trans <= num_times; trans++) begin
+    `uvm_info(`gfn, $sformatf("Running run_passthru_mem_tl_intg_err_vseq %0d/%0d",
+                              trans, num_times),
+              UVM_LOW)
+    `loop_ral_models_to_create_threads(run_passthru_mem_tl_intg_err_vseq_sub(num_times, ral_name);)
+    dut_init("HARD");
+  end
+  csr_utils_pkg::wait_no_outstanding_access();
+endtask
+
+virtual task run_passthru_mem_tl_intg_err_vseq_sub(int num_times = 1, string ral_name);
+  uvm_mem mems[$];
+  dv_base_mem passthru_mems[$];
+
+  cfg.ral_models[ral_name].get_memories(mems);
+
+  foreach (mems[i]) begin
+    dv_base_mem dv_mem;
+    `downcast(dv_mem, mems[i])
+    if (dv_mem.get_data_intg_passthru()) passthru_mems.push_back(dv_mem);
+  end
+
+  fork
+    // run csr_rw seq to send some normal CSR accesses in parallel
+    if (en_csr_vseq_w_passthru_mem_intg) begin
+      `uvm_info(`gfn, "Run csr_rw seq", UVM_HIGH)
+      run_csr_vseq(.csr_test_type("rw"), .ral_name(ral_name));
+    end
+    begin
+      if (passthru_mems.size > 0) begin
+        cfg.disable_d_user_data_intg_check_for_passthru_mem = 1;
+        test_intg_err_in_passthru_mem(passthru_mems);
+        cfg.disable_d_user_data_intg_check_for_passthru_mem = 0;
+      end
+    end
+  join
+endtask
+
+virtual task test_intg_err_in_passthru_mem(const ref dv_base_mem mems[$]);
+  foreach (mems[i]) begin
+    bit [BUS_AW-1:0] offset;
+    bit [BUS_AW-1:0] addr;
+    bit [BUS_DW-1:0] data;
+    string ral_name;
+    cip_tl_seq_item tl_access_rsp;
+    bit completed, saw_err;
+    bit data_intg_ok;
+
+    offset = $urandom_range(0, mems[i].get_n_bytes() - 1);
+    addr = mems[i].get_address(offset);
+    ral_name = mems[i].get_parent().get_name();
+
+    // Before inject faults, this read should have correct integrity
+    tl_access_sub(.addr(addr), .write(0), .data(data), .completed(completed), .saw_err(saw_err),
+                  .check_rsp(1),  .rsp(tl_access_rsp),
+                  .tl_sequencer_h(p_sequencer.tl_sequencer_hs[ral_name]));
+    `DV_CHECK_EQ(completed, 1)
+    `DV_CHECK_EQ(saw_err, 0)
+    tl_access_rsp.is_d_chan_intg_ok(.en_rsp_intg_chk(1),
+                                    .en_data_intg_chk(1),
+                                    .throw_error(1));
+
+    `uvm_info(`gfn, $sformatf("Backdoor inject intg fault to %s addr 0x%0h", ral_name, addr),
+              UVM_LOW)
+    inject_intg_fault_in_passthru_mem(mems[i], addr);
+
+    // Issue a read on the address that has been injected with integrity error
+    tl_access_sub(.addr(addr), .write(0), .data(data), .completed(completed), .saw_err(saw_err),
+                  .check_rsp(1),  .rsp(tl_access_rsp),
+                  .tl_sequencer_h(p_sequencer.tl_sequencer_hs[ral_name]));
+    `DV_CHECK_EQ(completed, 1)
+    `DV_CHECK_EQ(saw_err, 0)
+    // data integrity should be wrong
+    data_intg_ok = tl_access_rsp.is_d_chan_intg_ok(.en_rsp_intg_chk(0),
+                                                   .en_data_intg_chk(1),
+                                                   .throw_error(0));
+    `DV_CHECK_EQ(data_intg_ok, 0)
+  end
+endtask
+
+virtual function void inject_intg_fault_in_passthru_mem(dv_base_mem mem, bit [BUS_AW-1:0] addr);
+  `uvm_fatal(`gfn, "This must be overridden in extended block common_vseq")
+endfunction
+
 `undef create_tl_access_error_case
diff --git a/hw/dv/tools/dvsim/testplans/passthru_mem_intg_testplan.hjson b/hw/dv/tools/dvsim/testplans/passthru_mem_intg_testplan.hjson
new file mode 100644
index 0000000..fbe92f7
--- /dev/null
+++ b/hw/dv/tools/dvsim/testplans/passthru_mem_intg_testplan.hjson
@@ -0,0 +1,24 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+{
+  testpoints: [
+    {
+      name: passthru_mem_tl_intg_err
+      desc: '''Verify data integrity is stored in the passthru memory rather than generated after
+               a read.
+
+               - Randomly read a memory location and check the data integrity is correct.
+               - Backdoor inject fault into this location.
+               - Check the data integrity is incorrect but there is no d_error as the memory block
+                 should just pass the stored data and integrity to the processor where the
+                 integrity is compared.
+               - Above sequences will be run with `csr_rw_vseq` to ensure it won't affect CSR
+                 accesses.
+            '''
+      milestone: V2
+      tests: ["{name}_passthru_mem_tl_intg_err"]
+    }
+  ]
+}
+
diff --git a/hw/dv/tools/dvsim/tests/passthru_mem_intg_tests.hjson b/hw/dv/tools/dvsim/tests/passthru_mem_intg_tests.hjson
new file mode 100644
index 0000000..ec89813
--- /dev/null
+++ b/hw/dv/tools/dvsim/tests/passthru_mem_intg_tests.hjson
@@ -0,0 +1,20 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+{
+  build_modes: [
+    {
+      name: cover_reg_top
+    }
+  ]
+
+  tests: [
+    {
+      name: "{name}_passthru_mem_tl_intg_err"
+      build_mode: "cover_reg_top"
+      uvm_test_seq: "{name}_common_vseq"
+      run_opts: ["+run_passthru_mem_tl_intg_err"]
+      reseed: 20
+    }
+  ]
+}