[dv, cosim] Enable lock-step PC and GPR verification with MPACT-Sim
Implements the full end-to-end co-simulation comparison for scalar
instructions (PC and GPRs).
The `kelvin_cosim_checker` in UVM is to:
- Call the DPI functions to get the golden state from the simulator.
- Compare the retired PC and GPR writeback data from the DUT's RVVI
trace against the MPACT-Sim state.
- Report `COSIM_PC_MISMATCH` or `COSIM_GPR_MISMATCH` upon failure.
Change-Id: I5d7f02c6d347b4308bc4ea8a38f08ce2407659e0
diff --git a/tests/uvm/Makefile b/tests/uvm/Makefile
index 8f121e3..44aa59f 100644
--- a/tests/uvm/Makefile
+++ b/tests/uvm/Makefile
@@ -12,37 +12,36 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# Makefile for Kelvin UVM Testbench
+# Makefile for Kelvin UVM Testbench (Integrates with MPACT-Sim)
-# --- User Configuration ---
-# Tool Configuration
+# Check if ROOTDIR is set. If not, exit with an error.
+ifndef ROOTDIR
+ $(error ROOTDIR is not set. Please define it, e.g., export ROOTDIR=$(abspath ../../../..))
+endif
+
+# User Configuration
VCS = vcs
+CXX = clang++
-# Directories
+# UVM DV Directories
RTL_DIR = ./rtl
COMMON_DIR = ./common
TB_DIR = ./tb
ENV_DIR = ./env
TESTS_DIR = ./tests
BIN_DIR = ./bin
+
+# Simulation Work Directory
SIM_DIR = ./sim_work
LOG_DIR = $(SIM_DIR)/logs
WAVE_DIR = $(SIM_DIR)/waves
-# Source Files
-DUT_RTL = $(RTL_DIR)/RvvCoreMiniVerificationAxi.sv
-IF_FILES = $(COMMON_DIR)/kelvin_axi_master/kelvin_axi_master_if.sv \
- $(COMMON_DIR)/kelvin_axi_slave/kelvin_axi_slave_if.sv \
- $(COMMON_DIR)/kelvin_irq/kelvin_irq_if.sv
-TRANS_PKG_FILE = $(COMMON_DIR)/transaction_item/transaction_item_pkg.sv
-AXI_MASTER_AGENT_PKG = $(COMMON_DIR)/kelvin_axi_master/kelvin_axi_master_agent_pkg.sv
-AXI_SLAVE_AGENT_PKG = $(COMMON_DIR)/kelvin_axi_slave/kelvin_axi_slave_agent_pkg.sv
-IRQ_AGENT_PKG = $(COMMON_DIR)/kelvin_irq/kelvin_irq_agent_pkg.sv
-ENV_PKG = $(ENV_DIR)/kelvin_env_pkg.sv
-TEST_PKG = $(TESTS_DIR)/kelvin_test_pkg.sv
-TB_TOP_FILE = $(TB_DIR)/kelvin_tb_top.sv
+# MPACT-Sim Integration Paths
+MPACT_DIR = $(ROOTDIR)/sim/kelvin
+MPACT_BAZEL_BIN_DIR = $(MPACT_DIR)/bazel-bin/sim/cosim
+MPACT_COSIM_LIB_NAME = kelvin_cosim_lib_static
-# File List for VCS -f option (Assumed to exist and be checked in)
+# File List for VCS -f option
FILE_LIST = ./kelvin_dv.f
# Simulation Settings
@@ -51,14 +50,14 @@
TEST_BINARY ?= $(BIN_DIR)/program.bin
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) +TEST_BINARY=$(TEST_BINARY) \
+ +UVM_VERBOSITY=$(UVM_VERBOSITY) +TEST_TIMEOUT=$(TEST_TIMEOUT_NS)
# Waveform Dumping
DUMP_WAVES = 1
WAVE_FILE = $(WAVE_DIR)/$(UVM_TESTNAME).fsdb
-# --- VCS Options ---
-# Base compile options
+# VCS Options
VCS_COMPILE_OPTS = \
-full64 \
-sverilog \
@@ -69,7 +68,14 @@
-timescale=1ns/1ps \
-o $(SIM_EXEC)
-# Base run options
+# Add C++ compiler/linker options for MPACT-Sim
+VCS_COMPILE_OPTS += \
+ -cpp $(CXX) \
+ -cppflags "-std=c++17" \
+ -CFLAGS "-I$(MPACT_DIR)" \
+ -L$(MPACT_BAZEL_BIN_DIR) \
+ -l$(MPACT_COSIM_LIB_NAME)
+
VCS_RUN_OPTS = \
$(PLUSARGS)
@@ -80,9 +86,9 @@
+fsdbfile+$(WAVE_FILE)
endif
-# --- Targets ---
+# Targets
-.PHONY: all compile run clean dirs help
+.PHONY: all compile run clean dirs help build_mpact_lib
# Default target
all: run
@@ -91,8 +97,13 @@
dirs:
@mkdir -p $(SIM_DIR) $(LOG_DIR) $(WAVE_DIR) $(BIN_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)
+
# Compile the design
-compile: dirs
+compile: dirs build_mpact_lib
@echo "--- Compiling with VCS ---"
$(VCS) $(VCS_COMPILE_OPTS) -l $(LOG_DIR)/compile.log -f $(FILE_LIST)
@echo "--- Compilation Finished ---"
@@ -116,13 +127,16 @@
clean:
@echo "--- Cleaning Simulation Files ---"
rm -rf $(SIM_DIR) simv* csrc* *.log* *.key *.vpd *.fsdb ucli.key DVEfiles/ verdiLog/ novas.*
+ @echo "--- Cleaning MPACT-Sim Bazel cache ---"
+ cd $(MPACT_DIR) && bazel clean
-# --- Help ---
+# Help
help:
@echo "Makefile Targets:"
- @echo " make compile : Compiles the DUT and testbench (using $(FILE_LIST))"
- @echo " make run : Compiles (if needed) and runs the simulation"
- @echo " : Override defaults: make run UVM_TESTNAME=<test> TEST_BINARY=<path> UVM_VERBOSITY=<level>"
- @echo " make clean : Removes generated simulation files"
- @echo " make dirs : Creates simulation directories"
- @echo " make help : Shows this help message"
+ @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 build_mpact_lib : Only builds the MPACT-Sim C++ library"
+ @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/bin/program.bin b/tests/uvm/bin/program.bin
index 0ae87ca..803908a 100644
--- a/tests/uvm/bin/program.bin
+++ b/tests/uvm/bin/program.bin
Binary files differ
diff --git a/tests/uvm/common/cosim/kelvin_cosim_checker_pkg.sv b/tests/uvm/common/cosim/kelvin_cosim_checker_pkg.sv
index 72097f9..167847a 100644
--- a/tests/uvm/common/cosim/kelvin_cosim_checker_pkg.sv
+++ b/tests/uvm/common/cosim/kelvin_cosim_checker_pkg.sv
@@ -20,6 +20,19 @@
import uvm_pkg::*;
`include "uvm_macros.svh"
+ import kelvin_cosim_dpi_if::*;
+
+ //----------------------------------------------------------------------------
+ // Struct: retired_instr_info_s
+ // Description: A struct to hold information about a single retired
+ // instruction, captured from the RVVI trace.
+ //----------------------------------------------------------------------------
+ typedef struct {
+ logic [31:0] pc;
+ logic [31:0] insn;
+ logic [31:0] x_wb;
+ int retire_index; // Original index from the RVVI bus
+ } retired_instr_info_s;
//----------------------------------------------------------------------------
// Class: kelvin_cosim_checker
@@ -66,43 +79,123 @@
// Run phase: Contains the main co-simulation loop
virtual task run_phase(uvm_phase phase);
- // TODO: Initialize the MPACT simulator.
- `uvm_info("COSIM_STUB", "MPACT simulator would be initialized now.",
- UVM_MEDIUM);
+ retired_instr_info_s retired_instr_q[$];
+ int unsigned num_retired_this_cycle;
+ int unsigned mpact_pc;
+ logic [31:0] rtl_instr;
+ if (mpact_init() != 0)
+ `uvm_fatal(get_type_name(), "MPACT simulator DPI init failed.")
// Main co-simulation loop
forever begin
// Wait for the RVVI monitor to signal an instruction retirement
instruction_retired_event.wait_trigger();
+ retired_instr_q.delete();
- // This is a simplified example for one retirement channel (channel 0).
- // A full implementation would loop through all NRET channels.
- if (rvvi_vif.valid[0][0]) begin // Assuming NHART=1, check hart 0
- // Get the retired instruction from the DUT's trace
- logic [31:0] retired_instruction = rvvi_vif.insn[0][0];
+ // Collect all retired instructions and their state from the RVVI trace
+ for (int i = 0; i < rvvi_vif.RETIRE; i++) begin
+ if (rvvi_vif.valid[0][i]) begin
+ retired_instr_info_s info;
+ info.pc = rvvi_vif.pc_rdata[0][i];
+ info.insn = rvvi_vif.insn[0][i];
+ info.x_wb = rvvi_vif.x_wb[0][i];
+ info.retire_index = i; // Store the original channel index
+ retired_instr_q.push_back(info);
+ `uvm_info(get_type_name(),
+ $sformatf("RTL Retired: PC=0x%h, Insn=0x%h", info.pc, info.insn),
+ UVM_HIGH)
+ end
+ end
+ num_retired_this_cycle = retired_instr_q.size();
- `uvm_info("COSIM_STUB",
- $sformatf("DUT retired instruction 0x%h",
- retired_instruction), UVM_HIGH);
+ for (int i = 0; i < num_retired_this_cycle; i++) begin
+ bit pc_match_found = 0;
+ int match_index = -1;
- // TODO: Send this specific instruction to the MPACT simulator to
- // execute, then call comparison task.
- step_and_compare();
+ mpact_pc = mpact_get_pc();
+
+ foreach (retired_instr_q[j]) begin
+ if (retired_instr_q[j].pc == mpact_pc) begin
+ pc_match_found = 1;
+ match_index = j;
+ break;
+ end
+ end
+
+ if (!pc_match_found) begin
+ string rtl_pcs_str = "[ ";
+ foreach (retired_instr_q[j]) begin
+ rtl_pcs_str = $sformatf("%s0x%h ", rtl_pcs_str,
+ retired_instr_q[j].pc);
+ end
+ rtl_pcs_str = {rtl_pcs_str, "]"};
+ `uvm_error("COSIM_PC_MISMATCH",
+ $sformatf("MPACT PC 0x%h mismatches retired RTL PCs: %s",
+ mpact_pc, rtl_pcs_str))
+ phase.drop_objection(this, "Terminating on PC mismatch.");
+ return;
+ end
+
+ rtl_instr = retired_instr_q[match_index].insn;
+ `uvm_info(get_type_name(),
+ $sformatf("PC match (0x%h). Stepping MPACT with 0x%h",
+ mpact_pc, rtl_instr), UVM_HIGH)
+
+ if (mpact_step(rtl_instr) != 0) begin
+ `uvm_error("COSIM_STEP_FAIL", "mpact_step() DPI call failed.")
+ phase.drop_objection(this, "Terminating on MPACT step fail.");
+ return;
+ end
+
+ // Check return status and terminate on failure
+ if (!step_and_compare(retired_instr_q[match_index])) begin
+ phase.drop_objection(this, "Terminating on GPR mismatch.");
+ return;
+ end
+
+ retired_instr_q.delete(match_index);
end
end
endtask
- // Task to get state from DUT and MPACT simulator and compare them
- virtual task step_and_compare();
- // TODO: Implement the full state comparison.
- // 1. Make DPI calls to get post-execution state (PC, GPRs, CSRs)
- // from MPACT simulator.
- // 2. Get the same post-execution state from the DUT via the RVVI
- // virtual interface.
- // 3. Compare the RTL state against the MPACT simulator state and
- // report any mismatches.
- endtask
+ virtual function bit step_and_compare(retired_instr_info_s rtl_info);
+ int unsigned mpact_gpr_val;
+ int unsigned rd_index;
+ logic [31:0] rtl_wdata;
+
+ `uvm_info(get_type_name(), "Comparing GPR writeback state...", UVM_HIGH)
+
+ if (!$onehot0(rtl_info.x_wb)) begin
+ `uvm_error("COSIM_GPR_MISMATCH",
+ $sformatf("Invalid GPR writeback flag at PC 0x%h. x_wb is not one-hot: 0x%h",
+ rtl_info.pc, rtl_info.x_wb))
+ return 0; // FAIL
+ end
+
+ if (rtl_info.x_wb == 1) begin
+ `uvm_error("COSIM_GPR_MISMATCH",
+ $sformatf("Illegal write to x0 detected at PC 0x%h.", rtl_info.pc))
+ return 0; // FAIL
+ end
+ else if (rtl_info.x_wb != 0) begin
+ rd_index = $clog2(rtl_info.x_wb);
+ mpact_gpr_val = mpact_get_gpr(rd_index);
+
+ // Get the specific write data from the correct retire channel and register index
+ rtl_wdata = rvvi_vif.x_wdata[0][rtl_info.retire_index][rd_index];
+
+ if (mpact_gpr_val != rtl_wdata) begin
+ `uvm_error("COSIM_GPR_MISMATCH",
+ $sformatf("GPR[x%0d] mismatch at PC 0x%h. RTL: 0x%h, MPACT: 0x%h",
+ rd_index, rtl_info.pc,
+ rtl_wdata, mpact_gpr_val))
+ return 0; // FAIL
+ end
+ end
+ // If we reach here, all checks passed for this instruction.
+ return 1; // PASS
+ endfunction
endclass : kelvin_cosim_checker
diff --git a/tests/uvm/common/cosim/kelvin_cosim_dpi_if.sv b/tests/uvm/common/cosim/kelvin_cosim_dpi_if.sv
new file mode 100644
index 0000000..ce111f4
--- /dev/null
+++ b/tests/uvm/common/cosim/kelvin_cosim_dpi_if.sv
@@ -0,0 +1,57 @@
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//----------------------------------------------------------------------------
+// Package: kelvin_cosim_dpi_if
+// Description: Defines the DPI-C import declarations for interacting with the
+// MPACT simulator. This acts as the SystemVerilog-side "header".
+//----------------------------------------------------------------------------
+package kelvin_cosim_dpi_if;
+
+ // Function to initialize the MPACT simulator.
+ // Returns 0 on success.
+ import "DPI-C" context function int mpact_init();
+
+ // Function to reset the MPACT simulator.
+ // Returns 0 on success.
+ import "DPI-C" context function int mpact_reset();
+
+ // Function to execute one instruction in the MPACT simulator.
+ // Returns 0 on success.
+ import "DPI-C" context function int mpact_step(
+ input logic [31:0] instruction
+ );
+
+ // Function to check if the MPACT simulator has halted.
+ // Returns '1' (true) if halted.
+ import "DPI-C" context function bit mpact_is_halted();
+
+ // Function to get the program counter.
+ import "DPI-C" context function int unsigned mpact_get_pc();
+
+ // Function to get a general-purpose register value.
+ import "DPI-C" context function int unsigned mpact_get_gpr(
+ input int unsigned index
+ );
+
+ // Function to get a control and status register value.
+ import "DPI-C" context function int unsigned mpact_get_csr(
+ input int unsigned address
+ );
+
+ // Function to finalize the MPACT simulator.
+ // Returns 0 on success.
+ import "DPI-C" context function int mpact_fini();
+
+endpackage : kelvin_cosim_dpi_if
diff --git a/tests/uvm/common/cosim/kelvin_rvvi_agent_pkg.sv b/tests/uvm/common/cosim/kelvin_rvvi_agent_pkg.sv
index 62969fb..64cebc5 100644
--- a/tests/uvm/common/cosim/kelvin_rvvi_agent_pkg.sv
+++ b/tests/uvm/common/cosim/kelvin_rvvi_agent_pkg.sv
@@ -75,10 +75,10 @@
@(posedge rvvi_vif.clk);
for (int i = 0; i < RETIRE; i++) begin
- if (rvvi_vif.valid[i][0]) begin // Assuming NHART=1
+ if (rvvi_vif.valid[0][i]) begin // Assuming NHART=1
`uvm_info(get_type_name(),
$sformatf("Instruction retired on channel %0d, PC: 0x%h",
- i, rvvi_vif.pc_rdata[i][0]), UVM_HIGH)
+ i, rvvi_vif.pc_rdata[0][i]), UVM_HIGH)
any_instruction_retired = 1'b1;
end
end
diff --git a/tests/uvm/kelvin_dv.f b/tests/uvm/kelvin_dv.f
index bee6870..2f725f2 100644
--- a/tests/uvm/kelvin_dv.f
+++ b/tests/uvm/kelvin_dv.f
@@ -39,8 +39,9 @@
./common/kelvin_axi_master/kelvin_axi_master_if.sv
./common/kelvin_axi_slave/kelvin_axi_slave_if.sv
./common/kelvin_irq/kelvin_irq_if.sv
+./common/cosim/kelvin_cosim_dpi_if.sv
-// UVM Packages
+// UVM Packages (in dependency order)
./common/transaction_item/transaction_item_pkg.sv
./common/kelvin_axi_master/kelvin_axi_master_agent_pkg.sv
./common/kelvin_axi_slave/kelvin_axi_slave_agent_pkg.sv
diff --git a/tests/uvm/tests/kelvin_test_pkg.sv b/tests/uvm/tests/kelvin_test_pkg.sv
index 4949d6f..27e61c8 100644
--- a/tests/uvm/tests/kelvin_test_pkg.sv
+++ b/tests/uvm/tests/kelvin_test_pkg.sv
@@ -52,7 +52,7 @@
req.prot = 3'b000;
req.data.delete();
req.strb.delete();
- req.data.push_back(128'h00000034_00000000); // PC value
+ req.data.push_back(128'h00000000_00000000); // PC value
req.strb.push_back('1); // Write all bytes
finish_item(req);
`uvm_info(get_type_name(),