[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