Separated manager and subordinate drivers

Other clean up and changes
This commit is contained in:
2025-08-23 17:03:40 -07:00
parent 3e82cd5455
commit 8d8ea4443c
9 changed files with 252 additions and 116 deletions

View File

@@ -5,14 +5,15 @@
typedef axi_sequencer; typedef axi_sequencer;
class axi_agent extends uvm_agent; class axi_agent extends uvm_agent;
axi_agent_type_t agent_type; axi_agent_type_t agent_type;
virtual `AXI_INTF.MANAGER m_if; virtual `AXI_INTF.MANAGER m_if;
virtual `AXI_INTF.SUBORDINATE s_if; virtual `AXI_INTF.SUBORDINATE s_if;
virtual `AXI_INTF mon_if;
// Declare the sequencer and driver // Declare the sequencer and driver
axi_sequencer sequencer; axi_sequencer sequencer;
axi_driver driver; axi_driver driver;
axi_monitor monitor; axi_monitor monitor;
`uvm_component_utils_begin(axi_agent) `uvm_component_utils_begin(axi_agent)
`uvm_field_enum(axi_agent_type_t, agent_type, UVM_DEFAULT) `uvm_field_enum(axi_agent_type_t, agent_type, UVM_DEFAULT)
@@ -35,13 +36,23 @@ class axi_agent extends uvm_agent;
// Build phase // Build phase
virtual function void build_phase(uvm_phase phase); virtual function void build_phase(uvm_phase phase);
sequencer = axi_sequencer::type_id::create("sequencer", this); sequencer = axi_sequencer::type_id::create("sequencer", this);
driver = axi_driver::type_id::create("driver", this); if (agent_type == MANAGER) begin
// TODO: Additional variable md should not be needed. We should
// able to use the 'driver' variable directly. But in Verilator
// it results in an error: "No common base class exists"
axi_manager_driver md = axi_manager_driver::type_id::create("driver", this);
driver = md;
end else begin // if (agent_type == SUBORDINATE) begin
axi_subordinate_driver sd = axi_subordinate_driver::type_id::create("driver", this);
driver = sd;
end
monitor = axi_monitor::type_id::create("monitor", this); monitor = axi_monitor::type_id::create("monitor", this);
// Propagete the agent type to driver and monitor // Propagete the agent type to driver and monitor
driver.set_agent_type(agent_type); driver.set_agent_type(agent_type);
monitor.set_agent_type(agent_type); monitor.set_agent_type(agent_type);
// Acquire the virtual interfaces from the configuration database
if (agent_type == MANAGER) begin if (agent_type == MANAGER) begin
if (!uvm_config_db#(virtual `AXI_INTF.MANAGER)::get(this, "", "axi_dvr_vif", m_if)) begin if (!uvm_config_db#(virtual `AXI_INTF.MANAGER)::get(this, "", "axi_dvr_vif", m_if)) begin
`uvm_fatal("axi_agent", "AXI agent MANAGER interface not configured") `uvm_fatal("axi_agent", "AXI agent MANAGER interface not configured")
@@ -54,8 +65,15 @@ class axi_agent extends uvm_agent;
`uvm_info("axi_agent", $sformatf("Using AXI agent SUBORDINATE interface: axi_dvr_vif"), UVM_LOW) `uvm_info("axi_agent", $sformatf("Using AXI agent SUBORDINATE interface: axi_dvr_vif"), UVM_LOW)
end end
driver.set_virtual_intefaces(m_if, s_if); if (!uvm_config_db#(virtual `AXI_INTF)::get(this.get_parent(), "", "axi_mon_vif", mon_if)) begin
monitor.set_virtual_intefaces(m_if, s_if); `uvm_fatal("axi_agent", "AXI monitor interface not configured")
end
`uvm_info("axi_agent", $sformatf("Using AXI monitor interface: axi_mon_vif"), UVM_LOW)
// Propagate the virtual interfaces to driver and monitor
driver.set_virtual_interfaces(m_if, s_if);
monitor.set_virtual_interfaces(m_if, s_if);
monitor.set_monitor_virtual_interface(mon_if);
endfunction endfunction
// -------------------------------------------------- // --------------------------------------------------

View File

@@ -23,97 +23,10 @@ class axi_driver extends uvm_driver; // #(axi_transaction);
// -------------------------------------------------- // --------------------------------------------------
// Set virtual interfaces // Set virtual interfaces
function void set_virtual_intefaces(virtual `AXI_INTF.MANAGER m_if_p, function void set_virtual_interfaces(virtual `AXI_INTF.MANAGER m_if_p,
virtual `AXI_INTF.SUBORDINATE s_if_p); virtual `AXI_INTF.SUBORDINATE s_if_p);
`uvm_info("set_virtual_intefaces", $sformatf("Setting virtual interfaces"), UVM_LOW) `uvm_info("set_virtual_interfaces", $sformatf("Setting virtual interfaces"), UVM_LOW)
m_if = m_if_p; m_if = m_if_p;
s_if = s_if_p; s_if = s_if_p;
// if (m_if == null && s_if == null) begin
// `uvm_fatal("AXI_DRIVER", "Both MANAGER and SUBORDINATE interfaces are null")
// end else if ((agent_type == MANAGER) && (m_if == null)) begin
// `uvm_error("AXI_DRIVER", $sformatf("MANAGER interface is null"))
// end else if ((agent_type == SUBORDINATE) && (s_if == null)) begin
// `uvm_error("AXI_DRIVER", $sformatf("SUBORDINATE interface is null"))
// end
endfunction endfunction
// --------------------------------------------------
// Run phase
virtual task run_phase(uvm_phase phase);
`uvm_info("axi_driver", $sformatf("Running AXI driver: %s as %s",
get_full_name(), agent_type.name()), UVM_LOW)
if (agent_type == MANAGER) begin
`uvm_info("axi_driver", $sformatf("Manager agent detected, driving transactions"), UVM_LOW)
drive_transactions();
end else if (agent_type == SUBORDINATE) begin
`uvm_info("axi_driver", $sformatf("Subordinate agent detected, responding to transactions"), UVM_LOW)
respond_to_transactions();
end else begin
`uvm_fatal("AXI_DRIVER", "Unknown agent type")
end
endtask
// ------------------------------------------------------------
task drive_transactions();
if (m_if == null) begin
`uvm_error("AXI_DRIVER", "MANAGER interface is null, cannot drive transactions")
end
forever begin
`uvm_info("axi_driver", $sformatf("Starting to drive transactions for %s (rst_n = %0b)",
get_full_name(), m_if.ARESETn), UVM_LOW)
if (m_if.ARESETn == 0) begin
`uvm_info("axi_driver", $sformatf("Waiting for reset to be released (rst_n = %0b)",
m_if.ARESETn), UVM_LOW)
@(posedge m_if.ARESETn);
end
`uvm_info("axi_driver", $sformatf("Starting to drive transactions for %s (rst_n = %0b)",
get_full_name(), m_if.ARESETn), UVM_LOW)
while (m_if.ARESETn != 0) begin
uvm_sequence_item txn;
`uvm_info("axi_driver", $sformatf("Waiting for next transaction"), UVM_LOW)
seq_item_port.get_next_item(txn);
drive_item(txn);
seq_item_port.item_done();
end
end
endtask
// ------------------------------------------------------------
task drive_item(uvm_sequence_item txn);
axi_transaction req;
if (!$cast(req, txn)) begin
`uvm_fatal("AXI_DRIVER", "Invalid transaction type")
end
`uvm_info("drive_item", $sformatf("Driving AXI transaction:\n%s", req.sprint()), UVM_LOW)
@(posedge m_if.ACLK);
m_if.AWVALID = 1;
m_if.AWADDR = req.addr;
@(posedge m_if.ACLK);
m_if.WDATA = req.data;
m_if.WSTRB = req.strb;
`uvm_info("drive_item", $sformatf("Driving AXI transaction done."), UVM_LOW)
endtask
// ------------------------------------------------------------
task respond_to_transactions();
forever begin
@(posedge s_if.ARESETn);
while (s_if.ARESETn != 0) begin
@(posedge s_if.ACLK);
@(negedge s_if.ACLK);
end
end
endtask
endclass : axi_driver endclass : axi_driver

View File

@@ -0,0 +1,101 @@
// ----------------------------------------------------------------------
// AXI Manager Driver
// ----------------------------------------------------------------------
class axi_manager_driver extends axi_driver;
`uvm_component_utils(axi_manager_driver)
// --------------------------------------------------
// Constructor
function new(string name = "axi_manager_driver", uvm_component parent = null);
super.new(name, parent);
endfunction
// --------------------------------------------------
virtual task run_phase(uvm_phase phase);
`uvm_info("run_phase", $sformatf("Running AXI manager driver: %s",
get_full_name()), UVM_LOW)
if (m_if == null) begin
`uvm_error("run_phase", "MANAGER interface is null, cannot drive transactions")
end
forever begin
`uvm_info("run_phase", $sformatf("Starting to drive transactions for %s (rst_n = %0b)",
get_full_name(), m_if.ARESETn), UVM_LOW)
// Wait for reset to be de-asserted
if (m_if.ARESETn == 0) begin
`uvm_info("run_phase", $sformatf("Waiting for reset to be released (rst_n = %0b)",
m_if.ARESETn), UVM_LOW)
@(posedge m_if.ARESETn);
end
`uvm_info("run_phase", $sformatf("Starting to drive transactions for %s (rst_n = %0b)",
get_full_name(), m_if.ARESETn), UVM_LOW)
// Drive transactions until reset is asserted
while (m_if.ARESETn != 0) begin
uvm_sequence_item txn;
axi_transaction req;
`uvm_info("run_phase", $sformatf("Waiting for next transaction"), UVM_LOW)
seq_item_port.get_next_item(txn);
if (!$cast(req, txn)) begin
`uvm_fatal("drive_txn", "Invalid transaction type")
end
drive_txn(req);
seq_item_port.item_done();
end
end
endtask
// ------------------------------------------------------------
task drive_txn(axi_transaction req);
`uvm_info("drive_txn", $sformatf("Driving AXI transaction:\n%s", req.sprint()), UVM_LOW)
if (req.txn_type == AXI_WRITE) begin
drive_write_txn(req);
end else if (req.txn_type == AXI_READ) begin
drive_read_txn(req);
end else begin
`uvm_error("drive_txn", $sformatf("Unknown transaction type: %s", req.txn_type.name()))
end
`uvm_info("drive_txn", $sformatf("Driving AXI transaction done."), UVM_LOW)
endtask
// ------------------------------------------------------------
// Drive a write transaction
task drive_write_txn(axi_transaction req);
@(posedge m_if.ACLK);
m_if.AWVALID = 1;
m_if.AWADDR = req.addr;
@(posedge m_if.ACLK);
m_if.WDATA = req.data;
m_if.WSTRB = req.strb;
while (m_if.AWREADY != 1) begin
@(posedge m_if.ACLK);
end
`uvm_info("drive_write_txn", $sformatf("Address phase accepted (AWREADY=1)"), UVM_LOW)
m_if.AWVALID = 0;
`uvm_info("drive_write_txn", $sformatf("Done."), UVM_LOW)
endtask
// ------------------------------------------------------------
// Drive a read transaction
task drive_read_txn(axi_transaction req);
@(posedge m_if.ACLK);
m_if.ARVALID = 1;
m_if.ARADDR = req.addr;
@(posedge m_if.ACLK);
while (m_if.ARREADY != 1) begin
@(posedge m_if.ACLK);
end
`uvm_info("drive_read_txn", $sformatf("Address phase accepted (ARREADY=1)"), UVM_LOW)
m_if.ARVALID = 0;
`uvm_info("drive_read_txn", $sformatf("Done."), UVM_LOW)
endtask
endclass : axi_manager_driver

View File

@@ -4,6 +4,7 @@
class axi_monitor extends uvm_monitor; // #(axi_transaction); class axi_monitor extends uvm_monitor; // #(axi_transaction);
virtual `AXI_INTF.MANAGER m_if; virtual `AXI_INTF.MANAGER m_if;
virtual `AXI_INTF.SUBORDINATE s_if; virtual `AXI_INTF.SUBORDINATE s_if;
virtual `AXI_INTF mon_if;
axi_agent_type_t agent_type; axi_agent_type_t agent_type;
// Declare the analysis export // Declare the analysis export
@@ -30,14 +31,22 @@ class axi_monitor extends uvm_monitor; // #(axi_transaction);
// -------------------------------------------------- // --------------------------------------------------
// Set virtual interfaces // Set virtual interfaces
function void set_virtual_intefaces(virtual `AXI_INTF.MANAGER m_if_p, function void set_virtual_interfaces(virtual `AXI_INTF.MANAGER m_if_p,
virtual `AXI_INTF.SUBORDINATE s_if_p); virtual `AXI_INTF.SUBORDINATE s_if_p);
`uvm_info("set_virtual_intefaces", $sformatf("Setting virtual interfaces"), UVM_LOW) `uvm_info("set_virtual_interfaces", $sformatf("Setting virtual interfaces"), UVM_LOW)
m_if = m_if_p; m_if = m_if_p;
s_if = s_if_p; s_if = s_if_p;
endfunction endfunction
// --------------------------------------------------
// Set monitor virtual interfaces
function void set_monitor_virtual_interface(virtual `AXI_INTF mon_if_p);
`uvm_info("set_monitor_virtual_interface", $sformatf("Setting monitor virtual interface"), UVM_LOW)
mon_if = mon_if_p;
endfunction
// -------------------------------------------------- // --------------------------------------------------
// Run phase // Run phase
virtual function void run_phase(uvm_phase phase); virtual function void run_phase(uvm_phase phase);
@@ -56,13 +65,31 @@ class axi_monitor extends uvm_monitor; // #(axi_transaction);
// -------------------------------------------------- // --------------------------------------------------
// Monitor logic to capture transactions // Monitor logic to capture transactions
virtual task do_monitor(); virtual task do_monitor();
if (agent_type == MANAGER) begin $fwrite(trk_file, "AXI monitor (%0s Mode)\n", agent_type.name());
do_manager_monitor(); $fwrite(trk_file, "--------------------------------------------------\n");
end else if (agent_type == SUBORDINATE) begin
do_subordinate_monitor();
end else begin forever begin
`uvm_fatal("AXI_MONITOR", "Unknown agent type") @(posedge mon_if.ACLK);
if (mon_if.ARESETn != 0) begin
if (mon_if.AWVALID == 1) begin
$fwrite(trk_file, "AXI WRITE - ADDR=0x%h DATA=0x%h STRB=%h\n",
mon_if.AWADDR, mon_if.WDATA, mon_if.WSTRB);
end
if (mon_if.ARVALID == 1) begin
$fwrite(trk_file, "AXI READ - ADDR=0x%h\n",
mon_if.ARADDR);
end
end
end end
// if (agent_type == MANAGER) begin
// do_manager_monitor();
// end else if (agent_type == SUBORDINATE) begin
// do_subordinate_monitor();
// end else begin
// `uvm_fatal("AXI_MONITOR", "Unknown agent type")
// end
endtask endtask
// -------------------------------------------------- // --------------------------------------------------

View File

@@ -9,6 +9,8 @@ package axi_pkg;
// UVM components // UVM components
`include "axi_driver.sv" `include "axi_driver.sv"
`include "axi_subordinate_driver.sv"
`include "axi_manager_driver.sv"
`include "axi_monitor.sv" `include "axi_monitor.sv"
`include "axi_agent.sv" `include "axi_agent.sv"
endpackage endpackage

View File

@@ -0,0 +1,72 @@
// ----------------------------------------------------------------------
class axi_subordinate_driver extends axi_driver;
`uvm_component_utils(axi_subordinate_driver)
// --------------------------------------------------
// Constructor
function new(string name = "axi_subordinate_driver", uvm_component parent = null);
super.new(name, parent);
endfunction
// --------------------------------------------------
virtual task run_phase(uvm_phase phase);
`uvm_info("run_phase", $sformatf("Running AXI subordinate driver: %s",
get_full_name()), UVM_LOW)
if (s_if == null) begin
`uvm_error("run_phase", "subordinate interface is null, cannot respond to transactions")
end
forever begin
@(posedge s_if.ARESETn);
while (s_if.ARESETn != 0) begin
@(posedge s_if.ACLK);
// Following could happen on the same cycle
if (s_if.AWVALID == 1'b1) begin
respond_to_write_txn();
end
if (s_if.ARVALID == 1'b1) begin
respond_to_read_txn();
end
@(negedge s_if.ACLK);
end
end
endtask
// ------------------------------------------------------------
task respond_to_write_txn();
axi_transaction req;
req = axi_transaction::type_id::create("req");
req.txn_type = AXI_WRITE;
req.addr = s_if.AWADDR;
req.data = s_if.WDATA;
req.strb = s_if.WSTRB;
@(posedge s_if.ACLK);
`uvm_info("respond_to_write_txn", $sformatf("Responding to AXI write transaction:\n%s", req.sprint()), UVM_LOW)
s_if.AWREADY = 1'b1;
endtask
// ------------------------------------------------------------
task respond_to_read_txn();
axi_transaction req;
req = axi_transaction::type_id::create("req");
req.txn_type = AXI_READ;
req.addr = s_if.ARADDR;
@(posedge s_if.ACLK);
`uvm_info("respond_to_write_txn", $sformatf("Responding to AXI write transaction:\n%s", req.sprint()), UVM_LOW)
s_if.ARREADY = 1'b1;
endtask
endclass : axi_subordinate_driver

View File

@@ -1,10 +1,10 @@
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
class axi_transaction extends uvm_sequence_item; class axi_transaction extends uvm_sequence_item;
// Declare AXI transaction fields // Declare AXI transaction fields
rand axi_transaction_type_t txn_type; // Transaction type (read/write) rand axi_transaction_type_t txn_type; // Transaction type (read/write)
rand bit [31:0] addr; // Address rand bit [`ADDR_WIDTH-1:0] addr; // Address
rand bit [31:0] data; // Data rand bit [`DATA_WIDTH-1:0] data; // Data
rand bit [3:0] strb; // Byte enable rand bit [`DATA_WIDTH_DIV_8-1:0] strb; // Byte enable
`uvm_object_utils_begin(axi_transaction) `uvm_object_utils_begin(axi_transaction)
`uvm_field_enum(axi_transaction_type_t, txn_type, UVM_DEFAULT) `uvm_field_enum(axi_transaction_type_t, txn_type, UVM_DEFAULT)

View File

@@ -42,10 +42,12 @@ class axi_simple_seq extends axi_m_seq_base;
`uvm_info("axi_simple_seq", "Starting simple AXI sequence", UVM_LOW) `uvm_info("axi_simple_seq", "Starting simple AXI sequence", UVM_LOW)
`uvm_do(txn, env.get_axi_m_sequencer(), -1, { repeat(10) begin
addr == 32'h0000_0000; // Example address `uvm_do(txn, env.get_axi_m_sequencer(), -1, {
data == 32'hDEAD_BEEF; // Example data addr != {`ADDR_WIDTH{1'b0}};
strb == 'hf; // Example byte enable data != {`DATA_WIDTH{1'b0}};
}); strb == 'hf; // Example byte enable
});
end
endtask endtask
endclass : axi_simple_seq endclass : axi_simple_seq

View File

@@ -20,6 +20,7 @@ module tb_top (input logic sys_clk);
initial begin initial begin
uvm_config_db#(virtual `AXI_INTF.MANAGER)::set(uvm_root::get(), "uvm_test_top.env.axi_m", "axi_dvr_vif", a_if.MANAGER); uvm_config_db#(virtual `AXI_INTF.MANAGER)::set(uvm_root::get(), "uvm_test_top.env.axi_m", "axi_dvr_vif", a_if.MANAGER);
uvm_config_db#(virtual `AXI_INTF.SUBORDINATE)::set(uvm_root::get(), "uvm_test_top.env.axi_s", "axi_dvr_vif", a_if.SUBORDINATE); uvm_config_db#(virtual `AXI_INTF.SUBORDINATE)::set(uvm_root::get(), "uvm_test_top.env.axi_s", "axi_dvr_vif", a_if.SUBORDINATE);
uvm_config_db#(virtual `AXI_INTF)::set(uvm_root::get(), "uvm_test_top.env", "axi_mon_vif", a_if);
run_test(); run_test();
end end
@@ -27,7 +28,7 @@ module tb_top (input logic sys_clk);
// -------------------------------------------------- // --------------------------------------------------
initial begin initial begin
$dumpfile("wave.vcd"); $dumpfile("wave.vcd");
$dumpvars(); $dumpvars(0, "tb_top");
end end
// -------------------------------------------------- // --------------------------------------------------