@@ -1,2 +0,0 @@ | |||||
# AXI3 | |||||
@@ -0,0 +1,22 @@ | |||||
class axi_agent extends uvm_agent; | |||||
axi_drv drv; | |||||
axi_sqr sqr; | |||||
axi_mon mon; | |||||
axi_cov cov; | |||||
`uvm_component_utils_begin(axi_agent)// factory registration | |||||
`NEW_COMP | |||||
function void build_phase(uvm_phase phase); | |||||
super.build_phase(phase); | |||||
mon = axi_mon::type_id::create("mon", this); | |||||
drv =axi_drv::type_id::create("drv", this);// creating from factory | |||||
sqr = axi_sqr::type_id::create("sqr", this); | |||||
cov = axi_cov::type_id::create("cov", this); | |||||
end | |||||
endfunction | |||||
function void connect_phase(uvm_phase phase); | |||||
drv.seq_item_port.connect(sqr.seq_item_export); | |||||
mon.ap_port.connect(cov.analysis_export); | |||||
end | |||||
endfunction | |||||
endclass | |||||
@@ -0,0 +1,46 @@ | |||||
class axi_cov extends uvm_subscriber#(axi_tx); | |||||
axi_tx tx; | |||||
`uvm_component_utils(axi_cov) | |||||
covergroup axi_cg; | |||||
ADDR_CP : coverpoint tx.addr { | |||||
option.auto_bin_max = 8; | |||||
} | |||||
WR_RD_CP : coverpoint tx.wr_rd{ | |||||
bins WR = {1'b1}; | |||||
bins RD = {1'b0}; | |||||
} | |||||
BURST_TYPE_CP : coverpoint tx.burst_type { | |||||
bins FIXED = {FIXED}; | |||||
bins WRAP = {WRAP}; | |||||
bins INCR = {INCR}; | |||||
} | |||||
RESP_CP : coverpoint tx.resp { | |||||
bins OKAY = {OKAY}; | |||||
bins EXOKAY = {EXOKAY}; | |||||
bins SLVERR = {SLVERR}; | |||||
bins DECERR = {DECERR}; | |||||
} | |||||
ADDR_CP_X_WR_RD_CP : cross ADDR_CP, WR_RD_CP; | |||||
ADDR_CP_X_BURST_TYPE_CP : cross ADDR_CP, BURST_TYPE_CP; | |||||
ADDR_CP_X_RESP_CP : cross ADDR_CP, RESP_CP; | |||||
WR_RD_CP_X_BURST_TYPE_CP : cross WR_RD_CP, BURST_TYPE_CP; | |||||
WR_RD_CP_X_RESP_CP : cross WR_RD_CP, RESP_CP; | |||||
BURST_TYPE_CP_X_RESP_CP : cross BURST_TYPE_CP, RESP_CP; | |||||
endgroup | |||||
function new(string name = "", uvm_component parent=null); | |||||
super.new(name,parent); | |||||
axi_cg = new(); | |||||
endfunction | |||||
function void write(T t); | |||||
$cast(tx, t); | |||||
$display("time : %0t", $time); | |||||
tx.print(); | |||||
axi_cg.sample(); | |||||
endfunction | |||||
endclass | |||||
@@ -0,0 +1,126 @@ | |||||
class axi_drv extends uvm_driver#(axi_tx); | |||||
virtual axi_intf vif; | |||||
`uvm_component_utils(axi_drv) | |||||
`NEW_COMP | |||||
function void build_phase(uvm_phase phase); | |||||
super.build_phase(phase); | |||||
if(!uvm_config_db#(virtual axi_intf)::get(this, "", "vif", vif)) begin | |||||
`uvm_error("CONFG_DB", "Not able to get axi intf handle") | |||||
end | |||||
endfunction | |||||
task run_phase(uvm_phase phase); | |||||
@(negedge vif.arst);//waiting for release the reset | |||||
forever begin | |||||
seq_item_port.get_next_item(req); | |||||
req.print(); | |||||
drive_tx(req); | |||||
seq_item_port.item_done(); | |||||
end | |||||
endtask | |||||
task drive_tx(axi_tx tx); | |||||
if (tx.wr_rd == 1) begin | |||||
write_addr(tx); | |||||
write_data(tx); | |||||
write_resp(tx); | |||||
end | |||||
if (tx.wr_rd == 0) begin | |||||
read_addr(tx); | |||||
read_data(tx); | |||||
end | |||||
endtask | |||||
task write_addr(axi_tx tx); | |||||
`uvm_info("AXI_TX", "write_addr", UVM_MEDIUM); | |||||
//Master drives all write address channel signals (aw), it will drive at the posedge of clock | |||||
@(vif.mst_cb); | |||||
vif.mst_cb.awaddr <= tx.addr; | |||||
vif.mst_cb.awlen <= tx.burst_len; | |||||
vif.mst_cb.awsize <= tx.burst_size; | |||||
vif.mst_cb.awburst <= tx.burst_type; | |||||
vif.mst_cb.awid <= tx.txid; | |||||
vif.mst_cb.awvalid <= 1'b1; | |||||
wait (vif.mst_cb.awready == 1'b1); | |||||
@(vif.mst_cb); | |||||
vif.mst_cb.awaddr <= 0; | |||||
vif.mst_cb.awlen <= 0; | |||||
vif.mst_cb.awsize <= 0; | |||||
vif.mst_cb.awburst <= 0; | |||||
vif.mst_cb.awid <= 0; | |||||
vif.mst_cb.awvalid <= 0; | |||||
endtask | |||||
task write_data(axi_tx tx); | |||||
`uvm_info("AXI_TX", "write_data", UVM_MEDIUM); | |||||
for (int i = 0; i <= tx.burst_len; i++) begin | |||||
@(vif.mst_cb) | |||||
vif.mst_cb.wdata <= tx.dataQ.pop_front(); | |||||
vif.mst_cb.wstrb <= 4'b1111; //need to be updated as per the burst size | |||||
vif.mst_cb.wvalid <= 1'b1; | |||||
vif.mst_cb.wid <= tx.txid; | |||||
//wlast=1 will be in last beat | |||||
if(i == tx.burst_len) vif.mst_cb.wlast <= 1'b1; | |||||
else vif.mst_cb.wlast <= 0; | |||||
wait(vif.mst_cb.wready == 1'b1); | |||||
end | |||||
//once data beats are done, then reset all signals to 0 | |||||
@(vif.mst_cb); | |||||
vif.mst_cb.wvalid <= 0; | |||||
vif.mst_cb.wlast <= 0; | |||||
vif.mst_cb.wdata <= 0; | |||||
vif.mst_cb.wstrb <= 0; | |||||
vif.mst_cb.wid <= 0; | |||||
endtask | |||||
task write_resp(axi_tx tx); | |||||
bit bvalid_f = 0; | |||||
//`uvm_info("AXI_TX", "write_resp", UVM_MEDIUM); | |||||
//write response is initiated by slave | |||||
while (bvalid_f == 0) begin | |||||
@(vif.mst_cb); | |||||
bvalid_f = vif.mst_cb.bvalid; | |||||
end | |||||
vif.mst_cb.bready <= 1'b1; | |||||
@(vif.mst_cb); | |||||
vif.mst_cb.bready <= 1'b0; | |||||
endtask | |||||
task read_addr(axi_tx tx); | |||||
`uvm_info("AXI_TX", "read_addr", UVM_MEDIUM); | |||||
@(vif.mst_cb); | |||||
vif.mst_cb.araddr <= tx.addr; | |||||
vif.mst_cb.arlen <= tx.burst_len; | |||||
vif.mst_cb.arsize <= tx.burst_size; | |||||
vif.mst_cb.arburst <= tx.burst_type; | |||||
vif.mst_cb.arid <= tx.txid; | |||||
vif.mst_cb.arvalid <= 1'b1; | |||||
wait (vif.mst_cb.arready == 1'b1); | |||||
@(vif.mst_cb); | |||||
vif.mst_cb.araddr <= 0; | |||||
vif.mst_cb.arlen <= 0; | |||||
vif.mst_cb.arsize <= 0; | |||||
vif.mst_cb.arburst <= 0; | |||||
vif.mst_cb.arid <= 0; | |||||
vif.mst_cb.arvalid <= 0; | |||||
endtask | |||||
task read_data(axi_tx tx); | |||||
bit rvalid_f = 0; | |||||
`uvm_info("AXI_TX", "read_data", UVM_MEDIUM); | |||||
//since read data is happening multiple times, then handshaking should do multiple times | |||||
for (int i = 0; i <= tx.burst_len; i++) begin | |||||
rvalid_f = 0; | |||||
while (rvalid_f == 0) begin | |||||
@(vif.mst_cb); | |||||
rvalid_f = vif.mst_cb.rvalid; | |||||
vif.mst_cb.rready <= 1'b1; | |||||
end | |||||
end | |||||
@(vif.mst_cb); | |||||
vif.mst_cb.rready <= 1'b0; | |||||
endtask | |||||
endclass | |||||
@@ -0,0 +1,12 @@ | |||||
class axi_env extends uvm_env; | |||||
axi_agent magent; | |||||
`uvm_component_utils(axi_env);// factory registration | |||||
`NEW_COMP | |||||
function void build_phase(uvm_phase phase); | |||||
super.build_phase(phase); | |||||
magent = axi_agent::type_id::create("magent", this); | |||||
uvm_config_db#(int)::set(this, "magent", "mst_slv_f", `MSTR); | |||||
endfunction | |||||
endclass | |||||
@@ -0,0 +1,43 @@ | |||||
//base sequence : things common to all sequence | |||||
class axi_base_seq extends uvm_sequence #(axi_tx); | |||||
uvm_phase phase; | |||||
`uvm_object_utils(axi_base_seq) | |||||
`NEW_OBJ | |||||
task pre_body(); | |||||
//raise objection | |||||
phase = get_starting_phase(); | |||||
if (phase !=null) begin | |||||
phase.raise_objection(this); | |||||
phase.phase_done.set_drain_time(this,100); | |||||
end | |||||
endtask | |||||
task post_body(); | |||||
//drop objection | |||||
if (phase !=null) begin | |||||
phase.drop_objection(this); | |||||
end | |||||
endtask | |||||
endclass | |||||
//functional sequence: things specific to current sequence | |||||
class axi_wr_rd_seq extends axi_base_seq; | |||||
axi_tx tx; | |||||
axi_tx txQ[$]; | |||||
`uvm_object_utils(axi_wr_rd_seq) | |||||
`NEW_OBJ | |||||
task body(); | |||||
//write/read to same loc | |||||
repeat(1) begin | |||||
`uvm_do_with (req, {req.wr_rd==1;}); | |||||
tx = new req; //shallow copy | |||||
txQ.push_back(tx); | |||||
end | |||||
//read_tx | |||||
repeat(1) begin | |||||
tx = txQ.pop_front(); | |||||
`uvm_do_with (req, {req.wr_rd==0; req.burst_len ==tx.burst_len; req.addr == tx.addr;}); | |||||
end | |||||
endtask | |||||
endclass | |||||
@@ -0,0 +1,8 @@ | |||||
class axi_sqr extends uvm_sequencer #(axi_tx); | |||||
`uvm_component_utils(axi_sqr) | |||||
`NEW_COMP | |||||
function void build_phase(uvm_phase phase); | |||||
super.build_phase (phase); | |||||
endfunction | |||||
endclass |
@@ -0,0 +1,43 @@ | |||||
class axi_tx extends uvm_sequence_item; | |||||
rand bit wr_rd; | |||||
rand bit [`ADDR_WIDTH-1:0] addr; | |||||
rand bit [`WIDTH-1:0] dataQ[$]; //to support burst of data | |||||
rand bit [3:0] burst_len; | |||||
rand bit [2:0] burst_size; | |||||
rand burst_type_t burst_type; | |||||
rand bit [3:0] txid; | |||||
bit [1:0] resp; | |||||
bit [31:0] wrap_lower, wrap_upper; | |||||
int tx_size; | |||||
`uvm_object_utils_begin(axi_tx) | |||||
`uvm_field_int(addr, UVM_ALL_ON|UVM_NOPACK); | |||||
`uvm_field_queue_int(dataQ, UVM_ALL_ON|UVM_NOPACK); | |||||
`uvm_field_int(wr_rd, UVM_ALL_ON|UVM_NOPACK); | |||||
`uvm_field_int(burst_len, UVM_ALL_ON|UVM_NOPACK); | |||||
`uvm_field_int(burst_size, UVM_ALL_ON|UVM_NOPACK); | |||||
`uvm_field_enum(burst_type_t, burst_type, UVM_ALL_ON|UVM_NOPACK); | |||||
`uvm_field_int(txid, UVM_ALL_ON|UVM_NOPACK); | |||||
`uvm_field_int(wrap_lower, UVM_ALL_ON|UVM_NOPACK); | |||||
`uvm_field_int(wrap_upper, UVM_ALL_ON|UVM_NOPACK); | |||||
`uvm_object_utils_end | |||||
`NEW_OBJ | |||||
function void post_randomize(); | |||||
if (burst_type == WRAP) begin | |||||
tx_size = (burst_len+1) * (2**burst_size); | |||||
wrap_lower = addr - (addr%tx_size); | |||||
wrap_upper = wrap_lower + tx_size - 1; | |||||
end | |||||
endfunction | |||||
//constraint | |||||
constraint dataQ_c{ | |||||
dataQ.size() == burst_len+1; | |||||
} | |||||
constraint burst_type_c{ | |||||
burst_type != RSVD_BT; | |||||
} | |||||
constraint wrap_aligned_c{ | |||||
(burst_type) == WRAP -> (addr%(2**burst_size)==0); | |||||
} | |||||
endclass | |||||
@@ -0,0 +1,41 @@ | |||||
`include "uvm_pkg.sv" | |||||
import uvm_pkg ::*; | |||||
`include "uvm_macros.svh" | |||||
`define WIDTH 32 | |||||
`define DEPTH 64 | |||||
`define ADDR_WIDTH $clog2(`DEPTH) | |||||
`include "axi_tx.sv" | |||||
`include "axi_seq_lib.sv" | |||||
`include "axi_sqr.sv" | |||||
`include "axi_drv.sv" | |||||
`include "axi_mon.sv" | |||||
`include "axi_cov.sv" | |||||
`include "axi_agent.sv" | |||||
`include "axi_env.sv" | |||||
module top; | |||||
reg clk, rst; | |||||
axi_intf pif(clk,rst); | |||||
axi_env env = new(); | |||||
initial begin | |||||
clk = 0; | |||||
forever #5 clk = ~clk; | |||||
end | |||||
initial begin | |||||
rst = 1; | |||||
repeat(2) @(posedge clk); | |||||
rst = 0; | |||||
// #1000; | |||||
// $finish; | |||||
end | |||||
initial begin | |||||
run_test("axi_wr_rd_test"); | |||||
end | |||||
//initial begin | |||||
// $dumpvars(); | |||||
// $dumpfile("1.vcd"); | |||||
//end | |||||
endmodule |