| @@ -1,2 +0,0 @@ | |||||
| # shivakanta_project | |||||
| @@ -0,0 +1,23 @@ | |||||
| module axi_asserttions; | |||||
| ////// handshaking for awvalid & awready | |||||
| property pro; | |||||
| @(posedge clk) | |||||
| disable iff(reset) | |||||
| (awvalid && !awready) |=> (awvalid && awready) ##1 (!awvalid && !awready); | |||||
| endproperty | |||||
| property pro_n; | |||||
| disable iff(reset) | |||||
| !($isunknown({awaddr,awlen,awsize,awburst,awvalid})& awburst=2'b 10) |-> (awaddr%(2**awsize)==0) | |||||
| endproperty | |||||
| @@ -0,0 +1,45 @@ | |||||
| import uvm_pkg::*; | |||||
| /* Configuration object to configure the environment of Test Bench. | |||||
| Configurable fields: | |||||
| 1. intf: Virtual Interface for the agents | |||||
| 2. active: To set the agent as active or passive */ | |||||
| class env_config#(parameter A_WIDTH = 16, D_WIDTH = 16) extends uvm_object; | |||||
| `uvm_object_utils(env_config) | |||||
| // variables | |||||
| virtual axi_intf#(.D_WIDTH(D_WIDTH), .A_WIDTH(A_WIDTH)) intf; | |||||
| // Master and Slave are active or passive | |||||
| uvm_active_passive_enum active = UVM_ACTIVE; | |||||
| function new(string name = "env_config"); | |||||
| super.new(name); | |||||
| endfunction //new() | |||||
| endclass //env_config extends uvm_object | |||||
| // Configuration object which configures the sequence item generation | |||||
| class test_config extends uvm_object; | |||||
| `uvm_object_utils(test_config) | |||||
| // Vartiables | |||||
| int no_write_cases = 2; | |||||
| int no_read_cases = 2; | |||||
| // Set whether to produce aligned address or unalinged address | |||||
| // -1: produce both aligned and unaligned randomly | |||||
| // 0: produce unaligned adresses for all bursts | |||||
| // 1: produce alligned adress for all bursts | |||||
| byte isAligned = -1; | |||||
| // Set the specific burst type for a test | |||||
| // -1: produce all the burst type randomly | |||||
| // 0: produce fixed bursts | |||||
| // 1: produce incr bursts | |||||
| // 2: produce wrap bursts | |||||
| byte burst_type = -1; | |||||
| function new(string name = "test_config"); | |||||
| super.new(name); | |||||
| endfunction //new() | |||||
| endclass //test_config extends uvm_object | |||||
| @@ -0,0 +1,40 @@ | |||||
| class axi_env extends uvm_env; | |||||
| `uvm_component_utils(axi_env) | |||||
| // Components | |||||
| axi_master master; | |||||
| axi_slave slave; | |||||
| axi_scoreboard scb; | |||||
| env_config env_cfg; | |||||
| // | |||||
| function new(string name, uvm_component parent); | |||||
| super.new(name, parent); | |||||
| endfunction //new() | |||||
| // Function: build_phase | |||||
| extern function void build_phase(uvm_phase phase); | |||||
| // Function: connect_phase | |||||
| extern function void connect_phase(uvm_phase phase); | |||||
| endclass //axi_env extends uvm_env | |||||
| function void axi_env::build_phase(uvm_phase phase); | |||||
| /* note: Do not call super.build_phase() from any class that is extended from an UVM base class! */ | |||||
| /* For more information see UVM Cookbook v1800.2 p.503 */ | |||||
| //super.build_phase(phase); | |||||
| master = axi_master::type_id::create("master", this); | |||||
| slave = axi_slave::type_id::create("slave", this); | |||||
| scb = axi_scoreboard::type_id::create("scb", this); | |||||
| endfunction: build_phase | |||||
| function void axi_env::connect_phase(uvm_phase phase); | |||||
| super.connect_phase(phase); | |||||
| master.ap.connect(scb.m_ap_imp); | |||||
| slave.ap.connect(scb.s_ap_imp); | |||||
| endfunction: connect_phase | |||||
| @@ -0,0 +1,211 @@ | |||||
| class axi_driver extends uvm_driver#(axi_seq_item); | |||||
| `uvm_component_utils(axi_driver) | |||||
| virtual intf vif; | |||||
| axi_seq_item tx; | |||||
| int temp[]; | |||||
| int burst_len; | |||||
| int burst_size; | |||||
| int total_tr; | |||||
| int wrap_lower_boundary; | |||||
| int wrap_upper_boundary; | |||||
| function new(string name="axi_driver",uvm_component_parent); | |||||
| super.new(name,parent); | |||||
| endfunction | |||||
| function void build_phase(uvm_phase phase); | |||||
| super.new(); | |||||
| tx=axi_seq_item::type_id::create("tx",this); | |||||
| if(!uvm_config_db#(virtual intf)::get("this",*,"intf",vif) | |||||
| `uvm_error(get_type_name(),$sformatf("failed for get vif")) | |||||
| endfunction | |||||
| task run_phase(uvm_phase phase) | |||||
| forever begin | |||||
| seq_item_port.get_next_item(tx); | |||||
| drive(); | |||||
| seq_item_port.item_done(tx); | |||||
| end | |||||
| endtask | |||||
| task drive(); | |||||
| if(tx.wr_rd) | |||||
| begin | |||||
| write_address(); | |||||
| write_data(); | |||||
| write_response(); | |||||
| end | |||||
| else | |||||
| read_address(); | |||||
| endtask | |||||
| ////////write address/////// | |||||
| task write_adress(); | |||||
| @(posedge vif.clk) | |||||
| tx.awvalid<=1; | |||||
| vif.awvalid<=tx.awvalid; | |||||
| vif.awid<=tx.awid; | |||||
| //vif.awaddr<=tx.awaddr; | |||||
| vif.awsize<=tx.awsize; | |||||
| vif.awlen<=tx.awlen; | |||||
| vif.awburst<=tx.awburst; | |||||
| //////////fixed burst//////////// | |||||
| if(tx.awburst==2'b00) | |||||
| vif.awaddr<=tx.awaddr; | |||||
| ////////increament burst//////// | |||||
| else if(awburst==2'b01)begin | |||||
| vif.awaddr<=tx.awaddr; | |||||
| for(i=0;i<awlen;i++) | |||||
| vif.awaddr<=(tx.awaddr+(2**awsize)); | |||||
| end | |||||
| ////////wrap burst///////// | |||||
| else if(tx.awburst=2'b10)begin | |||||
| begin | |||||
| if(((tx.awaddr)%2**awsize)==0)begin | |||||
| burst_len=tx.awlen+1; | |||||
| burst_size=2**(tx.awsize); | |||||
| total_tr=burst_len*burst_size; | |||||
| wrap_lower_boundary=int'((tx.awaddr)/(total_tr))*total_tr; | |||||
| wrap_upper_boundary=wrap_lower_boundary+total_tr; | |||||
| end | |||||
| vif.awlock<=tx.awlock; | |||||
| @(posedge vif.clk) | |||||
| wait(vif.awready) | |||||
| @(posedge vif.clk) | |||||
| tx.awvalid<=0; | |||||
| vif.awvalid<=tx.awvalid; | |||||
| endtask | |||||
| //////write data/////////// | |||||
| task write_data(); | |||||
| @posedge vif.clk) | |||||
| tx.wvalid<=1; | |||||
| vif.wvalid<=tx.wvalid; | |||||
| vif.wid<=tx.wid; | |||||
| foreach(tx.wdata[i,j]) | |||||
| begin | |||||
| temp[i][8*i+:8]<=tx.wdata[i][j]; | |||||
| end | |||||
| for(i=0;i<awlen+1;i++); | |||||
| begin | |||||
| vif.wdata<=temp[i]; | |||||
| tx.wvalid<=1; | |||||
| vif.wvalid<=tx.wvalid; | |||||
| vif.wlast<=awlen==i?1:0; | |||||
| end | |||||
| wait(vif.wready); | |||||
| //endtask | |||||
| @posedge vif.clk) | |||||
| tx.wvalid<=0; | |||||
| vif.wvalid<=tx.wvalid; | |||||
| endtask | |||||
| //////////write response ////////// | |||||
| task write_response(); | |||||
| @posedge vif.clk) | |||||
| vif.bid<=tx.bid; | |||||
| vif.bresp<=tx.bresp; | |||||
| tx.bvalid<=1; | |||||
| vif.bvalid<=tx.bvalid; | |||||
| @posedge vif.clk) | |||||
| wait(vif.bready); | |||||
| @posedge vif.clk) | |||||
| tx.bvalid<=0; | |||||
| vif.bvalid<=tx.bvalid; | |||||
| endtask | |||||
| /////////read address///////// | |||||
| task read_adress(); | |||||
| @(posedge vif.clk) | |||||
| tx.arvalid<=1; | |||||
| vif.arvalid<=tx.arvalid; | |||||
| vif.arid<=tx.arid; | |||||
| //vif.araddr<=tx.araddr; | |||||
| vif.arsize<=tx.arsize; | |||||
| vif.arlen<=tx.arlen; | |||||
| vif.arburst<=tx.arburst; | |||||
| //////////fixed burst//////////// | |||||
| if(tx.arburst==2'b00) | |||||
| vif.araddr<=tx.araddr; | |||||
| vif.arid<=tx.arid; | |||||
| ////////increament burst//////// | |||||
| else if(arburst==2'b01)begin | |||||
| vif.araddr<=tx.araddr; | |||||
| for(i=0;i<arlen;i++) | |||||
| begin | |||||
| vif.arid<=tx.arid; | |||||
| vif.araddr<=(tx.araddr+(2**arsize)); | |||||
| end | |||||
| ////////wrap burst///////// | |||||
| else if(tx.arburst=2'b10)begin | |||||
| begin | |||||
| if(((tx.araddr)%2**arsize)==0)begin | |||||
| vif.arid<=tx.arid; | |||||
| burst_len=tx.arlen+1; | |||||
| burst_size=2**(tx.arsize); | |||||
| total_tr=burst_len*burst_size; | |||||
| wrap_lower_boundary=int'((tx.araddr)/(total_tr))*total_tr; | |||||
| wrap_upper_boundary=wrap_lower_boundary+total_tr; | |||||
| end | |||||
| vif.arlock<=tx.arlock; | |||||
| @(posedge vif.clk) | |||||
| wait(vif.arready) | |||||
| @(posedge vif.clk) | |||||
| tx.arvalid<=0; | |||||
| vif.arvalid<=tx.arvalid; | |||||
| endtask | |||||
| endclass | |||||
| @@ -0,0 +1,60 @@ | |||||
| // Class: axi_read_seq | |||||
| // | |||||
| class axi_read_seq#(D_WIDTH = 16, A_WIDTH = 16) extends uvm_sequence; | |||||
| `uvm_object_param_utils(axi_read_seq#(D_WIDTH, A_WIDTH)); | |||||
| // Group: Variables | |||||
| const int no_of_trans; | |||||
| bit[7:0] id; | |||||
| axi_transaction#(D_WIDTH, A_WIDTH) trans; | |||||
| test_config test_cfg; | |||||
| // Constructor: new | |||||
| function new(string name = "axi_read_seq"); | |||||
| super.new(name); | |||||
| if(!uvm_config_db#(test_config)::get(null, "uvm_test_top.seq", "config", test_cfg)) | |||||
| `uvm_fatal(get_name(), "config cannot be found in ConfigDB!") | |||||
| no_of_trans = test_cfg.no_read_cases; | |||||
| endfunction: new | |||||
| // Task: body | |||||
| // This is the user-defined task where the main sequence code resides. | |||||
| extern virtual task body(); | |||||
| endclass: axi_read_seq | |||||
| task axi_read_seq::body(); | |||||
| repeat(no_of_trans) begin | |||||
| id++; | |||||
| trans = axi_transaction#(D_WIDTH, A_WIDTH)::type_id::create("trans"); | |||||
| if(test_cfg.isAligned) begin | |||||
| trans.addr_val_align.constraint_mode(1); | |||||
| trans.addr_val_unalign.constraint_mode(0); | |||||
| trans.addr_val.constraint_mode(0); | |||||
| end | |||||
| else if (!test_cfg.isAligned) begin | |||||
| trans.addr_val_align.constraint_mode(0); | |||||
| trans.addr_val_unalign.constraint_mode(1); | |||||
| trans.addr_val.constraint_mode(0); | |||||
| end | |||||
| else begin | |||||
| trans.addr_val_align.constraint_mode(0); | |||||
| trans.addr_val_unalign.constraint_mode(0); | |||||
| trans.addr_val.constraint_mode(1); | |||||
| end | |||||
| start_item(trans); | |||||
| if(test_cfg.burst_type == 0) | |||||
| assert(trans.randomize() with { b_type == FIXED; }); | |||||
| else if(test_cfg.burst_type == 1) | |||||
| assert(trans.randomize() with { b_type == INCR; }); | |||||
| else if(test_cfg.burst_type == 2) | |||||
| assert(trans.randomize() with { b_type == WRAP;addr==16'h80c6; write==0;}); | |||||
| else | |||||
| assert(trans.randomize()); | |||||
| trans.id = {1, id}; | |||||
| finish_item(trans); | |||||
| trans.print(); | |||||
| #10; | |||||
| end | |||||
| endtask: body | |||||
| @@ -0,0 +1,350 @@ | |||||
| class axi_s_driver extends uvm_driver; | |||||
| `uvm_component_utils(axi_s_driver) | |||||
| // Components | |||||
| virtual axi_intf#(.D_WIDTH(D_WIDTH), .A_WIDTH(A_WIDTH)).SDRV vif; | |||||
| // Variables | |||||
| axi_transaction#(D_WIDTH, A_WIDTH) s_wtrans, s_rtrans; | |||||
| bit [7:0] mem [bit[A_WIDTH-1:0]]; | |||||
| bit [A_WIDTH-1:0] w_addr, r_addr; | |||||
| bit w_done, r_done; | |||||
| // Methods | |||||
| extern task drive(); | |||||
| extern task read_write_address(); | |||||
| extern task read_read_address(); | |||||
| extern task read_write_data(); | |||||
| extern task send_read_data(); | |||||
| function new(string name, uvm_component parent); | |||||
| super.new(name, parent); | |||||
| w_done = 1; | |||||
| r_done = 1; | |||||
| endfunction //new() | |||||
| // Function: build_phase | |||||
| extern function void build_phase(uvm_phase phase); | |||||
| // Function: run_phase | |||||
| extern task run_phase(uvm_phase phase); | |||||
| endclass //axi_s_driver extends uvm_driver | |||||
| function void axi_s_driver::build_phase(uvm_phase phase); | |||||
| s_wtrans = new("s_wtrans"); | |||||
| s_rtrans = new("s_rtrans"); | |||||
| endfunction: build_phase | |||||
| task axi_s_driver::run_phase(uvm_phase phase); | |||||
| vif.s_drv_cb.AWREADY <= 1; | |||||
| vif.s_drv_cb.ARREADY <= 1; | |||||
| vif.s_drv_cb.WREADY <= 1; | |||||
| // vif.s_drv_cb.BVALID <= 1; | |||||
| // vif.s_drv_cb.RLAST <= 1; | |||||
| vif.s_drv_cb.RVALID <= 1; | |||||
| vif.s_drv_cb.RDATA <= 'b0; | |||||
| forever begin | |||||
| @(vif.s_drv_cb); | |||||
| drive(); | |||||
| end | |||||
| endtask: run_phase | |||||
| task axi_s_driver::drive(); | |||||
| if(!vif.rstn) begin | |||||
| vif.s_drv_cb.RVALID <= 0; | |||||
| vif.s_drv_cb.BVALID <= 0; | |||||
| return; | |||||
| end | |||||
| // fork | |||||
| if(vif.s_drv_cb.WRITE) | |||||
| begin | |||||
| begin | |||||
| // `uvm_info("DEBUG_S", $sformatf("w_addr(), w_done = %0d", w_done), UVM_HIGH) | |||||
| //if(!s_wtrans.id[8]) | |||||
| begin | |||||
| if(w_done) begin | |||||
| w_done = 0; | |||||
| read_write_address(); | |||||
| #5 | |||||
| read_write_data(); | |||||
| w_done = 1; | |||||
| end | |||||
| end | |||||
| end | |||||
| end | |||||
| else if(!vif.s_drv_cb.WRITE) | |||||
| begin | |||||
| begin | |||||
| // `uvm_info("DEBUG_S", $sformatf("r_addr(), r_done = %0d", r_done), UVM_HIGH) | |||||
| // if(s_rtrans.id[8]) | |||||
| begin | |||||
| if(r_done) | |||||
| begin | |||||
| r_done = 0; | |||||
| $display("t is %t,enter read//////////",$time); | |||||
| #10 | |||||
| read_read_address(); | |||||
| #20 | |||||
| send_read_data(); | |||||
| r_done = 1; | |||||
| $display("t is %t,exit read//////////",$time); | |||||
| end | |||||
| end | |||||
| end | |||||
| end | |||||
| // join_none | |||||
| endtask: drive | |||||
| task axi_s_driver::read_write_address(); | |||||
| `uvm_info("DEBUG_S", "Inside read_write_address", UVM_LOW) | |||||
| wait(vif.s_drv_cb.AWVALID); | |||||
| s_wtrans.id = vif.s_drv_cb.AWID; | |||||
| s_wtrans.addr = vif.s_drv_cb.AWADDR; | |||||
| s_wtrans.b_size = vif.s_drv_cb.AWSIZE; | |||||
| s_wtrans.b_type = B_TYPE'(vif.s_drv_cb.AWBURST); | |||||
| s_wtrans.b_len = vif.s_drv_cb.AWLEN; | |||||
| s_wtrans.print(); | |||||
| endtask: read_write_address | |||||
| task axi_s_driver::read_write_data(); | |||||
| int addr_1, addr_n, addr_align; | |||||
| int lower_byte_lane, upper_byte_lane, upper_wrap_boundary, lower_wrap_boundary; | |||||
| int no_bytes, total_bytes; | |||||
| bit isAligned; | |||||
| int c; | |||||
| bit err, align_err; | |||||
| `uvm_info("DEBUG_S", "Inside read_write_data", UVM_LOW) | |||||
| vif.s_drv_cb.BVALID <= 0; | |||||
| // Initial values and calculations | |||||
| addr_1 = s_wtrans.addr; | |||||
| no_bytes = 2**s_wtrans.b_size; | |||||
| total_bytes = no_bytes * (s_wtrans.b_len+1); | |||||
| addr_align = int'(addr_1/no_bytes)*no_bytes; | |||||
| `uvm_info("DEBUG_S", $sformatf("Calculated aligned addr %0d,addr_1 %0d", addr_align,addr_1), UVM_LOW) | |||||
| isAligned = addr_1 == addr_align; | |||||
| // Calculate boundaries for WRAP Burst | |||||
| if(s_wtrans.b_type == WRAP) begin | |||||
| lower_wrap_boundary = int'(addr_1/total_bytes)*total_bytes; | |||||
| upper_wrap_boundary = lower_wrap_boundary + total_bytes; | |||||
| `uvm_info("DEBUG_S", $sformatf("Calculated Lower Wrap Boundary: %0d", lower_wrap_boundary), UVM_HIGH) | |||||
| `uvm_info("DEBUG_S", $sformatf("Calculated Upper Wrap Boundary: %0d", upper_wrap_boundary), UVM_HIGH) | |||||
| end | |||||
| // check whether the wrap burst is alligned or not | |||||
| if(s_wtrans.b_type == WRAP && !isAligned) | |||||
| align_err = 1; | |||||
| // Store data | |||||
| err = 0; | |||||
| for (int i=0; i<s_wtrans.b_len+1; i++) begin | |||||
| `uvm_info("DEBUG_S", "Inside read_data_loop", UVM_LOW) | |||||
| // addr_n = addr_align + i*no_bytes; | |||||
| // Lane selection for the first transfer. In case of unaligned transfer the bytes b/w the | |||||
| // start address and aligned address is not transferred. Thus for an unaligned burst, the | |||||
| // first transfer has less bytes and the actual burst size; | |||||
| // 'c' is a variable which stores which byte lane to select. In AXI, valid byte lane is used and | |||||
| // selected dynamically using lower_byte_lane and upper_byte_lane, but we for simplicity, we are | |||||
| // sending the data starting from WDATA[0:8*2**b_size], thus c converts the lower_byte_lane to | |||||
| // such that it always select the data lines within the valid byte lanes, i.e. [0:8*2**b_size] | |||||
| // This can be changed in future to match with proper AXI protocol | |||||
| if(i==0 || s_wtrans.b_type == FIXED) begin | |||||
| lower_byte_lane = addr_1-int'(addr_1/(D_WIDTH/8))*(D_WIDTH/8); | |||||
| upper_byte_lane = addr_align+no_bytes-1-int'(addr_1/(D_WIDTH/8))*(D_WIDTH/8); | |||||
| addr_n = addr_1; | |||||
| $display("entering"); | |||||
| c = isAligned ? 0 : lower_byte_lane; | |||||
| while (c>=no_bytes) begin | |||||
| c -= no_bytes; | |||||
| end | |||||
| end | |||||
| // For 2nd and all other transfers the address is always alligned and thus can read the entire | |||||
| // valid byte lane, i.e, [0:8*2**b_size]; and thus c always start with 0 | |||||
| else begin | |||||
| lower_byte_lane = addr_n-int'(addr_n/(D_WIDTH/8))*(D_WIDTH/8); | |||||
| upper_byte_lane = lower_byte_lane + no_bytes-1; | |||||
| c = 0; | |||||
| end | |||||
| `uvm_info("DEBUG_S", $sformatf("lower_byte_lane is %0d", lower_byte_lane), UVM_HIGH) | |||||
| `uvm_info("DEBUG_S", $sformatf("upper_byte_lane is %0d", upper_byte_lane), UVM_HIGH) | |||||
| `uvm_info("DEBUG_S", $sformatf("addr_n is %0d", addr_n), UVM_LOW) | |||||
| wait(vif.s_drv_cb.WVALID); | |||||
| `uvm_info("DEBUG_S", $sformatf("WVALID is %0d", vif.s_drv_cb.WVALID), UVM_LOW) | |||||
| // Follows little endian | |||||
| err = 0; | |||||
| for (int j=lower_byte_lane; j<=upper_byte_lane; j++) begin | |||||
| if(addr_n+j-lower_byte_lane >= 2**A_WIDTH) | |||||
| err = 1; | |||||
| if(err || align_err) | |||||
| continue; | |||||
| mem[addr_n+j-lower_byte_lane] = vif.s_drv_cb.WDATA[8*c+:8]; | |||||
| `uvm_info("DEBUG_S", $sformatf("c is %0d, addr is %0d, stored value is %h", c, addr_n+j-lower_byte_lane, mem[addr_n+j-lower_byte_lane]), UVM_HIGH) | |||||
| c++; | |||||
| c = c>=no_bytes ? 0:c; | |||||
| end | |||||
| // Update address | |||||
| if(s_wtrans.b_type != FIXED) begin | |||||
| if(isAligned) begin | |||||
| addr_n = addr_n+no_bytes; | |||||
| if(s_wtrans.b_type == WRAP) begin | |||||
| `uvm_info("DEBUG_S", $sformatf("Updated addrn before boundary check: %0d", addr_n), UVM_HIGH) | |||||
| addr_n = addr_n>=upper_wrap_boundary ? lower_wrap_boundary : addr_n; | |||||
| `uvm_info("DEBUG_S", $sformatf("Updated addrn after boundary check: %0d", addr_n), UVM_HIGH) | |||||
| end | |||||
| end | |||||
| else begin | |||||
| addr_n = addr_align + no_bytes; | |||||
| isAligned = 1; | |||||
| end | |||||
| end | |||||
| @(vif.s_drv_cb); | |||||
| end | |||||
| vif.s_drv_cb.BID <= s_wtrans.id; | |||||
| if(err || align_err) | |||||
| vif.s_drv_cb.BRESP <= 2'b01; | |||||
| else | |||||
| vif.s_drv_cb.BRESP <= 2'b00; | |||||
| @(vif.s_drv_cb); | |||||
| vif.s_drv_cb.BVALID <= 1; | |||||
| @(vif.s_drv_cb); | |||||
| wait(vif.s_drv_cb.BREADY) | |||||
| vif.s_drv_cb.BVALID <= 0; | |||||
| endtask: read_write_data | |||||
| task axi_s_driver::read_read_address(); | |||||
| `uvm_info("DEBUG_S", "Inside read_write_address", UVM_LOW) | |||||
| // `uvm_info("DEBUG_S",$sformatf("ARVALID IS %b",vif.s_drv_cb.ARVALID) , UVM_LOW) | |||||
| wait(vif.s_drv_cb.ARVALID); | |||||
| // `uvm_info("DEBUG_S", $sformatf("ARVALID IS %b",vif.s_drv_cb.ARVALID), UVM_LOW) | |||||
| s_rtrans.id = vif.s_drv_cb.ARID; | |||||
| s_rtrans.addr = vif.s_drv_cb.ARADDR; | |||||
| s_rtrans.b_size = vif.s_drv_cb.ARSIZE; | |||||
| s_rtrans.b_type = B_TYPE'(vif.s_drv_cb.ARBURST); | |||||
| s_rtrans.b_len = vif.s_drv_cb.ARLEN; | |||||
| s_rtrans.print(); | |||||
| endtask: read_read_address | |||||
| task axi_s_driver::send_read_data(); | |||||
| int addr_1, addr_n, addr_align; | |||||
| int lower_byte_lane, upper_byte_lane, upper_wrap_boundary, lower_wrap_boundary; | |||||
| int no_bytes, total_bytes; | |||||
| bit isAligned; | |||||
| int c; | |||||
| bit err; | |||||
| `uvm_info("DEBUG_S", "Inside send_read_data", UVM_LOW) | |||||
| addr_1 = s_rtrans.addr; | |||||
| no_bytes = 2**s_rtrans.b_size; | |||||
| total_bytes = no_bytes * (s_rtrans.b_len+1); | |||||
| // Calculate align address | |||||
| addr_align = int'(addr_1/no_bytes)*no_bytes; | |||||
| `uvm_info("DEBUG_S", $sformatf("Calculated aligned addr %0d", addr_align), UVM_HIGH) | |||||
| isAligned = addr_1 == addr_align; | |||||
| // If WRAP Burst then calculate the wrap boundary | |||||
| if(s_rtrans.b_type == WRAP) begin | |||||
| lower_wrap_boundary = int'(addr_1/total_bytes)*total_bytes; | |||||
| upper_wrap_boundary = lower_wrap_boundary + total_bytes; | |||||
| `uvm_info("DEBUG_S", $sformatf("Calculated Lower Wrap Boundary: %0d", lower_wrap_boundary), UVM_HIGH) | |||||
| `uvm_info("DEBUG_S", $sformatf("Calculated Upper Wrap Boundary: %0d", upper_wrap_boundary), UVM_HIGH) | |||||
| end | |||||
| // Initial signals | |||||
| vif.s_drv_cb.RLAST <= 0; | |||||
| vif.s_drv_cb.RVALID <=0; | |||||
| vif.s_drv_cb.RID <= s_rtrans.id; | |||||
| // Store data | |||||
| for (int i=0; i<s_rtrans.b_len+1; i++) begin | |||||
| // `uvm_info("DEBUG_S", "Inside send_data_loop", UVM_LOW) | |||||
| // addr_n = addr_align + i*no_bytes; | |||||
| // Lane selection for the first transfer. In case of unaligned transfer the bytes b/w the | |||||
| // start address and aligned address is not transferred. Thus for an unaligned burst, the | |||||
| // first transfer has less bytes and the actual burst size; | |||||
| // 'c' is a variable which stores which byte lane to select. In AXI, valid byte lane is used and | |||||
| // selected dynamically using lower_byte_lane and upper_byte_lane, but we for simplicity, we are | |||||
| // sending the data starting from WDATA[0:8*2**b_size], thus c converts the lower_byte_lane to | |||||
| // such that it always select the data lines within the valid byte lanes, i.e. [0:8*2**b_size] | |||||
| // This can be changed in future to match with proper AXI protocol | |||||
| if(i==0 || s_rtrans.b_type == FIXED) begin | |||||
| lower_byte_lane = addr_1-int'(addr_1/(D_WIDTH/8))*(D_WIDTH/8); | |||||
| upper_byte_lane = addr_align+no_bytes-1-int'(addr_1/(D_WIDTH/8))*(D_WIDTH/8); | |||||
| addr_n = addr_1; | |||||
| c = isAligned ? 0 : lower_byte_lane; | |||||
| while (c>=no_bytes) begin | |||||
| c -= no_bytes; | |||||
| end | |||||
| end | |||||
| // For 2nd and all other transfers the address is always alligned and thus can read the entire | |||||
| // valid byte lane, i.e, [0:8*2**b_size]; and thus c always start with 0 | |||||
| else begin | |||||
| lower_byte_lane = addr_n-int'(addr_n/(D_WIDTH/8))*(D_WIDTH/8); | |||||
| upper_byte_lane = lower_byte_lane + no_bytes-1; | |||||
| c = 0; | |||||
| end | |||||
| // @(vif.s_drv_cb); | |||||
| `uvm_info("DEBUG_S", $sformatf("lower_byte_lane is %0d", lower_byte_lane), UVM_HIGH) | |||||
| `uvm_info("DEBUG_S", $sformatf("upper_byte_lane is %0d", upper_byte_lane), UVM_HIGH) | |||||
| `uvm_info("DEBUG_S", $sformatf("addr_n is %0d", addr_n), UVM_HIGH) | |||||
| // Follows little endian | |||||
| err = 0; | |||||
| for (int j=lower_byte_lane; j<=upper_byte_lane; j++) begin | |||||
| if(!mem.exists(addr_n+j-lower_byte_lane)) begin | |||||
| err = 1; | |||||
| vif.s_drv_cb.RDATA[8*c+:8] <= 'b0; | |||||
| `uvm_info("DEBUG_S", $sformatf("c is %0d, addr is %0d, No data in location", c, addr_n+j-lower_byte_lane), UVM_HIGH) | |||||
| end | |||||
| else begin | |||||
| vif.s_drv_cb.RDATA[8*c+:8] <= mem[addr_n+j-lower_byte_lane]; | |||||
| `uvm_info("DEBUG_S", $sformatf("c is %0d, addr is %0d, stored value is %h", c, addr_n+j-lower_byte_lane, mem[addr_n+j-lower_byte_lane]), UVM_HIGH) | |||||
| end | |||||
| c++; | |||||
| c = c>=no_bytes ? 0:c; | |||||
| end | |||||
| if(i == s_rtrans.b_len) begin | |||||
| vif.s_drv_cb.RLAST <= 1; | |||||
| end | |||||
| if(err) | |||||
| vif.s_drv_cb.RRESP <= 2'b01; | |||||
| else | |||||
| vif.s_drv_cb.RRESP <= 2'b00; | |||||
| @(vif.s_drv_cb); | |||||
| vif.s_drv_cb.RVALID <= 1; | |||||
| // Update address | |||||
| if(s_rtrans.b_type != FIXED) begin | |||||
| if(isAligned) begin | |||||
| addr_n = addr_n+no_bytes; | |||||
| if(s_rtrans.b_type == WRAP) begin | |||||
| `uvm_info("DEBUG_S", $sformatf("Updated addrn before boundary check: %0d", addr_n), UVM_HIGH) | |||||
| addr_n = addr_n>=upper_wrap_boundary ? lower_wrap_boundary : addr_n; | |||||
| `uvm_info("DEBUG_S", $sformatf("Updated addrn after boundary check: %0d", addr_n), UVM_HIGH) | |||||
| end | |||||
| end | |||||
| else begin | |||||
| addr_n = addr_align + no_bytes; | |||||
| isAligned = 1; | |||||
| end | |||||
| end | |||||
| @(vif.s_drv_cb); | |||||
| wait(vif.s_drv_cb.RREADY); | |||||
| vif.s_drv_cb.RVALID <= 0; | |||||
| end | |||||
| endtask: send_read_data | |||||
| @@ -0,0 +1,109 @@ | |||||
| class axi_s_monitor extends uvm_monitor; | |||||
| `uvm_component_utils(axi_s_monitor) | |||||
| // Components | |||||
| uvm_analysis_port#(axi_transaction#(D_WIDTH, A_WIDTH)) ap; | |||||
| virtual axi_intf#(.D_WIDTH(D_WIDTH), .A_WIDTH(A_WIDTH)).SMON vif; | |||||
| // variables | |||||
| axi_transaction#(D_WIDTH, A_WIDTH) w_trans, r_trans; | |||||
| bit w_done, r_done; | |||||
| int b_size; | |||||
| // Methods | |||||
| extern task run_mon(uvm_phase phase); | |||||
| extern task write_monitor(); | |||||
| extern task read_monitor(); | |||||
| function new(string name, uvm_component parent); | |||||
| super.new(name, parent); | |||||
| w_done = 1; | |||||
| r_done = 1; | |||||
| endfunction //new() | |||||
| // Function: build_phase | |||||
| extern function void build_phase(uvm_phase phase); | |||||
| // Function: run_phase | |||||
| extern task run_phase(uvm_phase phase); | |||||
| endclass //axi_s_monitor extends uvm_monitor | |||||
| function void axi_s_monitor::build_phase(uvm_phase phase); | |||||
| ap = new("ap", this); | |||||
| endfunction: build_phase | |||||
| task axi_s_monitor::run_phase(uvm_phase phase); | |||||
| forever begin | |||||
| run_mon(phase); | |||||
| @(vif.mon_cb); | |||||
| end | |||||
| endtask: run_phase | |||||
| task axi_s_monitor::run_mon(uvm_phase phase); | |||||
| fork | |||||
| if(w_done) begin | |||||
| phase.raise_objection(this); | |||||
| w_done = 0; | |||||
| write_monitor(); | |||||
| w_done = 1; | |||||
| phase.drop_objection(this); | |||||
| end | |||||
| if(r_done) begin | |||||
| phase.raise_objection(this); | |||||
| r_done = 0; | |||||
| read_monitor(); | |||||
| r_done = 1; | |||||
| phase.drop_objection(this); | |||||
| end | |||||
| join_none | |||||
| endtask: run_mon | |||||
| task axi_s_monitor::write_monitor(); | |||||
| if(vif.mon_cb.AWVALID && vif.mon_cb.AWREADY) begin | |||||
| w_trans = axi_transaction#(D_WIDTH, A_WIDTH)::type_id::create("w_trans"); | |||||
| w_trans.addr = vif.mon_cb.AWADDR; | |||||
| w_trans.id = vif.mon_cb.AWID; | |||||
| w_trans.b_size = vif.mon_cb.AWSIZE; | |||||
| w_trans.b_len = vif.mon_cb.AWLEN; | |||||
| w_trans.b_type = B_TYPE'(vif.mon_cb.AWBURST); | |||||
| w_trans.data = new [w_trans.b_len+1]; | |||||
| for (int i=0; i<w_trans.b_len+1; i++) begin | |||||
| @(vif.mon_cb); | |||||
| wait(vif.mon_cb.WVALID && vif.mon_cb.WREADY); | |||||
| w_trans.data[i] = new [D_WIDTH/8]; | |||||
| for (int j=0; j<D_WIDTH/8; j++) begin | |||||
| w_trans.data[i][j] = vif.mon_cb.WDATA[8*j+:8]; | |||||
| end | |||||
| end | |||||
| wait(vif.mon_cb.BVALID); | |||||
| w_trans.b_resp = vif.mon_cb.BRESP; | |||||
| ap.write(w_trans); | |||||
| `uvm_info("SMON", $sformatf("WTRANS %s", w_trans.convert2string()), UVM_HIGH) | |||||
| end | |||||
| endtask: write_monitor | |||||
| task axi_s_monitor::read_monitor(); | |||||
| if(vif.mon_cb.ARVALID && vif.mon_cb.ARREADY) begin | |||||
| r_trans = axi_transaction#(D_WIDTH, A_WIDTH)::type_id::create("r_trans"); | |||||
| r_trans.addr = vif.mon_cb.ARADDR; | |||||
| r_trans.id = vif.mon_cb.ARID; | |||||
| r_trans.b_size = vif.mon_cb.ARSIZE; | |||||
| r_trans.b_len = vif.mon_cb.ARLEN; | |||||
| r_trans.b_type = B_TYPE'(vif.mon_cb.ARBURST); | |||||
| r_trans.data = new [r_trans.b_len+1]; | |||||
| r_trans.r_resp = new [r_trans.b_len+1]; | |||||
| for (int i=0; i<r_trans.b_len+1; i++) begin | |||||
| @(vif.mon_cb); | |||||
| wait(vif.mon_cb.RVALID && vif.mon_cb.RREADY); | |||||
| r_trans.data[i] = new [D_WIDTH/8]; | |||||
| for (int j=0; j<D_WIDTH/8; j++) begin | |||||
| r_trans.data[i][j] = vif.mon_cb.RDATA[8*j+:8]; | |||||
| end | |||||
| r_trans.r_resp[i] = vif.mon_cb.RRESP; | |||||
| end | |||||
| ap.write(r_trans); | |||||
| `uvm_info("SMON", $sformatf("RTRANS %s", r_trans.convert2string()), UVM_HIGH) | |||||
| end | |||||
| endtask: read_monitor | |||||
| @@ -0,0 +1,86 @@ | |||||
| `uvm_analysis_imp_decl(_master) | |||||
| `uvm_analysis_imp_decl(_slave) | |||||
| class axi_scoreboard extends uvm_scoreboard; | |||||
| `uvm_component_utils(axi_scoreboard) | |||||
| // Components | |||||
| uvm_analysis_imp_master#(axi_transaction#(D_WIDTH, A_WIDTH), axi_scoreboard) m_ap_imp; | |||||
| uvm_analysis_imp_slave#(axi_transaction#(D_WIDTH, A_WIDTH), axi_scoreboard) s_ap_imp; | |||||
| // Variables | |||||
| axi_transaction#(D_WIDTH, A_WIDTH) m_wtrans, m_rtrans, s_wtrans, s_rtrans; | |||||
| bit [1:0] w_rcvd, r_rcvd; | |||||
| int passCnt, failCnt; | |||||
| function new(string name, uvm_component parent); | |||||
| super.new(name, parent); | |||||
| endfunction //new() | |||||
| function void write_master(axi_transaction#(D_WIDTH, A_WIDTH) trans); | |||||
| if(trans.id[8]) begin | |||||
| m_rtrans = trans; | |||||
| r_rcvd[0] = 1; | |||||
| end | |||||
| else begin | |||||
| m_wtrans = trans; | |||||
| w_rcvd[0] = 1; | |||||
| end | |||||
| check(); | |||||
| endfunction | |||||
| function void write_slave(axi_transaction#(D_WIDTH, A_WIDTH) trans); | |||||
| if(trans.id[8]) begin | |||||
| s_rtrans = trans; | |||||
| r_rcvd[1] = 1; | |||||
| end | |||||
| else begin | |||||
| s_wtrans = trans; | |||||
| w_rcvd[1] = 1; | |||||
| end | |||||
| check(); | |||||
| endfunction | |||||
| function void check(); | |||||
| if(w_rcvd == 2'b11) begin | |||||
| if(m_wtrans.compare(s_wtrans)) begin | |||||
| `uvm_info("SCB", $sformatf("ID %0d: PASSED", m_wtrans.id), UVM_NONE) | |||||
| passCnt++; | |||||
| end | |||||
| else begin | |||||
| `uvm_error("SCB", $sformatf("ID %0d: FAILED", m_wtrans.id)) | |||||
| failCnt++; | |||||
| end | |||||
| w_rcvd = 2'b00; | |||||
| end | |||||
| if(r_rcvd == 2'b11) begin | |||||
| if(m_rtrans.compare(s_rtrans)) begin | |||||
| `uvm_info("SCB", $sformatf("ID %0d: PASSED", m_rtrans.id), UVM_NONE) | |||||
| passCnt++; | |||||
| end | |||||
| else begin | |||||
| `uvm_error("SCB", $sformatf("ID %0d: FAILED", m_rtrans.id)) | |||||
| failCnt++; | |||||
| end | |||||
| r_rcvd = 2'b00; | |||||
| end | |||||
| endfunction | |||||
| // Function: build_phase | |||||
| extern function void build_phase(uvm_phase phase); | |||||
| endclass //axi_scoreboard extends uvm_scoreboard | |||||
| function void axi_scoreboard::build_phase(uvm_phase phase); | |||||
| /* note: Do not call super.build_phase() from any class that is extended from an UVM base class! */ | |||||
| /* For more information see UVM Cookbook v1800.2 p.503 */ | |||||
| //super.build_phase(phase); | |||||
| m_ap_imp = new("m_ap_imp", this); | |||||
| s_ap_imp = new("s_ap_imp", this); | |||||
| endfunction: build_phase | |||||
| @@ -0,0 +1,85 @@ | |||||
| // axi write sequence | |||||
| class axi_write_sequence extends uvm_sequence#(axi_seq_item); | |||||
| `uvm_object_utils(axi_write_sequence); | |||||
| function new(string name="axi_write_sequence"); | |||||
| super.new(name); | |||||
| endfunction | |||||
| task body(); | |||||
| req=axi_seq_item::type_id::create(req); | |||||
| start_item(req); | |||||
| assert(req.randomize()with{req.wr_rd==1;req.awaddr==0x08;req.awburst==2'b 10;req.awlen==3;req.awsize==2;req.wdata==0x22fb;}); | |||||
| finish_item(req); | |||||
| endtask | |||||
| endclass | |||||
| // axi_read_sequence | |||||
| class axi_read_sequence extends uvm_sequence#(axi_seq_item); | |||||
| `uvm_object_utils(axi_read_sequence); | |||||
| function new(string name="axi_read_sequence"); | |||||
| super.new(name); | |||||
| endfunction | |||||
| task body(); | |||||
| req=axi_seq_item::type_id::create(req); | |||||
| start_item(req); | |||||
| assert(req.randomize()with{req.wr_rd==0;req.araddr==0x08;arlen==3;arburst==2'b 10;}); | |||||
| finish_item(req); | |||||
| endtask | |||||
| endclass | |||||
| // axi aligned sequence | |||||
| class axi_aligned_sequence extends uvm_sequence#(axi_seq_item); | |||||
| `uvm_object_utils(axi_aligned_sequence); | |||||
| function new(string name="axi_aligned_sequence"); | |||||
| super.new(name); | |||||
| endfunction | |||||
| task body(); | |||||
| req=axi_seq_item::type_id::create(req); | |||||
| start_item(req); | |||||
| assert(req.randomize()with{req.wr_rd==1;req.awaddr==0x08;req.awsize==3;req.awburst==2'b 10;req.wdata==0xfa30;}); | |||||
| finish_item(req); | |||||
| endtask | |||||
| endclass | |||||
| // write followed by read | |||||
| class write_read_sequence extends uvm_sequence#(axi_seq_item); | |||||
| `uvm_object_utils(write_read_sequence); | |||||
| axi_write_sequence axi_wr_seq; | |||||
| axi_read_sequence axi_rd_seq; | |||||
| function new(string name="write_read_sequence"); | |||||
| super.new(name); | |||||
| endfunction | |||||
| task body(); | |||||
| `uvm_do(axi_wr_seq); | |||||
| `uvm_do(axi_rd_seq); | |||||
| end | |||||
| endclass | |||||
| @@ -0,0 +1,44 @@ | |||||
| class axi_slave extends uvm_agent; | |||||
| `uvm_component_utils(axi_slave) | |||||
| // Components | |||||
| axi_s_driver drv; | |||||
| axi_s_monitor mon; | |||||
| uvm_analysis_port#(axi_transaction#(D_WIDTH, A_WIDTH)) ap; | |||||
| // Variables | |||||
| env_config env_cfg; | |||||
| function new(string name, uvm_component parent); | |||||
| super.new(name, parent); | |||||
| endfunction //new() | |||||
| // Function: build_phase | |||||
| extern function void build_phase(uvm_phase phase); | |||||
| // Function: connect_phase | |||||
| extern function void connect_phase(uvm_phase phase); | |||||
| endclass //axi_slave extends uvm_agent | |||||
| function void axi_slave::build_phase(uvm_phase phase); | |||||
| env_cfg = new("env_cfg"); | |||||
| assert (uvm_config_db#(env_config)::get(this, "", "config", env_cfg)) begin | |||||
| `uvm_info(get_name(), "vif has been found in ConfigDB.", UVM_LOW) | |||||
| end else `uvm_fatal(get_name(), "vif cannot be found in ConfigDB!") | |||||
| drv = axi_s_driver::type_id::create("drv", this); | |||||
| mon = axi_s_monitor::type_id::create("mon", this); | |||||
| drv.vif = env_cfg.intf; | |||||
| mon.vif = env_cfg.intf; | |||||
| ap = new("ap", this); | |||||
| endfunction: build_phase | |||||
| function void axi_slave::connect_phase(uvm_phase phase); | |||||
| super.connect_phase(phase); | |||||
| mon.ap.connect(ap); | |||||
| endfunction: connect_phase | |||||
| @@ -0,0 +1,42 @@ | |||||
| // `include "uvm_pkg.sv" | |||||
| import uvm_pkg::*; | |||||
| `include "uvm_macros.svh" | |||||
| `include "axi_config_objs.svh" | |||||
| `include "axi_interface.sv" | |||||
| `include "axi_transaction.sv" | |||||
| `include "axi_write_seq.sv" | |||||
| `include "axi_read_seq.sv" | |||||
| `include "axi_m_driver.sv" | |||||
| `include "axi_m_monitor.sv" | |||||
| `include "axi_master_agent.sv" | |||||
| `include "axi_s_driver.sv" | |||||
| `include "axi_s_monitor.sv" | |||||
| `include "axi_slave_agent.sv" | |||||
| `include "axi_scoreboard.sv" | |||||
| `include "axi_env.sv" | |||||
| `include "axi_test.sv" | |||||
| `include "uvm_pkg.sv" | |||||
| // parameter A_WIDTH = 8; // Address bus width | |||||
| // parameter D_WIDTH = 128; // Data bus width | |||||
| module top; | |||||
| bit clk, rstn; | |||||
| always #5 clk = ~clk; | |||||
| initial rstn = 1; | |||||
| axi_intf#(.A_WIDTH(A_WIDTH), .D_WIDTH(D_WIDTH)) intf(clk, rstn); | |||||
| env_config env_cfg; | |||||
| initial begin | |||||
| env_cfg = new(); | |||||
| env_cfg.intf = intf; | |||||
| uvm_config_db#(env_config)::set(null, "uvm_test_top", "config", env_cfg); | |||||
| uvm_config_db#(env_config)::set(null, "uvm_test_top.env.master", "config", env_cfg); | |||||
| uvm_config_db#(env_config)::set(null, "uvm_test_top.env.slave", "config", env_cfg); | |||||
| run_test("axi_base_test"); | |||||
| end | |||||
| endmodule | |||||
| @@ -0,0 +1,166 @@ | |||||
| class axi_base_test extends uvm_test; | |||||
| `uvm_component_utils(axi_base_test) | |||||
| // Components | |||||
| axi_env env; | |||||
| axi_write_seq#(.D_WIDTH(D_WIDTH), .A_WIDTH(A_WIDTH)) wr_seq; | |||||
| axi_read_seq#(.D_WIDTH(D_WIDTH), .A_WIDTH(A_WIDTH)) rd_seq; | |||||
| // variables | |||||
| env_config env_cfg; | |||||
| test_config test_cfg; | |||||
| function new(string name, uvm_component parent); | |||||
| super.new(name, parent); | |||||
| test_cfg = new("test_cfg"); | |||||
| test_cfg.no_write_cases = 1; | |||||
| test_cfg.no_read_cases = 1; | |||||
| endfunction //new() | |||||
| // Function: build_phase | |||||
| extern function void build_phase(uvm_phase phase); | |||||
| // Function: end_of_elaboration_phase | |||||
| extern function void end_of_elaboration_phase(uvm_phase phase); | |||||
| // Function: run_phase | |||||
| extern task run_phase(uvm_phase phase); | |||||
| endclass //axi_base_test extends uvm_test | |||||
| function void axi_base_test::build_phase(uvm_phase phase); | |||||
| test_cfg.burst_type = 2; | |||||
| uvm_config_db#(test_config)::set(null, "uvm_test_top.seq", "config", test_cfg); | |||||
| wr_seq = new("wr_seq"); | |||||
| rd_seq = new("rd_seq"); | |||||
| env = axi_env::type_id::create("env", this); | |||||
| endfunction: build_phase | |||||
| function void axi_base_test::end_of_elaboration_phase(uvm_phase phase); | |||||
| super.end_of_elaboration_phase(phase); | |||||
| uvm_top.print_topology(); | |||||
| endfunction: end_of_elaboration_phase | |||||
| task axi_base_test::run_phase(uvm_phase phase); | |||||
| phase.raise_objection(this); | |||||
| fork | |||||
| wr_seq.start(env.master.w_seqr); | |||||
| #200 | |||||
| rd_seq.start(env.master.r_seqr); | |||||
| // end | |||||
| join | |||||
| phase.drop_objection(this); | |||||
| endtask: run_phase | |||||
| // **************************************************************************************** | |||||
| // Directed Test Cases | |||||
| // **************************************************************************************** | |||||
| class axi_write_test extends axi_base_test; | |||||
| `uvm_component_utils(axi_write_test) | |||||
| function new(string name, uvm_component parent); | |||||
| super.new(name, parent); | |||||
| endfunction //new() | |||||
| function void build_phase(uvm_phase phase); | |||||
| super.build_phase(phase); | |||||
| endfunction: build_phase | |||||
| function void end_of_elaboration_phase(uvm_phase phase); | |||||
| super.end_of_elaboration_phase(phase); | |||||
| endfunction: end_of_elaboration_phase | |||||
| task run_phase(uvm_phase phase); | |||||
| phase.raise_objection(this); | |||||
| wr_seq.start(env.master.w_seqr); | |||||
| phase.drop_objection(this); | |||||
| endtask: run_phase | |||||
| endclass //write_test extends axi_base_test | |||||
| class axi_read_test extends axi_base_test; | |||||
| `uvm_component_utils(axi_read_test) | |||||
| function new(string name, uvm_component parent); | |||||
| super.new(name, parent); | |||||
| endfunction //new() | |||||
| function void build_phase(uvm_phase phase); | |||||
| super.build_phase(phase); | |||||
| endfunction: build_phase | |||||
| function void end_of_elaboration_phase(uvm_phase phase); | |||||
| super.end_of_elaboration_phase(phase); | |||||
| endfunction: end_of_elaboration_phase | |||||
| task run_phase(uvm_phase phase); | |||||
| phase.raise_objection(this); | |||||
| wr_seq.start(env.master.w_seqr); | |||||
| // rd_seq.start(env.master.r_seqr); | |||||
| phase.drop_objection(this); | |||||
| endtask: run_phase | |||||
| endclass //write_test extends axi_base_test | |||||
| class axi_fixed_test extends axi_base_test; | |||||
| `uvm_component_utils(axi_fixed_test) | |||||
| function new(string name, uvm_component parent); | |||||
| super.new(name, parent); | |||||
| endfunction //new() | |||||
| function void build_phase(uvm_phase phase); | |||||
| test_cfg.burst_type = 0; | |||||
| uvm_config_db#(test_config)::set(null, "uvm_test_top.seq", "config", test_cfg); | |||||
| wr_seq = new("wr_seq"); | |||||
| rd_seq = new("rd_seq"); | |||||
| env = axi_env::type_id::create("env", this); | |||||
| endfunction: build_phase | |||||
| task run_phase(uvm_phase phase); | |||||
| super.run_phase(phase); | |||||
| endtask: run_phase | |||||
| endclass //axi_fixed_test extends axi_base_test | |||||
| class axi_incr_test extends axi_base_test; | |||||
| `uvm_component_utils(axi_incr_test) | |||||
| function new(string name, uvm_component parent); | |||||
| super.new(name, parent); | |||||
| endfunction //new() | |||||
| function void build_phase(uvm_phase phase); | |||||
| test_cfg.burst_type = 1; | |||||
| uvm_config_db#(test_config)::set(null, "uvm_test_top.seq", "config", test_cfg); | |||||
| wr_seq = new("wr_seq"); | |||||
| rd_seq = new("rd_seq"); | |||||
| env = axi_env::type_id::create("env", this); | |||||
| endfunction: build_phase | |||||
| task run_phase(uvm_phase phase); | |||||
| super.run_phase(phase); | |||||
| endtask: run_phase | |||||
| endclass //axi_fixed_test extends axi_base_test | |||||
| class axi_wrap_test extends axi_base_test; | |||||
| `uvm_component_utils(axi_wrap_test) | |||||
| function new(string name, uvm_component parent); | |||||
| super.new(name, parent); | |||||
| endfunction //new() | |||||
| function void build_phase(uvm_phase phase); | |||||
| test_cfg.burst_type = 2; | |||||
| uvm_config_db#(test_config)::set(null, "uvm_test_top.seq", "config", test_cfg); | |||||
| wr_seq = new("wr_seq"); | |||||
| rd_seq = new("rd_seq"); | |||||
| env = axi_env::type_id::create("env", this); | |||||
| endfunction: build_phase | |||||
| task run_phase(uvm_phase phase); | |||||
| super.run_phase(phase); | |||||
| endtask: run_phase | |||||
| endclass //axi_fixed_test extends axi_base_test | |||||
| @@ -0,0 +1,185 @@ | |||||
| import uvm_pkg::*; | |||||
| typedef enum bit[1:0] { FIXED, INCR, WRAP } B_TYPE; | |||||
| // Class: axi_transaction | |||||
| // | |||||
| class axi_transaction#(d_width = 16, a_width = 16) extends uvm_sequence_item; | |||||
| typedef axi_transaction#(d_width, a_width) this_type_t; | |||||
| `uvm_object_param_utils(axi_transaction#(d_width, a_width)); | |||||
| // Group: Variables | |||||
| bit [8:0] id; | |||||
| rand bit [a_width-1:0] addr; | |||||
| rand bit write; | |||||
| rand bit [7:0] data [][]; | |||||
| rand bit [2:0] b_size; | |||||
| rand bit [3:0] b_len; | |||||
| rand B_TYPE b_type; | |||||
| bit b_last; | |||||
| bit [1:0] b_resp; | |||||
| bit [1:0] r_resp []; | |||||
| // Group: Constraints | |||||
| constraint b_size_val { 8*(2**b_size) <= d_width; } | |||||
| constraint data_size { | |||||
| /* solve order constraints */ | |||||
| solve b_len before data; | |||||
| solve b_size before data; | |||||
| /* rand variable constraints */ | |||||
| data.size() == b_len+1; | |||||
| foreach (data[i] ) | |||||
| data[i].size() == 2**b_size; | |||||
| } | |||||
| constraint b_len_val { | |||||
| /* solve order constraints */ | |||||
| solve b_type before b_len; | |||||
| /* rand variable constraints */ | |||||
| if(b_type == FIXED) | |||||
| b_len inside { 0, 1 }; | |||||
| else if(b_type == WRAP) | |||||
| b_len inside { 1, 3, 7, 15 }; | |||||
| } | |||||
| constraint addr_val { | |||||
| /* solve order constraints */ | |||||
| solve b_type before addr; | |||||
| solve b_size before addr; | |||||
| /* rand variable constraints */ | |||||
| if(b_type == WRAP) | |||||
| addr == int'(addr/2**b_size) * 2**b_size; | |||||
| } | |||||
| constraint addr_val_align { | |||||
| /* solve order constraints */ | |||||
| solve b_size before addr; | |||||
| /* rand variable constraints */ | |||||
| addr == int'(addr/2**b_size) * 2**b_size; | |||||
| } | |||||
| constraint addr_val_unalign { | |||||
| /* solve order constraints */ | |||||
| solve b_size before addr; | |||||
| /* rand variable constraints */ | |||||
| addr != int'(addr/2**b_size) * 2**b_size; | |||||
| } | |||||
| // Constructor: new | |||||
| function new(string name = "axi_transaction"); | |||||
| super.new(name); | |||||
| endfunction: new | |||||
| // Function: do_copy | |||||
| extern function void do_copy(uvm_object rhs); | |||||
| // Function: do_compare | |||||
| extern function bit do_compare(uvm_object rhs, uvm_comparer comparer); | |||||
| // Function: convert2string | |||||
| extern function string convert2string(); | |||||
| // Function: do_print | |||||
| extern function void do_print(uvm_printer printer); | |||||
| // Function: do_record | |||||
| // extern function void do_record(uvm_recorder recorder); | |||||
| // Function: do_pack | |||||
| // extern function void do_pack(); | |||||
| // Function: do_unpack | |||||
| // extern function void do_unpack(); | |||||
| endclass: axi_transaction | |||||
| /*----------------------------------------------------------------------------*/ | |||||
| /* Functions */ | |||||
| /*----------------------------------------------------------------------------*/ | |||||
| function void axi_transaction::do_print(uvm_printer printer); | |||||
| /* chain the print with parent classes */ | |||||
| super.do_print(printer); | |||||
| /* list of local properties to be printed: */ | |||||
| printer.print_field("ID", id, $bits(id), UVM_UNSIGNED); | |||||
| printer.print_field("Addr", addr, $bits(addr), UVM_HEX); | |||||
| printer.print_generic("Data", "dynamic array", 8*2**b_size*(b_len+1), $sformatf("%u", data)); | |||||
| printer.print_field("Burst Size", b_size, $bits(b_size), UVM_UNSIGNED); | |||||
| printer.print_field("Burst Length", b_len+1, $bits(b_len), UVM_UNSIGNED); | |||||
| printer.print_generic("Burst Type", "B_TYPE", $bits(b_len), b_type.name()); | |||||
| endfunction: do_print | |||||
| function string axi_transaction::convert2string(); | |||||
| string s; | |||||
| /* chain the convert2string with parent classes */ | |||||
| s = super.convert2string(); | |||||
| /* list of local properties to be printed: */ | |||||
| // guide 0---4---8--12--16--20--24--28--32--36--40--44--48-- | |||||
| s = {s, $sformatf("ID : %0d\n", id)}; | |||||
| s = {s, $sformatf("Addr : 0x%0h\n", addr)}; | |||||
| s = {s, $sformatf("Data : 0x%0u\n", data)}; | |||||
| s = {s, $sformatf("Busrt Type : %s\n", b_type.name())}; | |||||
| s = {s, $sformatf("Burst Size : %0d\n", b_size)}; | |||||
| s = {s, $sformatf("Busrt Length : %0d\n", b_len+1)}; | |||||
| s = {s, $sformatf("Busrt resp : 0x%0h\n", b_resp)}; | |||||
| s = {s, $sformatf("Read resp : %0u\n", r_resp)}; | |||||
| return s; | |||||
| endfunction: convert2string | |||||
| function void axi_transaction::do_copy(uvm_object rhs); | |||||
| this_type_t rhs_; | |||||
| if (!$cast(rhs_, rhs)) begin | |||||
| `uvm_error({this.get_name(), ".do_copy()"}, "Cast failed!"); | |||||
| return; | |||||
| end | |||||
| // `uvm_info({this.get_name(), ".do_copy()"}, "Cast succeded.", UVM_HIGH); | |||||
| /* chain the copy with parent classes */ | |||||
| super.do_copy(rhs); | |||||
| /* list of local properties to be copied */ | |||||
| this.id = rhs_.id; | |||||
| this.addr = rhs_.addr; | |||||
| this.data = rhs_.data; | |||||
| this.b_type = rhs_.b_type; | |||||
| this.b_size = rhs_.b_size; | |||||
| this.b_len = rhs_.b_len; | |||||
| this.b_resp = rhs_.b_resp; | |||||
| this.r_resp = rhs_.r_resp; | |||||
| endfunction: do_copy | |||||
| function bit axi_transaction::do_compare(uvm_object rhs, uvm_comparer comparer); | |||||
| this_type_t rhs_; | |||||
| if (!$cast(rhs_, rhs)) begin | |||||
| `uvm_error({this.get_name(), ".do_compare()"}, "Cast failed!"); | |||||
| return 0; | |||||
| end | |||||
| // `uvm_info({this.get_name(), ".do_compare()"}, "Cast succeded.", UVM_HIGH); | |||||
| /* chain the compare with parent classes */ | |||||
| do_compare = super.do_compare(rhs, comparer); | |||||
| /* list of local properties to be compared: */ | |||||
| do_compare &= ( | |||||
| this.id == rhs_.id && | |||||
| this.addr == rhs_.addr && | |||||
| this.b_type == rhs_.b_type && | |||||
| this.b_size == rhs_.b_size && | |||||
| this.b_len == rhs_.b_len && | |||||
| this.b_resp == rhs_.b_resp | |||||
| ); | |||||
| foreach(data[i,j]) begin | |||||
| do_compare &= this.data[i][j] == rhs_.data[i][j]; | |||||
| end | |||||
| foreach ( r_resp[i] ) begin | |||||
| do_compare &= this.r_resp[i] == rhs_.r_resp[i]; | |||||
| end | |||||
| endfunction: do_compare | |||||
| @@ -0,0 +1,61 @@ | |||||
| // Class: axi_write_seq | |||||
| // | |||||
| class axi_write_seq#(D_WIDTH = 16, int A_WIDTH = 16) extends uvm_sequence; | |||||
| `uvm_object_param_utils(axi_write_seq#(D_WIDTH, A_WIDTH)); | |||||
| // Group: Variables | |||||
| const int no_of_trans; | |||||
| bit[7:0] id; | |||||
| axi_transaction#(D_WIDTH, A_WIDTH) trans; | |||||
| test_config test_cfg; | |||||
| // Constructor: new | |||||
| function new(string name = "axi_write_seq"); | |||||
| super.new(name); | |||||
| test_cfg = new("test_cfg"); | |||||
| if(!uvm_config_db#(test_config)::get(null, "uvm_test_top.seq", "config", test_cfg)) | |||||
| `uvm_fatal(get_name(), "config cannot be found in ConfigDB!") | |||||
| no_of_trans = test_cfg.no_write_cases; | |||||
| endfunction: new | |||||
| // Task: body | |||||
| // This is the user-defined task where the main sequence code resides. | |||||
| extern virtual task body(); | |||||
| endclass: axi_write_seq | |||||
| task axi_write_seq::body(); | |||||
| repeat(no_of_trans) begin | |||||
| id++; | |||||
| trans = axi_transaction#(D_WIDTH, A_WIDTH)::type_id::create("trans"); | |||||
| if(test_cfg.isAligned) begin | |||||
| trans.addr_val_align.constraint_mode(1); | |||||
| trans.addr_val_unalign.constraint_mode(0); | |||||
| trans.addr_val.constraint_mode(0); | |||||
| end | |||||
| else if (!test_cfg.isAligned) begin | |||||
| trans.addr_val_align.constraint_mode(0); | |||||
| trans.addr_val_unalign.constraint_mode(1); | |||||
| trans.addr_val.constraint_mode(0); | |||||
| end | |||||
| else begin | |||||
| trans.addr_val_align.constraint_mode(0); | |||||
| trans.addr_val_unalign.constraint_mode(0); | |||||
| trans.addr_val.constraint_mode(1); | |||||
| end | |||||
| start_item(trans); | |||||
| if(test_cfg.burst_type == 0) | |||||
| assert(trans.randomize() with { b_type == FIXED; }); | |||||
| else if(test_cfg.burst_type == 1) | |||||
| assert(trans.randomize() with { b_type == INCR; }); | |||||
| else if(test_cfg.burst_type == 2) | |||||
| assert(trans.randomize() with { b_type == WRAP; addr==16'h80c6;write==1;}); | |||||
| else | |||||
| assert(trans.randomize()); | |||||
| trans.id = {1'b0, id}; | |||||
| finish_item(trans); | |||||
| trans.print(); | |||||
| #10; | |||||
| end | |||||
| endtask: body | |||||