diff --git a/testbench/uvm/mem/.gitignore b/testbench/uvm/mem/.gitignore new file mode 100644 index 00000000000..f2bb14d0747 --- /dev/null +++ b/testbench/uvm/mem/.gitignore @@ -0,0 +1,5 @@ +*-sim +snapshots +*.vcd +*.fst +uvm diff --git a/testbench/uvm/mem/Makefile b/testbench/uvm/mem/Makefile new file mode 100644 index 00000000000..e95b94d1960 --- /dev/null +++ b/testbench/uvm/mem/Makefile @@ -0,0 +1,110 @@ +all: simulate + +# ------------------------------------- +# Testbench setup +# ------------------------------------- +# Check for RV_ROOT +ifeq (,$(wildcard ${RV_ROOT}/configs/veer.config)) +$(error env var RV_ROOT does not point to a valid dir! Exiting!) +endif + +VERILATOR ?= verilator +ifdef VERILATOR_ROOT +VERILATOR := $(VERILATOR_ROOT)/bin/verilator +endif + +# Check for Verilator +ifeq ($(shell which $(VERILATOR)),) +$(error Verilator not found! Exiting!) +endif + +# Check Verilator version +VERILATOR_VERSION := $(shell $(VERILATOR) --version) +VERILATOR_RECOMMENDED_COMMIT := df36e9ca2 +ifeq ($(findstring $(VERILATOR_RECOMMENDED_COMMIT),$(VERILATOR_VERSION)),) +$(warning Using $(VERILATOR_VERSION)) +$(warning It is recommended to use Verilator revision: https://github.com/antmicro/verilator-1/commit/$(VERILATOR_RECOMMENDED_COMMIT)) +endif + +UVM_ROOT ?= uvm +UVM_TEST ?= mem_wr_rd_test +SNAPSHOT = verilator-uvm + +VERILOG_DEFINE_FILES = \ + ${UVM_ROOT}/src/uvm.sv \ + ${RV_ROOT}/snapshots/$(SNAPSHOT)/common_defines.vh \ + ${RV_ROOT}/design/include/el2_def.sv \ + ${RV_ROOT}/snapshots/$(SNAPSHOT)/el2_pdef.vh + +VERILOG_INCLUDE_DIRS = \ + hdl \ + ${UVM_ROOT}/src \ + ${RV_ROOT}/design/include \ + ${RV_ROOT}/snapshots/$(SNAPSHOT) + +VERILOG_SOURCES = \ + ${RV_ROOT}/design/lib/beh_lib.sv \ + ${RV_ROOT}/design/lib/mem_lib.sv \ + $(SIM_DIR)/el2_lsu_dccm_mem.sv \ + hdl/tbench_top.sv + +# ------------------------------------- +# Compilation/simulation configuration +# ------------------------------------- +SIM_NAME ?= mem_tb +SIM_DIR := $(SIM_NAME)-sim +COMPILE_ARGS += --top-module tbench_top +COMPILE_ARGS += -DUVM_NO_DPI +COMPILE_ARGS += --prefix $(SIM_NAME) -o $(SIM_NAME) +COMPILE_ARGS += $(addprefix +incdir+, $(VERILOG_INCLUDE_DIRS)) +EXTRA_ARGS += --timescale 1ns/1ps --error-limit 100 --trace --trace-structs +WARNING_ARGS += -Wno-lint \ + -Wno-style \ + -Wno-SYMRSVDWORD \ + -Wno-IGNOREDRETURN \ + -Wno-CONSTRAINTIGN \ + -Wno-ZERODLY + +# ------------------------------------- +# Fetch UVM +# ------------------------------------- +$(UVM_ROOT): + git clone https://github.com/antmicro/uvm-verilator -b current-patches $(UVM_ROOT) + +# ------------------------------------- +# Configure VeeR EL-2 +# ------------------------------------- +$(RV_ROOT)/snapshots/$(SNAPSHOT)/el2_param.vh: + $(RV_ROOT)/configs/veer.config -snapshot=$(SNAPSHOT) -fpga_optimize=0 + +# FIXME: Patch source to disable "ifdef VERILATOR". Can't undef it as it has to be set for UVM sources. +$(SIM_DIR)/el2_lsu_dccm_mem.sv: + mkdir -p $(SIM_DIR) + sed 's/ifdef VERILATOR/ifdef XXXX/g' $(RV_ROOT)/design/lsu/el2_lsu_dccm_mem.sv > $(SIM_DIR)/el2_lsu_dccm_mem.sv + +# ------------------------------------- +# Make UVM test with Verilator +# ------------------------------------- +$(SIM_DIR)/$(SIM_NAME).mk: ${UVM_ROOT} $(RV_ROOT)/snapshots/$(SNAPSHOT)/el2_param.vh $(VERILOG_SOURCES) $(wildcard hdl/*.sv) + $(VERILATOR) --cc --exe --main --timing -Mdir $(SIM_DIR) \ + ${COMPILE_ARGS} ${EXTRA_ARGS} \ + ${VERILOG_DEFINE_FILES} \ + ${VERILOG_SOURCES} \ + ${WARNING_ARGS} + +$(SIM_DIR)/$(SIM_NAME): $(SIM_DIR)/$(SIM_NAME).mk + $(MAKE) -C $(SIM_DIR) $(BUILD_ARGS) -f $(SIM_NAME).mk + +simulate: build + $(SIM_DIR)/$(SIM_NAME) +UVM_TESTNAME=$(UVM_TEST) + +clean: + rm -rf snapshots + rm -rf simv*.daidir csrc + rm -rf csrc* simv* + rm -rf *.vcd *.fst + rm -rf $(SIM_DIR) + rm -rf $(UVM_DIR) + + +.PHONY: simulate clean diff --git a/testbench/uvm/mem/hdl/dccm_agent.sv b/testbench/uvm/mem/hdl/dccm_agent.sv new file mode 100644 index 00000000000..7ebabad6a20 --- /dev/null +++ b/testbench/uvm/mem/hdl/dccm_agent.sv @@ -0,0 +1,36 @@ +`include "dccm_transaction_sequence_item.sv" +`include "dccm_sequencer.sv" +`include "dccm_sequence.sv" +`include "dccm_driver.sv" +`include "dccm_monitor.sv" + +class dccm_agent extends uvm_agent; + + dccm_driver driver; + dccm_sequencer sequencer; + dccm_monitor monitor; + + `uvm_component_utils(dccm_agent) + + function new(string name, uvm_component parent); + super.new(name, parent); + endfunction : new + + function void build_phase(uvm_phase phase); + super.build_phase(phase); + + monitor = dccm_monitor::type_id::create("monitor", this); + + if (get_is_active() == UVM_ACTIVE) begin + driver = dccm_driver::type_id::create("driver", this); + sequencer = dccm_sequencer::type_id::create("sequencer", this); + end + endfunction : build_phase + + function void connect_phase(uvm_phase phase); + if (get_is_active() == UVM_ACTIVE) begin + driver.seq_item_port.connect(sequencer.seq_item_export); + end + endfunction : connect_phase + +endclass : dccm_agent diff --git a/testbench/uvm/mem/hdl/dccm_base_test.sv b/testbench/uvm/mem/hdl/dccm_base_test.sv new file mode 100644 index 00000000000..933d17b8d2e --- /dev/null +++ b/testbench/uvm/mem/hdl/dccm_base_test.sv @@ -0,0 +1,40 @@ +`include "dccm_agent.sv" +`include "dccm_scoreboard.sv" +class mem_model_base_test extends uvm_test; + + `uvm_component_utils(mem_model_base_test) + + dccm_agent agent; + dccm_scoreboard scoreboard; + + function new(string name = "mem_model_base_test", uvm_component parent = null); + super.new(name, parent); + endfunction : new + + virtual function void build_phase(uvm_phase phase); + super.build_phase(phase); + + agent = dccm_agent::type_id::create("agent", this); + scoreboard = dccm_scoreboard::type_id::create("scoreboard", this); + endfunction : build_phase + + function void connect_phase(uvm_phase phase); + agent.monitor.transaction_analisys_port.connect(scoreboard.item_collected_export); + endfunction : connect_phase + + function void report_phase(uvm_phase phase); + uvm_report_server svr; + int errors; + super.report_phase(phase); + + svr = uvm_report_server::get_server(); + errors = svr.get_severity_count(UVM_FATAL) + svr.get_severity_count(UVM_ERROR); + if (errors > 0) begin + `uvm_info(get_type_name(), "DCCM TEST FAILED!!", UVM_NONE) + `uvm_info(get_type_name(), $sformatf("Found %d errors", errors), UVM_NONE) + end else begin + `uvm_info(get_type_name(), "DCCM TEST PASSED!!", UVM_NONE) + end + endfunction + +endclass : mem_model_base_test diff --git a/testbench/uvm/mem/hdl/dccm_driver.sv b/testbench/uvm/mem/hdl/dccm_driver.sv new file mode 100644 index 00000000000..20e9a5bef51 --- /dev/null +++ b/testbench/uvm/mem/hdl/dccm_driver.sv @@ -0,0 +1,46 @@ +class dccm_driver extends uvm_driver #(dccm_transaction_sequence_item); + + virtual dccm_interface memory_vif; + `uvm_component_utils(dccm_driver) + + function new(string name, uvm_component parent); + super.new(name, parent); + endfunction : new + + function void build_phase(uvm_phase phase); + super.build_phase(phase); + if (!uvm_config_db#(virtual dccm_interface)::get(this, "", "memory_vif", memory_vif)) + `uvm_fatal("NO_VIF", {"virtual interface must be set for: ", get_full_name(), ".memory_vif"}); + endfunction : build_phase + + virtual task run_phase(uvm_phase phase); + forever begin + seq_item_port.get_next_item(req); + drive(); + seq_item_port.item_done(); + end + endtask : run_phase + + virtual task drive(); + memory_vif.wr_en <= 0; + memory_vif.rd_en <= 0; + @(posedge memory_vif.clk); + + memory_vif.addr <= req.addr; + + if (req.wr_en) begin // write operation + `uvm_info(get_type_name(), $sformatf("WR: 0x%08X <= 0x%08X", req.addr, req.wdata), UVM_LOW) + memory_vif.wr_en <= 1'b1; //req.wr_en; + memory_vif.wdata <= req.wdata; + @(posedge memory_vif.clk); + end else if (req.rd_en) begin //read operation + memory_vif.rd_en <= 1'b1; //req.rd_en; + @(posedge memory_vif.clk); + memory_vif.rd_en <= 0; + @(posedge memory_vif.clk); + req.rdata = memory_vif.rdata; + `uvm_info(get_type_name(), $sformatf("RD: 0x%08X => 0x%08X", req.addr, req.rdata), UVM_LOW) + end + + endtask : drive +endclass : dccm_driver diff --git a/testbench/uvm/mem/hdl/dccm_interface.sv b/testbench/uvm/mem/hdl/dccm_interface.sv new file mode 100644 index 00000000000..59b36b45d36 --- /dev/null +++ b/testbench/uvm/mem/hdl/dccm_interface.sv @@ -0,0 +1,15 @@ +interface dccm_interface ( + input logic clk, + reset +); + + `include "el2_param.vh" + ; + + logic [pt.DCCM_BITS-1:0] addr; + logic wr_en; + logic rd_en; + logic [pt.DCCM_FDATA_WIDTH-1:0] wdata; + logic [pt.DCCM_FDATA_WIDTH-1:0] rdata; + +endinterface diff --git a/testbench/uvm/mem/hdl/dccm_memtest.sv b/testbench/uvm/mem/hdl/dccm_memtest.sv new file mode 100644 index 00000000000..a735159679e --- /dev/null +++ b/testbench/uvm/mem/hdl/dccm_memtest.sv @@ -0,0 +1,27 @@ +class mem_wr_rd_test extends mem_model_base_test; + + `uvm_component_utils(mem_wr_rd_test) + + dccm_memtest_sequence memtest; + + function new(string name = "mem_wr_rd_test", uvm_component parent = null); + super.new(name, parent); + endfunction : new + + virtual function void build_phase(uvm_phase phase); + super.build_phase(phase); + + memtest = dccm_memtest_sequence::type_id::create(); + // Run the memtest 10 times + memtest.loops = 10; + endfunction : build_phase + + task run_phase(uvm_phase phase); + + phase.raise_objection(this); + memtest.start(agent.sequencer); + phase.drop_objection(this); + + endtask : run_phase + +endclass : mem_wr_rd_test diff --git a/testbench/uvm/mem/hdl/dccm_monitor.sv b/testbench/uvm/mem/hdl/dccm_monitor.sv new file mode 100644 index 00000000000..6d88e4c3d0f --- /dev/null +++ b/testbench/uvm/mem/hdl/dccm_monitor.sv @@ -0,0 +1,45 @@ +class dccm_monitor extends uvm_monitor; + + virtual dccm_interface memory_vif; + uvm_analysis_port #(dccm_transaction_sequence_item) transaction_analisys_port; + dccm_transaction_sequence_item transaction; + + `uvm_component_utils(dccm_monitor) + + function new(string name, uvm_component parent); + super.new(name, parent); + transaction = new(); + transaction_analisys_port = new("trensaction_analisys_port", this); + endfunction : new + + function void build_phase(uvm_phase phase); + super.build_phase(phase); + if (!uvm_config_db#(virtual dccm_interface)::get(this, "", "memory_vif", memory_vif)) + `uvm_fatal("NOVIF", {"virtual interface must be set for: ", get_full_name(), ".memory_vif"}); + endfunction : build_phase + + virtual task run_phase(uvm_phase phase); + forever begin + @(posedge memory_vif.clk); + wait (memory_vif.wr_en || memory_vif.rd_en); + // store control signals and address + transaction.addr = memory_vif.addr; + transaction.wdata = memory_vif.wdata; + transaction.rd_en = memory_vif.rd_en; + if (memory_vif.wr_en) begin + // store write data + transaction.wr_en = memory_vif.wr_en; + @(posedge memory_vif.clk); + end + if (memory_vif.rd_en) begin + // it takes 2 clocks to get the data on the output port + @(posedge memory_vif.clk); + @(posedge memory_vif.clk); + // store read data + transaction.rdata = memory_vif.rdata; + end + transaction_analisys_port.write(transaction); + end + endtask : run_phase + +endclass : dccm_monitor diff --git a/testbench/uvm/mem/hdl/dccm_scoreboard.sv b/testbench/uvm/mem/hdl/dccm_scoreboard.sv new file mode 100644 index 00000000000..58487e11c5b --- /dev/null +++ b/testbench/uvm/mem/hdl/dccm_scoreboard.sv @@ -0,0 +1,57 @@ +class dccm_scoreboard extends uvm_scoreboard; + + `include "el2_param.vh" + ; + + dccm_transaction_sequence_item data_queue[$]; + bit [pt.DCCM_FDATA_WIDTH-1:0] sc_mem; + + uvm_analysis_imp #(dccm_transaction_sequence_item, dccm_scoreboard) item_collected_export; + `uvm_component_utils(dccm_scoreboard) + + function new(string name, uvm_component parent); + super.new(name, parent); + endfunction : new + + function void build_phase(uvm_phase phase); + super.build_phase(phase); + item_collected_export = new("item_collected_export", this); + sc_mem = {(pt.DCCM_FDATA_WIDTH) {1'b1}}; + endfunction : build_phase + + virtual function void write(dccm_transaction_sequence_item pkt); + pkt.print(); + data_queue.push_back(pkt); + endfunction : write + + virtual task run_phase(uvm_phase phase); + dccm_transaction_sequence_item mem_pkt; + + forever begin + wait (data_queue.size() > 0); + mem_pkt = data_queue.pop_front(); + + if (mem_pkt.wr_en) begin + sc_mem = mem_pkt.wdata; + `uvm_info(get_type_name(), $sformatf("------ :: WRITE DATA :: ------"), UVM_LOW) + `uvm_info(get_type_name(), $sformatf("Addr: %0h", mem_pkt.addr), UVM_LOW) + `uvm_info(get_type_name(), $sformatf("Data: %0h", mem_pkt.wdata), UVM_LOW) + `uvm_info(get_type_name(), "------------------------------------", UVM_LOW) + end else if (mem_pkt.rd_en) begin + if (sc_mem == mem_pkt.rdata) begin + `uvm_info(get_type_name(), $sformatf("------ :: READ DATA Match :: ------"), UVM_LOW) + `uvm_info(get_type_name(), $sformatf("Addr: %0h", mem_pkt.addr), UVM_LOW) + `uvm_info(get_type_name(), $sformatf("Expected Data: %0h Actual Data: %0h", + sc_mem, mem_pkt.rdata), UVM_LOW) + `uvm_info(get_type_name(), "------------------------------------", UVM_LOW) + end else begin + `uvm_error(get_type_name(), "------ :: READ DATA Mismatch :: ------") + `uvm_error(get_type_name(), $sformatf("Addr: %0h", mem_pkt.addr)) + `uvm_error(get_type_name(), $sformatf( + "Expected Data: %0h Actual Data: %0h", sc_mem, mem_pkt.rdata)) + `uvm_error(get_type_name(), "------------------------------------") + end + end + end + endtask : run_phase +endclass : dccm_scoreboard diff --git a/testbench/uvm/mem/hdl/dccm_sequence.sv b/testbench/uvm/mem/hdl/dccm_sequence.sv new file mode 100644 index 00000000000..edd6c8283fc --- /dev/null +++ b/testbench/uvm/mem/hdl/dccm_sequence.sv @@ -0,0 +1,48 @@ +// this sequence wites random random data to random address, then reads it back +class dccm_write_read_sequence extends uvm_sequence #(dccm_transaction_sequence_item); + + `uvm_object_utils(dccm_write_read_sequence) + + function new(string name = "write_read_sequence"); + super.new(name); + endfunction + + virtual task body(); + + // Do randomized write + req = dccm_transaction_sequence_item::type_id::create("req"); + wait_for_grant(); + req.randomize(); + req.wr_en = 1'b1; + req.rd_en = 1'b0; + send_request(req); + wait_for_item_done(); + + // Do read from the same address + wait_for_grant(); + req.wr_en = 1'b0; + req.rd_en = 1'b1; + send_request(req); + wait_for_item_done(); + + endtask +endclass + +class dccm_memtest_sequence extends uvm_sequence #(dccm_transaction_sequence_item); + + dccm_write_read_sequence seq; + int loops; + + `uvm_object_utils(dccm_memtest_sequence) + + function new(string name = "dccm_memtest_sequence"); + super.new(name); + endfunction + + virtual task body(); + repeat (loops) begin + `uvm_do(seq) + end + endtask +endclass +//========================================================================= diff --git a/testbench/uvm/mem/hdl/dccm_sequencer.sv b/testbench/uvm/mem/hdl/dccm_sequencer.sv new file mode 100644 index 00000000000..0410768152f --- /dev/null +++ b/testbench/uvm/mem/hdl/dccm_sequencer.sv @@ -0,0 +1,19 @@ +// FIXME: +// In Verilator, user should always use: +// uvm_sequencer#(item, item); +// instead standard (other simulators): +// uvm_sequencer#(item); + +`ifdef VERILATOR +class dccm_sequencer extends uvm_sequencer #(dccm_transaction_sequence_item, dccm_transaction_sequence_item); +`else +class dccm_sequencer extends uvm_sequencer #(dccm_transaction_sequence_item); +`endif + + `uvm_component_utils(dccm_sequencer) + + function new(string name, uvm_component parent); + super.new(name, parent); + endfunction + +endclass diff --git a/testbench/uvm/mem/hdl/dccm_transaction_sequence_item.sv b/testbench/uvm/mem/hdl/dccm_transaction_sequence_item.sv new file mode 100644 index 00000000000..15268c66845 --- /dev/null +++ b/testbench/uvm/mem/hdl/dccm_transaction_sequence_item.sv @@ -0,0 +1,24 @@ +class dccm_transaction_sequence_item extends uvm_sequence_item; + + `include "el2_param.vh" + ; + + rand bit [ pt.DCCM_BITS-1:0] addr; + rand bit [pt.DCCM_FDATA_WIDTH-1:0] wdata; + bit [pt.DCCM_FDATA_WIDTH-1:0] rdata; + // we want to manually control wr_en anf rd_en in the test + bit wr_en; + bit rd_en; + + `uvm_object_utils_begin(dccm_transaction_sequence_item) + `uvm_field_int(addr, UVM_ALL_ON) + `uvm_field_int(wr_en, UVM_ALL_ON) + `uvm_field_int(rd_en, UVM_ALL_ON) + `uvm_field_int(wdata, UVM_ALL_ON) + `uvm_object_utils_end + + function new(string name = "dccm_transation_sequence_item"); + super.new(name); + endfunction + +endclass diff --git a/testbench/uvm/mem/hdl/tbench_top.sv b/testbench/uvm/mem/hdl/tbench_top.sv new file mode 100644 index 00000000000..53dfdd92e0a --- /dev/null +++ b/testbench/uvm/mem/hdl/tbench_top.sv @@ -0,0 +1,69 @@ +`include "uvm_pkg.sv" + +import uvm_pkg::*; + +`include "dccm_interface.sv" +`include "dccm_base_test.sv" +`include "dccm_memtest.sv" + +module tbench_top; + + bit clk; + bit reset; + + always #5 clk = ~clk; + + initial begin + reset = 1; + #5 reset = 0; + end + + initial begin + $dumpfile("dump.vcd"); + $dumpvars(0, tbench_top); + end + + dccm_interface intf ( + clk, + reset + ); + + el2_lsu_dccm_mem DUT ( + .clk (intf.clk), + .active_clk (intf.clk), + .rst_l (~intf.reset), + .clk_override(1'b0), + .scan_mode (1'b0), + + .dccm_wren (intf.wr_en), + .dccm_rden (intf.rd_en), + .dccm_wr_addr_hi(intf.addr), + .dccm_wr_addr_lo(intf.addr), + .dccm_rd_addr_hi(intf.addr), + .dccm_rd_addr_lo(intf.addr), + .dccm_wr_data_hi(intf.wdata), + .dccm_wr_data_lo(intf.wdata), + .dccm_rd_data_hi(), // For aligned read/write this should match the lo part + .dccm_rd_data_lo(intf.rdata) + ); + + // Debug, dump memory signals on each clock cycle + always @(posedge intf.clk) + $display( + "a:%08X w:%d wd:%08X r:%d rd:%08X", + intf.addr, + intf.wr_en, + intf.wdata, + intf.rd_en, + intf.rdata + ); + + initial begin + uvm_config_db#(virtual dccm_interface)::set(uvm_root::get(), "*", "memory_vif", intf); + end + + initial begin + run_test(); + end + +endmodule