From b5457e08a5f0e5f101cc11d0a7b1d34a5d083109 Mon Sep 17 00:00:00 2001 From: Csaba Kiss Date: Tue, 24 Mar 2026 11:59:55 +0000 Subject: [PATCH 1/4] [axi,dv] Initial AXI4 VIP development with passive mode support Signed-off-by: Csaba Kiss --- hw/dv/vip/axi4_vip/axi4_vip.core | 28 ++ hw/dv/vip/axi4_vip/axi4_vip_cfg.svh | 102 ++++++++ hw/dv/vip/axi4_vip/axi4_vip_defines.svh | 16 ++ hw/dv/vip/axi4_vip/axi4_vip_driver.svh | 49 ++++ hw/dv/vip/axi4_vip/axi4_vip_env.svh | 47 ++++ hw/dv/vip/axi4_vip/axi4_vip_if.sv | 159 ++++++++++++ hw/dv/vip/axi4_vip/axi4_vip_item.svh | 160 ++++++++++++ hw/dv/vip/axi4_vip/axi4_vip_master_agent.svh | 55 ++++ hw/dv/vip/axi4_vip/axi4_vip_monitor.svh | 260 +++++++++++++++++++ hw/dv/vip/axi4_vip/axi4_vip_pkg.sv | 33 +++ hw/dv/vip/axi4_vip/axi4_vip_sequencer.svh | 25 ++ hw/dv/vip/axi4_vip/axi4_vip_slave_agent.svh | 56 ++++ hw/dv/vip/axi4_vip/axi4_vip_types.svh | 19 ++ hw/dv/vip/axi4_vip/uvc/axi4_vip_item.svh | 148 +++++++++++ 14 files changed, 1157 insertions(+) create mode 100644 hw/dv/vip/axi4_vip/axi4_vip.core create mode 100644 hw/dv/vip/axi4_vip/axi4_vip_cfg.svh create mode 100644 hw/dv/vip/axi4_vip/axi4_vip_defines.svh create mode 100644 hw/dv/vip/axi4_vip/axi4_vip_driver.svh create mode 100644 hw/dv/vip/axi4_vip/axi4_vip_env.svh create mode 100644 hw/dv/vip/axi4_vip/axi4_vip_if.sv create mode 100644 hw/dv/vip/axi4_vip/axi4_vip_item.svh create mode 100644 hw/dv/vip/axi4_vip/axi4_vip_master_agent.svh create mode 100644 hw/dv/vip/axi4_vip/axi4_vip_monitor.svh create mode 100644 hw/dv/vip/axi4_vip/axi4_vip_pkg.sv create mode 100644 hw/dv/vip/axi4_vip/axi4_vip_sequencer.svh create mode 100644 hw/dv/vip/axi4_vip/axi4_vip_slave_agent.svh create mode 100644 hw/dv/vip/axi4_vip/axi4_vip_types.svh create mode 100644 hw/dv/vip/axi4_vip/uvc/axi4_vip_item.svh diff --git a/hw/dv/vip/axi4_vip/axi4_vip.core b/hw/dv/vip/axi4_vip/axi4_vip.core new file mode 100644 index 000000000..8dd8bbcc8 --- /dev/null +++ b/hw/dv/vip/axi4_vip/axi4_vip.core @@ -0,0 +1,28 @@ +CAPI=2: +# Copyright lowRISC contributors (COSMIC project). +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +name : lowrisc:dv:axi4_vip:0.1 + +filesets: + files_dv: + files: + - axi4_vip_if.sv + - axi4_vip_pkg.sv + - axi4_vip_defines.svh: {is_include_file: true} + - axi4_vip_types.svh: {is_include_file: true} + - axi4_vip_cfg.svh: {is_include_file: true} + - axi4_vip_item.svh: {is_include_file: true} + - axi4_vip_driver.svh: {is_include_file: true} + - axi4_vip_sequencer.svh: {is_include_file: true} + - axi4_vip_monitor.svh: {is_include_file: true} + - axi4_vip_master_agent.svh: {is_include_file: true} + - axi4_vip_slave_agent.svh: {is_include_file: true} + - axi4_vip_env.svh: {is_include_file: true} + file_type: systemVerilogSource + +targets: + default: + filesets: + - files_dv \ No newline at end of file diff --git a/hw/dv/vip/axi4_vip/axi4_vip_cfg.svh b/hw/dv/vip/axi4_vip/axi4_vip_cfg.svh new file mode 100644 index 000000000..21ab7079c --- /dev/null +++ b/hw/dv/vip/axi4_vip/axi4_vip_cfg.svh @@ -0,0 +1,102 @@ +// Copyright lowRISC contributors (COSMIC project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +`ifndef AXI4_VIP_CFG_SVH +`define AXI4_VIP_CFG_SVH + +class axi4_vip_cfg extends uvm_object; + + // Currently this is a passive VIP (monitor only) + string m_inst_id = "AXI4"; + bit m_has_master = 0; + uvm_active_passive_enum m_master_active_passive = UVM_PASSIVE; + bit m_has_slave = 0; + uvm_active_passive_enum m_slave_active_passive = UVM_PASSIVE; + + // Future placeholders + bit m_has_coverage = 1; + bit m_has_checker = 1; + + // actual bus widths (<= max defines) + int unsigned m_id_width = 16; + int unsigned m_addr_width = 64; + int unsigned m_data_width = 512; + int unsigned m_user_width = 32; + int unsigned m_region_width = 8; + int unsigned m_qos_width = 8; + + `uvm_object_utils_begin(axi4_vip_cfg) + `uvm_field_string( m_inst_id, UVM_DEFAULT | UVM_STRING) + `uvm_field_int ( m_has_master, UVM_DEFAULT) + `uvm_field_int ( m_has_slave, UVM_DEFAULT) + `uvm_field_int ( m_has_coverage, UVM_DEFAULT) + `uvm_field_int ( m_has_checker, UVM_DEFAULT) + `uvm_field_enum (uvm_active_passive_enum, m_master_active_passive, UVM_DEFAULT) + `uvm_field_enum (uvm_active_passive_enum, m_slave_active_passive, UVM_DEFAULT) + `uvm_field_int ( m_id_width, UVM_DEFAULT) + `uvm_field_int ( m_addr_width, UVM_DEFAULT) + `uvm_field_int ( m_data_width, UVM_DEFAULT) + `uvm_field_int ( m_user_width, UVM_DEFAULT) + `uvm_field_int ( m_region_width, UVM_DEFAULT) + `uvm_field_int ( m_qos_width, UVM_DEFAULT) + `uvm_object_utils_end + + // External Method Declarations + extern function new(string name="axi4_vip_cfg"); + + extern virtual function void set_config(string inst_id = "", + bit has_master = 0, + uvm_active_passive_enum master_active_passive = UVM_PASSIVE, + bit has_slave = 0, + uvm_active_passive_enum slave_active_passive = UVM_PASSIVE, + bit has_coverage = 0, + bit has_checker = 0, + int unsigned id_width = 16, + int unsigned addr_width = 64, + int unsigned data_width = 512, + int unsigned user_width = 32, + int unsigned region_width = 8, + int unsigned qos_width = 8 + ); + +endclass : axi4_vip_cfg + +//------------------------------------------------------------------------------ +// External Method Implementations +//------------------------------------------------------------------------------ + +function axi4_vip_cfg::new(string name="axi4_vip_cfg"); + super.new(name); +endfunction : new + +function void axi4_vip_cfg::set_config(string inst_id = "", + bit has_master = 0, + uvm_active_passive_enum master_active_passive = UVM_PASSIVE, + bit has_slave = 0, + uvm_active_passive_enum slave_active_passive = UVM_PASSIVE, + bit has_coverage = 0, + bit has_checker = 0, + int unsigned id_width = 16, + int unsigned addr_width = 64, + int unsigned data_width = 512, + int unsigned user_width = 32, + int unsigned region_width = 8, + int unsigned qos_width = 8 + ); + m_inst_id = inst_id; + m_has_master = has_master; + m_master_active_passive = master_active_passive; + m_has_slave = has_slave; + m_slave_active_passive = slave_active_passive; + m_has_coverage = has_coverage; + m_has_checker = has_checker; + m_id_width = id_width; + m_addr_width = addr_width; + m_data_width = data_width; + m_user_width = user_width; + m_region_width = region_width; + m_qos_width = qos_width; +endfunction : set_config + +`endif // AXI4_VIP_CFG_SVH \ No newline at end of file diff --git a/hw/dv/vip/axi4_vip/axi4_vip_defines.svh b/hw/dv/vip/axi4_vip/axi4_vip_defines.svh new file mode 100644 index 000000000..16f71e3e7 --- /dev/null +++ b/hw/dv/vip/axi4_vip/axi4_vip_defines.svh @@ -0,0 +1,16 @@ +// Copyright lowRISC contributors (COSMIC project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +`ifndef AXI4_VIP_DEFINES_SVH +`define AXI4_VIP_DEFINES_SVH + +// maximum supported bus widths +`define AXI4_MAX_ID_WIDTH 16 +`define AXI4_MAX_ADDR_WIDTH 64 +`define AXI4_MAX_DATA_WIDTH 512 +`define AXI4_MAX_USER_WIDTH 32 +`define AXI4_MAX_REGION_WIDTH 8 +`define AXI4_MAX_QOS_WIDTH 8 + +`endif // AXI4_VIP_DEFINES_SVH \ No newline at end of file diff --git a/hw/dv/vip/axi4_vip/axi4_vip_driver.svh b/hw/dv/vip/axi4_vip/axi4_vip_driver.svh new file mode 100644 index 000000000..8cf4f7e31 --- /dev/null +++ b/hw/dv/vip/axi4_vip/axi4_vip_driver.svh @@ -0,0 +1,49 @@ +// Copyright lowRISC contributors (COSMIC project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +`ifndef AXI4_VIP_DRIVER_SVH +`define AXI4_VIP_DRIVER_SVH + +class axi4_vip_driver extends uvm_driver #(axi4_vip_item); + + `uvm_component_utils(axi4_vip_driver) + + axi4_vip_cfg m_cfg; + virtual axi4_vip_if vif; + + // External Method Declarations + extern function new(string name, uvm_component parent); + extern function void build_phase(uvm_phase phase); + extern task run_phase(uvm_phase phase); + +endclass : axi4_vip_driver + +//------------------------------------------------------------------------------ +// External Method Implementations +//------------------------------------------------------------------------------ + +function axi4_vip_driver::new(string name, uvm_component parent); + super.new(name, parent); +endfunction : new + +function void axi4_vip_driver::build_phase(uvm_phase phase); + super.build_phase(phase); + if (! uvm_config_db #(axi4_vip_cfg)::get(this, "", "m_cfg", m_cfg)) begin + `uvm_fatal("NOCFG", {"Configuration item must be set for: ", get_full_name(), "m_cfg"}) + end + + if (! uvm_config_db #(virtual interface axi4_vip_if)::get(this, get_full_name(),"vif", vif)) begin + `uvm_fatal("NOVIF",{"virtual interface must be set for: ",get_full_name(),".vif"}) + end +endfunction : build_phase + +task axi4_vip_driver::run_phase(uvm_phase phase); + forever begin + // TODO: Placeholder + seq_item_port.get_next_item(req); + seq_item_port.item_done(); + end +endtask : run_phase + +`endif // AXI4_VIP_DRIVER_SVH \ No newline at end of file diff --git a/hw/dv/vip/axi4_vip/axi4_vip_env.svh b/hw/dv/vip/axi4_vip/axi4_vip_env.svh new file mode 100644 index 000000000..9fa8ec480 --- /dev/null +++ b/hw/dv/vip/axi4_vip/axi4_vip_env.svh @@ -0,0 +1,47 @@ +// Copyright lowRISC contributors (COSMIC project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +`ifndef AXI4_VIP_ENV_SVH +`define AXI4_VIP_ENV_SVH + +class axi4_vip_env extends uvm_env; + + `uvm_component_utils(axi4_vip_env) + + axi4_vip_cfg m_cfg; + + axi4_vip_master_agent m_master; + axi4_vip_slave_agent m_slave; + + // External Method Declarations + extern function new(string name, uvm_component parent); + extern function void build_phase(uvm_phase phase); + +endclass : axi4_vip_env + +//------------------------------------------------------------------------------ +// External Method Implementations +//------------------------------------------------------------------------------ + +function axi4_vip_env::new(string name, uvm_component parent); + super.new(name, parent); +endfunction : new + +function void axi4_vip_env::build_phase(uvm_phase phase); + super.build_phase(phase); + + if (! uvm_config_db #(axi4_vip_cfg)::get(this, "", "m_cfg", m_cfg)) begin + `uvm_fatal("NOCFG", {"Configuration item must be set for: ", get_full_name(), "m_cfg"}) + end + + if (m_cfg.m_has_master == 1) begin + m_master = axi4_vip_master_agent::type_id::create("m_master", this); + end + + if (m_cfg.m_has_slave == 1) begin + m_slave = axi4_vip_slave_agent::type_id::create("m_slave", this); + end +endfunction : build_phase + +`endif // AXI4_VIP_ENV_SVH \ No newline at end of file diff --git a/hw/dv/vip/axi4_vip/axi4_vip_if.sv b/hw/dv/vip/axi4_vip/axi4_vip_if.sv new file mode 100644 index 000000000..d5fada553 --- /dev/null +++ b/hw/dv/vip/axi4_vip/axi4_vip_if.sv @@ -0,0 +1,159 @@ +// Copyright lowRISC contributors (COSMIC project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +`ifndef __AXI4_VIP_IF_SV +`define __AXI4_VIP_IF_SV + +interface axi4_vip_if #( + parameter int ID_WIDTH = 4, + parameter int ADDR_WIDTH = 32, + parameter int DATA_WIDTH = 64, + parameter int USER_WIDTH = 8, + parameter int REGION_WIDTH = 4, + parameter int QOS_WIDTH = 4 +)( + input logic aclk, + input logic aresetn +); + + // ========================================================= + // write address channel + // ========================================================= + logic awvalid; + logic awready; + logic [ID_WIDTH-1:0] awid; + logic [ADDR_WIDTH-1:0] awaddr; + logic [7:0] awlen; + logic [2:0] awsize; + logic [1:0] awburst; + logic awlock; + logic [3:0] awcache; + logic [2:0] awprot; + logic [QOS_WIDTH-1:0] awqos; + logic [REGION_WIDTH-1:0] awregion; + logic [USER_WIDTH-1:0] awuser; + + // ========================================================= + // write data channel + // ========================================================= + logic wvalid; + logic wready; + logic [DATA_WIDTH-1:0] wdata; + logic [(DATA_WIDTH/8)-1:0] wstrb; + logic wlast; + logic [USER_WIDTH-1:0] wuser; + + // ========================================================= + // write response channel + // ========================================================= + logic bvalid; + logic bready; + logic [ID_WIDTH-1:0] bid; + logic [1:0] bresp; + logic [USER_WIDTH-1:0] buser; + + // ========================================================= + // read address channel + // ========================================================= + logic arvalid; + logic arready; + logic [ID_WIDTH-1:0] arid; + logic [ADDR_WIDTH-1:0] araddr; + logic [7:0] arlen; + logic [2:0] arsize; + logic [1:0] arburst; + logic arlock; + logic [3:0] arcache; + logic [2:0] arprot; + logic [QOS_WIDTH-1:0] arqos; + logic [REGION_WIDTH-1:0] arregion; + logic [USER_WIDTH-1:0] aruser; + + // ========================================================= + // read data channel + // ========================================================= + logic rvalid; + logic rready; + logic [ID_WIDTH-1:0] rid; + logic [DATA_WIDTH-1:0] rdata; + logic [1:0] rresp; + logic rlast; + logic [USER_WIDTH-1:0] ruser; + + // master clocking block + clocking master_cb @(posedge aclk); + + // write address + output awvalid, awid, awaddr, awlen, awsize, awburst; + output awlock, awcache, awprot, awqos, awregion, awuser; + input awready; + + // write data + output wvalid, wdata, wstrb, wlast, wuser; + input wready; + + // write response + input bvalid, bid, bresp, buser; + output bready; + + // read address + output arvalid, arid, araddr, arlen, arsize, arburst; + output arlock, arcache, arprot, arqos, arregion, aruser; + input arready; + + // read data + input rvalid, rid, rdata, rresp, rlast, ruser; + output rready; + + endclocking + + + // slave clocking block + clocking slave_cb @(posedge aclk); + + // write address + input awvalid, awid, awaddr, awlen, awsize, awburst; + input awlock, awcache, awprot, awqos, awregion, awuser; + output awready; + + // write data + input wvalid, wdata, wstrb, wlast, wuser; + output wready; + + // write response + output bvalid, bid, bresp, buser; + input bready; + + // read address + input arvalid, arid, araddr, arlen, arsize, arburst; + input arlock, arcache, arprot, arqos, arregion, aruser; + output arready; + + // read data + output rvalid, rid, rdata, rresp, rlast, ruser; + input rready; + + endclocking + + + // monitor clocking block + clocking monitor_cb @(posedge aclk); + + input awvalid, awready, awid, awaddr, awlen, awsize, awburst; + input awlock, awcache, awprot, awqos, awregion, awuser; + + input wvalid, wready, wdata, wstrb, wlast, wuser; + + input bvalid, bready, bid, bresp, buser; + + input arvalid, arready, arid, araddr, arlen, arsize, arburst; + input arlock, arcache, arprot, arqos, arregion, aruser; + + input rvalid, rready, rid, rdata, rresp, rlast, ruser; + + endclocking + +endinterface + +`endif // __AXI4_VIP_IF_SV \ No newline at end of file diff --git a/hw/dv/vip/axi4_vip/axi4_vip_item.svh b/hw/dv/vip/axi4_vip/axi4_vip_item.svh new file mode 100644 index 000000000..6af4b917a --- /dev/null +++ b/hw/dv/vip/axi4_vip/axi4_vip_item.svh @@ -0,0 +1,160 @@ +// Copyright lowRISC contributors (COSMIC project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +`ifndef axi4_vip_item_SVH +`define axi4_vip_item_SVH + +class axi4_vip_item extends uvm_sequence_item; + + // observation type + axi_obs_t obs_kind; + axi_dir_t dir; + + // ------------------------------------------------- + // Write Address Channel + // ------------------------------------------------- + rand bit [`AXI4_MAX_ID_WIDTH-1:0] awid; + rand bit [`AXI4_MAX_ADDR_WIDTH-1:0] awaddr; + rand bit [7:0] awlen; + rand bit [2:0] awsize; + rand bit [1:0] awburst; + rand bit awlock; + rand bit [3:0] awcache; + rand bit [2:0] awprot; + rand bit [`AXI4_MAX_QOS_WIDTH-1:0] awqos; + rand bit [`AXI4_MAX_REGION_WIDTH-1:0] awregion; + rand bit [`AXI4_MAX_USER_WIDTH-1:0] awuser; + + // ------------------------------------------------- + // Write Data Channel (Queues for multi-beat bursts) + // ------------------------------------------------- + rand bit [`AXI4_MAX_DATA_WIDTH-1:0] wdata[$]; + rand bit [`AXI4_MAX_DATA_WIDTH/8-1:0] wstrb[$]; + rand bit wlast[$]; + rand bit [`AXI4_MAX_USER_WIDTH-1:0] wuser[$]; + + // ------------------------------------------------- + // Write Response Channel + // ------------------------------------------------- + rand bit [`AXI4_MAX_ID_WIDTH-1:0] bid; + rand bit [1:0] bresp; + rand bit [`AXI4_MAX_USER_WIDTH-1:0] buser; + + // ------------------------------------------------- + // Read Address Channel + // ------------------------------------------------- + rand bit [`AXI4_MAX_ID_WIDTH-1:0] arid; + rand bit [`AXI4_MAX_ADDR_WIDTH-1:0] araddr; + rand bit [7:0] arlen; + rand bit [2:0] arsize; + rand bit [1:0] arburst; + rand bit arlock; + rand bit [3:0] arcache; + rand bit [2:0] arprot; + rand bit [`AXI4_MAX_QOS_WIDTH-1:0] arqos; + rand bit [`AXI4_MAX_REGION_WIDTH-1:0] arregion; + rand bit [`AXI4_MAX_USER_WIDTH-1:0] aruser; + + // ------------------------------------------------- + // Read Data Channel + // ------------------------------------------------- + rand bit [`AXI4_MAX_ID_WIDTH-1:0] rid; + rand bit [`AXI4_MAX_DATA_WIDTH-1:0] rdata[$]; + rand bit [1:0] rresp[$]; + rand bit rlast[$]; + rand bit [`AXI4_MAX_USER_WIDTH-1:0] ruser[$]; + + // ------------------------------------------------- + // UVM Automation Macros + // ------------------------------------------------- + `uvm_object_utils_begin(axi4_vip_item) + `uvm_field_enum(axi_obs_t, obs_kind, UVM_ALL_ON) + `uvm_field_enum(axi_dir_t, dir, UVM_ALL_ON) + + // Write Address + `uvm_field_int(awid, UVM_ALL_ON) + `uvm_field_int(awaddr, UVM_ALL_ON) + `uvm_field_int(awlen, UVM_ALL_ON) + `uvm_field_int(awsize, UVM_ALL_ON) + `uvm_field_int(awburst, UVM_ALL_ON) + `uvm_field_int(awlock, UVM_ALL_ON) + `uvm_field_int(awcache, UVM_ALL_ON) + `uvm_field_int(awprot, UVM_ALL_ON) + `uvm_field_int(awqos, UVM_ALL_ON) + `uvm_field_int(awregion, UVM_ALL_ON) + `uvm_field_int(awuser, UVM_ALL_ON) + + // Write Data (Queues) + `uvm_field_queue_int(wdata, UVM_ALL_ON) + `uvm_field_queue_int(wstrb, UVM_ALL_ON) + `uvm_field_queue_int(wlast, UVM_ALL_ON) + `uvm_field_queue_int(wuser, UVM_ALL_ON) + + // Write Response + `uvm_field_int(bid, UVM_ALL_ON) + `uvm_field_int(bresp, UVM_ALL_ON) + `uvm_field_int(buser, UVM_ALL_ON) + + // Read Address + `uvm_field_int(arid, UVM_ALL_ON) + `uvm_field_int(araddr, UVM_ALL_ON) + `uvm_field_int(arlen, UVM_ALL_ON) + `uvm_field_int(arsize, UVM_ALL_ON) + `uvm_field_int(arburst, UVM_ALL_ON) + `uvm_field_int(arlock, UVM_ALL_ON) + `uvm_field_int(arcache, UVM_ALL_ON) + `uvm_field_int(arprot, UVM_ALL_ON) + `uvm_field_int(arqos, UVM_ALL_ON) + `uvm_field_int(arregion, UVM_ALL_ON) + `uvm_field_int(aruser, UVM_ALL_ON) + + // Read Data (Queues) + `uvm_field_int(rid, UVM_ALL_ON) + `uvm_field_queue_int(rdata, UVM_ALL_ON) + `uvm_field_queue_int(rresp, UVM_ALL_ON) + `uvm_field_queue_int(rlast, UVM_ALL_ON) + `uvm_field_queue_int(ruser, UVM_ALL_ON) + `uvm_object_utils_end + + // ------------------------------------------------- + // Constraints (Stay inside class) + // ------------------------------------------------- + constraint c_awlen_wdata_match { + if (dir == AXI_WRITE) { + wdata.size() == (awlen + 1); + wstrb.size() == wdata.size(); + wlast.size() == wdata.size(); + foreach(wlast[i]) wlast[i] == (i == awlen); + } + } + + constraint c_arlen_rdata_match { + if (dir == AXI_READ) { + rdata.size() == (arlen + 1); + rresp.size() == rdata.size(); + rlast.size() == rdata.size(); + foreach(rlast[i]) rlast[i] == (i == arlen); + } + } + + // External Method Declarations + extern function new(string name="axi4_vip_item"); + extern virtual function axi4_vip_item clone(); + +endclass : axi4_vip_item + +//------------------------------------------------------------------------------ +// External Method Implementations +//------------------------------------------------------------------------------ + +function axi4_vip_item::new(string name="axi4_vip_item"); + super.new(name); +endfunction : new + +function axi4_vip_item axi4_vip_item::clone(); + clone = new(); + clone.copy(this); +endfunction : clone + +`endif // axi4_vip_item_SVH \ No newline at end of file diff --git a/hw/dv/vip/axi4_vip/axi4_vip_master_agent.svh b/hw/dv/vip/axi4_vip/axi4_vip_master_agent.svh new file mode 100644 index 000000000..8cdf637e4 --- /dev/null +++ b/hw/dv/vip/axi4_vip/axi4_vip_master_agent.svh @@ -0,0 +1,55 @@ +// Copyright lowRISC contributors (COSMIC project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +`ifndef __AXI4_VIP_MASTER_AGENT_SVH +`define __AXI4_VIP_MASTER_AGENT_SVH + +class axi4_vip_master_agent extends uvm_agent; + + `uvm_component_utils(axi4_vip_master_agent) + + axi4_vip_cfg m_cfg; + + axi4_vip_monitor m_monitor; + axi4_vip_driver m_driver; + axi4_vip_sequencer m_sequencer; + + // External Method Declarations + extern function new(string name, uvm_component parent); + extern function void build_phase(uvm_phase phase); + extern function void connect_phase(uvm_phase phase); + +endclass : axi4_vip_master_agent + +//------------------------------------------------------------------------------ +// External Method Implementations +//------------------------------------------------------------------------------ + +function axi4_vip_master_agent::new(string name, uvm_component parent); + super.new(name, parent); +endfunction : new + +function void axi4_vip_master_agent::build_phase(uvm_phase phase); + super.build_phase(phase); + + if(!uvm_config_db#(axi4_vip_cfg)::get(this,"","m_cfg",m_cfg)) begin + `uvm_fatal("NOCFG", {"Configuration item must be set for: ", get_full_name(), "m_cfg"}) + end + + m_monitor = axi4_vip_monitor::type_id::create("m_monitor", this); + + if(m_cfg.m_master_active_passive == UVM_ACTIVE) begin + m_driver = axi4_vip_driver ::type_id::create("m_driver", this); + m_sequencer = axi4_vip_sequencer::type_id::create("m_sequencer", this); + end +endfunction : build_phase + +function void axi4_vip_master_agent::connect_phase(uvm_phase phase); + // Future placeholder, UVM_ACTIVE is not supported + if(m_cfg.m_master_active_passive == UVM_ACTIVE) begin + m_driver.seq_item_port.connect(m_sequencer.seq_item_export); + end +endfunction : connect_phase + +`endif // __AXI4_VIP_MASTER_AGENT_SVH \ No newline at end of file diff --git a/hw/dv/vip/axi4_vip/axi4_vip_monitor.svh b/hw/dv/vip/axi4_vip/axi4_vip_monitor.svh new file mode 100644 index 000000000..255c831ae --- /dev/null +++ b/hw/dv/vip/axi4_vip/axi4_vip_monitor.svh @@ -0,0 +1,260 @@ +// Copyright lowRISC contributors (COSMIC project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +`ifndef __AXI4_VIP_MONITOR_SVH +`define __AXI4_VIP_MONITOR_SVH + +class axi4_vip_monitor extends uvm_monitor; + + `uvm_component_utils(axi4_vip_monitor) + + axi4_vip_cfg m_cfg; + virtual axi4_vip_if vif; + + // Analysis Ports + uvm_analysis_port #(uvm_sequence_item) aw_ap; + uvm_analysis_port #(uvm_sequence_item) w_ap; + uvm_analysis_port #(uvm_sequence_item) ar_ap; + uvm_analysis_port #(uvm_sequence_item) r_ap; + uvm_analysis_port #(uvm_sequence_item) tx_ap; + + // --- Internal Storage --- + axi4_vip_item aw_pending_q[$]; + axi4_vip_item w_pending_q[$]; + + // ID-indexed queues to pair Request with Response + axi4_vip_item write_q_by_id [bit [`AXI4_MAX_ID_WIDTH-1:0]] [$]; + axi4_vip_item read_q_by_id [bit [`AXI4_MAX_ID_WIDTH-1:0]] [$]; + + // Process handles for granular thread control + local process aw_proc, w_proc, b_proc, ar_proc, r_proc; + + // External Method Declarations + extern function new(string name, uvm_component parent); + extern function void build_phase(uvm_phase phase); + extern task run_phase(uvm_phase phase); + extern function void stop_processes(); + extern function void cleanup_queues(); + extern task collect_aw_channel(); + extern task collect_w_channel(); + extern task collect_b_channel(); + extern task collect_ar_channel(); + extern task collect_r_channel(); + extern function void merge_tx(axi4_vip_item req, axi4_vip_item data); + +endclass : axi4_vip_monitor + +//------------------------------------------------------------------------------ +// External Method Implementations +//------------------------------------------------------------------------------ + +function axi4_vip_monitor::new(string name, uvm_component parent); + super.new(name, parent); +endfunction : new + +function void axi4_vip_monitor::build_phase(uvm_phase phase); + super.build_phase(phase); + if (!uvm_config_db #(axi4_vip_cfg)::get(this, "", "m_cfg", m_cfg)) begin + `uvm_fatal("NOCFG", {"Configuration item must be set for: ", get_full_name(), "m_cfg"}) + end + if (!uvm_config_db #(virtual axi4_vip_if)::get(this, get_full_name(), "vif", vif)) begin + `uvm_fatal("NOVIF",{"virtual interface must be set for: ",get_full_name(),".vif"}) + end + + aw_ap = new("aw_ap", this); + w_ap = new("w_ap", this); + ar_ap = new("ar_ap", this); + r_ap = new("r_ap", this); + tx_ap = new("tx_ap", this); +endfunction : build_phase + +task axi4_vip_monitor::run_phase(uvm_phase phase); + forever begin + wait(vif.aresetn === 1'b1); + + fork + begin aw_proc = process::self(); collect_aw_channel(); end + begin w_proc = process::self(); collect_w_channel(); end + begin b_proc = process::self(); collect_b_channel(); end + begin ar_proc = process::self(); collect_ar_channel(); end + begin r_proc = process::self(); collect_r_channel(); end + join_none + + wait(vif.aresetn === 1'b0); + stop_processes(); + cleanup_queues(); + end +endtask : run_phase + +function void axi4_vip_monitor::stop_processes(); + process p_list[$] = {aw_proc, w_proc, b_proc, ar_proc, r_proc}; + foreach (p_list[i]) begin + if (p_list[i] != null) p_list[i].kill(); + end +endfunction : stop_processes + +function void axi4_vip_monitor::cleanup_queues(); + aw_pending_q.delete(); + w_pending_q.delete(); + write_q_by_id.delete(); + read_q_by_id.delete(); +endfunction : cleanup_queues + +task axi4_vip_monitor::collect_aw_channel(); + forever begin + @(vif.monitor_cb); + if(vif.monitor_cb.awvalid && vif.monitor_cb.awready) begin + axi4_vip_item tr = axi4_vip_item::type_id::create("aw_tr"); + tr.dir = AXI_WRITE; + tr.obs_kind = AXI_AW_CH; + + tr.awid = vif.monitor_cb.awid & ((64'h1 << m_cfg.m_id_width) - 1); + tr.awaddr = vif.monitor_cb.awaddr & ((64'h1 << m_cfg.m_addr_width) - 1); + tr.awuser = vif.monitor_cb.awuser & ((64'h1 << m_cfg.m_user_width) - 1); + tr.awregion = vif.monitor_cb.awregion & ((64'h1 << m_cfg.m_region_width) - 1); + tr.awqos = vif.monitor_cb.awqos & ((64'h1 << m_cfg.m_qos_width) - 1); + + tr.awlen = vif.monitor_cb.awlen; + tr.awsize = vif.monitor_cb.awsize; + tr.awburst = vif.monitor_cb.awburst; + tr.awlock = vif.monitor_cb.awlock; + tr.awcache = vif.monitor_cb.awcache; + tr.awprot = vif.monitor_cb.awprot; + + if (w_pending_q.size() > 0) begin + axi4_vip_item w_tr = w_pending_q.pop_front(); + merge_tx(tr, w_tr); + write_q_by_id[tr.awid].push_back(w_tr); + end else begin + aw_pending_q.push_back(tr); + end + `uvm_info(get_full_name(), $sformatf("AW transaction collected: %s", tr.sprint()), UVM_MEDIUM) + aw_ap.write(tr.clone()); + end + end +endtask : collect_aw_channel + +task axi4_vip_monitor::collect_w_channel(); + axi4_vip_item current_burst; + forever begin + @(vif.monitor_cb); + if(vif.monitor_cb.wvalid && vif.monitor_cb.wready) begin + if (current_burst == null) current_burst = axi4_vip_item::type_id::create("w_burst"); + + current_burst.dir = AXI_WRITE; + current_burst.wdata.push_back(vif.monitor_cb.wdata & ((64'h1 << m_cfg.m_data_width) - 1)); + current_burst.wstrb.push_back(vif.monitor_cb.wstrb & ((64'h1 << (m_cfg.m_data_width/8)) - 1)); + current_burst.wuser.push_back(vif.monitor_cb.wuser & ((64'h1 << m_cfg.m_user_width) - 1)); + current_burst.wlast.push_back(vif.monitor_cb.wlast); + + if (vif.monitor_cb.wlast) begin + if (aw_pending_q.size() > 0) begin + axi4_vip_item aw_tr = aw_pending_q.pop_front(); + merge_tx(aw_tr, current_burst); + write_q_by_id[aw_tr.awid].push_back(current_burst); + end else begin + w_pending_q.push_back(current_burst); + end + current_burst.obs_kind = AXI_W_CH; + `uvm_info(get_full_name(), $sformatf("W transaction collected: %s", current_burst.sprint()), UVM_MEDIUM) + w_ap.write(current_burst.clone()); + current_burst = null; + end + end + end +endtask : collect_w_channel + +task axi4_vip_monitor::collect_b_channel(); + bit [`AXI4_MAX_ID_WIDTH-1:0] id; + forever begin + @(vif.monitor_cb); + if(vif.monitor_cb.bvalid && vif.monitor_cb.bready) begin + id = vif.monitor_cb.bid & ((64'h1 << m_cfg.m_id_width) - 1); + if(write_q_by_id.exists(id) && write_q_by_id[id].size() > 0) begin + axi4_vip_item tr = write_q_by_id[id].pop_front(); + tr.obs_kind = AXI_FULL_WRITE_TR; + tr.bid = id; + tr.bresp = vif.monitor_cb.bresp; + tr.buser = vif.monitor_cb.buser & ((64'h1 << m_cfg.m_user_width) - 1); + `uvm_info(get_full_name(), $sformatf("FULL AXI4 write transaction collected: %s", tr.sprint()), UVM_LOW) + tx_ap.write(tr.clone()); + end else begin + `uvm_error("MON_B", $sformatf("B-Response for unexpected ID: %0h", id)) + end + end + end +endtask : collect_b_channel + +task axi4_vip_monitor::collect_ar_channel(); + forever begin + @(vif.monitor_cb); + if(vif.monitor_cb.arvalid && vif.monitor_cb.arready) begin + axi4_vip_item tr = axi4_vip_item::type_id::create("ar_tr"); + tr.dir = AXI_READ; + tr.obs_kind = AXI_AR_CH; + + tr.arid = vif.monitor_cb.arid & ((64'h1 << m_cfg.m_id_width) - 1); + tr.araddr = vif.monitor_cb.araddr & ((64'h1 << m_cfg.m_addr_width) - 1); + tr.aruser = vif.monitor_cb.aruser & ((64'h1 << m_cfg.m_user_width) - 1); + tr.arregion = vif.monitor_cb.arregion & ((64'h1 << m_cfg.m_region_width) - 1); + tr.arqos = vif.monitor_cb.arqos & ((64'h1 << m_cfg.m_qos_width) - 1); + + tr.arlen = vif.monitor_cb.arlen; + tr.arsize = vif.monitor_cb.arsize; + tr.arburst = vif.monitor_cb.arburst; + tr.arlock = vif.monitor_cb.arlock; + tr.arcache = vif.monitor_cb.arcache; + tr.arprot = vif.monitor_cb.arprot; + + read_q_by_id[tr.arid].push_back(tr); + `uvm_info(get_full_name(), $sformatf("AR transaction collected: %s", tr.sprint()), UVM_MEDIUM) + ar_ap.write(tr.clone()); + end + end +endtask : collect_ar_channel + +task axi4_vip_monitor::collect_r_channel(); + bit [`AXI4_MAX_ID_WIDTH-1:0] id; + forever begin + @(vif.monitor_cb); + if(vif.monitor_cb.rvalid && vif.monitor_cb.rready) begin + id = vif.monitor_cb.rid & ((64'h1 << m_cfg.m_id_width) - 1); + if(read_q_by_id.exists(id) && read_q_by_id[id].size() > 0) begin + axi4_vip_item tr = read_q_by_id[id][0]; // Peek + tr.rid = id; + tr.rdata.push_back(vif.monitor_cb.rdata & ((64'h1 << m_cfg.m_data_width) - 1)); + tr.rresp.push_back(vif.monitor_cb.rresp); + tr.ruser.push_back(vif.monitor_cb.ruser & ((64'h1 << m_cfg.m_user_width) - 1)); + + if(vif.monitor_cb.rlast) begin + void'(read_q_by_id[id].pop_front()); + tr.obs_kind = AXI_FULL_READ_TR; + `uvm_info(get_full_name(), $sformatf("FULL AXI4 Read transaction collected: %s", tr.sprint()), UVM_LOW) + tx_ap.write(tr.clone()); + end + tr.obs_kind = AXI_R_CH; + `uvm_info(get_full_name(), $sformatf("R transaction collected: %s", tr.sprint()), UVM_MEDIUM) + r_ap.write(tr.clone()); + end else begin + `uvm_error("MON_R", $sformatf("R-Data for unexpected ID: %0h", id)) + end + end + end +endtask : collect_r_channel + +function void axi4_vip_monitor::merge_tx(axi4_vip_item req, axi4_vip_item data); + data.awid = req.awid; + data.awaddr = req.awaddr; + data.awlen = req.awlen; + data.awsize = req.awsize; + data.awburst = req.awburst; + data.awlock = req.awlock; + data.awcache = req.awcache; + data.awprot = req.awprot; + data.awqos = req.awqos; + data.awregion = req.awregion; + data.awuser = req.awuser; +endfunction : merge_tx + +`endif // __AXI4_VIP_MONITOR_SVH \ No newline at end of file diff --git a/hw/dv/vip/axi4_vip/axi4_vip_pkg.sv b/hw/dv/vip/axi4_vip/axi4_vip_pkg.sv new file mode 100644 index 000000000..3f438ccac --- /dev/null +++ b/hw/dv/vip/axi4_vip/axi4_vip_pkg.sv @@ -0,0 +1,33 @@ +// Copyright lowRISC contributors (COSMIC project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +`ifndef __AXI4_VIP_PKG_SV +`define __AXI4_VIP_PKG_SV + +`include "axi4_vip_if.sv" + +package axi4_vip_pkg; + + import uvm_pkg::*; + `include "uvm_macros.svh" + + `include "axi4_vip_defines.svh" + `include "axi4_vip_types.svh" + + `include "axi4_vip_cfg.svh" + + `include "axi4_vip_item.svh" + + `include "axi4_vip_driver.svh" + `include "axi4_vip_sequencer.svh" + + `include "axi4_vip_monitor.svh" + `include "axi4_vip_master_agent.svh" + `include "axi4_vip_slave_agent.svh" + + `include "axi4_vip_env.svh" + +endpackage + +`endif // __AXI4_VIP_PKG_SV \ No newline at end of file diff --git a/hw/dv/vip/axi4_vip/axi4_vip_sequencer.svh b/hw/dv/vip/axi4_vip/axi4_vip_sequencer.svh new file mode 100644 index 000000000..63060776b --- /dev/null +++ b/hw/dv/vip/axi4_vip/axi4_vip_sequencer.svh @@ -0,0 +1,25 @@ +// Copyright lowRISC contributors (COSMIC project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +`ifndef AXI4_VIP_SEQUENCER_SVH +`define AXI4_VIP_SEQUENCER_SVH + +class axi4_vip_sequencer extends uvm_sequencer #(axi4_vip_item); + + `uvm_component_utils(axi4_vip_sequencer) + + // External Method Declarations + extern function new(string name, uvm_component parent); + +endclass : axi4_vip_sequencer + +//------------------------------------------------------------------------------ +// External Method Implementations +//------------------------------------------------------------------------------ + +function axi4_vip_sequencer::new(string name, uvm_component parent); + super.new(name, parent); +endfunction : new + +`endif // AXI4_VIP_SEQUENCER_SVH \ No newline at end of file diff --git a/hw/dv/vip/axi4_vip/axi4_vip_slave_agent.svh b/hw/dv/vip/axi4_vip/axi4_vip_slave_agent.svh new file mode 100644 index 000000000..725693380 --- /dev/null +++ b/hw/dv/vip/axi4_vip/axi4_vip_slave_agent.svh @@ -0,0 +1,56 @@ +// Copyright lowRISC contributors (COSMIC project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +`ifndef __AXI4_VIP_SLAVE_AGENT_SVH +`define __AXI4_VIP_SLAVE_AGENT_SVH + +class axi4_vip_slave_agent extends uvm_agent; + + `uvm_component_utils(axi4_vip_slave_agent) + + axi4_vip_cfg m_cfg; + + axi4_vip_monitor m_monitor; + axi4_vip_driver m_driver; + axi4_vip_sequencer m_sequencer; + + // External Method Declarations + extern function new(string name, uvm_component parent); + extern function void build_phase(uvm_phase phase); + extern function void connect_phase(uvm_phase phase); + +endclass : axi4_vip_slave_agent + +//------------------------------------------------------------------------------ +// External Method Implementations +//------------------------------------------------------------------------------ + +function axi4_vip_slave_agent::new(string name, uvm_component parent); + super.new(name, parent); +endfunction : new + +function void axi4_vip_slave_agent::build_phase(uvm_phase phase); + super.build_phase(phase); + + if(!uvm_config_db#(axi4_vip_cfg)::get(this,"","m_cfg",m_cfg)) begin + `uvm_fatal("NOCFG", {"Configuration item must be set for: ", get_full_name(), "m_config"}) + end + + m_monitor = axi4_vip_monitor::type_id::create("m_monitor", this); + + // Future placeholder + if(m_cfg.m_slave_active_passive == UVM_ACTIVE) begin + m_driver = axi4_vip_driver ::type_id::create("m_driver", this); + m_sequencer = axi4_vip_sequencer::type_id::create("m_sequencer", this); + end +endfunction : build_phase + +function void axi4_vip_slave_agent::connect_phase(uvm_phase phase); + // Future placeholder, UVM_ACTIVE is not supported + if(m_cfg.m_slave_active_passive == UVM_ACTIVE) begin + m_driver.seq_item_port.connect(m_sequencer.seq_item_export); + end +endfunction : connect_phase + +`endif // __AXI4_VIP_SLAVE_AGENT_SVH \ No newline at end of file diff --git a/hw/dv/vip/axi4_vip/axi4_vip_types.svh b/hw/dv/vip/axi4_vip/axi4_vip_types.svh new file mode 100644 index 000000000..7176ed2d0 --- /dev/null +++ b/hw/dv/vip/axi4_vip/axi4_vip_types.svh @@ -0,0 +1,19 @@ +// Copyright lowRISC contributors (COSMIC project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +`ifndef AXI4_VIP_TYPES_SVH +`define AXI4_VIP_TYPES_SVH + +typedef enum {AXI_READ, AXI_WRITE} axi_dir_t; + +typedef enum { + AXI_AW_CH, + AXI_W_CH, + AXI_FULL_WRITE_TR, + AXI_AR_CH, + AXI_R_CH, + AXI_FULL_READ_TR +} axi_obs_t; + +`endif // AXI4_VIP_TYPES_SVH \ No newline at end of file diff --git a/hw/dv/vip/axi4_vip/uvc/axi4_vip_item.svh b/hw/dv/vip/axi4_vip/uvc/axi4_vip_item.svh new file mode 100644 index 000000000..4facc317d --- /dev/null +++ b/hw/dv/vip/axi4_vip/uvc/axi4_vip_item.svh @@ -0,0 +1,148 @@ +`ifndef axi4_vip_item_SVH +`define axi4_vip_item_SVH + +class axi4_vip_item extends uvm_sequence_item; + + // observation type + axi_obs_t obs_kind; + axi_dir_t dir; + + // ------------------------------------------------- + // Write Address Channel + // ------------------------------------------------- + rand bit [`AXI4_MAX_ID_WIDTH-1:0] awid; + rand bit [`AXI4_MAX_ADDR_WIDTH-1:0] awaddr; + rand bit [7:0] awlen; + rand bit [2:0] awsize; + rand bit [1:0] awburst; + rand bit awlock; + rand bit [3:0] awcache; + rand bit [2:0] awprot; + rand bit [`AXI4_MAX_QOS_WIDTH-1:0] awqos; + rand bit [`AXI4_MAX_REGION_WIDTH-1:0] awregion; + rand bit [`AXI4_MAX_USER_WIDTH-1:0] awuser; + + // ------------------------------------------------- + // Write Data Channel (Queues for multi-beat bursts) + // ------------------------------------------------- + rand bit [`AXI4_MAX_DATA_WIDTH-1:0] wdata[$]; + rand bit [`AXI4_MAX_DATA_WIDTH/8-1:0] wstrb[$]; + rand bit wlast[$]; + rand bit [`AXI4_MAX_USER_WIDTH-1:0] wuser[$]; + + // ------------------------------------------------- + // Write Response Channel + // ------------------------------------------------- + rand bit [`AXI4_MAX_ID_WIDTH-1:0] bid; + rand bit [1:0] bresp; + rand bit [`AXI4_MAX_USER_WIDTH-1:0] buser; + + // ------------------------------------------------- + // Read Address Channel + // ------------------------------------------------- + rand bit [`AXI4_MAX_ID_WIDTH-1:0] arid; + rand bit [`AXI4_MAX_ADDR_WIDTH-1:0] araddr; + rand bit [7:0] arlen; + rand bit [2:0] arsize; + rand bit [1:0] arburst; + rand bit arlock; + rand bit [3:0] arcache; + rand bit [2:0] arprot; + rand bit [`AXI4_MAX_QOS_WIDTH-1:0] arqos; + rand bit [`AXI4_MAX_REGION_WIDTH-1:0] arregion; + rand bit [`AXI4_MAX_USER_WIDTH-1:0] aruser; + + // ------------------------------------------------- + // Read Data Channel + // ------------------------------------------------- + rand bit [`AXI4_MAX_ID_WIDTH-1:0] rid; + rand bit [`AXI4_MAX_DATA_WIDTH-1:0] rdata[$]; + rand bit [1:0] rresp[$]; + rand bit rlast[$]; + rand bit [`AXI4_MAX_USER_WIDTH-1:0] ruser[$]; + + // ------------------------------------------------- + // UVM Automation Macros + // ------------------------------------------------- + `uvm_object_utils_begin(axi4_vip_item) + `uvm_field_enum(axi_obs_t, obs_kind, UVM_ALL_ON) + `uvm_field_enum(axi_dir_t, dir, UVM_ALL_ON) + + // Write Address + `uvm_field_int(awid, UVM_ALL_ON) + `uvm_field_int(awaddr, UVM_ALL_ON) + `uvm_field_int(awlen, UVM_ALL_ON) + `uvm_field_int(awsize, UVM_ALL_ON) + `uvm_field_int(awburst, UVM_ALL_ON) + `uvm_field_int(awlock, UVM_ALL_ON) + `uvm_field_int(awcache, UVM_ALL_ON) + `uvm_field_int(awprot, UVM_ALL_ON) + `uvm_field_int(awqos, UVM_ALL_ON) + `uvm_field_int(awregion, UVM_ALL_ON) + `uvm_field_int(awuser, UVM_ALL_ON) + + // Write Data (Queues) + `uvm_field_queue_int(wdata, UVM_ALL_ON) + `uvm_field_queue_int(wstrb, UVM_ALL_ON) + `uvm_field_queue_int(wlast, UVM_ALL_ON) + `uvm_field_queue_int(wuser, UVM_ALL_ON) + + // Write Response + `uvm_field_int(bid, UVM_ALL_ON) + `uvm_field_int(bresp, UVM_ALL_ON) + `uvm_field_int(buser, UVM_ALL_ON) + + // Read Address + `uvm_field_int(arid, UVM_ALL_ON) + `uvm_field_int(araddr, UVM_ALL_ON) + `uvm_field_int(arlen, UVM_ALL_ON) + `uvm_field_int(arsize, UVM_ALL_ON) + `uvm_field_int(arburst, UVM_ALL_ON) + `uvm_field_int(arlock, UVM_ALL_ON) + `uvm_field_int(arcache, UVM_ALL_ON) + `uvm_field_int(arprot, UVM_ALL_ON) + `uvm_field_int(arqos, UVM_ALL_ON) + `uvm_field_int(arregion, UVM_ALL_ON) + `uvm_field_int(aruser, UVM_ALL_ON) + + // Read Data (Queues) + `uvm_field_int(rid, UVM_ALL_ON) + `uvm_field_queue_int(rdata, UVM_ALL_ON) + `uvm_field_queue_int(rresp, UVM_ALL_ON) + `uvm_field_queue_int(rlast, UVM_ALL_ON) + `uvm_field_queue_int(ruser, UVM_ALL_ON) + `uvm_object_utils_end + + // ------------------------------------------------- + // Constraints (Basic AXI4 Sanity) + // ------------------------------------------------- + constraint c_awlen_wdata_match { + if (dir == AXI_WRITE) { + wdata.size() == (awlen + 1); + wstrb.size() == wdata.size(); + wlast.size() == wdata.size(); + foreach(wlast[i]) wlast[i] == (i == awlen); + } + } + + constraint c_arlen_rdata_match { + if (dir == AXI_READ) { + rdata.size() == (arlen + 1); + rresp.size() == rdata.size(); + rlast.size() == rdata.size(); + foreach(rlast[i]) rlast[i] == (i == arlen); + } + } + + function new(string name="axi4_vip_item"); + super.new(name); + endfunction + + virtual function axi4_vip_item clone(); + clone = new(); + clone.copy(this); + endfunction + +endclass + +`endif \ No newline at end of file From 3ad89f81e1f8a3cecbc7ebb3395e9aa77beb6769 Mon Sep 17 00:00:00 2001 From: Csaba Kiss Date: Tue, 24 Mar 2026 12:00:25 +0000 Subject: [PATCH 2/4] [top,script] Added AXI4 VIP files to the top chip simulation build Signed-off-by: Csaba Kiss --- hw/top_chip/dv/top_chip_sim.core | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/top_chip/dv/top_chip_sim.core b/hw/top_chip/dv/top_chip_sim.core index acf5c8930..bba2ceb38 100644 --- a/hw/top_chip/dv/top_chip_sim.core +++ b/hw/top_chip/dv/top_chip_sim.core @@ -25,6 +25,7 @@ filesets: - lowrisc:dv:xbar_macros - lowrisc:dv_dpi_c:uartdpi:0.1 - lowrisc:dv_dpi_sv:uartdpi:0.1 + - lowrisc:dv:axi4_vip:0.1 files: - tb/tb.sv - tb/chip_hier_macros.svh: {is_include_file: true} From a13e23c177688a5130ab9f13599b78ac5d25b12d Mon Sep 17 00:00:00 2001 From: Csaba Kiss Date: Tue, 24 Mar 2026 12:00:37 +0000 Subject: [PATCH 3/4] [top,dv] Integrated AXI4 passive VIP and added XBAR path defines Signed-off-by: Csaba Kiss --- hw/top_chip/dv/env/top_chip_dv_env.core | 2 + hw/top_chip/dv/env/top_chip_dv_env.sv | 19 ++- hw/top_chip/dv/env/top_chip_dv_env_pkg.sv | 7 + hw/top_chip/dv/tb/chip_hier_macros.svh | 1 + hw/top_chip/dv/tb/tb.sv | 192 ++++++++++++++++++++++ 5 files changed, 220 insertions(+), 1 deletion(-) diff --git a/hw/top_chip/dv/env/top_chip_dv_env.core b/hw/top_chip/dv/env/top_chip_dv_env.core index 26dddaeaf..3bd17d135 100644 --- a/hw/top_chip/dv/env/top_chip_dv_env.core +++ b/hw/top_chip/dv/env/top_chip_dv_env.core @@ -11,12 +11,14 @@ filesets: - lowrisc:dv:dv_utils - lowrisc:dv:mem_bkdr_util - lowrisc:dv:uart_agent + - lowrisc:dv:axi4_vip:0.1 - lowrisc:dv:common_ifs files: - top_chip_dv_env_pkg.sv - mem_clear_util.sv: {is_include_file: true} - top_chip_dv_env_cfg.sv: {is_include_file: true} - top_chip_dv_env_cov.sv: {is_include_file: true} + - top_chip_dv_axi_scoreboard.sv: {is_include_file: true} - top_chip_dv_env.sv: {is_include_file: true} - top_chip_dv_virtual_sequencer.sv: {is_include_file: true} - seq_lib/top_chip_dv_vseq_list.sv: {is_include_file: true} diff --git a/hw/top_chip/dv/env/top_chip_dv_env.sv b/hw/top_chip/dv/env/top_chip_dv_env.sv index 096fd6853..e34e9ed1b 100644 --- a/hw/top_chip/dv/env/top_chip_dv_env.sv +++ b/hw/top_chip/dv/env/top_chip_dv_env.sv @@ -12,7 +12,10 @@ class top_chip_dv_env extends uvm_env; mem_bkdr_util mem_bkdr_util_h[chip_mem_e]; // Agents - uart_agent m_uart_agent; + uart_agent m_uart_agent; + axi4_vip_env m_axi[]; + + top_chip_dv_axi_scoreboard m_axi_scb; // Standard SV/UVM methods extern function new(string name="", uvm_component parent=null); @@ -29,6 +32,8 @@ function top_chip_dv_env::new(string name="", uvm_component parent=null); endfunction : new function void top_chip_dv_env::build_phase(uvm_phase phase); + axi_if_t axi_name; + super.build_phase(phase); foreach (CHIP_MEM_LIST[i]) begin @@ -70,6 +75,14 @@ function void top_chip_dv_env::build_phase(uvm_phase phase); m_uart_agent = uart_agent::type_id::create("m_uart_agent", this); uvm_config_db#(uart_agent_cfg)::set(this, "m_uart_agent*", "cfg", cfg.m_uart_agent_cfg); + m_axi = new[NUM_OF_AXI_IFS]; + foreach(m_axi[i]) begin + axi_name = axi_if_t'(i); + m_axi[i] = axi4_vip_env::type_id::create(.name($sformatf("m_axi_%s", axi_name.name())), .parent(this)); + end + + m_axi_scb = top_chip_dv_axi_scoreboard::type_id::create("m_axi_scb", this); + uvm_config_db#(top_chip_dv_env_cfg)::set(this, "", "cfg", cfg); top_vsqr = top_chip_dv_virtual_sequencer::type_id::create("top_vsqr", this); @@ -86,6 +99,10 @@ function void top_chip_dv_env::connect_phase(uvm_phase phase); // Connect monitor output to matching FIFO in the virtual sequencer. // Allows virtual sequences to check TX items. m_uart_agent.monitor.tx_analysis_port.connect(top_vsqr.uart_tx_fifo.analysis_export); + + m_axi[mst0].m_master.m_monitor.tx_ap.connect(m_axi_scb.mst0_imp); + m_axi[slv0].m_slave.m_monitor.tx_ap.connect(m_axi_scb.slv0_imp); + m_axi[slv1].m_slave.m_monitor.tx_ap.connect(m_axi_scb.slv1_imp); endfunction : connect_phase task top_chip_dv_env::load_memories(); diff --git a/hw/top_chip/dv/env/top_chip_dv_env_pkg.sv b/hw/top_chip/dv/env/top_chip_dv_env_pkg.sv index cfc6ad0a3..13921facf 100644 --- a/hw/top_chip/dv/env/top_chip_dv_env_pkg.sv +++ b/hw/top_chip/dv/env/top_chip_dv_env_pkg.sv @@ -9,6 +9,7 @@ package top_chip_dv_env_pkg; import mem_bkdr_util_pkg::*; import sw_test_status_pkg::*; import uart_agent_pkg::*; + import axi4_vip_pkg::*; // Macro includes `include "uvm_macros.svh" @@ -20,6 +21,9 @@ package top_chip_dv_env_pkg; typedef chip_mem_e chip_mem_list_t[$]; + typedef enum bit[1:0] {mst0 = 0, slv0 = 1, slv1 = 2} axi_if_t; + + // Generate the list of all chip_mem_e values, this helps to simplify iterating over them with // foreach loops. const chip_mem_list_t CHIP_MEM_LIST = chip_mem_values(); @@ -46,11 +50,14 @@ package top_chip_dv_env_pkg; parameter bit [31:0] SW_DV_TEST_STATUS_ADDR = SW_DV_START_ADDR + 'h00; parameter bit [31:0] SW_DV_LOG_ADDR = SW_DV_START_ADDR + 'h04; + localparam NUM_OF_AXI_IFS = 3; + // File includes `include "mem_clear_util.sv" `include "top_chip_dv_env_cfg.sv" `include "top_chip_dv_env_cov.sv" `include "top_chip_dv_virtual_sequencer.sv" + `include "top_chip_dv_axi_scoreboard.sv" `include "top_chip_dv_env.sv" `include "top_chip_dv_vseq_list.sv" endpackage : top_chip_dv_env_pkg diff --git a/hw/top_chip/dv/tb/chip_hier_macros.svh b/hw/top_chip/dv/tb/chip_hier_macros.svh index 35eb5bb16..2fc13ca1c 100644 --- a/hw/top_chip/dv/tb/chip_hier_macros.svh +++ b/hw/top_chip/dv/tb/chip_hier_macros.svh @@ -7,6 +7,7 @@ `define CPU_HIER `SYSTEM_HIER.i_cva6 `define SRAM_MEM_HIER `SYSTEM_HIER.u_axi_sram.u_ram.mem `define TAG_MEM_HIER `SYSTEM_HIER.u_axi_sram.u_tag_mem_prim.mem +`define AXI_XBAR_HIER `SYSTEM_HIER.u_axi_xbar // Testbench related `define SIM_SRAM_IF u_sim_sram.u_sim_sram_if diff --git a/hw/top_chip/dv/tb/tb.sv b/hw/top_chip/dv/tb/tb.sv index 039df2ba1..08a82f29c 100644 --- a/hw/top_chip/dv/tb/tb.sv +++ b/hw/top_chip/dv/tb/tb.sv @@ -10,6 +10,7 @@ module tb; import mem_bkdr_util_pkg::mem_bkdr_util; import top_chip_dv_env_pkg::*; import top_chip_dv_test_pkg::*; + import axi4_vip_pkg::*; import top_chip_dv_env_pkg::SW_DV_START_ADDR; import top_chip_dv_env_pkg::SW_DV_TEST_STATUS_ADDR; @@ -30,6 +31,192 @@ module tb; clk_rst_if sys_clk_if(.clk(clk), .rst_n(rst_n)); clk_rst_if peri_clk_if(.clk(peri_clk), .rst_n(peri_rst_n)); uart_if uart_if(); + axi4_vip_if axi4_if[NUM_OF_AXI_IFS](); + + // This AXI4 VIP shall be always UVM_PASSIVE on top level + // LHS is the VIP, RHS is the RTL + // ========================================================= + // mst0 - Tapping the XBAR Slave Port 0 (Master VIP side) + // ========================================================= + assign axi4_if[mst0].aclk = clk; + assign axi4_if[mst0].aresetn = rst_n; + + always @(*) begin + // Request signals (Master -> XBAR) + axi4_if[mst0].awvalid = `AXI_XBAR_HIER.slv_ports_req_i[0].aw_valid; + axi4_if[mst0].awid = `AXI_XBAR_HIER.slv_ports_req_i[0].aw.id; + axi4_if[mst0].awaddr = `AXI_XBAR_HIER.slv_ports_req_i[0].aw.addr; + axi4_if[mst0].awlen = `AXI_XBAR_HIER.slv_ports_req_i[0].aw.len; + axi4_if[mst0].awsize = `AXI_XBAR_HIER.slv_ports_req_i[0].aw.size; + axi4_if[mst0].awburst = `AXI_XBAR_HIER.slv_ports_req_i[0].aw.burst; + axi4_if[mst0].awlock = `AXI_XBAR_HIER.slv_ports_req_i[0].aw.lock; + axi4_if[mst0].awcache = `AXI_XBAR_HIER.slv_ports_req_i[0].aw.cache; + axi4_if[mst0].awprot = `AXI_XBAR_HIER.slv_ports_req_i[0].aw.prot; + axi4_if[mst0].awqos = `AXI_XBAR_HIER.slv_ports_req_i[0].aw.qos; + axi4_if[mst0].awregion = `AXI_XBAR_HIER.slv_ports_req_i[0].aw.region; + axi4_if[mst0].awuser = `AXI_XBAR_HIER.slv_ports_req_i[0].aw.user; + + axi4_if[mst0].wvalid = `AXI_XBAR_HIER.slv_ports_req_i[0].w_valid; + axi4_if[mst0].wdata = `AXI_XBAR_HIER.slv_ports_req_i[0].w.data; + axi4_if[mst0].wstrb = `AXI_XBAR_HIER.slv_ports_req_i[0].w.strb; + axi4_if[mst0].wlast = `AXI_XBAR_HIER.slv_ports_req_i[0].w.last; + axi4_if[mst0].wuser = `AXI_XBAR_HIER.slv_ports_req_i[0].w.user; + + axi4_if[mst0].arvalid = `AXI_XBAR_HIER.slv_ports_req_i[0].ar_valid; + axi4_if[mst0].arid = `AXI_XBAR_HIER.slv_ports_req_i[0].ar.id; + axi4_if[mst0].araddr = `AXI_XBAR_HIER.slv_ports_req_i[0].ar.addr; + axi4_if[mst0].arlen = `AXI_XBAR_HIER.slv_ports_req_i[0].ar.len; + axi4_if[mst0].arsize = `AXI_XBAR_HIER.slv_ports_req_i[0].ar.size; + axi4_if[mst0].arburst = `AXI_XBAR_HIER.slv_ports_req_i[0].ar.burst; + axi4_if[mst0].arlock = `AXI_XBAR_HIER.slv_ports_req_i[0].ar.lock; + axi4_if[mst0].arcache = `AXI_XBAR_HIER.slv_ports_req_i[0].ar.cache; + axi4_if[mst0].arprot = `AXI_XBAR_HIER.slv_ports_req_i[0].ar.prot; + axi4_if[mst0].arqos = `AXI_XBAR_HIER.slv_ports_req_i[0].ar.qos; + axi4_if[mst0].arregion = `AXI_XBAR_HIER.slv_ports_req_i[0].ar.region; + axi4_if[mst0].aruser = `AXI_XBAR_HIER.slv_ports_req_i[0].ar.user; + + axi4_if[mst0].bready = `AXI_XBAR_HIER.slv_ports_req_i[0].b_ready; + axi4_if[mst0].rready = `AXI_XBAR_HIER.slv_ports_req_i[0].r_ready; + + // Response signals (XBAR -> Master) + axi4_if[mst0].awready = `AXI_XBAR_HIER.slv_ports_resp_o[0].aw_ready; + axi4_if[mst0].wready = `AXI_XBAR_HIER.slv_ports_resp_o[0].w_ready; + axi4_if[mst0].arready = `AXI_XBAR_HIER.slv_ports_resp_o[0].ar_ready; + + axi4_if[mst0].bvalid = `AXI_XBAR_HIER.slv_ports_resp_o[0].b_valid; + axi4_if[mst0].bid = `AXI_XBAR_HIER.slv_ports_resp_o[0].b.id; + axi4_if[mst0].bresp = `AXI_XBAR_HIER.slv_ports_resp_o[0].b.resp; + axi4_if[mst0].buser = `AXI_XBAR_HIER.slv_ports_resp_o[0].b.user; + + axi4_if[mst0].rvalid = `AXI_XBAR_HIER.slv_ports_resp_o[0].r_valid; + axi4_if[mst0].rid = `AXI_XBAR_HIER.slv_ports_resp_o[0].r.id; + axi4_if[mst0].rdata = `AXI_XBAR_HIER.slv_ports_resp_o[0].r.data; + axi4_if[mst0].rresp = `AXI_XBAR_HIER.slv_ports_resp_o[0].r.resp; + axi4_if[mst0].rlast = `AXI_XBAR_HIER.slv_ports_resp_o[0].r.last; + axi4_if[mst0].ruser = `AXI_XBAR_HIER.slv_ports_resp_o[0].r.user; + end + + // ========================================================= + // slv0 - Tapping the XBAR Master Port 0 (Slave VIP side) + // ========================================================= + assign axi4_if[slv0].aclk = clk; + assign axi4_if[slv0].aresetn = rst_n; + + always @(*) begin + // Request signals (XBAR -> Slave) + axi4_if[slv0].awvalid = `AXI_XBAR_HIER.mst_ports_req_o[0].aw_valid; + axi4_if[slv0].awid = `AXI_XBAR_HIER.mst_ports_req_o[0].aw.id; + axi4_if[slv0].awaddr = `AXI_XBAR_HIER.mst_ports_req_o[0].aw.addr; + axi4_if[slv0].awlen = `AXI_XBAR_HIER.mst_ports_req_o[0].aw.len; + axi4_if[slv0].awsize = `AXI_XBAR_HIER.mst_ports_req_o[0].aw.size; + axi4_if[slv0].awburst = `AXI_XBAR_HIER.mst_ports_req_o[0].aw.burst; + axi4_if[slv0].awlock = `AXI_XBAR_HIER.mst_ports_req_o[0].aw.lock; + axi4_if[slv0].awcache = `AXI_XBAR_HIER.mst_ports_req_o[0].aw.cache; + axi4_if[slv0].awprot = `AXI_XBAR_HIER.mst_ports_req_o[0].aw.prot; + axi4_if[slv0].awqos = `AXI_XBAR_HIER.mst_ports_req_o[0].aw.qos; + axi4_if[slv0].awregion = `AXI_XBAR_HIER.mst_ports_req_o[0].aw.region; + axi4_if[slv0].awuser = `AXI_XBAR_HIER.mst_ports_req_o[0].aw.user; + + axi4_if[slv0].wvalid = `AXI_XBAR_HIER.mst_ports_req_o[0].w_valid; + axi4_if[slv0].wdata = `AXI_XBAR_HIER.mst_ports_req_o[0].w.data; + axi4_if[slv0].wstrb = `AXI_XBAR_HIER.mst_ports_req_o[0].w.strb; + axi4_if[slv0].wlast = `AXI_XBAR_HIER.mst_ports_req_o[0].w.last; + axi4_if[slv0].wuser = `AXI_XBAR_HIER.mst_ports_req_o[0].w.user; + + axi4_if[slv0].arvalid = `AXI_XBAR_HIER.mst_ports_req_o[0].ar_valid; + axi4_if[slv0].arid = `AXI_XBAR_HIER.mst_ports_req_o[0].ar.id; + axi4_if[slv0].araddr = `AXI_XBAR_HIER.mst_ports_req_o[0].ar.addr; + axi4_if[slv0].arlen = `AXI_XBAR_HIER.mst_ports_req_o[0].ar.len; + axi4_if[slv0].arsize = `AXI_XBAR_HIER.mst_ports_req_o[0].ar.size; + axi4_if[slv0].arburst = `AXI_XBAR_HIER.mst_ports_req_o[0].ar.burst; + axi4_if[slv0].arlock = `AXI_XBAR_HIER.mst_ports_req_o[0].ar.lock; + axi4_if[slv0].arcache = `AXI_XBAR_HIER.mst_ports_req_o[0].ar.cache; + axi4_if[slv0].arprot = `AXI_XBAR_HIER.mst_ports_req_o[0].ar.prot; + axi4_if[slv0].arqos = `AXI_XBAR_HIER.mst_ports_req_o[0].ar.qos; + axi4_if[slv0].arregion = `AXI_XBAR_HIER.mst_ports_req_o[0].ar.region; + axi4_if[slv0].aruser = `AXI_XBAR_HIER.mst_ports_req_o[0].ar.user; + + axi4_if[slv0].bready = `AXI_XBAR_HIER.mst_ports_req_o[0].b_ready; + axi4_if[slv0].rready = `AXI_XBAR_HIER.mst_ports_req_o[0].r_ready; + + // Response signals (Slave -> XBAR) + axi4_if[slv0].awready = `AXI_XBAR_HIER.mst_ports_resp_i[0].aw_ready; + axi4_if[slv0].wready = `AXI_XBAR_HIER.mst_ports_resp_i[0].w_ready; + axi4_if[slv0].arready = `AXI_XBAR_HIER.mst_ports_resp_i[0].ar_ready; + + axi4_if[slv0].bvalid = `AXI_XBAR_HIER.mst_ports_resp_i[0].b_valid; + axi4_if[slv0].bid = `AXI_XBAR_HIER.mst_ports_resp_i[0].b.id; + axi4_if[slv0].bresp = `AXI_XBAR_HIER.mst_ports_resp_i[0].b.resp; + axi4_if[slv0].buser = `AXI_XBAR_HIER.mst_ports_resp_i[0].b.user; + + axi4_if[slv0].rvalid = `AXI_XBAR_HIER.mst_ports_resp_i[0].r_valid; + axi4_if[slv0].rid = `AXI_XBAR_HIER.mst_ports_resp_i[0].r.id; + axi4_if[slv0].rdata = `AXI_XBAR_HIER.mst_ports_resp_i[0].r.data; + axi4_if[slv0].rresp = `AXI_XBAR_HIER.mst_ports_resp_i[0].r.resp; + axi4_if[slv0].rlast = `AXI_XBAR_HIER.mst_ports_resp_i[0].r.last; + axi4_if[slv0].ruser = `AXI_XBAR_HIER.mst_ports_resp_i[0].r.user; + end + + // ========================================================= + // slv1 - Tapping the XBAR Master Port 1 (Slave VIP side) + // ========================================================= + assign axi4_if[slv1].aclk = clk; + assign axi4_if[slv1].aresetn = rst_n; + + always @(*) begin + // Request signals (XBAR -> Slave) + axi4_if[slv1].awvalid = `AXI_XBAR_HIER.mst_ports_req_o[1].aw_valid; + axi4_if[slv1].awid = `AXI_XBAR_HIER.mst_ports_req_o[1].aw.id; + axi4_if[slv1].awaddr = `AXI_XBAR_HIER.mst_ports_req_o[1].aw.addr; + axi4_if[slv1].awlen = `AXI_XBAR_HIER.mst_ports_req_o[1].aw.len; + axi4_if[slv1].awsize = `AXI_XBAR_HIER.mst_ports_req_o[1].aw.size; + axi4_if[slv1].awburst = `AXI_XBAR_HIER.mst_ports_req_o[1].aw.burst; + axi4_if[slv1].awlock = `AXI_XBAR_HIER.mst_ports_req_o[1].aw.lock; + axi4_if[slv1].awcache = `AXI_XBAR_HIER.mst_ports_req_o[1].aw.cache; + axi4_if[slv1].awprot = `AXI_XBAR_HIER.mst_ports_req_o[1].aw.prot; + axi4_if[slv1].awqos = `AXI_XBAR_HIER.mst_ports_req_o[1].aw.qos; + axi4_if[slv1].awregion = `AXI_XBAR_HIER.mst_ports_req_o[1].aw.region; + axi4_if[slv1].awuser = `AXI_XBAR_HIER.mst_ports_req_o[1].aw.user; + + axi4_if[slv1].wvalid = `AXI_XBAR_HIER.mst_ports_req_o[1].w_valid; + axi4_if[slv1].wdata = `AXI_XBAR_HIER.mst_ports_req_o[1].w.data; + axi4_if[slv1].wstrb = `AXI_XBAR_HIER.mst_ports_req_o[1].w.strb; + axi4_if[slv1].wlast = `AXI_XBAR_HIER.mst_ports_req_o[1].w.last; + axi4_if[slv1].wuser = `AXI_XBAR_HIER.mst_ports_req_o[1].w.user; + + axi4_if[slv1].arvalid = `AXI_XBAR_HIER.mst_ports_req_o[1].ar_valid; + axi4_if[slv1].arid = `AXI_XBAR_HIER.mst_ports_req_o[1].ar.id; + axi4_if[slv1].araddr = `AXI_XBAR_HIER.mst_ports_req_o[1].ar.addr; + axi4_if[slv1].arlen = `AXI_XBAR_HIER.mst_ports_req_o[1].ar.len; + axi4_if[slv1].arsize = `AXI_XBAR_HIER.mst_ports_req_o[1].ar.size; + axi4_if[slv1].arburst = `AXI_XBAR_HIER.mst_ports_req_o[1].ar.burst; + axi4_if[slv1].arlock = `AXI_XBAR_HIER.mst_ports_req_o[1].ar.lock; + axi4_if[slv1].arcache = `AXI_XBAR_HIER.mst_ports_req_o[1].ar.cache; + axi4_if[slv1].arprot = `AXI_XBAR_HIER.mst_ports_req_o[1].ar.prot; + axi4_if[slv1].arqos = `AXI_XBAR_HIER.mst_ports_req_o[1].ar.qos; + axi4_if[slv1].arregion = `AXI_XBAR_HIER.mst_ports_req_o[1].ar.region; + axi4_if[slv1].aruser = `AXI_XBAR_HIER.mst_ports_req_o[1].ar.user; + + axi4_if[slv1].bready = `AXI_XBAR_HIER.mst_ports_req_o[1].b_ready; + axi4_if[slv1].rready = `AXI_XBAR_HIER.mst_ports_req_o[1].r_ready; + + // Response signals (Slave -> XBAR) + axi4_if[slv1].awready = `AXI_XBAR_HIER.mst_ports_resp_i[1].aw_ready; + axi4_if[slv1].wready = `AXI_XBAR_HIER.mst_ports_resp_i[1].w_ready; + axi4_if[slv1].arready = `AXI_XBAR_HIER.mst_ports_resp_i[1].ar_ready; + + axi4_if[slv1].bvalid = `AXI_XBAR_HIER.mst_ports_resp_i[1].b_valid; + axi4_if[slv1].bid = `AXI_XBAR_HIER.mst_ports_resp_i[1].b.id; + axi4_if[slv1].bresp = `AXI_XBAR_HIER.mst_ports_resp_i[1].b.resp; + axi4_if[slv1].buser = `AXI_XBAR_HIER.mst_ports_resp_i[1].b.user; + + axi4_if[slv1].rvalid = `AXI_XBAR_HIER.mst_ports_resp_i[1].r_valid; + axi4_if[slv1].rid = `AXI_XBAR_HIER.mst_ports_resp_i[1].r.id; + axi4_if[slv1].rdata = `AXI_XBAR_HIER.mst_ports_resp_i[1].r.data; + axi4_if[slv1].rresp = `AXI_XBAR_HIER.mst_ports_resp_i[1].r.resp; + axi4_if[slv1].rlast = `AXI_XBAR_HIER.mst_ports_resp_i[1].r.last; + axi4_if[slv1].ruser = `AXI_XBAR_HIER.mst_ports_resp_i[1].r.user; + end // ------ DUT ------ top_chip_system #() dut ( @@ -161,6 +348,11 @@ module tb; uvm_config_db#(virtual clk_rst_if)::set(null, "*", "peri_clk_if", peri_clk_if); uvm_config_db#(virtual uart_if)::set(null, "*.env.m_uart_agent*", "vif", uart_if); + // AXI VIFs + uvm_config_db#(virtual axi4_vip_if)::set(null, "*.m_axi_mst0.*", "vif", axi4_if[mst0]); + uvm_config_db#(virtual axi4_vip_if)::set(null, "*.m_axi_slv0.*", "vif", axi4_if[slv0]); + uvm_config_db#(virtual axi4_vip_if)::set(null, "*.m_axi_slv1.*", "vif", axi4_if[slv1]); + // SW logger and test status interfaces. uvm_config_db#(virtual sw_test_status_if)::set( null, "*.env", "sw_test_status_vif", `SIM_SRAM_IF.u_sw_test_status_if); From ba0c3e76c89d45f0e972fbc7e4be05e42e7d71b9 Mon Sep 17 00:00:00 2001 From: Csaba Kiss Date: Tue, 24 Mar 2026 12:00:55 +0000 Subject: [PATCH 4/4] [top,dv] Added AXI scoreboard and updated base tests Signed-off-by: Csaba Kiss --- .../dv/env/top_chip_dv_axi_scoreboard.sv | 154 ++++++++++++++++++ hw/top_chip/dv/test/top_chip_dv_base_test.sv | 64 ++++++++ hw/top_chip/dv/test/top_chip_dv_test_pkg.sv | 1 + 3 files changed, 219 insertions(+) create mode 100644 hw/top_chip/dv/env/top_chip_dv_axi_scoreboard.sv diff --git a/hw/top_chip/dv/env/top_chip_dv_axi_scoreboard.sv b/hw/top_chip/dv/env/top_chip_dv_axi_scoreboard.sv new file mode 100644 index 000000000..7ab5c0816 --- /dev/null +++ b/hw/top_chip/dv/env/top_chip_dv_axi_scoreboard.sv @@ -0,0 +1,154 @@ +// Copyright lowRISC contributors (COSMIC project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +class top_chip_dv_axi_scoreboard extends uvm_scoreboard; + `uvm_component_utils(top_chip_dv_axi_scoreboard) + + // Local Address Map Struct + typedef struct { + string slave_name; + bit [63:0] start_addr; + bit [63:0] end_addr; + } local_addr_range_t; + + local_addr_range_t mem_map[$]; + string default_slave_id = "INTERNAL_XBAR_DEFAULT"; + int unsigned master_id_width = 4; + + // Analysis Implementation Ports + `uvm_analysis_imp_decl(_mst0) + `uvm_analysis_imp_decl(_slv0) + `uvm_analysis_imp_decl(_slv1) + + uvm_analysis_imp_mst0 #(uvm_sequence_item, top_chip_dv_axi_scoreboard) mst0_imp; + uvm_analysis_imp_slv0 #(uvm_sequence_item, top_chip_dv_axi_scoreboard) slv0_imp; + uvm_analysis_imp_slv1 #(uvm_sequence_item, top_chip_dv_axi_scoreboard) slv1_imp; + + // Queues to handle out-of-order monitor arrivals + // [SlaveName][MaskedID] + axi4_vip_item expect_q[string][bit[63:0]][$]; // Master arrived first + axi4_vip_item actual_q[string][bit[63:0]][$]; // Slave arrived first + + function new(string name, uvm_component parent); + super.new(name, parent); + mst0_imp = new("mst0_imp", this); + slv0_imp = new("slv0_imp", this); + slv1_imp = new("slv1_imp", this); + endfunction + + virtual function void build_phase(uvm_phase phase); + super.build_phase(phase); + // Specific Address Mapping + mem_map.push_back('{"slv0", 64'h1000_0000, 64'h1001_FFFF}); + mem_map.push_back('{"slv1", 64'h4000_0000, 64'h4FFF_FFFF}); + endfunction + + // ------------------------------------------------------------------ + // Helpers + // ------------------------------------------------------------------ + function bit [63:0] get_orig_id(bit [63:0] full_id); + return full_id & ((1 << master_id_width) - 1); + endfunction + + function string decode_addr(bit [63:0] addr); + foreach (mem_map[i]) begin + if (addr >= mem_map[i].start_addr && addr <= mem_map[i].end_addr) + return mem_map[i].slave_name; + end + return default_slave_id; + endfunction + + // ------------------------------------------------------------------ + // Unified Comparison Logic + // ------------------------------------------------------------------ + virtual function void perform_comparison(axi4_vip_item exp, axi4_vip_item act, string slv); + bit [63:0] mid = get_orig_id((act.dir == AXI_WRITE) ? act.bid : act.rid); + + if (act.dir == AXI_WRITE) begin + if (act.awaddr !== exp.awaddr || act.awlen !== exp.awlen || + act.awsize !== exp.awsize || act.awburst !== exp.awburst) begin + `uvm_error("SCB_ATTR_WR", $sformatf("Write Attribute mismatch on %s ID:%h", slv, mid)) + end + if (act.wdata != exp.wdata) `uvm_error("SCB_WDATA", $sformatf("Write Data mismatch on %s ID:%h", slv, mid)) + if (act.wstrb != exp.wstrb) `uvm_error("SCB_WSTRB", $sformatf("Write Strobe mismatch on %s ID:%h", slv, mid)) + end else begin + if (act.araddr !== exp.araddr || act.arlen !== exp.arlen || + act.arsize !== exp.arsize || act.arburst !== exp.arburst) begin + `uvm_error("SCB_ATTR_RD", $sformatf("Read Attribute mismatch on %s ID:%h", slv, mid)) + end + if (act.rdata != exp.rdata) `uvm_error("SCB_RDATA", $sformatf("Read Data mismatch on %s ID:%h", slv, mid)) + if (act.rresp != exp.rresp) `uvm_error("SCB_RRESP", $sformatf("Read Response mismatch on %s ID:%h", slv, mid)) + end + endfunction + + // ------------------------------------------------------------------ + // Master Port Callback (mst0) + // ------------------------------------------------------------------ + virtual function void write_mst0(uvm_sequence_item item); + axi4_vip_item tr; + if (!$cast(tr, item)) `uvm_fatal("TYPE", "Cast failed") + + begin + bit [63:0] addr = (tr.dir == AXI_WRITE) ? tr.awaddr : tr.araddr; + bit [63:0] id = (tr.dir == AXI_WRITE) ? tr.awid : tr.arid; + bit [1:0] resp = (tr.dir == AXI_WRITE) ? tr.bresp[0] : tr.rresp[0]; + string target_slv = decode_addr(addr); + bit [63:0] mid = get_orig_id(id); + + if (target_slv == default_slave_id) begin + if (resp != 2'b11) `uvm_error("SCB_DECERR", $sformatf("No DECERR for addr %h", addr)) + end else begin + // Check if Slave monitor already sent the data + if (actual_q[target_slv].exists(mid) && actual_q[target_slv][mid].size() > 0) begin + axi4_vip_item act_tr = actual_q[target_slv][mid].pop_front(); + perform_comparison(tr, act_tr, target_slv); + if (actual_q[target_slv][mid].size() == 0) actual_q[target_slv].delete(mid); + end else begin + axi4_vip_item exp_tr; + $cast(exp_tr, tr.clone()); + expect_q[target_slv][mid].push_back(exp_tr); + end + end + end + endfunction + + // ------------------------------------------------------------------ + // Slave Port Arrival Logic + // ------------------------------------------------------------------ + virtual function void check_slave_arrival(uvm_sequence_item item, string slv_id); + axi4_vip_item act; + if (!$cast(act, item)) `uvm_fatal("TYPE", "Cast failed") + + begin + bit [63:0] mid = get_orig_id((act.dir == AXI_WRITE) ? act.bid : act.rid); + + // Check if Master monitor already sent the request + if (expect_q[slv_id].exists(mid) && expect_q[slv_id][mid].size() > 0) begin + axi4_vip_item exp_tr = expect_q[slv_id][mid].pop_front(); + perform_comparison(exp_tr, act, slv_id); + if (expect_q[slv_id][mid].size() == 0) expect_q[slv_id].delete(mid); + end else begin + axi4_vip_item act_tr; + $cast(act_tr, act.clone()); + actual_q[slv_id][mid].push_back(act_tr); + end + end + endfunction + + virtual function void write_slv0(uvm_sequence_item tr); check_slave_arrival(tr, "slv0"); endfunction + virtual function void write_slv1(uvm_sequence_item tr); check_slave_arrival(tr, "slv1"); endfunction + + // ------------------------------------------------------------------ + // Final Checks + // ------------------------------------------------------------------ + virtual function void check_phase(uvm_phase phase); + super.check_phase(phase); + foreach (expect_q[s]) foreach (expect_q[s][i]) if (expect_q[s][i].size() > 0) + `uvm_error("SCB_DRAIN", $sformatf("Master req for %s (ID %h) never reached Slave", s, i)) + + foreach (actual_q[s]) foreach (actual_q[s][i]) if (actual_q[s][i].size() > 0) + `uvm_error("SCB_DRAIN", $sformatf("Slave trans on %s (ID %h) never reached Master", s, i)) + endfunction + +endclass \ No newline at end of file diff --git a/hw/top_chip/dv/test/top_chip_dv_base_test.sv b/hw/top_chip/dv/test/top_chip_dv_base_test.sv index 58813d062..490133dec 100644 --- a/hw/top_chip/dv/test/top_chip_dv_base_test.sv +++ b/hw/top_chip/dv/test/top_chip_dv_base_test.sv @@ -28,6 +28,9 @@ function top_chip_dv_base_test::new(string name="", uvm_component parent=null); endfunction : new function void top_chip_dv_base_test::build_phase(uvm_phase phase); + axi4_vip_cfg axi_cfg[]; + axi_if_t axi_name; + dv_report_server m_dv_report_server = new(); uvm_report_server::set_server(m_dv_report_server); @@ -36,6 +39,67 @@ function void top_chip_dv_base_test::build_phase(uvm_phase phase); env = top_chip_dv_env::type_id::create("env", this); env.cfg = top_chip_dv_env_cfg::type_id::create("cfg", this); env.cfg.initialize(); + + // AXI VIP configuration + axi_cfg = new[NUM_OF_AXI_IFS]; + foreach(axi_cfg[i]) begin + axi_name = axi_if_t'(i); + axi_cfg[i] = axi4_vip_cfg::type_id::create(.name($sformatf("m_axi_%s_cfg", axi_name.name())), .parent(this)); + + case(axi_name) + mst0: begin + axi_cfg[i].set_config(.inst_id (axi_name.name()), + .has_master (1), + .master_active_passive(UVM_PASSIVE), + .has_slave (0), + .slave_active_passive (UVM_PASSIVE), + .has_coverage (0), + .has_checker (0), + .id_width ( 4), + .addr_width (64), + .data_width (64), + .user_width ( 1), + .region_width ( 4), + .qos_width ( 4) + ); + end + slv0: begin + axi_cfg[i].set_config(.inst_id (axi_name.name()), + .has_master (0), + .master_active_passive(UVM_PASSIVE), + .has_slave (1), + .slave_active_passive (UVM_PASSIVE), + .has_coverage (0), + .has_checker (0), + .id_width ( 4), + .addr_width (64), + .data_width (64), + .user_width ( 1), + .region_width ( 4), + .qos_width ( 4) + ); + end + slv1: begin + axi_cfg[i].set_config(.inst_id (axi_name.name()), + .has_master (0), + .master_active_passive(UVM_PASSIVE), + .has_slave (1), + .slave_active_passive (UVM_PASSIVE), + .has_coverage (0), + .has_checker (0), + .id_width ( 4), + .addr_width (64), + .data_width (64), + .user_width ( 1), + .region_width ( 4), + .qos_width ( 4) + ); + end + endcase + + uvm_config_db#(axi4_vip_cfg)::set(this, $sformatf("env.m_axi_%s*", axi_name.name()), "m_cfg", axi_cfg[i]); + end + endfunction : build_phase function void top_chip_dv_base_test::connect_phase(uvm_phase phase); diff --git a/hw/top_chip/dv/test/top_chip_dv_test_pkg.sv b/hw/top_chip/dv/test/top_chip_dv_test_pkg.sv index 11f5b0b27..f41a285dd 100644 --- a/hw/top_chip/dv/test/top_chip_dv_test_pkg.sv +++ b/hw/top_chip/dv/test/top_chip_dv_test_pkg.sv @@ -6,6 +6,7 @@ package top_chip_dv_test_pkg; import uvm_pkg::*; import dv_utils_pkg::*; import top_chip_dv_env_pkg::*; + import axi4_vip_pkg::*; `include "uvm_macros.svh" `include "dv_macros.svh"