[dv/common] add csr field access

Update csr_rd to support partial read (sub-word)
Update csr read/write to include field read
Update all IPs scbs to read csr registers address
diff --git a/hw/dv/sv/csr_utils/csr_seq_lib.sv b/hw/dv/sv/csr_utils/csr_seq_lib.sv
index 0160b6d..92764f1 100644
--- a/hw/dv/sv/csr_utils/csr_seq_lib.sv
+++ b/hw/dv/sv/csr_utils/csr_seq_lib.sv
@@ -205,10 +205,19 @@
 
   `uvm_object_new
 
+  rand bit do_csr_rd_check;
+  rand bit do_csr_field_rd_check;
+
+  constraint csr_or_field_rd_check_c {
+    // at least one of them should be set
+    do_csr_rd_check || do_csr_field_rd_check;
+  }
+
   virtual task body();
     foreach (test_csrs[i]) begin
       uvm_reg_data_t wdata;
       uvm_reg_data_t compare_mask;
+      uvm_reg_field  test_fields[$];
 
       // check if parent block or register is excluded from write
       if (m_csr_excl_item.is_excl(test_csrs[i], CsrExclWrite)) begin
@@ -220,6 +229,7 @@
       `uvm_info(`gtn, $sformatf("Verifying register read/write for %0s",
                                 test_csrs[i].get_full_name()), UVM_MEDIUM)
 
+      `DV_CHECK_FATAL(randomize(do_csr_rd_check, do_csr_field_rd_check))
       `DV_CHECK_STD_RANDOMIZE_FATAL(wdata)
       wdata &= get_mask_excl_fields(test_csrs[i], CsrExclWrite);
       csr_wr(.csr(test_csrs[i]), .value(wdata), .blocking(0));
@@ -239,11 +249,24 @@
       end
 
       compare_mask = get_mask_excl_fields(test_csrs[i], CsrExclWriteCheck);
-      csr_rd_check(.ptr           (test_csrs[i]),
-                   .blocking      (0),
-                   .compare       (!external_checker),
-                   .compare_vs_ral(1'b1),
-                   .compare_mask  (compare_mask));
+      if (do_csr_rd_check) begin
+        csr_rd_check(.ptr           (test_csrs[i]),
+                     .blocking      (0),
+                     .compare       (!external_checker),
+                     .compare_vs_ral(1'b1),
+                     .compare_mask  (compare_mask));
+      end
+      if (do_csr_field_rd_check) begin
+        test_csrs[i].get_fields(test_fields);
+        test_fields.shuffle();
+        foreach (test_fields[j]) begin
+          csr_rd_check(.ptr           (test_fields[j]),
+                       .blocking      (0),
+                       .compare       (!external_checker),
+                       .compare_vs_ral(1'b1),
+                       .compare_mask  (compare_mask));
+        end
+      end
     end
   endtask
 
diff --git a/hw/dv/sv/csr_utils/csr_utils_pkg.sv b/hw/dv/sv/csr_utils/csr_utils_pkg.sv
index 4c9021a..b5cb56e 100644
--- a/hw/dv/sv/csr_utils/csr_utils_pkg.sv
+++ b/hw/dv/sv/csr_utils/csr_utils_pkg.sv
@@ -256,9 +256,10 @@
           begin
             outstanding_csr_accesses++;
             csr_or_fld = decode_csr_or_field(ptr);
-            csr_or_fld.csr.read(.status(status), .value(value), .path(path), .map(map));
             if (csr_or_fld.field != null) begin
-              value = get_field_val(csr_or_fld.field, value);
+              csr_or_fld.field.read(.status(status), .value(value), .path(path), .map(map));
+            end else begin
+              csr_or_fld.csr.read(.status(status), .value(value), .path(path), .map(map));
             end
             if (check == UVM_CHECK) begin
               `DV_CHECK_EQ(status, UVM_IS_OK, "", error, msg_id)
diff --git a/hw/dv/sv/tl_agent/tl_reg_adapter.sv b/hw/dv/sv/tl_agent/tl_reg_adapter.sv
index 0bc0439..5712f86 100644
--- a/hw/dv/sv/tl_agent/tl_reg_adapter.sv
+++ b/hw/dv/sv/tl_agent/tl_reg_adapter.sv
@@ -19,13 +19,43 @@
     ITEM_T reg_item;
     reg_item = ITEM_T::type_id::create("reg_item");
     `DV_CHECK_RANDOMIZE_FATAL(reg_item)
-    reg_item.a_addr   = rw.addr;
-    reg_item.a_size   = 2; // 4 bytes
     reg_item.a_opcode = (rw.kind == UVM_WRITE) ? tlul_pkg::PutFullData : tlul_pkg::Get;
-    reg_item.a_mask   = rw.byte_en;
-    reg_item.a_data   = rw.data;
-    `uvm_info(`gtn, $sformatf("tl reg req item: addr=0x%0h, op=%0s data=0x%0h",
-                              reg_item.a_addr, reg_item.a_opcode.name, reg_item.a_data), UVM_HIGH)
+
+    // tlul support partial rd but follow protocol standards
+    // TODO: this code assumes TLUL data is always 32 bits wide, can explore more generic solution
+    if (reg_item.a_opcode == tlul_pkg::Get) begin
+      case ($countones(rw.byte_en))
+        3, 4: begin // no partial rd
+          reg_item.a_mask = 'hf;
+          reg_item.a_addr = rw.addr;
+        end
+        1: begin // partial rd 1 byte
+          reg_item.a_mask = rw.byte_en;
+          reg_item.a_addr = rw.addr + $clog2(rw.byte_en);
+        end
+        2: begin
+          if (rw.byte_en == 'b0110) begin // no partial rd
+            reg_item.a_mask = 'hf;
+            reg_item.a_addr = rw.addr;
+          end else begin // partial rd 2 bytes
+            reg_item.a_mask = rw.byte_en;
+            reg_item.a_addr = (rw.byte_en == 'b0011) ? rw.addr : rw.addr + 2;
+          end
+        end
+        default: begin
+          `uvm_fatal(`gtn, $sformatf("invalid byte_en value = 0x%0h", rw.byte_en));
+        end
+      endcase
+    end
+    else begin
+      reg_item.a_mask = rw.byte_en;
+      reg_item.a_addr = rw.addr;
+    end
+    reg_item.a_size = $clog2($countones(reg_item.a_mask));
+    reg_item.a_data = rw.data;
+    `uvm_info(`gtn, $sformatf("tl reg req item: addr=0x%0h, op=%0s data=0x%0h, mask = %0h",
+                              reg_item.a_addr, reg_item.a_opcode.name, reg_item.a_data,
+                              reg_item.a_mask), UVM_HIGH)
     return reg_item;
   endfunction : reg2bus
 
diff --git a/hw/ip/gpio/dv/env/gpio_scoreboard.sv b/hw/ip/gpio/dv/env/gpio_scoreboard.sv
index 0a505d0..3d98739 100644
--- a/hw/ip/gpio/dv/env/gpio_scoreboard.sv
+++ b/hw/ip/gpio/dv/env/gpio_scoreboard.sv
@@ -43,12 +43,13 @@
   // process monitored tl transaction
   virtual task process_tl_access(tl_seq_item item, tl_channels_e channel = DataChannel);
     uvm_reg csr;
-    bit do_read_check = 1'b1;
-    bit write = item.is_write();
+    bit do_read_check       = 1'b1;
+    bit write               = item.is_write();
+    uvm_reg_addr_t csr_addr = {item.a_addr[TL_AW-1:2], 2'b00};
 
     // if access was to a valid csr, get the csr handle
-    if (item.a_addr inside {cfg.csr_addrs}) begin
-      csr = ral.default_map.get_reg_by_offset(item.a_addr);
+    if (csr_addr inside {cfg.csr_addrs}) begin
+      csr = ral.default_map.get_reg_by_offset(csr_addr);
       `DV_CHECK_NE_FATAL(csr, null)
     end
     if (csr == null) begin
diff --git a/hw/ip/hmac/dv/env/hmac_scoreboard.sv b/hw/ip/hmac/dv/env/hmac_scoreboard.sv
index ca6c5fe..a95d8d0 100644
--- a/hw/ip/hmac/dv/env/hmac_scoreboard.sv
+++ b/hw/ip/hmac/dv/env/hmac_scoreboard.sv
@@ -25,20 +25,19 @@
 
   virtual task process_tl_access(tl_seq_item item, tl_channels_e channel = DataChannel);
     uvm_reg csr;
-    bit     do_read_check = 1'b1;
-    bit     write         = item.is_write();
+    bit     do_read_check   = 1'b1;
+    bit     write           = item.is_write();
+    uvm_reg_addr_t csr_addr = {item.a_addr[TL_AW-1:2], 2'b00};
 
     // if access was to a valid csr, get the csr handle
-    if (item.a_addr inside {cfg.csr_addrs}) begin
-      csr = ral.default_map.get_reg_by_offset(item.a_addr);
+    if (csr_addr inside {cfg.csr_addrs}) begin
+      csr = ral.default_map.get_reg_by_offset(csr_addr);
       `DV_CHECK_NE_FATAL(csr, null)
-    end
-    if (csr == null) begin
-      if (!(item.a_addr inside {[HMAC_MSG_FIFO_BASE : HMAC_MSG_FIFO_LAST_ADDR]})) begin
-        // we hit an oob addr - expect error response and return
-        `DV_CHECK_EQ(item.d_error, 1'b1)
-        return;
-      end
+    // if addr inside msg fifo, no ral model
+    end else if (!(item.a_addr inside {[HMAC_MSG_FIFO_BASE : HMAC_MSG_FIFO_LAST_ADDR]})) begin
+      // we hit an oob addr - expect error response and return
+      `DV_CHECK_EQ(item.d_error, 1'b1)
+      return;
     end
 
     // if incoming access is a write to a valid csr or mem, then update right away on addr channel
diff --git a/hw/ip/rv_timer/dv/env/rv_timer_scoreboard.sv b/hw/ip/rv_timer/dv/env/rv_timer_scoreboard.sv
index da44ed3..966b28a 100644
--- a/hw/ip/rv_timer/dv/env/rv_timer_scoreboard.sv
+++ b/hw/ip/rv_timer/dv/env/rv_timer_scoreboard.sv
@@ -35,12 +35,13 @@
   virtual task process_tl_access(tl_seq_item item, tl_channels_e channel = DataChannel);
     uvm_reg csr;
     string  csr_name;
-    bit     do_read_check = 1'b1;
-    bit     write = item.is_write();
+    bit     do_read_check   = 1'b1;
+    bit     write           = item.is_write();
+    uvm_reg_addr_t csr_addr = {item.a_addr[TL_AW-1:2], 2'b00};
 
     // if access was to a valid csr, get the csr handle
-    if (item.a_addr inside {cfg.csr_addrs}) begin
-      csr = ral.default_map.get_reg_by_offset(item.a_addr);
+    if (csr_addr inside {cfg.csr_addrs}) begin
+      csr = ral.default_map.get_reg_by_offset(csr_addr);
       `DV_CHECK_NE_FATAL(csr, null)
       csr_name = csr.get_name();
     end
diff --git a/hw/ip/spi_device/dv/env/spi_device_scoreboard.sv b/hw/ip/spi_device/dv/env/spi_device_scoreboard.sv
index 666b5c0..21b943e 100644
--- a/hw/ip/spi_device/dv/env/spi_device_scoreboard.sv
+++ b/hw/ip/spi_device/dv/env/spi_device_scoreboard.sv
@@ -67,13 +67,13 @@
   // this is already called in cip_base_scoreboard::process_tl_a/d_chan_fifo tasks
   virtual task process_tl_access(tl_seq_item item, tl_channels_e channel = DataChannel);
     uvm_reg csr;
-    bit     do_read_check = 1'b0; // TODO: fixme
-    bit     write = item.is_write();
-
+    bit     do_read_check   = 1'b0; // TODO: fixme
+    bit     write           = item.is_write();
+    uvm_reg_addr_t csr_addr = {item.a_addr[TL_AW-1:2], 2'b00};
 
     // if access was to a valid csr, get the csr handle
-    if (item.a_addr inside {cfg.csr_addrs}) begin
-      csr = ral.default_map.get_reg_by_offset(item.a_addr);
+    if (csr_addr inside {cfg.csr_addrs}) begin
+      csr = ral.default_map.get_reg_by_offset(csr_addr);
       `DV_CHECK_NE_FATAL(csr, null)
     end
     else if (item.a_addr inside {[cfg.sram_start_addr:cfg.sram_end_addr]}) begin
diff --git a/hw/ip/uart/dv/env/uart_scoreboard.sv b/hw/ip/uart/dv/env/uart_scoreboard.sv
index 2917521..d656429 100644
--- a/hw/ip/uart/dv/env/uart_scoreboard.sv
+++ b/hw/ip/uart/dv/env/uart_scoreboard.sv
@@ -128,12 +128,13 @@
 
   virtual task process_tl_access(tl_seq_item item, tl_channels_e channel = DataChannel);
     uvm_reg csr;
-    bit     do_read_check = 1'b1;
-    bit     write         = item.is_write();
+    bit     do_read_check   = 1'b1;
+    bit     write           = item.is_write();
+    uvm_reg_addr_t csr_addr = {item.a_addr[TL_AW-1:2], 2'b00};
 
     // if access was to a valid csr, get the csr handle
-    if (item.a_addr inside {cfg.csr_addrs}) begin
-      csr = ral.default_map.get_reg_by_offset(item.a_addr);
+    if (csr_addr inside {cfg.csr_addrs}) begin
+      csr = ral.default_map.get_reg_by_offset(csr_addr);
       `DV_CHECK_NE_FATAL(csr, null)
     end
     if (csr == null) begin
diff --git a/util/uvmdvgen/scoreboard.sv.tpl b/util/uvmdvgen/scoreboard.sv.tpl
index 3a2f9ab..68727c4 100644
--- a/util/uvmdvgen/scoreboard.sv.tpl
+++ b/util/uvmdvgen/scoreboard.sv.tpl
@@ -60,12 +60,13 @@
 
   virtual task process_tl_access(tl_seq_item item, tl_channels_e channel = DataChannel);
     uvm_reg csr;
-    bit     do_read_check = 1'b1;
-    bit     write         = item.is_write();
+    bit     do_read_check   = 1'b1;
+    bit     write           = item.is_write();
+    uvm_reg_addr_t csr_addr = {item.a_addr[TL_AW-1:2], 2'b00};
 
     // if access was to a valid csr, get the csr handle
-    if (item.a_addr inside {cfg.csr_addrs}) begin
-      csr = ral.default_map.get_reg_by_offset(item.a_addr);
+    if (csr_addr inside {cfg.csr_addrs}) begin
+      csr = ral.default_map.get_reg_by_offset(csr_addr);
       `DV_CHECK_NE_FATAL(csr, null)
     end
     if (csr == null) begin