[dv] Implement tohost termination mechanism - Update Makefile and testbench to use `$readmemh` for memory loading and detect writes to the `tohost` address. - The base test is updated to use this new `tohost` event for test termination and pass/fail checking based on the written status code. Change-Id: Ieef12b5b360645b2e3dc2dfb9263f180042ecaae
diff --git a/tests/uvm/Makefile b/tests/uvm/Makefile index 44aa59f..924fe8f 100644 --- a/tests/uvm/Makefile +++ b/tests/uvm/Makefile
@@ -30,11 +30,14 @@ ENV_DIR = ./env TESTS_DIR = ./tests BIN_DIR = ./bin +UTILS_DIR = $(ROOTDIR)/hw/kelvin/utils # Simulation Work Directory SIM_DIR = ./sim_work LOG_DIR = $(SIM_DIR)/logs WAVE_DIR = $(SIM_DIR)/waves +MEM_FILE_DIR = $(SIM_DIR)/mem_files +RUN_OPTS_FILE = $(MEM_FILE_DIR)/elf_run_opts.f # MPACT-Sim Integration Paths MPACT_DIR = $(ROOTDIR)/sim/kelvin @@ -47,11 +50,12 @@ # Simulation Settings SIM_EXEC = $(SIM_DIR)/simv UVM_TESTNAME ?= kelvin_base_test -TEST_BINARY ?= $(BIN_DIR)/program.bin +TEST_ELF ?= $(BIN_DIR)/program.elf UVM_VERBOSITY ?= UVM_MEDIUM TEST_TIMEOUT_NS ?= 20000 -PLUSARGS = +UVM_TESTNAME=$(UVM_TESTNAME) +TEST_BINARY=$(TEST_BINARY) \ - +UVM_VERBOSITY=$(UVM_VERBOSITY) +TEST_TIMEOUT=$(TEST_TIMEOUT_NS) +PLUSARGS = +UVM_TESTNAME=$(UVM_TESTNAME) \ + +UVM_VERBOSITY=$(UVM_VERBOSITY) +TEST_TIMEOUT=$(TEST_TIMEOUT_NS) \ + +TEST_ELF=$(TEST_ELF) # Waveform Dumping DUMP_WAVES = 1 @@ -77,7 +81,8 @@ -l$(MPACT_COSIM_LIB_NAME) VCS_RUN_OPTS = \ - $(PLUSARGS) + $(PLUSARGS) \ + -f $(RUN_OPTS_FILE) # Add define to COMPILE options for waveform dumping ifeq ($(DUMP_WAVES),1) @@ -88,20 +93,26 @@ # Targets -.PHONY: all compile run clean dirs help build_mpact_lib +.PHONY: all compile run clean dirs help build_mpact_lib gen_mem_files # Default target all: run # Create necessary directories dirs: - @mkdir -p $(SIM_DIR) $(LOG_DIR) $(WAVE_DIR) $(BIN_DIR) + @mkdir -p $(SIM_DIR) $(LOG_DIR) $(WAVE_DIR) $(BIN_DIR) $(MEM_FILE_DIR) # Build the MPACT-Sim library using Bazel with CC=clang build_mpact_lib: @echo "--- Building MPACT-Sim Co-sim Library via Bazel ---" cd $(MPACT_DIR) && CC=clang bazel build //sim/cosim:$(MPACT_COSIM_LIB_NAME) +# Generate memory and run option files from the ELF file +gen_mem_files: dirs + @echo "--- Generating Memory and Run Option Files from ELF ---" + @rm -f $(MEM_FILE_DIR)/* + python3 $(UTILS_DIR)/elf_to_mem.py --elf_file $(TEST_ELF) --out_dir $(MEM_FILE_DIR) + # Compile the design compile: dirs build_mpact_lib @echo "--- Compiling with VCS ---" @@ -109,10 +120,10 @@ @echo "--- Compilation Finished ---" # Run the simulation -run: compile +run: compile gen_mem_files @echo "--- Running Simulation ---" @echo "Test: $(UVM_TESTNAME)" - @echo "Binary: $(TEST_BINARY)" + @echo "ELF File: $(TEST_ELF)" @echo "Verbosity: $(UVM_VERBOSITY)" @echo "Timeout: $(TEST_TIMEOUT_NS) ns" @echo "Plusargs: $(PLUSARGS)" @@ -134,9 +145,10 @@ help: @echo "Makefile Targets:" @echo " make compile : Builds MPACT lib and compiles DUT/testbench" - @echo " make run : Builds and runs the full co-simulation" - @echo " : Override defaults: make run UVM_TESTNAME=<test> TEST_BINARY=<path> UVM_VERBOSITY=<level>" + @echo " make run : Generates mem files, builds, and runs the simulation" + @echo " : Override defaults: make run UVM_TESTNAME=<test> TEST_ELF=<path> UVM_VERBOSITY=<level>" @echo " make build_mpact_lib : Only builds the MPACT-Sim C++ library" + @echo " make gen_mem_files : Only generates ITCM/DTCM and run opt files" @echo " make clean : Removes generated simulation and MPACT-Sim files" @echo " make dirs : Creates simulation directories" @echo " make help : Shows this help message"
diff --git a/tests/uvm/common/kelvin_axi_slave/kelvin_axi_slave_agent_pkg.sv b/tests/uvm/common/kelvin_axi_slave/kelvin_axi_slave_agent_pkg.sv index 64d0f2f..ebe70ed 100644 --- a/tests/uvm/common/kelvin_axi_slave/kelvin_axi_slave_agent_pkg.sv +++ b/tests/uvm/common/kelvin_axi_slave/kelvin_axi_slave_agent_pkg.sv
@@ -29,11 +29,6 @@ `uvm_component_utils(kelvin_axi_slave_model) virtual kelvin_axi_slave_if.TB_SLAVE_MODEL vif; - // Handle to an event that is triggered when the signature address is written - uvm_event signature_written_event; - // The signature address to monitor for - logic [31:0] end_signature_addr; - function new(string name = "kelvin_axi_slave_model", uvm_component parent = null); super.new(name, parent); @@ -46,16 +41,6 @@ `uvm_fatal(get_type_name(), "Virtual interface 'vif' not found for TB_SLAVE_MODEL") end - - // Get the signature address and event handle from the test - if (!uvm_config_db#(logic [31:0])::get(this, "", "end_signature_addr", - end_signature_addr)) begin - `uvm_fatal(get_type_name(), "end_signature_addr not found!") - end - if (!uvm_config_db#(uvm_event)::get(this, "", "signature_written_event", - signature_written_event)) begin - `uvm_fatal(get_type_name(), "signature_written_event not found!") - end endfunction virtual task run_phase(uvm_phase phase); @@ -85,38 +70,15 @@ `uvm_info(get_type_name(), $sformatf("Slave Rcvd AW: Addr=0x%h ID=%0d", vif.tb_slave_cb.awaddr, current_bid), UVM_HIGH) + vif.tb_slave_cb.awready <= 1'b1; + @(vif.tb_slave_cb); + vif.tb_slave_cb.awready <= 1'b0; - // Check for the magic signature address - if (vif.tb_slave_cb.awaddr == end_signature_addr) begin - `uvm_info(get_type_name(), "SIGNATURE ADDRESS WRITE DETECTED", - UVM_LOW) - // Accept the address and data, then trigger the event - vif.tb_slave_cb.awready <= 1'b1; - @(vif.tb_slave_cb); - vif.tb_slave_cb.awready <= 1'b0; - - vif.tb_slave_cb.wready <= 1'b0; - @(vif.tb_slave_cb iff vif.tb_slave_cb.wvalid); - // Pass the signature data back to the test for checking - uvm_config_db#(logic [127:0])::set(null, "*", - "final_signature_data", vif.tb_slave_cb.wdata); - // Trigger the event to end the test - signature_written_event.trigger(); - vif.tb_slave_cb.wready <= 1'b1; - @(vif.tb_slave_cb); - vif.tb_slave_cb.wready <= 1'b0; - end else begin - // Standard write handling for non-signature addresses - vif.tb_slave_cb.awready <= 1'b1; - @(vif.tb_slave_cb); - vif.tb_slave_cb.awready <= 1'b0; - - vif.tb_slave_cb.wready <= 1'b0; - @(vif.tb_slave_cb iff vif.tb_slave_cb.wvalid); - vif.tb_slave_cb.wready <= 1'b1; - @(vif.tb_slave_cb); - vif.tb_slave_cb.wready <= 1'b0; - end + vif.tb_slave_cb.wready <= 1'b0; + @(vif.tb_slave_cb iff vif.tb_slave_cb.wvalid); + vif.tb_slave_cb.wready <= 1'b1; + @(vif.tb_slave_cb); + vif.tb_slave_cb.wready <= 1'b0; // Send write response (OKAY) @(vif.tb_slave_cb);
diff --git a/tests/uvm/tb/kelvin_tb_top.sv b/tests/uvm/tb/kelvin_tb_top.sv index 9005f28..1a1a6c4 100644 --- a/tests/uvm/tb/kelvin_tb_top.sv +++ b/tests/uvm/tb/kelvin_tb_top.sv
@@ -229,6 +229,70 @@ `endif //-------------------------------------------------------------------------- + // ELF Memory Loading and `tohost` Monitor + //-------------------------------------------------------------------------- + initial begin + string itcm_mem_file; + string dtcm_mem_file; + string tohost_addr_str; + logic [31:0] tohost_addr; + uvm_event tohost_written_event; + + tohost_written_event = new("tohost_written_event"); + uvm_config_db#(uvm_event)::set(null, "*", + "tohost_written_event", + tohost_written_event); + + // Load memories at time 0 + if ($value$plusargs("ITCM_MEM_FILE=%s", itcm_mem_file)) begin + `uvm_info("TB_TOP", $sformatf("Loading ITCM from %s", itcm_mem_file), UVM_LOW) + $readmemh(itcm_mem_file, kelvin_tb_top.u_dut.itcm.sram.sramModules_0.mem); + end + if ($value$plusargs("DTCM_MEM_FILE=%s", dtcm_mem_file)) begin + `uvm_info("TB_TOP", $sformatf("Loading DTCM from %s", dtcm_mem_file), UVM_LOW) + $readmemh(dtcm_mem_file, kelvin_tb_top.u_dut.dtcm.sram.sramModules_0.mem); + end + + // Get the tohost address from the plusargs + if ($value$plusargs("TOHOST_ADDR=%s", tohost_addr_str)) begin + if ($sscanf(tohost_addr_str, "'h%h", tohost_addr) != 1) begin + `uvm_fatal("TB_TOP", "Invalid +TOHOST_ADDR format.") + end + end + + // Fork a process that waits for the write + fork + forever begin + @(posedge clk); + // Check internal data bus (dbus) + if (u_dut.core.io_dbus_valid && u_dut.core.io_dbus_write && + u_dut.core.io_dbus_addr == tohost_addr) begin + if (u_dut.core.io_dbus_wdata[0] == 1'b1) begin + `uvm_info("TB_TOP_MONITOR", "tohost write detected on DBUS.", UVM_LOW) + uvm_config_db#(logic [127:0])::set(null, "*", + "final_tohost_data", u_dut.core.io_dbus_wdata); + tohost_written_event.trigger(); + break; // Stop monitoring once triggered + end + end + + // Check external bus (ebus) + if (u_dut.core.io_ebus_dbus_valid && u_dut.core.io_ebus_dbus_ready && + u_dut.core.io_ebus_dbus_write && u_dut.core.io_ebus_dbus_addr == tohost_addr) begin + if (u_dut.core.io_ebus_dbus_wdata[0] == 1'b1) begin + `uvm_info("TB_TOP_MONITOR", "tohost write detected on EBUS.", UVM_LOW) + uvm_config_db#(logic [127:0])::set(null, "*", + "final_tohost_data", u_dut.core.io_ebus_dbus_wdata); + tohost_written_event.trigger(); + break; // Stop monitoring once triggered + end + end + end + join + end + + + //-------------------------------------------------------------------------- // UVM Test Execution //-------------------------------------------------------------------------- initial begin
diff --git a/tests/uvm/tests/kelvin_test_pkg.sv b/tests/uvm/tests/kelvin_test_pkg.sv index 27e61c8..cff058e 100644 --- a/tests/uvm/tests/kelvin_test_pkg.sv +++ b/tests/uvm/tests/kelvin_test_pkg.sv
@@ -107,40 +107,30 @@ `uvm_component_utils(kelvin_base_test) kelvin_env env; - string test_binary = ""; time test_timeout = 20_000_000ns; - logic [31:0] end_signature_addr = 32'h10000; - string dut_mem_path_prefix = "kelvin_tb_top.u_dut.itcm.sram.sramModules_"; protected bit test_passed = 1'b0; protected bit test_timed_out = 1'b0; protected bit dut_halted_flag = 1'b0; protected bit dut_faulted_flag = 1'b0; - protected bit signature_written_flag = 1'b0; - protected logic [127:0] final_signature_data; + protected bit tohost_written_flag = 1'b0; + protected logic [127:0] final_tohost_data; virtual kelvin_irq_if.DUT_IRQ_PORT irq_vif; - uvm_event signature_written_event; + uvm_event tohost_written_event; function new(string name = "kelvin_base_test", uvm_component parent = null); super.new(name, parent); endfunction virtual function void build_phase(uvm_phase phase); - string timeout_str, sig_addr_str; + string timeout_str; int timeout_int; uvm_cmdline_processor clp = uvm_cmdline_processor::get_inst(); super.build_phase(phase); `uvm_info(get_type_name(), "Build phase starting", UVM_MEDIUM) - if (!clp.get_arg_value("+TEST_BINARY=", test_binary)) begin - uvm_config_db#(string)::get(this, "", "test_binary", test_binary); - end - if (test_binary != "") `uvm_info(get_type_name(), - $sformatf("Using Test Binary: %s", test_binary), UVM_MEDIUM) - else `uvm_warning(get_type_name(), "No test binary specified!") - if (clp.get_arg_value("+TEST_TIMEOUT=", timeout_str)) begin if ($sscanf(timeout_str, "%d", timeout_int) == 1 && timeout_int > 0) begin @@ -152,22 +142,13 @@ $sformatf("Invalid +TEST_TIMEOUT value: %s", timeout_str)) end - if (clp.get_arg_value("+END_SIGNATURE_ADDR=", sig_addr_str)) begin - if ($sscanf(sig_addr_str, "'h%h", end_signature_addr) == 1) begin - `uvm_info(get_type_name(), - $sformatf("Using custom End Signature Addr: 0x%h", - end_signature_addr), UVM_MEDIUM) - end else `uvm_warning(get_type_name(), - $sformatf("Invalid +END_SIGNATURE_ADDR: %s", sig_addr_str)) - end - env = kelvin_env::type_id::create("env", this); - signature_written_event = new("signature_written_event"); - uvm_config_db#(uvm_event)::set(this, "*.m_slave_agent.slave_model", - "signature_written_event", signature_written_event); - uvm_config_db#(logic [31:0])::set(this, "*.m_slave_agent.slave_model", - "end_signature_addr", end_signature_addr); + // Get the event handle that was created and set by tb_top + if (!uvm_config_db#(uvm_event)::get(this, "", + "tohost_written_event", tohost_written_event)) begin + `uvm_fatal(get_type_name(), "tohost_written_event handle not found!") + end if (!uvm_config_db#(virtual kelvin_irq_if.DUT_IRQ_PORT)::get(this, "", "irq_vif", irq_vif)) @@ -182,7 +163,7 @@ kelvin_kickoff_write_seq kickoff_seq; phase.raise_objection(this, "Base test running"); - if (test_binary != "") load_binary_to_mem(test_binary); + // Memory is loaded by $readmemh in tb_top before run phase starts. `uvm_info(get_type_name(), "Waiting for reset deassertion...", UVM_MEDIUM) @(posedge irq_vif.clk iff irq_vif.resetn == 1'b1); @@ -194,16 +175,16 @@ `uvm_info(get_type_name(), "Waiting for completion or timeout...", UVM_MEDIUM) fork - begin - signature_written_event.wait_trigger(); - signature_written_flag = 1'b1; + begin // Wait for tohost write + tohost_written_event.wait_trigger(); + tohost_written_flag = 1'b1; end - begin + begin // Wait for DUT halted/faulted (backup termination) wait (irq_vif.halted == 1'b1 || irq_vif.fault == 1'b1); dut_halted_flag = irq_vif.halted; dut_faulted_flag = irq_vif.fault; end - begin + begin // Timeout mechanism #(test_timeout); test_timed_out = 1'b1; end @@ -217,28 +198,29 @@ endtask virtual function void report_phase(uvm_phase phase); + logic [31:1] status_code; super.report_phase(phase); - if (signature_written_flag) begin + if (tohost_written_flag) begin if (uvm_config_db#(logic [127:0])::get(this, "", - "final_signature_data", final_signature_data)) begin - if (final_signature_data == 0) begin + "final_tohost_data", final_tohost_data)) begin + status_code = final_tohost_data[31:1]; + if (status_code == 0) begin test_passed = 1'b1; `uvm_info(get_type_name(), - $sformatf("Signature write detected with PASS status (0x%h).", - final_signature_data), UVM_LOW) + "tohost write detected with PASS status (0).", UVM_LOW) end else begin test_passed = 1'b0; `uvm_error(get_type_name(), - $sformatf("Signature write detected with FAIL status (0x%h).", - final_signature_data)) + $sformatf("tohost write detected with FAIL status code: %0d", + status_code)) end end else `uvm_error(get_type_name(), - "Signature event triggered, but final status not found!") + "tohost event triggered, but final status not found!") end else if (dut_halted_flag && !dut_faulted_flag) begin - `uvm_info(get_type_name(), "Halt without signature. Passing.", UVM_LOW) + `uvm_info(get_type_name(), "Test ended on DUT halt.", UVM_LOW) test_passed = 1'b1; end else if (dut_faulted_flag) begin - `uvm_error(get_type_name(), "Test ended on DUT fault.") + `uvm_info(get_type_name(), "Test ended on DUT fault.", UVM_LOW) test_passed = 1'b0; end else if (test_timed_out) begin `uvm_error(get_type_name(), @@ -254,76 +236,6 @@ else `uvm_error(get_type_name(), "** UVM TEST FAILED **") endfunction - virtual task load_binary_to_mem(string binary_path); - int unsigned file_handle; - int unsigned byte_count = 0; - logic [31:0] base_addr = 0; - int unsigned num_sram_modules = 32; - int unsigned sram_module_size_bytes = (2048 * 128) / 8; - int unsigned total_mem_size_bytes = num_sram_modules * - sram_module_size_bytes; - int char_code; - logic [7:0] byte_val; - logic [31:0] current_byte_addr; - int unsigned sram_module_idx; - int unsigned addr_in_module; - string element_path; - int unsigned word_index; - int unsigned byte_offset_in_word; - int unsigned bit_offset_low; - int unsigned bit_offset_high; - - `uvm_info(get_type_name(), - $sformatf("Attempting to load binary '%s'", binary_path), - UVM_MEDIUM) - file_handle = $fopen(binary_path, "rb"); - if (file_handle == 0) begin - `uvm_fatal(get_type_name(), - $sformatf("Failed to open binary file: %s", binary_path)) - return; - end - - forever begin - char_code = $fgetc(file_handle); - if ($feof(file_handle)) break; - if (byte_count >= total_mem_size_bytes) begin - `uvm_error(get_type_name(), - $sformatf("Binary file too large (%0d bytes). Truncating.", - total_mem_size_bytes)); - break; - end - - byte_val = char_code; - current_byte_addr = base_addr + byte_count; - - sram_module_idx = current_byte_addr / sram_module_size_bytes; - addr_in_module = current_byte_addr % sram_module_size_bytes; - - word_index = addr_in_module / 16; - byte_offset_in_word = addr_in_module % 16; - bit_offset_low = byte_offset_in_word * 8; - bit_offset_high = bit_offset_low + 7; - element_path = $sformatf("%s%0d.mem[%0d][%0d:%0d]", - dut_mem_path_prefix, sram_module_idx, - word_index, bit_offset_high, bit_offset_low); - - if (!uvm_hdl_deposit(element_path, byte_val)) - `uvm_error(get_type_name(), - $sformatf("uvm_hdl_deposit failed for path: %s", - element_path)) - - byte_count++; - end - - $fclose(file_handle); - if (byte_count > 0) - `uvm_info(get_type_name(), - $sformatf("Finished loading %0d bytes.", byte_count), - UVM_MEDIUM) - else - `uvm_warning(get_type_name(), "Binary file was empty or not read.") - endtask - endclass endpackage : kelvin_test_pkg