@@ -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 |