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