Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
e686aa6
[dv,axi] Implement interfaces for the subset of AXI in use
rswarbrick Jun 13, 2026
579723f
[dv,axi] Implement items for AXI read transactions
rswarbrick Jun 13, 2026
b59c688
[dv,axi] Implement items for AXI write transactions
rswarbrick Jun 14, 2026
76a5434
[dv,axi] Define an item type to control how we accept responses
rswarbrick Jun 15, 2026
0ab815d
[dv,axi] Implement drivers for the Manager side of write channels
rswarbrick Jun 14, 2026
eeea7ec
[dv,axi] Implement drivers for the Manager side of read channels
rswarbrick Jun 15, 2026
8676709
[dv,axi] Define item and monitor for resets on each AXI interface
rswarbrick Jun 16, 2026
c1089f4
[dv,axi] Define a router that maps IDs to responses
rswarbrick Jun 16, 2026
e078470
[dv,axi] Define axi_mgr_agent
rswarbrick Jun 15, 2026
270499c
[dv,axi] Define a sequence with a single read or write request
rswarbrick Jun 15, 2026
a474fd2
[dv,axi] Define sequences with AXI data writes
rswarbrick Jun 15, 2026
ebca3b5
[dv,axi] Define a sequence to consume the AXI write response
rswarbrick Jun 15, 2026
c161b61
[dv,axi] Define a sequence to consume AXI read data
rswarbrick Jun 15, 2026
5df0054
[dv,axi] A virtual sequence that does a single write transfer
rswarbrick Jun 15, 2026
fa138f2
[dv,axi] A virtual sequence that does a single read transfer
rswarbrick Jun 16, 2026
525ed3e
[dv,axi] Allow axi_agent to connect to the register layer
rswarbrick Jun 16, 2026
216931e
[dv,axi] fix axi handshaking for beat sampling on multi-beat traffic
tchilikov-semify Jul 8, 2026
a20857e
[dv,axi] add multi-beat transaction support to driver
tchilikov-semify Jul 8, 2026
f123077
[dv,axi] add clk_rst_if to axi interfaces
tchilikov-semify Jul 16, 2026
5a23f42
[dv,axi] add monitor to AXI agent
tchilikov-semify Jul 8, 2026
e9265c4
[dv,axi] add axi_vip wrapper to agent
tchilikov-semify Jul 17, 2026
7b39df5
[dv,top] integrate AXI4 monitors in top tb
tchilikov-semify Jul 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions hw/ip/dv/axi_agent/axi_agent.core
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
CAPI=2:
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
name: "lowrisc:dv:axi_agent:0.1"
description: "AXI agent"
filesets:
files_dv:
depend:
- lowrisc:dv:dv_utils
- lowrisc:dv:common_ifs
files:
- axi_read_data_if.sv
- axi_read_request_if.sv
- axi_write_data_if.sv
- axi_write_request_if.sv
- axi_write_response_if.sv

- axi_agent_pkg.sv
- axi_agent_cfg.svh: {is_include_file: true}

- axi_mon_types.svh: {is_include_file: true}
- axi_mon_item.svh: {is_include_file: true}
- axi_mon_write_item.svh: {is_include_file: true}
- axi_mon_read_item.svh: {is_include_file: true}
- axi_monitor.svh: {is_include_file: true}

- axi_reset_item.svh: {is_include_file: true}
- axi_status_item.svh: {is_include_file: true}
- axi_txn_request_item.svh: {is_include_file: true}
- axi_response_accept_item.svh: {is_include_file: true}
- axi_read_data_item.svh: {is_include_file: true}
- axi_write_data_item.svh: {is_include_file: true}
- axi_write_response_item.svh: {is_include_file: true}

- axi_fixed_write_req_item.svh: {is_include_file: true}
- axi_fixed_write_rsp_item.svh: {is_include_file: true}
- axi_fixed_read_req_item.svh: {is_include_file: true}
- axi_fixed_read_rsp_item.svh: {is_include_file: true}

- axi_reset_monitor.svh: {is_include_file: true}
Comment thread
tchilikov-semify marked this conversation as resolved.

- axi_mgr_write_request_driver.svh: {is_include_file: true}
- axi_mgr_write_data_driver.svh: {is_include_file: true}
- axi_mgr_write_response_driver.svh: {is_include_file: true}
- axi_mgr_read_request_driver.svh: {is_include_file: true}
- axi_mgr_read_data_driver.svh: {is_include_file: true}

- axi_response_router.svh: {is_include_file: true}

- axi_reg_op_item.svh: {is_include_file: true}
- axi_reg_adapter.svh: {is_include_file: true}

- axi_mgr_agent.svh: {is_include_file: true}

- seq_lib/axi_mgr_txn_request_seq.svh: {is_include_file: true}
- seq_lib/axi_mgr_write_data_seq.svh: {is_include_file: true}
- seq_lib/axi_mgr_write_single_data_seq.svh: {is_include_file: true}
- seq_lib/axi_mgr_write_listed_data_seq.svh: {is_include_file: true}
- seq_lib/axi_mgr_write_response_seq.svh: {is_include_file: true}
- seq_lib/axi_mgr_read_data_seq.svh: {is_include_file: true}

- seq_lib/axi_mgr_write_fixed_vseq.svh: {is_include_file: true}
- seq_lib/axi_mgr_write_burst_vseq.svh: {is_include_file: true}
- seq_lib/axi_mgr_read_fixed_vseq.svh: {is_include_file: true}
- seq_lib/axi_mgr_read_burst_vseq.svh: {is_include_file: true}

- seq_lib/axi_mgr_register_layer_vseq.svh: {is_include_file: true}

file_type: systemVerilogSource

targets:
default:
filesets:
- files_dv
42 changes: 42 additions & 0 deletions hw/ip/dv/axi_agent/axi_agent_cfg.svh
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright lowRISC contributors
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

// The configuration for an agent driving the interfaces for AXI (AW, W, B, AR, R).

class axi_agent_cfg extends uvm_object;
`uvm_object_utils(axi_agent_cfg)

// Interfaces
virtual clk_rst_if clk_rst_vif; // ACLK/ARESETn

Comment thread
tchilikov-semify marked this conversation as resolved.
virtual axi_write_request_if write_request_vif;
virtual axi_write_data_if write_data_vif;
virtual axi_write_response_if write_response_vif;
virtual axi_read_request_if read_request_vif;
virtual axi_read_data_if read_data_vif;

// ID and knobs
string inst_id = "axi_mgr"; // tag for logging
uvm_active_passive_enum is_active = UVM_ACTIVE; // gates the driver; the monitor is always built
bit enable_coverage = 1'b0;

extern function new(string name = "axi_agent_cfg");

// Set the config fields in one call. Interfaces are set by the VIP instantiation.
extern function void set_config(string inst_id = "axi_mgr",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm really not a fan of big lines of arguments with default values. As I wrote above, I think you only need to set is_active and enable_coverage. I'd suggest forcing the caller to pass both.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that the other fields were not needed - However I think that the inst_id carries value and should be kept. This field is rather useful for logging and debugging when there are multiple VIP instances. Even in our current integration, we have multiple monitors. I like the idea of being able to set a descriptive name for each instance that can show up on the logs.

As for the default values - I can remove these, although since its only 3 values, I dont think they hinder much.

Let me know what you prefer for both!

uvm_active_passive_enum is_active = UVM_ACTIVE,
bit enable_coverage = 1'b0);
endclass

function axi_agent_cfg::new(string name = "axi_agent_cfg");
super.new(name);
endfunction

function void axi_agent_cfg::set_config(string inst_id = "axi_mgr",
uvm_active_passive_enum is_active = UVM_ACTIVE,
bit enable_coverage = 1'b0);
this.inst_id = inst_id;
this.is_active = is_active;
this.enable_coverage = enable_coverage;
endfunction
78 changes: 78 additions & 0 deletions hw/ip/dv/axi_agent/axi_agent_pkg.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Copyright lowRISC contributors
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

package axi_agent_pkg;
import uvm_pkg::*;
`include "uvm_macros.svh"

// The possible encodings of the AxBURST signal
typedef enum bit [1:0] {
BurstFixed = 0,
BurstIncr = 1,
BurstWrap = 2
} burst_e;

`include "axi_agent_cfg.svh"

`include "axi_txn_request_item.svh"
`include "axi_read_data_item.svh"

`include "axi_write_data_item.svh"
`include "axi_write_response_item.svh"

`include "axi_response_accept_item.svh"

`include "axi_status_item.svh"

`include "axi_mgr_write_request_driver.svh"
`include "axi_mgr_write_data_driver.svh"
`include "axi_mgr_write_response_driver.svh"

`include "axi_mgr_read_request_driver.svh"
`include "axi_mgr_read_data_driver.svh"

`include "axi_reset_item.svh"

// Reset monitor for the shared AXI clock/reset (one instance covers all channels).
`include "axi_reset_monitor.svh"

`include "axi_reg_op_item.svh"
`include "axi_reg_adapter.svh"

typedef uvm_sequencer#(axi_txn_request_item, axi_status_item) write_request_sequencer_t;
typedef uvm_sequencer#(axi_write_data_item, axi_status_item) write_data_sequencer_t;
typedef uvm_sequencer#(axi_response_accept_item, uvm_sequence_item) write_response_sequencer_t;
typedef uvm_sequencer#(axi_txn_request_item, axi_status_item) read_request_sequencer_t;
typedef uvm_sequencer#(axi_response_accept_item, uvm_sequence_item) read_data_sequencer_t;
typedef uvm_sequencer#(axi_reg_op_item, uvm_sequence_item) reg_op_sequencer_t;

`include "axi_response_router.svh"

`include "seq_lib/axi_mgr_txn_request_seq.svh"
`include "seq_lib/axi_mgr_write_data_seq.svh"
`include "seq_lib/axi_mgr_write_single_data_seq.svh"
`include "seq_lib/axi_mgr_write_listed_data_seq.svh"
`include "seq_lib/axi_mgr_write_response_seq.svh"
`include "seq_lib/axi_mgr_read_data_seq.svh"

`include "axi_fixed_write_req_item.svh"
`include "axi_fixed_write_rsp_item.svh"
`include "seq_lib/axi_mgr_write_fixed_vseq.svh"
`include "seq_lib/axi_mgr_write_burst_vseq.svh"

`include "axi_fixed_read_req_item.svh"
`include "axi_fixed_read_rsp_item.svh"
`include "seq_lib/axi_mgr_read_fixed_vseq.svh"
`include "seq_lib/axi_mgr_read_burst_vseq.svh"

`include "seq_lib/axi_mgr_register_layer_vseq.svh"

`include "axi_mon_types.svh"
`include "axi_mon_item.svh"
`include "axi_mon_write_item.svh"
`include "axi_mon_read_item.svh"
`include "axi_monitor.svh"

`include "axi_mgr_agent.svh"
endpackage
106 changes: 106 additions & 0 deletions hw/ip/dv/axi_agent/axi_fixed_read_req_item.svh
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// Copyright lowRISC contributors
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

// A sequence item that represents the request that should be made to send a single AXI read with
// AWBURST set to FIXED and AWLEN = 0 (so just a single data transfer).

class axi_fixed_read_req_item extends uvm_sequence_item;
`uvm_object_utils(axi_fixed_read_req_item)
// Transaction identifier for the read channel
//
// This is sent as AWID, whose width is configurable (and will be checked against the
// corresponding width in the interface by the driver).
rand bit [31:0] m_id;

// The address.
//
// This is AWADDR, whose width is configurable (and will be checked against the
// corresponding width in the interface by the driver).
rand bit [63:0] m_addr;

// The region identifier, sent as AWREGION in the request transfer.
rand bit [3:0] m_region;

// Number of bytes in the data transfer. Sent as AWSIZE and encoded as log2(byte_size).
rand bit [2:0] m_size;

// Request exclusive access? Sent as AWLOCK.
rand bit m_lock;

// Memory attributes (applying to caches in the system). Sent as AWCACHE.
rand bit [3:0] m_cache;

// Memory access attributes. Sent as AWPROT.
rand bit [2:0] m_prot;

// Traffic stream QoS identifier. Sent as AWQOS.
rand bit [3:0] m_qos;

// Extra user bits.
//
// This is sent as AWUSER, whose width is configurable (and will be checked against the
// corresponding width in the interface by the driver).
rand bit [127:0] m_user;

extern function new(string name = "");
extern function void do_print(uvm_printer printer);
extern function void do_copy(uvm_object rhs);
extern function bit do_compare(uvm_object rhs, uvm_comparer comparer);
endclass

function axi_fixed_read_req_item::new(string name = "");
super.new(name);
endfunction

function void axi_fixed_read_req_item::do_print(uvm_printer printer);
super.do_print(printer);
printer.print_field_int("m_id", m_id, 32, UVM_HEX);
printer.print_field("m_addr", m_addr, 64, UVM_HEX);
printer.print_field_int("m_region", m_region, 4, UVM_HEX);
printer.print_field_int("m_size", m_size, 3, UVM_DEC);
printer.print_field_int("m_lock", m_lock, 1, UVM_BIN);
printer.print_field_int("m_cache", m_cache, 4, UVM_BIN);
printer.print_field_int("m_prot", m_prot, 3, UVM_BIN);
printer.print_field_int("m_qos", m_qos, 4, UVM_HEX);
printer.print_field("m_user", m_user, 128, UVM_HEX);
endfunction

function void axi_fixed_read_req_item::do_copy(uvm_object rhs);
axi_fixed_read_req_item rhs_;

if (rhs == null) `uvm_fatal("do_copy", "Cannot copy from RHS: it is null.")
if (!$cast(rhs_, rhs)) `uvm_fatal("do_copy", "Cannot cast RHS: wrong type?")

super.do_copy(rhs);
this.m_id = rhs_.m_id;
this.m_addr = rhs_.m_addr;
this.m_region = rhs_.m_region;
this.m_size = rhs_.m_size;
this.m_lock = rhs_.m_lock;
this.m_cache = rhs_.m_cache;
this.m_prot = rhs_.m_prot;
this.m_qos = rhs_.m_qos;
this.m_user = rhs_.m_user;
endfunction

function bit axi_fixed_read_req_item::do_compare(uvm_object rhs, uvm_comparer comparer);
axi_fixed_read_req_item rhs_;

// These items are only equivalent if rhs is actually an axi_fixed_read_req_item.
if (rhs == null || !$cast(rhs_, rhs)) begin
comparer.print_msg("RHS is null or is not an axi_fixed_read_req_item.");
return 0;
end

return (super.do_compare(rhs, comparer) &
comparer.compare_field_int("m_id", m_id, rhs_.m_id, 32, UVM_HEX) &
comparer.compare_field("m_addr", m_addr, rhs_.m_addr, 64, UVM_HEX) &
comparer.compare_field_int("m_region", m_region, rhs_.m_region, 4, UVM_HEX) &
comparer.compare_field_int("m_size", m_size, rhs_.m_size, 3, UVM_DEC) &
comparer.compare_field_int("m_lock", m_lock, rhs_.m_lock, 1, UVM_BIN) &
comparer.compare_field_int("m_cache", m_cache, rhs_.m_cache, 4, UVM_BIN) &
comparer.compare_field_int("m_prot", m_prot, rhs_.m_prot, 3, UVM_BIN) &
comparer.compare_field_int("m_qos", m_qos, rhs_.m_qos, 4, UVM_HEX) &
comparer.compare_field("m_user", m_user, rhs_.m_user, 128, UVM_HEX));
endfunction
65 changes: 65 additions & 0 deletions hw/ip/dv/axi_agent/axi_fixed_read_rsp_item.svh
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright lowRISC contributors
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

// A sequence item that represents a response to an AXI read with a single beat
//
// The fields are null when the instance is created, and should be filled with results of the two
// sequences (which may be null if the sequences were interrupted by a reset).

class axi_fixed_read_rsp_item extends uvm_sequence_item;
`uvm_object_utils(axi_fixed_read_rsp_item)
// The status of the read request transfer (which either completed or was interrupted by reset)
axi_status_item m_ar_status;

// The single read data item seen (on the R channel)
axi_read_data_item m_read_data;

extern function new(string name = "");
extern function void do_print(uvm_printer printer);
extern function void do_copy(uvm_object rhs);
extern function bit do_compare(uvm_object rhs, uvm_comparer comparer);
endclass

function axi_fixed_read_rsp_item::new(string name = "");
super.new(name);
endfunction

function void axi_fixed_read_rsp_item::do_print(uvm_printer printer);
super.do_print(printer);
printer.print_object("m_ar_status", m_ar_status);
printer.print_object("m_read_data", m_read_data);
endfunction

function void axi_fixed_read_rsp_item::do_copy(uvm_object rhs);
axi_fixed_read_rsp_item rhs_;

if (rhs == null) `uvm_fatal("do_copy", "Cannot copy from RHS: it is null.")
if (!$cast(rhs_, rhs)) `uvm_fatal("do_copy", "Cannot cast RHS: wrong type?")

super.do_copy(rhs);
if (rhs_.m_ar_status == null) m_ar_status = null;
else if (!$cast(m_ar_status, rhs_.m_ar_status.clone())) begin
`uvm_fatal("do_copy", "Failed to clone m_ar_status.")
end

if (rhs_.m_read_data == null) m_read_data = null;
else if (!$cast(m_read_data, rhs_.m_read_data.clone())) begin
`uvm_fatal("do_copy", "Failed to clone m_read_data.")
end
endfunction

function bit axi_fixed_read_rsp_item::do_compare(uvm_object rhs, uvm_comparer comparer);
bit all_match = 1;
axi_fixed_read_rsp_item rhs_;

// These items are only equivalent if rhs is actually an axi_fixed_read_rsp_item.
if (rhs == null || !$cast(rhs_, rhs)) begin
comparer.print_msg("RHS is null or is not an axi_fixed_read_rsp_item.");
return 0;
end

return (super.do_compare(rhs, comparer) &
comparer.compare_object("m_ar_status", m_ar_status, rhs_.m_ar_status) &
comparer.compare_object("m_read_data", m_read_data, rhs_.m_read_data));
endfunction
Loading