diff --git a/src/axi/axi_agent.sv b/src/axi/axi_agent.sv index a52de46..541b66c 100644 --- a/src/axi/axi_agent.sv +++ b/src/axi/axi_agent.sv @@ -5,14 +5,15 @@ typedef axi_sequencer; class axi_agent extends uvm_agent; - axi_agent_type_t agent_type; - virtual `AXI_INTF.MANAGER m_if; - virtual `AXI_INTF.SUBORDINATE s_if; + axi_agent_type_t agent_type; + virtual `AXI_INTF.MANAGER m_if; + virtual `AXI_INTF.SUBORDINATE s_if; + virtual `AXI_INTF mon_if; // Declare the sequencer and driver - axi_sequencer sequencer; - axi_driver driver; - axi_monitor monitor; + axi_sequencer sequencer; + axi_driver driver; + axi_monitor monitor; `uvm_component_utils_begin(axi_agent) `uvm_field_enum(axi_agent_type_t, agent_type, UVM_DEFAULT) @@ -35,13 +36,23 @@ class axi_agent extends uvm_agent; // Build phase virtual function void build_phase(uvm_phase phase); 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); // Propagete the agent type to driver and monitor driver.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 (!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") @@ -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) end - driver.set_virtual_intefaces(m_if, s_if); - monitor.set_virtual_intefaces(m_if, s_if); + if (!uvm_config_db#(virtual `AXI_INTF)::get(this.get_parent(), "", "axi_mon_vif", mon_if)) begin + `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 // -------------------------------------------------- diff --git a/src/axi/axi_driver.sv b/src/axi/axi_driver.sv index c52216b..3e7cc18 100644 --- a/src/axi/axi_driver.sv +++ b/src/axi/axi_driver.sv @@ -23,97 +23,10 @@ class axi_driver extends uvm_driver; // #(axi_transaction); // -------------------------------------------------- // 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); - `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; 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 - - // -------------------------------------------------- - // 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 diff --git a/src/axi/axi_manager_driver.sv b/src/axi/axi_manager_driver.sv new file mode 100644 index 0000000..0c1335d --- /dev/null +++ b/src/axi/axi_manager_driver.sv @@ -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 diff --git a/src/axi/axi_monitor.sv b/src/axi/axi_monitor.sv index 4b9a37b..bdf7d2d 100644 --- a/src/axi/axi_monitor.sv +++ b/src/axi/axi_monitor.sv @@ -4,6 +4,7 @@ class axi_monitor extends uvm_monitor; // #(axi_transaction); virtual `AXI_INTF.MANAGER m_if; virtual `AXI_INTF.SUBORDINATE s_if; + virtual `AXI_INTF mon_if; axi_agent_type_t agent_type; // Declare the analysis export @@ -30,14 +31,22 @@ class axi_monitor extends uvm_monitor; // #(axi_transaction); // -------------------------------------------------- // 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); - `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; s_if = s_if_p; 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 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 virtual task do_monitor(); - 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") + $fwrite(trk_file, "AXI monitor (%0s Mode)\n", agent_type.name()); + $fwrite(trk_file, "--------------------------------------------------\n"); + + + forever begin + @(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 + + // 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 // -------------------------------------------------- diff --git a/src/axi/axi_pkg.sv b/src/axi/axi_pkg.sv index 4619aff..d25a2cf 100644 --- a/src/axi/axi_pkg.sv +++ b/src/axi/axi_pkg.sv @@ -9,6 +9,8 @@ package axi_pkg; // UVM components `include "axi_driver.sv" + `include "axi_subordinate_driver.sv" + `include "axi_manager_driver.sv" `include "axi_monitor.sv" `include "axi_agent.sv" endpackage diff --git a/src/axi/axi_subordinate_driver.sv b/src/axi/axi_subordinate_driver.sv new file mode 100644 index 0000000..0a82125 --- /dev/null +++ b/src/axi/axi_subordinate_driver.sv @@ -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 diff --git a/src/axi/axi_transaction.sv b/src/axi/axi_transaction.sv index d76597c..c474ae1 100644 --- a/src/axi/axi_transaction.sv +++ b/src/axi/axi_transaction.sv @@ -1,10 +1,10 @@ // ---------------------------------------------------------------------- class axi_transaction extends uvm_sequence_item; // Declare AXI transaction fields - rand axi_transaction_type_t txn_type; // Transaction type (read/write) - rand bit [31:0] addr; // Address - rand bit [31:0] data; // Data - rand bit [3:0] strb; // Byte enable + rand axi_transaction_type_t txn_type; // Transaction type (read/write) + rand bit [`ADDR_WIDTH-1:0] addr; // Address + rand bit [`DATA_WIDTH-1:0] data; // Data + rand bit [`DATA_WIDTH_DIV_8-1:0] strb; // Byte enable `uvm_object_utils_begin(axi_transaction) `uvm_field_enum(axi_transaction_type_t, txn_type, UVM_DEFAULT) diff --git a/tb/tb_seq_base.sv b/tb/tb_seq_base.sv index b8ecef8..bcda858 100644 --- a/tb/tb_seq_base.sv +++ b/tb/tb_seq_base.sv @@ -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_do(txn, env.get_axi_m_sequencer(), -1, { - addr == 32'h0000_0000; // Example address - data == 32'hDEAD_BEEF; // Example data - strb == 'hf; // Example byte enable - }); + repeat(10) begin + `uvm_do(txn, env.get_axi_m_sequencer(), -1, { + addr != {`ADDR_WIDTH{1'b0}}; + data != {`DATA_WIDTH{1'b0}}; + strb == 'hf; // Example byte enable + }); + end endtask endclass : axi_simple_seq diff --git a/tb/tb_top.sv b/tb/tb_top.sv index 17447c6..d453c91 100644 --- a/tb/tb_top.sv +++ b/tb/tb_top.sv @@ -20,6 +20,7 @@ module tb_top (input logic sys_clk); 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.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(); end @@ -27,7 +28,7 @@ module tb_top (input logic sys_clk); // -------------------------------------------------- initial begin $dumpfile("wave.vcd"); - $dumpvars(); + $dumpvars(0, "tb_top"); end // --------------------------------------------------