[top_earlgrey/dv] Add filtering variant of SPI passthrough test

Add variant of SPI passthrough test that incorporates the use of
filtered opcodes.

Signed-off-by: Alexander Williams <awill@google.com>
diff --git a/hw/top_earlgrey/dv/env/seq_lib/chip_sw_spi_passthrough_vseq.sv b/hw/top_earlgrey/dv/env/seq_lib/chip_sw_spi_passthrough_vseq.sv
index a4cb170..fe1169b 100644
--- a/hw/top_earlgrey/dv/env/seq_lib/chip_sw_spi_passthrough_vseq.sv
+++ b/hw/top_earlgrey/dv/env/seq_lib/chip_sw_spi_passthrough_vseq.sv
@@ -17,6 +17,8 @@
     33_000, // 30 MHz
     167_000 // 6 MHz
   };
+  // A bit map of command slots that will have passthrough filters enabled.
+  rand bit [spi_device_pkg::NumTotalCmdInfo-1:0] passthrough_filters;
 
   // Generate a random permutation of the following opcodes, and place them in
   // the test_opcodes queue:
@@ -47,6 +49,30 @@
       "Observed SPI transaction on spi_host1, which should be inactive"));
   endtask
 
+  task get_filtered_commands(output bit [255:0] filter_map);
+    filter_map = '0;
+    for (int i = 0; i < spi_device_pkg::NumTotalCmdInfo; i++) begin
+      uvm_object csr;
+      uvm_reg_data_t opcode;
+      case (spi_device_pkg::cmd_info_index_e'(i))
+        spi_device_pkg::CmdInfoEn4B:
+            csr = ral.spi_device.cmd_info_en4b.opcode;
+        spi_device_pkg::CmdInfoEx4B:
+            csr = ral.spi_device.cmd_info_ex4b.opcode;
+        spi_device_pkg::CmdInfoWrEn:
+            csr = ral.spi_device.cmd_info_wren.opcode;
+        spi_device_pkg::CmdInfoWrDi:
+            csr = ral.spi_device.cmd_info_wrdi.opcode;
+        default:
+            csr = ral.spi_device.cmd_info[i].opcode;
+      endcase
+      if (passthrough_filters[i]) begin
+        csr_rd(.ptr(csr), .value(opcode), .backdoor(1));
+        filter_map[opcode] = 1'b1;
+      end
+    end
+  endtask
+
   // Send the sequence of commands indicated by the `test_opcodes` member.
   // These commands must be registered with the host and device agents, as the
   // command info determines the actions takes for each opcode. Random data of
@@ -56,38 +82,60 @@
     spi_device_flash_seq m_spi_device_seq;
     spi_host_flash_seq m_spi_host_seq;
     spi_item host_rsp, device_rsp;
+    spi_item device_rsp_q[$];
     spi_agent_cfg agent_cfg = cfg.m_spi_host_agent_cfg;
+    bit [255:0] filter_map;
+    get_filtered_commands(filter_map);
 
-    while (test_opcodes.size() > 0) begin
-      bit [7:0] opcode = test_opcodes.pop_front();
-
-      `uvm_create_on(m_spi_device_seq, p_sequencer.spi_device_sequencer_hs[0]);
-      `uvm_create_on(m_spi_host_seq, p_sequencer.spi_host_sequencer_h);
-      // Prepare for specific opcode. The address_q is kept empty, which will
-      // trigger m_spi_host_seq to do the lookup and supply a random value.
-      `DV_CHECK_RANDOMIZE_WITH_FATAL(m_spi_host_seq,
-                                     opcode == local::opcode;
-                                     address_q.size() == 0;
-                                     payload_q.size() <= local::max_payload_size;
-                                     read_size == payload_q.size(););
+    fork begin : isolation_fork
+      // The device agent handles the incoming command, if it is not filtered.
       fork
-        begin
+        forever begin
+          `uvm_create_on(m_spi_device_seq, p_sequencer.spi_device_sequencer_hs[0]);
           `uvm_send(m_spi_device_seq);
+          device_rsp_q.push_back(m_spi_device_seq.rsp);
         end
-        begin
-          `uvm_send(m_spi_host_seq);
-        end
-      join
+      join_none
 
-      // Check that the command, address, and data sent matches on both sides.
-      host_rsp = m_spi_host_seq.rsp;
-      device_rsp = m_spi_device_seq.rsp;
-      `DV_CHECK_EQ(host_rsp.opcode, device_rsp.opcode);
-      `DV_CHECK_Q_EQ(host_rsp.address_q, device_rsp.address_q);
-      `DV_CHECK_EQ(host_rsp.dummy_cycles, device_rsp.dummy_cycles);
-      `DV_CHECK_EQ(host_rsp.num_lanes, device_rsp.num_lanes);
-      `DV_CHECK_Q_EQ(host_rsp.payload_q, device_rsp.payload_q);
-    end
+      while (test_opcodes.size() > 0) begin
+        bit [7:0] opcode = test_opcodes.pop_front();
+
+        `uvm_create_on(m_spi_host_seq, p_sequencer.spi_host_sequencer_h);
+        // Prepare for specific opcode. The address_q is kept empty, which will
+        // trigger m_spi_host_seq to do the lookup and supply a random value.
+        `DV_CHECK_RANDOMIZE_WITH_FATAL(m_spi_host_seq,
+                                       opcode == local::opcode;
+                                       address_q.size() == 0;
+                                       payload_q.size() <= local::max_payload_size;
+                                       read_size == payload_q.size(););
+        `uvm_send(m_spi_host_seq);
+        // Wait for a small delay to allow the device agent to push the response
+        // into the queue.
+        #1ps;
+        if (!filter_map[opcode]) begin
+          // Check that the command, address, and data sent matches on both sides.
+          `DV_CHECK_EQ(device_rsp_q.size(), 1);
+          host_rsp = m_spi_host_seq.rsp;
+          device_rsp = device_rsp_q.pop_front();
+          `DV_CHECK_EQ(host_rsp.opcode, device_rsp.opcode);
+          `DV_CHECK_Q_EQ(host_rsp.address_q, device_rsp.address_q);
+          `DV_CHECK_EQ(host_rsp.dummy_cycles, device_rsp.dummy_cycles);
+          `DV_CHECK_EQ(host_rsp.num_lanes, device_rsp.num_lanes);
+          `DV_CHECK_Q_EQ(host_rsp.payload_q, device_rsp.payload_q);
+        end else begin
+          `DV_CHECK_EQ(device_rsp_q.size(), 0);
+        end
+      end
+      disable fork;
+    end join
+  endtask
+
+  virtual task cpu_init();
+    bit [7:0] sw_filter_config[4];
+    super.cpu_init();
+    `DV_CHECK_MEMBER_RANDOMIZE_FATAL(passthrough_filters);
+    sw_filter_config = {<<byte{passthrough_filters}};
+    sw_symbol_backdoor_overwrite("kFilteredCommands", sw_filter_config);
   endtask
 
   virtual task body();
diff --git a/sw/device/tests/sim_dv/BUILD b/sw/device/tests/sim_dv/BUILD
index e5bf5b6..fd790ce 100644
--- a/sw/device/tests/sim_dv/BUILD
+++ b/sw/device/tests/sim_dv/BUILD
@@ -825,6 +825,7 @@
     deps = [
         "//hw/top_earlgrey/sw/autogen:top_earlgrey",
         "//sw/device/lib/arch:device",
+        "//sw/device/lib/base:bitfield",
         "//sw/device/lib/base:mmio",
         "//sw/device/lib/dif:pinmux",
         "//sw/device/lib/dif:spi_device",
diff --git a/sw/device/tests/sim_dv/spi_passthrough_test.c b/sw/device/tests/sim_dv/spi_passthrough_test.c
index c5c66c3..fcaa45e 100644
--- a/sw/device/tests/sim_dv/spi_passthrough_test.c
+++ b/sw/device/tests/sim_dv/spi_passthrough_test.c
@@ -6,6 +6,7 @@
 #include <stdbool.h>
 
 #include "sw/device/lib/arch/device.h"
+#include "sw/device/lib/base/bitfield.h"
 #include "sw/device/lib/base/mmio.h"
 #include "sw/device/lib/dif/dif_pinmux.h"
 #include "sw/device/lib/dif/dif_spi_device.h"
@@ -21,6 +22,10 @@
 
 OTTF_DEFINE_TEST_CONFIG();
 
+// Bit map of command slots to be filtered. This is supplied by the DV
+// environment.
+const volatile uint32_t kFilteredCommands;
+
 static dif_pinmux_t pinmux;
 static dif_spi_device_handle_t spi_device;
 static dif_spi_host_t spi_host0;
@@ -219,10 +224,11 @@
   CHECK_DIF_OK(dif_spi_device_set_passthrough_intercept_config(
       &spi_device, intercept_config));
 
-  // Set up passthrough filter to allow all commands.
+  // Set up passthrough filter to allow all commands, initially.
   CHECK_DIF_OK(dif_spi_device_set_all_passthrough_command_filters(
       &spi_device, kDifToggleDisabled));
 
+  uint32_t filters = kFilteredCommands;
   dif_spi_device_flash_command_t read_commands[] = {
       {
           // Slot 0: ReadStatus1
@@ -303,6 +309,10 @@
   };
   for (int i = 0; i < ARRAYSIZE(read_commands); ++i) {
     uint8_t slot = i + kSpiDeviceReadCommandSlotBase;
+    if (bitfield_bit32_read(filters, slot)) {
+      CHECK_DIF_OK(dif_spi_device_set_passthrough_command_filter(
+          &spi_device, read_commands[i].opcode, kDifToggleEnabled));
+    }
     CHECK_DIF_OK(dif_spi_device_set_flash_command_slot(
         &spi_device, slot, kDifToggleEnabled, read_commands[i]));
   }
@@ -350,6 +360,10 @@
   };
   for (int i = 0; i < ARRAYSIZE(write_commands); ++i) {
     uint8_t slot = i + kSpiDeviceWriteCommandSlotBase;
+    if (bitfield_bit32_read(filters, slot)) {
+      CHECK_DIF_OK(dif_spi_device_set_passthrough_command_filter(
+          &spi_device, write_commands[i].opcode, kDifToggleEnabled));
+    }
     CHECK_DIF_OK(dif_spi_device_set_flash_command_slot(
         &spi_device, slot, kDifToggleEnabled, write_commands[i]));
   }