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