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..29fbfc496 --- /dev/null +++ b/hw/dv/vip/axi4_vip/axi4_vip.core @@ -0,0 +1,30 @@ +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: + depend: + - lowrisc:dv:dv_macros + 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_env_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_manager_agent.svh: {is_include_file: true} + - axi4_vip_subordinate_agent.svh: {is_include_file: true} + - axi4_vip_env.svh: {is_include_file: true} + file_type: systemVerilogSource + +targets: + default: + filesets: + - files_dv 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..10f2e4a6a --- /dev/null +++ b/hw/dv/vip/axi4_vip/axi4_vip_defines.svh @@ -0,0 +1,11 @@ +// Copyright lowRISC contributors (COSMIC project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +// maximum supported bus widths +`define AXI4_MAX_ID_WIDTH 16 +`define AXI4_MAX_ADDR_WIDTH 64 +`define AXI4_MAX_DATA_WIDTH 1024 +`define AXI4_MAX_USER_WIDTH 32 +`define AXI4_MAX_REGION_WIDTH 8 +`define AXI4_MAX_QOS_WIDTH 8 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..0826ca954 --- /dev/null +++ b/hw/dv/vip/axi4_vip/axi4_vip_driver.svh @@ -0,0 +1,30 @@ +// Copyright lowRISC contributors (COSMIC project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +class axi4_vip_driver extends uvm_driver #(axi4_vip_item); + + `uvm_component_utils(axi4_vip_driver) + + axi4_vip_env_cfg m_cfg; + virtual axi4_vip_if vif; + + extern function new(string name, uvm_component parent); + extern function void build_phase(uvm_phase phase); + +endclass : axi4_vip_driver + +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_env_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, "", "vif", vif)) begin + `uvm_fatal("NOVIF", {"virtual interface must be set for: ", get_full_name(), ".vif"}) + end +endfunction : build_phase 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..dec471841 --- /dev/null +++ b/hw/dv/vip/axi4_vip/axi4_vip_env.svh @@ -0,0 +1,43 @@ +// Copyright lowRISC contributors (COSMIC project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +class axi4_vip_env extends uvm_env; + + `uvm_component_utils(axi4_vip_env) + + axi4_vip_env_cfg m_cfg; + + // Created when the env is configured to monitor the manager side of an AXI link. + axi4_vip_manager_agent m_manager; + // Created when the env is configured to monitor the subordinate side of an AXI link. + axi4_vip_subordinate_agent m_subordinate; + + extern function new(string name, uvm_component parent); + extern function void build_phase(uvm_phase phase); + +endclass : axi4_vip_env + +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_env_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_manager && !m_cfg.m_has_subordinate) begin + `uvm_fatal("BADCFG", "An AXI VIP instance must monitor at least one side") + end + + if (m_cfg.m_has_manager) begin + m_manager = axi4_vip_manager_agent::type_id::create("m_manager", this); + end + + if (m_cfg.m_has_subordinate) begin + m_subordinate = axi4_vip_subordinate_agent::type_id::create("m_subordinate", this); + end +endfunction : build_phase diff --git a/hw/dv/vip/axi4_vip/axi4_vip_env_cfg.svh b/hw/dv/vip/axi4_vip/axi4_vip_env_cfg.svh new file mode 100644 index 000000000..585be449b --- /dev/null +++ b/hw/dv/vip/axi4_vip/axi4_vip_env_cfg.svh @@ -0,0 +1,104 @@ +// Copyright lowRISC contributors (COSMIC project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +class axi4_vip_env_cfg extends uvm_object; + + string m_inst_id = "AXI4"; + bit m_has_manager = 0; + uvm_active_passive_enum m_manager_active_passive = UVM_PASSIVE; + bit m_has_subordinate = 0; + uvm_active_passive_enum m_subordinate_active_passive = UVM_PASSIVE; + + int unsigned m_id_width = `AXI4_MAX_ID_WIDTH; + int unsigned m_addr_width = `AXI4_MAX_ADDR_WIDTH; + int unsigned m_data_width = `AXI4_MAX_DATA_WIDTH; + int unsigned m_user_width = `AXI4_MAX_USER_WIDTH; + int unsigned m_region_width = `AXI4_MAX_REGION_WIDTH; + int unsigned m_qos_width = `AXI4_MAX_QOS_WIDTH; + + `uvm_object_utils_begin(axi4_vip_env_cfg) + `uvm_field_string(m_inst_id, UVM_DEFAULT | UVM_STRING) + `uvm_field_int(m_has_manager, UVM_DEFAULT) + `uvm_field_int(m_has_subordinate, UVM_DEFAULT) + `uvm_field_enum(uvm_active_passive_enum, m_manager_active_passive, UVM_DEFAULT) + `uvm_field_enum(uvm_active_passive_enum, m_subordinate_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 + + extern function new(string name = ""); + + // Set the configuration with a single function call. + // + // Width arguments use zero to mean the maximum supported width. + extern virtual function void + set_config(string inst_id = "AXI4", + bit has_manager = 0, + uvm_active_passive_enum manager_active_passive = UVM_PASSIVE, + bit has_subordinate = 0, + uvm_active_passive_enum subordinate_active_passive = UVM_PASSIVE, + int unsigned id_width = 0, + int unsigned addr_width = 0, + int unsigned data_width = 0, + int unsigned user_width = 0, + int unsigned region_width = 0, + int unsigned qos_width = 0); + + extern local function int unsigned translate_width(string field_name, + int unsigned max_val, + int unsigned provided); + +endclass : axi4_vip_env_cfg + +function axi4_vip_env_cfg::new(string name = ""); + super.new(name); +endfunction : new + +function void + axi4_vip_env_cfg::set_config(string inst_id = "AXI4", + bit has_manager = 0, + uvm_active_passive_enum manager_active_passive = UVM_PASSIVE, + bit has_subordinate = 0, + uvm_active_passive_enum subordinate_active_passive = UVM_PASSIVE, + int unsigned id_width = 0, + int unsigned addr_width = 0, + int unsigned data_width = 0, + int unsigned user_width = 0, + int unsigned region_width = 0, + int unsigned qos_width = 0); + m_inst_id = inst_id; + m_has_manager = has_manager; + m_manager_active_passive = manager_active_passive; + m_has_subordinate = has_subordinate; + m_subordinate_active_passive = subordinate_active_passive; + + m_id_width = translate_width("id_width", `AXI4_MAX_ID_WIDTH, id_width); + m_addr_width = translate_width("addr_width", `AXI4_MAX_ADDR_WIDTH, addr_width); + m_data_width = translate_width("data_width", `AXI4_MAX_DATA_WIDTH, data_width); + m_user_width = translate_width("user_width", `AXI4_MAX_USER_WIDTH, user_width); + m_region_width = translate_width("region_width", `AXI4_MAX_REGION_WIDTH, region_width); + m_qos_width = translate_width("qos_width", `AXI4_MAX_QOS_WIDTH, qos_width); +endfunction : set_config + +function int unsigned axi4_vip_env_cfg::translate_width(string field_name, + int unsigned max_val, + int unsigned provided); + if (provided == 0) begin + return max_val; + end + + if (provided > max_val) begin + `uvm_error(m_inst_id, + $sformatf({"Width for %0s cannot be set to %0d. This is greater than %0d ", + "(the maximum supported width for this field)."}, + field_name, provided, max_val)) + return max_val; + end + + return provided; +endfunction : translate_width 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..31e126fd6 --- /dev/null +++ b/hw/dv/vip/axi4_vip/axi4_vip_if.sv @@ -0,0 +1,146 @@ +// Copyright lowRISC contributors (COSMIC project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +`include "axi4_vip_defines.svh" + +interface axi4_vip_if ( + input logic aclk, + input logic aresetn +); + + localparam int ID_WIDTH = `AXI4_MAX_ID_WIDTH; + localparam int ADDR_WIDTH = `AXI4_MAX_ADDR_WIDTH; + localparam int DATA_WIDTH = `AXI4_MAX_DATA_WIDTH; + localparam int USER_WIDTH = `AXI4_MAX_USER_WIDTH; + localparam int REGION_WIDTH = `AXI4_MAX_REGION_WIDTH; + localparam int QOS_WIDTH = `AXI4_MAX_QOS_WIDTH; + + // 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; + + // manager clocking block + clocking manager_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 + + + // subordinate clocking block + clocking subordinate_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 : axi4_vip_if 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..5587617f9 --- /dev/null +++ b/hw/dv/vip/axi4_vip/axi4_vip_item.svh @@ -0,0 +1,111 @@ +// Copyright lowRISC contributors (COSMIC project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +class axi4_vip_item extends uvm_sequence_item; + + axi_obs_e obs_kind; + axi_dir_e dir; + + bit [`AXI4_MAX_ID_WIDTH-1:0] awid; + bit [`AXI4_MAX_ADDR_WIDTH-1:0] awaddr; + bit [7:0] awlen; + bit [2:0] awsize; + bit [1:0] awburst; + bit awlock; + bit [3:0] awcache; + bit [2:0] awprot; + bit [`AXI4_MAX_QOS_WIDTH-1:0] awqos; + bit [`AXI4_MAX_REGION_WIDTH-1:0] awregion; + bit [`AXI4_MAX_USER_WIDTH-1:0] awuser; + + bit [`AXI4_MAX_DATA_WIDTH-1:0] wdata[$]; + bit [`AXI4_MAX_DATA_WIDTH/8-1:0] wstrb[$]; + bit wlast[$]; + bit [`AXI4_MAX_USER_WIDTH-1:0] wuser[$]; + + bit [`AXI4_MAX_ID_WIDTH-1:0] bid; + bit [1:0] bresp; + bit [`AXI4_MAX_USER_WIDTH-1:0] buser; + + bit [`AXI4_MAX_ID_WIDTH-1:0] arid; + bit [`AXI4_MAX_ADDR_WIDTH-1:0] araddr; + bit [7:0] arlen; + bit [2:0] arsize; + bit [1:0] arburst; + bit arlock; + bit [3:0] arcache; + bit [2:0] arprot; + bit [`AXI4_MAX_QOS_WIDTH-1:0] arqos; + bit [`AXI4_MAX_REGION_WIDTH-1:0] arregion; + bit [`AXI4_MAX_USER_WIDTH-1:0] aruser; + + bit [`AXI4_MAX_ID_WIDTH-1:0] rid; + bit [`AXI4_MAX_DATA_WIDTH-1:0] rdata[$]; + bit [1:0] rresp[$]; + bit rlast[$]; + bit [`AXI4_MAX_USER_WIDTH-1:0] ruser[$]; + + `uvm_object_utils_begin(axi4_vip_item) + `uvm_field_enum(axi_obs_e, obs_kind, UVM_DEFAULT) + `uvm_field_enum(axi_dir_e, dir, UVM_DEFAULT) + + // Write Address + `uvm_field_int(awid, UVM_DEFAULT) + `uvm_field_int(awaddr, UVM_DEFAULT) + `uvm_field_int(awlen, UVM_DEFAULT) + `uvm_field_int(awsize, UVM_DEFAULT) + `uvm_field_int(awburst, UVM_DEFAULT) + `uvm_field_int(awlock, UVM_DEFAULT) + `uvm_field_int(awcache, UVM_DEFAULT) + `uvm_field_int(awprot, UVM_DEFAULT) + `uvm_field_int(awqos, UVM_DEFAULT) + `uvm_field_int(awregion, UVM_DEFAULT) + `uvm_field_int(awuser, UVM_DEFAULT) + + // Write Data (Queues) + `uvm_field_queue_int(wdata, UVM_DEFAULT) + `uvm_field_queue_int(wstrb, UVM_DEFAULT) + `uvm_field_queue_int(wlast, UVM_DEFAULT) + `uvm_field_queue_int(wuser, UVM_DEFAULT) + + // Write Response + `uvm_field_int(bid, UVM_DEFAULT) + `uvm_field_int(bresp, UVM_DEFAULT) + `uvm_field_int(buser, UVM_DEFAULT) + + // Read Address + `uvm_field_int(arid, UVM_DEFAULT) + `uvm_field_int(araddr, UVM_DEFAULT) + `uvm_field_int(arlen, UVM_DEFAULT) + `uvm_field_int(arsize, UVM_DEFAULT) + `uvm_field_int(arburst, UVM_DEFAULT) + `uvm_field_int(arlock, UVM_DEFAULT) + `uvm_field_int(arcache, UVM_DEFAULT) + `uvm_field_int(arprot, UVM_DEFAULT) + `uvm_field_int(arqos, UVM_DEFAULT) + `uvm_field_int(arregion, UVM_DEFAULT) + `uvm_field_int(aruser, UVM_DEFAULT) + + // Read Data (Queues) + `uvm_field_int(rid, UVM_DEFAULT) + `uvm_field_queue_int(rdata, UVM_DEFAULT) + `uvm_field_queue_int(rresp, UVM_DEFAULT) + `uvm_field_queue_int(rlast, UVM_DEFAULT) + `uvm_field_queue_int(ruser, UVM_DEFAULT) + `uvm_object_utils_end + + extern function new(string name = ""); + + // Clone and return the result as axi4_vip_item. + extern virtual function axi4_vip_item item_clone(); + +endclass : axi4_vip_item + +function axi4_vip_item::new(string name = ""); + super.new(name); +endfunction : new + +function axi4_vip_item axi4_vip_item::item_clone(); + $cast(item_clone, clone()); +endfunction : item_clone diff --git a/hw/dv/vip/axi4_vip/axi4_vip_manager_agent.svh b/hw/dv/vip/axi4_vip/axi4_vip_manager_agent.svh new file mode 100644 index 000000000..f88ac0b88 --- /dev/null +++ b/hw/dv/vip/axi4_vip/axi4_vip_manager_agent.svh @@ -0,0 +1,34 @@ +// Copyright lowRISC contributors (COSMIC project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +class axi4_vip_manager_agent extends uvm_agent; + + `uvm_component_utils(axi4_vip_manager_agent) + + axi4_vip_env_cfg m_cfg; + + axi4_vip_monitor m_monitor; + + extern function new(string name, uvm_component parent); + extern function void build_phase(uvm_phase phase); + +endclass : axi4_vip_manager_agent + +function axi4_vip_manager_agent::new(string name, uvm_component parent); + super.new(name, parent); +endfunction : new + +function void axi4_vip_manager_agent::build_phase(uvm_phase phase); + super.build_phase(phase); + + if (!uvm_config_db#(axi4_vip_env_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_manager_active_passive == UVM_ACTIVE) begin + `uvm_fatal("BADCFG", "Active manager mode is not implemented") + end + + m_monitor = axi4_vip_monitor::type_id::create("m_monitor", this); +endfunction : build_phase 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..d0931dddb --- /dev/null +++ b/hw/dv/vip/axi4_vip/axi4_vip_monitor.svh @@ -0,0 +1,292 @@ +// Copyright lowRISC contributors (COSMIC project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +class axi4_vip_monitor extends uvm_monitor; + + `uvm_component_utils(axi4_vip_monitor) + + typedef bit [ `AXI4_MAX_ID_WIDTH-1:0] axi4_id_t; + typedef bit [ `AXI4_MAX_ADDR_WIDTH-1:0] axi4_addr_t; + typedef bit [ `AXI4_MAX_USER_WIDTH-1:0] axi4_user_t; + typedef bit [ `AXI4_MAX_REGION_WIDTH-1:0] axi4_region_t; + typedef bit [ `AXI4_MAX_QOS_WIDTH-1:0] axi4_qos_t; + typedef bit [ `AXI4_MAX_DATA_WIDTH-1:0] axi4_data_t; + typedef bit [(`AXI4_MAX_DATA_WIDTH/8)-1:0] axi4_strb_t; + + axi4_vip_env_cfg m_cfg; + virtual axi4_vip_if vif; + + uvm_analysis_port #(axi4_vip_item) aw_ap; + uvm_analysis_port #(axi4_vip_item) w_ap; + uvm_analysis_port #(axi4_vip_item) ar_ap; + uvm_analysis_port #(axi4_vip_item) r_ap; + uvm_analysis_port #(axi4_vip_item) tx_ap; + + protected axi4_vip_item aw_pending_q[$]; + protected axi4_vip_item w_pending_q[$]; + + protected axi4_vip_item write_q_by_id [axi4_id_t][$]; + protected axi4_vip_item read_q_by_id [axi4_id_t][$]; + + extern function new(string name, uvm_component parent); + extern function void build_phase(uvm_phase phase); + extern task run_phase(uvm_phase phase); + + extern protected function void cleanup_queues(); + extern protected task collect_aw_channel(); + extern protected task collect_w_channel(); + extern protected task collect_b_channel(); + extern protected task collect_ar_channel(); + extern protected task collect_r_channel(); + + // Return a version of req with AW information from aw_item. + extern protected function axi4_vip_item merge_aw(axi4_vip_item req, axi4_vip_item aw_item); + +endclass : axi4_vip_monitor + +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_env_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, "", "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 : isolation_fork + begin + fork + wait (vif.aresetn === 1'b0); + + collect_aw_channel(); + collect_w_channel(); + collect_b_channel(); + collect_ar_channel(); + collect_r_channel(); + join_any + + disable fork; + end + join + + cleanup_queues(); + end +endtask : run_phase + +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(); + axi4_id_t id_mask = (axi4_id_t'(1) << m_cfg.m_id_width) - 1; + axi4_addr_t addr_mask = (axi4_addr_t'(1) << m_cfg.m_addr_width) - 1; + axi4_user_t user_mask = (axi4_user_t'(1) << m_cfg.m_user_width) - 1; + axi4_region_t region_mask = (axi4_region_t'(1) << m_cfg.m_region_width) - 1; + axi4_qos_t qos_mask = (axi4_qos_t'(1) << m_cfg.m_qos_width) - 1; + + 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 & id_mask; + tr.awaddr = vif.monitor_cb.awaddr & addr_mask; + tr.awuser = vif.monitor_cb.awuser & user_mask; + tr.awregion = vif.monitor_cb.awregion & region_mask; + tr.awqos = vif.monitor_cb.awqos & qos_mask; + + 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(); + write_q_by_id[tr.awid].push_back(merge_aw(w_tr, tr)); + end else begin + aw_pending_q.push_back(tr); + end + `uvm_info(get_full_name(), + $sformatf("AW collected: ID=%0h Addr=%0h", tr.awid, tr.awaddr), + UVM_HIGH) + aw_ap.write(tr.item_clone()); + end + end +endtask : collect_aw_channel + +task axi4_vip_monitor::collect_w_channel(); + axi4_data_t data_mask = (axi4_data_t'(1) << m_cfg.m_data_width) - 1; + axi4_strb_t strb_mask = (axi4_strb_t'(1) << (m_cfg.m_data_width / 8)) - 1; + axi4_user_t user_mask = (axi4_user_t'(1) << m_cfg.m_user_width) - 1; + axi4_vip_item w_burst; + + forever begin + @(vif.monitor_cb); + if (vif.monitor_cb.wvalid && vif.monitor_cb.wready) begin + if (w_burst == null) begin + w_burst = axi4_vip_item::type_id::create("w_burst"); + end + + w_burst.dir = AXI_WRITE; + w_burst.wdata.push_back(vif.monitor_cb.wdata & data_mask); + w_burst.wstrb.push_back(vif.monitor_cb.wstrb & strb_mask); + w_burst.wuser.push_back(vif.monitor_cb.wuser & user_mask); + w_burst.wlast.push_back(vif.monitor_cb.wlast); + + if (vif.monitor_cb.wlast) begin + w_burst.obs_kind = AXI_W_CH; + + if (aw_pending_q.size() > 0) begin + axi4_vip_item aw_tr = aw_pending_q.pop_front(); + write_q_by_id[aw_tr.awid].push_back(merge_aw(w_burst, aw_tr)); + end else begin + w_pending_q.push_back(w_burst); + end + + `uvm_info(get_full_name(), "W burst collected", UVM_HIGH) + w_ap.write(w_burst.item_clone()); + w_burst = null; + end + end + end +endtask : collect_w_channel + +task axi4_vip_monitor::collect_b_channel(); + axi4_id_t id_mask = (axi4_id_t'(1) << m_cfg.m_id_width) - 1; + axi4_user_t user_mask = (axi4_user_t'(1) << m_cfg.m_user_width) - 1; + axi4_id_t id; + + forever begin + @(vif.monitor_cb); + if (vif.monitor_cb.bvalid && vif.monitor_cb.bready) begin + id = vif.monitor_cb.bid & id_mask; + 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 & user_mask; + `uvm_info(get_full_name(), $sformatf("FULL Write complete: ID=%0h", id), UVM_LOW) + tx_ap.write(tr.item_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(); + axi4_id_t id_mask = (axi4_id_t'(1) << m_cfg.m_id_width) - 1; + axi4_addr_t addr_mask = (axi4_addr_t'(1) << m_cfg.m_addr_width) - 1; + axi4_user_t user_mask = (axi4_user_t'(1) << m_cfg.m_user_width) - 1; + axi4_region_t region_mask = (axi4_region_t'(1) << m_cfg.m_region_width) - 1; + axi4_qos_t qos_mask = (axi4_qos_t'(1) << m_cfg.m_qos_width) - 1; + + 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 & id_mask; + tr.araddr = vif.monitor_cb.araddr & addr_mask; + tr.aruser = vif.monitor_cb.aruser & user_mask; + tr.arregion = vif.monitor_cb.arregion & region_mask; + tr.arqos = vif.monitor_cb.arqos & qos_mask; + + 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 collected: ID=%0h Addr=%0h", tr.arid, tr.araddr), + UVM_HIGH) + ar_ap.write(tr.item_clone()); + end + end +endtask : collect_ar_channel + +task axi4_vip_monitor::collect_r_channel(); + axi4_id_t id_mask = (axi4_id_t'(1) << m_cfg.m_id_width) - 1; + axi4_data_t data_mask = (axi4_data_t'(1) << m_cfg.m_data_width) - 1; + axi4_user_t user_mask = (axi4_user_t'(1) << m_cfg.m_user_width) - 1; + axi4_id_t id; + + forever begin + @(vif.monitor_cb); + if (vif.monitor_cb.rvalid && vif.monitor_cb.rready) begin + id = vif.monitor_cb.rid & id_mask; + 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]; + tr.rid = id; + tr.rdata.push_back(vif.monitor_cb.rdata & data_mask); + tr.rresp.push_back(vif.monitor_cb.rresp); + tr.rlast.push_back(vif.monitor_cb.rlast); + tr.ruser.push_back(vif.monitor_cb.ruser & user_mask); + + 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 Read complete: ID=%0h", id), UVM_LOW) + tx_ap.write(tr.item_clone()); + end else begin + tr.obs_kind = AXI_R_CH; + end + r_ap.write(tr.item_clone()); + end else begin + `uvm_error("MON_R", $sformatf("R-Data for unexpected ID: %0h", id)) + end + end + end +endtask : collect_r_channel + +function axi4_vip_item axi4_vip_monitor::merge_aw(axi4_vip_item req, axi4_vip_item aw_item); + axi4_vip_item write_item = req.item_clone(); + + if (aw_item.dir != AXI_WRITE) begin + `uvm_fatal("MON_AW", "Cannot take AW information from non-write aw_item.") + end + + write_item.dir = AXI_WRITE; + write_item.awid = aw_item.awid; + write_item.awaddr = aw_item.awaddr; + write_item.awlen = aw_item.awlen; + write_item.awsize = aw_item.awsize; + write_item.awburst = aw_item.awburst; + write_item.awlock = aw_item.awlock; + write_item.awcache = aw_item.awcache; + write_item.awprot = aw_item.awprot; + write_item.awqos = aw_item.awqos; + write_item.awregion = aw_item.awregion; + write_item.awuser = aw_item.awuser; + + return write_item; +endfunction : merge_aw 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..53e371685 --- /dev/null +++ b/hw/dv/vip/axi4_vip/axi4_vip_pkg.sv @@ -0,0 +1,27 @@ +// Copyright lowRISC contributors (COSMIC project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +package axi4_vip_pkg; + + import uvm_pkg::*; + `include "uvm_macros.svh" + `include "dv_macros.svh" + + `include "axi4_vip_defines.svh" + `include "axi4_vip_types.svh" + + `include "axi4_vip_env_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_manager_agent.svh" + `include "axi4_vip_subordinate_agent.svh" + + `include "axi4_vip_env.svh" + +endpackage 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..838dcad4e --- /dev/null +++ b/hw/dv/vip/axi4_vip/axi4_vip_sequencer.svh @@ -0,0 +1,15 @@ +// Copyright lowRISC contributors (COSMIC project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +class axi4_vip_sequencer extends uvm_sequencer #(axi4_vip_item); + + `uvm_component_utils(axi4_vip_sequencer) + + extern function new(string name, uvm_component parent); + +endclass : axi4_vip_sequencer + +function axi4_vip_sequencer::new(string name, uvm_component parent); + super.new(name, parent); +endfunction : new diff --git a/hw/dv/vip/axi4_vip/axi4_vip_subordinate_agent.svh b/hw/dv/vip/axi4_vip/axi4_vip_subordinate_agent.svh new file mode 100644 index 000000000..bd701ec06 --- /dev/null +++ b/hw/dv/vip/axi4_vip/axi4_vip_subordinate_agent.svh @@ -0,0 +1,34 @@ +// Copyright lowRISC contributors (COSMIC project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +class axi4_vip_subordinate_agent extends uvm_agent; + + `uvm_component_utils(axi4_vip_subordinate_agent) + + axi4_vip_env_cfg m_cfg; + + axi4_vip_monitor m_monitor; + + extern function new(string name, uvm_component parent); + extern function void build_phase(uvm_phase phase); + +endclass : axi4_vip_subordinate_agent + +function axi4_vip_subordinate_agent::new(string name, uvm_component parent); + super.new(name, parent); +endfunction : new + +function void axi4_vip_subordinate_agent::build_phase(uvm_phase phase); + super.build_phase(phase); + + if (!uvm_config_db#(axi4_vip_env_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_subordinate_active_passive == UVM_ACTIVE) begin + `uvm_fatal("BADCFG", "Active subordinate mode is not implemented") + end + + m_monitor = axi4_vip_monitor::type_id::create("m_monitor", this); +endfunction : build_phase 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..49fba0a73 --- /dev/null +++ b/hw/dv/vip/axi4_vip/axi4_vip_types.svh @@ -0,0 +1,17 @@ +// Copyright lowRISC contributors (COSMIC project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +typedef enum { + AXI_READ, + AXI_WRITE +} axi_dir_e; + +typedef enum { + AXI_AW_CH, + AXI_W_CH, + AXI_FULL_WRITE_TR, + AXI_AR_CH, + AXI_R_CH, + AXI_FULL_READ_TR +} axi_obs_e; 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..be6ff9fc9 --- /dev/null +++ b/hw/top_chip/dv/env/top_chip_dv_axi_scoreboard.sv @@ -0,0 +1,212 @@ +// 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) + + typedef bit [top_pkg::AxiIdWidth-1:0] masked_id_t; + + axi_addr_range_t mem_map[$]; + string default_subordinate_id = "INTERNAL_XBAR_DEFAULT"; + + `uvm_analysis_imp_decl(_mgr0_cva6) + `uvm_analysis_imp_decl(_sub0_romctrlmem) + `uvm_analysis_imp_decl(_sub1_sram) + `uvm_analysis_imp_decl(_sub2_mailbox) + `uvm_analysis_imp_decl(_sub3_tlcrossbar) + `uvm_analysis_imp_decl(_sub4_dram) + + uvm_analysis_imp_mgr0_cva6 #(axi4_vip_item, top_chip_dv_axi_scoreboard) mgr0_cva6_imp; + uvm_analysis_imp_sub0_romctrlmem #(axi4_vip_item, top_chip_dv_axi_scoreboard) sub0_romctrlmem_imp; + uvm_analysis_imp_sub1_sram #(axi4_vip_item, top_chip_dv_axi_scoreboard) sub1_sram_imp; + uvm_analysis_imp_sub2_mailbox #(axi4_vip_item, top_chip_dv_axi_scoreboard) sub2_mailbox_imp; + uvm_analysis_imp_sub3_tlcrossbar #(axi4_vip_item, top_chip_dv_axi_scoreboard) sub3_tlcrossbar_imp; + uvm_analysis_imp_sub4_dram #(axi4_vip_item, top_chip_dv_axi_scoreboard) sub4_dram_imp; + + protected axi4_vip_item expected_queue[string][masked_id_t][$]; + protected axi4_vip_item actual_queue[string][masked_id_t][$]; + + extern function new(string name, uvm_component parent); + extern virtual function void build_phase(uvm_phase phase); + // Record a subordinate address range from its base and size. + extern protected function void add_mem_range(string name, + bit [63:0] start_addr, + bit [63:0] addr_size); + // Mask the AXI ID back to the manager ID width before matching transactions. + extern protected function masked_id_t get_masked_id(bit [63:0] raw_id); + // Return the subordinate range name that contains addr, or the default target. + extern protected function string addr_to_mem_range(bit [63:0] addr); + extern virtual function void perform_comparison(axi4_vip_item exp, + axi4_vip_item act, + string sub); + + extern virtual function void write_mgr0_cva6(axi4_vip_item tr); + extern virtual function void write_sub0_romctrlmem(axi4_vip_item tr); + extern virtual function void write_sub1_sram(axi4_vip_item tr); + extern virtual function void write_sub2_mailbox(axi4_vip_item tr); + extern virtual function void write_sub3_tlcrossbar(axi4_vip_item tr); + extern virtual function void write_sub4_dram(axi4_vip_item tr); + + extern protected function void check_subordinate_arrival(axi4_vip_item act, string sub_id); + extern virtual function void check_phase(uvm_phase phase); + +endclass : top_chip_dv_axi_scoreboard + +function top_chip_dv_axi_scoreboard::new(string name, uvm_component parent); + super.new(name, parent); + mgr0_cva6_imp = new("mgr0_cva6_imp", this); + sub0_romctrlmem_imp = new("sub0_romctrlmem_imp", this); + sub1_sram_imp = new("sub1_sram_imp", this); + sub2_mailbox_imp = new("sub2_mailbox_imp", this); + sub3_tlcrossbar_imp = new("sub3_tlcrossbar_imp", this); + sub4_dram_imp = new("sub4_dram_imp", this); +endfunction : new + +function void top_chip_dv_axi_scoreboard::build_phase(uvm_phase phase); + super.build_phase(phase); + + add_mem_range("sub0_romctrlmem", top_pkg::RomCtrlMemBase, top_pkg::RomCtrlMemLength); + add_mem_range("sub1_sram", top_pkg::SRAMBase, top_pkg::SRAMLength); + add_mem_range("sub2_mailbox", top_pkg::MailboxBase, top_pkg::MailboxLength); + add_mem_range("sub3_tlcrossbar", top_pkg::TlCrossbarBase, top_pkg::TlCrossbarLength); + add_mem_range("sub4_dram", top_pkg::DRAMBase, top_pkg::DRAMUsableLength); +endfunction : build_phase + +function void top_chip_dv_axi_scoreboard::add_mem_range(string name, + bit [63:0] start_addr, + bit [63:0] addr_size); + bit [63:0] end_addr = start_addr + addr_size - 1; + mem_map.push_back('{name, start_addr, end_addr}); +endfunction : add_mem_range + +function top_chip_dv_axi_scoreboard::masked_id_t + top_chip_dv_axi_scoreboard::get_masked_id(bit [63:0] raw_id); + bit [63:0] mask = (64'(1) << top_pkg::AxiIdWidth) - 1; + return masked_id_t'(raw_id & mask); +endfunction : get_masked_id + +function string top_chip_dv_axi_scoreboard::addr_to_mem_range(bit [63:0] addr); + foreach (mem_map[i]) begin + if (addr >= mem_map[i].start_addr && addr <= mem_map[i].end_addr) begin + return mem_map[i].subordinate_name; + end + end + return default_subordinate_id; +endfunction : addr_to_mem_range + +function void top_chip_dv_axi_scoreboard::perform_comparison(axi4_vip_item exp, + axi4_vip_item act, + string sub); + masked_id_t mid = get_masked_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: ", + "act={addr:%h len:%h size:%h burst:%h} ", + "exp={addr:%h len:%h size:%h burst:%h}"}, + sub, mid, act.awaddr, act.awlen, act.awsize, act.awburst, + exp.awaddr, exp.awlen, exp.awsize, exp.awburst)) + end + if (act.wdata != exp.wdata) begin + `uvm_error("SCB_WDATA", + $sformatf("Write data mismatch on %s ID:%h: act=%p exp=%p", + sub, mid, act.wdata, exp.wdata)) + end + if (act.wstrb != exp.wstrb) begin + `uvm_error("SCB_WSTRB", + $sformatf("Write strobe mismatch on %s ID:%h: act=%p exp=%p", + sub, mid, act.wstrb, exp.wstrb)) + end + 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: ", + "act={addr:%h len:%h size:%h burst:%h} ", + "exp={addr:%h len:%h size:%h burst:%h}"}, + sub, mid, act.araddr, act.arlen, act.arsize, act.arburst, + exp.araddr, exp.arlen, exp.arsize, exp.arburst)) + end + if (act.rdata != exp.rdata) begin + `uvm_error("SCB_RDATA", + $sformatf("Read data mismatch on %s ID:%h: act=%p exp=%p", + sub, mid, act.rdata, exp.rdata)) + end + if (act.rresp != exp.rresp) begin + `uvm_error("SCB_RRESP", + $sformatf("Read response mismatch on %s ID:%h: act=%p exp=%p", + sub, mid, act.rresp, exp.rresp)) + end + end +endfunction : perform_comparison + +function void top_chip_dv_axi_scoreboard::write_mgr0_cva6(axi4_vip_item tr); + bit [63:0] addr = (tr.dir == AXI_WRITE) ? tr.awaddr : tr.araddr; + bit [63:0] raw_id = (tr.dir == AXI_WRITE) ? tr.awid : tr.arid; + string target_sub = addr_to_mem_range(addr); + masked_id_t mid = get_masked_id(raw_id); + + if (target_sub == default_subordinate_id) begin + `uvm_error("SCB_ADDR_DECODE", $sformatf("Manager access to unmapped address: %h", addr)) + end else begin + if (actual_queue[target_sub].exists(mid) && actual_queue[target_sub][mid].size() > 0) begin + axi4_vip_item act_tr = actual_queue[target_sub][mid].pop_front(); + perform_comparison(tr, act_tr, target_sub); + end else begin + expected_queue[target_sub][mid].push_back(tr.item_clone()); + end + end +endfunction : write_mgr0_cva6 + +function void top_chip_dv_axi_scoreboard::check_subordinate_arrival(axi4_vip_item act, + string sub_id); + masked_id_t mid = get_masked_id((act.dir == AXI_WRITE) ? act.bid : act.rid); + + if (expected_queue[sub_id].exists(mid) && expected_queue[sub_id][mid].size() > 0) begin + axi4_vip_item exp_tr = expected_queue[sub_id][mid].pop_front(); + perform_comparison(exp_tr, act, sub_id); + end else begin + actual_queue[sub_id][mid].push_back(act.item_clone()); + end +endfunction : check_subordinate_arrival + +function void top_chip_dv_axi_scoreboard::write_sub0_romctrlmem(axi4_vip_item tr); + check_subordinate_arrival(tr, "sub0_romctrlmem"); +endfunction : write_sub0_romctrlmem + +function void top_chip_dv_axi_scoreboard::write_sub1_sram(axi4_vip_item tr); + check_subordinate_arrival(tr, "sub1_sram"); +endfunction : write_sub1_sram + +function void top_chip_dv_axi_scoreboard::write_sub2_mailbox(axi4_vip_item tr); + check_subordinate_arrival(tr, "sub2_mailbox"); +endfunction : write_sub2_mailbox + +function void top_chip_dv_axi_scoreboard::write_sub3_tlcrossbar(axi4_vip_item tr); + check_subordinate_arrival(tr, "sub3_tlcrossbar"); +endfunction : write_sub3_tlcrossbar + +function void top_chip_dv_axi_scoreboard::write_sub4_dram(axi4_vip_item tr); + check_subordinate_arrival(tr, "sub4_dram"); +endfunction : write_sub4_dram + +function void top_chip_dv_axi_scoreboard::check_phase(uvm_phase phase); + super.check_phase(phase); + + foreach (expected_queue[s, i]) begin + if (expected_queue[s][i].size() > 0) begin + `uvm_error("SCB_DRAIN_DROP", $sformatf("DROPPED: Manager request for %s (ID %h) lost", s, i)) + end + end + + foreach (actual_queue[s, i]) begin + if (actual_queue[s][i].size() > 0) begin + `uvm_error("SCB_DRAIN_GHOST", + $sformatf("GHOST: Subordinate %s (ID %h) responded without a manager request", + s, i)) + end + end +endfunction : check_phase 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 7083a2a41..d4ec6690e 100644 --- a/hw/top_chip/dv/env/top_chip_dv_env.core +++ b/hw/top_chip/dv/env/top_chip_dv_env.core @@ -11,6 +11,7 @@ filesets: - lowrisc:dv:dv_utils - lowrisc:dv:mem_bkdr_util - lowrisc:dv:uart_agent + - lowrisc:dv:axi4_vip:0.1 - lowrisc:dv:common_ifs - lowrisc:mocha_dv:gpio_env files: @@ -18,6 +19,7 @@ filesets: - 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 b8896f4d1..3004b957d 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,11 @@ 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_mgr_axi[]; + axi4_vip_env m_sub_axi[]; + + top_chip_dv_axi_scoreboard m_axi_scb; // Standard SV/UVM methods extern function new(string name = "", uvm_component parent = null); @@ -71,6 +75,18 @@ 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_mgr_axi = new[top_pkg::AxiXbarHosts]; + m_mgr_axi[top_pkg::CVA6] = axi4_vip_env::type_id::create("m_mgr_axi_CVA6", this); + + m_sub_axi = new[top_pkg::AxiXbarDevices]; + m_sub_axi[top_pkg::RomCtrlMem] = axi4_vip_env::type_id::create("m_sub_axi_RomCtrlMem", this); + m_sub_axi[top_pkg::SRAM] = axi4_vip_env::type_id::create("m_sub_axi_SRAM", this); + m_sub_axi[top_pkg::Mailbox] = axi4_vip_env::type_id::create("m_sub_axi_Mailbox", this); + m_sub_axi[top_pkg::TlCrossbar] = axi4_vip_env::type_id::create("m_sub_axi_TlCrossbar", this); + m_sub_axi[top_pkg::DRAM] = axi4_vip_env::type_id::create("m_sub_axi_DRAM", this); + + 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); @@ -87,6 +103,13 @@ 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_mgr_axi[top_pkg::CVA6].m_manager.m_monitor.tx_ap.connect(m_axi_scb.mgr0_cva6_imp); + m_sub_axi[top_pkg::RomCtrlMem].m_subordinate.m_monitor.tx_ap.connect(m_axi_scb.sub0_romctrlmem_imp); + m_sub_axi[top_pkg::SRAM].m_subordinate.m_monitor.tx_ap.connect(m_axi_scb.sub1_sram_imp); + m_sub_axi[top_pkg::Mailbox].m_subordinate.m_monitor.tx_ap.connect(m_axi_scb.sub2_mailbox_imp); + m_sub_axi[top_pkg::TlCrossbar].m_subordinate.m_monitor.tx_ap.connect(m_axi_scb.sub3_tlcrossbar_imp); + m_sub_axi[top_pkg::DRAM].m_subordinate.m_monitor.tx_ap.connect(m_axi_scb.sub4_dram_imp); endfunction : connect_phase task top_chip_dv_env::load_memories(); diff --git a/hw/top_chip/dv/env/top_chip_dv_env_cfg.sv b/hw/top_chip/dv/env/top_chip_dv_env_cfg.sv index c3aef365d..c448d1386 100644 --- a/hw/top_chip/dv/env/top_chip_dv_env_cfg.sv +++ b/hw/top_chip/dv/env/top_chip_dv_env_cfg.sv @@ -18,6 +18,8 @@ class top_chip_dv_env_cfg extends uvm_object; // External interface agent configs rand uart_agent_cfg m_uart_agent_cfg; + axi4_vip_env_cfg m_axi_mgr_cfg[]; + axi4_vip_env_cfg m_axi_sub_cfg[]; `uvm_object_utils_begin(top_chip_dv_env_cfg) `uvm_object_utils_end 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 e65aaab3d..9bf8a2f38 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 @@ -10,6 +10,7 @@ package top_chip_dv_env_pkg; import sw_test_status_pkg::*; import uart_agent_pkg::*; import gpio_env_pkg::NUM_GPIOS; + import axi4_vip_pkg::*; // Macro includes `include "uvm_macros.svh" @@ -22,13 +23,20 @@ package top_chip_dv_env_pkg; typedef chip_mem_e chip_mem_list_t[$]; + // Local Address Map Struct + typedef struct { + string subordinate_name; + bit [63:0] start_addr; + bit [63:0] end_addr; + } axi_addr_range_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(); function automatic chip_mem_list_t chip_mem_values; chip_mem_list_t list; - chip_mem_e tmp = tmp.first; + chip_mem_e tmp = tmp.first; do begin list.push_back(tmp); tmp = tmp.next; @@ -52,6 +60,7 @@ package top_chip_dv_env_pkg; `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/axi_vip_connections.sv b/hw/top_chip/dv/tb/axi_vip_connections.sv new file mode 100644 index 000000000..216d88c8d --- /dev/null +++ b/hw/top_chip/dv/tb/axi_vip_connections.sv @@ -0,0 +1,89 @@ +// Copyright lowRISC contributors (COSMIC project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +`define AXI4_VIP_CONNECT_CLK_RST(dst) \ + assign dst.aclk = clk; \ + assign dst.aresetn = rst_n + +`define AXI4_VIP_CONNECT_REQ(dst, src) \ + assign dst.awvalid = src.aw_valid; \ + assign dst.awid = src.aw.id; \ + assign dst.awaddr = src.aw.addr; \ + assign dst.awlen = src.aw.len; \ + assign dst.awsize = src.aw.size; \ + assign dst.awburst = src.aw.burst; \ + assign dst.awlock = src.aw.lock; \ + assign dst.awcache = src.aw.cache; \ + assign dst.awprot = src.aw.prot; \ + assign dst.awqos = src.aw.qos; \ + assign dst.awregion = src.aw.region; \ + assign dst.awuser = src.aw.user; \ + assign dst.wvalid = src.w_valid; \ + assign dst.wdata = src.w.data; \ + assign dst.wstrb = src.w.strb; \ + assign dst.wlast = src.w.last; \ + assign dst.wuser = src.w.user; \ + assign dst.arvalid = src.ar_valid; \ + assign dst.arid = src.ar.id; \ + assign dst.araddr = src.ar.addr; \ + assign dst.arlen = src.ar.len; \ + assign dst.arsize = src.ar.size; \ + assign dst.arburst = src.ar.burst; \ + assign dst.arlock = src.ar.lock; \ + assign dst.arcache = src.ar.cache; \ + assign dst.arprot = src.ar.prot; \ + assign dst.arqos = src.ar.qos; \ + assign dst.arregion = src.ar.region; \ + assign dst.aruser = src.ar.user; \ + assign dst.bready = src.b_ready; \ + assign dst.rready = src.r_ready + +`define AXI4_VIP_CONNECT_RESP(dst, src) \ + assign dst.awready = src.aw_ready; \ + assign dst.wready = src.w_ready; \ + assign dst.arready = src.ar_ready; \ + assign dst.bvalid = src.b_valid; \ + assign dst.bid = src.b.id; \ + assign dst.bresp = src.b.resp; \ + assign dst.buser = src.b.user; \ + assign dst.rvalid = src.r_valid; \ + assign dst.rid = src.r.id; \ + assign dst.rdata = src.r.data; \ + assign dst.rresp = src.r.resp; \ + assign dst.rlast = src.r.last; \ + assign dst.ruser = src.r.user + +`define AXI4_VIP_CONNECT_IF(dst, req, rsp) \ + `AXI4_VIP_CONNECT_CLK_RST(dst); \ + `AXI4_VIP_CONNECT_REQ(dst, req); \ + `AXI4_VIP_CONNECT_RESP(dst, rsp); + +`AXI4_VIP_CONNECT_IF(axi4_mgr_if[top_pkg::CVA6], + `AXI_XBAR_HIER.slv_ports_req_i[top_pkg::CVA6], + `AXI_XBAR_HIER.slv_ports_resp_o[top_pkg::CVA6]) + +`AXI4_VIP_CONNECT_IF(axi4_sub_if[top_pkg::RomCtrlMem], + `AXI_XBAR_HIER.mst_ports_req_o[top_pkg::RomCtrlMem], + `AXI_XBAR_HIER.mst_ports_resp_i[top_pkg::RomCtrlMem]) + +`AXI4_VIP_CONNECT_IF(axi4_sub_if[top_pkg::SRAM], + `AXI_XBAR_HIER.mst_ports_req_o[top_pkg::SRAM], + `AXI_XBAR_HIER.mst_ports_resp_i[top_pkg::SRAM]) + +`AXI4_VIP_CONNECT_IF(axi4_sub_if[top_pkg::Mailbox], + `AXI_XBAR_HIER.mst_ports_req_o[top_pkg::Mailbox], + `AXI_XBAR_HIER.mst_ports_resp_i[top_pkg::Mailbox]) + +`AXI4_VIP_CONNECT_IF(axi4_sub_if[top_pkg::TlCrossbar], + `AXI_XBAR_HIER.mst_ports_req_o[top_pkg::TlCrossbar], + `AXI_XBAR_HIER.mst_ports_resp_i[top_pkg::TlCrossbar]) + +`AXI4_VIP_CONNECT_IF(axi4_sub_if[top_pkg::DRAM], + `AXI_XBAR_HIER.mst_ports_req_o[top_pkg::DRAM], + `AXI_XBAR_HIER.mst_ports_resp_i[top_pkg::DRAM]) + +`undef AXI4_VIP_CONNECT_IF +`undef AXI4_VIP_CONNECT_RESP +`undef AXI4_VIP_CONNECT_REQ +`undef AXI4_VIP_CONNECT_CLK_RST diff --git a/hw/top_chip/dv/tb/chip_hier_macros.svh b/hw/top_chip/dv/tb/chip_hier_macros.svh index 210841a32..62d3de765 100644 --- a/hw/top_chip/dv/tb/chip_hier_macros.svh +++ b/hw/top_chip/dv/tb/chip_hier_macros.svh @@ -8,6 +8,7 @@ `define SRAM_MEM_HIER `SYSTEM_HIER.u_axi_sram.u_ram.mem `define TAG_MEM_HIER `SYSTEM_HIER.u_axi_sram.u_tag_ram.mem `define ROM_MEM_HIER `SYSTEM_HIER.u_rom_ctrl.gen_rom_scramble_disabled.u_rom.u_prim_rom.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 f842fe624..1f52a014b 100644 --- a/hw/top_chip/dv/tb/tb.sv +++ b/hw/top_chip/dv/tb/tb.sv @@ -11,6 +11,7 @@ module tb; import top_chip_dv_env_pkg::*; import top_chip_dv_test_pkg::*; import gpio_env_pkg::NUM_GPIOS; + 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; @@ -37,6 +38,11 @@ module tb; clk_rst_if sys_clk_if(.clk(clk), .rst_n(rst_n)); uart_if uart_if(); pins_if #(NUM_GPIOS) gpio_pins_if (.pins(gpio_pads)); + axi4_vip_if axi4_mgr_if[top_pkg::AxiXbarHosts](); + axi4_vip_if axi4_sub_if[top_pkg::AxiXbarDevices](); + + // AXI VIP connections are included from a separate file for better reading + `include "axi_vip_connections.sv" // ------ Mock DRAM ------ top_pkg::axi_dram_req_t dram_req; @@ -241,6 +247,20 @@ module tb; uvm_config_db#(virtual uart_if)::set(null, "*.env.m_uart_agent*", "vif", uart_if); uvm_config_db#(virtual pins_if #(NUM_GPIOS))::set(null, "*.env", "gpio_vif", gpio_pins_if); + // AXI VIFs + uvm_config_db#(virtual axi4_vip_if)::set( + null, "*.m_mgr_axi_CVA6.*", "vif", axi4_mgr_if[top_pkg::CVA6]); + uvm_config_db#(virtual axi4_vip_if)::set( + null, "*.m_sub_axi_RomCtrlMem.*", "vif", axi4_sub_if[top_pkg::RomCtrlMem]); + uvm_config_db#(virtual axi4_vip_if)::set( + null, "*.m_sub_axi_SRAM.*", "vif", axi4_sub_if[top_pkg::SRAM]); + uvm_config_db#(virtual axi4_vip_if)::set( + null, "*.m_sub_axi_Mailbox.*", "vif", axi4_sub_if[top_pkg::Mailbox]); + uvm_config_db#(virtual axi4_vip_if)::set( + null, "*.m_sub_axi_TlCrossbar.*", "vif", axi4_sub_if[top_pkg::TlCrossbar]); + uvm_config_db#(virtual axi4_vip_if)::set( + null, "*.m_sub_axi_DRAM.*", "vif", axi4_sub_if[top_pkg::DRAM]); + // 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); 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 4be12e3b9..9c306bcc1 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 = nul endfunction : new function void top_chip_dv_base_test::build_phase(uvm_phase phase); + axi4_vip_env_cfg axi_mgr_cfg[]; + axi4_vip_env_cfg axi_sub_cfg[]; + dv_report_server m_dv_report_server = new(); uvm_report_server::set_server(m_dv_report_server); @@ -36,6 +39,104 @@ 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_mgr_cfg = new[top_pkg::AxiXbarHosts]; + axi_mgr_cfg[top_pkg::CVA6] = axi4_vip_env_cfg::type_id::create("m_axi_CVA6_cfg", this); + axi_mgr_cfg[top_pkg::CVA6].set_config(.inst_id ("CVA6"), + .has_manager (1), + .manager_active_passive (UVM_PASSIVE), + .has_subordinate (0), + .subordinate_active_passive (UVM_PASSIVE), + .id_width (top_pkg::AxiIdWidth), + .addr_width (top_pkg::AxiAddrWidth), + .data_width (top_pkg::AxiDataWidth), + .user_width (top_pkg::AxiUserWidth), + .region_width (4), + .qos_width (4)); + uvm_config_db#(axi4_vip_env_cfg)::set(this, "env.m_mgr_axi_CVA6*", "m_cfg", + axi_mgr_cfg[top_pkg::CVA6]); + env.cfg.m_axi_mgr_cfg = axi_mgr_cfg; + + axi_sub_cfg = new[top_pkg::AxiXbarDevices]; + axi_sub_cfg[top_pkg::RomCtrlMem] = axi4_vip_env_cfg::type_id::create("m_axi_RomCtrlMem_cfg", + this); + axi_sub_cfg[top_pkg::RomCtrlMem].set_config(.inst_id ("RomCtrlMem"), + .has_manager (0), + .manager_active_passive (UVM_PASSIVE), + .has_subordinate (1), + .subordinate_active_passive (UVM_PASSIVE), + .id_width (top_pkg::AxiIdWidth), + .addr_width (top_pkg::AxiAddrWidth), + .data_width (top_pkg::AxiDataWidth), + .user_width (top_pkg::AxiUserWidth), + .region_width (4), + .qos_width (4)); + uvm_config_db#(axi4_vip_env_cfg)::set(this, "env.m_sub_axi_RomCtrlMem*", "m_cfg", + axi_sub_cfg[top_pkg::RomCtrlMem]); + + axi_sub_cfg[top_pkg::SRAM] = axi4_vip_env_cfg::type_id::create("m_axi_SRAM_cfg", this); + axi_sub_cfg[top_pkg::SRAM].set_config(.inst_id ("SRAM"), + .has_manager (0), + .manager_active_passive (UVM_PASSIVE), + .has_subordinate (1), + .subordinate_active_passive (UVM_PASSIVE), + .id_width (top_pkg::AxiIdWidth), + .addr_width (top_pkg::AxiAddrWidth), + .data_width (top_pkg::AxiDataWidth), + .user_width (top_pkg::AxiUserWidth), + .region_width (4), + .qos_width (4)); + uvm_config_db#(axi4_vip_env_cfg)::set(this, "env.m_sub_axi_SRAM*", "m_cfg", + axi_sub_cfg[top_pkg::SRAM]); + + axi_sub_cfg[top_pkg::Mailbox] = axi4_vip_env_cfg::type_id::create("m_axi_Mailbox_cfg", this); + axi_sub_cfg[top_pkg::Mailbox].set_config(.inst_id ("Mailbox"), + .has_manager (0), + .manager_active_passive (UVM_PASSIVE), + .has_subordinate (1), + .subordinate_active_passive (UVM_PASSIVE), + .id_width (top_pkg::AxiIdWidth), + .addr_width (top_pkg::AxiAddrWidth), + .data_width (top_pkg::AxiDataWidth), + .user_width (top_pkg::AxiUserWidth), + .region_width (4), + .qos_width (4)); + uvm_config_db#(axi4_vip_env_cfg)::set(this, "env.m_sub_axi_Mailbox*", "m_cfg", + axi_sub_cfg[top_pkg::Mailbox]); + + axi_sub_cfg[top_pkg::TlCrossbar] = axi4_vip_env_cfg::type_id::create( + "m_axi_TlCrossbar_cfg", this); + axi_sub_cfg[top_pkg::TlCrossbar].set_config(.inst_id ("TlCrossbar"), + .has_manager (0), + .manager_active_passive (UVM_PASSIVE), + .has_subordinate (1), + .subordinate_active_passive (UVM_PASSIVE), + .id_width (top_pkg::AxiIdWidth), + .addr_width (top_pkg::AxiAddrWidth), + .data_width (top_pkg::AxiDataWidth), + .user_width (top_pkg::AxiUserWidth), + .region_width (4), + .qos_width (4)); + uvm_config_db#(axi4_vip_env_cfg)::set(this, "env.m_sub_axi_TlCrossbar*", "m_cfg", + axi_sub_cfg[top_pkg::TlCrossbar]); + + axi_sub_cfg[top_pkg::DRAM] = axi4_vip_env_cfg::type_id::create("m_axi_DRAM_cfg", this); + axi_sub_cfg[top_pkg::DRAM].set_config(.inst_id ("DRAM"), + .has_manager (0), + .manager_active_passive (UVM_PASSIVE), + .has_subordinate (1), + .subordinate_active_passive (UVM_PASSIVE), + .id_width (top_pkg::AxiIdWidth), + .addr_width (top_pkg::AxiAddrWidth), + .data_width (top_pkg::AxiDataWidth), + .user_width (top_pkg::AxiUserWidth), + .region_width (4), + .qos_width (4)); + uvm_config_db#(axi4_vip_env_cfg)::set(this, "env.m_sub_axi_DRAM*", "m_cfg", + axi_sub_cfg[top_pkg::DRAM]); + env.cfg.m_axi_sub_cfg = axi_sub_cfg; + 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" diff --git a/hw/top_chip/dv/top_chip_sim.core b/hw/top_chip/dv/top_chip_sim.core index 400dbaf69..dec522fef 100644 --- a/hw/top_chip/dv/top_chip_sim.core +++ b/hw/top_chip/dv/top_chip_sim.core @@ -28,6 +28,7 @@ filesets: files: - tb/tb.sv - tb/chip_hier_macros.svh: {is_include_file: true} + - tb/axi_vip_connections.sv: {is_include_file: true} - verilator/dram_wrapper_sim.sv file_type: systemVerilogSource