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