[dv] Move mem checking to scb

1. Move mem checking from cip_base_vseq to scb and use mem_model
2. Use separate switches to enable/disable tl_err checking and mem
checking
3. Add mask support and compare function in mem_model

Signed-off-by: Weicai Yang <weicai@google.com>
diff --git a/hw/dv/sv/cip_lib/cip_base_env.sv b/hw/dv/sv/cip_lib/cip_base_env.sv
index 62412bf..035e374 100644
--- a/hw/dv/sv/cip_lib/cip_base_env.sv
+++ b/hw/dv/sv/cip_lib/cip_base_env.sv
@@ -57,10 +57,8 @@
 
   virtual function void connect_phase(uvm_phase phase);
     super.connect_phase(phase);
-    if (cfg.en_scb) begin
-      m_tl_agent.monitor.a_chan_port.connect(scoreboard.tl_a_chan_fifo.analysis_export);
-      m_tl_agent.monitor.d_chan_port.connect(scoreboard.tl_d_chan_fifo.analysis_export);
-    end
+    m_tl_agent.monitor.a_chan_port.connect(scoreboard.tl_a_chan_fifo.analysis_export);
+    m_tl_agent.monitor.d_chan_port.connect(scoreboard.tl_d_chan_fifo.analysis_export);
     if (cfg.is_active) begin
       virtual_sequencer.tl_sequencer_h = m_tl_agent.sequencer;
     end
diff --git a/hw/dv/sv/cip_lib/cip_base_pkg.sv b/hw/dv/sv/cip_lib/cip_base_pkg.sv
index 376df60..ccbff1f 100644
--- a/hw/dv/sv/cip_lib/cip_base_pkg.sv
+++ b/hw/dv/sv/cip_lib/cip_base_pkg.sv
@@ -12,6 +12,7 @@
   import dv_base_reg_pkg::*;
   import tl_agent_pkg::*;
   import alert_esc_agent_pkg::*;
+  import mem_model_pkg::*;
 
   // macro includes
   `include "uvm_macros.svh"
diff --git a/hw/dv/sv/cip_lib/cip_base_scoreboard.sv b/hw/dv/sv/cip_lib/cip_base_scoreboard.sv
index 77dded8..5463db9 100644
--- a/hw/dv/sv/cip_lib/cip_base_scoreboard.sv
+++ b/hw/dv/sv/cip_lib/cip_base_scoreboard.sv
@@ -12,19 +12,19 @@
   uvm_tlm_analysis_fifo #(tl_seq_item)  tl_a_chan_fifo;
   uvm_tlm_analysis_fifo #(tl_seq_item)  tl_d_chan_fifo;
 
+  mem_model exp_mem;
+
   `uvm_component_new
 
   virtual function void build_phase(uvm_phase phase);
     super.build_phase(phase);
     tl_a_chan_fifo = new("tl_a_chan_fifo", this);
     tl_d_chan_fifo = new("tl_d_chan_fifo", this);
+    exp_mem = mem_model::type_id::create("exp_mem", this);
   endfunction
 
   virtual task run_phase(uvm_phase phase);
     super.run_phase(phase);
-    // if en_scb is off at the beginning, below processes aren't enabled
-    // but monitor_reset in super.run_phase is always running
-    wait(cfg.en_scb);
     fork
       process_tl_a_chan_fifo();
       process_tl_d_chan_fifo();
@@ -35,8 +35,13 @@
     tl_seq_item item;
     forever begin
       tl_a_chan_fifo.get(item);
+
+      if (cfg.en_scb_tl_err_chk) begin
+        if (predict_tl_err(item, AddrChannel)) continue;
+      end
+      if (cfg.en_scb_mem_chk && item.is_write() && is_mem_addr(item)) process_mem_write(item);
+
       if (!cfg.en_scb) continue;
-      if (predict_tl_err(item, AddrChannel)) continue;
       process_tl_access(item, AddrChannel);
     end
   endtask
@@ -45,11 +50,16 @@
     tl_seq_item item;
     forever begin
       tl_d_chan_fifo.get(item);
-      if (!cfg.en_scb) continue;
       `uvm_info(`gfn, $sformatf("received tl d_chan item:\n%0s", item.sprint()), UVM_HIGH)
-      // check tl packet integrity
-      void'(item.is_ok());
-      if (predict_tl_err(item, DataChannel)) continue;
+
+      if (cfg.en_scb_tl_err_chk) begin
+        // check tl packet integrity
+        void'(item.is_ok());
+        if (predict_tl_err(item, DataChannel)) continue;
+      end
+      if (cfg.en_scb_mem_chk && !item.is_write() && is_mem_addr(item)) process_mem_read(item);
+
+      if (!cfg.en_scb) continue;
       process_tl_access(item, DataChannel);
     end
   endtask
@@ -59,6 +69,18 @@
     `uvm_fatal(`gfn, "this method is not supposed to be called directly!")
   endtask
 
+  virtual task process_mem_write(tl_seq_item item);
+    uvm_reg_addr_t addr = get_normalized_addr(item.a_addr);
+    if (!cfg.under_reset)  exp_mem.write(addr, item.a_data, item.a_mask);
+  endtask
+
+  virtual task process_mem_read(tl_seq_item item);
+    uvm_reg_addr_t addr = get_normalized_addr(item.a_addr);
+    if (!cfg.under_reset && get_mem_access_by_addr(ral, addr) == "RW") begin
+      exp_mem.compare(addr, item.d_data, item.a_mask);
+    end
+  endtask
+
   // only lsb addr is used, the other can be ignored, use this function to normalize the addr to
   // the format that RAL uses
   virtual function uvm_reg_addr_t get_normalized_addr(uvm_reg_addr_t addr);
@@ -73,6 +95,7 @@
         return 1;
       end
     end
+    return 0;
   endfunction
 
   // check if there is any tl error, return 1 in case of error or if it is an unmapped addr
diff --git a/hw/dv/sv/cip_lib/cip_base_vseq.sv b/hw/dv/sv/cip_lib/cip_base_vseq.sv
index ecf2708..c966f78 100644
--- a/hw/dv/sv/cip_lib/cip_base_vseq.sv
+++ b/hw/dv/sv/cip_lib/cip_base_vseq.sv
@@ -24,8 +24,7 @@
     bit [TL_DBW-1:0] mask;
   } addr_mask_t;
 
-  bit [TL_DW-1:0]  exp_mem[int];
-  addr_mask_t      mem_exist_addr_q[$];
+  addr_mask_t mem_exist_addr_q[$];
 
   // mem_ranges without base address
   addr_range_t     updated_mem_ranges[$];
@@ -43,7 +42,6 @@
   }
   `uvm_object_param_utils_begin(cip_base_vseq #(RAL_T, CFG_T, COV_T, VIRTUAL_SEQUENCER_T))
     `uvm_field_string(common_seq_type, UVM_DEFAULT)
-    `uvm_field_aa_int_int(exp_mem, UVM_DEFAULT)
     `uvm_field_queue_int(mem_exist_addr_q, UVM_DEFAULT)
   `uvm_object_utils_end
 
@@ -510,32 +508,19 @@
                 tl_access(.addr(addr), .write(1), .data(data), .mask(mask), .blocking(1));
 
                 if (!cfg.under_reset) begin
-                  bit [TL_DW-1:0]  data_mask;
                   addr[1:0] = 0;
-                  foreach (mask[i]) data_mask[i*8+:8] = {8{mask[i]}};
-                  exp_mem[addr] = (exp_mem[addr] & ~data_mask) | (data & data_mask);
                   mem_exist_addr_q.push_back(addr_mask_t'{addr, mask});
                 end
               end
             end
             // Randomly pick a previously written address for partial read.
-            exp_mem.size > 0: begin // read
+            mem_exist_addr_q.size() > 0: begin // read
               // get all the programmed addresses and randomly pick one
               addr_mask_t addr_mask = mem_exist_addr_q[$urandom_range(0, mem_exist_addr_q.size - 1)];
               addr = addr_mask.addr;
               if (get_mem_access_by_addr(ral, addr) != "WO") begin
                 mask = get_rand_contiguous_mask(addr_mask.mask);
                 tl_access(.addr(addr), .write(0), .data(data), .mask(mask), .blocking(1));
-
-                if (!cfg.under_reset) begin
-                  bit [TL_DW-1:0]  compare_mask;
-                  bit [TL_DW-1:0]  act_data, exp_data;
-                  // calculate compare_mask which is data width wide
-                  foreach (mask[i]) compare_mask[i*8+:8] = {8{mask[i]}};
-                  act_data = data & compare_mask;
-                  exp_data = exp_mem[addr] & compare_mask;
-                  `DV_CHECK_EQ(act_data, exp_data, $sformatf("addr 0x%0h read out mismatch", addr))
-                end
               end
             end
           endcase
diff --git a/hw/dv/sv/cip_lib/cip_lib.core b/hw/dv/sv/cip_lib/cip_lib.core
index 65aeb03..74d3dbd 100644
--- a/hw/dv/sv/cip_lib/cip_lib.core
+++ b/hw/dv/sv/cip_lib/cip_lib.core
@@ -12,6 +12,7 @@
       - lowrisc:dv:tl_agent
       - lowrisc:dv:alert_esc_agent
       - lowrisc:dv:dv_base_reg
+      - lowrisc:dv:mem_model
     files:
       - cip_base_pkg.sv
       - cip_base_env_cfg.sv: {is_include_file: true}
diff --git a/hw/dv/sv/dv_lib/dv_base_env_cfg.sv b/hw/dv/sv/dv_lib/dv_base_env_cfg.sv
index 0512e1e..24487ad 100644
--- a/hw/dv/sv/dv_lib/dv_base_env_cfg.sv
+++ b/hw/dv/sv/dv_lib/dv_base_env_cfg.sv
@@ -6,6 +6,8 @@
 
   bit                   is_active    = 1;
   bit                   en_scb       = 1; // can be changed at run-time
+  bit                   en_scb_tl_err_chk = 1;
+  bit                   en_scb_mem_chk    = 1;
   bit                   en_cov       = 1;
   bit                   has_ral      = 1;
   bit                   under_reset  = 0;
diff --git a/hw/dv/sv/dv_lib/dv_base_test.sv b/hw/dv/sv/dv_lib/dv_base_test.sv
index 069955b..5435b82 100644
--- a/hw/dv/sv/dv_lib/dv_base_test.sv
+++ b/hw/dv/sv/dv_lib/dv_base_test.sv
@@ -32,6 +32,8 @@
 
     // knob to en/dis scb (enabled by default)
     void'($value$plusargs("en_scb=%0b", cfg.en_scb));
+    void'($value$plusargs("en_scb_tl_err_chk=%0b", cfg.en_scb_tl_err_chk));
+    void'($value$plusargs("en_scb_mem_chk=%0b", cfg.en_scb_mem_chk));
     // knob to cfg all agents with zero delays
     void'($value$plusargs("zero_delays=%0b", cfg.zero_delays));
   endfunction : build_phase
diff --git a/hw/dv/sv/mem_model/mem_model.sv b/hw/dv/sv/mem_model/mem_model.sv
index 18005b1..ffce8a5 100644
--- a/hw/dv/sv/mem_model/mem_model.sv
+++ b/hw/dv/sv/mem_model/mem_model.sv
@@ -3,55 +3,83 @@
 // SPDX-License-Identifier: Apache-2.0
 
 class mem_model #(int AddrWidth = top_pkg::TL_AW,
-                 int DataWidth = top_pkg::TL_DW) extends uvm_object;
+                  int DataWidth = top_pkg::TL_DW,
+                  int MaskWidth = top_pkg::TL_DBW) extends uvm_object;
 
   typedef bit [AddrWidth-1:0] mem_addr_t;
   typedef bit [DataWidth-1:0] mem_data_t;
+  typedef bit [MaskWidth-1:0] mem_mask_t;
 
   bit [7:0] system_memory[mem_addr_t];
 
   `uvm_object_param_utils(mem_model#(AddrWidth, DataWidth))
 
-  function new(string name="");
-    super.new(name);
+  `uvm_object_new
+
+  function int get_written_bytes();
+    return system_memory.size();
   endfunction
 
   function bit [7:0] read_byte(mem_addr_t addr);
     bit [7:0] data;
     if (system_memory.exists(addr)) begin
       data = system_memory[addr];
-      `uvm_info(get_full_name(),
-                $sformatf("Read Mem  : Addr[0x%0h], Data[0x%0h]", addr, data), UVM_HIGH)
-    end
-    else begin
+      `uvm_info(`gfn, $sformatf("Read Mem  : Addr[0x%0h], Data[0x%0h]", addr, data), UVM_HIGH)
+    end else begin
       `DV_CHECK_STD_RANDOMIZE_FATAL(data)
-      `uvm_error(get_full_name(), $sformatf("read to uninitialzed addr 0x%0h", addr))
+      `uvm_error(`gfn, $sformatf("read to uninitialzed addr 0x%0h", addr))
     end
     return data;
   endfunction
 
   function void write_byte(mem_addr_t addr, bit [7:0] data);
-   `uvm_info(get_full_name(),
-             $sformatf("Write Mem : Addr[0x%0h], Data[0x%0h]", addr, data), UVM_HIGH)
+   `uvm_info(`gfn, $sformatf("Write Mem : Addr[0x%0h], Data[0x%0h]", addr, data), UVM_HIGH)
     system_memory[addr] = data;
   endfunction
 
-  function void write(input mem_addr_t addr, mem_data_t data);
+  function void compare_byte(mem_addr_t addr, bit [7:0] act_data);
+   `uvm_info(`gfn, $sformatf("Compare Mem : Addr[0x%0h], Act Data[0x%0h], Exp Data[0x%0h]",
+                             addr, act_data, system_memory[addr]), UVM_HIGH)
+    system_memory[addr] = act_data;
+    `DV_CHECK_EQ(act_data, system_memory[addr], $sformatf("addr 0x%0h read out mismatch", addr))
+  endfunction
+
+  function void write(input mem_addr_t addr, mem_data_t data, mem_mask_t mask = '1);
     bit [7:0] byte_data;
     for (int i = 0; i < DataWidth / 8; i++) begin
-      byte_data = data[7:0];
-      write_byte(addr + i, byte_data);
+      if (mask[0]) begin
+        byte_data = data[7:0];
+        write_byte(addr + i, byte_data);
+      end
       data = data >> 8;
+      mask = mask >> 1;
     end
   endfunction
 
-  function mem_data_t read(mem_addr_t addr);
+  function mem_data_t read(mem_addr_t addr, mem_mask_t mask = '1);
     mem_data_t data;
     for (int i = DataWidth / 8 - 1; i >= 0; i--) begin
       data = data << 8;
-      data[7:0] = read_byte(addr + i);
+      if (mask[MaskWidth - 1]) data[7:0] = read_byte(addr + i);
+      else                     data[7:0] = 0;
+      mask = mask << 1;
     end
     return data;
   endfunction
 
+  function void compare(mem_addr_t addr, mem_data_t act_data, mem_mask_t mask = '1);
+    bit [7:0] byte_data;
+    for (int i = 0; i < DataWidth / 8; i++) begin
+      byte_data = act_data[7:0];
+      if (mask[0]) begin
+        compare_byte(addr + i, byte_data);
+      end else begin
+        `DV_CHECK_EQ(byte_data, 0,
+                     $sformatf("addr 0x%0h masked data aren't 0, mask 0x%0h", addr, mask))
+      end
+      act_data = act_data>> 8;
+      mask = mask >> 1;
+    end
+  endfunction
+
 endclass