fpga: lib: Add synthesizable AXI4-Stream SV components

Components are connected together with AxiStreamIfc. Some features
include:
  (1) Add bytes to the start of a packet
  (2) Remove bytes from a packet
  (3) Wrappers for some older components
      a.  fifo - buffer but imediately pass a packet
      b.  packet_gate - buffer and hold till end of packet
      c.  width_conv - cross clock domains and change width of axi bus

The AxiStreamIf was moved from PkgAxiStreamBfm to its own file. It can
be used to connect to ports with continuous assignment.
AxiStreamPacketIf must be used procedurally but allows the following
new methods:
  - reached_packet_byte - notify when tdata contains a paritcular byte
  - get_packet_byte/get_packet_field - extract a byte or field from axi
  - put_packet_byte/put_packet_field - overwrite a byte or field onto axi


Original-commit: c3bca6c87700054c96320de119a58f6a688dbd5a
This commit is contained in:
Andrew Moch
2020-06-25 14:44:04 -05:00
committed by Wade Fife
parent 6940db90e3
commit 23be04661f
20 changed files with 4197 additions and 80 deletions
@@ -0,0 +1,48 @@
#
# Copyright 2020 Ettus Research, a National Instruments Brand
#
# 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/axi4s_sv/Makefile.srcs
DESIGN_SRCS = $(abspath \
$(AXI4S_SV_SRCS) \
)
#-------------------------------------------------
# Testbench Specific
#-------------------------------------------------
# Define only one toplevel module
TB_TOP_MODULE = axi4s_remove_bytes_all_tb
SIM_TOP = $(TB_TOP_MODULE)
SIM_SRCS = \
$(abspath $(TB_TOP_MODULE).sv )\
$(abspath axi4s_remove_bytes_tb.sv )
# supressing the following worthless reminder.
#* Warning: M:/usrp4-hw/oss-repo/fpga/usrp3/lib/axi4s_sv/axi4s_remove_bytes.sv(228): (vlog-2583) [SVCHK] -
# Extra checking for conflicts with always_comb and always_latch variables is done at vopt time
SVLOG_ARGS = -suppress 2583
#-------------------------------------------------
# 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,139 @@
//
// Copyright 2020 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: axi4s_remove_bytes_all_tb
//
// Description: Testbench for axi4s_remove_bytes_all_tb
//
module axi4s_remove_bytes_all_tb #(
/* no PARAM */
)(
/* no IO */
);
// actual use cases
// Strip Ethernet header from a packet includes a preamble
axi4s_remove_bytes_tb #(.TEST_NAME("ENET_64_HREMOVE48"),.WIDTH(64),.REM_START(0),.REM_END(47))
ENET_64_HREMOVE48 ();
axi4s_remove_bytes_tb #(.TEST_NAME("ENET_128_HREMOVE48"),.WIDTH(128),.REM_START(0),.REM_END(47))
ENET_128_HREMOVE48 ();
axi4s_remove_bytes_tb #(.TEST_NAME("ENET_256_HREMOVE48"),.WIDTH(256),.REM_START(0),.REM_END(47))
ENET_256_HREMOVE48 ();
axi4s_remove_bytes_tb #(.TEST_NAME("ENET_512_HREMOVE48"),.WIDTH(512),.REM_START(0),.REM_END(47))
ENET_512_HREMOVE48 ();
// Strip Ethernet header from a packet without a preamble
axi4s_remove_bytes_tb #(.TEST_NAME("ENET_64_HREMOVE42"),.WIDTH(64),.REM_START(0),.REM_END(41))
ENET_64_HREMOVE42 ();
axi4s_remove_bytes_tb #(.TEST_NAME("ENET_128_HREMOVE42"),.WIDTH(128),.REM_START(0),.REM_END(41))
ENET_128_HREMOVE42 ();
axi4s_remove_bytes_tb #(.TEST_NAME("ENET_256_HREMOVE42"),.WIDTH(256),.REM_START(0),.REM_END(41))
ENET_256_HREMOVE42 ();
axi4s_remove_bytes_tb #(.TEST_NAME("ENET_512_HREMOVE42"),.WIDTH(512),.REM_START(0),.REM_END(41))
ENET_512_HREMOVE42 ();
// Strip Preamble header from a packet
axi4s_remove_bytes_tb #(.TEST_NAME("ENET_64_PREMOVE6"),.WIDTH(64),.REM_START(0),.REM_END(5))
ENET_64_PREMOVE6 ();
axi4s_remove_bytes_tb #(.TEST_NAME("ENET_128_PREMOVE6"),.WIDTH(128),.REM_START(0),.REM_END(5))
ENET_128_PREMOVE6 ();
axi4s_remove_bytes_tb #(.TEST_NAME("ENET_256_PREMOVE6"),.WIDTH(256),.REM_START(0),.REM_END(5))
ENET_256_PREMOVE6 ();
axi4s_remove_bytes_tb #(.TEST_NAME("ENET_512_PREMOVE6"),.WIDTH(512),.REM_START(0),.REM_END(5))
ENET_512_PREMOVE6 ();
// TRUNCATE cases -
axi4s_remove_bytes_tb #(.TEST_NAME("TRUNCATE_32_1:0"),.WIDTH(32),.REM_START(1),.REM_END(-1))
TRUNCATE_32_0to1 ();
axi4s_remove_bytes_tb #(.TEST_NAME("TRUNCATE_32_2:0"),.WIDTH(32),.REM_START(2),.REM_END(-1))
TRUNCATE_32_0to2 ();
axi4s_remove_bytes_tb #(.TEST_NAME("TRUNCATE_32_3:0"), .WIDTH(32),.REM_START(3),.REM_END(-1))
TRUNCATE_32_0to3 ();
axi4s_remove_bytes_tb #(.TEST_NAME("TRUNCATE_32_4:0"),.WIDTH(32),.REM_START(4),.REM_END(-1))
TRUNCATE_32_0to4 ();
axi4s_remove_bytes_tb #(.TEST_NAME("TRUNCATE_32_5:0"),.WIDTH(32),.REM_START(5),.REM_END(-1))
TRUNCATE_32_0to5 ();
axi4s_remove_bytes_tb #(.TEST_NAME("TRUNCATE_32_6:0"),.WIDTH(32),.REM_START(6),.REM_END(-1))
TRUNCATE_32_0to6 ();
axi4s_remove_bytes_tb #(.TEST_NAME("TRUNCATE_32_7:0"), .WIDTH(32),.REM_START(7),.REM_END(-1))
TRUNCATE_32_0to7 ();
// START cases - removal starts at LSB of word
axi4s_remove_bytes_tb #(.TEST_NAME("SSTART_32_0:0"),.WIDTH(32),.REM_START(0),.REM_END(0))
SSTART_32_0to0 ();
axi4s_remove_bytes_tb #(.TEST_NAME("SSTART_32_1:0"),.WIDTH(32),.REM_START(0),.REM_END(1))
SSTART_32_0to1 ();
axi4s_remove_bytes_tb #(.TEST_NAME("SSTART_32_2:0"),.WIDTH(32),.REM_START(0),.REM_END(2))
SSTART_32_0to2 ();
axi4s_remove_bytes_tb #(.TEST_NAME("EXACT_32_3:0"), .WIDTH(32),.REM_START(0),.REM_END(3))
EXACT_32_0to3 ();
axi4s_remove_bytes_tb #(.TEST_NAME("MSTART_32_4:0"),.WIDTH(32),.REM_START(0),.REM_END(4))
MSTART_32_0to4 ();
axi4s_remove_bytes_tb #(.TEST_NAME("MSTART_32_5:0"),.WIDTH(32),.REM_START(0),.REM_END(5))
MSTART_32_0to5 ();
axi4s_remove_bytes_tb #(.TEST_NAME("MSTART_32_6:0"),.WIDTH(32),.REM_START(0),.REM_END(6))
MSTART_32_0to6 ();
axi4s_remove_bytes_tb #(.TEST_NAME("EXACT_32_7:0"), .WIDTH(32),.REM_START(0),.REM_END(7))
EXACT_32_0to7 ();
// END cases - removal ends at MSB of word
axi4s_remove_bytes_tb #(.TEST_NAME("MEND_32_7:1"),.WIDTH(32),.REM_START(1),.REM_END(7))
MEND_32_1to7 ();
axi4s_remove_bytes_tb #(.TEST_NAME("MEND_32_7:2"),.WIDTH(32),.REM_START(2),.REM_END(7))
MEND_32_2to7 ();
axi4s_remove_bytes_tb #(.TEST_NAME("MEND_32_7:3"),.WIDTH(32),.REM_START(3),.REM_END(7))
MEND_32_3to7 ();
axi4s_remove_bytes_tb #(.TEST_NAME("EXACT_32_7:4"),.WIDTH(32),.REM_START(4),.REM_END(7))
EXACT_32_4to7 ();
axi4s_remove_bytes_tb #(.TEST_NAME("SEND_32_7:5"),.WIDTH(32),.REM_START(5),.REM_END(7))
SEND_32_5to7 ();
axi4s_remove_bytes_tb #(.TEST_NAME("SEND_32_7:6"),.WIDTH(32),.REM_START(6),.REM_END(7))
SEND_32_6to7 ();
axi4s_remove_bytes_tb #(.TEST_NAME("SEND_32_7:7"),.WIDTH(32),.REM_START(7),.REM_END(7))
SEND_32_7to7 ();
axi4s_remove_bytes_tb #(.TEST_NAME("SEND_32_3:1"),.WIDTH(32),.REM_START(1),.REM_END(3))
SEND_32_1to3 ();
axi4s_remove_bytes_tb #(.TEST_NAME("SEND_32_3:2"),.WIDTH(32),.REM_START(2),.REM_END(3))
SEND_32_2to3 ();
axi4s_remove_bytes_tb #(.TEST_NAME("SEND_32_3:3"),.WIDTH(32),.REM_START(3),.REM_END(3))
SEND_32_3to3 ();
// MIDDLE cases - removal is in the middle of words
axi4s_remove_bytes_tb #(.TEST_NAME("SMID_32_1:1"),.WIDTH(32),.REM_START(1),.REM_END(1))
SMID_32_1to1 ();
axi4s_remove_bytes_tb #(.TEST_NAME("SMID_32_2:1"),.WIDTH(32),.REM_START(1),.REM_END(2))
SMID_32_2to1 ();
axi4s_remove_bytes_tb #(.TEST_NAME("SMID_32_2:2"),.WIDTH(32),.REM_START(2),.REM_END(2))
SMID_32_2to2 ();
axi4s_remove_bytes_tb #(.TEST_NAME("MMID_32_5:1"),.WIDTH(32),.REM_START(1),.REM_END(5))
MMID_32_1to5 ();
axi4s_remove_bytes_tb #(.TEST_NAME("MMID_32_6:1"),.WIDTH(32),.REM_START(1),.REM_END(6))
MMID_32_1to6 ();
axi4s_remove_bytes_tb #(.TEST_NAME("MMID_32_6:2"),.WIDTH(32),.REM_START(2),.REM_END(6))
MMID_32_2to6 ();
// WRAP cases - removal is over word boundary
axi4s_remove_bytes_tb #(.TEST_NAME("SWRAP_32_5:2"),.WIDTH(32),.REM_START(2),.REM_END(5))
SWRAP_32_2to5 ();
axi4s_remove_bytes_tb #(.TEST_NAME("SWRAP_32_5:3"),.WIDTH(32),.REM_START(3),.REM_END(5))
SWRAP_32_3to5 ();
axi4s_remove_bytes_tb #(.TEST_NAME("SWRAP_32_6:3"),.WIDTH(32),.REM_START(3),.REM_END(6))
SWRAP_32_3to6 ();
axi4s_remove_bytes_tb #(.TEST_NAME("MMID_32_9:2"),.WIDTH(32),.REM_START(2),.REM_END(9))
MWRAP_32_2to9 ();
axi4s_remove_bytes_tb #(.TEST_NAME("SEND_32_9:3"),.WIDTH(32),.REM_START(3),.REM_END(9))
MWRAP_32_3to9 ();
axi4s_remove_bytes_tb #(.TEST_NAME("SEND_32_10:3"),.WIDTH(32),.REM_START(3),.REM_END(10))
MWRAP_32_3to10();
endmodule
@@ -0,0 +1,233 @@
//
// Copyright 2020 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: axi4s_remove_bytes_tb
//
// Description: Testbench for axi_remove_bytes
//
module axi4s_remove_bytes_tb #(
parameter TEST_NAME = "axi_remove_bytes",
WIDTH=32,REM_START=0,REM_END=7
)(
/* no IO */
);
// Include macros and time declarations for use with PkgTestExec
// To change the name of the TestExec object being used by the assertion
// macros, `define TEST_EXEC_OBJ before including this file and `undef it at
// the end of your testbench. Otherwise, it defaults to the shared object
// "PkgTestExec::test".
`define TEST_EXEC_OBJ test
`include "test_exec.svh"
import PkgAxiStreamBfm::*;
import PkgTestExec::*;
import PkgEthernet::*;
//---------------------------------------------------------------------------
// Local Parameters
//---------------------------------------------------------------------------
localparam UWIDTH = $clog2((WIDTH/8)+1);
localparam MAX_PACKET_BYTES = 16*1024;
typedef AxiStreamPacket #(WIDTH, UWIDTH) AxisPacket_t;
typedef XportStreamPacket #(WIDTH) XportPacket_t;
//---------------------------------------------------------------------------
// Clocks
//---------------------------------------------------------------------------
bit clk;
bit reset;
sim_clock_gen #(.PERIOD(5.0), .AUTOSTART(1))
clk_gen (.clk(clk), .rst(reset));
//---------------------------------------------------------------------------
// Bus Functional Models
//---------------------------------------------------------------------------
TestExec test = new();
AxiStreamIf #(.DATA_WIDTH(WIDTH),.USER_WIDTH(UWIDTH),.TKEEP(0),
.MAX_PACKET_BYTES(MAX_PACKET_BYTES))
i (clk, reset);
AxiStreamIf #(.DATA_WIDTH(WIDTH),.USER_WIDTH(UWIDTH),.TKEEP(0),
.MAX_PACKET_BYTES(MAX_PACKET_BYTES))
o (clk, reset);
// Bus functional model for a axi_stream controller
AxiStreamBfm #(.DATA_WIDTH(WIDTH),.USER_WIDTH(UWIDTH),.TKEEP(0),
.MAX_PACKET_BYTES(MAX_PACKET_BYTES))
axis = new(.master(i), .slave(o));
//----------------------------------------------------
// Instantiate DUT
//----------------------------------------------------
axi4s_remove_bytes #(.REM_START(REM_START),.REM_END(REM_END))
DUT (.*);
//---------------------------------------------------------------------------
// Reset
//---------------------------------------------------------------------------
task test_reset();
test.start_test("Wait for Reset", 10us);
clk_gen.reset();
wait(!reset);
repeat (10) @(posedge clk);
test.end_test();
endtask : test_reset
//---------------------------------------------------------------------------
// Ethernet to CPU test
//---------------------------------------------------------------------------
task automatic test_samples(int num_samples);
localparam PKTS_TO_SEND = 10;
localparam LOW_LIM = DUT.EXACT?DUT.START_WORD-1:DUT.START_WORD;
localparam HIGH_LIM = REM_END;
automatic string test_str;
automatic raw_pkt_t raw;
automatic XportPacket_t send[$];
automatic XportPacket_t expected[$];
automatic integer last_word_index;
test_str = $sformatf({TEST_NAME,"::Remove Bytes L=%3d"},num_samples);
test.start_test(test_str, 10us);
for (int i= 0 ; i < PKTS_TO_SEND; i++) begin
expected[i] = new;
send[i] = new;
get_ramp_raw_pkt(.num_samps(num_samples),.ramp_start((i*num_samples)%256),.ramp_inc(1),.pkt(raw),.SWIDTH(8));
send[i].push_bytes(raw);
// Remove the bytes from raw packet to build expected.
if (REM_END < 0) begin
raw = raw[0:REM_START-1];
end else begin
for (int i = 0 ; i < REM_END-REM_START+1 ; ++i) begin
if (raw.size()-1 >= REM_START) begin
raw.delete(REM_START);
end
end
end
expected[i].push_bytes(raw);
send[i].tkeep_to_tuser(.ERROR_PROB(10));
expected[i].tkeep_to_tuser();
if (send[i].has_error())
expected[i].set_error();
// if the incoming packet stops at a word
// where we have no where to mark an error
// then we set an error
last_word_index = send[i].data.size()-1;
if (last_word_index > LOW_LIM &&
num_samples-1 <= HIGH_LIM) begin
if (!DUT.TRUNCATE)
if (DUT.START_WORD != DUT.END_WORD || DUT.EXACT)
expected[i].set_error();
end
end
for (int i= PKTS_TO_SEND-1 ; i >= 0; i--) begin
if (expected[i].data.size() == 0) expected.delete(i);
end
fork
begin : send_thread
foreach(send[i])begin
axis.put(send[i]);
end
end
begin : expected_thread
if (expected.size() == 0) begin : no_expected
automatic string str;
automatic AxisPacket_t actual_a = new();
// I need a better way to flush the send queue.
#(2*WIDTH); // wait a bit for completion
o.tready = 1;
axis.wait_send(); // wait complete hung
#(2*WIDTH); // wait a bit for completion
str = $sformatf("UNEXPECTED_PACKET L=%3d\nREM_START=%3d:%1d REM_END=%3d:%1d SIZE =%3d WIDTH=%3d\nSEND\n%s",
num_samples,REM_START,DUT.START_WORD,REM_END,DUT.END_WORD,send[0].data.size(),WIDTH,
send[0].sprint());
if (axis.try_get(actual_a)) begin
$display("ACTUAL");
actual_a.print();
`ASSERT_ERROR(actual_a.data.size() == 0,str);
end
$display("Wait completed");
end else begin : get_expected
automatic string str;
automatic AxisPacket_t actual_a = new();
automatic XportPacket_t actual = new();
foreach(expected[i]) begin
axis.get(actual_a);
actual.import_axis(actual_a);
actual.tuser_to_tkeep();
str = $sformatf({"L= %3d REM =%2d:%1d WORD=%1d:%1d ERROR_LIM=%1d<%1d %1d<=%1d WIDTH=%3d","\nSEND\n%s"},
num_samples,
REM_START,REM_END,
DUT.START_WORD,DUT.END_WORD,
LOW_LIM,last_word_index,num_samples-1,HIGH_LIM,WIDTH,
send[i].sprint());
`ASSERT_ERROR(!actual.compare_w_error(expected[i],.COMPARE_ERROR_PACKETS(0)),str);
end
end
end
join
test.end_test();
endtask : test_samples
//----------------------------------------------------
// Main test loop
//----------------------------------------------------
initial begin : tb_main
automatic integer min_length;
test.tb_name = TEST_NAME;
if (REM_END == -1)
min_length = REM_START-4;
else
min_length = REM_END-4;
if ( min_length < 1) min_length = 1;
axis.run();
test_reset();
// power of 2 from zero
for (int i=0 ; i < 6 ; ++i) begin
test_samples(2**i);
end
for (int i=1 ; i < (WIDTH/8)*3 ; ++i) begin
test_samples(min_length+i);
end
// repeat back to back
axis.set_slave_stall_prob(0);
axis.set_master_stall_prob(0);
for (int i=1 ; i < (WIDTH/8)*3 ; ++i) begin
test_samples(min_length+i);
end
// End the TB, but don't $finish, since we don't want to kill other
// instances of this testbench that may be running.
test.end_tb(0);
// Kill the clocks to end this instance of the testbench
clk_gen.kill();
end // initial begin
endmodule
`undef TEST_EXEC_OBJ