[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