AXI Verification IP
AXI Verification IP
13/10/24
Table of Contents
1. Introduction
4 . Appendices
2) Specified burst size greater than supported burst size : SLV ERR / 2'b10
2. awready : Output from slave and input to master. Master will send awvalid after that we
will wait till the time slave send us acknowledgement that it is ready to accept the
transfer.Our transfer will not transfer till the time we don’t get awready.
3. awid : For each transaction we assign a unique id.
awready is high 8
times,8 total
transactions
happened in one
single transfer
Burst = awlen + 1 → this is valid for read transfer as well, but for read we have arlen
since awlen is 7 , our burst length will be 8,burst will consist of 8 transactions.
5. awsize : Maximum size of a single transaction.It helps us get control over size of data we
are sending.
awdata [ 31 : 0 ] i.e 32 bits which is 4 bytes, so awsize will be 4
6. awburst : Helps us specify burst type. (awburst [1 : 0 ] =0 ,1,2)
Address is
incremented by 4 ,
hence incr mode is
working here.
wstrb 1 1 1 1
awsize → max byte size of transaction (4 bytes).
Write
Address
channel
Write
Data
channel Completion of
transfer
Write
Indicates we are
response
ready to receive the
channel
response of
transaction
READ CHANNEL
1. arvalid : whenever master wants to read data from slave it will convey through arvalid to
slave.when we make it high it indicates the start of read transfer.
2. arlen : It is use to indicate the burst length.
burst length =arlen +1
3. arsize : It indicates maximum valid byte size in a transaction.
3.1 Overview of Universal Verification Methodology (UVM)
In UVM:
• Coverage and randomization help achieve thorough design verification, making UVM
suitable for verifying complex protocols like AXI.
• Test: The test class starts the environment and sequences to simulate different
transaction patterns.
• Environment (env): The env class instantiates the AXI agent, which contains the driver,
monitor, and sequencer.
• Agent: The agent encapsulates the driver, monitor, and sequencer components.
In the code:
• The env class contains the UVM agent, which ties together the driver and monitor.
• The test class controls the sequences and initiates the test.
This section outlines the key UVM components in the AXI Verification IP for stimulus generation
and design monitoring.
• The AXI master initiates transactions like address requests, data write, or read
operations.
• The slave responds to master’s requests, handling data reception and responding with
status.
• Driver: The driver class extends uvm_driver and is responsible for driving the AXI signals
to the DUT. It receives a transaction from the sequencer and implements the write/read
logic (e.g., wrrd_fixed_wr() and wrrd_fixed_rd() tasks).
• Monitor: The monitor passively observes the communication between the DUT and
driver. It captures transactions, compares expected and actual results, and reports
errors if there are mismatches (using compare() task).
• Sequence: The sequence class generates stimulus for the DUT. The sequence items
(transactions) represent AXI operations such as write/read commands. Each sequence
triggers different types of operations.
• Sequence Item: The transaction class extends uvm_sequence_item and defines the
attributes of a typical AXI transaction like awvalid, awaddr, wdata for writes, and arvalid,
araddr, rdata for reads.
• Scoreboard: Collects the results from both the monitor and the DUT. It compares the
DUT's output with expected results to ensure correctness.
• Protocol Checker: Ensures the AXI protocol's timing, signal integrity, and transaction
order are correctly followed.
This section describes the practical implementation of the AXI Verification IP using UVM in
SystemVerilog.
• Transactions are modeled using the transaction class, where randomization is applied
to parameters like awvalid, awaddr, and wdata.
• Drivers implement the actual protocol logic by assigning AXI signal values to the
interface (vif).
AXI Burst Transaction Modes: Fixed, Increment, Wrap, and Error Handling
In the process of verifying AXI-based systems, different burst transaction modes are used to test
data transfers. The AXI protocol defines several burst types, each with unique characteristics for
addressing, data transfer, and error handling. These burst types include Fixed, Increment,
Wrap, and Error Handling. Each mode is critical for validating the robustness and versatility of
AXI transactions. In this section, I will explain each mode and the tasks implemented to handle
write and read transactions for each burst type.
In Fixed Burst Mode, the address remains constant for the duration of the burst. This is useful
when multiple data beats are to be written or read from a single address, commonly used in
memory-mapped peripherals.
The task wrrd_fixed_wr initiates a fixed burst write operation. It configures the AXI write address
and data channels to keep the address constant across multiple beats. The process involves
the following steps:
• The address channel (awvalid, awaddr) is asserted with the required transaction ID and
address (awaddr).
• The burst type is set to fixed (awburst = 0), ensuring the address remains constant.
• Multiple beats of data are sent over the write data channel (wdata), with the data being
valid (wvalid) and strobes enabled (wstrb = 4'b1111).
• The process waits for the slave’s wready signal before sending each data beat. The
transaction completes when all beats are sent, and the wlast signal is asserted to
indicate the end of the burst.
The task wrrd_fixed_rd initiates a fixed burst read operation. Like the write transaction, the
address remains constant, and the task performs the following:
• The address channel (arvalid, araddr) is asserted with the read address and transaction
ID (arid).
• The burst type is set to fixed (arburst = 0), ensuring the read address remains
unchanged.
• Data beats are read sequentially, with the task waiting for the slave’s rready signal to
receive each beat. The process concludes once all beats are received, with the rlast
signal indicating the end of the transaction.
2. Increment Burst Mode
In Increment Burst Mode, the address increments after each data beat. This mode is often
used for accessing sequential memory locations.
The task wrrd_incr_wr performs an increment burst write transaction. The key difference from
fixed mode is the increment in address for each beat, configured by setting awburst = 1. The
write address is sequentially updated after each beat, making this mode suitable for scenarios
where data needs to be written to consecutive memory locations.
Similarly, the task wrrd_incr_rd handles an increment burst read transaction. The read address
increments after each data beat, configured by setting arburst = 1. This allows for sequential
data reading from memory, with each beat corresponding to the next memory address.
The task wrrd_wrap_wr initiates a wrap burst write transaction by configuring the awburst signal
to 2, indicating wrap mode. During the transaction, the address wraps around a boundary,
allowing the system to repeatedly access the same memory block without exceeding
predefined limits.
The task wrrd_wrap_rd performs a wrap burst read transaction, with the address similarly
configured to wrap around a boundary (arburst = 2). This mode is particularly useful in
applications where continuous data streams need to be written or read within a specific
address range.
Error handling is a critical aspect of verification, ensuring that the system responds correctly to
erroneous transactions. Error conditions may arise when accessing out-of-bound addresses or
when performing unsupported operations.
The task err_wr simulates an error scenario by writing to an invalid or out-of-bound address
(awaddr = 128). This task tests how the system responds to such conditions, verifying whether
appropriate error responses are generated. It is essential in ensuring the robustness of the AXI
protocol in handling erroneous writes.
The task err_rd handles an error condition during a read transaction by reading from an invalid
address (araddr = 128). Similar to the error write task, it ensures that the system can detect and
respond to read errors appropriately.
virtual interface to the AXI interface, allowing the driver
to manipulate the AXI signals.
We update
operation to
rstdut. op is a
Randomize data
variable which
is of enum type
which will store
the type of
operation
which we are
verifying
The code below describes a run_phase task that handles multiple AXI burst transaction modes
(Fixed, Increment, Wrap, and Error) in a UVM environment. This task performs a sequence of
write and read operations based on the transaction type and is part of the AXI Verification IP
driver.
Randomization and constraints are critical to ensure all AXI protocol behaviors are tested
comprehensively. Random values for awaddr, wdata, etc., allow for varying transaction
patterns, while constraints ensure protocol-specific rules are followed.
• Randomization: The keyword rand is used to randomize fields like awaddr and wdata,
which generate different stimulus for the DUT.
o constraint txid { awid == id; wid == id; bid == id; arid == id; rid == id; } ensures that
all ID fields remain consistent within a transaction.
o constraint burst {awburst inside {0,1,2}; arburst inside {0,1,2};} restricts the burst
types to valid AXI values.
o constraint valid {awvalid !=arvalid} at a time we can either read or write, that is
why at same time awvalid and arvalid should not be equal.
3.6 Monitor
This block is executed if the system is not in reset and the address being written to
(awaddr) is less than 128 (presumably within a valid range). It waits until the awvalid
signal is asserted, indicating that a write address is valid
WRITE OPERATION monitor waits for the arvalid signal to go high,
indicating that the DUT has a valid read address
on the AXI bus.
o The monitor waits for the arvalid signal to go high, indicating that the DUT has a
valid read address on the AXI bus.
o The loop iterates over the number of data beats specified by arlen + 1. This
means it waits for the data transfers based on how many beats were specified in
the read transaction.
o Inside the loop, the code waits for rvalid to be asserted, indicating that the DUT
has valid read data on the bus.
o The condition checks if the received data (vif.rdata) does not match the
expected data from the memory array (arr[vif.next_addrrd]). If there’s a
mismatch, it increments the error counter (err++).
o This process ensures that any discrepancy between what was written and what
was read back is counted as an error.
3.7 AGENT
‘3.9 TEST
Objects of
all
sequences
Raise objection
and one by one
call sequence
We provide access to
interface to component
Appendices
A. UVM AXI Testbench Code
`include "uvm_macros.svh"
import uvm_pkg::*;
`uvm_object_utils(transaction)
super.new(name);
endfunction
int len = 0;
oper_mode op;
bit awready;
bit wvalid;
bit wready;
bit wlast;
bit bready;
bit bvalid;
rand bit [3:0] arlen; ////// burst length AXI3 : 1 to 16, AXI4 : 1 to 256
constraint txid { awid == id; wid == id; bid == id; arid == id; rid == id; }
endclass : transaction
////////////////////////////////////////////////////////////////////////////////
`uvm_object_utils(rst_dut)
transaction tr;
endfunction
repeat(5)
begin
tr = transaction::type_id::create("tr");
$display("------------------------------");
start_item(tr);
assert(tr.randomize);
tr.op = rstdut;
finish_item(tr);
end
endtask
endclass
///////////////////////////////////////////////////////////////////////
`uvm_object_utils(valid_wrrd_fixed)
transaction tr;
endfunction
tr = transaction::type_id::create("tr");
$display("------------------------------");
start_item(tr);
assert(tr.randomize);
tr.op = wrrdfixed;
tr.awlen = 7;
tr.awburst = 0;
tr.awsize = 2;
finish_item(tr);
endtask
endclass
////////////////////////////////////////////////////////////
`uvm_object_utils(valid_wrrd_incr)
transaction tr;
super.new(name);
endfunction
virtual task body();
tr = transaction::type_id::create("tr");
$display("------------------------------");
start_item(tr);
assert(tr.randomize);
tr.op = wrrdincr;
tr.awlen = 7;
tr.awburst = 1;
tr.awsize = 2;
finish_item(tr);
endtask
endclass
///////////////////////////////////////////////////////////
`uvm_object_utils(valid_wrrd_wrap)
transaction tr;
super.new(name);
endfunction
virtual task body();
tr = transaction::type_id::create("tr");
$display("------------------------------");
start_item(tr);
assert(tr.randomize);
tr.op = wrrdwrap;
tr.awlen = 7;
tr.awburst = 2;
tr.awsize = 2;
finish_item(tr);
endtask
endclass
/////////////////////////////////////////////////////////////////////////////////
`uvm_object_utils(err_wrrd_fix)
transaction tr;
super.new(name);
endfunction
tr = transaction::type_id::create("tr");
$display("------------------------------");
start_item(tr);
assert(tr.randomize);
tr.op = wrrderrfix;
tr.awlen = 7;
tr.awburst = 0;
tr.awsize = 2;
finish_item(tr);
endtask
endclass
/////////////////////////////////////////////////////////////
`uvm_component_utils(driver)
transaction tr;
super.new(path,parent);
endfunction
super.build_phase(phase);
tr = transaction::type_id::create("tr");
if(!uvm_config_db#(virtual axi_if)::get(this,"","vif",vif))
task reset_dut();
begin
vif.awlen <= 0;
vif.awsize <= 0;
vif.awaddr <= 0;
vif.awburst <= 0;
vif.wvalid <= 0;
vif.wid <= 0;
vif.wdata <= 0;
vif.wstrb <= 0;
vif.wlast <= 0;
vif.bready <= 0;
vif.arlen <= 0;
vif.arsize <= 0;
vif.araddr <= 0;
vif.arburst <= 0;
vif.rready <= 0;
@(posedge vif.clk);
end
endtask
/////////////////////////write read in fixed mode
task wrrd_fixed_wr();
/////////////////////////write logic
vif.awlen <= 7;
vif.awsize <= 2;
vif.awaddr <= 5;
vif.awburst <= 0;
vif.wlast <= 0;
@(posedge vif.clk);
@(posedge vif.wready);
@(posedge vif.clk);
@(posedge vif.wready);
@(posedge vif.clk);
end
@(negedge vif.bvalid);
endtask
task wrrd_fixed_rd();
@(posedge vif.clk);
vif.arlen <= 7;
vif.arsize <= 2;
vif.araddr <= 5;
vif.arburst <= 0;
@(posedge vif.arready);
@(posedge vif.clk);
end
@(negedge vif.rlast);
endtask
//////////////////////////////////////////////////////////////////////
task wrrd_incr_wr();
/////////////////////////write logic
vif.awlen <= 7;
vif.awsize <= 2;
vif.awaddr <= 5;
vif.awburst <= 1;
vif.wlast <= 0;
vif.arvalid <= 1'b0; ///turn off read
@(posedge vif.wready);
@(posedge vif.clk);
begin
@(posedge vif.wready);
@(posedge vif.clk);
end
@(negedge vif.bvalid);
endtask
task wrrd_incr_rd();
@(posedge vif.clk);
vif.arid <= tr.id;
vif.arlen <= 7;
vif.arsize <= 2;
vif.araddr <= 5;
vif.arburst <= 1;
@(posedge vif.arready);
@(posedge vif.clk);
end
@(negedge vif.rlast);
endtask
//////////////////////////////////////////////////////////////////////////////
task wrrd_wrap_wr();
/////////////////////////write logic
vif.awlen <= 7;
vif.awsize <= 2;
vif.awaddr <= 5;
vif.awburst <= 2;
vif.wlast <= 0;
@(posedge vif.wready);
@(posedge vif.clk);
begin
@(posedge vif.wready);
@(posedge vif.clk);
end
endtask
task wrrd_wrap_rd();
@(posedge vif.clk);
vif.arlen <= 7;
vif.arsize <= 2;
vif.araddr <= 5;
vif.arburst <= 2;
@(posedge vif.arready);
@(posedge vif.clk);
end
@(negedge vif.rlast);
//////////////////////////////////////////////////////////////////////////
task err_wr();
/////////////////////////write logic
vif.awlen <= 7;
vif.awsize <= 2;
vif.awburst <= 0;
vif.wlast <= 0;
@(posedge vif.wready);
@(posedge vif.clk);
for(int i = 0; i < (vif.awlen); i++)
begin
@(posedge vif.wready);
@(posedge vif.clk);
end
@(negedge vif.bvalid);
endtask
task err_rd();
@(posedge vif.clk);
vif.arlen <= 7;
vif.arsize <= 2;
@(posedge vif.arready);
@(posedge vif.clk);
end
@(negedge vif.rlast);
endtask
////////////////////////////////////////////////////////////////////////
forever begin
seq_item_port.get_next_item(tr);
if(tr.op == rstdut)
reset_dut();
begin
wrrd_fixed_wr();
wrrd_fixed_rd();
end
begin
wrrd_incr_rd();
end
begin
wrrd_wrap_wr();
wrrd_wrap_rd();
end
begin
err_wr();
err_rd();
end
seq_item_port.item_done();
end
endtask
endclass
///////////////////////////////////////////////////////////////////////
`uvm_component_utils(mon)
transaction tr;
logic resp;
int err = 0;
super.new(inst,parent);
endfunction
super.build_phase(phase);
tr = transaction::type_id::create("tr");
if(!uvm_config_db#(virtual axi_if)::get(this,"","vif",vif))//uvm_test_top.env.agent.drv.aif
endfunction
/////////////////////////////////////////////////////////////////////////
task compare();
begin
`uvm_info("MON", $sformatf("Test Passed err :%0d wrresp :%0d rdresp :%0d ", err, rdresp,
wrresp), UVM_MEDIUM);
err = 0;
end
else
begin
`uvm_info("MON", $sformatf("Test Failed err :%0d wrresp :%0d rdresp :%0d ", err, rdresp,
wrresp), UVM_MEDIUM);
err = 0;
end
endtask
///////////////////////////////////////////////////////////////////////
forever begin
@(posedge vif.clk);
if(!vif.resetn)
begin
end
begin
wait(vif.awvalid == 1'b1);
@(posedge vif.wready);
arr[vif.next_addrwr] = vif.wdata;
end
// @(negedge vif.wlast);
@(posedge vif.bvalid);
wrresp = vif.bresp;///0
//////////////////////////////////////////////////////
wait(vif.arvalid == 1'b1);
@(posedge vif.rvalid);
if(vif.rdata != arr[vif.next_addrrd])
begin
err++;
end
end
@(posedge vif.rlast);
rdresp = vif.rresp;
compare();
$display("------------------------------");
end
begin
wait(vif.awvalid == 1'b1);
@(negedge vif.wready);
end
@(posedge vif.bvalid);
wrresp = vif.bresp;
wait(vif.arvalid == 1'b1);
if(vif.rresp != 2'b00)
begin
err++;
end
end
@(posedge vif.rlast);
rdresp = vif.rresp;
compare();
$display("------------------------------");
end
end
endtask
endclass
///////////////////////////////////////////////////////////
`uvm_component_utils(agent)
super.new(inst,parent);
endfunction
driver d;
uvm_sequencer#(transaction) seqr;
mon m;
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
m = mon::type_id::create("m",this);
d = driver::type_id::create("d",this);
endfunction
super.connect_phase(phase);
d.seq_item_port.connect(seqr.seq_item_export);
endfunction
endclass
////////////////////////////////////////////////////////////////////////////////
`uvm_component_utils(env)
super.new(inst,c);
endfunction
agent a;
super.build_phase(phase);
a = agent::type_id::create("a",this);
endfunction
endclass
//////////////////////////////////////////////////
`uvm_component_utils(test)
super.new(inst,c);
endfunction
env e;
valid_wrrd_fixed vwrrdfx;
valid_wrrd_incr vwrrdincr;
valid_wrrd_wrap vwrrdwrap;
err_wrrd_fix errwrrdfix;
rst_dut rdut;
super.build_phase(phase);
e = env::type_id::create("env",this);
vwrrdfx = valid_wrrd_fixed::type_id::create("vwrrdfx");
vwrrdincr = valid_wrrd_incr::type_id::create("vwrrdincr");
vwrrdwrap = valid_wrrd_wrap::type_id::create("vwrrdwrap");
errwrrdfix = err_wrrd_fix::type_id::create("errwrrdfix");
rdut = rst_dut::type_id::create("rdut");
endfunction
phase.raise_objection(this);
//rdut.start(e.a.seqr);
//#20;
//vwrrdfx.start(e.a.seqr);
//#20;
//vwrrdincr.start(e.a.seqr);
//#20;
//vwrrdwrap.start(e.a.seqr);
//#20;
errwrrdfix.start(e.a.seqr);
#20;
phase.drop_objection(this);
endtask
endclass
/////////////////////////////////////////////////////////////////////
module tb;
axi_if vif();
initial begin
vif.clk <= 0;
end
always #5 vif.clk <= ~vif.clk;
initial begin
run_test("test");
end
initial begin
$dumpfile("dump.vcd");
$dumpvars;
end
endmodule