blob: 1a1a6c4619582a41610b1997e6113b8f787c13ed [file] [log] [blame]
// 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.
//----------------------------------------------------------------------------
// Module: kelvin_tb_top
// Description: Top-level testbench module for the Kelvin DUT.
// Instantiates the DUT, interfaces, and starts the UVM simulation.
//----------------------------------------------------------------------------
module kelvin_tb_top;
import uvm_pkg::*;
`include "uvm_macros.svh"
// Import all necessary UVM packages
import kelvin_test_pkg::*;
import kelvin_env_pkg::*;
import transaction_item_pkg::*;
import kelvin_axi_master_agent_pkg::*;
import kelvin_axi_slave_agent_pkg::*;
import kelvin_irq_agent_pkg::*;
import kelvin_rvvi_agent_pkg::*;
import kelvin_cosim_checker_pkg::*;
//--------------------------------------------------------------------------
// Parameters
//--------------------------------------------------------------------------
localparam int unsigned AXI_ADDR_WIDTH = 32;
localparam int unsigned AXI_DATA_WIDTH = 128;
localparam int unsigned AXI_ID_WIDTH = 6;
localparam time CLK_PERIOD = 10ns;
//--------------------------------------------------------------------------
// Clock and Reset Signals
//--------------------------------------------------------------------------
bit clk;
bit resetn;
//--------------------------------------------------------------------------
// Interface Instantiations
//--------------------------------------------------------------------------
kelvin_axi_master_if #(
.AWIDTH(AXI_ADDR_WIDTH),
.DWIDTH(AXI_DATA_WIDTH),
.IDWIDTH(AXI_ID_WIDTH)
) master_axi_if ( .clk(clk), .resetn(resetn) );
kelvin_axi_slave_if #(
.AWIDTH(AXI_ADDR_WIDTH),
.DWIDTH(AXI_DATA_WIDTH),
.IDWIDTH(AXI_ID_WIDTH)
) slave_axi_if ( .clk(clk), .resetn(resetn) );
kelvin_irq_if irq_if ( .clk(clk), .resetn(resetn) );
virtual rvviTrace #(
.ILEN(32), .XLEN(32), .FLEN(32), .VLEN(128), .NHART(1), .RETIRE(8)
) rvvi_vif;
//--------------------------------------------------------------------------
// DUT Instantiation
//--------------------------------------------------------------------------
RvvCoreMiniVerificationAxi u_dut (
.io_aclk(clk),
.io_aresetn(resetn),
// AXI Slave Port (Driven by TB Master)
.io_axi_slave_write_addr_valid (master_axi_if.awvalid),
.io_axi_slave_write_addr_ready (master_axi_if.awready),
.io_axi_slave_write_addr_bits_addr (master_axi_if.awaddr),
.io_axi_slave_write_addr_bits_prot (master_axi_if.awprot),
.io_axi_slave_write_addr_bits_id (master_axi_if.awid),
.io_axi_slave_write_addr_bits_len (master_axi_if.awlen),
.io_axi_slave_write_addr_bits_size (master_axi_if.awsize),
.io_axi_slave_write_addr_bits_burst(master_axi_if.awburst),
.io_axi_slave_write_addr_bits_lock (master_axi_if.awlock),
.io_axi_slave_write_addr_bits_cache(master_axi_if.awcache),
.io_axi_slave_write_addr_bits_qos (master_axi_if.awqos),
.io_axi_slave_write_addr_bits_region(master_axi_if.awregion),
.io_axi_slave_write_data_valid (master_axi_if.wvalid),
.io_axi_slave_write_data_ready (master_axi_if.wready),
.io_axi_slave_write_data_bits_data (master_axi_if.wdata),
.io_axi_slave_write_data_bits_last (master_axi_if.wlast),
.io_axi_slave_write_data_bits_strb (master_axi_if.wstrb),
.io_axi_slave_write_resp_valid (master_axi_if.bvalid),
.io_axi_slave_write_resp_ready (master_axi_if.bready),
.io_axi_slave_write_resp_bits_id (master_axi_if.bid),
.io_axi_slave_write_resp_bits_resp (master_axi_if.bresp),
.io_axi_slave_read_addr_valid (master_axi_if.arvalid),
.io_axi_slave_read_addr_ready (master_axi_if.arready),
.io_axi_slave_read_addr_bits_addr (master_axi_if.araddr),
.io_axi_slave_read_addr_bits_prot (master_axi_if.arprot),
.io_axi_slave_read_addr_bits_id (master_axi_if.arid),
.io_axi_slave_read_addr_bits_len (master_axi_if.arlen),
.io_axi_slave_read_addr_bits_size (master_axi_if.arsize),
.io_axi_slave_read_addr_bits_burst (master_axi_if.arburst),
.io_axi_slave_read_addr_bits_lock (master_axi_if.arlock),
.io_axi_slave_read_addr_bits_cache (master_axi_if.arcache),
.io_axi_slave_read_addr_bits_qos (master_axi_if.arqos),
.io_axi_slave_read_addr_bits_region(master_axi_if.arregion),
.io_axi_slave_read_data_valid (master_axi_if.rvalid),
.io_axi_slave_read_data_ready (master_axi_if.rready),
.io_axi_slave_read_data_bits_data (master_axi_if.rdata),
.io_axi_slave_read_data_bits_id (master_axi_if.rid),
.io_axi_slave_read_data_bits_resp (master_axi_if.rresp),
.io_axi_slave_read_data_bits_last (master_axi_if.rlast),
// AXI Master Port (Drives TB Slave)
.io_axi_master_write_addr_valid (slave_axi_if.awvalid),
.io_axi_master_write_addr_ready (slave_axi_if.awready),
.io_axi_master_write_addr_bits_addr(slave_axi_if.awaddr),
.io_axi_master_write_addr_bits_prot(slave_axi_if.awprot),
.io_axi_master_write_addr_bits_id (slave_axi_if.awid),
.io_axi_master_write_addr_bits_len(slave_axi_if.awlen),
.io_axi_master_write_addr_bits_size(slave_axi_if.awsize),
.io_axi_master_write_addr_bits_burst(slave_axi_if.awburst),
.io_axi_master_write_addr_bits_lock(slave_axi_if.awlock),
.io_axi_master_write_addr_bits_cache(slave_axi_if.awcache),
.io_axi_master_write_addr_bits_qos(slave_axi_if.awqos),
.io_axi_master_write_addr_bits_region(slave_axi_if.awregion),
.io_axi_master_write_data_valid (slave_axi_if.wvalid),
.io_axi_master_write_data_ready (slave_axi_if.wready),
.io_axi_master_write_data_bits_data(slave_axi_if.wdata),
.io_axi_master_write_data_bits_last(slave_axi_if.wlast),
.io_axi_master_write_data_bits_strb(slave_axi_if.wstrb),
.io_axi_master_write_resp_valid (slave_axi_if.bvalid),
.io_axi_master_write_resp_ready (slave_axi_if.bready),
.io_axi_master_write_resp_bits_id (slave_axi_if.bid),
.io_axi_master_write_resp_bits_resp(slave_axi_if.bresp),
.io_axi_master_read_addr_valid (slave_axi_if.arvalid),
.io_axi_master_read_addr_ready (slave_axi_if.arready),
.io_axi_master_read_addr_bits_addr(slave_axi_if.araddr),
.io_axi_master_read_addr_bits_prot(slave_axi_if.arprot),
.io_axi_master_read_addr_bits_id (slave_axi_if.arid),
.io_axi_master_read_addr_bits_len (slave_axi_if.arlen),
.io_axi_master_read_addr_bits_size(slave_axi_if.arsize),
.io_axi_master_read_addr_bits_burst(slave_axi_if.arburst),
.io_axi_master_read_addr_bits_lock (slave_axi_if.arlock),
.io_axi_master_read_addr_bits_cache(slave_axi_if.arcache),
.io_axi_master_read_addr_bits_qos (slave_axi_if.arqos),
.io_axi_master_read_addr_bits_region(slave_axi_if.arregion),
.io_axi_master_read_data_valid (slave_axi_if.rvalid),
.io_axi_master_read_data_ready (slave_axi_if.rready),
.io_axi_master_read_data_bits_data(slave_axi_if.rdata),
.io_axi_master_read_data_bits_id (slave_axi_if.rid),
.io_axi_master_read_data_bits_resp(slave_axi_if.rresp),
.io_axi_master_read_data_bits_last(slave_axi_if.rlast),
// IRQ, Control, and Status Signals
.io_irq(irq_if.irq),
.io_te(irq_if.te),
.io_halted(irq_if.halted),
.io_fault(irq_if.fault),
.io_wfi(irq_if.wfi),
// TODO: Connect Debug and Logging ports if needed by TB
.io_debug_en(),
.io_debug_addr_0(),
.io_debug_addr_1(),
.io_debug_addr_2(),
.io_debug_addr_3(),
.io_debug_inst_0(),
.io_debug_inst_1(),
.io_debug_inst_2(),
.io_debug_inst_3(),
.io_debug_cycles(),
.io_slog_valid(),
.io_slog_addr(),
.io_slog_data()
);
//--------------------------------------------------------------------------
// Clock Generation
//--------------------------------------------------------------------------
initial begin
clk = 0;
forever #(CLK_PERIOD/2) clk = ~clk;
end
//--------------------------------------------------------------------------
// Reset Generation and Initial IRQ/TE Driving
//--------------------------------------------------------------------------
initial begin
// Initialize signals before reset
irq_if.irq = 1'b0;
irq_if.te = 1'b0;
// Reset Sequence
resetn = 1'b0; // Assert reset
`uvm_info("TB_TOP", "Reset Asserted", UVM_LOW)
repeat (5) @(posedge clk);
resetn = 1'b1; // Deassert reset
`uvm_info("TB_TOP", "Reset Deasserted", UVM_LOW)
end
//--------------------------------------------------------------------------
// Waveform Dumping
//--------------------------------------------------------------------------
`ifdef DUMP_WAVES
initial begin
$fsdbDumpfile($sformatf("./sim_work/waves/%s.fsdb", "kelvin_base_test"));
$fsdbDumpvars(0, kelvin_tb_top, "+mda");
`uvm_info("TB_TOP",
$sformatf("FSDB Waveform Dumping Enabled to: %s",
$sformatf("./sim_work/waves/%s.fsdb", "kelvin_base_test")),
UVM_LOW);
end
`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
// Assign virtual interface handle procedurally
rvvi_vif = u_dut.core.score.rvvi.rvviTraceBlackBox.rvvi;
// Set virtual interfaces in the config_db for the agents/test
uvm_config_db#(virtual kelvin_axi_master_if.TB_MASTER_DRIVER)::set(null,
"*.env.m_master_agent*", "vif", master_axi_if);
uvm_config_db#(virtual kelvin_axi_slave_if.TB_SLAVE_MODEL)::set(null,
"*.env.m_slave_agent*", "vif", slave_axi_if);
uvm_config_db#(virtual kelvin_irq_if.TB_IRQ_DRIVER)::set(null,
"*.env.m_irq_agent*", "vif", irq_if);
uvm_config_db#(virtual kelvin_irq_if.DUT_IRQ_PORT)::set(null,
"*", "irq_vif", irq_if);
uvm_config_db#(virtual rvviTrace #(.ILEN(32), .XLEN(32), .FLEN(32),
.VLEN(128), .NHART(1), .RETIRE(8)))::set(null,
"*.env.m_cosim_checker*", "rvvi_vif", rvvi_vif);
uvm_config_db#(virtual rvviTrace #(.ILEN(32), .XLEN(32), .FLEN(32),
.VLEN(128), .NHART(1), .RETIRE(8)))::set(null,
"*.env.m_rvvi_agent*", "rvvi_vif", rvvi_vif);
// Run the test
run_test();
end
endmodule : kelvin_tb_top