Merge FPGA repository back into UHD repository

The FPGA codebase was removed from the UHD repository in 2014 to reduce
the size of the repository. However, over the last half-decade, the
split between the repositories has proven more burdensome than it has
been helpful. By merging the FPGA code back, it will be possible to
create atomic commits that touch both FPGA and UHD codebases. Continuous
integration testing is also simplified by merging the repositories,
because it was previously difficult to automatically derive the correct
UHD branch when testing a feature branch on the FPGA repository.

This commit also updates the license files and paths therein.

We are therefore merging the repositories again. Future development for
FPGA code will happen in the same repository as the UHD host code and
MPM code.

== Original Codebase and Rebasing ==

The original FPGA repository will be hosted for the foreseeable future
at its original local location: https://github.com/EttusResearch/fpga/

It can be used for bisecting, reference, and a more detailed history.

The final commit from said repository to be merged here is
05003794e2da61cabf64dd278c45685a7abad7ec. This commit is tagged as
v4.0.0.0-pre-uhd-merge.

If you have changes in the FPGA repository that you want to rebase onto
the UHD repository, simply run the following commands:

- Create a directory to store patches (this should be an empty
  directory):

    mkdir ~/patches

- Now make sure that your FPGA codebase is based on the same state as
  the code that was merged:

    cd src/fpga # Or wherever your FPGA code is stored
    git rebase v4.0.0.0-pre-uhd-merge

  Note: The rebase command may look slightly different depending on what
  exactly you're trying to rebase.

- Create a patch set for your changes versus v4.0.0.0-pre-uhd-merge:

    git format-patch v4.0.0.0-pre-uhd-merge -o ~/patches

  Note: Make sure that only patches are stored in your output directory.
  It should otherwise be empty. Make sure that you picked the correct
  range of commits, and only commits you wanted to rebase were exported
  as patch files.

- Go to the UHD repository and apply the patches:

    cd src/uhd # Or wherever your UHD repository is stored
    git am --directory fpga ~/patches/*
    rm -rf ~/patches # This is for cleanup

== Contributors ==

The following people have contributed mainly to these files (this list
is not complete):

Co-authored-by: Alex Williams <alex.williams@ni.com>
Co-authored-by: Andrej Rode <andrej.rode@ettus.com>
Co-authored-by: Ashish Chaudhari <ashish@ettus.com>
Co-authored-by: Ben Hilburn <ben.hilburn@ettus.com>
Co-authored-by: Ciro Nishiguchi <ciro.nishiguchi@ni.com>
Co-authored-by: Daniel Jepson <daniel.jepson@ni.com>
Co-authored-by: Derek Kozel <derek.kozel@ettus.com>
Co-authored-by: EJ Kreinar <ej@he360.com>
Co-authored-by: Humberto Jimenez <humberto.jimenez@ni.com>
Co-authored-by: Ian Buckley <ian.buckley@gmail.com>
Co-authored-by: Jörg Hofrichter <joerg.hofrichter@ni.com>
Co-authored-by: Jon Kiser <jon.kiser@ni.com>
Co-authored-by: Josh Blum <josh@joshknows.com>
Co-authored-by: Jonathon Pendlum <jonathan.pendlum@ettus.com>
Co-authored-by: Martin Braun <martin.braun@ettus.com>
Co-authored-by: Matt Ettus <matt@ettus.com>
Co-authored-by: Michael West <michael.west@ettus.com>
Co-authored-by: Moritz Fischer <moritz.fischer@ettus.com>
Co-authored-by: Nick Foster <nick@ettus.com>
Co-authored-by: Nicolas Cuervo <nicolas.cuervo@ettus.com>
Co-authored-by: Paul Butler <paul.butler@ni.com>
Co-authored-by: Paul David <paul.david@ettus.com>
Co-authored-by: Ryan Marlow <ryan.marlow@ettus.com>
Co-authored-by: Sugandha Gupta <sugandha.gupta@ettus.com>
Co-authored-by: Sylvain Munaut <tnt@246tNt.com>
Co-authored-by: Trung Tran <trung.tran@ettus.com>
Co-authored-by: Vidush Vishwanath <vidush.vishwanath@ettus.com>
Co-authored-by: Wade Fife <wade.fife@ettus.com>


Original-commit: bafa9d95453387814ef25e6b6256ba8db2df612f
This commit is contained in:
Martin Braun
2020-01-28 09:35:36 -08:00
co-authored by Alex Williams Andrej Rode Ashish Chaudhari Ben Hilburn Ciro Nishiguchi Daniel Jepson Derek Kozel EJ Kreinar Humberto Jimenez Ian Buckley Jörg Hofrichter Jon Kiser Josh Blum Jonathon Pendlum Matt Ettus Michael West Moritz Fischer Nick Foster Nicolas Cuervo Paul Butler Paul David Ryan Marlow Sugandha Gupta Sylvain Munaut Trung Tran Vidush Vishwanath Wade Fife
parent 74893643ca
commit 6b67702ad7
2157 changed files with 1282567 additions and 0 deletions
@@ -0,0 +1,45 @@
#
# Copyright 2019 Ettus Research, A National Instruments Company
#
# SPDX-License-Identifier: LGPL-3.0-or-later
#
#-------------------------------------------------
# Top-of-Makefile
#-------------------------------------------------
# Define BASE_DIR to point to the "top" dir
BASE_DIR = $(abspath ../../../../top)
# Include viv_sim_preamble after defining BASE_DIR
include $(BASE_DIR)/../tools/make/viv_sim_preamble.mak
#-------------------------------------------------
# Design Specific
#-------------------------------------------------
# Include makefiles and sources for the DUT and its dependencies
include $(BASE_DIR)/../lib/rfnoc/core/Makefile.srcs
include $(BASE_DIR)/../lib/rfnoc/utils/Makefile.srcs
include Makefile.srcs
DESIGN_SRCS += $(abspath \
$(RFNOC_CORE_SRCS) \
$(RFNOC_UTIL_SRCS) \
$(RFNOC_BLOCK_AXI_RAM_FIFO_SRCS) \
)
#-------------------------------------------------
# Testbench Specific
#-------------------------------------------------
SIM_TOP = rfnoc_block_axi_ram_fifo_all_tb
SIM_SRCS = \
$(abspath sim_axi_ram.sv) \
$(abspath rfnoc_block_axi_ram_fifo_tb.sv) \
$(abspath rfnoc_block_axi_ram_fifo_all_tb.sv)
#-------------------------------------------------
# Bottom-of-Makefile
#-------------------------------------------------
# Include all simulator specific makefiles here
# Each should define a unique target to simulate
# e.g. xsim, vsim, etc and a common "clean" target
include $(BASE_DIR)/../tools/make/viv_simulator.mak
@@ -0,0 +1,18 @@
#
# Copyright 2019 Ettus Research, A National Instruments Company
#
# SPDX-License-Identifier: LGPL-3.0-or-later
#
##################################################
# RFNoC Utility Sources
##################################################
RFNOC_BLOCK_AXI_RAM_FIFO_SRCS = $(abspath $(addprefix $(BASE_DIR)/../lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/, \
noc_shell_axi_ram_fifo.v \
axi_ram_fifo_regs.vh \
axi_ram_fifo_regs.v \
axi_ram_fifo_bist.v \
axi_ram_fifo_bist_regs.v \
axi_ram_fifo.v \
rfnoc_block_axi_ram_fifo.v \
))
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,294 @@
//
// Copyright 2019 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: axi_ram_fifo_bist
//
// Description:
//
// Implements a built-in self test for the RAM FIFO. It can generate random
// or sequential data that it outputs as quickly as possible. The output of
// the RAM is verified to make sure that it matches what was input to the RAM.
//
// Parameters:
//
// DATA_W : The width of the data port to use for the AXI4-Stream interface
//
// COUNT_W : Width of internal counters. This must be wide enough so that
// word, cycle, and and error counters don't overflow during a
// test.
//
// CLK_RATE : The frequency of clk in Hz
//
// RAND : Set to 1 for random data, 0 for sequential data.
//
module axi_ram_fifo_bist #(
parameter DATA_W = 64,
parameter COUNT_W = 48,
parameter CLK_RATE = 200e6,
parameter RAND = 1
) (
input clk,
input rst,
//--------------------------------------------------------------------------
// CTRL Port
//--------------------------------------------------------------------------
input wire s_ctrlport_req_wr,
input wire s_ctrlport_req_rd,
input wire [19:0] s_ctrlport_req_addr,
input wire [31:0] s_ctrlport_req_data,
output wire s_ctrlport_resp_ack,
output wire [31:0] s_ctrlport_resp_data,
//--------------------------------------------------------------------------
// AXI-Stream Interface
//--------------------------------------------------------------------------
// Output to RAM FIFO
output wire [DATA_W-1:0] m_tdata,
output reg m_tvalid,
input wire m_tready,
// Input from RAM FIFO
input wire [DATA_W-1:0] s_tdata,
input wire s_tvalid,
output wire s_tready,
//---------------------------------------------------------------------------
// Status
//---------------------------------------------------------------------------
output reg running
);
//---------------------------------------------------------------------------
// Local Parameters
//---------------------------------------------------------------------------
// Internal word size to use for data generation. The output word will be a
// multiple of this size.
localparam WORD_W = 32;
// Random number seed (must not be 0)
localparam [WORD_W-1:0] SEED = 'h012345678;
// Test data reset value
localparam [WORD_W-1:0] INIT = RAND ? SEED : 0;
//---------------------------------------------------------------------------
// Assertions
//---------------------------------------------------------------------------
if (DATA_W % WORD_W != 0) begin
DATA_W_must_be_a_multiple_of_WORD_W();
end
// LFSR only supports 8, 16, and 32 bits
if (WORD_W != 32 && WORD_W != 16 && WORD_W != 8) begin
WORD_W_not_supported();
end
//---------------------------------------------------------------------------
// Functions
//---------------------------------------------------------------------------
// Linear-feedback Shift Register for random number generation.
function [WORD_W-1:0] lfsr(input [WORD_W-1:0] din);
reg new_bit;
begin
case (WORD_W)
8 : new_bit = din[7] ^ din[5] ^ din[4] ^ din[3];
16 : new_bit = din[15] ^ din[14] ^ din[12] ^ din[3];
32 : new_bit = din[31] ^ din[21] ^ din[1] ^ din[0];
endcase
lfsr = { din[WORD_W-2:0], new_bit };
end
endfunction
function [WORD_W-1:0] next(input [WORD_W-1:0] din);
next = RAND ? lfsr(din) : din + 1;
endfunction
//---------------------------------------------------------------------------
// Signal Declarations
//---------------------------------------------------------------------------
reg [COUNT_W-1:0] tx_count; // Number of words transmitted to FIFO
reg [COUNT_W-1:0] rx_count; // Number of words received back from FIFO
reg [COUNT_W-1:0] error_count; // Number of words that show errors
reg [WORD_W-1:0] tx_data = next(INIT); // Transmitted data word
reg [DATA_W-1:0] rx_data = INIT; // Received data words
reg [WORD_W-1:0] exp_data; // Expected data word
reg rx_valid; // Received word is value (strobe)
wire [COUNT_W-1:0] num_words; // Number of words to test
reg [COUNT_W-1:0] cycle_count; // Number of clock cycles test has been running for
wire start; // Start test
wire stop; // Stop test
wire clear; // Clear the counters
wire continuous; // Continuous test mode
//---------------------------------------------------------------------------
// Registers
//---------------------------------------------------------------------------
axi_ram_fifo_bist_regs #(
.DATA_W (DATA_W),
.COUNT_W (COUNT_W),
.CLK_RATE (CLK_RATE)
) axi_ram_fifo_bist_regs_i (
.clk (clk),
.rst (rst),
.s_ctrlport_req_wr (s_ctrlport_req_wr),
.s_ctrlport_req_rd (s_ctrlport_req_rd),
.s_ctrlport_req_addr (s_ctrlport_req_addr),
.s_ctrlport_req_data (s_ctrlport_req_data),
.s_ctrlport_resp_ack (s_ctrlport_resp_ack),
.s_ctrlport_resp_data (s_ctrlport_resp_data),
.tx_count (tx_count),
.rx_count (rx_count),
.error_count (error_count),
.cycle_count (cycle_count),
.num_words (num_words),
.start (start),
.stop (stop),
.clear (clear),
.continuous (continuous),
.running (running)
);
//---------------------------------------------------------------------------
// State Machine
//---------------------------------------------------------------------------
localparam ST_IDLE = 0;
localparam ST_ACTIVE = 1;
localparam ST_WAIT_DONE = 2;
reg [ 1:0] state;
reg [COUNT_W-1:0] num_words_m1;
always @(posedge clk) begin
if (rst) begin
state <= ST_IDLE;
m_tvalid <= 0;
running <= 0;
end else begin
m_tvalid <= 0;
case (state)
ST_IDLE : begin
num_words_m1 <= num_words-1;
if (start) begin
running <= 1;
state <= ST_ACTIVE;
end
end
ST_ACTIVE : begin
if (stop || (tx_count == num_words_m1 && m_tvalid && m_tready && !continuous)) begin
m_tvalid <= 0;
state <= ST_WAIT_DONE;
end else begin
m_tvalid <= 1;
running <= 1;
end
end
ST_WAIT_DONE : begin
if (rx_count >= tx_count) begin
running <= 0;
state <= ST_IDLE;
end
end
endcase
end
end
//---------------------------------------------------------------------------
// Data Generator
//---------------------------------------------------------------------------
reg count_en;
// Output data is the concatenation of our generated test word.
assign m_tdata = {(DATA_W/WORD_W){ tx_data }};
// We were born ready
assign s_tready = 1;
always @(posedge clk) begin
if (rst) begin
tx_data <= next(INIT);
exp_data <= INIT;
rx_valid <= 0;
tx_count <= 0;
rx_count <= 0;
error_count <= 0;
cycle_count <= 0;
count_en <= 0;
end else begin
//
// Output Data generation
//
if (m_tvalid && m_tready) begin
tx_data <= next(tx_data);
tx_count <= tx_count + 1;
end
//
// Expected Data Generation
//
if (s_tvalid & s_tready) begin
rx_valid <= 1;
exp_data <= next(exp_data);
rx_count <= rx_count + 1;
rx_data <= s_tdata;
end else begin
rx_valid <= 0;
end
//
// Data checker
//
if (rx_valid) begin
if (rx_data !== {(DATA_W/WORD_W){exp_data}}) begin
error_count <= error_count + 1;
end
end
//
// Cycle Counter
//
// Start counting after get the first word back so that we measure
// throughput and not latency.
if (state == ST_IDLE) count_en <= 0;
else if (s_tvalid) count_en <= 1;
if (count_en) cycle_count <= cycle_count + 1;
//
// Clear counters upon request
//
if (clear) begin
tx_count <= 0;
rx_count <= 0;
error_count <= 0;
cycle_count <= 0;
end
end
end
endmodule
@@ -0,0 +1,206 @@
//
// Copyright 2019 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: axi_ram_fifo_bist_regs
//
// Description:
//
// Implements the registers for the RAM FIFO BIST logic.
//
// Parameters:
//
// DATA_W : The width of the data port to use for the AXI4-Stream
// interface.
//
// COUNT_W : Width of internal counters. This must be wide enough so that
// word, cycle, and and error counters don't overflow during a
// test.
//
// CLK_RATE : The frequency of clk in Hz
//
module axi_ram_fifo_bist_regs #(
parameter DATA_W = 64,
parameter COUNT_W = 48,
parameter CLK_RATE = 200e6
) (
input clk,
input rst,
//--------------------------------------------------------------------------
// CTRL Port
//--------------------------------------------------------------------------
input wire s_ctrlport_req_wr,
input wire s_ctrlport_req_rd,
input wire [19:0] s_ctrlport_req_addr,
input wire [31:0] s_ctrlport_req_data,
output reg s_ctrlport_resp_ack,
output reg [31:0] s_ctrlport_resp_data,
//--------------------------------------------------------------------------
// Control and Status
//--------------------------------------------------------------------------
input wire [COUNT_W-1:0] tx_count,
input wire [COUNT_W-1:0] rx_count,
input wire [COUNT_W-1:0] error_count,
input wire [COUNT_W-1:0] cycle_count,
output wire [COUNT_W-1:0] num_words,
output reg start,
output reg stop,
output reg clear,
output reg continuous,
input wire running
);
`include "axi_ram_fifo_regs.vh"
localparam BYTES_PER_WORD = DATA_W/8;
localparam WORD_SHIFT = $clog2(BYTES_PER_WORD);
// Make sure DATA_W is a power of 2, or else the word/byte count conversion
// logic won't be correct.
if (2**$clog2(DATA_W) != DATA_W) begin
DATA_W_must_be_a_power_of_2();
end
// The register logic currently assumes that COUNT_W is at least 33 bits.
if (COUNT_W <= 32) begin
COUNT_W_must_be_larger_than_32();
end
wire [19:0] word_addr;
wire [63:0] tx_byte_count;
wire [63:0] rx_byte_count;
reg [63:0] num_bytes = 0;
reg [31:0] tx_byte_count_hi = 0;
reg [31:0] rx_byte_count_hi = 0;
reg [31:0] error_count_hi = 0;
reg [31:0] cycle_count_hi = 0;
// Only use the word address to simplify address decoding logic
assign word_addr = {s_ctrlport_req_addr[19:2], 2'b00 };
// Convert between words and bytes
assign tx_byte_count = tx_count << WORD_SHIFT;
assign rx_byte_count = rx_count << WORD_SHIFT;
assign num_words = num_bytes >> WORD_SHIFT;
always @(posedge clk) begin
if (rst) begin
s_ctrlport_resp_ack <= 0;
start <= 0;
stop <= 0;
continuous <= 0;
clear <= 0;
num_bytes <= 0;
end else begin
// Default values
s_ctrlport_resp_ack <= 0;
start <= 0;
stop <= 0;
clear <= 0;
//-----------------------------------------------------------------------
// Read Logic
//-----------------------------------------------------------------------
if (s_ctrlport_req_rd) begin
case (word_addr)
REG_BIST_CTRL : begin
s_ctrlport_resp_data <= 0;
s_ctrlport_resp_data[REG_BIST_RUNNING_POS] <= running;
s_ctrlport_resp_data[REG_BIST_CONT_POS] <= continuous;
s_ctrlport_resp_ack <= 1;
end
REG_BIST_CLK_RATE : begin
s_ctrlport_resp_data <= CLK_RATE;
s_ctrlport_resp_ack <= 1;
end
REG_BIST_NUM_BYTES_LO : begin
s_ctrlport_resp_data <= num_bytes[31:0];
s_ctrlport_resp_ack <= 1;
end
REG_BIST_NUM_BYTES_HI : begin
s_ctrlport_resp_data <= num_bytes[63:32];
s_ctrlport_resp_ack <= 1;
end
REG_BIST_TX_BYTE_COUNT_LO : begin
s_ctrlport_resp_data <= tx_byte_count[31:0];
tx_byte_count_hi <= tx_byte_count[63:32];
s_ctrlport_resp_ack <= 1;
end
REG_BIST_TX_BYTE_COUNT_HI : begin
s_ctrlport_resp_data <= tx_byte_count_hi;
s_ctrlport_resp_ack <= 1;
end
REG_BIST_RX_BYTE_COUNT_LO : begin
s_ctrlport_resp_data <= rx_byte_count[31:0];
rx_byte_count_hi[COUNT_W-33:0] <= rx_byte_count[COUNT_W-1:32];
s_ctrlport_resp_ack <= 1;
end
REG_BIST_RX_BYTE_COUNT_HI : begin
s_ctrlport_resp_data <= rx_byte_count_hi;
s_ctrlport_resp_ack <= 1;
end
REG_BIST_ERROR_COUNT_LO : begin
s_ctrlport_resp_data <= error_count[31:0];
error_count_hi[COUNT_W-33:0] <= error_count[COUNT_W-1:32];
s_ctrlport_resp_ack <= 1;
end
REG_BIST_ERROR_COUNT_HI : begin
s_ctrlport_resp_data <= 0;
s_ctrlport_resp_data <= error_count_hi;
s_ctrlport_resp_ack <= 1;
end
REG_BIST_CYCLE_COUNT_LO : begin
s_ctrlport_resp_data <= cycle_count[31:0];
cycle_count_hi[COUNT_W-33:0] <= cycle_count[COUNT_W-1:32];
s_ctrlport_resp_ack <= 1;
end
REG_BIST_CYCLE_COUNT_HI : begin
s_ctrlport_resp_data <= 0;
s_ctrlport_resp_data <= cycle_count_hi;
s_ctrlport_resp_ack <= 1;
end
endcase
end
//-----------------------------------------------------------------------
// Write Logic
//-----------------------------------------------------------------------
if (s_ctrlport_req_wr) begin
case (word_addr)
REG_BIST_CTRL : begin
start <= s_ctrlport_req_data[REG_BIST_START_POS];
stop <= s_ctrlport_req_data[REG_BIST_STOP_POS];
clear <= s_ctrlport_req_data[REG_BIST_CLEAR_POS];
continuous <= s_ctrlport_req_data[REG_BIST_CONT_POS];
s_ctrlport_resp_ack <= 1;
end
REG_BIST_NUM_BYTES_LO : begin
// Update only the word-count portion
num_bytes[31:WORD_SHIFT] <= s_ctrlport_req_data[31:WORD_SHIFT];
s_ctrlport_resp_ack <= 1;
end
REG_BIST_NUM_BYTES_HI : begin
num_bytes[COUNT_W-1:32] <= s_ctrlport_req_data[COUNT_W-33:0];
s_ctrlport_resp_ack <= 1;
end
endcase
end
end
end
endmodule
@@ -0,0 +1,207 @@
//
// Copyright 2019 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: axi_ram_fifo_regs
//
// Description:
//
// Implements the software-accessible registers for the axi_ram_fifo block.
//
module axi_ram_fifo_regs #(
parameter MEM_ADDR_W = 32,
parameter MEM_DATA_W = 64,
parameter [MEM_ADDR_W-1:0] FIFO_ADDR_BASE = 'h0,
parameter [MEM_ADDR_W-1:0] FIFO_ADDR_MASK = 'h0000FFFF,
parameter [MEM_ADDR_W-1:0] FIFO_ADDR_MASK_MIN = 'h00000FFF,
parameter BIST = 1,
parameter IN_FIFO_SIZE = 10,
parameter WORD_ADDR_W = 29,
parameter BURST_TIMEOUT = 128,
parameter TIMEOUT_W = 12
) (
input wire clk,
input wire rst,
//--------------------------------------------------------------------------
// CTRL Port
//--------------------------------------------------------------------------
input wire s_ctrlport_req_wr,
input wire s_ctrlport_req_rd,
input wire [19:0] s_ctrlport_req_addr,
input wire [31:0] s_ctrlport_req_data,
output reg s_ctrlport_resp_ack,
output reg [31:0] s_ctrlport_resp_data,
//---------------------------------------------------------------------------
// Register Inputs and Outputs
//---------------------------------------------------------------------------
// Read-back Registers
input wire [ 31:0] rb_out_pkt_count,
input wire [WORD_ADDR_W:0] rb_occupied,
// Settings Registers
output reg [ 15:0] set_suppress_threshold,
output reg [ TIMEOUT_W-1:0] set_timeout,
output reg [MEM_ADDR_W-1:0] set_fifo_addr_base = FIFO_ADDR_BASE,
output reg [MEM_ADDR_W-1:0] set_fifo_addr_mask = FIFO_ADDR_MASK
);
`include "axi_ram_fifo_regs.vh"
function automatic integer min(input integer a, b);
min = a < b ? a : b;
endfunction
function automatic integer max(input integer a, b);
max = a > b ? a : b;
endfunction
wire [19:0] word_addr;
wire [63:0] reg_fifo_fullness;
reg [31:0] reg_fifo_fullness_hi;
// Only use the word address to simplify address decoding logic
assign word_addr = {s_ctrlport_req_addr[19:2], 2'b00 };
// Convert the "occupied" word count to a 64-bit byte value
assign reg_fifo_fullness = {
{64-MEM_ADDR_W{1'b0}}, // Set unused upper bits to 0
rb_occupied,
{(MEM_ADDR_W-WORD_ADDR_W){1'b0}} // Set byte offset bits to 0
};
always @(posedge clk) begin
if (rst) begin
s_ctrlport_resp_ack <= 0;
set_suppress_threshold <= 0;
set_timeout <= BURST_TIMEOUT;
set_fifo_addr_base <= FIFO_ADDR_BASE;
set_fifo_addr_mask <= FIFO_ADDR_MASK;
end else begin
s_ctrlport_resp_ack <= 0;
//-----------------------------------------------------------------------
// Write Logic
//-----------------------------------------------------------------------
if (s_ctrlport_req_wr) begin
case (word_addr)
REG_FIFO_READ_SUPPRESS : begin
set_suppress_threshold <= s_ctrlport_req_data[REG_FIFO_SUPPRESS_THRESH_POS +: REG_FIFO_SUPPRESS_THRESH_W];
s_ctrlport_resp_ack <= 1;
end
REG_FIFO_TIMEOUT : begin
set_timeout[REG_TIMEOUT_W-1:0] <= s_ctrlport_req_data[REG_TIMEOUT_W-1:0];
s_ctrlport_resp_ack <= 1;
end
REG_FIFO_ADDR_BASE_LO : begin
set_fifo_addr_base[min(32, MEM_ADDR_W)-1:0] <= s_ctrlport_req_data[min(32, MEM_ADDR_W)-1:0];
s_ctrlport_resp_ack <= 1;
end
REG_FIFO_ADDR_BASE_HI : begin
if (MEM_ADDR_W > 32) begin
set_fifo_addr_base[max(32, MEM_ADDR_W-1):32] <= s_ctrlport_req_data[max(0, MEM_ADDR_W-33):0];
end
s_ctrlport_resp_ack <= 1;
end
REG_FIFO_ADDR_MASK_LO : begin
// Coerce the lower bits so we are guaranteed to meet the minimum mask size requirement.
set_fifo_addr_mask[min(32, MEM_ADDR_W)-1:0] <=
s_ctrlport_req_data[min(32, MEM_ADDR_W)-1:0] | FIFO_ADDR_MASK_MIN;
s_ctrlport_resp_ack <= 1;
end
REG_FIFO_ADDR_MASK_HI : begin
if (MEM_ADDR_W > 32) begin
set_fifo_addr_mask[max(32, MEM_ADDR_W-1):32] <= s_ctrlport_req_data[max(0, MEM_ADDR_W-33):0];
end
s_ctrlport_resp_ack <= 1;
end
endcase
end
//-----------------------------------------------------------------------
// Read Logic
//-----------------------------------------------------------------------
if (s_ctrlport_req_rd) begin
case (word_addr)
REG_FIFO_INFO : begin
s_ctrlport_resp_data <= 0;
s_ctrlport_resp_data[REG_FIFO_MAGIC_POS +: REG_FIFO_MAGIC_W] <= 16'hF1F0;
s_ctrlport_resp_data[REG_FIFO_BIST_PRSNT_POS] <= (BIST != 0);
s_ctrlport_resp_ack <= 1;
end
REG_FIFO_READ_SUPPRESS : begin
s_ctrlport_resp_data <= 0;
s_ctrlport_resp_data[REG_FIFO_IN_FIFO_SIZE_POS +: REG_FIFO_IN_FIFO_SIZE_W]
<= IN_FIFO_SIZE;
s_ctrlport_resp_data[REG_FIFO_SUPPRESS_THRESH_POS +: REG_FIFO_SUPPRESS_THRESH_W]
<= set_suppress_threshold;
s_ctrlport_resp_ack <= 1;
end
REG_FIFO_MEM_SIZE : begin
s_ctrlport_resp_data <= 0;
s_ctrlport_resp_data[REG_FIFO_DATA_SIZE_POS +: REG_FIFO_DATA_SIZE_W]
<= MEM_DATA_W;
s_ctrlport_resp_data[REG_FIFO_ADDR_SIZE_POS +: REG_FIFO_ADDR_SIZE_W]
<= MEM_ADDR_W;
s_ctrlport_resp_ack <= 1;
end
REG_FIFO_TIMEOUT : begin
s_ctrlport_resp_data <= 0;
s_ctrlport_resp_data[REG_TIMEOUT_W-1:0] <= set_timeout[REG_TIMEOUT_W-1:0];
s_ctrlport_resp_ack <= 1;
end
REG_FIFO_FULLNESS_LO : begin
s_ctrlport_resp_data <= reg_fifo_fullness[31:0];
reg_fifo_fullness_hi <= reg_fifo_fullness[63:32];
s_ctrlport_resp_ack <= 1;
end
REG_FIFO_FULLNESS_HI : begin
s_ctrlport_resp_data <= reg_fifo_fullness_hi;
s_ctrlport_resp_ack <= 1;
end
REG_FIFO_ADDR_BASE_LO : begin
s_ctrlport_resp_data <= 0;
s_ctrlport_resp_data[min(32, MEM_ADDR_W)-1:0] <= set_fifo_addr_base[min(32, MEM_ADDR_W)-1:0];
s_ctrlport_resp_ack <= 1;
end
REG_FIFO_ADDR_BASE_HI : begin
s_ctrlport_resp_data <= 0;
if (MEM_ADDR_W > 32) begin
s_ctrlport_resp_data[max(0,MEM_ADDR_W-33):0] <= set_fifo_addr_base[max(32, MEM_ADDR_W-1):32];
end
s_ctrlport_resp_ack <= 1;
end
REG_FIFO_ADDR_MASK_LO : begin
s_ctrlport_resp_data <= 0;
s_ctrlport_resp_data[min(32, MEM_ADDR_W)-1:0] <= set_fifo_addr_mask[min(32, MEM_ADDR_W)-1:0];
s_ctrlport_resp_ack <= 1;
end
REG_FIFO_ADDR_MASK_HI : begin
s_ctrlport_resp_data <= 0;
if (MEM_ADDR_W > 32) begin
s_ctrlport_resp_data[max(0, MEM_ADDR_W-33):0] <= set_fifo_addr_mask[max(32, MEM_ADDR_W-1):32];
end
s_ctrlport_resp_ack <= 1;
end
REG_FIFO_PACKET_CNT : begin
s_ctrlport_resp_data <= 0;
s_ctrlport_resp_data <= rb_out_pkt_count;
s_ctrlport_resp_ack <= 1;
end
endcase
end
end
end
endmodule
@@ -0,0 +1,228 @@
//
// Copyright 2019 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: axi_ram_fifo_regs (Header)
//
// Description: Header file for axi_ram_fifo_regs. All registers are 32-bit
// words from software's perspective.
//
// Address space size, per FIFO. That is, each FIFO is separated in the CTRL
// Port address space by 2^FIFO_ADDR_W bytes.
localparam RAM_FIFO_ADDR_W = 7;
// REG_FIFO_INFO (R|W)
//
// Contains info/control bits for the FIFO.
//
// [31:16] : Returns the magic number 0xF1F0 (read-only)
// [0] : Indicates if BIST logic is present (read-only)
//
localparam REG_FIFO_INFO = 'h0;
//
localparam REG_FIFO_MAGIC_POS = 16;
localparam REG_FIFO_BIST_PRSNT_POS = 0;
//
localparam REG_FIFO_MAGIC_W = 16;
// REG_FIFO_READ_SUPPRESS (R|W)
//
// Controls the read suppression threshold. RAM reads will be disabled whenever
// the amount of free space in the input buffer (in units of RAM words) falls
// below this threshold. This is intended to prevent input buffer overflows
// caused by the RAM being too busy with reads. To disable the read suppression
// feature, set the threshold to 0. In general, the threshold should be set to
// a small value relative to the input FIFO buffer size (the IN_FIFO_SIZE
// field) so that it is only enabled when the input FIFO buffer is close to
// overflowing.
//
// [31:16] : Address width of input buffer. In other words, the input buffer is
// 2**REG_FIFO_IN_FIFO_SIZE RAM words deep. (read-only)
// [15: 0] : Read suppression threshold, in RAM words (read/write)
//
localparam REG_FIFO_READ_SUPPRESS = 'h4;
//
localparam REG_FIFO_IN_FIFO_SIZE_POS = 16;
localparam REG_FIFO_SUPPRESS_THRESH_POS = 0;
//
localparam REG_FIFO_IN_FIFO_SIZE_W = 16;
localparam REG_FIFO_SUPPRESS_THRESH_W = 16;
// REG_FIFO_MEM_SIZE (R)
//
// Returns information about the size of the attached memory. The address size
// allows software to determine what mask and base address values are valid.
//
// [31:16] : Returns the bit width of the RAM word size.
// [15: 0] : Returns the bit width of the RAM byte address size. That is, the
// addressable portion of the attached memory is
// 2**REG_FIFO_ADDR_SIZE bytes.
//
localparam REG_FIFO_MEM_SIZE = 'h8;
//
localparam REG_FIFO_DATA_SIZE_POS = 16;
localparam REG_FIFO_ADDR_SIZE_POS = 0;
//
localparam REG_FIFO_DATA_SIZE_W = 16;
localparam REG_FIFO_ADDR_SIZE_W = 16;
// REG_FIFO_TIMEOUT (R/W)
//
// Programs the FIFO timeout, in memory interface clock cycles. For efficiency,
// we want the memory to read and write full bursts. But we also don't want
// smaller amounts of data to be stuck in the FIFO. This timeout determines how
// long we wait for new data before we go ahead and perform a smaller
// read/write. A longer timeout will make more efficient use of the memory, but
// will increase latency. The default value is set by a module parameter.
//
// [31:12] : <Reserved>
// [11: 0] : Timeout
//
localparam REG_FIFO_TIMEOUT = 'hC;
//
localparam REG_TIMEOUT_POS = 0;
localparam REG_TIMEOUT_W = 12;
// REG_FIFO_FULLNESS (R)
//
// Returns the fullness of the FIFO in bytes. This is is a 64-bit register in
// which the least-significant 32-bit word must be read first.
//
localparam REG_FIFO_FULLNESS_LO = 'h10;
localparam REG_FIFO_FULLNESS_HI = 'h14;
// REG_FIFO_ADDR_BASE (R|W)
//
// Sets the base byte address to use for this FIFO. This should only be updated
// when the FIFO is idle. This should be set to a multiple of
// REG_FIFO_ADDR_MASK+1. Depending on the size of the memory connected, upper
// bits might be ignored.
//
localparam REG_FIFO_ADDR_BASE_LO = 'h18;
localparam REG_FIFO_ADDR_BASE_HI = 'h1C;
// REG_FIFO_ADDR_MASK (R|W)
//
// The byte address mask that controls the portion of the memory address that
// is allocated to this FIFO. For example, set to 0xFFFF for a 64 KiB memory.
//
// This should only be updated when the FIFO is idle. It must be equal to a
// power-of-2 minus 1. It should be no smaller than FIFO_ADDR_MASK_MIN, defined
// in axi_ram_fifo.v, otherwise it will be coerced up to that size.
//
// This is is a 64-bit register in which the least-significant 32-bit word must
// be read/written first. Depending on the size of the memory connected, the
// upper bits might be ignored.
//
localparam REG_FIFO_ADDR_MASK_LO = 'h20;
localparam REG_FIFO_ADDR_MASK_HI = 'h24;
// REG_FIFO_PACKET_CNT (R)
//
// Returns the number of packets transferred out of the FIFO block.
//
localparam REG_FIFO_PACKET_CNT = 'h28;
//-----------------------------------------------------------------------------
// BIST Registers
//-----------------------------------------------------------------------------
//
// Only read these registers if the BIST component is included.
//
//-----------------------------------------------------------------------------
// REG_BIST_CTRL (R|W)
//
// Control register for the BIST component.
//
// [4] : BIST is running. Changes to 1 after a test is started, then returns to
// 0 when BIST is complete.
//
// [3] : Continuous mode (run until stopped). When set to 1, test will continue
// to run until Stop bit is set.
//
// [2] : Clear the BIST counters (i.e., the TX, RX, cycle, and error counters)
//
// [1] : Stop BIST (strobe). Write a 1 to this bit to stop the test that is
// currently running
//
// [0] : Start BIST (strobe). Write a 1 to this bit to start a test using the
// configured NUM_BYTES and continuous mode setting.
//
localparam REG_BIST_CTRL = 'h30;
//
localparam REG_BIST_RUNNING_POS = 4;
localparam REG_BIST_CONT_POS = 3;
localparam REG_BIST_CLEAR_POS = 2; // Strobe
localparam REG_BIST_STOP_POS = 1; // Strobe
localparam REG_BIST_START_POS = 0; // Strobe
// REG_BIST_CLOCK_RATE (R)
//
// Reports the clock rate of the BIST component in Hz. This can be used with
// REG_BIST_CYCLE_COUNT to calculate throughput.
//
localparam REG_BIST_CLK_RATE = 'h34;
// REG_BIST_NUM_BYTES (R|W)
//
// Number of bytes to generate for the next BIST run. THis is not used if the
// REG_BIST_CONT_POS bit is set. This register should not be updated while the
// BIST is running.
//
localparam REG_BIST_NUM_BYTES_LO = 'h38;
localparam REG_BIST_NUM_BYTES_HI = 'h3C;
// REG_BIST_TX_BYTE_COUNT (R)
//
// Reports the number of bytes transmitted by the BIST component. This should
// always be read least-significant word first to ensure coherency. Once BIST
// is complete, the TX count will equal the RX count.
//
localparam REG_BIST_TX_BYTE_COUNT_LO = 'h40;
localparam REG_BIST_TX_BYTE_COUNT_HI = 'h44;
// REG_BIST_RX_BYTE_COUNT (R)
//
// Reports the number of bytes received by the BIST component. This should
// always be read least-significant word first to ensure coherency. Once BIST
// is complete, the TX count will equal the RX count.
//
localparam REG_BIST_RX_BYTE_COUNT_LO = 'h48;
localparam REG_BIST_RX_BYTE_COUNT_HI = 'h4C;
// REG_BIST_ERROR_COUNT (R)
//
// Reports the number of words in which the BIST component detected errors.
// This should always be read least-significant word first to ensure coherency.
//
localparam REG_BIST_ERROR_COUNT_LO = 'h50;
localparam REG_BIST_ERROR_COUNT_HI = 'h54;
// REG_BIST_CYCLE_COUNT (R)
//
// Reports the number of clock cycles that have elapsed while the BIST was
// running. This can be used to calculate throughput. This should always be
// read least-significant word first to ensure coherency.
//
localparam REG_BIST_CYCLE_COUNT_LO = 'h58;
localparam REG_BIST_CYCLE_COUNT_HI = 'h5C;
@@ -0,0 +1,319 @@
//
// Copyright 2019 Ettus Research, A National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: noc_shell_axi_ram_fifo
//
// Description: A NoC Shell for the RFNoC AXI RAM FIFO. This NoC Shell
// implements the control port interface but does nothing to the
// data path other than moving it to the requested clock domain.
//
`define MAX(X,Y) ((X) > (Y) ? (X) : (Y))
module noc_shell_axi_ram_fifo #(
parameter [31:0] NOC_ID = 32'h0,
parameter [ 9:0] THIS_PORTID = 10'd0,
parameter CHDR_W = 64,
parameter DATA_W = 64,
parameter [ 5:0] CTRL_FIFO_SIZE = 0,
parameter [ 0:0] CTRLPORT_MST_EN = 1,
parameter [ 0:0] CTRLPORT_SLV_EN = 1,
parameter [ 5:0] NUM_DATA_I = 1,
parameter [ 5:0] NUM_DATA_O = 1,
parameter [ 5:0] MTU = 10,
parameter SYNC_DATA_CLOCKS = 0
) (
//---------------------------------------------------------------------------
// Framework Interface
//---------------------------------------------------------------------------
// RFNoC Framework Clocks and Resets
input wire rfnoc_chdr_clk,
output wire rfnoc_chdr_rst,
input wire rfnoc_ctrl_clk,
output wire rfnoc_ctrl_rst,
// RFNoC Backend Interface
input wire [ 511:0] rfnoc_core_config,
output wire [ 511:0] rfnoc_core_status,
// CHDR Input Ports (from framework)
input wire [(CHDR_W*NUM_DATA_I)-1:0] s_rfnoc_chdr_tdata,
input wire [ NUM_DATA_I-1:0] s_rfnoc_chdr_tlast,
input wire [ NUM_DATA_I-1:0] s_rfnoc_chdr_tvalid,
output wire [ NUM_DATA_I-1:0] s_rfnoc_chdr_tready,
// CHDR Output Ports (to framework)
output wire [(CHDR_W*NUM_DATA_O)-1:0] m_rfnoc_chdr_tdata,
output wire [ NUM_DATA_O-1:0] m_rfnoc_chdr_tlast,
output wire [ NUM_DATA_O-1:0] m_rfnoc_chdr_tvalid,
input wire [ NUM_DATA_O-1:0] m_rfnoc_chdr_tready,
// AXIS-Ctrl Input Port (from framework)
input wire [ 31:0] s_rfnoc_ctrl_tdata,
input wire s_rfnoc_ctrl_tlast,
input wire s_rfnoc_ctrl_tvalid,
output wire s_rfnoc_ctrl_tready,
// AXIS-Ctrl Output Port (to framework)
output wire [ 31:0] m_rfnoc_ctrl_tdata,
output wire m_rfnoc_ctrl_tlast,
output wire m_rfnoc_ctrl_tvalid,
input wire m_rfnoc_ctrl_tready,
//---------------------------------------------------------------------------
// Client Control Port Interface
//---------------------------------------------------------------------------
// Clock
input wire ctrlport_clk,
input wire ctrlport_rst,
// Master
output wire m_ctrlport_req_wr,
output wire m_ctrlport_req_rd,
output wire [19:0] m_ctrlport_req_addr,
output wire [31:0] m_ctrlport_req_data,
output wire [ 3:0] m_ctrlport_req_byte_en,
output wire m_ctrlport_req_has_time,
output wire [63:0] m_ctrlport_req_time,
input wire m_ctrlport_resp_ack,
input wire [ 1:0] m_ctrlport_resp_status,
input wire [31:0] m_ctrlport_resp_data,
// Slave
input wire s_ctrlport_req_wr,
input wire s_ctrlport_req_rd,
input wire [19:0] s_ctrlport_req_addr,
input wire [ 9:0] s_ctrlport_req_portid,
input wire [15:0] s_ctrlport_req_rem_epid,
input wire [ 9:0] s_ctrlport_req_rem_portid,
input wire [31:0] s_ctrlport_req_data,
input wire [ 3:0] s_ctrlport_req_byte_en,
input wire s_ctrlport_req_has_time,
input wire [63:0] s_ctrlport_req_time,
output wire s_ctrlport_resp_ack,
output wire [ 1:0] s_ctrlport_resp_status,
output wire [31:0] s_ctrlport_resp_data,
//---------------------------------------------------------------------------
// Client Data Interface
//---------------------------------------------------------------------------
// Clock
input wire axis_data_clk,
input wire axis_data_rst,
// Output data stream (to user logic)
output wire [ (NUM_DATA_I*DATA_W)-1:0] m_axis_tdata,
output wire [(NUM_DATA_I*`MAX(DATA_W/CHDR_W, 1))-1:0] m_axis_tkeep,
output wire [ NUM_DATA_I-1:0] m_axis_tlast,
output wire [ NUM_DATA_I-1:0] m_axis_tvalid,
input wire [ NUM_DATA_I-1:0] m_axis_tready,
// Input data stream (from user logic)
input wire [ (NUM_DATA_O*DATA_W)-1:0] s_axis_tdata,
input wire [(NUM_DATA_O*`MAX(DATA_W/CHDR_W, 1))-1:0] s_axis_tkeep,
input wire [ NUM_DATA_O-1:0] s_axis_tlast,
input wire [ NUM_DATA_O-1:0] s_axis_tvalid,
output wire [ NUM_DATA_O-1:0] s_axis_tready
);
//---------------------------------------------------------------------------
// Backend Interface
//---------------------------------------------------------------------------
wire data_i_flush_en;
wire [31:0] data_i_flush_timeout;
wire [63:0] data_i_flush_active;
wire [63:0] data_i_flush_done;
wire data_o_flush_en;
wire [31:0] data_o_flush_timeout;
wire [63:0] data_o_flush_active;
wire [63:0] data_o_flush_done;
backend_iface #(
.NOC_ID (NOC_ID),
.NUM_DATA_I (NUM_DATA_I),
.NUM_DATA_O (NUM_DATA_O),
.CTRL_FIFOSIZE (CTRL_FIFO_SIZE),
.MTU (MTU)
) backend_iface_i (
.rfnoc_chdr_clk (rfnoc_chdr_clk),
.rfnoc_ctrl_clk (rfnoc_ctrl_clk),
.rfnoc_core_config (rfnoc_core_config),
.rfnoc_core_status (rfnoc_core_status),
.rfnoc_chdr_rst (rfnoc_chdr_rst),
.rfnoc_ctrl_rst (rfnoc_ctrl_rst),
.data_i_flush_en (data_i_flush_en),
.data_i_flush_timeout (data_i_flush_timeout),
.data_i_flush_active (data_i_flush_active),
.data_i_flush_done (data_i_flush_done),
.data_o_flush_en (data_o_flush_en),
.data_o_flush_timeout (data_o_flush_timeout),
.data_o_flush_active (data_o_flush_active),
.data_o_flush_done (data_o_flush_done)
);
//---------------------------------------------------------------------------
// Control Path
//---------------------------------------------------------------------------
ctrlport_endpoint #(
.THIS_PORTID (THIS_PORTID ),
.SYNC_CLKS (0 ),
.AXIS_CTRL_MST_EN (CTRLPORT_SLV_EN),
.AXIS_CTRL_SLV_EN (CTRLPORT_MST_EN),
.SLAVE_FIFO_SIZE (CTRL_FIFO_SIZE )
) ctrlport_ep_i (
.rfnoc_ctrl_clk (rfnoc_ctrl_clk ),
.rfnoc_ctrl_rst (rfnoc_ctrl_rst ),
.ctrlport_clk (ctrlport_clk ),
.ctrlport_rst (ctrlport_rst ),
.s_rfnoc_ctrl_tdata (s_rfnoc_ctrl_tdata ),
.s_rfnoc_ctrl_tlast (s_rfnoc_ctrl_tlast ),
.s_rfnoc_ctrl_tvalid (s_rfnoc_ctrl_tvalid ),
.s_rfnoc_ctrl_tready (s_rfnoc_ctrl_tready ),
.m_rfnoc_ctrl_tdata (m_rfnoc_ctrl_tdata ),
.m_rfnoc_ctrl_tlast (m_rfnoc_ctrl_tlast ),
.m_rfnoc_ctrl_tvalid (m_rfnoc_ctrl_tvalid ),
.m_rfnoc_ctrl_tready (m_rfnoc_ctrl_tready ),
.m_ctrlport_req_wr (m_ctrlport_req_wr ),
.m_ctrlport_req_rd (m_ctrlport_req_rd ),
.m_ctrlport_req_addr (m_ctrlport_req_addr ),
.m_ctrlport_req_data (m_ctrlport_req_data ),
.m_ctrlport_req_byte_en (m_ctrlport_req_byte_en ),
.m_ctrlport_req_has_time (m_ctrlport_req_has_time ),
.m_ctrlport_req_time (m_ctrlport_req_time ),
.m_ctrlport_resp_ack (m_ctrlport_resp_ack ),
.m_ctrlport_resp_status (m_ctrlport_resp_status ),
.m_ctrlport_resp_data (m_ctrlport_resp_data ),
.s_ctrlport_req_wr (s_ctrlport_req_wr ),
.s_ctrlport_req_rd (s_ctrlport_req_rd ),
.s_ctrlport_req_addr (s_ctrlport_req_addr ),
.s_ctrlport_req_portid (s_ctrlport_req_portid ),
.s_ctrlport_req_rem_epid (s_ctrlport_req_rem_epid ),
.s_ctrlport_req_rem_portid(s_ctrlport_req_rem_portid),
.s_ctrlport_req_data (s_ctrlport_req_data ),
.s_ctrlport_req_byte_en (s_ctrlport_req_byte_en ),
.s_ctrlport_req_has_time (s_ctrlport_req_has_time ),
.s_ctrlport_req_time (s_ctrlport_req_time ),
.s_ctrlport_resp_ack (s_ctrlport_resp_ack ),
.s_ctrlport_resp_status (s_ctrlport_resp_status ),
.s_ctrlport_resp_data (s_ctrlport_resp_data )
);
//---------------------------------------------------------------------------
// Data Path
//---------------------------------------------------------------------------
// Set WORD_W to the smaller of DATA_W and CHDR_W. This will be our common
// word size between the CHDR and user data ports.
localparam WORD_W = DATA_W < CHDR_W ? DATA_W : CHDR_W;
localparam KEEP_W = `MAX(DATA_W/CHDR_W, 1);
genvar i;
for (i = 0; i < NUM_DATA_I; i = i + 1) begin : gen_in
wire [CHDR_W-1:0] temp_in_tdata;
wire temp_in_tlast;
wire temp_in_tvalid;
wire temp_in_tready;
axis_packet_flush #(
.WIDTH (CHDR_W),
.FLUSH_PARTIAL_PKTS (0),
.TIMEOUT_W (32),
.PIPELINE ("IN")
) in_packet_flush_i (
.clk (rfnoc_chdr_clk),
.reset (rfnoc_chdr_rst),
.enable (data_i_flush_en),
.timeout (data_i_flush_timeout),
.flushing (data_i_flush_active[i]),
.done (data_i_flush_done[i]),
.s_axis_tdata (s_rfnoc_chdr_tdata[i*CHDR_W +: CHDR_W]),
.s_axis_tlast (s_rfnoc_chdr_tlast[i]),
.s_axis_tvalid (s_rfnoc_chdr_tvalid[i]),
.s_axis_tready (s_rfnoc_chdr_tready[i]),
.m_axis_tdata (temp_in_tdata),
.m_axis_tlast (temp_in_tlast),
.m_axis_tvalid (temp_in_tvalid),
.m_axis_tready (temp_in_tready)
);
axis_width_conv #(
.WORD_W (WORD_W),
.IN_WORDS (CHDR_W/WORD_W),
.OUT_WORDS (DATA_W/WORD_W),
.SYNC_CLKS (SYNC_DATA_CLOCKS),
.PIPELINE ("NONE")
) in_width_conv_i (
.s_axis_aclk (rfnoc_chdr_clk),
.s_axis_rst (rfnoc_chdr_rst),
.s_axis_tdata (temp_in_tdata),
.s_axis_tkeep ({CHDR_W/WORD_W{1'b1}}),
.s_axis_tlast (temp_in_tlast),
.s_axis_tvalid (temp_in_tvalid),
.s_axis_tready (temp_in_tready),
.m_axis_aclk (axis_data_clk),
.m_axis_rst (axis_data_rst),
.m_axis_tdata (m_axis_tdata[i*DATA_W +: DATA_W]),
.m_axis_tkeep (m_axis_tkeep[i*KEEP_W +: KEEP_W]),
.m_axis_tlast (m_axis_tlast[i]),
.m_axis_tvalid (m_axis_tvalid[i]),
.m_axis_tready (m_axis_tready[i])
);
end
for (i = 0; i < NUM_DATA_O; i = i + 1) begin : gen_out
wire [ CHDR_W-1:0] temp_out_tdata;
wire [CHDR_W/WORD_W-1:0] temp_out_tkeep;
wire temp_out_tlast;
wire temp_out_tvalid;
wire temp_out_tready;
axis_width_conv #(
.WORD_W (WORD_W),
.IN_WORDS (DATA_W/WORD_W),
.OUT_WORDS (CHDR_W/WORD_W),
.SYNC_CLKS (SYNC_DATA_CLOCKS),
.PIPELINE ("NONE")
) out_width_conv_i (
.s_axis_aclk (axis_data_clk),
.s_axis_rst (axis_data_rst),
.s_axis_tdata (s_axis_tdata[i*DATA_W +: DATA_W]),
.s_axis_tkeep (s_axis_tkeep[i*KEEP_W +: KEEP_W]),
.s_axis_tlast (s_axis_tlast[i]),
.s_axis_tvalid (s_axis_tvalid[i]),
.s_axis_tready (s_axis_tready[i]),
.m_axis_aclk (rfnoc_chdr_clk),
.m_axis_rst (rfnoc_chdr_rst),
.m_axis_tdata (temp_out_tdata),
.m_axis_tkeep (),
.m_axis_tlast (temp_out_tlast),
.m_axis_tvalid (temp_out_tvalid),
.m_axis_tready (temp_out_tready)
);
axis_packet_flush #(
.WIDTH (CHDR_W),
.FLUSH_PARTIAL_PKTS (0),
.TIMEOUT_W (32),
.PIPELINE ("OUT")
) out_packet_flush_i (
.clk (rfnoc_chdr_clk),
.reset (rfnoc_chdr_rst),
.enable (data_o_flush_en),
.timeout (data_o_flush_timeout),
.flushing (data_o_flush_active[i]),
.done (data_o_flush_done[i]),
.s_axis_tdata (temp_out_tdata),
.s_axis_tlast (temp_out_tlast),
.s_axis_tvalid (temp_out_tvalid),
.s_axis_tready (temp_out_tready),
.m_axis_tdata (m_rfnoc_chdr_tdata[i*CHDR_W +: CHDR_W]),
.m_axis_tlast (m_rfnoc_chdr_tlast[i]),
.m_axis_tvalid (m_rfnoc_chdr_tvalid[i]),
.m_axis_tready (m_rfnoc_chdr_tready[i])
);
end
endmodule
@@ -0,0 +1,485 @@
//
// Copyright 2019 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: rfnoc_block_axi_ram_fifo
//
// Description:
//
// Implements a FIFO using an AXI memory-mapped interface to an external
// memory.
//
// Parameters:
//
// THIS_PORTID : Control crossbar port to which this block is connected
//
// CHDR_W : CHDR AXI-Stream data bus width
//
// NUM_PORTS : Number of independent FIFOs to support, all sharing the
// same memory.
//
// MTU : Maximum transfer unit (maximum packet size) to support,
// in CHDR_W-sized words.
//
// MEM_DATA_W : Width of the data bus to use for the AXI memory-mapped
// interface. This must be no bigger than CHDR_W and it must
// evenly divide CHDR_W.
//
// MEM_ADDR_W : Width of the byte address to use for RAM addressing. This
// effectively sets the maximum combined size of all FIFOs.
// This must be less than or equal to AWIDTH.
//
// AWIDTH : Width of the address bus for the AXI memory-mapped
// interface. This must be at least as big as MEM_DATA_W.
//
// FIFO_ADDR_BASE : Default base byte address of each FIFO. When NUM_PORTS >
// 1, this should be the concatenation of all the FIFO base
// addresses. These values can be reconfigured by software.
//
// FIFO_ADDR_MASK : Default byte address mask used by each FIFO. It must be
// all ones. The size of the FIFO in bytes will be this
// minus one. These values can be reconfigured by software.
//
// BURST_TIMEOUT : Default number of memory clock cycles to wait for new
// data before performing a short, sub-optimal burst. One
// value per FIFO.
//
// IN_FIFO_SIZE : Size of the input buffer. This is used to mitigate the
// effects of memory write latency, which can be significant
// when the external memory is DRAM.
//
// OUT_FIFO_SIZE : Size of the output buffer. This is used to mitigate the
// effects of memory read latency, which can be significant
// when the external memory is DRAM.
//
// BIST : Includes BIST logic when true.
//
// MEM_CLK_RATE : Frequency of mem_clk in Hz. This is used by BIST for
// throughput calculation.
//
module rfnoc_block_axi_ram_fifo #(
parameter THIS_PORTID = 0,
parameter CHDR_W = 64,
parameter NUM_PORTS = 1,
parameter MTU = 10,
parameter MEM_DATA_W = CHDR_W,
parameter MEM_ADDR_W = 32,
parameter AWIDTH = 32,
parameter [NUM_PORTS*MEM_ADDR_W-1:0] FIFO_ADDR_BASE = {NUM_PORTS{ {MEM_ADDR_W{1'b0}} }},
parameter [NUM_PORTS*MEM_ADDR_W-1:0] FIFO_ADDR_MASK = {NUM_PORTS{ {(MEM_ADDR_W-$clog2(NUM_PORTS)){1'b1}} }},
parameter [ NUM_PORTS*32-1:0] BURST_TIMEOUT = {NUM_PORTS{ 32'd256 }},
parameter IN_FIFO_SIZE = 11,
parameter OUT_FIFO_SIZE = 11,
parameter BIST = 1,
parameter MEM_CLK_RATE = 200e6
) (
//---------------------------------------------------------------------------
// AXIS CHDR Port
//---------------------------------------------------------------------------
input wire rfnoc_chdr_clk,
// CHDR inputs from framework
input wire [NUM_PORTS*CHDR_W-1:0] s_rfnoc_chdr_tdata,
input wire [ NUM_PORTS-1:0] s_rfnoc_chdr_tlast,
input wire [ NUM_PORTS-1:0] s_rfnoc_chdr_tvalid,
output wire [ NUM_PORTS-1:0] s_rfnoc_chdr_tready,
// CHDR outputs to framework
output wire [NUM_PORTS*CHDR_W-1:0] m_rfnoc_chdr_tdata,
output wire [ NUM_PORTS-1:0] m_rfnoc_chdr_tlast,
output wire [ NUM_PORTS-1:0] m_rfnoc_chdr_tvalid,
input wire [ NUM_PORTS-1:0] m_rfnoc_chdr_tready,
// Backend interface
input wire [511:0] rfnoc_core_config,
output wire [511:0] rfnoc_core_status,
//---------------------------------------------------------------------------
// AXIS CTRL Port
//---------------------------------------------------------------------------
input wire rfnoc_ctrl_clk,
// CTRL port requests from framework
input wire [31:0] s_rfnoc_ctrl_tdata,
input wire s_rfnoc_ctrl_tlast,
input wire s_rfnoc_ctrl_tvalid,
output wire s_rfnoc_ctrl_tready,
// CTRL port requests to framework
output wire [31:0] m_rfnoc_ctrl_tdata,
output wire m_rfnoc_ctrl_tlast,
output wire m_rfnoc_ctrl_tvalid,
input wire m_rfnoc_ctrl_tready,
//---------------------------------------------------------------------------
// AXI Memory Mapped Interface
//---------------------------------------------------------------------------
// AXI Interface Clock and Reset
input wire mem_clk,
input wire axi_rst,
// AXI Write Address Channel
output wire [ NUM_PORTS*1-1:0] m_axi_awid, // Write address ID. This signal is the identification tag for the write address signals
output wire [ NUM_PORTS*AWIDTH-1:0] m_axi_awaddr, // Write address. The write address gives the address of the first transfer in a write burst
output wire [ NUM_PORTS*8-1:0] m_axi_awlen, // Burst length. The burst length gives the exact number of transfers in a burst.
output wire [ NUM_PORTS*3-1:0] m_axi_awsize, // Burst size. This signal indicates the size of each transfer in the burst.
output wire [ NUM_PORTS*2-1:0] m_axi_awburst, // Burst type. The burst type and the size information, determine how the address is calculated
output wire [ NUM_PORTS*1-1:0] m_axi_awlock, // Lock type. Provides additional information about the atomic characteristics of the transfer.
output wire [ NUM_PORTS*4-1:0] m_axi_awcache, // Memory type. This signal indicates how transactions are required to progress
output wire [ NUM_PORTS*3-1:0] m_axi_awprot, // Protection type. This signal indicates the privilege and security level of the transaction
output wire [ NUM_PORTS*4-1:0] m_axi_awqos, // Quality of Service, QoS. The QoS identifier sent for each write transaction
output wire [ NUM_PORTS*4-1:0] m_axi_awregion, // Region identifier. Permits a single physical interface on a slave to be re-used.
output wire [ NUM_PORTS*1-1:0] m_axi_awuser, // User signal. Optional User-defined signal in the write address channel.
output wire [ NUM_PORTS*1-1:0] m_axi_awvalid, // Write address valid. This signal indicates that the channel is signaling valid write addr
input wire [ NUM_PORTS*1-1:0] m_axi_awready, // Write address ready. This signal indicates that the slave is ready to accept an address
// AXI Write Data Channel
output wire [ NUM_PORTS*MEM_DATA_W-1:0] m_axi_wdata, // Write data
output wire [NUM_PORTS*MEM_DATA_W/8-1:0] m_axi_wstrb, // Write strobes. This signal indicates which byte lanes hold valid data.
output wire [ NUM_PORTS*1-1:0] m_axi_wlast, // Write last. This signal indicates the last transfer in a write burst
output wire [ NUM_PORTS*1-1:0] m_axi_wuser, // User signal. Optional User-defined signal in the write data channel.
output wire [ NUM_PORTS*1-1:0] m_axi_wvalid, // Write valid. This signal indicates that valid write data and strobes are available.
input wire [ NUM_PORTS*1-1:0] m_axi_wready, // Write ready. This signal indicates that the slave can accept the write data.
// AXI Write Response Channel
input wire [ NUM_PORTS*1-1:0] m_axi_bid, // Response ID tag. This signal is the ID tag of the write response.
input wire [ NUM_PORTS*2-1:0] m_axi_bresp, // Write response. This signal indicates the status of the write transaction.
input wire [ NUM_PORTS*1-1:0] m_axi_buser, // User signal. Optional User-defined signal in the write response channel.
input wire [ NUM_PORTS*1-1:0] m_axi_bvalid, // Write response valid. This signal indicates that the channel is signaling a valid response
output wire [ NUM_PORTS*1-1:0] m_axi_bready, // Response ready. This signal indicates that the master can accept a write response
// AXI Read Address Channel
output wire [ NUM_PORTS*1-1:0] m_axi_arid, // Read address ID. This signal is the identification tag for the read address group of signals
output wire [ NUM_PORTS*AWIDTH-1:0] m_axi_araddr, // Read address. The read address gives the address of the first transfer in a read burst
output wire [ NUM_PORTS*8-1:0] m_axi_arlen, // Burst length. This signal indicates the exact number of transfers in a burst.
output wire [ NUM_PORTS*3-1:0] m_axi_arsize, // Burst size. This signal indicates the size of each transfer in the burst.
output wire [ NUM_PORTS*2-1:0] m_axi_arburst, // Burst type. The burst type and the size information determine how the address for each transfer
output wire [ NUM_PORTS*1-1:0] m_axi_arlock, // Lock type. This signal provides additional information about the atomic characteristics
output wire [ NUM_PORTS*4-1:0] m_axi_arcache, // Memory type. This signal indicates how transactions are required to progress
output wire [ NUM_PORTS*3-1:0] m_axi_arprot, // Protection type. This signal indicates the privilege and security level of the transaction
output wire [ NUM_PORTS*4-1:0] m_axi_arqos, // Quality of Service, QoS. QoS identifier sent for each read transaction.
output wire [ NUM_PORTS*4-1:0] m_axi_arregion, // Region identifier. Permits a single physical interface on a slave to be re-used
output wire [ NUM_PORTS*1-1:0] m_axi_aruser, // User signal. Optional User-defined signal in the read address channel.
output wire [ NUM_PORTS*1-1:0] m_axi_arvalid, // Read address valid. This signal indicates that the channel is signaling valid read addr
input wire [ NUM_PORTS*1-1:0] m_axi_arready, // Read address ready. This signal indicates that the slave is ready to accept an address
// AXI Read Data Channel
input wire [ NUM_PORTS*1-1:0] m_axi_rid, // Read ID tag. This signal is the identification tag for the read data group of signals
input wire [NUM_PORTS*MEM_DATA_W-1:0] m_axi_rdata, // Read data.
input wire [ NUM_PORTS*2-1:0] m_axi_rresp, // Read response. This signal indicates the status of the read transfer
input wire [ NUM_PORTS*1-1:0] m_axi_rlast, // Read last. This signal indicates the last transfer in a read burst.
input wire [ NUM_PORTS*1-1:0] m_axi_ruser, // User signal. Optional User-defined signal in the read data channel.
input wire [ NUM_PORTS*1-1:0] m_axi_rvalid, // Read valid. This signal indicates that the channel is signaling the required read data.
output wire [ NUM_PORTS*1-1:0] m_axi_rready // Read ready. This signal indicates that the master can accept the read data and response
);
`include "axi_ram_fifo_regs.vh"
localparam NOC_ID = 'hF1F0_0000;
// If the memory width is larger than the CHDR width, then we need to use
// tkeep to track which CHDR words are valid as they go through the FIFO.
// Calculate the TKEEP width here. Set to 1 if it's not needed.
localparam KEEP_W = (MEM_DATA_W/CHDR_W) > 1 ? (MEM_DATA_W/CHDR_W) : 1;
//---------------------------------------------------------------------------
// Parameter Checks
//---------------------------------------------------------------------------
if (CHDR_W % MEM_DATA_W != 0 && MEM_DATA_W % CHDR_W != 0)
CHDR_W_must_be_a_multiple_of_MEM_DATA_W_or_vice_versa();
if (MEM_ADDR_W > AWIDTH)
MEM_ADDR_W_must_be_greater_than_AWIDTH();
//---------------------------------------------------------------------------
// NoC Shell
//---------------------------------------------------------------------------
wire rfnoc_chdr_rst;
wire ctrlport_req_wr;
wire ctrlport_req_rd;
wire [19:0] ctrlport_req_addr;
wire [31:0] ctrlport_req_data;
wire ctrlport_resp_ack;
wire [31:0] ctrlport_resp_data;
wire [NUM_PORTS*MEM_DATA_W-1:0] m_axis_data_tdata;
wire [ NUM_PORTS*KEEP_W-1:0] m_axis_data_tkeep;
wire [ NUM_PORTS-1:0] m_axis_data_tlast;
wire [ NUM_PORTS-1:0] m_axis_data_tvalid;
wire [ NUM_PORTS-1:0] m_axis_data_tready;
wire [NUM_PORTS*MEM_DATA_W-1:0] s_axis_data_tdata;
wire [ NUM_PORTS*KEEP_W-1:0] s_axis_data_tkeep;
wire [ NUM_PORTS-1:0] s_axis_data_tlast;
wire [ NUM_PORTS-1:0] s_axis_data_tvalid;
wire [ NUM_PORTS-1:0] s_axis_data_tready;
noc_shell_axi_ram_fifo #(
.NOC_ID (NOC_ID),
.THIS_PORTID (THIS_PORTID),
.CHDR_W (CHDR_W),
.DATA_W (MEM_DATA_W),
.CTRL_FIFO_SIZE (5),
.CTRLPORT_MST_EN (1),
.CTRLPORT_SLV_EN (0),
.NUM_DATA_I (NUM_PORTS),
.NUM_DATA_O (NUM_PORTS),
.MTU (MTU),
.SYNC_DATA_CLOCKS (0)
) noc_shell_axi_ram_fifo_i (
.rfnoc_chdr_clk (rfnoc_chdr_clk),
.rfnoc_chdr_rst (rfnoc_chdr_rst),
.rfnoc_ctrl_clk (rfnoc_ctrl_clk),
.rfnoc_ctrl_rst (),
.rfnoc_core_config (rfnoc_core_config),
.rfnoc_core_status (rfnoc_core_status),
.s_rfnoc_chdr_tdata (s_rfnoc_chdr_tdata),
.s_rfnoc_chdr_tlast (s_rfnoc_chdr_tlast),
.s_rfnoc_chdr_tvalid (s_rfnoc_chdr_tvalid),
.s_rfnoc_chdr_tready (s_rfnoc_chdr_tready),
.m_rfnoc_chdr_tdata (m_rfnoc_chdr_tdata),
.m_rfnoc_chdr_tlast (m_rfnoc_chdr_tlast),
.m_rfnoc_chdr_tvalid (m_rfnoc_chdr_tvalid),
.m_rfnoc_chdr_tready (m_rfnoc_chdr_tready),
.s_rfnoc_ctrl_tdata (s_rfnoc_ctrl_tdata),
.s_rfnoc_ctrl_tlast (s_rfnoc_ctrl_tlast),
.s_rfnoc_ctrl_tvalid (s_rfnoc_ctrl_tvalid),
.s_rfnoc_ctrl_tready (s_rfnoc_ctrl_tready),
.m_rfnoc_ctrl_tdata (m_rfnoc_ctrl_tdata),
.m_rfnoc_ctrl_tlast (m_rfnoc_ctrl_tlast),
.m_rfnoc_ctrl_tvalid (m_rfnoc_ctrl_tvalid),
.m_rfnoc_ctrl_tready (m_rfnoc_ctrl_tready),
.ctrlport_clk (mem_clk),
.ctrlport_rst (axi_rst),
.m_ctrlport_req_wr (ctrlport_req_wr),
.m_ctrlport_req_rd (ctrlport_req_rd),
.m_ctrlport_req_addr (ctrlport_req_addr),
.m_ctrlport_req_data (ctrlport_req_data),
.m_ctrlport_req_byte_en (),
.m_ctrlport_req_has_time (),
.m_ctrlport_req_time (),
.m_ctrlport_resp_ack (ctrlport_resp_ack),
.m_ctrlport_resp_status (2'b0),
.m_ctrlport_resp_data (ctrlport_resp_data),
.s_ctrlport_req_wr (1'b0),
.s_ctrlport_req_rd (1'b0),
.s_ctrlport_req_addr (20'b0),
.s_ctrlport_req_portid (10'b0),
.s_ctrlport_req_rem_epid (16'b0),
.s_ctrlport_req_rem_portid (10'b0),
.s_ctrlport_req_data (32'b0),
.s_ctrlport_req_byte_en (4'b0),
.s_ctrlport_req_has_time (1'b0),
.s_ctrlport_req_time (64'b0),
.s_ctrlport_resp_ack (),
.s_ctrlport_resp_status (),
.s_ctrlport_resp_data (),
.axis_data_clk (mem_clk),
.axis_data_rst (axi_rst),
.m_axis_tdata (m_axis_data_tdata),
.m_axis_tkeep (m_axis_data_tkeep),
.m_axis_tlast (m_axis_data_tlast),
.m_axis_tvalid (m_axis_data_tvalid),
.m_axis_tready (m_axis_data_tready),
.s_axis_tdata (s_axis_data_tdata),
.s_axis_tkeep (s_axis_data_tkeep),
.s_axis_tlast (s_axis_data_tlast),
.s_axis_tvalid (s_axis_data_tvalid),
.s_axis_tready (s_axis_data_tready)
);
wire rfnoc_chdr_rst_mem_clk;
reg mem_rst_block;
// Cross the CHDR reset to the mem_clk domain
pulse_synchronizer #(
.MODE ("POSEDGE")
) ctrl_rst_sync_i (
.clk_a (rfnoc_chdr_clk),
.rst_a (1'b0),
.pulse_a (rfnoc_chdr_rst),
.busy_a (),
.clk_b (mem_clk),
.pulse_b (rfnoc_chdr_rst_mem_clk)
);
// Combine the resets in a glitch-free manner
always @(posedge mem_clk) begin
mem_rst_block <= axi_rst | rfnoc_chdr_rst_mem_clk;
end
//---------------------------------------------------------------------------
// CTRL Port Splitter
//---------------------------------------------------------------------------
wire [ NUM_PORTS-1:0] m_ctrlport_req_wr;
wire [ NUM_PORTS-1:0] m_ctrlport_req_rd;
wire [20*NUM_PORTS-1:0] m_ctrlport_req_addr;
wire [32*NUM_PORTS-1:0] m_ctrlport_req_data;
wire [ NUM_PORTS-1:0] m_ctrlport_resp_ack;
wire [32*NUM_PORTS-1:0] m_ctrlport_resp_data;
ctrlport_decoder #(
.NUM_SLAVES (NUM_PORTS),
.BASE_ADDR (0),
.SLAVE_ADDR_W (RAM_FIFO_ADDR_W)
) ctrlport_splitter_i (
.ctrlport_clk (mem_clk),
.ctrlport_rst (mem_rst_block),
.s_ctrlport_req_wr (ctrlport_req_wr),
.s_ctrlport_req_rd (ctrlport_req_rd),
.s_ctrlport_req_addr (ctrlport_req_addr),
.s_ctrlport_req_data (ctrlport_req_data),
.s_ctrlport_req_byte_en (4'b1111),
.s_ctrlport_req_has_time (1'b0),
.s_ctrlport_req_time (64'b0),
.s_ctrlport_resp_ack (ctrlport_resp_ack),
.s_ctrlport_resp_status (),
.s_ctrlport_resp_data (ctrlport_resp_data),
.m_ctrlport_req_wr (m_ctrlport_req_wr),
.m_ctrlport_req_rd (m_ctrlport_req_rd),
.m_ctrlport_req_addr (m_ctrlport_req_addr),
.m_ctrlport_req_data (m_ctrlport_req_data),
.m_ctrlport_req_byte_en (),
.m_ctrlport_req_has_time (),
.m_ctrlport_req_time (),
.m_ctrlport_resp_ack (m_ctrlport_resp_ack),
.m_ctrlport_resp_status ({NUM_PORTS*2{1'b0}}),
.m_ctrlport_resp_data (m_ctrlport_resp_data)
);
//---------------------------------------------------------------------------
// FIFO Instances
//---------------------------------------------------------------------------
genvar i;
for (i = 0; i < NUM_PORTS; i = i + 1) begin : gen_ram_fifos
wire [MEM_ADDR_W-1:0] m_axi_awaddr_int;
wire [MEM_ADDR_W-1:0] m_axi_araddr_int;
// Resize the addresses from MEM_ADDR_W to AWIDTH
assign m_axi_awaddr[(AWIDTH*(i+1))-1:AWIDTH*i] = m_axi_awaddr_int;
assign m_axi_araddr[(AWIDTH*(i+1))-1:AWIDTH*i] = m_axi_araddr_int;
axi_ram_fifo #(
.MEM_ADDR_W (MEM_ADDR_W),
.MEM_DATA_W (MEM_DATA_W),
.KEEP_W (KEEP_W),
.FIFO_ADDR_BASE (FIFO_ADDR_BASE[MEM_ADDR_W*i +: MEM_ADDR_W]),
.FIFO_ADDR_MASK (FIFO_ADDR_MASK[MEM_ADDR_W*i +: MEM_ADDR_W]),
.BURST_TIMEOUT (BURST_TIMEOUT[32*i +: 32]),
.BIST (BIST),
.CLK_RATE (MEM_CLK_RATE),
.IN_FIFO_SIZE (IN_FIFO_SIZE),
.OUT_FIFO_SIZE (OUT_FIFO_SIZE)
) axi_ram_fifo_i (
.clk(mem_clk),
.rst(mem_rst_block),
//-----------------------------------------------------------------------
// Control Port
//-----------------------------------------------------------------------
.s_ctrlport_req_wr (m_ctrlport_req_wr[i]),
.s_ctrlport_req_rd (m_ctrlport_req_rd[i]),
.s_ctrlport_req_addr (m_ctrlport_req_addr[20*i +: 20]),
.s_ctrlport_req_data (m_ctrlport_req_data[32*i +: 32]),
.s_ctrlport_resp_ack (m_ctrlport_resp_ack[i]),
.s_ctrlport_resp_data (m_ctrlport_resp_data[32*i +: 32]),
//-----------------------------------------------------------------------
// AXI-Stream FIFO Interface
//-----------------------------------------------------------------------
// AXI-Stream Input
.s_tdata (m_axis_data_tdata[MEM_DATA_W*i +: MEM_DATA_W]),
.s_tkeep (m_axis_data_tkeep[KEEP_W*i +: KEEP_W]),
.s_tlast (m_axis_data_tlast[i]),
.s_tvalid (m_axis_data_tvalid[i]),
.s_tready (m_axis_data_tready[i]),
//
// AXI-Stream Output
.m_tdata (s_axis_data_tdata[MEM_DATA_W*i +: MEM_DATA_W]),
.m_tkeep (s_axis_data_tkeep[KEEP_W*i +: KEEP_W]),
.m_tlast (s_axis_data_tlast[i]),
.m_tvalid (s_axis_data_tvalid[i]),
.m_tready (s_axis_data_tready[i]),
//-----------------------------------------------------------------------
// AXI4 Memory Interface
//-----------------------------------------------------------------------
// AXI Write address channel
.m_axi_awid (m_axi_awid[i]),
.m_axi_awaddr (m_axi_awaddr_int),
.m_axi_awlen (m_axi_awlen[(8*(i+1))-1:8*i]),
.m_axi_awsize (m_axi_awsize[(3*(i+1))-1:3*i]),
.m_axi_awburst (m_axi_awburst[(2*(i+1))-1:2*i]),
.m_axi_awlock (m_axi_awlock[i]),
.m_axi_awcache (m_axi_awcache[(4*(i+1))-1:4*i]),
.m_axi_awprot (m_axi_awprot[(3*(i+1))-1:3*i]),
.m_axi_awqos (m_axi_awqos[(4*(i+1))-1:4*i]),
.m_axi_awregion (m_axi_awregion[(4*(i+1))-1:4*i]),
.m_axi_awuser (m_axi_awuser[i]),
.m_axi_awvalid (m_axi_awvalid[i]),
.m_axi_awready (m_axi_awready[i]),
//
// AXI Write data channel.
.m_axi_wdata (m_axi_wdata[(MEM_DATA_W*(i+1))-1:MEM_DATA_W*i]),
.m_axi_wstrb (m_axi_wstrb[((MEM_DATA_W/8)*(i+1))-1:(MEM_DATA_W/8)*i]),
.m_axi_wlast (m_axi_wlast[i]),
.m_axi_wuser (m_axi_wuser[i]),
.m_axi_wvalid (m_axi_wvalid[i]),
.m_axi_wready (m_axi_wready[i]),
//
// AXI Write response channel signals
.m_axi_bid (m_axi_bid[i]),
.m_axi_bresp (m_axi_bresp[(2*(i+1))-1:2*i]),
.m_axi_buser (m_axi_buser[i]),
.m_axi_bvalid (m_axi_bvalid[i]),
.m_axi_bready (m_axi_bready[i]),
//
// AXI Read address channel
.m_axi_arid (m_axi_arid[i]),
.m_axi_araddr (m_axi_araddr_int),
.m_axi_arlen (m_axi_arlen[(8*(i+1))-1:8*i]),
.m_axi_arsize (m_axi_arsize[(3*(i+1))-1:3*i]),
.m_axi_arburst (m_axi_arburst[(2*(i+1))-1:2*i]),
.m_axi_arlock (m_axi_arlock[i]),
.m_axi_arcache (m_axi_arcache[(4*(i+1))-1:4*i]),
.m_axi_arprot (m_axi_arprot[(3*(i+1))-1:3*i]),
.m_axi_arqos (m_axi_arqos[(4*(i+1))-1:4*i]),
.m_axi_arregion (m_axi_arregion[(4*(i+1))-1:4*i]),
.m_axi_aruser (m_axi_aruser[i]),
.m_axi_arvalid (m_axi_arvalid[i]),
.m_axi_arready (m_axi_arready[i]),
//
// AXI Read data channel
.m_axi_rid (m_axi_rid[i]),
.m_axi_rdata (m_axi_rdata[(MEM_DATA_W*(i+1))-1:MEM_DATA_W*i]),
.m_axi_rresp (m_axi_rresp[(2*(i+1))-1:2*i]),
.m_axi_rlast (m_axi_rlast[i]),
.m_axi_ruser (m_axi_ruser[i]),
.m_axi_rvalid (m_axi_rvalid[i]),
.m_axi_rready (m_axi_rready[i])
);
end
endmodule
@@ -0,0 +1,70 @@
//
// Copyright 2019 Ettus Research, A National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: rfnoc_block_axi_ram_fifo_all_tb
//
// Description:
//
// This is the testbench for rfnoc_block_axi_ram_fifo that instantiates
// several variations of rfnoc_block_axi_ram_fifo_tb to test different
// configurations.
//
module rfnoc_block_axi_ram_fifo_all_tb;
timeunit 1ns;
timeprecision 1ps;
import PkgTestExec::*;
//---------------------------------------------------------------------------
// Test Definitions
//---------------------------------------------------------------------------
typedef struct {
int CHDR_W;
int NUM_PORTS;
int MEM_DATA_W;
int MEM_ADDR_W;
int FIFO_ADDR_W;
int IN_FIFO_SIZE;
int OUT_FIFO_SIZE;
bit OVERFLOW;
bit BIST;
} test_config_t;
localparam NUM_TESTS = 4;
localparam test_config_t test[NUM_TESTS] = '{
'{CHDR_W: 64, NUM_PORTS: 2, MEM_DATA_W: 64, MEM_ADDR_W: 13, FIFO_ADDR_W: 12, IN_FIFO_SIZE: 9, OUT_FIFO_SIZE: 9, OVERFLOW: 1, BIST: 1 },
'{CHDR_W: 64, NUM_PORTS: 1, MEM_DATA_W: 128, MEM_ADDR_W: 14, FIFO_ADDR_W: 13, IN_FIFO_SIZE: 9, OUT_FIFO_SIZE: 9, OVERFLOW: 1, BIST: 1 },
'{CHDR_W: 128, NUM_PORTS: 1, MEM_DATA_W: 64, MEM_ADDR_W: 13, FIFO_ADDR_W: 12, IN_FIFO_SIZE: 9, OUT_FIFO_SIZE: 10, OVERFLOW: 0, BIST: 1 },
'{CHDR_W: 128, NUM_PORTS: 1, MEM_DATA_W: 128, MEM_ADDR_W: 16, FIFO_ADDR_W: 14, IN_FIFO_SIZE: 12, OUT_FIFO_SIZE: 12, OVERFLOW: 0, BIST: 0 }
};
//---------------------------------------------------------------------------
// DUT Instances
//---------------------------------------------------------------------------
genvar i;
for (i = 0; i < NUM_TESTS; i++) begin : gen_test_config
rfnoc_block_axi_ram_fifo_tb #(
.CHDR_W (test[i].CHDR_W),
.NUM_PORTS (test[i].NUM_PORTS),
.MEM_DATA_W (test[i].MEM_DATA_W),
.MEM_ADDR_W (test[i].MEM_ADDR_W),
.FIFO_ADDR_W (test[i].FIFO_ADDR_W),
.IN_FIFO_SIZE (test[i].IN_FIFO_SIZE),
.OUT_FIFO_SIZE (test[i].OUT_FIFO_SIZE),
.OVERFLOW (test[i].OVERFLOW),
.BIST (test[i].BIST)
) rfnoc_block_radio_tb_i ();
end : gen_test_config
endmodule : rfnoc_block_axi_ram_fifo_all_tb
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,637 @@
//
// Copyright 2019 Ettus Research, A National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: sim_axi_ram
//
// Description:
//
// Simulation model for a basic AXI4 memory mapped memory. A few notes on its
// behavior:
//
// - This model does not reorder requests (regardless of WID/RID). All
// requests are evaluated strictly in order.
// - The only supported response is OKAY
// - This model supports misaligned memory accesses, which cause a
// simulation warning.
// - A reset does not clear the memory contents
// - The memory itself is implemented using an associative array (sparse
// matrix) so that large memories can be supported.
// - This model is half duplex, meaning read and write data transfers won't
// happen at the same time. A new data transfer won't begin until the
// previous one has completed.
//
module sim_axi_ram #(
parameter AWIDTH = 32,
parameter DWIDTH = 64,
parameter IDWIDTH = 2,
parameter BIG_ENDIAN = 0,
parameter STALL_PROB = 25
) (
input logic s_aclk,
input logic s_aresetn,
// Write Address Channel
input logic [IDWIDTH-1:0] s_axi_awid,
input logic [ AWIDTH-1:0] s_axi_awaddr,
input logic [ 7:0] s_axi_awlen,
input logic [ 2:0] s_axi_awsize,
input logic [ 1:0] s_axi_awburst,
input logic s_axi_awvalid,
output logic s_axi_awready,
// Write Data Channel
input logic [ DWIDTH-1:0] s_axi_wdata,
input logic [DWIDTH/8-1:0] s_axi_wstrb,
input logic s_axi_wlast,
input logic s_axi_wvalid,
output logic s_axi_wready,
// Write Response Channel
output logic [IDWIDTH-1:0] s_axi_bid,
output logic [ 1:0] s_axi_bresp,
output logic s_axi_bvalid,
input logic s_axi_bready,
// Read Address Channel
input logic [IDWIDTH-1:0] s_axi_arid,
input logic [ AWIDTH-1:0] s_axi_araddr,
input logic [ 7:0] s_axi_arlen,
input logic [ 2:0] s_axi_arsize,
input logic [ 1:0] s_axi_arburst,
input logic s_axi_arvalid,
output logic s_axi_arready,
// Read Data Channel
output logic [ 0:0] s_axi_rid,
output logic [DWIDTH-1:0] s_axi_rdata,
output logic [ 1:0] s_axi_rresp,
output logic s_axi_rlast,
output logic s_axi_rvalid,
input logic s_axi_rready
);
localparam DEBUG = 0;
//---------------------------------------------------------------------------
// Data Types
//---------------------------------------------------------------------------
typedef enum logic [1:0] { FIXED, INCR, WRAP } burst_t;
typedef enum logic [1:0] { OKAY, EXOKAY, SLVERR, DECERR } resp_t;
typedef struct packed {
longint count; // Number of requests to wait for before executing
logic [IDWIDTH-1:0] id;
logic [AWIDTH-1:0] addr;
logic [8:0] len; // Add an extra bit, since actual true length is +1
logic [7:0] size; // Add extra bits to store size in bytes, instead of clog2(size)
burst_t burst;
} req_t;
// Make the address type an extra bit wide so that we can detect
// out-of-bounds accesses easily.
typedef bit [AWIDTH:0] addr_t;
// Data word type
typedef logic [DWIDTH-1:0] data_t;
// Mask to indicate which bits should be written.
typedef bit [DWIDTH/8-1:0] mask_t;
//---------------------------------------------------------------------------
// Data Structures
//---------------------------------------------------------------------------
byte memory [addr_t]; // Byte addressable memory
mailbox #(req_t) read_req = new(); // Read request queue
mailbox #(req_t) write_req = new(); // Write request queue
mailbox #(req_t) write_resp = new(); // Write response queue
longint req_count; // Number of requests received
longint compl_count; // Number of requests completed
//---------------------------------------------------------------------------
// External Configuration Interface
//---------------------------------------------------------------------------
int waddr_stall_prob = STALL_PROB;
int wdata_stall_prob = STALL_PROB;
int wresp_stall_prob = STALL_PROB;
int raddr_stall_prob = STALL_PROB;
int rdata_stall_prob = STALL_PROB;
// Set ALL stall probabilities to the same value
function void set_stall_prob(int probability);
assert(probability >= 0 && probability <= 100) else begin
$error("Probability must be from 0 to 100");
end
waddr_stall_prob = probability;
wdata_stall_prob = probability;
wresp_stall_prob = probability;
raddr_stall_prob = probability;
rdata_stall_prob = probability;
endfunction : set_stall_prob
// Set WRITE stall probabilities to the same value
function void set_write_stall_prob(int probability);
assert(probability >= 0 && probability <= 100) else begin
$error("Probability must be from 0 to 100");
end
waddr_stall_prob = probability;
wdata_stall_prob = probability;
wresp_stall_prob = probability;
endfunction : set_write_stall_prob
// Set READ stall probabilities to the same value
function void set_read_stall_prob(int probability);
assert(probability >= 0 && probability <= 100) else begin
$error("Probability must be from 0 to 100");
end
raddr_stall_prob = probability;
rdata_stall_prob = probability;
endfunction : set_read_stall_prob
// Set Write Address Channel stall probability
function void set_waddr_stall_prob(int probability);
assert(probability >= 0 && probability <= 100) else begin
$error("Probability must be from 0 to 100");
end
waddr_stall_prob = probability;
endfunction : set_waddr_stall_prob
// Set Write Data Channel stall probability
function void set_wdata_stall_prob(int probability);
assert(probability >= 0 && probability <= 100) else begin
$error("Probability must be from 0 to 100");
end
wdata_stall_prob = probability;
endfunction : set_wdata_stall_prob
// Set Write Response Channel stall probability
function void set_wresp_stall_prob(int probability);
assert(probability >= 0 && probability <= 100) else begin
$error("Probability must be from 0 to 100");
end
wresp_stall_prob = probability;
endfunction : set_wresp_stall_prob
// Set Read Address Channel stall probability
function void set_raddr_stall_prob(int probability);
assert(probability >= 0 && probability <= 100) else begin
$error("Probability must be from 0 to 100");
end
raddr_stall_prob = probability;
endfunction : set_raddr_stall_prob
// Set Read Data Channel stall probability
function void set_rdata_stall_prob(int probability);
assert(probability >= 0 && probability <= 100) else begin
$error("Probability must be from 0 to 100");
end
rdata_stall_prob = probability;
endfunction : set_rdata_stall_prob
// Get Write Address Channel stall probability
function int get_waddr_stall_prob();
return waddr_stall_prob;
endfunction : get_waddr_stall_prob
// Get Write Data Channel stall probability
function int get_wdata_stall_prob();
return wdata_stall_prob;
endfunction : get_wdata_stall_prob
// Get Write Response Channel stall probability
function int get_wresp_stall_prob();
return wresp_stall_prob;
endfunction : get_wresp_stall_prob
// Get Read Address Channel stall probability
function int get_raddr_stall_prob();
return raddr_stall_prob;
endfunction : get_raddr_stall_prob
// Get Read Data Channel stall probability
function int get_rdata_stall_prob();
return rdata_stall_prob;
endfunction : get_rdata_stall_prob
//---------------------------------------------------------------------------
// Helper Functions
//---------------------------------------------------------------------------
function data_t read_mem(addr_t byte_addr, int num_bytes);
data_t data;
addr_t incr;
if (BIG_ENDIAN) begin
byte_addr = byte_addr + num_bytes-1;
incr = -1;
end else begin
incr = 1;
end
for (int i = 0; i < num_bytes; i++) begin
if (byte_addr >= 2**AWIDTH) begin
$fatal(1, "Read extends beyond memory range");
end
if (memory.exists(byte_addr)) data[i*8 +: 8] = memory[byte_addr];
else data[i*8 +: 8] = 'X;
byte_addr += incr;
end
return data;
endfunction : read_mem
function void write_mem(addr_t byte_addr, int num_bytes, data_t data, mask_t mask);
addr_t incr;
if (BIG_ENDIAN) begin
byte_addr = byte_addr + num_bytes-1;
incr = -1;
end else begin
incr = 1;
end
for (int i = 0; i < num_bytes; i++) begin
if (mask[i]) begin
if (byte_addr >= 2**AWIDTH) begin
$fatal(1, "Write extends beyond memory range");
end
memory[byte_addr] = data[i*8 +: 8];
end
byte_addr += incr;
end
endfunction : write_mem
//---------------------------------------------------------------------------
// Write Requests
//---------------------------------------------------------------------------
initial begin : write_req_proc
req_t req;
burst_t burst;
s_axi_awready <= 0;
forever begin
@(posedge s_aclk);
if (!s_aresetn) continue;
if (s_axi_awvalid) begin
if (s_axi_awready) begin
req.count = req_count;
req.id = s_axi_awid;
req.addr = s_axi_awaddr;
req.len = s_axi_awlen + 1; // Per AXI4 spec, Burst_length = AxLEN[7:0] + 1
req.size = 2**s_axi_awsize; // Store as true size in bytes, not clog2(size)
req.burst = burst_t'(s_axi_awburst);
// Check that the request is valid
assert (!$isunknown(req)) else begin
$fatal(1, "Write request signals are unknown");
end
assert (s_axi_araddr % (DWIDTH/8) == 0) else begin
$warning("Unaligned memory write");
end
assert (2**s_axi_awsize <= DWIDTH/8) else begin
$fatal(1, "AWSIZE must not be larger than DWIDTH");
end
assert ($cast(burst, s_axi_awburst)) else begin
$fatal(1, "Invalid AWBURST value");
end
if (DEBUG) begin
$display("WRITE REQ: id=%X, addr=%X, len=%X, size=%X, burst=%s, %t, %m",
req.id, req.addr, req.len, req.size, req.burst.name, $realtime);
end
req_count++;
write_req.put(req);
end
// Randomly deassert ready
s_axi_awready <= $urandom_range(99) < waddr_stall_prob ? 0 : 1;
end
end
end : write_req_proc
//---------------------------------------------------------------------------
// Read Requests
//---------------------------------------------------------------------------
initial begin : read_req_proc
req_t req;
burst_t burst;
s_axi_arready <= 0;
forever begin
@(posedge s_aclk);
if (!s_aresetn) continue;
if (s_axi_arvalid) begin
if (s_axi_arready) begin
req.count = req_count;
req.id = s_axi_arid;
req.addr = s_axi_araddr;
req.len = s_axi_arlen + 1; // Per AXI4 spec, Burst_length = AxLEN[7:0] + 1
req.size = 2**s_axi_arsize; // Store as true size in bytes, not clog2(size)
req.burst = burst_t'(s_axi_arburst);
// Check that the request is valid
assert(!$isunknown(req)) else begin
$fatal(1, "Read request signals are unknown");
end
assert(s_axi_araddr % (DWIDTH/8) == 0) else begin
$warning("Unaligned memory read");
end
assert(2**s_axi_arsize <= DWIDTH/8) else begin
$fatal(1, "ARSIZE must not be larger than DWIDTH");
end
assert ($cast(burst, s_axi_awburst)) else begin
$fatal(1, "Invalid ARBURST value");
end
if (DEBUG) begin
$display("READ REQ: id=%X, addr=%X, len=%X, size=%X, burst=%s, %t, %m",
req.id, req.addr, req.len, req.size, req.burst.name, $realtime);
end
req_count++;
read_req.put(req);
end
// Randomly deassert ready to cause a stall
s_axi_arready <= $urandom_range(99) < raddr_stall_prob ? 0 : 1;
end
end
end : read_req_proc
//---------------------------------------------------------------------------
// Write Data
//---------------------------------------------------------------------------
initial begin : write_data_proc
req_t req;
bit [AWIDTH-1:0] addr;
forever begin
// Wait for the next write request
s_axi_wready <= 0;
write_req.get(req);
// Wait for previous requests to complete
while (compl_count < req.count) begin
@(posedge s_aclk);
if (!s_aresetn) break;
end
// If reset was asserted, clear the request queue and start over
if (!s_aresetn) begin
while(write_req.try_get(req));
continue;
end
// Iterate over the number of words in the request
for (int i = 0; i < req.len; ) begin
@(posedge s_aclk);
if (!s_aresetn) break;
// Check if we have a new data word
if (s_axi_wvalid) begin
if (s_axi_wready) begin
// Check the inputs
if ($isunknown(s_axi_wstrb)) begin
$fatal(1, "WSTRB is unknown");
end
if ($isunknown(s_axi_wdata)) begin
$warning(1, "WDATA is unknown; data will be changed to zero");
end
case (req.burst)
FIXED : begin
addr = req.addr;
end
INCR : begin
// If the address rolls over, we've reached the end of the
// memory and we should stop here.
addr = req.addr + i*req.size;
if (addr < req.addr) break;
end
WRAP : begin
// Allow roll-over
addr = req.addr + i*req.size;
end
endcase
write_mem(addr, req.size, s_axi_wdata, s_axi_wstrb);
if (DEBUG) begin
$display("WRITE: count=%3X, ADDR=%X, DATA=%X, SIZE=%X, STRB=%X, %t, %m",
i, addr, s_axi_wdata, req.size, s_axi_wstrb, $realtime);
end
i++;
end
// Randomly deassert ready to cause a stall
s_axi_wready <= $urandom_range(99) < wdata_stall_prob ? 0 : 1;
end
end // for
// If reset was asserted, clear the request queue and start over
if (!s_aresetn) begin
while(write_req.try_get(req));
continue;
end
compl_count++;
// Enqueue write response
write_resp.put(req);
// Make sure WLAST asserted for the last word. If not we report an error.
// Per the AXI4 standard, "a slave is not required to use the WLAST
// signal" because "a slave can calculate the last write data transfer
// from the burst length AWLEN".
if (s_axi_wlast != 1'b1) begin
$error("WLAST not asserted on last word of burst");
end
end // forever
end : write_data_proc
//---------------------------------------------------------------------------
// Write Response
//---------------------------------------------------------------------------
initial begin : write_resp_proc
req_t resp;
bit [AWIDTH-1:0] addr;
forever begin
s_axi_bid <= 'X;
s_axi_bresp <= 'X;
s_axi_bvalid <= 0;
// Wait for the next write response
write_resp.get(resp);
@(posedge s_aclk);
// If there's a reset, clear the response queue and start over
if (!s_aresetn) begin
while(write_resp.try_get(resp));
continue;
end
// Randomly keep bvalid deasserted for next word to cause a stall
if ($urandom_range(99) < wresp_stall_prob) begin
do begin
@(posedge s_aclk);
if (!s_aresetn) break;
end while ($urandom_range(99) < wresp_stall_prob);
// If reset was asserted, clear the response queue and start over
if (!s_aresetn) begin
while(write_resp.try_get(resp));
continue;
end
end
// Output the next response
s_axi_bid <= resp.id;
s_axi_bresp <= OKAY;
s_axi_bvalid <= 1;
if (DEBUG) begin
$display("WRITE RESP: ID=%X, %t, %m", resp.id, $realtime);
end
// Wait for the response to be accepted
do begin
@(posedge s_aclk);
if (!s_aresetn) break;
end while (!s_axi_bready);
// Output the next response
s_axi_bid <= 'X;
s_axi_bresp <= 'X;
s_axi_bvalid <= 0;
// If reset was asserted, clear the response queue and start over
if (!s_aresetn) begin
while(write_resp.try_get(resp));
continue;
end
end // forever
end : write_resp_proc
//---------------------------------------------------------------------------
// Read Data
//---------------------------------------------------------------------------
initial begin : read_data_proc
req_t req;
bit [AWIDTH-1:0] addr;
logic [DWIDTH-1:0] data;
forever begin
s_axi_rid <= 'X;
s_axi_rdata <= 'X;
s_axi_rresp <= 'X;
s_axi_rlast <= 'X;
s_axi_rvalid <= 0;
// Wait for the next read request
read_req.get(req);
// Wait for previous requests to complete
do begin
@(posedge s_aclk);
if (!s_aresetn) break;
end while (compl_count < req.count);
// If reset was asserted, clear the request queue and start over
if (!s_aresetn) begin
while(read_req.try_get(req));
continue;
end
for (int i = 0; i < req.len; i++) begin
// Randomly keep rvalid deasserted for next word to cause a stall
if ($urandom_range(99) < rdata_stall_prob) begin
do begin
@(posedge s_aclk);
if (!s_aresetn) break;
end while ($urandom_range(99) < rdata_stall_prob);
if (!s_aresetn) break;
end
case (req.burst)
FIXED : begin
addr = req.addr;
end
INCR : begin
// If the address rolls over, we've reached the end of the memory
// and we should stop here.
addr = req.addr + i*req.size;
if (addr < req.addr) break;
end
WRAP : begin
// Allow roll-over
addr = req.addr + i*req.size;
end
endcase
// Read the memory
data = read_mem(addr, req.size);
// Output the next word
s_axi_rid <= req.id;
s_axi_rdata <= data;
s_axi_rresp <= OKAY;
s_axi_rlast <= (i == req.len-1);
s_axi_rvalid <= 1;
if (DEBUG) begin
$display("READ: count=%3X, ADDR=%X, DATA=%X, SIZE=%X, %t, %m", i, addr, data, req.size, $realtime);
end
// Wait for the word to be captured
do begin
@(posedge s_aclk);
if (!s_aresetn) break;
end while (!s_axi_rready);
s_axi_rid <= 'X;
s_axi_rdata <= 'X;
s_axi_rresp <= 'X;
s_axi_rlast <= 'X;
s_axi_rvalid <= 0;
end // for
// If reset was asserted, clear the request queue and start over
if (!s_aresetn) begin
while(read_req.try_get(req));
end
compl_count++;
end // forever
end : read_data_proc
endmodule