| // 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 |