hdk/common/verif/tb/sv/dma_classes.sv (1,057 lines of code) (raw):
// ============================================================================
// Amazon FPGA Hardware Development Kit
//
// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Amazon Software License (the "License"). You may not use
// this file except in compliance with the License. A copy of the License is
// located at
//
// http://aws.amazon.com/asl/
//
// or in the "license" file accompanying this file. This file is distributed on
// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, express or
// implied. See the License for the specific language governing permissions and
// limitations under the License.
// ============================================================================
// This has DMA helper classes
//--------------------------------------
//TODO:
// - Add support for freeing, reusing pages
// - Add flow control (Desc, and wb ring)
//************************************************************************************
//------------------------------------------------------------------------------------
// Access class, this provides access to the DUT and testing infrastructure. We
// use a class so that can move easily bewteen testbenches without changing the tests.
// The access given here are:
//
// - BAR4 peek/poke
// - Host memory write/read
//
//------------------------------------------------------------------------------------
//************************************************************************************
`ifdef SH_BFM_TB
import tb_type_defines_pkg::*;
`endif
class mem_access_t;
logic rnw;
logic [63:0] addr;
logic [31:0] data [$];
logic wc;
function new();
endfunction // new
endclass // mem_access_t
// Create alias of mailbox since XSIM does not support "Generic (non-parameterized) mailbox"
typedef mailbox #(mem_access_t) mem_access_t_mailbox;
class access_t;
bit wc; //Enable write combining
mem_access_t_mailbox poke_mb;
bit[63:0] bar;
bit[63:0] ocl_bar;
semaphore access_lock; //Semaphore to make sure read/write are exclusive. This is required for BFMs that cannot handle multiple
// outstanding cycles simultaneously
function new (input bit wc=0, input[63:0] bar=0, input[63:0] ocl_bar=0);
this.wc = wc;
this.poke_mb = new(1);
this.bar = bar;
this.ocl_bar = ocl_bar;
access_lock = new(1);
endfunction
virtual task peek_verify (input[63:0] addr, output logic[31:0] data, input [31:0] exp_data, input [31:0] mask=32'hffff_ffff, output logic error, input exit_on_error = 0, input int max_iters = 10);
int iter = 0;
while (iter < max_iters) begin
peek(.addr(addr), .data(data));
if ((data & mask) != exp_data) begin
if (iter == (max_iters-1)) begin
$display($time,,,"***ERROR*** : peek_verify - Addr = 0x%016x, Expected Data = 0x%08x, Actual Data = 0x%08x, Mask = 0x%08x", addr, exp_data, data, mask);
error = 1;
if (exit_on_error) begin
$display($time,,,"***FATAL*** : peek_verify - Exiting on Error");
$finish;
end // if (exit_on_error)
end // if (iter >= max_iters)
else begin
error = 0;
end // else: !if(iter >= max_iters)
end // if ((data & mask) != exp_data)
else
return;
iter++;
end // while (iter < max_iters)
endtask // peek_verify
task start_mem_access_threads();
fork : FORK_MEM_ACCESS_THREADS
mem_write_thread();
join_none
endtask // start_mem_access_threads
//Poke
task poke (input[63:0] addr, input[31:0] data[$], input bit wc=this.wc);
mem_access_t wr_txn;
wr_txn = new();
wr_txn.rnw = 0;
wr_txn.addr = addr;
wr_txn.data = data;
wr_txn.wc = wc;
poke_mb.put(wr_txn);
endtask // poke
virtual task poke_dw (input [63:0] addr, input [31:0] data);
logic [31:0] data_q [$];
data_q.push_back(data);
this.poke (.addr(addr), .data(data_q), .wc(0));
endtask // poke_dw
//Allocate a page. Note in sh_bfm tb don't need to do any allocation because is assoc array
virtual function void alloc_mem (input[63:0] addr);
//tb.map_host_memory(.addr(addr));
endfunction
//Read task
extern virtual task peek (input[63:0] addr, output logic[31:0] data);
//Mem Write processing thread
extern task mem_write_thread ();
//Write DW to the host from the buffer
extern virtual function void write_host_dw (input[63:0] addr, input[31:0] data);
//Read DW from the host into buffer
extern virtual function[31:0] read_host_dw (input[63:0] addr);
//Write byte to the host from the buffer
extern virtual function void write_host_byte (input[63:0] addr, input[7:0] data);
//Read byte from the host into buffer
extern virtual function[7:0] read_host_byte (input[63:0] addr);
//Write to OCL
extern virtual task poke_ocl(input[15:0] addr, input[31:0] data);
//Read from OCL
extern virtual task peek_ocl(input[15:0] addr, output[31:0] data);
endclass
//Peek
task access_t::peek (input[63:0] addr, output logic[31:0] data);
tb.peek(.addr(addr), .data(data));
endtask
//mem_write_thread
task access_t::mem_write_thread();
mem_access_t wr_txn;
forever begin
poke_mb.get(wr_txn);
if (!wr_txn.wc) begin
for (int i=0; i<wr_txn.data.size(); i++)
tb.poke(.addr(wr_txn.addr + i*4), .data(wr_txn.data[i]));
end
else begin
tb.poke_pcis_wc(.addr(wr_txn.addr), .data(wr_txn.data));
end
end
endtask // mem_write_thread
//write_host_dw
function void access_t::write_host_dw (input[63:0] addr, input[31:0] data);
for (int i=0; i<4; i++)
begin
tb.hm_put_byte(.addr(addr+i), .d(data[i*8+:8]));
end
endfunction
//read_host_dw
function[31:0] access_t::read_host_dw (input[63:0] addr);
logic[31:0] tmp_data;
for (int i=0; i<4; i++)
tmp_data[i*8+:8] = tb.hm_get_byte(.addr(addr + i));
read_host_dw = tmp_data;
endfunction
//write_host_byte
function void access_t::write_host_byte (input[63:0] addr, input[7:0] data);
tb.hm_put_byte(.addr(addr), .d(data));
endfunction
//read_host_byte
function[7:0] access_t::read_host_byte (input[63:0] addr);
logic[7:0] tmp_data;
tmp_data = tb.hm_get_byte(.addr(addr));
read_host_byte = tmp_data;
endfunction
//Write to OCL
task access_t::poke_ocl(input[15:0] addr, input[31:0] data);
tb.poke_ocl(.addr(addr), .data(data));
endtask
//Read from OCL
task access_t::peek_ocl(input[15:0] addr, output[31:0] data);
tb.peek_ocl(.addr(addr), .data(data));
endtask
//**************************************************************
//--------------------------------------------------------------
// WriteBack descriptor class
//--------------------------------------------------------------
//**************************************************************
class wb_desc_t;
logic valid;
logic eop;
logic[31:0] length;
logic[63:0] user_bits;
function new(logic valid=0, logic eop=0, logic[31:0] length=0, logic[63:0] user_bits=0);
this.valid = valid;
this.eop = eop;
this.length = length;
this.user_bits = user_bits;
endfunction
endclass
//*************************************************************************************
//-------------------------------------------------------------------------------------
// Buffer class. has data array, and methods to read/write to/from host memory,
// and compare buffers.
//-------------------------------------------------------------------------------------
//*************************************************************************************
class dma_buf_t extends gen_buf_t;
logic[63:0] addr; //this is the base address of the buffer
access_t access; //Access function
int buf_size;
bit verbose;
//Input size of buffer in bytes -- assume address of all f's is "null"
function new (access_t access, input[63:0] addr = 64'hffffffff_ffffffff, input int buf_size = 4096, input verbose=0);
super.new();
this.addr = addr;
this.access = access;
this.buf_size = buf_size;
if (verbose)
$display($time,,,"dma_buf_t: Allocating buffer addr=0x%x, size=0x%x", this.addr, this.buf_size);
endfunction
//Write to memory.
// Start_offset - byte offset of start
// Length - length in bytes
function void write_buf_host(input[63:0] start_offset=0, input[31:0] length=32'hffffffff);
// bit[31:0] length;
//If length is 0xffff_ffff, that is special value meaning use the buf_size
if (length==32'hffffffff)
length = this.buf_size;
// $display($time,,,"dma_buf_t:write_host_buf length=%0d", length);
for (int i=0; i<length; i++)
begin
//Write byte to host memory
access.write_host_byte((this.addr + start_offset + i), this.data[start_offset + i]);
end
endfunction
//Read from memory. Length is in bytes
function void read_host_buf(input[63:0] start_offset=0, input[31:0] length=32'hffffffff, input bit verbose=0);
logic [31:0] cur_dw;
// int length;
//If length is 0xffff_ffff, that is special value meaning use the buf_size
if (length==32'hffffffff)
length = (this.buf_size);
if (verbose)
$display($time,,,"dma_buf_t:read_host_buf length=%0d", length);
for (int i=0; i<length; i++)
begin
//Get byte from host memory
this.data[start_offset + i] = access.read_host_byte(this.addr + start_offset + i);
if (verbose)
$display($time,,,"dma_buf_t:read_host_buf addr=0x%016x, data[%0d]=0x%08x", this.addr + start_offset + i, start_offset + i, this.data[start_offset + i]);
end
endfunction
endclass
//*************************************************************************************
//-------------------------------------------------------------------------------------
// DMA class.
//
// Has the descriptor rings, and methods to post descriptors
//-------------------------------------------------------------------------------------
//*************************************************************************************
class sde_dma_t;
parameter C2H_DESC_BASE = 16'h0000;
parameter H2C_DESC_BASE = 16'h1000;
parameter H2C_SPB_BASE = 16'h2000;
parameter CSR_BASE = 16'h3000;
bit verbose = 0;
bit inc_page_alloc_mode = 1; //Allocate buffers in incrementing order
bit desc_type = 0; //0 = Regular, 1 = Compact
bit[15:0] c2h_desc_size; //Descriptor size in bytes
bit[15:0] h2c_desc_size; //Descriptor size in bytes
bit[7:0] c2h_wb_desc_size; //Writeback descriptor size in bytes
int page_size; //Page size in bytes
int error_count;
randc bit[15:0] rnd_alloc_page;
int alloc_num_pages;
int inc_page_alloc = 0;
int num_chan = 1; //Don't yet support multiple channels
int wb_poll_interval; //Writeback descriptor poll interval in ns
int num_c2h_pkts; //Number of c2h packets received by Host
logic[31:0] h2c_desc_q[$]; //Store all the descriptors we are creating before we post
logic[31:0] c2h_desc_q[$]; //Store all the descriptors we are creating before we post
logic[15:0] c2h_wb_desc_wr_ptr; //Have a page size ring for writeback descriptors
//logic[15:0] h2c_wb_desc_wr_ptr; //Have a page size ring for writeback descriptors
logic[15:0] c2h_wb_desc_rd_ptr; //Have a page size ring for writeback descriptors
//logic[15:0] h2c_wb_desc_rd_ptr; //Have a page size ring for writeback descriptors
dma_buf_t c2h_wb_buf; //Page for C2H writeback
//dma_buf_t h2c_wb_buf; //Page for H2C writeback
access_t access; //Access functions (peek, poke, host buf access)
dma_buf_t c2h_post_buf_q[$]; //C2H buffers posted in the descriptors
gen_buf_t c2h_pkt_rx_q[$]; //C2H received packets. This can be used by scoreboard for checking
logic[63:0] c2h_pkt_rq_user_q[$]; //C2H received user bits.
bit [63:0] cfg_c2h_wb_status_addr; //C2H WB status base address in host
bit[63:0] cfg_c2h_wb_desc_cnt_addr; //C2H WB descriptor credit count address in host
bit cfg_c2h_wb_desc_cdt_en; //Enable WB descriptor credit count
bit cfg_c2h_wb_desc_cnt_en; //Enable WB completed descriptor count
bit cfg_c2h_wb_md_ptr_en; //Enable WB metadata pointer count
bit[63:0] cfg_c2h_cdt_limit_addr; //C2H descriptor credit count address in host
bit[63:0] cfg_c2h_wb_pkt_cnt_addr; //C2H WB descriptor packet count address in host
bit cfg_c2h_wb_pkt_cnt_en; //Enable WB packet credit count
bit [63:0] cfg_c2h_wb_wr_ptr_addr; //C2H WB Read Pointer Address in host
int cfg_desc_wc_min;
int cfg_desc_wc_max;
int pc2h_min_length = 1; //Post C2H descriptor min length in bytes
int pc2h_max_length = 4096; //Post C2H descriptor max length in bytes
int pc2h_min_offset = 0; //Minimum offset of address
int pc2h_max_offset = 64; //Maximum offset of address
bit [63:0] cfg_h2c_wb_status_addr; //H2C WB status base address in host
bit[63:0] cfg_h2c_cdt_limit_addr; //H2C credit limit address
bit cfg_h2c_wb_desc_cdt_en; //Enable WB descriptor credit count
bit cfg_h2c_wb_desc_cnt_en; //Enable WB completed descriptor count
bit[63:0] cfg_h2c_wb_desc_cnt_addr; //H2C descriptor credit count address in host
bit[63:0] cfg_h2c_wb_pkt_cnt_addr; //H2C WB descriptor packet count address in host
bit cfg_h2c_wb_pkt_cnt_en; //Enable WB packet credit count
logic [63:0] c2h_csr_offset = 64'h400;
logic [63:0] c2h_csr_base_addr;
logic [63:0] h2c_csr_offset = 64'hA00;
logic [63:0] h2c_csr_base_addr;
int c2h_wb_rdptr_coalesce_cnt = 20; // Number of write-backs to wait for before updating read pointer
logic [5:0] cfg_c2h_wc_cnt = 6'd0;
logic cfg_c2h_md_wr_ptr_wc_en = 0;
logic cfg_c2h_pkt_cnt_wc_en = 0;
logic cfg_c2h_desc_cnt_wc_en = 0;
logic cfg_c2h_desc_cdt_wc_en = 0;
logic [3:0] cfg_c2h_wc_to_cnt = 4'd0;
logic [19:0] cfg_c2h_wc_to_rsln = 20'd0;
logic [5:0] cfg_h2c_wc_cnt = 6'd0;
logic cfg_h2c_pkt_cnt_wc_en = 0;
logic cfg_h2c_desc_cnt_wc_en = 0;
logic cfg_h2c_desc_cdt_wc_en = 0;
logic [3:0] cfg_h2c_wc_to_cnt = 4'd0;
logic [19:0] cfg_h2c_wc_to_rsln = 20'd0;
logic [31:0] cfg_c2h_ring_size = 512;
logic [15:0] cfg_c2h_num_md = 64;
logic [15:0] cfg_c2h_ring_offset = 0;
bit desc_wc = 0;
int h2c_total_desc = 0; //Total number of descriptors posted H2C
int c2h_total_desc = 0; //Total number of descriptors posted C2H
// C2H:
// credit_limit : 64'h00001000_00000000
// desc_cnt : 64'h00001000_00000004
// pkt_cnt : 64'h00001000_00000008
//
// H2C:
// credit_limit : 64'h00004000_00000000
// desc_cnt : 64'h00004000_00000004
// pkt_cnt : 64'h00004000_00000008
//---------------------------------------------------
// New function -- desc_size is in bytes
//---------------------------------------------------
function new(access_t access, input int page_size = 4096, input desc_type = 0, input int wb_poll_interval = 100,
input[63:0] cfg_c2h_wb_status_addr=64'h00001000_00000000,
input cfg_c2h_wb_desc_cdt_en=1,
input cfg_c2h_wb_desc_cnt_en=0,
input cfg_c2h_wb_md_ptr_en = 1,
input cfg_c2h_wb_pkt_cnt_en=0,
input[63:0] cfg_h2c_wb_status_addr=64'h00004000_00000000,
input cfg_h2c_wb_desc_cdt_en=1,
input cfg_h2c_wb_desc_cnt_en=0,
input cfg_h2c_wb_pkt_cnt_en=0,
input int cfg_desc_wc_min = 64,
input int cfg_desc_wc_max = 64,
input [5:0] cfg_c2h_wc_cnt = 6'd0,
input cfg_c2h_md_wr_ptr_wc_en = 0,
input cfg_c2h_pkt_cnt_wc_en = 0,
input cfg_c2h_desc_cnt_wc_en = 0,
input cfg_c2h_desc_cdt_wc_en = 0,
input [3:0] cfg_c2h_wc_to_cnt = 4'd0,
input [19:0] cfg_c2h_wc_to_rsln = 20'd0,
input [5:0] cfg_h2c_wc_cnt = 6'd0,
input cfg_h2c_pkt_cnt_wc_en = 0,
input cfg_h2c_desc_cnt_wc_en = 0,
input cfg_h2c_desc_cdt_wc_en = 0,
input [3:0] cfg_h2c_wc_to_cnt = 4'd0,
input [19:0] cfg_h2c_wc_to_rsln = 20'd0,
input [15:0] cfg_c2h_num_md = 256,
input [15:0] cfg_c2h_ring_offset = 0,
input verbose = 0
);
bit[15:0] cur_page;
this.desc_type = desc_type;
this.page_size = page_size;
this.access = access;
this.desc_wc = access.wc;
this.c2h_desc_size = 16;
this.h2c_desc_size = desc_type ? 16 : 32;
this.c2h_wb_desc_size = desc_type ? 8 : 16;
this.alloc_num_pages = 0;
this.c2h_wb_desc_wr_ptr = 0;
this.c2h_wb_desc_rd_ptr = 0;
this.wb_poll_interval = (wb_poll_interval<1)? 1: wb_poll_interval;
this.cfg_desc_wc_min = cfg_desc_wc_min;
this.cfg_desc_wc_max = cfg_desc_wc_max;
this.verbose = verbose;
num_c2h_pkts = 0;
this.h2c_csr_base_addr = CSR_BASE + this.h2c_csr_offset;
this.c2h_csr_base_addr = CSR_BASE + this.c2h_csr_offset;
this.cfg_c2h_wb_status_addr = cfg_c2h_wb_status_addr;
this.cfg_c2h_cdt_limit_addr = this.cfg_c2h_wb_status_addr + 4;
this.cfg_c2h_wb_desc_cnt_addr = this.cfg_c2h_wb_status_addr + 8;
this.cfg_c2h_wb_desc_cdt_en = cfg_c2h_wb_desc_cdt_en;
this.cfg_c2h_wb_desc_cnt_en = cfg_c2h_wb_desc_cnt_en;
this.cfg_c2h_wb_md_ptr_en = cfg_c2h_wb_md_ptr_en;
this.cfg_c2h_wb_pkt_cnt_addr = this.cfg_c2h_wb_status_addr + 12;
this.cfg_c2h_wb_pkt_cnt_en = cfg_c2h_wb_pkt_cnt_en;
this.cfg_c2h_wb_wr_ptr_addr = this.cfg_c2h_wb_status_addr + 16;
this.cfg_h2c_wb_status_addr = cfg_h2c_wb_status_addr;
this.cfg_h2c_cdt_limit_addr = this.cfg_h2c_wb_status_addr + 4;
this.cfg_h2c_wb_desc_cnt_addr = this.cfg_h2c_wb_status_addr + 8;
this.cfg_h2c_wb_desc_cdt_en = cfg_h2c_wb_desc_cdt_en;
this.cfg_h2c_wb_desc_cnt_en = cfg_h2c_wb_desc_cnt_en;
this.cfg_h2c_wb_pkt_cnt_addr = this.cfg_h2c_wb_status_addr + 12;
this.cfg_h2c_wb_pkt_cnt_en = cfg_h2c_wb_pkt_cnt_en;
this.cfg_c2h_wc_cnt = cfg_c2h_wc_cnt ;
this.cfg_c2h_md_wr_ptr_wc_en = cfg_c2h_md_wr_ptr_wc_en;
this.cfg_c2h_pkt_cnt_wc_en = cfg_c2h_pkt_cnt_wc_en ;
this.cfg_c2h_desc_cnt_wc_en = cfg_c2h_desc_cnt_wc_en ;
this.cfg_c2h_desc_cdt_wc_en = cfg_c2h_desc_cdt_wc_en ;
this.cfg_c2h_wc_to_cnt = cfg_c2h_wc_to_cnt ;
this.cfg_c2h_wc_to_rsln = cfg_c2h_wc_to_rsln ;
this.cfg_h2c_wc_cnt = cfg_h2c_wc_cnt ;
this.cfg_h2c_pkt_cnt_wc_en = cfg_h2c_pkt_cnt_wc_en ;
this.cfg_h2c_desc_cnt_wc_en = cfg_h2c_desc_cnt_wc_en ;
this.cfg_h2c_desc_cdt_wc_en = cfg_h2c_desc_cdt_wc_en ;
this.cfg_h2c_wc_to_cnt = cfg_h2c_wc_to_cnt ;
this.cfg_h2c_wc_to_rsln = cfg_h2c_wc_to_rsln ;
this.cfg_c2h_num_md = cfg_c2h_num_md;
this.cfg_c2h_ring_size = this.cfg_c2h_num_md * this.c2h_wb_desc_size;
this.cfg_c2h_ring_offset = cfg_c2h_ring_offset;
// Allocate metadata ring
c2h_wb_buf = new(.access(access), .addr(alloc_page(.s("C2H_WB_BUF")) + cfg_c2h_ring_offset), .buf_size(cfg_c2h_ring_size), .verbose(this.verbose));
//Initialize the writeback descriptors to 0
c2h_wb_buf.init_const(.first_data(0), .length(cfg_c2h_ring_size));
c2h_wb_buf.write_buf_host();
//Initialize the Credit limit values in Host memory to 0
access.write_host_dw(.addr(this.cfg_c2h_cdt_limit_addr), .data(32'h0));
access.write_host_dw(.addr(this.cfg_c2h_wb_desc_cnt_addr), .data(32'h0));
access.write_host_dw(.addr(this.cfg_h2c_cdt_limit_addr), .data(32'h0));
$display($time,,,"NOTE: sde_dma: NEW desc_type=%s, page_size=0x%x, c2h_wb_buf_addr=0x%x, cfg_c2h_wb_desc_cnt_addr=0x%x, cfg_c2h_wb_desc_cdt_en=0x%0x, cfg_c2h_wb_desc_cnt_en=0x%0x, cfg_c2h_wb_md_ptr_en=0x%0x cfg_c2h_wb_status_addr=0x%x, cfg_c2h_num_md=%0d, cfg_c2h_ring_offset=%0d", (this.desc_type ? "COMPACT" : "REGULAR"), this.page_size, c2h_wb_buf.addr, this.cfg_c2h_wb_desc_cnt_addr, this.cfg_c2h_wb_desc_cdt_en, this.cfg_c2h_wb_desc_cnt_en, this.cfg_c2h_wb_md_ptr_en, this.cfg_c2h_wb_status_addr, cfg_c2h_num_md, cfg_c2h_ring_offset);
endfunction
virtual task configure (input verify = 0);
// perform all required CSR configuration of the dma engine
c2h_configure(verify);
h2c_configure(verify);
endtask // configure
virtual task c2h_configure(input verify=0);
logic [31:0] rd_data;
logic [31:0] wr_data;
bit error;
logic [63:0] addr;
$display($time,,,"sde.c2h_configure");
// Initialize counters and write-back addresses
// Enable Descriptor Count and Packet Count Write-Backs
addr = c2h_csr_base_addr + 16'h300;
wr_data = {16'd0,
2'd0, cfg_c2h_wc_cnt[5:4],
cfg_c2h_wc_cnt[3:0],
cfg_c2h_md_wr_ptr_wc_en, cfg_c2h_pkt_cnt_wc_en, cfg_c2h_desc_cnt_wc_en, cfg_c2h_desc_cdt_wc_en,
cfg_c2h_wb_md_ptr_en, cfg_c2h_wb_desc_cdt_en, cfg_c2h_wb_pkt_cnt_en, cfg_c2h_wb_desc_cnt_en};
access.poke_dw (.addr(addr), .data(wr_data));
if (verify)
access.peek_verify (.addr(addr), .data(rd_data), .exp_data(wr_data), .error(error), .exit_on_error(1));
// Configure counter Write-Back Address
addr = c2h_csr_base_addr + 16'h304;
wr_data = cfg_c2h_wb_status_addr[31:0];
access.poke_dw (.addr(addr), .data(wr_data));
if (verify)
access.peek_verify (.addr(addr), .data(rd_data), .exp_data(wr_data), .error(error), .exit_on_error(1));
addr = c2h_csr_base_addr + 16'h308;
wr_data = cfg_c2h_wb_status_addr[63:32];
access.poke_dw (.addr(addr), .data(wr_data));
if (verify)
access.peek_verify (.addr(addr), .data(rd_data), .exp_data(wr_data), .error(error), .exit_on_error(1));
// Configure Coalesce Timeout Counter
addr = c2h_csr_base_addr + 16'h30C;
wr_data = {cfg_c2h_wc_to_cnt, cfg_c2h_wc_to_rsln};
access.poke_dw (.addr(addr), .data(wr_data));
if (verify)
access.peek_verify (.addr(addr), .data(rd_data), .exp_data(wr_data), .error(error), .exit_on_error(1));
// Clear credit "limit" counter
addr = c2h_csr_base_addr + 16'h100;
access.poke_dw (.addr(addr), .data(32'd0));
// Read and check if it got cleared
if (verify)
access.peek_verify (.addr(addr), .data(rd_data), .exp_data(32'd0), .error(error), .exit_on_error(1));
// Clear credit "consumed" counter
addr = c2h_csr_base_addr + 16'h104;
access.poke_dw (.addr(addr), .data(32'd0));
// Read and check if it got cleared
if (verify)
access.peek_verify (.addr(addr), .data(rd_data), .exp_data(32'd64), .error(error), .exit_on_error(1));
// Clear descriptor count counter
addr = c2h_csr_base_addr + 16'h108;
access.poke_dw (.addr(addr), .data(32'd0));
// Read and check if it got cleared
if (verify)
access.peek_verify (.addr(addr), .data(rd_data), .exp_data(32'd0), .error(error), .exit_on_error(1));
// Clear Packet Counters
addr = c2h_csr_base_addr + 16'h500;
access.poke_dw (.addr(addr), .data(32'd0));
// Read and check if it got cleared
if (verify)
access.peek_verify (.addr(addr), .data(rd_data), .exp_data(32'd0), .error(error), .exit_on_error(1));
// WB Ring configuration
// Ring Base Address
addr = c2h_csr_base_addr + 16'h318;
wr_data = c2h_wb_buf.addr[31:0];
access.poke_dw (.addr(addr), .data(wr_data));
if (verify)
access.peek_verify (.addr(addr), .data(rd_data), .exp_data(wr_data), .error(error), .exit_on_error(1));
addr = c2h_csr_base_addr + 16'h31C;
wr_data = c2h_wb_buf.addr[63:32];
access.poke_dw (.addr(addr), .data(wr_data));
if (verify)
access.peek_verify (.addr(addr), .data(rd_data), .exp_data(wr_data), .error(error), .exit_on_error(1));
// Ring Size in Bytes
addr = c2h_csr_base_addr + 16'h320;
wr_data = c2h_wb_buf.buf_size;
access.poke_dw (.addr(addr), .data(wr_data));
if (verify)
access.peek_verify (.addr(addr), .data(rd_data), .exp_data(wr_data), .error(error), .exit_on_error(1));
// Initialize Read-pointer
addr = c2h_csr_base_addr + 16'h324;
wr_data = 0;
access.poke_dw (.addr(addr), .data(wr_data));
if (verify)
access.peek_verify (.addr(addr), .data(rd_data), .exp_data(wr_data), .error(error), .exit_on_error(1));
// Clear Write-Pointer
addr = c2h_csr_base_addr + 16'h328;
wr_data = 0;
access.poke_dw (.addr(addr), .data(wr_data));
if (verify)
access.peek_verify (.addr(addr), .data(rd_data), .exp_data(wr_data), .error(error), .exit_on_error(1));
endtask // c2h_configure
virtual task h2c_configure(input verify=0);
logic [31:0] rd_data;
logic [31:0] wr_data;
bit error;
logic [63:0] addr;
$display($time,,,"sde.h2c_configure");
// Initialize counters and write-back addresses
// Enable Descriptor Count and Packet Count Write-Backs
addr = h2c_csr_base_addr + 16'h300;
wr_data = {16'd0,
2'd0, cfg_h2c_wc_cnt[5:4],
cfg_h2c_wc_cnt[3:0],
1'b0, cfg_h2c_pkt_cnt_wc_en, cfg_h2c_desc_cnt_wc_en, cfg_h2c_desc_cdt_wc_en,
1'd0, cfg_h2c_wb_desc_cdt_en, cfg_h2c_wb_pkt_cnt_en, cfg_h2c_wb_desc_cnt_en};
access.poke_dw (.addr(addr), .data(wr_data));
access.peek_verify (.addr(addr), .data(rd_data), .exp_data(wr_data), .error(error), .exit_on_error(1));
// Configure counter Write-Back Address
addr = h2c_csr_base_addr + 16'h304;
wr_data = cfg_h2c_wb_status_addr[31:0];
access.poke_dw (.addr(addr), .data(wr_data));
if (verify)
access.peek_verify (.addr(addr), .data(rd_data), .exp_data(wr_data), .error(error), .exit_on_error(1));
addr = h2c_csr_base_addr + 16'h308;
wr_data = cfg_h2c_wb_status_addr[63:32];
access.poke_dw (.addr(addr), .data(wr_data));
if (verify)
access.peek_verify (.addr(addr), .data(rd_data), .exp_data(wr_data), .error(error), .exit_on_error(1));
// Configure Coalesce Timeout Counter
addr = h2c_csr_base_addr + 16'h30C;
wr_data = {cfg_h2c_wc_to_cnt, cfg_h2c_wc_to_rsln};
access.poke_dw (.addr(addr), .data(wr_data));
if (verify)
access.peek_verify (.addr(addr), .data(rd_data), .exp_data(wr_data), .error(error), .exit_on_error(1));
// Clear credit "limit" counter
addr = h2c_csr_base_addr + 16'h100;
access.poke_dw (.addr(addr), .data(32'd0));
// Read and check if it got cleared
if (verify)
access.peek_verify (.addr(addr), .data(rd_data), .exp_data(32'd0), .error(error), .exit_on_error(1));
// Clear credit "consumed" counter
addr = h2c_csr_base_addr + 16'h104;
access.poke_dw (.addr(addr), .data(32'd0));
// Read and check if it got cleared
if (verify)
access.peek_verify (.addr(addr), .data(rd_data), .exp_data(32'd64), .error(error), .exit_on_error(1));
// Clear descriptor count counter
addr = h2c_csr_base_addr + 16'h108;
access.poke_dw (.addr(addr), .data(32'd0));
// Read and check if it got cleared
if (verify)
access.peek_verify (.addr(addr), .data(rd_data), .exp_data(32'd0), .error(error), .exit_on_error(1));
// Clear Packet Counters
addr = h2c_csr_base_addr + 16'h500;
access.poke_dw (.addr(addr), .data(32'd0));
// Read and check if it got cleared
if (verify)
access.peek_verify (.addr(addr), .data(rd_data), .exp_data(32'd0), .error(error), .exit_on_error(1));
endtask // h2c_configure
//------------------------------------------------------------------------
//Allocate a page. Note we do not support freeing and re-using pages.
// Returns the byte address of the page
//------------------------------------------------------------------------
function[63:0] alloc_page (input string s="");
if (this.alloc_num_pages == 'h10_0000)
begin
$display($time,,,"***FATAL*** Only support allocating up to 1M pages");
$finish;
end
else
begin
alloc_page = (inc_page_alloc_mode)? (inc_page_alloc++) * this.page_size: std::randomize(rnd_alloc_page) * this.page_size;
access.alloc_mem(alloc_page);
if (this.verbose)
$display($time,,,"sde_dma_t: Allocating page - %s: addr=0x%x (page_size=0x%x)", s, alloc_page, this.page_size);
end
endfunction
//---------------------------
//Post H2C descriptor
//---------------------------
virtual function void post_h2c_desc (input dma_buf_t post_buf, input sop=0, input eop=0, input spb=0, input[63:0] user=0);
logic[31:0] desc[];
if (this.h2c_desc_size==16)
begin
desc = new[4];
desc[0] = post_buf.buf_size[31:0];
desc[1] = post_buf.addr[31:0];
desc[2] = {spb, eop, post_buf.addr[47:32]};
desc[3] = 0;
end
else
begin
desc = new[8];
desc[0] = post_buf.buf_size[31:0];
desc[1] = post_buf.addr[31:0];
desc[2] = post_buf.addr[63:32];
desc[3] = {spb, eop};
desc[4] = 0;
desc[5] = 0;
desc[6] = user[31:0];
desc[7] = user[63:32];
end
for (int i=0; i<desc.size(); i++)
h2c_desc_q.push_back(desc[i]);
endfunction
//-------------------------------
//Post a C2H descriptor
//-------------------------------
virtual function void post_c2h_desc (input dma_buf_t post_buf, input sop=0, input eop=0, input[63:0] wb_addr=64'hffffffff_ffffffff);
logic[31:0] desc[];
logic [31:0] c2h_wb_desc_wr_ptr_next;
//Auto writeback address
if (wb_addr==64'hffffffff_ffffffff)
begin
c2h_wb_desc_wr_ptr_next = c2h_wb_desc_wr_ptr == (cfg_c2h_num_md-1) ? 0 : (c2h_wb_desc_wr_ptr + 1);
if (c2h_wb_desc_wr_ptr_next == c2h_wb_desc_rd_ptr)
begin
$display($time,,,"***FATAL*** sde_dma_t.post_c2h_desc write_back pointer overflow");
$finish;
end
else
begin
wb_addr = c2h_wb_buf.addr + (c2h_wb_desc_wr_ptr * c2h_wb_desc_size);
if (c2h_wb_desc_wr_ptr == (cfg_c2h_num_md - 1))
c2h_wb_desc_wr_ptr = 0;
else
c2h_wb_desc_wr_ptr += 1;
end
end
if (this.desc_type)
begin
desc = new[4];
desc[0] = post_buf.buf_size[31:0];
desc[1] = post_buf.addr[31:0];
desc[2] = post_buf.addr[47:32];
desc[3] = 32'd0;
end
else
begin
desc = new[4];
desc[0] = post_buf.buf_size[31:0];
desc[1] = post_buf.addr[31:00];
desc[2] = post_buf.addr[63:32];
desc[3] = 32'd0;
end
for (int i=0; i<desc.size(); i++)
begin
this.c2h_desc_q.push_back(desc[i]);
end
//Push onto posted buffer queue so can assemble packet when recevie packet
this.c2h_post_buf_q.push_back(post_buf);
endfunction
//----------------------------------------------------
//H2C "doorbell", is write descriptors to SDE
//----------------------------------------------------
virtual task h2c_doorbell;
int cur_wc_length;
logic[31:0] cur_wdata_q[$];
h2c_total_desc += this.h2c_desc_q.size()/(this.h2c_desc_size/4);
if (this.verbose)
$display($time,,,"sde_dma_t: H2C_DOORBELL: NumDesc=%0d, TotalDesc=0x%x", this.h2c_desc_q.size()/(this.h2c_desc_size/4), h2c_total_desc);
//Make sure have some descriptors to poke
while (this.h2c_desc_q.size()>0)
begin
//Clear out the write data
cur_wdata_q = {};
//Supported DW lengths for H2C/C2H descriptors = 1DW, 4DW, 8DW
this.cfg_desc_wc_max = (this.cfg_desc_wc_max > 8) ? 8 : this.cfg_desc_wc_max;
this.cfg_desc_wc_min = (this.cfg_desc_wc_min > 8) ? 8 : this.cfg_desc_wc_min;
if ((this.cfg_desc_wc_min == this.cfg_desc_wc_max) && (this.cfg_desc_wc_min == 1 || this.cfg_desc_wc_min == 4 || this.cfg_desc_wc_min == 8))
cur_wc_length = this.cfg_desc_wc_min;
else
std::randomize(cur_wc_length) with {cur_wc_length inside {1, 4, 8};};
//If amount of data is less than write combine length, use that amount for data length
if (this.h2c_desc_q.size<cur_wc_length)
cur_wc_length = this.h2c_desc_q.size;
//Transfer data from h2c_desc_q to cur_wdata_q;
for (int i=0; i<cur_wc_length; i++)
cur_wdata_q.push_back(this.h2c_desc_q.pop_front());
if (this.verbose)
$display($time,,,"sde_dma_t: H2C_DOORBELL: CurDW=%0d, DWLeft=%0d", cur_wdata_q.size(), h2c_desc_q.size());
access.poke(.addr(H2C_DESC_BASE), .data(cur_wdata_q), .wc(desc_wc));
end
this.h2c_desc_q = {};
endtask // h2c_doorbell
//----------------------------------------------------
//H2C "doorbell", induce Out of Order Error, Unaligned addr error
//----------------------------------------------------
virtual task h2c_doorbell_error(input out_of_order=0, input unalign_addr=0);
int cur_wc_length;
logic [31:0] cur_wdata_q[$];
logic induce_error = 0;
logic [31:0] addr1, addr2;
h2c_total_desc += this.h2c_desc_q.size()/(this.h2c_desc_size/4);
if (this.verbose)
$display($time,,,"sde_dma_t: H2C_DOORBELL: NumDesc=%0d, TotalDesc=0x%x", this.h2c_desc_q.size()/(this.h2c_desc_size/4), h2c_total_desc);
//Make sure have some descriptors to poke
while (this.h2c_desc_q.size()>0)
begin
//Clear out the write data
cur_wdata_q = {};
//Get a random write combine length -- note this works with no write combine as well (just takes a bit longer)
//Supported DW lengths for H2C/C2H descriptors = 1DW, 4DW, 8DW
this.cfg_desc_wc_max = (this.cfg_desc_wc_max > 8) ? 8 : this.cfg_desc_wc_max;
this.cfg_desc_wc_min = (this.cfg_desc_wc_min > 8) ? 8 : this.cfg_desc_wc_min;
if ((this.cfg_desc_wc_min == this.cfg_desc_wc_max) && (this.cfg_desc_wc_min == 1 || this.cfg_desc_wc_min == 4 || this.cfg_desc_wc_min == 8))
cur_wc_length = this.cfg_desc_wc_min;
else
std::randomize(cur_wc_length) with {cur_wc_length inside {1, 4, 8};};
//If amount of data is less than write combine length, use that amount for data length
if (this.h2c_desc_q.size<cur_wc_length)
cur_wc_length = this.h2c_desc_q.size;
//Transfer data from h2c_desc_q to cur_wdata_q;
for (int i=0; i<cur_wc_length; i++)
cur_wdata_q.push_back(this.h2c_desc_q.pop_front());
if (this.verbose)
$display($time,,,"sde_dma_t: H2C_DOORBELL: CurDW=%0d, DWLeft=%0d", cur_wdata_q.size(), h2c_desc_q.size());
if (out_of_order) begin
if (induce_error == 0) begin //{
//addr1 = ($urandom_range(H2C_DESC_BASE + 'h80, H2C_DESC_BASE + 16'hFC0)) & 32'hFFFF_FFC0; //64B aligned address
addr1 = H2C_DESC_BASE + 'h80;
access.poke(.addr(addr1), .data(cur_wdata_q), .wc(desc_wc));
induce_error = 1;
end else begin //}{
//do
//addr2 = ($urandom_range(H2C_DESC_BASE, H2C_DESC_BASE + 16'hFC0)) & 32'hFFFF_FFC0; //64B aligned address
//while (addr2 < addr1);
addr2 = H2C_DESC_BASE + 'h40;
access.poke(.addr(addr2), .data(cur_wdata_q), .wc(desc_wc));
end //}
end else if (unalign_addr) begin
if (induce_error == 0) begin //{
access.poke(.addr(H2C_DESC_BASE), .data(cur_wdata_q), .wc(desc_wc));
induce_error = 1;
end else begin //}{
//access.poke(.addr(H2C_DESC_BASE + $urandom_range(5,63)), .data(cur_wdata_q), .wc(desc_wc));
access.poke(.addr(H2C_DESC_BASE + 'h20), .data(cur_wdata_q), .wc(desc_wc));
end //}
end else begin
access.poke(.addr(H2C_DESC_BASE), .data(cur_wdata_q), .wc(desc_wc));
end
end
this.h2c_desc_q = {};
endtask
//----------------------------------------------------
//C2H "doorbell", is write descriptors to SDE
//----------------------------------------------------
virtual task c2h_doorbell;
int cur_wc_length;
logic[31:0] cur_wdata_q[$];
c2h_total_desc += this.c2h_desc_q.size()/(this.c2h_desc_size/4);
if (this.verbose)
$display($time,,,"sde_dma_t: C2H_DOORBELL: NumDesc=%0d, TotalDesc=0x%x", this.c2h_desc_q.size()/(this.c2h_desc_size/4), c2h_total_desc);
//Make sure have some descriptors to poke
while (this.c2h_desc_q.size()>0)
begin
//Clear out the write data
cur_wdata_q = {};
//Get a random write combine length -- note this works with no write combine as well (just takes a bit longer)
//CHAKRA: cur_wc_length = $urandom_range(this.cfg_desc_wc_max, this.cfg_desc_wc_min);
//Supported DW lengths for H2C/C2H descriptors = 1DW, 4DW, 8DW
this.cfg_desc_wc_max = (this.cfg_desc_wc_max > 8) ? 8 : this.cfg_desc_wc_max;
this.cfg_desc_wc_min = (this.cfg_desc_wc_min > 8) ? 8 : this.cfg_desc_wc_min;
if ((this.cfg_desc_wc_min == this.cfg_desc_wc_max) && (this.cfg_desc_wc_min == 1 || this.cfg_desc_wc_min == 4 || this.cfg_desc_wc_min == 8))
cur_wc_length = this.cfg_desc_wc_min;
else
std::randomize(cur_wc_length) with {cur_wc_length inside {1, 4, 8};};
//If amount of data is less than write combine length, use that amount for data length
if (this.c2h_desc_q.size<cur_wc_length)
cur_wc_length = this.c2h_desc_q.size;
//Transfer data from c2h_desc_q to cur_wdata_q;
for (int i=0; i<cur_wc_length; i++)
cur_wdata_q.push_back(this.c2h_desc_q.pop_front());
if (this.verbose)
$display($time,,,"sde_dma_t: C2H_DOORBELL: CurDW=%0d, DWLeft=%0d", cur_wdata_q.size(), c2h_desc_q.size());
access.poke(.addr(C2H_DESC_BASE), .data(cur_wdata_q), .wc(desc_wc));
end
this.c2h_desc_q = {};
endtask // c2h_doorbell
//----------------------------------------------------------------
//C2H "doorbell", induce Out of Order Error, Unaligned addr error
//----------------------------------------------------------------
virtual task c2h_doorbell_error (input out_of_order=0, input unalign_addr=0);
int cur_wc_length;
logic [31:0] cur_wdata_q[$];
logic induce_error = 0;
c2h_total_desc += this.c2h_desc_q.size()/(this.c2h_desc_size/4);
if (this.verbose)
$display($time,,,"sde_dma_t: C2H_DOORBELL Out Of Order: NumDesc=%0d, TotalDesc=0x%x", this.c2h_desc_q.size()/(this.c2h_desc_size/4), c2h_total_desc);
//Make sure have some descriptors to poke
while (this.c2h_desc_q.size()>0)
begin
//Clear out the write data
cur_wdata_q = {};
//Get a random write combine length -- note this works with no write combine as well (just takes a bit longer)
//Supported DW lengths for H2C/C2H descriptors = 1DW, 4DW, 8DW
this.cfg_desc_wc_max = (this.cfg_desc_wc_max > 8) ? 8 : this.cfg_desc_wc_max;
this.cfg_desc_wc_min = (this.cfg_desc_wc_min > 8) ? 8 : this.cfg_desc_wc_min;
if ((this.cfg_desc_wc_min == this.cfg_desc_wc_max) && (this.cfg_desc_wc_min == 1 || this.cfg_desc_wc_min == 4 || this.cfg_desc_wc_min == 8))
cur_wc_length = this.cfg_desc_wc_min;
else
std::randomize(cur_wc_length) with {cur_wc_length inside {1, 4, 8};};
//If amount of data is less than write combine length, use that amount for data length
if (this.c2h_desc_q.size<cur_wc_length)
cur_wc_length = this.c2h_desc_q.size;
//Transfer data from c2h_desc_q to cur_wdata_q;
for (int i=0; i<cur_wc_length; i++)
cur_wdata_q.push_back(this.c2h_desc_q.pop_front());
if (this.verbose)
$display($time,,,"sde_dma_t: C2H_DOORBELL: CurDW=%0d, DWLeft=%0d", cur_wdata_q.size(), c2h_desc_q.size());
if (out_of_order) begin
if (induce_error == 0) begin //{
access.poke(.addr(C2H_DESC_BASE + 'h80), .data(cur_wdata_q), .wc(desc_wc));
induce_error = 1;
end else begin //}{
access.poke(.addr(C2H_DESC_BASE + 'h40), .data(cur_wdata_q), .wc(desc_wc));
end //}
end else if (unalign_addr) begin
if (induce_error == 0) begin //{
access.poke(.addr(C2H_DESC_BASE), .data(cur_wdata_q), .wc(desc_wc));
induce_error = 1;
end else begin //}{
access.poke(.addr(C2H_DESC_BASE + 'h20), .data(cur_wdata_q), .wc(desc_wc));
end //}
end else begin
access.poke(.addr(C2H_DESC_BASE), .data(cur_wdata_q), .wc(desc_wc));
end
end
this.c2h_desc_q = {};
endtask
//-----------------------------------------------------------------------------
// C2H Writeback descriptor processing
//
// - Wait for WB descriptors to be valid
// - Get the corresponding packet data and assemble into a packet buffer
// - Clear WB desciptor so can be re-used by H/W
// - On EOP push onto RX Pkt Q that can be used by scoreboard
//-----------------------------------------------------------------------------
virtual task process_c2h_wb_thread();
gen_buf_t cur_asm_pkt; //Current packet we are assembling
dma_buf_t cur_post_buf; //Current posted buffer
wb_desc_t cur_wb_desc; //Current wb descriptor
bit pkt_inp; //Currently assembling a packet
int num_desc;
string desc_info_q[$]; //Descriptors
logic [63:0] addr;
logic [31:0] wr_data;
logic [31:0] rd_data;
logic error;
int rd_ptr_pend_cnt = 0;
int accum_length;
int num_wb_to_read = 0;
logic [31:0] curr_md_wr_ptr;
pkt_inp = 0;
forever
begin
#(this.wb_poll_interval * 1ns);
num_wb_to_read = 0;
while (num_wb_to_read == 0) begin
curr_md_wr_ptr = access.read_host_dw(cfg_c2h_wb_wr_ptr_addr);
num_wb_to_read = curr_md_wr_ptr >= c2h_wb_desc_rd_ptr ? (curr_md_wr_ptr - c2h_wb_desc_rd_ptr) :
(cfg_c2h_num_md + curr_md_wr_ptr - c2h_wb_desc_rd_ptr);
#1ns;
end
// $display($time,,,"dma_buf_t.process_c2h_wb_thread curr_md_wr_ptr = %0d, c2h_wb_desc_rd_ptr = %0d, num_wb_to_read = %0d", curr_md_wr_ptr, c2h_wb_desc_rd_ptr, num_wb_to_read);
//Get the next descriptor fields
//$display($time,,,"Getting next descriptor. c2h_wb_desc_rd_ptr=0x%0x", c2h_wb_desc_rd_ptr);
repeat (num_wb_to_read) begin
cur_wb_desc = get_nxt_wb_desc(.wb_buf(c2h_wb_buf), .wb_ptr(c2h_wb_desc_rd_ptr));
//$display($time,,,"POLL: valid=0x%x; dw=0x%x", desc_valid, cur_wb_desc[0]);
// while(cur_wb_desc.valid)
// begin
assert(cur_wb_desc.valid == 1'b1) else begin
$display($time,,,"***ERROR*** dma_buf_t.process_c2h_wb_thread Pkt[%0d] cur_wb_desc.valid=0", num_c2h_pkts);
$finish;
end
//If packet is not in progress, create a new buffer to assemble packet
if (!pkt_inp)
begin
cur_asm_pkt = new();
pkt_inp = 1;
accum_length = 0;
end
//Get the next RX buffer that was posted so can extract the packet data
cur_post_buf = c2h_post_buf_q.pop_front();
//Populate the buffer from Host Memory
cur_post_buf.read_host_buf(.length(cur_wb_desc.length));
//Make sure the WB length is <= buffer length
if (cur_wb_desc.length>cur_post_buf.buf_size) begin
$display($time,,,"***ERROR*** dma_buf_t.process_c2h_wb_thread Pkt[%0d] cur_wb_desc.length=0x%0x is greater than posted length cur_post_buf.buf_size=0x%0x", num_c2h_pkts, cur_wb_desc.length, cur_post_buf.buf_size);
$finish;
end
//Copy the post buffer data to the assembled packet data
for (int i=0; i<cur_wb_desc.length; i++)
cur_asm_pkt.data.push_back(cur_post_buf.data[i]);
//Update the Descriptor info string for display
// desc_info_q.push_back(sprintf("DESC: addr=0x%x, buf_length=0x%0x, wb_length=0x%0x, accum_length=0x%0x", cur_post_buf.addr, cur_post_buf.buf_size, cur_wb_desc.length, accum_length));
accum_length += cur_wb_desc.length;
//If EOP, then add the packet to the RX_Q
if (cur_wb_desc.eop)
begin
if (this.verbose)
begin
$display($time,,,"dma_buf_t.process_c2h_wb_thread Host RX_PKT [%0d] length=0x%0x, start_data=0x%x", num_c2h_pkts, cur_asm_pkt.data.size(), {cur_asm_pkt.data[3], cur_asm_pkt.data[2], cur_asm_pkt.data[1], cur_asm_pkt.data[0]});
//foreach (desc_info_q[i])
// $display($time,,," %s", desc_info_q[i]);
end
c2h_pkt_rx_q.push_back(cur_asm_pkt);
c2h_pkt_rq_user_q.push_back(cur_wb_desc.user_bits);
pkt_inp = 0;
desc_info_q = {};
num_c2h_pkts++;
end
//Clear out the WB desc in memory (clear the valid bit)
clr_wb_desc(.wb_buf(c2h_wb_buf), .wb_ptr(c2h_wb_desc_rd_ptr));
//Update Read Pointer
c2h_wb_desc_rd_ptr = (c2h_wb_desc_rd_ptr == (cfg_c2h_num_md-1))? 0: c2h_wb_desc_rd_ptr + 1;
rd_ptr_pend_cnt++;
if (rd_ptr_pend_cnt >= c2h_wb_rdptr_coalesce_cnt) begin
// if (rd_ptr_pend_cnt >= (cfg_c2h_num_md-1)) begin
//Update Read Pointer in HW
addr = c2h_csr_base_addr + 16'h324;
wr_data = c2h_wb_desc_rd_ptr;
access.poke_dw (.addr(addr), .data(wr_data));
// There is a delay for writes to reach the HW// access.peek_verify (.addr(addr), .data(rd_data), .exp_data(wr_data), .error(error), .exit_on_error(1));
rd_ptr_pend_cnt = 0;
c2h_wb_rdptr_coalesce_cnt = $urandom_range((cfg_c2h_num_md-1), 1);
end
//Read the next Descriptor
//cur_wb_desc = get_nxt_wb_desc(.wb_buf(c2h_wb_buf), .wb_ptr(c2h_wb_desc_rd_ptr));
//end
end // repeat (num_wb_to_read)
end // forever begin
endtask
//Get the next WB descriptor
function wb_desc_t get_nxt_wb_desc (dma_buf_t wb_buf, input int wb_ptr);
wb_desc_t wb_desc;
logic [29:0] dummy;
wb_desc = new();
// $display("Calling read_host_buf with length = %0d", c2h_wb_desc_size);
wb_buf.read_host_buf(.start_offset(wb_ptr * c2h_wb_desc_size), .length(c2h_wb_desc_size));
{wb_desc.length[31:0]} = { wb_buf.data[(wb_ptr * c2h_wb_desc_size) + 3],
wb_buf.data[(wb_ptr * c2h_wb_desc_size) + 2],
wb_buf.data[(wb_ptr * c2h_wb_desc_size) + 1],
wb_buf.data[(wb_ptr * c2h_wb_desc_size) + 0] };
{dummy[29:0], wb_desc.eop, wb_desc.valid} = { wb_buf.data[(wb_ptr * c2h_wb_desc_size) + 7],
wb_buf.data[(wb_ptr * c2h_wb_desc_size) + 6],
wb_buf.data[(wb_ptr * c2h_wb_desc_size) + 5],
wb_buf.data[(wb_ptr * c2h_wb_desc_size) + 4] };
if (this.desc_type == 0)
begin
{wb_desc.user_bits[32:0]} = { wb_buf.data[(wb_ptr * c2h_wb_desc_size) + 11],
wb_buf.data[(wb_ptr * c2h_wb_desc_size) + 10],
wb_buf.data[(wb_ptr * c2h_wb_desc_size) + 9],
wb_buf.data[(wb_ptr * c2h_wb_desc_size) + 8] };
{wb_desc.user_bits[63:32]} = { wb_buf.data[(wb_ptr * c2h_wb_desc_size) + 15],
wb_buf.data[(wb_ptr * c2h_wb_desc_size) + 14],
wb_buf.data[(wb_ptr * c2h_wb_desc_size) + 13],
wb_buf.data[(wb_ptr * c2h_wb_desc_size) + 12] };
end // if (this.desc_type == 0)
else begin
wb_desc.user_bits[63:0] = 64'd0;
end
get_nxt_wb_desc = wb_desc;
endfunction
//Clear the WB descriptor
function void clr_wb_desc(dma_buf_t wb_buf, input int wb_ptr);
//Clear the WB descriptor
for (int i=0; i<c2h_wb_desc_size; i++)
begin
wb_buf.data[wb_ptr * c2h_wb_desc_size + i] = 0;
end
//Update host memory
wb_buf.write_buf_host(.start_offset(wb_ptr * c2h_wb_desc_size), .length(c2h_wb_desc_size));
endfunction
//---------------------------------------------------------------------
//Task to continually post C2H descriptors
// - Will keep posting descriptors until reach max_num_desc limit (max number of outstanding descriptors)
// - Randomize the length and offset
// - Randomly ring doorbell (currently fixed at 50% chance, FIXME -- Make this parameterizable)
// - If reached the max number of descriptors outstanding always ring doorbell (otherwise will deadlock)
//---------------------------------------------------------------------
task post_c2h_desc_thread(input int max_num_desc=64);
bit[31:0] c2h_cdt_cons = 0; //C2H descriptor credit consumed
bit[31:0] c2h_cdt_limit = 64; //C2H descriptor credit consumed
bit[31:0] c2h_cdt_limit_old;
bit[31:0] pkt_count;
bit[31:0] desc_count;
bit[31:0] wb_wr_ptr;
bit[31:0] credit_diff;
int tmp_length;
int tmp_offset;
dma_buf_t tmp_buf;
bit[31:0] tmp_rnd;
bit [31:0] c2h_wb_desc_wr_ptr_next;
forever
begin
credit_diff = c2h_cdt_limit - c2h_cdt_cons;
c2h_wb_desc_wr_ptr_next = c2h_wb_desc_wr_ptr == (cfg_c2h_num_md-1) ? 0 : (c2h_wb_desc_wr_ptr + 1);
while ((c2h_cdt_limit==c2h_cdt_cons || ((32'h64 - credit_diff) >= max_num_desc)) ||
(c2h_wb_desc_wr_ptr_next == c2h_wb_desc_rd_ptr))
begin
#(this.wb_poll_interval * 1ns);
c2h_cdt_limit_old = c2h_cdt_limit;
c2h_cdt_limit = access.read_host_dw(cfg_c2h_cdt_limit_addr);
credit_diff = c2h_cdt_limit - c2h_cdt_cons;
if (c2h_cdt_limit_old != c2h_cdt_limit)
begin
pkt_count = access.read_host_dw(cfg_c2h_wb_pkt_cnt_addr);
desc_count = access.read_host_dw(cfg_c2h_wb_desc_cnt_addr);
wb_wr_ptr = access.read_host_dw(cfg_c2h_wb_status_addr);
if (this.verbose)
$display($time,,,"Poll C2H Status: desc_cons=0x%0x, desc_limit=0x%0x, pkt_count=0x%0x, desc_count=0x%0x, wb_wr_ptr=0x%0x", c2h_cdt_cons, c2h_cdt_limit, pkt_count, desc_count, wb_wr_ptr);
end
end
//Randomize length/offset
tmp_length = $urandom_range(pc2h_max_length, pc2h_min_length);
tmp_offset = $urandom_range(pc2h_max_offset, pc2h_min_offset);
//Adjust length if offset + length > page_size
if ((tmp_offset + tmp_length) > page_size)
tmp_length = page_size - tmp_offset;
//Allocate the buffer
tmp_buf = new(.access(this.access), .addr(this.alloc_page(.s("POST_C2H_DESC_THREAD")) + tmp_offset), .buf_size(tmp_length), .verbose(this.verbose));
//Post the Buffer
this.post_c2h_desc(.post_buf(tmp_buf));
//Increment the consumed
c2h_cdt_cons++;
credit_diff = c2h_cdt_limit - c2h_cdt_cons;
//50/50 chance of ringing the doorbell
tmp_rnd = $urandom;
//Always ring doorbell if posted max_num_desc
if (tmp_rnd[0] || ((32'h64 - credit_diff) >= max_num_desc))
this.c2h_doorbell();
end
endtask
endclass