[spi_device, dv] Refactor spi_agent to support spi_host rtl

  - Extend spi_if to provide 4 channels required by spi_host rtl
  - Replace uni-direction sdi and sdo ports by bi-direction sio ports in spi_if
  - Update spi_device and top_earlgrey testbench with new ports

Signed-off-by: Tung Hoang <hoang.tung@wdc.com>
diff --git a/hw/dv/sv/spi_agent/spi_agent_cfg.sv b/hw/dv/sv/spi_agent/spi_agent_cfg.sv
index e593d93..fbac367 100644
--- a/hw/dv/sv/spi_agent/spi_agent_cfg.sv
+++ b/hw/dv/sv/spi_agent/spi_agent_cfg.sv
@@ -4,19 +4,22 @@
 
 class spi_agent_cfg extends dv_base_agent_cfg;
 
-  // agent cfg knobs
-  bit             en_monitor_collect_trans = 1'b1; // enable monitor to collect trans on-the-fly
-  bit             en_monitor_checks        = 1'b1; // enable checkers in monitor
+  // enable checkers in monitor
+  bit en_monitor_checks = 1'b1;
 
   // host mode cfg knobs
-  time            sck_period_ps = 50_000; // 20MHz
-  bit             sck_polarity;       // aka CPOL
-  bit             sck_phase;          // aka CPHA
-  bit             host_bit_dir;       // 1 - lsb -> msb, 0 - msb -> lsb
-  bit             device_bit_dir;     // 1 - lsb -> msb, 0 - msb -> lsb
-  bit             sck_on;             // keep sck on
+  time sck_period_ps = 50_000; // 20MHz
+  bit sck_polarity;       // aka CPOL
+  bit sck_phase;          // aka CPHA
+  bit host_bit_dir;       // 1 - lsb -> msb, 0 - msb -> lsb
+  bit device_bit_dir;     // 1 - lsb -> msb, 0 - msb -> lsb
+  bit sck_on;             // keep sck on
+
+  // spi mode knob
+  spi_mode_e spi_mode;
+
   // how many bytes monitor samples per transaction
-  int             num_bytes_per_trans_in_mon = 4;
+  int num_bytes_per_trans_in_mon = 4;
 
   // enable randomly injecting extra delay between 2 sck/word
   bit  en_extra_dly_btw_sck;
@@ -31,20 +34,21 @@
   uint extra_dly_chance_pc_btw_word = 5;    // percentage of extra delay btw each word
 
   // interface handle used by driver, monitor & the sequencer
-  virtual spi_if  vif;
+  virtual spi_if vif;
 
   `uvm_object_utils_begin(spi_agent_cfg)
-    `uvm_field_int (sck_period_ps,    UVM_DEFAULT)
-    `uvm_field_int (sck_polarity,     UVM_DEFAULT)
-    `uvm_field_int (sck_phase,        UVM_DEFAULT)
-    `uvm_field_int (host_bit_dir,     UVM_DEFAULT)
-    `uvm_field_int (device_bit_dir,   UVM_DEFAULT)
+    `uvm_field_int (sck_period_ps,  UVM_DEFAULT)
+    `uvm_field_int (sck_polarity,   UVM_DEFAULT)
+    `uvm_field_int (sck_phase,      UVM_DEFAULT)
+    `uvm_field_int (host_bit_dir,   UVM_DEFAULT)
+    `uvm_field_int (device_bit_dir, UVM_DEFAULT)
     `uvm_field_int (en_extra_dly_btw_sck,         UVM_DEFAULT)
     `uvm_field_int (max_extra_dly_ns_btw_sck,     UVM_DEFAULT)
     `uvm_field_int (extra_dly_chance_pc_btw_sck,  UVM_DEFAULT)
     `uvm_field_int (en_extra_dly_btw_word,        UVM_DEFAULT)
     `uvm_field_int (max_extra_dly_ns_btw_word,    UVM_DEFAULT)
     `uvm_field_int (extra_dly_chance_pc_btw_word, UVM_DEFAULT)
+    `uvm_field_enum(spi_mode_e, spi_mode,         UVM_DEFAULT)
   `uvm_object_utils_end
 
   `uvm_object_new
@@ -72,4 +76,4 @@
     else              @(negedge vif.sck);
   endtask
 
-endclass
+endclass : spi_agent_cfg
diff --git a/hw/dv/sv/spi_agent/spi_agent_pkg.sv b/hw/dv/sv/spi_agent/spi_agent_pkg.sv
index f1bc2b4..7f34c99 100644
--- a/hw/dv/sv/spi_agent/spi_agent_pkg.sv
+++ b/hw/dv/sv/spi_agent/spi_agent_pkg.sv
@@ -27,6 +27,13 @@
     SamplingEdge
   } sck_edge_type_e;
 
+  // spi mode
+  typedef enum {
+    Single = 0,  // Full duplex, tx: sio[0], rx: sio[1]
+    Dual   = 1,  // Half duplex, tx and rx: sio[1:0]
+    Quad   = 2   // Half duplex, tx and rx: sio[3:0]
+  } spi_mode_e;
+
   // functions
 
   // package sources
diff --git a/hw/dv/sv/spi_agent/spi_device_driver.sv b/hw/dv/sv/spi_agent/spi_device_driver.sv
index f1684f5..48c1f27 100644
--- a/hw/dv/sv/spi_agent/spi_device_driver.sv
+++ b/hw/dv/sv/spi_agent/spi_device_driver.sv
@@ -10,7 +10,7 @@
     forever begin
       @(negedge cfg.vif.rst_n);
       under_reset = 1'b1;
-      cfg.vif.sdo <= 1'b0;
+      cfg.vif.sio <= 'hz;
       @(posedge cfg.vif.rst_n);
       under_reset = 1'b0;
     end
diff --git a/hw/dv/sv/spi_agent/spi_host_driver.sv b/hw/dv/sv/spi_agent/spi_host_driver.sv
index 6263214..ca618ce 100644
--- a/hw/dv/sv/spi_agent/spi_host_driver.sv
+++ b/hw/dv/sv/spi_agent/spi_host_driver.sv
@@ -21,8 +21,8 @@
       @(negedge cfg.vif.rst_n);
       under_reset = 1'b1;
       cfg.vif.sck <= cfg.sck_polarity;
-      cfg.vif.csb <= 1'b1;
-      cfg.vif.sdi <= 1'bx;
+      cfg.vif.csb <= 'hf;
+      cfg.vif.sio <= 'hz;
       sck_pulses = 0;
       @(posedge cfg.vif.rst_n);
       under_reset = 1'b0;
@@ -80,7 +80,7 @@
   endtask
 
   task drive_normal_item();
-    cfg.vif.csb <= 1'b0;
+    cfg.vif.csb[0] <= 1'b0;
     sck_pulses = req.data.size() * 8;
 
     // for mode 1 and 3, get the leading edges out of the way
@@ -93,13 +93,13 @@
       int       which_bit;
       host_byte = req.data[i];
       for (int j = 0; j < 8; j++) begin
-        // drive sdi early so that it is stable at the sampling edge
+        // drive sio early so that it is stable at the sampling edge
         which_bit = cfg.host_bit_dir ? j : 7 - j;
-        cfg.vif.sdi <= host_byte[which_bit];
-        // wait for sampling edge to sample sdo (half cycle)
+        cfg.vif.sio[0] <= host_byte[which_bit];
+        // wait for sampling edge to sample sio (half cycle)
         cfg.wait_sck_edge(SamplingEdge);
         which_bit = cfg.device_bit_dir ? j : 7 - j;
-        device_byte[which_bit] = cfg.vif.sdo;
+        device_byte[which_bit] = cfg.vif.sio[1];
         // wait for driving edge to complete 1 cycle
         if (i != req.data.size() - 1 || j != 7) cfg.wait_sck_edge(DrivingEdge);
       end
@@ -107,8 +107,8 @@
     end
 
     wait(sck_pulses == 0);
-    cfg.vif.csb <= 1'b1;
-    cfg.vif.sdi <= 1'bx;
+    cfg.vif.csb[0] <= 1'b1;
+    cfg.vif.sio[0] <= 1'bx;
   endtask
 
   task drive_sck_no_csb_item();
@@ -121,9 +121,9 @@
   endtask
 
   task drive_csb_no_sck_item();
-    cfg.vif.csb <= 1'b0;
+    cfg.vif.csb[0] <= 1'b0;
     #(req.dummy_sck_length_ns * 1ns);
-    cfg.vif.csb <= 1'b1;
+    cfg.vif.csb[0] <= 1'b1;
   endtask
 
   function uint get_rand_extra_delay_ns_btw_sck();
diff --git a/hw/dv/sv/spi_agent/spi_if.sv b/hw/dv/sv/spi_agent/spi_if.sv
index 06a23b4..562f011 100644
--- a/hw/dv/sv/spi_agent/spi_if.sv
+++ b/hw/dv/sv/spi_agent/spi_if.sv
@@ -2,13 +2,14 @@
 // Licensed under the Apache License, Version 2.0, see LICENSE for details.
 // SPDX-License-Identifier: Apache-2.0
 
-interface spi_if (input rst_n);
+interface spi_if (
+  input rst_n
+);
 
   // standard spi interface pins
-  logic sck;
-  logic csb;
-  logic sdo;
-  logic sdi;
+  logic       sck;
+  logic [3:0] csb;
+  logic [3:0] sio;
 
   // debug signals
   logic [7:0] host_byte;
@@ -19,4 +20,4 @@
   bit         sck_polarity;
   bit         sck_phase;
 
-endinterface
+endinterface : spi_if
diff --git a/hw/dv/sv/spi_agent/spi_monitor.sv b/hw/dv/sv/spi_agent/spi_monitor.sv
index 8d47573..81b6ab1 100644
--- a/hw/dv/sv/spi_agent/spi_monitor.sv
+++ b/hw/dv/sv/spi_agent/spi_monitor.sv
@@ -20,7 +20,7 @@
 
   function void build_phase(uvm_phase phase);
     super.build_phase(phase);
-    host_analysis_port = new("host_analysis_port", this);
+    host_analysis_port   = new("host_analysis_port", this);
     device_analysis_port = new("device_analysis_port", this);
   endfunction
 
@@ -35,43 +35,40 @@
 
     forever begin
       @(negedge cfg.vif.csb);
-      if (cfg.en_monitor_collect_trans) collect_curr_trans();
+      collect_curr_trans();
     end
-  endtask
+  endtask : collect_trans
 
   virtual protected task collect_curr_trans();
-
     fork
       begin: isolation_thread
         fork
           begin: csb_deassert_thread
-            wait(cfg.vif.csb == 1'b1);
+            wait(cfg.vif.csb[0] == 1'b1);
           end
           begin: sample_thread
             // for mode 1 and 3, get the leading edges out of the way
             cfg.wait_sck_edge(LeadingEdge);
             forever begin
-              bit [7:0] host_byte;    // from sdi
-              bit [7:0] device_byte;  // from sdo
+              bit [7:0] host_byte;    // from sio
+              bit [7:0] device_byte;  // from sio
               int       which_bit;
               for (int i = 0; i < 8; i++) begin
                 // wait for the sampling edge
                 cfg.wait_sck_edge(SamplingEdge);
-                // check sdi/sdo not x or z
+                // check sio not x or z
                 if (cfg.en_monitor_checks) begin
-                  `DV_CHECK_CASE_NE(cfg.vif.sdi, 1'bx)
-                  `DV_CHECK_CASE_NE(cfg.vif.sdi, 1'bz)
-                  `DV_CHECK_CASE_NE(cfg.vif.sdo, 1'bx)
-                  `DV_CHECK_CASE_NE(cfg.vif.sdo, 1'bz)
+                  `DV_CHECK_CASE_NE(cfg.vif.sio[1:0], 2'bxx)
+                  `DV_CHECK_CASE_NE(cfg.vif.sio[1:0], 2'bxx)
                 end
-                // sample sdi
+                // sample sio[0] for tx
                 which_bit = cfg.host_bit_dir ? i : 7 - i;
-                host_byte[which_bit] = cfg.vif.sdi;
-                cfg.vif.host_bit = which_bit;
+                host_byte[which_bit] = cfg.vif.sio[0];
+                cfg.vif.host_bit  = which_bit;
                 cfg.vif.host_byte = host_byte;
-                // sample sdo
+                // sample sio[1] for rx
                 which_bit = cfg.device_bit_dir ? i : 7 - i;
-                device_byte[which_bit] = cfg.vif.sdo;
+                device_byte[which_bit] = cfg.vif.sio[1];
                 cfg.vif.device_bit = which_bit;
                 cfg.vif.device_byte = device_byte;
               end
@@ -96,13 +93,13 @@
         disable fork;
       end
     join
-  endtask
+  endtask : collect_curr_trans
 
   virtual task monitor_ready_to_end();
     forever begin
       @(cfg.vif.csb);
       ok_to_end = !cfg.vif.csb;
     end
-  endtask
+  endtask : monitor_ready_to_end
 
-endclass
+endclass : spi_monitor
diff --git a/hw/dv/sv/spi_agent/spi_sequencer.sv b/hw/dv/sv/spi_agent/spi_sequencer.sv
index b835891..62017f8 100644
--- a/hw/dv/sv/spi_agent/spi_sequencer.sv
+++ b/hw/dv/sv/spi_agent/spi_sequencer.sv
@@ -7,4 +7,4 @@
 
   `uvm_component_new
 
-endclass
+endclass : spi_sequencer
diff --git a/hw/ip/spi_device/dv/env/seq_lib/spi_device_base_vseq.sv b/hw/ip/spi_device/dv/env/seq_lib/spi_device_base_vseq.sv
index 3a90ae3..a849fe5 100644
--- a/hw/ip/spi_device/dv/env/seq_lib/spi_device_base_vseq.sv
+++ b/hw/ip/spi_device/dv/env/seq_lib/spi_device_base_vseq.sv
@@ -105,8 +105,8 @@
   endtask
 
   // NOTE on terminology
-  // from spi_device IP perspective, tx is data sent out over sdo (device traffic from the IP),
-  // rx is data received over sdi (host traffic from SPI agent)
+  // from spi_device IP perspective, tx is data sent out over sio[0] (device traffic from the IP),
+  // rx is data received over sio[1] (host traffic from SPI agent)
 
   // TODO: use spi_device_pkg spi_mode enum instead
   virtual task spi_device_init();
diff --git a/hw/ip/spi_device/dv/env/seq_lib/spi_device_fifo_underflow_overflow_vseq.sv b/hw/ip/spi_device/dv/env/seq_lib/spi_device_fifo_underflow_overflow_vseq.sv
index 2c99f9d..a84f555 100644
--- a/hw/ip/spi_device/dv/env/seq_lib/spi_device_fifo_underflow_overflow_vseq.sv
+++ b/hw/ip/spi_device/dv/env/seq_lib/spi_device_fifo_underflow_overflow_vseq.sv
@@ -10,7 +10,7 @@
 
   virtual task body();
     allow_underflow_overflow = 1;
-    // when underflow, sdo may be unknown, disable checking it
+    // when underflow, sio may be unknown, disable checking it
     cfg.m_spi_agent_cfg.en_monitor_checks = 0;
     super.body();
     cfg.m_spi_agent_cfg.en_monitor_checks = 1;
diff --git a/hw/ip/spi_device/dv/tb/tb.sv b/hw/ip/spi_device/dv/tb/tb.sv
index 1503af3..302184d 100644
--- a/hw/ip/spi_device/dv/tb/tb.sv
+++ b/hw/ip/spi_device/dv/tb/tb.sv
@@ -33,10 +33,10 @@
 
   // interfaces
   clk_rst_if clk_rst_if(.clk(clk), .rst_n(rst_n));
+  tl_if tl_if(.clk(clk), .rst_n(rst_n));
   pins_if #(NUM_MAX_INTERRUPTS) intr_if(.pins(interrupts));
   pins_if #(1) devmode_if(devmode);
-  tl_if tl_if(.clk(clk), .rst_n(rst_n));
-  spi_if spi_if(.rst_n(rst_n));
+  spi_if  spi_if(.rst_n(rst_n));
 
   // dut
   spi_device dut (
@@ -61,11 +61,11 @@
     .scanmode_i     (lc_ctrl_pkg::Off)
   );
 
-  assign sck          = spi_if.sck;
-  assign csb          = spi_if.csb;
+  assign sck           = spi_if.sck;
+  assign csb           = spi_if.csb[0];
   // TODO: quad SPI mode is currently not yet implemented
-  assign sd_in        = {3'b000, spi_if.sdi};
-  assign spi_if.sdo   = sd_out_en[1] ? sd_out[1] : 1'bz;
+  assign sd_in         = {3'b000, spi_if.sio[0]};
+  assign spi_if.sio[1] = sd_out_en[1] ? sd_out[1] : 1'bz;
 
   assign interrupts[RxFifoFull]      = intr_rxf;
   assign interrupts[RxFifoGeLevel]   = intr_rxlvl;
diff --git a/hw/top_earlgrey/dv/tb/tb.sv b/hw/top_earlgrey/dv/tb/tb.sv
index 4c53732..0151b6e 100644
--- a/hw/top_earlgrey/dv/tb/tb.sv
+++ b/hw/top_earlgrey/dv/tb/tb.sv
@@ -180,8 +180,8 @@
 
   assign spi_device_sck     = spi_if.sck;
   assign spi_device_csb     = spi_if.csb;
-  assign spi_device_sdi_i   = spi_if.sdi;
-  assign spi_if.sdo         = spi_device_sdo_o;
+  assign spi_device_sdi_i   = spi_if.sio[0];
+  assign spi_if.sio[1]      = spi_device_sdo_o;
 
   // TODO: Replace this weak pull to a known value with initialization
   // in the agent/interface.