aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/lib/axi4s_sv
diff options
context:
space:
mode:
Diffstat (limited to 'fpga/usrp3/lib/axi4s_sv')
-rw-r--r--fpga/usrp3/lib/axi4s_sv/AxiStreamIf.sv594
-rw-r--r--fpga/usrp3/lib/axi4s_sv/Makefile.srcs18
-rw-r--r--fpga/usrp3/lib/axi4s_sv/axi4s.vh51
-rw-r--r--fpga/usrp3/lib/axi4s_sv/axi4s_add_bytes.sv395
-rw-r--r--fpga/usrp3/lib/axi4s_sv/axi4s_add_bytes_tb/Makefile47
-rw-r--r--fpga/usrp3/lib/axi4s_sv/axi4s_add_bytes_tb/axi4s_add_bytes_all_tb.sv53
-rw-r--r--fpga/usrp3/lib/axi4s_sv/axi4s_add_bytes_tb/axi4s_add_bytes_tb.sv171
-rw-r--r--fpga/usrp3/lib/axi4s_sv/axi4s_fifo.sv82
-rw-r--r--fpga/usrp3/lib/axi4s_sv/axi4s_packet_gate.sv98
-rw-r--r--fpga/usrp3/lib/axi4s_sv/axi4s_remove_bytes.sv802
-rw-r--r--fpga/usrp3/lib/axi4s_sv/axi4s_remove_bytes_start.sv333
-rw-r--r--fpga/usrp3/lib/axi4s_sv/axi4s_remove_bytes_tb/Makefile48
-rw-r--r--fpga/usrp3/lib/axi4s_sv/axi4s_remove_bytes_tb/axi4s_remove_bytes_all_tb.sv139
-rw-r--r--fpga/usrp3/lib/axi4s_sv/axi4s_remove_bytes_tb/axi4s_remove_bytes_tb.sv233
-rw-r--r--fpga/usrp3/lib/axi4s_sv/axi4s_width_conv.sv137
15 files changed, 3201 insertions, 0 deletions
diff --git a/fpga/usrp3/lib/axi4s_sv/AxiStreamIf.sv b/fpga/usrp3/lib/axi4s_sv/AxiStreamIf.sv
new file mode 100644
index 000000000..cfba6d2eb
--- /dev/null
+++ b/fpga/usrp3/lib/axi4s_sv/AxiStreamIf.sv
@@ -0,0 +1,594 @@
+//
+// Copyright 2020 Ettus Research, A National Instruments Brand
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Interface: AxiStreamIf
+// Description:
+// AXI4-Stream is an ARM standard for representing streams or packets in
+// a design. For more information on the spec see:
+// https://static.docs.arm.com/ihi0051/a/IHI0051A_amba4_axi4_stream_v1_0_protocol_spec.pdf
+//
+// The interface contains methods for
+// (1) Monitoring progress of a packet
+// (2) Extracting fields from a packet
+// (3) Overwriting fields in a packet
+//
+// Parameters:
+// - DATA_WIDTH - Width of tdata on AXI4S bus
+// - USER_WIDTH - Width of tuser on AXI4S bus
+// - MAX_PACKET_BYTES - Maximum number of bytes between tlast. If 0, no
+// word_count resources are added.
+// - TDATA - use tData if 1
+// - TUSER - use tUser if 1
+// - TKEEP - use tKeep if 1
+// - TLAST - use tLast if 1
+//
+// Some Historic USRP code uses tuser without tkeep frequently.
+// The usage of tuser varies, here are some examples:
+// (1) Instead of using tkeep, user can encode number of valid bytes in a word
+// (2) The MSB can indicate an FCS failure from the MAC
+// (3) Header information is passed from xport to chdr system
+//
+
+//-----------------------------------------------------------------------------
+// Unidirectional AXI4-Stream interface
+//-----------------------------------------------------------------------------
+
+// Minimal - Interface Capable of continuous assignment
+interface AxiStreamIf #(
+ int DATA_WIDTH = 64,
+
+ int USER_WIDTH = 1,
+ int MAX_PACKET_BYTES = 0,
+ bit TDATA = 1,
+ bit TUSER = 1,
+ bit TKEEP = 1,
+ bit TLAST = 1
+) (
+ input logic clk,
+ input logic rst = 1'b0
+);
+
+// We don't want to check this, but this is an example of how to
+// check an input parameter
+// initial begin
+// assert (DATA_WIDTH % 8 == 0) else begin
+// $display("DATA_WIDTH == %1d",DATA_WIDTH);
+// $fatal(1,"Invalid data width on AxiStreamIf");
+// end
+// end
+
+ localparam BYTES_PER_WORD = DATA_WIDTH/8;
+ localparam DWIDTH = TDATA ? DATA_WIDTH : 0;
+ localparam UWIDTH = TUSER ? USER_WIDTH : 0;
+ localparam KWIDTH = TKEEP ? DATA_WIDTH/8 : 0;
+ localparam LWIDTH = TLAST ? 1 : 0;
+ localparam PACKED_WIDTH = DWIDTH + UWIDTH + KWIDTH + LWIDTH ;
+
+ // local type defs
+ typedef logic [DATA_WIDTH-1:0] data_t;
+ typedef logic [USER_WIDTH-1:0] user_t;
+ typedef logic [DATA_WIDTH/8-1:0] keep_t;
+
+ // Signals that make up a unidirectional AXI-Stream interface
+ logic tlast;
+ logic tvalid;
+ logic tready;
+ data_t tdata;
+ user_t tuser;
+ keep_t tkeep;
+
+ //---------------------------------------
+ // Trailing Bytes/Keep functions
+ // Trailing byte of 0 means full word else trailing bytes is the
+ // number of bytes in the last word.
+ //---------------------------------------
+ // bits needed to represent trailing bytes
+ localparam TRAILING_WIDTH = $clog2(DATA_WIDTH/8);
+ typedef logic [TRAILING_WIDTH-1:0] trailing_bytes_t;
+
+ // translate between bytes and tkeep
+ function automatic trailing_bytes_t keep2trailing(keep_t keep);
+ trailing_bytes_t bytes = '0;
+ if (tlast == 1) begin
+ // mux between values based on the high bit = 1
+ for(int b = 0; b < DATA_WIDTH/8 ; b++) begin
+ if (keep[b]) bytes = b+1;
+ end
+ end
+ return bytes;
+ endfunction : keep2trailing
+
+ function automatic keep_t trailing2keep(trailing_bytes_t bytes);
+ keep_t keep = '1;
+ if (tlast == 1 && bytes != 0) begin
+ foreach(keep[b]) begin
+ keep[b] = bytes > b;
+ end
+ end
+ return keep;
+ endfunction : trailing2keep
+
+ // View from the master side
+ modport master (
+ input clk, rst,
+ output tvalid, tdata, tuser, tkeep, tlast,
+ input tready,
+ import keep2trailing,
+ import trailing2keep
+ );
+
+ // View from the slave side
+ modport slave (
+ input clk, rst,
+ input tvalid, tdata, tuser, tkeep, tlast,
+ output tready,
+ import keep2trailing,
+ import trailing2keep
+ );
+
+
+endinterface : AxiStreamIf
+
+// Full featured for packet modification. Must use procedural assignment.
+interface AxiStreamPacketIf #(
+ int DATA_WIDTH = 64,
+ int USER_WIDTH = 1,
+ int MAX_PACKET_BYTES = 0,
+ bit TDATA = 1,
+ bit TUSER = 1,
+ bit TKEEP = 1,
+ bit TLAST = 1
+) (
+ input logic clk,
+ input logic rst = 1'b0
+);
+
+// We don't want to check this, but this is an example of how to
+// check an input parameter:
+// initial begin
+// assert (DATA_WIDTH % 8 == 0) else begin
+// $display("DATA_WIDTH == %1d",DATA_WIDTH);
+// $fatal(1,"Invalid data width on AxiStreamIf");
+// end
+// end
+
+ localparam BYTES_PER_WORD = DATA_WIDTH/8;
+ localparam MAX_PACKET_WORDS = MAX_PACKET_BYTES/BYTES_PER_WORD;
+ // local type defs
+ typedef logic [DATA_WIDTH-1:0] data_t;
+ typedef logic [USER_WIDTH-1:0] user_t;
+ typedef logic [DATA_WIDTH/8-1:0] keep_t;
+ typedef logic [$clog2(MAX_PACKET_WORDS)-1:0] word_count_t;
+
+ // Signals that make up a unidirectional AXI-Stream interface
+ logic tlast;
+ logic tvalid;
+ logic tready;
+ data_t tdata;
+ user_t tuser;
+ keep_t tkeep;
+ word_count_t word_count = 0;
+
+ if (MAX_PACKET_BYTES>0) begin
+ always_ff @(posedge clk) begin
+ if (rst) begin
+ word_count <= 0;
+ end else begin
+ // reset at last
+ if (tlast && tvalid && tready) begin
+ word_count <= 0;
+ // increment when valid and ready
+ end else if (tvalid && tready)begin
+ word_count <= word_count+1;
+ end
+ end
+ end
+ end
+
+ //---------------------------------------
+ // Packing functions
+ //---------------------------------------
+
+ localparam DWIDTH = TDATA ? DATA_WIDTH : 0;
+ localparam UWIDTH = TUSER ? USER_WIDTH : 0;
+ localparam KWIDTH = TKEEP ? DATA_WIDTH/8 : 0;
+ localparam LWIDTH = TLAST ? 1 : 0;
+ localparam PACKED_WIDTH = DWIDTH + UWIDTH + KWIDTH + LWIDTH ;
+ typedef logic [PACKED_WIDTH-1:0] packed_t;
+
+ function automatic packed_t pack(INC_DATA=1,INC_USER=1,INC_KEEP=1,INC_LAST=1);
+ logic [PACKED_WIDTH-1:0] data = 'X;
+
+ int USTART;
+ int KSTART;
+ int LSTART;
+
+ USTART = INC_DATA ? DWIDTH :0;
+ KSTART = INC_USER ? USTART+UWIDTH :USTART;
+ LSTART = INC_KEEP ? KSTART+KWIDTH :KSTART;
+
+ if (TDATA && INC_DATA) begin
+ // in the LSB
+ data[0+:DATA_WIDTH] = tdata;
+ end
+ if (TUSER && INC_USER) begin
+ // in the 1st MIDDLE
+ data[USTART+:USER_WIDTH] = tuser;
+ end
+ if (TKEEP && INC_KEEP) begin
+ // in the 2nd MIDDLE
+ data[KSTART+:DATA_WIDTH/8] = tkeep;
+ end
+ if (TLAST && INC_LAST) begin
+ // in the MSB
+ data[LSTART] = tlast;
+ end
+ return data;
+ endfunction : pack
+
+ task automatic unpack (packed_t data,INC_DATA=1,INC_USER=1,INC_KEEP=1,INC_LAST=1);
+
+ int USTART;
+ int KSTART;
+ int LSTART;
+
+ USTART = INC_DATA ? DWIDTH :0;
+ KSTART = INC_USER ? USTART+UWIDTH :USTART;
+ LSTART = INC_KEEP ? KSTART+KWIDTH :KSTART;
+
+ if (TDATA && INC_DATA) begin
+ // in the LSB
+ tdata = data[0+:DATA_WIDTH];
+ end
+ if (TUSER && INC_USER) begin
+ // in the 1st MIDDLE
+ tuser = data[USTART+:USER_WIDTH];
+ end
+ if (TKEEP && INC_KEEP) begin
+ // in the 2nd MIDDLE
+ tkeep = data[KSTART+:DATA_WIDTH/8];
+ end
+ if (TLAST && INC_LAST) begin
+ // in the MSB
+ tlast = data[LSTART];
+ end
+ endtask : unpack
+
+ //---------------------------------------
+ // Trailing Bytes/Keep functions
+ // Trailing byte of 0 means full word else trailing bytes is the
+ // number of bytes in the last word.
+ //---------------------------------------
+ // bits needed to represent trailing bytes
+ localparam TRAILING_WIDTH = $clog2(DATA_WIDTH/8);
+ typedef logic [TRAILING_WIDTH-1:0] trailing_bytes_t;
+
+ // translate between bytes and tkeep
+ function automatic trailing_bytes_t keep2trailing(keep_t keep);
+ trailing_bytes_t bytes = '0;
+ if (tlast == 1) begin
+ // mux between values based on the high bit = 1
+ for(int b = 0; b < DATA_WIDTH/8 ; b++) begin
+ if (keep[b]) bytes = b+1;
+ end
+ end
+ return bytes;
+ endfunction : keep2trailing
+
+ function automatic keep_t trailing2keep(trailing_bytes_t bytes);
+ keep_t keep = '1;
+ if (tlast == 1 && bytes != 0) begin
+ foreach(keep[b]) begin
+ keep[b] = bytes > b;
+ end
+ end
+ return keep;
+ endfunction : trailing2keep
+
+ // do the translation directly to/from the ifc
+ function automatic keep_t get_trailing_bytes();
+ localparam USER_TRAILING_WIDTH =
+ USER_WIDTH >= TRAILING_WIDTH ? TRAILING_WIDTH : USER_WIDTH;
+ assert (TUSER) else
+ $fatal("Can't get trailing if TUSER doesn't exist");
+ assert (USER_WIDTH >= TRAILING_WIDTH) else
+ $fatal("USER_WIDTH is to narrow to contain trailing");
+ return trailing2keep(tuser[USER_TRAILING_WIDTH-1:0]);
+ endfunction : get_trailing_bytes
+
+ task automatic set_trailing_bytes(keep_t keep);
+ localparam USER_TRAILING_WIDTH =
+ USER_WIDTH >= TRAILING_WIDTH ? TRAILING_WIDTH : USER_WIDTH;
+ assert (TUSER) else
+ $fatal("Can't set trailing if TUSER doesn't exist");
+ assert (USER_WIDTH >= TRAILING_WIDTH) else
+ $fatal("USER_WIDTH is to narrow to set trailing");
+ tuser[USER_TRAILING_WIDTH-1:0] = keep2trailing(keep);
+ endtask : set_trailing_bytes
+
+
+ //---------------------------------------
+ // Packet PROGRESS functions
+ //---------------------------------------
+ // Notify when a byte is reached(VALID)
+ function automatic bit reached_packet_byte(
+ input int OFFSET
+ );
+ // constant because they only depend on offset
+ int WORD_OFFSET;
+ assert (MAX_PACKET_BYTES > 0) else
+ $fatal(1,"checking packet on a non packet bus");
+ // Constant
+ WORD_OFFSET = OFFSET / (DATA_WIDTH/8);
+
+ return (word_count == WORD_OFFSET);
+
+ endfunction : reached_packet_byte
+
+ // Notify when a byte is being transmitted(VALID && READY)
+ function automatic bit xfering_packet_byte(
+ input int OFFSET
+ );
+
+ return reached_packet_byte(OFFSET) && tvalid && tready;
+
+ endfunction : xfering_packet_byte
+
+ // drive functions (These are not particularly useful
+ // but they guarantee the modules using the package don't
+ // drive the interface with a continuous assignment
+ task automatic drive_tlast(input logic last);
+ tlast = last;
+ endtask
+ task automatic drive_tvalid(input logic valid);
+ tvalid = valid;
+ endtask
+ task automatic drive_tready(input logic ready);
+ tready = ready;
+ endtask
+ task automatic drive_tdata(input logic data);
+ tdata = data;
+ endtask
+ task automatic drive_tuser(input logic user);
+ tuser = user;
+ endtask
+ task automatic drive_tkeep(input logic keep);
+ tkeep = keep;
+ endtask
+ function automatic word_count_t get_word_count();
+ return word_count;
+ endfunction
+
+ //---------------------------------------
+ // Packet FIELD READ functions
+ //
+ // USAGE:
+ //
+ // // get the fields - don't use assign. assign will not activate with changes to in1.
+ // always_comb begin : get_fields
+ // eth_dst_addr_new = in1.get_packet_field48(eth_dst_addr_old,DST_MAC_BYTE,.NETWORK_ORDER(1));
+ // reached_end_of_udp = in1.reached_packet_byte(DST_PORT_BYTE+3);// we have enough to decide
+ // end
+ //
+ // // use the fields
+ // always_comb begin : use_fields
+ // if (reached_end_of_udp) begin // Wait until you know the field is fully captured before using it!
+ // ** DO SOMETHING INTERESTING WITH est_dst_addr_new
+ // end
+
+
+ //---------------------------------------
+ // Grab a byte from a passing stream
+ function automatic logic [7:0] get_packet_byte(
+ input logic [7:0] old_value,
+ input int OFFSET
+ );
+ // constant because they only depend on offset
+ int BYTE_OFFSET;
+
+ logic [7:0] new_value;
+
+ // Constant
+ BYTE_OFFSET = OFFSET % (DATA_WIDTH/8);
+
+ // default : Stay the same
+ new_value = old_value;
+
+ if (reached_packet_byte(OFFSET)) begin
+ new_value = tdata[BYTE_OFFSET*8+:8];
+ end
+ return new_value;
+ endfunction : get_packet_byte
+
+ // Grab a 16 bit field from a passing stream
+ function automatic logic [15:0] get_packet_field16(
+ input logic [15:0] old_value,
+ input int OFFSET,
+ input int NETWORK_ORDER=0
+ );
+ logic [15:0] new_value;
+
+ localparam BYTES=$size(new_value)/8;
+ for (int i=0;i < BYTES; i++) begin
+ if (NETWORK_ORDER==1) begin
+ new_value[i*8+:8] = get_packet_byte(old_value[i*8+:8],OFFSET+BYTES-1-i);
+ end else begin
+ new_value[i*8+:8] = get_packet_byte(old_value[i*8+:8],OFFSET+i);
+ end
+ end
+ return new_value;
+
+ endfunction : get_packet_field16
+
+ // Grab a 32 bit field from a passing stream
+ function automatic logic [31:0] get_packet_field32(
+ input logic [31:0] old_value,
+ input int OFFSET,
+ input int NETWORK_ORDER=0
+ );
+ logic [31:0] new_value;
+
+ localparam BYTES=$size(new_value)/8;
+ for (int i=0;i < BYTES; i++) begin
+ if (NETWORK_ORDER==1) begin
+ new_value[i*8+:8] = get_packet_byte(old_value[i*8+:8],OFFSET+BYTES-1-i);
+ end else begin
+ new_value[i*8+:8] = get_packet_byte(old_value[i*8+:8],OFFSET+i);
+ end
+ end
+
+ return new_value;
+
+ endfunction : get_packet_field32
+
+ // Grab a 48 bit field from a passing stream
+ function automatic logic [47:0] get_packet_field48(
+ input logic [47:0] old_value,
+ input int OFFSET,
+ input int NETWORK_ORDER=0
+ );
+ logic [47:0] new_value;
+
+ localparam BYTES=$size(new_value)/8;
+ for (int i=0;i < BYTES; i++) begin
+ if (NETWORK_ORDER==1) begin
+ new_value[i*8+:8] = get_packet_byte(old_value[i*8+:8],OFFSET+BYTES-1-i);
+ end else begin
+ new_value[i*8+:8] = get_packet_byte(old_value[i*8+:8],OFFSET+i);
+ end
+ end
+
+ return new_value;
+
+ endfunction : get_packet_field48
+
+ //---------------------------------------
+ // Packet FIELD OVERWRITE functions
+ //
+ // USAGE:
+ //
+ // // Each call muxes data as it goes by. This chains together muxing which synthesis will simplify
+ // always_comb begin : set_header_fields
+ // v2e5.tdata = v2e4.tdata;
+ // v2e5.put_packet_field48(mac_dst,DST_MAC_BYTE,.NETWORK_ORDER(1));
+ // end
+ //---------------------------------------
+
+ // Overwrite a byte in a passing stream
+ task automatic put_packet_byte(
+ input logic [7:0] new_value,
+ input int OFFSET
+ );
+ // constant because they only depend on offset
+ int BYTE_OFFSET;
+
+ // Constant
+ BYTE_OFFSET = OFFSET % (DATA_WIDTH/8);
+ if (reached_packet_byte(OFFSET)) begin
+ tdata[BYTE_OFFSET*8+:8] = new_value;
+ end
+ endtask : put_packet_byte
+
+ // Overwrite a 16 bit field in a passing stream
+ task automatic put_packet_field16(
+ input logic [15:0] new_value,
+ input int OFFSET,
+ input int NETWORK_ORDER=0
+ );
+ localparam BYTES=$size(new_value)/8;
+ for (int i=0;i < BYTES; i++) begin
+ if (NETWORK_ORDER==1) begin
+ put_packet_byte(new_value[i*8+:8],OFFSET+BYTES-1-i);
+ end else begin
+ put_packet_byte(new_value[i*8+:8],OFFSET+i);
+ end
+ end
+ endtask : put_packet_field16
+
+ // Overwrite a 32 bit field in a passing stream
+ task automatic put_packet_field32(
+ input logic [31:0] new_value,
+ input int OFFSET,
+ input int NETWORK_ORDER=0
+ );
+ localparam BYTES=$size(new_value)/8;
+ for (int i=0;i < BYTES; i++) begin
+ if (NETWORK_ORDER==1) begin
+ put_packet_byte(new_value[i*8+:8],OFFSET+BYTES-1-i);
+ end else begin
+ put_packet_byte(new_value[i*8+:8],OFFSET+i);
+ end
+ end
+ endtask : put_packet_field32
+
+ // Overwrite a 48 bit field in a passing stream
+ task automatic put_packet_field48(
+ input logic [47:0] new_value,
+ input int OFFSET,
+ input int NETWORK_ORDER=0
+ );
+ localparam BYTES=$size(new_value)/8;
+ for (int i=0;i < BYTES; i++) begin
+ if (NETWORK_ORDER==1) begin
+ put_packet_byte(new_value[i*8+:8],OFFSET+BYTES-1-i);
+ end else begin
+ put_packet_byte(new_value[i*8+:8],OFFSET+i);
+ end
+ end
+ endtask : put_packet_field48
+
+
+ // View from the master side
+ modport master (
+ input clk, rst,
+ output tvalid, tdata, tuser, tkeep, tlast,
+ input tready,
+ //import methods
+ import pack,
+ import unpack,
+ import keep2trailing,
+ import trailing2keep,
+ import get_trailing_bytes,
+ import set_trailing_bytes,
+ import get_word_count,
+ import reached_packet_byte,
+ import xfering_packet_byte,
+ import get_packet_byte,
+ import get_packet_field16,
+ import get_packet_field32,
+ import get_packet_field48,
+ import put_packet_byte,
+ import put_packet_field16,
+ import put_packet_field32,
+ import put_packet_field48
+ );
+
+ // View from the slave side
+ modport slave (
+ input clk, rst,
+ input tvalid, tdata, tuser, tkeep, tlast,
+ output tready,
+ //import methods
+ import pack,
+ import unpack,
+ import keep2trailing,
+ import trailing2keep,
+ import get_trailing_bytes,
+ import set_trailing_bytes,
+ import get_word_count,
+ import reached_packet_byte,
+ import xfering_packet_byte,
+ import get_packet_byte,
+ import get_packet_field16,
+ import get_packet_field32,
+ import get_packet_field48,
+ import put_packet_byte,
+ import put_packet_field16,
+ import put_packet_field32,
+ import put_packet_field48
+ );
+
+
+endinterface : AxiStreamPacketIf
diff --git a/fpga/usrp3/lib/axi4s_sv/Makefile.srcs b/fpga/usrp3/lib/axi4s_sv/Makefile.srcs
new file mode 100644
index 000000000..b86de5b41
--- /dev/null
+++ b/fpga/usrp3/lib/axi4s_sv/Makefile.srcs
@@ -0,0 +1,18 @@
+#
+# Copyright 2020 Ettus Research, A National Instruments Brand
+#
+# SPDX-License-Identifier: LGPL-3.0-or-later
+#
+
+##################################################
+# RFNoC Utility Sources
+##################################################
+AXI4S_SV_SRCS = $(abspath $(addprefix $(BASE_DIR)/../lib/axi4s_sv/, \
+AxiStreamIf.sv \
+axi4s_remove_bytes.sv \
+axi4s_remove_bytes_start.sv \
+axi4s_add_bytes.sv \
+axi4s_fifo.sv \
+axi4s_packet_gate.sv \
+axi4s_width_conv.sv \
+))
diff --git a/fpga/usrp3/lib/axi4s_sv/axi4s.vh b/fpga/usrp3/lib/axi4s_sv/axi4s.vh
new file mode 100644
index 000000000..1a0b80256
--- /dev/null
+++ b/fpga/usrp3/lib/axi4s_sv/axi4s.vh
@@ -0,0 +1,51 @@
+//
+// Copyright 2020 Ettus Research, A National Instruments Brand
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Header File: axi4s
+// Description: Macros for use with AXI4S
+//
+
+//-----------------------------------------------------------------------------
+// Unidirectional AXI4-Stream interface
+//-----------------------------------------------------------------------------
+
+// Macro that drives o from i for all fields. Of course ready runs in the
+// counter direction.
+
+`define AXI4S_ASSIGN(O,I) \
+ if (``I.TDATA) ``O.tdata = ``I.tdata;\
+ if (``I.TUSER) ``O.tuser = ``I.tuser;\
+ else ``O.tuser = 0;\
+ if (``I.TKEEP) ``O.tkeep = ``I.tkeep;\
+ else ``O.tkeep = '1;\
+ if (``I.TLAST) ``O.tlast = ``I.tlast;\
+ ``O.tvalid = ``I.tvalid;\
+ ``I.tready = ``O.tready;
+
+`define AXI4S_DEBUG_ASSIGN(O,I) \
+ (* mark_debug = "true" *) logic [``I.DATA_WIDTH-1:0] ``I``_debug_tdata;\
+ (* mark_debug = "true" *) logic [``I.USER_WIDTH-1:0] ``I``_debug_tuser;\
+ (* mark_debug = "true" *) logic [``I.DATA_WIDTH/8-1:0] ``I``_debug_tkeep;\
+ (* mark_debug = "true" *) logic ``I``_debug_tlast;\
+ (* mark_debug = "true" *) logic ``I``_debug_tvalid;\
+ (* mark_debug = "true" *) logic ``I``_debug_tready;\
+ always_comb begin\
+ if (``I.TDATA) ``I``_debug_tdata = ``I.tdata;\
+ if (``I.TUSER) ``I``_debug_tuser = ``I.tuser;\
+ if (``I.TKEEP) ``I``_debug_tkeep = ``I.tkeep;\
+ if (``I.TLAST) ``I``_debug_tlast = ``I.tlast;\
+ ``I``_debug_tvalid = ``I.tvalid;\
+ ``I``_debug_tready = ``O.tready;\
+ end\
+ always_comb begin\
+ if (``I.TDATA) ``O.tdata = ``I``_debug_tdata;\
+ if (``I.TUSER) ``O.tuser = ``I``_debug_tuser;\
+ else ``O.tuser = 0;\
+ if (``I.TKEEP) ``O.tkeep = ``I``_debug_tkeep;\
+ else ``O.tkeep = '1;\
+ if (``I.TLAST) ``O.tlast = ``I``_debug_tlast;\
+ ``O.tvalid = ``I``_debug_tvalid;\
+ ``I.tready = ``I``_debug_tready;\
+ end
diff --git a/fpga/usrp3/lib/axi4s_sv/axi4s_add_bytes.sv b/fpga/usrp3/lib/axi4s_sv/axi4s_add_bytes.sv
new file mode 100644
index 000000000..398942210
--- /dev/null
+++ b/fpga/usrp3/lib/axi4s_sv/axi4s_add_bytes.sv
@@ -0,0 +1,395 @@
+//
+// Copyright 2020 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: axi4s_add_bytes
+//
+// Description:
+//
+// Add zero filled bytes to a packet.
+// tUser = {error,trailing bytes};
+//
+// LIMITATIONS
+// The block only adds bytes to the beginning of a word.
+//
+// Parameters:
+// ADD_START - Add bytes before this point (0 means start)
+// 0 is the only supported value right now
+// ADD_BYTES - Number of bytes to add
+// SYNC - When 1 we wait for the start word to be
+// valid before we start shifting.
+// When 0 we aggressively pad 0 early, but
+// it means the extra space may be added before
+// we setup the values we want to overwrite onto
+// that space.
+
+module axi4s_add_bytes #(
+ int ADD_START = 0,
+ int ADD_BYTES = 6,
+ bit SYNC = 1
+) (
+ interface i, // AxiStreamIf or AxiStreamPacketIf
+ interface o // AxiStreamIf or AxiStreamPacketIf
+);
+
+ localparam BYTES_PER_WORD = i.DATA_WIDTH/8;
+ // tUSER - always {error,numbytes}
+ localparam UWIDTH = $clog2(BYTES_PER_WORD+1);
+
+ //packet position in bytes of the last removed byte.
+ localparam ADD_END = ADD_START + ADD_BYTES-1;
+ //packet position in bytes of the 1st byte after removal.
+ localparam ADD_RESTART = ADD_END+1;
+
+ ////////////// Byte offsets in a word /////////////////
+ localparam START_BYTE = ADD_START % BYTES_PER_WORD;
+ localparam END_BYTE = ADD_END % BYTES_PER_WORD;
+ localparam RESTART_BYTE = ADD_RESTART % BYTES_PER_WORD;
+
+ // An Important shift offset
+ localparam BYTE_SHIFT = (BYTES_PER_WORD - RESTART_BYTE)%BYTES_PER_WORD;
+ // Subcase Recognition
+ // EXACT case - the removal expression is removing an entire word
+ localparam EXACT = BYTE_SHIFT == 0;
+
+ `include "axi4s.vh"
+ // Parameter Checks
+ initial begin
+ assert (i.DATA_WIDTH == o.DATA_WIDTH) else
+ $fatal("DATA_WIDTH mismatch");
+ assert (i.USER_WIDTH == o.USER_WIDTH) else
+ $fatal("USER_WIDTH mismatch");
+ assert (i.USER_WIDTH >= UWIDTH) else
+ $fatal("i.USER_WIDTH is to small");
+ assert (o.USER_WIDTH >= UWIDTH) else
+ $fatal("o.USER_WIDTH is to small");
+ assert (ADD_START == 0) else
+ $fatal("Only tested for ADD_START = 0");
+ end
+
+ AxiStreamPacketIf #(.DATA_WIDTH(i.DATA_WIDTH),.USER_WIDTH(i.USER_WIDTH),
+ .TKEEP(0),.MAX_PACKET_BYTES(i.MAX_PACKET_BYTES))
+ s0(i.clk,i.rst);
+ AxiStreamPacketIf #(.DATA_WIDTH(i.DATA_WIDTH),.USER_WIDTH(i.USER_WIDTH),
+ .TKEEP(0),.MAX_PACKET_BYTES(i.MAX_PACKET_BYTES))
+ s1(i.clk,i.rst);
+
+ // move from AxiStreamIfc to AxiStreamPacketIf
+ always_comb begin
+ `AXI4S_ASSIGN(s0,i)
+ end
+
+ logic reached_start;
+ logic reached_end;
+ logic byte_overflow;
+ logic [s0.DATA_WIDTH-1:0] zero_data;
+ logic [s0.DATA_WIDTH-1:0] last_tdata;
+ logic [s0.DATA_WIDTH-1:0] remaining_shift_data;
+ logic [s0.DATA_WIDTH-1:0] last_shift_data;
+ logic [s0.DATA_WIDTH-1:0] first_shifted_data;
+
+ logic error_bit, error_bit_old;
+
+ // Cache a couple of words from the bus
+ always_ff @(posedge s0.clk) begin
+ if (s0.rst) begin
+ last_tdata <= 0;
+ end else if (s0.tvalid && s0.tready) begin
+ last_tdata <= s0.tdata;
+ end
+ end
+
+ if (EXACT) begin
+ always_comb begin
+ // If END_BYTE=3
+ zero_data = 'b0;
+ first_shifted_data = s0.tdata;
+ remaining_shift_data = s0.tdata;
+ last_shift_data = s0.tdata;
+ end
+ end else begin
+ always_comb begin
+ zero_data = 'b0;
+ // If END_BYTE=2 [7:0] [23:0]
+ // If END_BYTE=1 [15:0] [15:0]
+ // If END_BYTE=0 [23:0] [7:0]
+ first_shifted_data = {s0.tdata[BYTE_SHIFT*8-1:0],zero_data[END_BYTE*8+7:0]};
+ // If END_BYTE=0 [23:0] [31:24]
+ remaining_shift_data = {s0.tdata[BYTE_SHIFT*8-1:0],last_tdata[s0.DATA_WIDTH-1:BYTE_SHIFT*8]};
+ // If END_BYTE=0 [23:0] [31:24]
+ last_shift_data = {zero_data[BYTE_SHIFT*8-1:0],s0.tdata[s0.DATA_WIDTH-1:BYTE_SHIFT*8]};
+ end
+ end
+
+ //-----------------------------------------------------------------------
+ // user write function
+ // this module ASSUMES user includes error in the MSB and the rest is the
+ // number of bytes in the word
+ //-----------------------------------------------------------------------
+ function automatic [UWIDTH-1:0] uwrite(error=0,[UWIDTH-2:0] bytes=0);
+ begin
+ return {error,bytes};
+ end
+ endfunction
+
+ //-----------------------------------------------------------------------
+ // get_error -extract error from tuser
+ //-----------------------------------------------------------------------
+ function automatic get_error([UWIDTH-1:0] tuser);
+ begin
+ return tuser[UWIDTH-1];
+ end
+ endfunction
+
+ //-----------------------------------------------------------------------
+ // get_bytes -extract num_bytes from tuser
+ //-----------------------------------------------------------------------
+ function automatic [UWIDTH-1:0] get_bytes([UWIDTH-1:0] tuser);
+ logic [UWIDTH-1:0] bytes;
+ begin
+ if (tuser[UWIDTH-2:0] == 0) bytes = BYTES_PER_WORD;
+ else bytes = tuser[UWIDTH-2:0];
+ return bytes;
+ end
+ endfunction
+
+ //---------------------------------------
+ // remove state machine
+ //---------------------------------------
+ typedef enum {ST_PRE_ADD, ST_ADDING, ST_POST_ADD,ST_BONUS} add_state_t;
+
+ add_state_t add_state = ST_PRE_ADD;
+ add_state_t next_add_state = ST_PRE_ADD;
+
+
+ always_ff @(posedge s0.clk) begin
+ if (s0.rst) begin
+ error_bit_old <= 0;
+ end else begin
+
+ // must hold until output completes
+ if (s1.tlast && s1.tvalid && s1.tready) begin
+ error_bit_old <= 0;
+ // but they set based on the input
+ end else if (s0.tvalid && s0.tready) begin
+ error_bit_old <= error_bit;
+ end
+ end
+ end
+
+ // Find the landmark bytes
+ always_comb error_bit = get_error(s0.tuser) || error_bit_old;
+
+ always_comb begin
+ reached_start = s1.reached_packet_byte(ADD_START);
+ reached_end = s1.reached_packet_byte(ADD_START+ADD_BYTES);
+ end
+
+ if (EXACT) begin
+ always_comb byte_overflow = 0;
+ end else begin
+ always_comb byte_overflow = get_bytes(s0.tuser) > BYTE_SHIFT;
+ end
+
+ // because s0.tready feeds back and generates a
+ // change event for the entire interface,
+ // it can trigger an infinite loop of assignment
+ // even when nothing is changing. This breaks
+ // the feedback loop.
+ logic s0_tready;
+ always_comb s0.tready = s0_tready;
+
+ // ADD state machine
+ always_comb begin
+
+ // default assignment of next_state
+ next_add_state = add_state;
+ s1.tuser = s0.tuser;
+ s1.tlast = s0.tlast;
+ s1.tvalid = s0.tvalid;
+ s1.tdata = first_shifted_data;
+ s0_tready = s1.tready;
+
+ case (add_state)
+ // *****************************************************
+ // PRE_ADD - wait till we reach ADD_START
+ // *****************************************************
+ ST_PRE_ADD: begin
+
+ if (!SYNC || s0.tvalid) begin
+ // reached start and end in same clock and end of word
+ if (reached_start && reached_end && s0.tlast) begin
+
+ // if final word has more bytes than we can fit.
+ if (byte_overflow) begin
+ s1.tlast = 0;
+ s1.tvalid = s0.tvalid;
+ s0_tready = 0; // don't advance
+ s1.tdata = first_shifted_data;
+ s1.tuser = uwrite(error_bit,BYTES_PER_WORD);
+
+ if (s0.tvalid && s1.tready) begin
+ next_add_state = ST_BONUS;
+ end
+ // we can finish this clock because final word
+ // didn't overflow into an additional word.
+ end else begin
+ s1.tlast = 1;
+ s1.tvalid = s0.tvalid;
+ s0_tready = s1.tready;
+ s1.tdata = first_shifted_data;
+ s1.tuser = uwrite(error_bit,get_bytes(s0.tuser) + RESTART_BYTE);
+ // NO state advance
+ end
+ // reached start and end, and not the end of the packet
+ end else if (reached_start && reached_end && !s0.tlast) begin
+ s1.tlast = 0;
+ s1.tvalid = s0.tvalid;
+ s0_tready = s1.tready;
+ s1.tdata = first_shifted_data;
+ s1.tuser = uwrite(error_bit,BYTES_PER_WORD);
+
+ if (s0.tvalid && s1.tready) begin
+ next_add_state = ST_POST_ADD;
+ end
+
+ // reached start but not the end of byte insertion
+ end else if (reached_start && !reached_end) begin
+ s1.tlast = 0;
+ s1.tvalid = 1;
+ s0_tready = 0; // don't advance
+ s1.tdata = zero_data;
+ s1.tuser = uwrite(0,BYTES_PER_WORD);
+
+ if (s1.tready) begin
+ next_add_state = ST_ADDING;
+ end
+
+ end
+ end
+ end //ST_PRE_REMOVE
+
+
+ // *****************************************************
+ // REMOVING - burn words until we have data to
+ // start sending again
+ // *****************************************************
+ ST_ADDING: begin
+ //defaults
+ s1.tlast = 0;
+ s1.tvalid = 1;
+ s0_tready = 0; // don't advance
+ s1.tdata = zero_data;
+ s1.tuser = uwrite(0,BYTES_PER_WORD);
+
+ // reached the end of incoming packet and data insertion
+ if (reached_end && s0.tlast) begin
+ // if final word has more bytes than we can fit.
+ if (byte_overflow) begin
+ s1.tlast = 0;
+ s1.tvalid = s0.tvalid;
+ s0_tready = 0; // don't advance
+ s1.tdata = first_shifted_data;
+ s1.tuser = uwrite(error_bit,BYTES_PER_WORD);
+
+ if (s0.tvalid && s1.tready) begin
+ next_add_state = ST_BONUS;
+ end
+ end else begin
+ // we can finish this clock because final word
+ // didn't overflow into an additional word.
+ s1.tlast = 1;
+ s1.tvalid = s0.tvalid;
+ s0_tready = s1.tready;
+ s1.tdata = first_shifted_data;
+ s1.tuser = uwrite(error_bit,get_bytes(s0.tuser) + RESTART_BYTE);
+
+ if (s0.tvalid && s1.tready) begin
+ next_add_state = ST_PRE_ADD;
+ end
+ end
+
+ // reached the end of data insertion - not end of packet
+ end else if (reached_end && !s0.tlast) begin
+ s1.tlast = 0;
+ s1.tvalid = s0.tvalid;
+ s0_tready = s1.tready;
+ s1.tdata = first_shifted_data;
+ s1.tuser = uwrite(error_bit,BYTES_PER_WORD);
+
+ if (s0.tvalid && s1.tready) begin
+ next_add_state = ST_POST_ADD;
+ end
+
+ end
+ end
+ // *****************************************************
+ // POST_ADD waiting for end
+ // *****************************************************
+ ST_POST_ADD: begin
+ //defaults
+ s1.tlast = 0;
+ s1.tvalid = s0.tvalid;
+ s0_tready = s1.tready;
+ s1.tdata = remaining_shift_data;
+ s1.tuser = uwrite(error_bit,BYTES_PER_WORD);
+ // reached the end, but we have extra bytes to send
+ if (s0.tlast && byte_overflow) begin
+ s1.tlast = 0;
+ s0_tready = 0; // don't let a advance
+
+ if (s0.tvalid && s1.tready) begin
+ next_add_state = ST_BONUS;
+ end
+
+ // reached the end, and don't need the bonus state
+ end else if (s0.tlast) begin
+ s1.tlast = 1;
+ s1.tuser = uwrite(error_bit,get_bytes(s0.tuser) + RESTART_BYTE);
+
+ if (s1.tready && s0.tvalid) begin
+ next_add_state = ST_PRE_ADD;
+ end
+
+ end
+ end
+
+ // *****************************************************
+ // BONUS write out any overflow words
+ // *****************************************************
+ ST_BONUS: begin
+ //defaults
+ s1.tdata = last_shift_data;
+ s1.tuser = uwrite(error_bit,get_bytes(s0.tuser)+ RESTART_BYTE);
+ s1.tlast = 1;
+ s1.tvalid = s0.tvalid;
+ s0_tready = s1.tready;
+
+ if (s1.tready && s0.tvalid) begin
+ next_add_state = ST_PRE_ADD;
+ end
+
+ end
+
+ // We should never get here
+ default: begin
+ next_add_state = ST_PRE_ADD;
+ end
+ endcase
+ end
+
+ always_ff @(posedge s0.clk) begin
+ if (s0.rst) begin
+ add_state <= ST_PRE_ADD;
+ end else begin
+ add_state <= next_add_state;
+ end
+ end
+
+ always_comb begin
+ `AXI4S_ASSIGN(o,s1)
+ end
+
+
+endmodule : axi4s_add_bytes
diff --git a/fpga/usrp3/lib/axi4s_sv/axi4s_add_bytes_tb/Makefile b/fpga/usrp3/lib/axi4s_sv/axi4s_add_bytes_tb/Makefile
new file mode 100644
index 000000000..f1d36d7c2
--- /dev/null
+++ b/fpga/usrp3/lib/axi4s_sv/axi4s_add_bytes_tb/Makefile
@@ -0,0 +1,47 @@
+#
+# 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_add_bytes_all_tb
+SIM_TOP = $(TB_TOP_MODULE)
+
+SIM_SRCS = \
+$(abspath $(TB_TOP_MODULE).sv )\
+$(abspath axi4s_add_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
diff --git a/fpga/usrp3/lib/axi4s_sv/axi4s_add_bytes_tb/axi4s_add_bytes_all_tb.sv b/fpga/usrp3/lib/axi4s_sv/axi4s_add_bytes_tb/axi4s_add_bytes_all_tb.sv
new file mode 100644
index 000000000..4fb0edda3
--- /dev/null
+++ b/fpga/usrp3/lib/axi4s_sv/axi4s_add_bytes_tb/axi4s_add_bytes_all_tb.sv
@@ -0,0 +1,53 @@
+//
+// Copyright 2020 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: axi4s_add_bytes_all_tb
+//
+// Description: Testbench for axi_add_bytes
+// Exercise corner cases parameters for adding bytes to the front of a packet
+//
+
+module axi4s_add_bytes_all_tb;
+
+ // actual use cases
+ // Add Ethernet header from a packet includes a preamble
+ axi4s_add_bytes_tb #(.TEST_NAME("ENET_64_HADD"),.WIDTH(64),.ADD_START(0),.ADD_BYTES(48))
+ ENET_64_HADD48 ();
+ axi4s_add_bytes_tb #(.TEST_NAME("ENET_128_HADD"),.WIDTH(128),.ADD_START(0),.ADD_BYTES(48))
+ ENET_128_HADD48 ();
+ axi4s_add_bytes_tb #(.TEST_NAME("ENET_256_HADD"),.WIDTH(256),.ADD_START(0),.ADD_BYTES(48))
+ ENET_256_HADD48 ();
+ axi4s_add_bytes_tb #(.TEST_NAME("ENET_512_HADD"),.WIDTH(512),.ADD_START(0),.ADD_BYTES(48))
+ ENET_512_HADD48 ();
+
+ // Add Preamble header from a packet
+ axi4s_add_bytes_tb #(.TEST_NAME("ENET_64_PADD"),.WIDTH(64),.ADD_START(0),.ADD_BYTES(6))
+ ENET_64_PADD6 ();
+ axi4s_add_bytes_tb #(.TEST_NAME("ENET_128_PADD"),.WIDTH(128),.ADD_START(0),.ADD_BYTES(6))
+ ENET_128_PADD6 ();
+ axi4s_add_bytes_tb #(.TEST_NAME("ENET_256_PADD"),.WIDTH(256),.ADD_START(0),.ADD_BYTES(6))
+ ENET_256_PADD6 ();
+ axi4s_add_bytes_tb #(.TEST_NAME("ENET_512_PADD"),.WIDTH(512),.ADD_START(0),.ADD_BYTES(6))
+ ENET_512_PADD6 ();
+
+ // START cases - removal starts at LSB of word
+ axi4s_add_bytes_tb #(.TEST_NAME("SSTART_32_0:0"),.WIDTH(32),.ADD_START(0),.ADD_BYTES(1))
+ SSTART_32_0to0 ();
+ axi4s_add_bytes_tb #(.TEST_NAME("SSTART_32_1:0"),.WIDTH(32),.ADD_START(0),.ADD_BYTES(2))
+ SSTART_32_0to1 ();
+ axi4s_add_bytes_tb #(.TEST_NAME("SSTART_32_2:0"),.WIDTH(32),.ADD_START(0),.ADD_BYTES(3))
+ SSTART_32_0to2 ();
+ axi4s_add_bytes_tb #(.TEST_NAME("EXACT_32_3:0"), .WIDTH(32),.ADD_START(0),.ADD_BYTES(4))
+ EXACT_32_0to3 ();
+ axi4s_add_bytes_tb #(.TEST_NAME("MSTART_32_4:0"),.WIDTH(32),.ADD_START(0),.ADD_BYTES(5))
+ MSTART_32_0to4 ();
+ axi4s_add_bytes_tb #(.TEST_NAME("MSTART_32_5:0"),.WIDTH(32),.ADD_START(0),.ADD_BYTES(6))
+ MSTART_32_0to5 ();
+ axi4s_add_bytes_tb #(.TEST_NAME("MSTART_32_6:0"),.WIDTH(32),.ADD_START(0),.ADD_BYTES(7))
+ MSTART_32_0to6 ();
+ axi4s_add_bytes_tb #(.TEST_NAME("EXACT_32_7:0"), .WIDTH(32),.ADD_START(0),.ADD_BYTES(8))
+ EXACT_32_0to7 ();
+
+endmodule
diff --git a/fpga/usrp3/lib/axi4s_sv/axi4s_add_bytes_tb/axi4s_add_bytes_tb.sv b/fpga/usrp3/lib/axi4s_sv/axi4s_add_bytes_tb/axi4s_add_bytes_tb.sv
new file mode 100644
index 000000000..5e96684ec
--- /dev/null
+++ b/fpga/usrp3/lib/axi4s_sv/axi4s_add_bytes_tb/axi4s_add_bytes_tb.sv
@@ -0,0 +1,171 @@
+//
+// Copyright 2020 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: axi4s_add_bytes_tb
+//
+// Description: Testbench for axi_add_bytes
+//
+
+module axi4s_add_bytes_tb #(
+ parameter TEST_NAME = "axi_add_bytes",
+ WIDTH=32,ADD_START=0,ADD_BYTES=7
+);
+ // 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;
+ //---------------------------------------------------------------------------
+ // 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_add_bytes #(.ADD_START(ADD_START),.ADD_BYTES(ADD_BYTES))
+ 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
+ //---------------------------------------------------------------------------
+ typedef AxiStreamPacket #(WIDTH, UWIDTH) AxisPacket_t;
+ typedef XportStreamPacket #(WIDTH) XportPacket_t;
+
+ task test_samples(int num_samples);
+ raw_pkt_t raw;
+
+ localparam PKTS_TO_SEND = 10;
+ automatic XportPacket_t send[$];
+ automatic XportPacket_t expected[$];
+
+ test.start_test({TEST_NAME,"::Remove Bytes"}, 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);
+ // Add the bytes to the raw packet to build expected.
+ for (int i = 0 ; i < ADD_BYTES; ++i) begin
+ raw.insert(ADD_START,0);
+ 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();
+ end
+
+ fork
+ begin // Send Thread
+ foreach(send[i])begin
+ axis.put(send[i]);
+ end
+ end
+ begin // Expected Thread
+ foreach(expected[i]) begin
+ automatic string str;
+ automatic AxisPacket_t actual_a = new();
+ automatic XportPacket_t actual = new();
+ axis.get(actual_a);
+ actual.import_axis(actual_a);
+ actual.tuser_to_tkeep();
+ str = $sformatf("ADD_START=%3d ADD_BYTES=%3d",ADD_START,ADD_BYTES);
+ `ASSERT_ERROR(!actual.compare_w_error(expected[i]),str);
+ end
+ end
+ join
+
+
+ test.end_test();
+
+ endtask : test_samples
+
+
+ //----------------------------------------------------
+ // Main test loop
+ //----------------------------------------------------
+ initial begin : tb_main
+ integer min_length;
+
+ test.tb_name = TEST_NAME;
+ min_length = ADD_START+1;
+
+
+ axis.run();
+
+ test_reset();
+
+ // Test with default holdoff.
+ 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
diff --git a/fpga/usrp3/lib/axi4s_sv/axi4s_fifo.sv b/fpga/usrp3/lib/axi4s_sv/axi4s_fifo.sv
new file mode 100644
index 000000000..0aa1daca0
--- /dev/null
+++ b/fpga/usrp3/lib/axi4s_sv/axi4s_fifo.sv
@@ -0,0 +1,82 @@
+//
+// Copyright 2020 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: axi4s_fifo
+//
+// Description: System Verilog wrapper for axi_fifo that accepts an
+// AxiStreamIfc with slave_user/master_uer interface.
+//
+// Parameters:
+// SIZE - 2**SIZE words are stored
+
+module axi4s_fifo #(
+ int SIZE = 1 // default size set to one to act as a pipe phase
+ ) (
+ input logic clear=1'b0,
+ interface i, // AxiStreamIf or AxiStreamPacketIf
+ interface o, // AxiStreamIf or AxiStreamPacketIf
+ output logic [15:0] space,
+ output logic [15:0] occupied
+);
+
+ `include "axi4s.vh"
+
+ // Parameter Checks
+ initial begin
+ assert (i.DATA_WIDTH == o.DATA_WIDTH) else
+ $fatal("DATA_WIDTH mismatch");
+ assert (i.USER_WIDTH == o.USER_WIDTH) else
+ $fatal("USER_WIDTH mismatch");
+ assert (i.TDATA == o.TDATA) else
+ $fatal("TDATA present mismatch");
+ assert (i.TUSER == o.TUSER) else
+ $fatal("TUSER present mismatch");
+ assert (i.TKEEP == o.TKEEP) else
+ $fatal("TKEEP present mismatch");
+ assert (i.TLAST == o.TLAST) else
+ $fatal("TLAST present mismatch");
+ end
+
+ AxiStreamPacketIf #(.DATA_WIDTH(i.DATA_WIDTH),.USER_WIDTH(i.USER_WIDTH),
+ .TDATA(i.TDATA),.TKEEP(i.TKEEP),.TUSER(i.TUSER),.TLAST(i.TLAST),
+ .MAX_PACKET_BYTES(i.MAX_PACKET_BYTES))
+ s0(i.clk,i.rst);
+ AxiStreamPacketIf #(.DATA_WIDTH(i.DATA_WIDTH),.USER_WIDTH(i.USER_WIDTH),
+ .TDATA(i.TDATA),.TKEEP(i.TKEEP),.TUSER(i.TUSER),.TLAST(i.TLAST),
+ .MAX_PACKET_BYTES(i.MAX_PACKET_BYTES))
+ s1(i.clk,i.rst);
+
+ // move from AxiStreamIfc to AxiStreamPacketIf
+ always_comb begin
+ `AXI4S_ASSIGN(s0,i)
+ end
+ // move from AxiStreamPacketIf to AxiStreamIfc
+ always_comb begin
+ `AXI4S_ASSIGN(o,s1)
+ end
+
+ logic [s0.PACKED_WIDTH-1:0] s0_data;
+ logic [s1.PACKED_WIDTH-1:0] s1_data;
+ always_comb s0_data = s0.pack();
+ always_comb s1.unpack(s1_data);
+
+ logic s0_ready, s1_valid;
+ always_comb s0.tready = s0_ready;
+ always_comb s1.tvalid = s1_valid;
+
+ axi_fifo #(
+ .WIDTH(s0.PACKED_WIDTH),.SIZE(SIZE)
+ ) axi_fifo_i (
+ .clk(s0.clk), .reset(s0.rst), .clear(clear),
+ .i_tdata(s0_data),
+ .i_tvalid(s0.tvalid),
+ .i_tready(s0_ready),
+ .o_tdata(s1_data),
+ .o_tvalid(s1_valid),
+ .o_tready(s1.tready),
+ .space(space), .occupied(occupied)
+ );
+
+endmodule : axi4s_fifo
diff --git a/fpga/usrp3/lib/axi4s_sv/axi4s_packet_gate.sv b/fpga/usrp3/lib/axi4s_sv/axi4s_packet_gate.sv
new file mode 100644
index 000000000..a1b744adf
--- /dev/null
+++ b/fpga/usrp3/lib/axi4s_sv/axi4s_packet_gate.sv
@@ -0,0 +1,98 @@
+//
+// Copyright 2020 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: axi4s_packet_gate
+//
+// Description:
+// System Verilog wrapper for axi_gate_packet that accepts
+// a AxiStreamIfc with slave_user/master_uer interface.
+//
+// Parameters:
+// TDATA - store tData if 1
+// TUSER - store tUser if 1
+// SIZE - 2**SIZE words are stored
+// USE_AS_BUFF - Allow the packet gate to be used as a buffer (uses more RAM)
+// MIN_PKT_SIZE - log2 of minimum valid packet size (rounded down, used to
+// reduce addr fifo size)
+
+module axi4s_packet_gate #(
+ bit TDATA = 1,
+ bit TUSER = 1,
+ int SIZE = 10,
+ bit USE_AS_BUFF = 1,
+ int MIN_PKT_SIZE= 1
+) (
+ input logic clear=1'b0,
+ input logic error=1'b0,
+ interface i, // AxiStreamIf or AxiStreamPacketIf
+ interface o // AxiStreamIf or AxiStreamPacketIf
+);
+
+ `include "axi4s.vh"
+
+ // Parameter Checks
+ initial begin
+ assert (i.DATA_WIDTH == o.DATA_WIDTH) else
+ $fatal("DATA_WIDTH mismatch");
+ assert (i.USER_WIDTH == o.USER_WIDTH) else
+ $fatal("USER_WIDTH mismatch");
+ assert (i.TDATA == o.TDATA) else
+ $fatal("TDATA present mismatch");
+ assert (i.TUSER == o.TUSER) else
+ $fatal("TUSER present mismatch");
+ assert (i.TKEEP == o.TKEEP) else
+ $fatal("TKEEP present mismatch");
+ assert (i.TLAST == 1) else
+ $fatal("i.TLAST not present");
+ assert (o.TLAST == 1) else
+ $fatal("o.TLAST not present");
+ end
+
+ localparam WIDTH = i.DWIDTH + i.UWIDTH + i.KWIDTH;
+
+ AxiStreamPacketIf #(.DATA_WIDTH(i.DATA_WIDTH),.USER_WIDTH(i.USER_WIDTH),
+ .TDATA(i.TDATA),.TKEEP(i.TKEEP),.TUSER(i.TUSER),.TLAST(i.TLAST),
+ .MAX_PACKET_BYTES(i.MAX_PACKET_BYTES))
+ s0(i.clk,i.rst);
+ AxiStreamPacketIf #(.DATA_WIDTH(i.DATA_WIDTH),.USER_WIDTH(i.USER_WIDTH),
+ .TDATA(i.TDATA),.TKEEP(i.TKEEP),.TUSER(i.TUSER),.TLAST(i.TLAST),
+ .MAX_PACKET_BYTES(i.MAX_PACKET_BYTES))
+ s1(i.clk,i.rst);
+
+ // move from AxiStreamIfc to AxiStreamPacketIf
+ always_comb begin
+ `AXI4S_ASSIGN(s0,i)
+ end
+ // move from AxiStreamPacketIf to AxiStreamIfc
+ always_comb begin
+ `AXI4S_ASSIGN(o,s1)
+ end
+
+ logic [WIDTH-1:0] s0_data;
+ logic [WIDTH-1:0] s1_data;
+ always_comb s0_data = s0.pack(.INC_LAST(0));
+ always_comb s1.unpack(s1_data,.INC_LAST(0));
+
+ logic s0_ready, s1_valid, s1_last;
+ always_comb s0.tready = s0_ready;
+ always_comb s1.tvalid = s1_valid;
+ always_comb s1.tlast = s1_last;
+
+ axi_packet_gate #(
+ .WIDTH(WIDTH),.SIZE(SIZE), .USE_AS_BUFF(USE_AS_BUFF), .MIN_PKT_SIZE(MIN_PKT_SIZE)
+ ) axi_packet_gate_i (
+ .clk(s0.clk), .reset(s0.rst), .clear(clear),
+ .i_terror(error),
+ .i_tdata(s0_data),
+ .i_tvalid(s0.tvalid),
+ .i_tready(s0_ready),
+ .i_tlast(s0.tlast),
+ .o_tdata(s1_data),
+ .o_tvalid(s1_valid),
+ .o_tready(s1.tready),
+ .o_tlast(s1_last)
+ );
+
+endmodule : axi4s_packet_gate
diff --git a/fpga/usrp3/lib/axi4s_sv/axi4s_remove_bytes.sv b/fpga/usrp3/lib/axi4s_sv/axi4s_remove_bytes.sv
new file mode 100644
index 000000000..4321d87d4
--- /dev/null
+++ b/fpga/usrp3/lib/axi4s_sv/axi4s_remove_bytes.sv
@@ -0,0 +1,802 @@
+//
+// Copyright 2020 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module : axi4s_remove_bytes
+//
+// Description:
+// Remove bytes from a packet. 1 removal can happen per
+// packet. The removal is made by delaying the output
+// by a clock, and then combining the new and old word
+// and providing a combination of shifted words.
+// This implementation requires that the user field
+// holds the number of valid bytes in the word, and the MSB of the user field
+// indicates if the MAC had an error.
+//
+// The block will hold off the input if it goes to the BONUS State.
+//
+// This block is intended to remove data from the beginning or middle
+// of a packet. You can truncate a packet by setting REM_END to -1.
+//
+// LIMITATIONS
+// The block will set the error bit if you put in a packet between
+// REM_END and REM_START length, and it is unable to cleanly signal
+// and end to the packet. (there is no way to send a zero byte valid
+// packet using tuser protocol.
+// Packets must be terminated with tlast.
+//
+// Parameters:
+// REM_START - First byte to remove (0 means start)
+// REM_END - Last byte to remove (-1 means truncate from REM START)
+//
+
+module axi4s_remove_bytes #(
+ REM_START=0,
+ REM_END=8
+)(
+ interface i, // AxiStreamIf or AxiStreamPacketIf
+ interface o // AxiStreamIf or AxiStreamPacketIf
+);
+
+ localparam BYTES_PER_WORD = i.DATA_WIDTH/8;
+ // tUSER - always {error,numbytes}
+ localparam UWIDTH = $clog2(BYTES_PER_WORD+1);
+ localparam ERROR = UWIDTH-1; // MSB is the error bit.
+
+ localparam TRUNCATE = REM_END < 0;
+ // END is inclusive so +1
+ localparam BYTES_REMOVED = TRUNCATE ? 1 :
+ REM_END-REM_START+1;
+
+ // how many bytes into the word for start and end point
+ localparam START_BYTE = REM_START% BYTES_PER_WORD;
+ localparam START_WORD = REM_START/ BYTES_PER_WORD;
+ localparam END_BYTE = TRUNCATE ? BYTES_PER_WORD-1 :
+ REM_END % BYTES_PER_WORD;
+ localparam END_WORD = TRUNCATE ? 65535 : // max word counter value
+ REM_END / BYTES_PER_WORD;
+
+ localparam FIRST_BYTE_AFTER = (END_BYTE+1) % BYTES_PER_WORD;
+
+ localparam BYTE_SHIFT = BYTES_REMOVED % BYTES_PER_WORD;
+ localparam BYTE_CARRY = BYTES_PER_WORD - BYTE_SHIFT;
+ // CASE differentiators
+ localparam SINGLE = BYTES_REMOVED <= BYTES_PER_WORD;
+ localparam START_AT_LSB = START_BYTE == 0;
+ localparam END_AT_MSB = END_BYTE == BYTES_PER_WORD-1;
+ localparam EXACT = START_AT_LSB && END_AT_MSB;
+ localparam MIDDLE = END_BYTE >= START_BYTE;
+
+ `include "axi4s.vh"
+
+ // Parameter Checks
+ initial begin
+ assert (i.DATA_WIDTH == o.DATA_WIDTH) else
+ $fatal("DATA_WIDTH mismatch");
+ end
+
+ AxiStreamPacketIf #(.DATA_WIDTH(i.DATA_WIDTH),.USER_WIDTH(i.USER_WIDTH),
+ .TKEEP(0),.MAX_PACKET_BYTES(i.MAX_PACKET_BYTES))
+ s0(i.clk,i.rst);
+ AxiStreamPacketIf #(.DATA_WIDTH(i.DATA_WIDTH),.USER_WIDTH(i.USER_WIDTH),
+ .TKEEP(0),.MAX_PACKET_BYTES(i.MAX_PACKET_BYTES))
+ s1(i.clk,i.rst);
+
+
+ // implement specialized cases
+ if (REM_START == 0 && !EXACT) begin : start_not_exact
+
+ // START at zero but still shifted
+ axi4s_remove_bytes_start #(
+ .REM_END(REM_END)
+ ) axi4s_remove_bytes_start_i (
+ .i(i), .o(o)
+ );
+
+ end else begin : general
+
+ // move from AxiStreamIfc to AxiStreamPacketIf
+ always_comb begin
+ `AXI4S_ASSIGN(s0,i)
+ end
+
+ typedef enum {MS_EXACT, MS_START_AT_LSB, MS_END_AT_MSB,
+ SINGLE_MIDDLE,MULTI_MIDDLE, MS_WRAP} case_t;
+ case_t MCASE;
+
+ logic reached_start;
+ logic reached_end;
+ logic reached_end_plus;
+ // memory for holding old values
+ logic [s0.DATA_WIDTH-1:0] last_tdata;
+ logic [s0.DATA_WIDTH-1:0] first_tdata;
+ logic [UWIDTH-1:0] first_tuser;
+
+ // various flavors of data shifting
+ logic [s0.DATA_WIDTH-1:0] trunc_data;
+ logic [s0.DATA_WIDTH-1:0] remaining_shift_data;
+ logic [s0.DATA_WIDTH-1:0] prefirst_shifted_data;
+ logic [s0.DATA_WIDTH-1:0] first_shifted_data;
+ logic [s0.DATA_WIDTH-1:0] one_word_data;
+ logic [s0.DATA_WIDTH-1:0] bonus_data;
+
+ logic [15:0] word_count; // Oversized to 65536 words
+ logic error_bit, error_bit_old;
+ logic [UWIDTH-1:0] in_byte_count;
+
+ //---------------------------------------
+ // remove state machine
+ //---------------------------------------
+ typedef enum {ST_PRE_REMOVE, ST_TRUNCATE, ST_REMOVING,
+ ST_POST_REMOVE, ST_BONUS} remove_state_t;
+ remove_state_t remove_state = ST_PRE_REMOVE;
+ remove_state_t next_remove_state = ST_PRE_REMOVE;
+
+ always_comb in_byte_count = get_bytes(s0.tuser);
+
+ // Cache a couple of words from the bus
+ always_ff @(posedge s0.clk) begin
+ if (s0.rst) begin
+ last_tdata = 0;
+ first_tdata = 0;
+ first_tuser = 0;
+ end else
+ if (s0.tvalid && s0.tready &&
+ (MCASE == MULTI_MIDDLE || MCASE==MS_START_AT_LSB))
+ last_tdata = s0.tdata;
+ if (s0.tvalid && s0.tready &&
+ (reached_start || next_remove_state==ST_POST_REMOVE ||
+ (remove_state!=ST_REMOVING && remove_state!= ST_TRUNCATE))) begin
+ first_tdata = s0.tdata;
+ first_tuser = s0.tuser;
+ end
+ end
+
+ //***************** DATA SHIFTING CASES ***********************/
+
+ //-----------------------------------------------------------------------
+ // user write function
+ // this module ASSUMES user includes error in the MSB and the rest is the
+ // number of bytes in the word
+ //-----------------------------------------------------------------------
+ function automatic logic [START_BYTE*8-1:0] start_part([s0.DATA_WIDTH-1:0] data);
+ begin
+ // workaround :: modelsim optimizer can fail if there is aposibility of a 0+:0
+ localparam MY_START_BYTE = START_BYTE ? START_BYTE : 1;
+ return data[0+:MY_START_BYTE*8];
+ end
+ endfunction
+
+
+ function automatic logic [s0.DATA_WIDTH-1-FIRST_BYTE_AFTER*8:0] end_part([s0.DATA_WIDTH-1:0] data);
+ begin
+ return data[s0.DATA_WIDTH-1:FIRST_BYTE_AFTER*8];
+ end
+ endfunction
+
+ function automatic logic [s0.DATA_WIDTH-1-BYTE_SHIFT*8:0] bs_part([s0.DATA_WIDTH-1:0] data);
+ begin
+ return data[s0.DATA_WIDTH-1:BYTE_SHIFT*8];
+ end
+ endfunction
+
+// Examples
+//
+// ENDING CASE 1
+// Incoming packet outgoing packet
+// ///////////////////////////////////////////////
+// D0 C0 B0 A0 <- word 0
+// D1 XX XX XX <- R(6:4)) D0 C0 B0 A0
+// D2 C2 B2 A2 C2 B2 A2 D1
+// D0 C0 B0 A0 <- next packet D2
+// D0 C0 B0 A0
+//
+// ENDING CASE2
+// Incoming packet outgoing packet
+// ///////////////////////////////////////////////
+// D0 C0 B0 A0 <- word 0
+// D1 XX XX XX <- R(6:4)) D0 C0 B0 A0
+// C2 B2 A2 C2 B2 A2 D1
+// D0 C0 B0 A0 <- next packet
+// D0 C0 B0 A0
+// Middle of Word case
+// Incoming packet outgoing packet
+// ///////////////////////////////////////////////
+// D0 C0 B0 A0 <- word 0
+// D1 XX XX A1 <- R(7:6) D0 C0 B0 A0
+// D2 C2 B2 A2 B2 A2 D1 A1
+// D3 C3 B3 A3 B3 A3 D2 C2
+// D0 C0 B0 A0 <- next packet D3 C3
+//
+// Easy Truncation (can handle dynamically)
+// Incoming packet outgoing packet
+// ///////////////////////////////////////////////
+// D0 C0 B0 A0 <- word 0
+// D1 C1 B1 A1 D0 C0 B0 A0
+// XX XX XX <- R(11:8)) D1 C1 B1 A1 <- TLAST HERE
+// D0 C0 B0 A0 <- next packet
+// D0 C0 B0 A0
+// Truncation case requiring REM_END=-1
+// because last word is to far away to see tlast.
+// Incoming packet outgoing packet
+// ///////////////////////////////////////////////
+// D0 C0 B0 A0 <- word 0
+// XX XX XX XX <- R(-1:4)) D0 C0 B0 A0 <- TLAST HERE
+// XX XX XX XX
+// XX XX XX XX
+// D0 C0 B0 A0 <- next packet
+// D0 C0 B0 A0
+// Remove from Front
+// Incoming packet outgoing packet
+// ///////////////////////////////////////////////
+// XX XX XX XX <- R(0:7)
+// XX XX XX XX <-
+// C2 B2 A2
+// D0 C0 B0 A0 <- next packet C2 B2 A2
+// D0 C0 B0 A0
+//
+// Remove 1 byte on back to back 1 word packets
+// Incoming packet outgoing packet
+// ///////////////////////////////////////////////
+// D0 C0 XX A0 <- R(1:1)
+// D0 C0 XX A0 <- R(1:1) D0 C0 A0
+// D0 C0 XX A0 <- R(1:1) D0 C0 A0
+// D0 C0 A0
+//
+//
+
+ // Note these should all be static shifts. We don't want to infer a barrel shifter.
+ if (EXACT) begin // Remove whole words
+ always_comb begin
+ MCASE = MS_EXACT;
+ first_shifted_data = s0.tdata;
+ remaining_shift_data = s0.tdata;
+ one_word_data = s0.tdata;
+ trunc_data = first_tdata;
+ bonus_data = 'bX;
+ end
+ end else if (START_AT_LSB) begin // Remove start of word shift case
+ // EXAMPLE XX XX XX
+ // for 8 byte word H0 G0 F0 E0 D0 C0 B0 A0 with START BYTE = 0(A0) END_BYTE = 2(C0) BYTE_SHIFT=3
+ // 1st word would be C1 B1 A1/H0 G0 F0 E0 D0
+ // [23:0] C1 B1 A1 / [63:24] H0 G0 F0 E0 D0
+ // same as remaining_shift_data above
+ // EXAMPLE XX XX XX XX XX XX XX XX
+ // for 8 byte word H0 G0 F0 E0 D0 C0 B0 A0
+ // XX XX XX
+ // H1 G1 F1 E1 D1 C1 B1 A1 with START BYTE = 0(A0) END_BYTE = 2(10)(C1) BYTE_SHIFT=3
+ // 1st word would be C2 B2 A2/H1 G1 F1 E1 D1
+ // [23:0] C2 B2 A2 / [63:24] H1 G1 F1 E1 D1
+ // same as remaining_shift_data above
+ // NOTE: Entire words are thrown away at start, so no caching required
+ always_comb begin
+ MCASE = MS_START_AT_LSB;
+ first_shifted_data = {s0.tdata,bs_part(last_tdata)};
+ if (BYTE_SHIFT==0)
+ remaining_shift_data = s0.tdata;
+ else
+ remaining_shift_data = {s0.tdata,bs_part(last_tdata)};
+ bonus_data = 'bX;
+ one_word_data = end_part(s0.tdata);
+ trunc_data = first_tdata;
+ bonus_data = 'bX;
+ bonus_data = bs_part(s0.tdata);
+ end
+ end else if (END_AT_MSB) begin // Remove end of word shift case
+ // EXAMPLE XX XX
+ // for 8 byte word H0 G0 F0 E0 D0 C0 B0 A0 with START BYTE = 6(G0) END_BYTE = 7(H0) BYTE_SHIFT=2
+ // 1st word would be B1 A1/F0 E0 D0 C0 B0 A0
+ // [15:0] B1 A1 / [47:0] F0 E0 D0 C0 B0 A0
+ // EXAMPLE XX XX
+ // for 8 byte word H0 G0 F0 E0 D0 C0 B0 A0
+ // XX XX XX XX XX XX XX XX
+ // H1 G1 F1 E1 D1 C1 B1 A1 with START BYTE = 6(G0) END_BYTE = 7(15)(H0) BYTE_SHIFT=2
+ // 1st word would be B2 A2/F0 E0 D0 C0 B0 A0
+ // NOTE: Uses 1st Data (from when we reach the first word
+ // [15:0] B2 A2 / [47:0] F0 E0 D0 C0 B0 A0
+ always_comb begin
+ MCASE = MS_END_AT_MSB;
+ first_shifted_data = {s0.tdata,start_part(first_tdata)};
+ if (BYTE_SHIFT==0)
+ remaining_shift_data = s0.tdata;
+ else
+ remaining_shift_data = {s0.tdata,bs_part(first_tdata)};
+ one_word_data = s0.tdata;
+ trunc_data = first_tdata;
+ bonus_data = 'bX;
+ bonus_data = bs_part(s0.tdata);
+ end
+ end else if(MIDDLE) begin // Remove middle of word shift case
+ // EXAMPLE XX XX XX XX XX XX
+ // for 8 byte word H0 G0 F0 E0 D0 C0 B0 A0 with START BYTE = 1(B0) END_BYTE = 6(G0) BYTE_SHIFT=6
+ // 1st word would be F1 E1 D1 C1 B1 A1/H0/A0
+ // [47:0] F1 E1 D1 C1 B1 A1 [63:56] H0 [7:0] A0
+ // EXAMPLE XX XX XX XX XX XX XX
+ // for 8 byte word H0 G0 F0 E0 D0 C0 B0 A0
+ // XX XX XX XX XX XX XX
+ // H1 G1 F1 E1 D1 C1 B1 A1 with START BYTE = 1(B0) END_BYTE = 6(14)(G0) BYTE_SHIFT=6
+ // 1st word would be F2 E2 D2 C2 B2 A2/H1/A0
+ // NOTE: Uses first Data from when we reach the first word.
+ // Also, must advance one clock beyond end for this case.
+ // [47:0] F2 E2 D2 C2 B2 A2 [63:56] H1 [7:0] A0
+ // remaining words F2 E2 D2 C2 B2 A2/H1 G1
+ // [47:0] F2 E2 D2 C2 B2 A2 [63:48] H1 G1
+ // same as remaining_shift_data above
+ always_comb begin
+ if (SINGLE) begin
+ MCASE = SINGLE_MIDDLE;
+ first_shifted_data = {s0.tdata,end_part(first_tdata),start_part(first_tdata)};
+ end else begin
+ MCASE = MULTI_MIDDLE;
+ prefirst_shifted_data = {end_part(s0.tdata),start_part(first_tdata)};
+ first_shifted_data = {s0.tdata,end_part(last_tdata),start_part(first_tdata)};
+ end
+ if (BYTE_SHIFT==0)
+ remaining_shift_data = s0.tdata;
+ else
+ remaining_shift_data = {s0.tdata,bs_part(first_tdata)};
+ one_word_data = {end_part(s0.tdata),start_part(s0.tdata)};
+ trunc_data = first_tdata;
+ bonus_data = 'bX;
+ bonus_data = bs_part(s0.tdata);
+ end
+ end else begin //wrapped case
+ // EXAMPLE XX XX
+ // for 8 byte word H0 G0 F0 E0 D0 C0 B0 A0 with START BYTE = 6(G0) END_BYTE = 2(10)(C1) BYTE_SHIFT=5
+ // XX XX XX
+ // H1 G1 F1 E1 D1 C1 B1 A1
+ //
+ // 1st word would be E1 D1/F0 E0 D0 C0 B0 A0
+ // [39:24] E1 D1 / [47:0] F0 E0 D0 C0 B0 A0
+ // remaining words E2 D2 C2 B2 A2/H1 G1 F1
+ // [39:0] E2 D2 C2 B2 A2 / H1 G1 F1 [63:40]
+ // same as remaining_shift_data_above // EXAMPLE XX XX
+ // for 8 byte word H0 G0 F0 E0 D0 C0 B0 A0
+ // XX XX XX XX XX XX XX XX
+ // H1 G1 F1 E1 D1 C1 B1 A1
+ // XX XX XX
+ // H2 G2 F2 E2 D2 C2 B2 A2 with START BYTE = 6(G0) END_BYTE = 2(10)(C1) BYTE_SHIFT=5
+ //
+ // 1st word would be E2 D2/F0 E0 D0 C0 B0 A0
+ // NOTE: Uses 1st Data (from when we reach the first word ;
+ // [39:24] E2 D2 / [47:0] F0 E0 D0 C0 B0 A0
+ always_comb begin
+ MCASE = MS_WRAP;
+ first_shifted_data = {end_part(s0.tdata),start_part(first_tdata)};
+ if (BYTE_SHIFT==0)
+ remaining_shift_data = s0.tdata;
+ else
+ remaining_shift_data = {s0.tdata,bs_part(first_tdata)};
+ one_word_data = s0.tdata;
+ trunc_data = first_tdata;
+ bonus_data = 'bX;
+ bonus_data = bs_part(s0.tdata);
+
+ end
+ end
+
+
+ typedef enum {PASS_THRU,BONUS,REM_SHIFT_DATA,FIRST_SHIFT_DATA,
+ PREFIRST_SHIFT_DATA,TRUNCATE_DATA,ONE_WORD} data_mux_sel_t;
+ data_mux_sel_t data_mux_sel = PASS_THRU;
+
+ always_comb begin : data_mux
+ s1.tdata = s0.tdata;
+ case (data_mux_sel)
+ PASS_THRU : s1.tdata = s0.tdata;
+ ONE_WORD : s1.tdata = one_word_data;
+ FIRST_SHIFT_DATA : s1.tdata = first_shifted_data;
+ PREFIRST_SHIFT_DATA : if (MCASE==MULTI_MIDDLE)
+ s1.tdata = prefirst_shifted_data;
+ else
+ s1.tdata = first_shifted_data;
+ REM_SHIFT_DATA : if (!TRUNCATE)
+ s1.tdata = remaining_shift_data;
+ TRUNCATE_DATA : if (TRUNCATE)
+ s1.tdata = trunc_data;
+ BONUS : if (!TRUNCATE && !EXACT)
+ s1.tdata = bonus_data;
+ default : s1.tdata = s0.tdata;
+ endcase
+ end
+
+ //-----------------------------------------------------------------------
+ // user write function
+ // this module ASSUMES user includes error in the MSB and the rest is the
+ // number of bytes in the word
+ //-----------------------------------------------------------------------
+ function automatic [UWIDTH-1:0] uwrite(error=0,[UWIDTH-2:0] bytes=0);
+ begin
+ return {error,bytes};
+ end
+ endfunction
+
+ //-----------------------------------------------------------------------
+ // get_error -extract error from tuser
+ //-----------------------------------------------------------------------
+ function automatic get_error([UWIDTH-1:0] tuser);
+ begin
+ return tuser[ERROR];
+ end
+ endfunction
+
+ //-----------------------------------------------------------------------
+ // get_bytes -extract num_bytes from tuser
+ //-----------------------------------------------------------------------
+ function automatic [UWIDTH-1:0] get_bytes([UWIDTH-1:0] tuser);
+ logic [UWIDTH-1:0] bytes;
+ begin
+ if (tuser[UWIDTH-2:0] == 0) bytes = BYTES_PER_WORD;
+ else bytes = tuser[UWIDTH-2:0];
+ return bytes;
+ end
+ endfunction
+
+ // Debug state used to determine which sub-case is taken in simulation
+ typedef enum {D_IDLE, D_REACHED_START, D_TRUNCATE, D_LAST, D_NOT_LAST,
+ D_LAST_WO_END, D_LAST_W_END, D_LAST_W_END_BONUS, D_LAST_W_END_PLUS,
+ D_REACHED_END_PLUS} debug_t;
+ debug_t debug = D_IDLE;
+
+ always_ff @(posedge s0.clk) begin
+ if (s0.rst) begin
+ error_bit_old <= 0;
+ end else begin
+ // must hold until bonus completes
+ if (s1.tlast && s1.tvalid && s1.tready && remove_state==ST_BONUS) begin
+ error_bit_old <= 0;
+ // or clear if not going to bonus
+ end else if (s0.tlast && s0.tvalid && s0.tready && next_remove_state!=ST_BONUS) begin
+ error_bit_old <= 0;
+ // but they set based on the input
+ end else if (s0.tvalid && s0.tready) begin
+ error_bit_old <= error_bit;
+ end
+ end
+ end
+
+ assign error_bit = get_error(s0.tuser) || error_bit_old;
+
+ // When truncating we want to hold the last valid word until
+ // the end so we can accumulate any errors that might of occured
+ if (TRUNCATE && START_BYTE==0) begin
+ always_comb reached_start = s0.reached_packet_byte(REM_START-1);
+ end else begin
+ always_comb reached_start = s0.reached_packet_byte(REM_START);
+ end
+
+ // the WRAP case leans forward one word since it bridges to
+ // the next word so it needs to reach end_plus early
+ // REMOVE statemachine
+ always_comb begin : reached_end_comb
+ if (MCASE==MS_WRAP) begin
+ reached_end = s0.reached_packet_byte(REM_END);
+ reached_end_plus = s0.reached_packet_byte(REM_END);
+ end else begin
+ reached_end = s0.reached_packet_byte(REM_END);
+ reached_end_plus = s0.reached_packet_byte(REM_END+BYTES_PER_WORD);
+ end
+ end
+
+ // because s0.tready feeds back and generates a
+ // change event for the entire interface,
+ // it can trigger an infinite loop of assignment
+ // even when nothing is changing. This breaks
+ // the feedback loop.
+ logic s0_tready;
+ always_comb s0.tready = s0_tready;
+
+ // Remove Statemachine
+ always_comb begin : remove_next_state
+ // default assignment of next_state
+ next_remove_state = remove_state;
+ debug = D_IDLE;
+ data_mux_sel = PASS_THRU;
+ s1.tuser = s0.tuser;
+ s1.tlast = s0.tlast;
+ s1.tvalid = s0.tvalid;
+ s0_tready = s1.tready;
+ case (remove_state)
+
+ // *****************************************************
+ // PRE_REMOVE - wait till we reach REM_START
+ // *****************************************************
+ ST_PRE_REMOVE: begin
+ //defaults
+ data_mux_sel = PASS_THRU;
+ s1.tuser = s0.tuser;
+ s1.tlast = s0.tlast;
+ s1.tvalid = s0.tvalid;
+ s0_tready = s1.tready;
+
+ if (reached_start) begin // based only on word count
+ debug = D_REACHED_START;
+
+ // Truncating word end
+ if (TRUNCATE && s0.tlast) begin
+ s1.tlast = 1;
+ data_mux_sel = ONE_WORD;
+ debug = D_TRUNCATE;
+
+ // get number of bytes based on if we had enough to surpass END_BYTE
+ if (START_BYTE == 0) // Exact case
+ s1.tuser = uwrite(error_bit,in_byte_count);
+ else if (in_byte_count < START_BYTE)
+ s1.tuser = uwrite(error_bit,in_byte_count);
+ else
+ s1.tuser = uwrite(error_bit,START_BYTE);
+
+ end else if (TRUNCATE && !s0.tlast) begin
+ s1.tlast = 1;
+ data_mux_sel = ONE_WORD;
+ debug = D_TRUNCATE;
+
+ if (s1.tready && s0.tvalid) begin
+ s1.tlast = 0;
+ s1.tvalid = 0;
+ next_remove_state = ST_TRUNCATE;
+ end
+
+ // packet ends
+ end else if (s0.tlast) begin
+ s1.tlast = 1;
+ data_mux_sel = ONE_WORD;
+ debug = D_LAST;
+ // get number of bytes based on if we had enough to surpass END_BYTE
+ if (in_byte_count < START_BYTE)
+ s1.tuser = uwrite(error_bit,in_byte_count);
+ else if (START_WORD != END_WORD)
+ s1.tuser = uwrite(error_bit,START_BYTE);
+ else if (in_byte_count < END_BYTE+1)
+ s1.tuser = uwrite(error_bit,START_BYTE);
+ else
+ s1.tuser = uwrite(error_bit,in_byte_count - BYTES_REMOVED);
+
+ // if we are on the first word of the removal and have no way to terminate the packet
+ // set error.
+ if ((START_WORD != END_WORD && START_BYTE == 0) || EXACT)
+ s1.tuser[ERROR] = 1;
+
+ // if removal starts at the start of the packet, squelch the packet.
+ if ( (START_WORD != END_WORD && REM_START == 0) ||
+ // also if we don't have enough data to publish 1 byte
+ ((in_byte_count <= END_BYTE+1) && REM_START == 0)) begin
+ s1.tlast = 0;
+ s1.tvalid = 0;
+ end
+
+ end else begin // not the last word
+ debug = D_NOT_LAST;
+
+ s1.tvalid = 0;
+ if (s0.tvalid) begin
+ // we will always need to wait for some more data before
+ // forming the next word if this was not the start of the packet
+ next_remove_state = ST_REMOVING;
+ end
+ end
+ end
+ end //ST_PRE_REMOVE
+
+ // *****************************************************
+ // TRUNCATE - wait for end of packet to put out the
+ // last word (so we can see if error bit asserts)
+ // *****************************************************
+ ST_TRUNCATE: begin
+
+ if (TRUNCATE) begin // Simplify synthesis
+ // get number of bytes based on if we had enough to surpass END_BYTE
+ if (get_bytes(first_tuser) < START_BYTE)
+ s1.tuser = uwrite(error_bit,get_bytes(first_tuser));
+ else
+ s1.tuser = uwrite(error_bit,START_BYTE);
+
+ data_mux_sel = TRUNCATE_DATA;
+ s1.tlast = s0.tlast;
+ s1.tvalid = s0.tlast && s0.tvalid;
+ s0_tready = 1;
+ if (s1.tready && s0.tvalid && s0.tlast) begin
+ next_remove_state = ST_PRE_REMOVE;
+ end
+ end
+ end
+
+ // *****************************************************
+ // REMOVING - burn words until we have data to
+ // start sending again
+ // *****************************************************
+ ST_REMOVING: begin
+ //defaults
+ data_mux_sel = FIRST_SHIFT_DATA;
+ s1.tuser = 0;
+ s1.tlast = 0;
+ s1.tvalid = 0;
+ s0_tready = 1;
+
+ // if we don't reach the end of the removal
+ // it is an error case because we don't
+ // have any valid data to send with the tlast.
+ if (s0.tlast && !reached_end && !reached_end_plus) begin
+ debug = D_LAST_WO_END;
+ s1.tuser = uwrite(1,0);
+ s1.tlast = 1;
+ s1.tvalid = s0.tvalid;
+ s0_tready = s1.tready;
+
+ // started from zero we have pushed
+ // zero data so we can just squelch the packet
+ if (REM_START==0)
+ s1.tvalid = 0;
+
+ if (s1.tready && s0.tvalid) begin
+ next_remove_state = ST_PRE_REMOVE;
+ end
+
+ // end of packet and we have some data to send
+ // but we didn't buffer the extra word of end data yet
+ end else if (s0.tlast && reached_end && !reached_end_plus) begin
+ debug = D_LAST_W_END;
+
+ if (MCASE==MULTI_MIDDLE)
+ data_mux_sel = PREFIRST_SHIFT_DATA;
+ else if (MCASE==MS_START_AT_LSB)
+ data_mux_sel = ONE_WORD;
+ s1.tlast = 1;
+
+ // if we are exact and started from zero we have pushed
+ // zero data so we can just squelch the packet
+ if (EXACT && REM_START==0)
+ s1.tvalid = 0;
+ else
+ s1.tvalid = s0.tvalid;
+
+ s0_tready = s1.tready;
+
+ if (MCASE==MULTI_MIDDLE)
+ if (in_byte_count <= FIRST_BYTE_AFTER)
+ s1.tuser = uwrite(1,0); // not enough data to avoid error
+ else
+ s1.tuser = uwrite(error_bit,in_byte_count + BYTE_CARRY);
+ else if (MCASE==MS_END_AT_MSB)
+ s1.tuser = uwrite(1,0); // not enough data to avoid error
+ else if (in_byte_count <= FIRST_BYTE_AFTER)
+ if (REM_START == 0)
+ s1.tvalid = 0;
+ else
+ s1.tuser = uwrite(1,0); // not enough data to avoid error
+ else
+ s1.tuser = uwrite(error_bit,in_byte_count - FIRST_BYTE_AFTER);
+
+ // if we are exact and have already published some data
+ // that data is unterminated and we have no way to
+ // set a packet end.
+ if (EXACT && REM_START!=0)
+ s1.tuser[ERROR] = 1;
+
+ if (s1.tready && s0.tvalid) begin
+ next_remove_state = ST_PRE_REMOVE;
+ end
+
+ // end of packet and we have some some data to send
+ // and we have more data then we can fit in the
+ // the current word
+ end else if (s0.tlast && reached_end_plus && in_byte_count > BYTE_SHIFT
+ && BYTE_SHIFT != 0) begin
+ debug = D_LAST_W_END_BONUS;
+ s1.tlast = 0;
+ s1.tvalid = s0.tvalid;
+ s0_tready = 0; // don't let a advance
+
+ if (s0.tvalid && s1.tready) begin
+ next_remove_state = ST_BONUS;
+ end
+
+ // end of packet and we have some some data to send
+ // and we were ready to send data anyways
+ end else if(s0.tlast && reached_end_plus) begin
+ debug = D_LAST_W_END_PLUS;
+ s1.tlast = 1;
+ s1.tvalid = s0.tvalid;
+ s0_tready = s1.tready;
+
+ if (EXACT)
+ s1.tuser = uwrite(error_bit,in_byte_count);
+ else begin
+ s1.tuser = uwrite(error_bit,in_byte_count + BYTE_CARRY);
+ end
+
+ if (MCASE==MS_WRAP && in_byte_count <= FIRST_BYTE_AFTER)
+ s1.tuser = uwrite(1,0); // not enough data to avoid error
+
+ if (s1.tready && s0.tvalid) begin
+ next_remove_state = ST_PRE_REMOVE;
+ end
+
+ // we are ready to send the first byte after the shift
+ end else if(!s0.tlast && reached_end_plus) begin
+ debug = D_REACHED_END_PLUS;
+ s1.tlast = 0;
+ s1.tvalid = s0.tvalid;
+ s0_tready = s1.tready;
+ s1.tuser = uwrite(error_bit,BYTES_PER_WORD);
+
+ if (s1.tready && s0.tvalid) begin
+ next_remove_state = ST_POST_REMOVE;
+ end
+
+ end
+ end
+
+ // *****************************************************
+ // POST_REMOVAL waiting for end
+ // *****************************************************
+ ST_POST_REMOVE: begin
+ //defaults
+ data_mux_sel = REM_SHIFT_DATA;
+ s1.tuser = uwrite(error_bit,BYTES_PER_WORD);
+ s1.tlast = 0;
+ s1.tvalid = s0.tvalid;
+ s0_tready = s1.tready;
+ // reached the end, but we have extra bytes to send
+ if (s0.tlast && in_byte_count > BYTE_SHIFT
+ && BYTE_SHIFT != 0) begin
+ s1.tlast = 0;
+ s0_tready = 0; // don't let a advance
+
+ if (s0.tvalid && s1.tready) begin
+ next_remove_state = ST_BONUS;
+ end
+
+ // reached the end, and don't need the bonus state
+ end else if (s0.tlast) begin
+ s1.tlast = 1;
+ s1.tuser = uwrite(error_bit,in_byte_count + BYTES_PER_WORD-BYTE_SHIFT);
+
+ if (s1.tready && s0.tvalid) begin
+ next_remove_state = ST_PRE_REMOVE;
+ end
+
+ end
+ end
+
+ // *****************************************************
+ // BONUS write out any overflow words
+ // *****************************************************
+ ST_BONUS: begin
+ //defaults
+ data_mux_sel = BONUS;
+ s1.tuser = uwrite(error_bit,in_byte_count-BYTE_SHIFT);
+ s1.tlast = 1;
+ s1.tvalid = s0.tvalid;
+ s0_tready = s1.tready;
+
+ if (s1.tready && s0.tvalid) begin
+ next_remove_state = ST_PRE_REMOVE;
+ end
+
+ end
+
+ // We should never get here
+ default: begin
+ next_remove_state = ST_PRE_REMOVE;
+ end
+ endcase
+ end
+
+ always_ff @(posedge s0.clk) begin
+ if (s0.rst) begin
+ remove_state <= ST_PRE_REMOVE;
+ end else begin
+ remove_state <= next_remove_state;
+ end
+ end
+
+ // move from AxiStreamIfc to AxiStreamPacketIf
+ always_comb begin
+ `AXI4S_ASSIGN(o,s1)
+ end
+
+ end
+
+endmodule : axi4s_remove_bytes
diff --git a/fpga/usrp3/lib/axi4s_sv/axi4s_remove_bytes_start.sv b/fpga/usrp3/lib/axi4s_sv/axi4s_remove_bytes_start.sv
new file mode 100644
index 000000000..7c6950ae7
--- /dev/null
+++ b/fpga/usrp3/lib/axi4s_sv/axi4s_remove_bytes_start.sv
@@ -0,0 +1,333 @@
+//
+// Copyright 2020 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module : axi4s_remove_bytes_start
+// Description:
+// Specialized case of the Remove Bytes problem. This
+// sub design is called when REM_START=0 and not EXACT
+//
+// This implementation requires that the user field
+// holds the number of valid bytes in the word, and the MSB of the user field
+// indicates if the MAC had an error.
+//
+// The block will NOT hold off the input if it goes to the BONUS State.
+//
+// Parameters:
+// REM_END - Last byte to remove (Truncate case is invalid)
+//
+
+module axi4s_remove_bytes_start #(
+ REM_END=3
+)(
+ interface i, // AxiStreamIf or AxiStreamPacketIf
+ interface o // AxiStreamIf or AxiStreamPacketIf
+);
+
+ // tUSER - always {error,numbytes}
+ // +1 ,0 to numbytes-1 (0 is a full word)
+ localparam UWIDTH = $clog2(i.BYTES_PER_WORD)+1;
+ localparam ERROR = UWIDTH-1; // MSB is the error bit.
+
+ // END is inclusive so +1
+ localparam BYTES_REMOVED = REM_END+1;
+
+ // how many bytes into the word for start and end point
+ localparam END_BYTE = REM_END % i.BYTES_PER_WORD;
+ localparam END_WORD = REM_END / i.BYTES_PER_WORD;
+
+ // SHIFT_POINT
+ // BYTE_CARY | BYTE_SHIFT //
+ // EXAMPLES (32 bit)
+ // REM_END = 0 3 | 1 i[0] a[3:1]
+ // REM_END = 1 2 | 2 i[1:0] a[3:2]
+ // REM_END = 2 1 | 3 i[2:0] a[3]
+ // REM_END = 3 4 | 0 // EXACT - not valid here
+ // we use BYTE_CARY bytes from a
+ // we use BYTE_SHIFT bytes from i
+ localparam BYTE_SHIFT = BYTES_REMOVED % i.BYTES_PER_WORD;
+ localparam BYTE_CARRY = i.BYTES_PER_WORD - BYTE_SHIFT;
+
+ initial begin
+ assert(END_BYTE != i.BYTES_PER_WORD-1 ) else
+ $fatal(1,"Required to be not EXACT");
+ end
+
+ `include "axi4s.vh"
+
+ AxiStreamPacketIf #(.DATA_WIDTH(i.DATA_WIDTH),.USER_WIDTH(i.USER_WIDTH),
+ .TKEEP(0),.MAX_PACKET_BYTES(i.MAX_PACKET_BYTES))
+ s0(i.clk,i.rst);
+ AxiStreamPacketIf #(.DATA_WIDTH(i.DATA_WIDTH),.USER_WIDTH(i.USER_WIDTH),
+ .TKEEP(0),.MAX_PACKET_BYTES(i.MAX_PACKET_BYTES))
+ s1(i.clk,i.rst);
+ AxiStreamPacketIf #(.DATA_WIDTH(i.DATA_WIDTH),.USER_WIDTH(i.USER_WIDTH),
+ .TKEEP(0),.MAX_PACKET_BYTES(i.MAX_PACKET_BYTES))
+ s2(i.clk,i.rst);
+
+ // move from AxiStreamIfc to AxiStreamPacketIf
+ always_comb begin
+ `AXI4S_ASSIGN(s0,i)
+ end
+
+ //---------------------------------------
+ // remove state machine
+ //---------------------------------------
+ typedef enum {ST_REMOVING, ST_POST_REMOVE} remove_state_t;
+ remove_state_t remove_state = ST_REMOVING;
+ remove_state_t next_remove_state = ST_REMOVING;
+
+
+ logic s0_wr,s1_rd;
+ always_comb s0_wr = s0.tvalid && s0.tready;
+ always_comb s1_rd = s1.tvalid && s1.tready;
+ // FIFO to buffer up 1 previous value. MUST be 1!
+ always_ff @(posedge s1.clk) begin
+ if (s1.rst) begin
+ // upstream
+ s1.tdata <= 0;
+ s1.tuser <= 0;
+ s1.tlast <= 0;
+ s1.tvalid <= 0; // fullness
+ end else begin
+
+ // store data on write
+ if (s0_wr) begin
+ // check that we never attempt
+ // a write that will overflow
+ // Only 2 valid write times
+ // (1) Write while empty(tvalid=0)
+ // (2) Write while reading
+ assert (!s1.tvalid || s1_rd) else
+ $fatal(1,"Overflow write");
+
+ s1.tdata <= s0.tdata;
+ s1.tuser <= s0.tuser;
+ s1.tlast <= s0.tlast;
+ s1.tvalid <= 1; //full
+
+ // if we read without a write
+ // declare it empty!
+ end else if (s1_rd) begin
+ s1.tdata <= 'X;
+ s1.tuser <= 'X;
+ s1.tlast <= 1'bX;
+ s1.tvalid <= 0; //empty
+ end
+
+ end
+ end
+
+ // ready to write when not full, or being read
+ // passes s1_ready back cominatorially
+ always_comb s0.tready = !s1.tvalid || s1_rd;
+
+ //***************** DATA SHIFTING ***********************/
+ function automatic logic [s0.DATA_WIDTH-1-BYTE_SHIFT*8:0] bs_part([s0.DATA_WIDTH-1:0] data);
+ begin
+ return data[s0.DATA_WIDTH-1:BYTE_SHIFT*8];
+ end
+ endfunction
+
+ // Note these should all be static shifts. We don't want to infer a barrel shifter.
+
+ // EXAMPLE XX XX XX
+ // for 8 byte word H0 G0 F0 E0 D0 C0 B0 A0 with START BYTE = 0(A0) END_BYTE = 2(C0) BYTE_SHIFT=3
+ // 1st word would be C1 B1 A1/H0 G0 F0 E0 D0
+ // [23:0] C1 B1 A1 / [63:24] H0 G0 F0 E0 D0
+ // same as shift_data above
+ // EXAMPLE XX XX XX XX XX XX XX XX
+ // for 8 byte word H0 G0 F0 E0 D0 C0 B0 A0
+ // XX XX XX
+ // H1 G1 F1 E1 D1 C1 B1 A1 with START BYTE = 0(A0) END_BYTE = 2(10)(C1) BYTE_SHIFT=3
+ // 1st word would be C2 B2 A2/H1 G1 F1 E1 D1
+ // [23:0] C2 B2 A2 / [63:24] H1 G1 F1 E1 D1
+ // same as shift_data above
+ // NOTE: Entire words are thrown away at start, so no caching required
+ logic [s0.DATA_WIDTH-1:0] shift_data;
+ always_comb shift_data = {s0.tdata,bs_part(s1.tdata)};
+
+ //-----------------------------------------------------------------------
+ // user write function
+ // this module ASSUMES user includes error in the MSB and the rest is the
+ // number of bytes in the word
+ //-----------------------------------------------------------------------
+ function automatic [UWIDTH-1:0] uwrite(error=0,[UWIDTH-2:0] bytes=0);
+ begin
+ return {error,bytes};
+ end
+ endfunction
+
+ //-----------------------------------------------------------------------
+ // get_error -extract error from tuser
+ //-----------------------------------------------------------------------
+ function automatic get_error([UWIDTH-1:0] tuser);
+ begin
+ return tuser[ERROR];
+ end
+ endfunction
+
+ //-----------------------------------------------------------------------
+ // get_bytes -extract num_bytes from tuser
+ //-----------------------------------------------------------------------
+ function automatic [UWIDTH-1:0] get_bytes([UWIDTH-1:0] tuser);
+ logic [UWIDTH-1:0] bytes;
+ begin
+ if (tuser[UWIDTH-2:0] == 0) bytes = s0.BYTES_PER_WORD;
+ else bytes = tuser[UWIDTH-2:0];
+ return bytes;
+ end
+ endfunction
+
+ logic [UWIDTH-1:0] s0_byte_count, s1_byte_count;
+ always_comb s0_byte_count = get_bytes(s0.tuser);
+ always_comb s1_byte_count = get_bytes(s1.tuser);
+
+ logic s1_error_bit, s1_error_bit_old;
+ logic s0_error_bit, s0_error_bit_old;
+
+ always_ff @(posedge s0.clk) begin
+ if (s0.rst) begin
+ s0_error_bit_old <= 0;
+ s1_error_bit_old <= 0;
+ end else begin
+ // clear at the end of the packet
+ if (s1.tlast && s1.tvalid && s1.tready) begin
+ s1_error_bit_old <= 0;
+ // but they set based on the input
+ end else if (s1.tvalid && s1.tready) begin
+ s1_error_bit_old <= s1_error_bit;
+ end
+ // clear at the end of the packet
+ if (s0.tlast && s0.tvalid && s0.tready) begin
+ s0_error_bit_old <= 0;
+ // but they set based on the input
+ end else if (s1.tvalid && s1.tready) begin
+ s0_error_bit_old <= s0_error_bit;
+ end
+
+ end
+ end
+
+ assign s1_error_bit = get_error(s1.tuser) && s1.tvalid || s1_error_bit_old;
+ assign s0_error_bit = get_error(s0.tuser) && s0.tvalid || s0_error_bit_old;
+
+ logic s1_reached_end;
+ always_comb s1_reached_end = s1.reached_packet_byte(REM_END);
+
+ // Remove Statemachine
+ always_comb begin : remove_next_state
+ // default assignment of next_state
+ next_remove_state = remove_state;
+ // In REM_START=0 case, always shift the same ammount.
+ s2.tdata = shift_data;
+ s2.tuser = s1.tuser;
+ s2.tlast = s1.tlast;
+ s2.tvalid = s1.tvalid;
+ s1.tready = s2.tready;
+ case (remove_state)
+ // *****************************************************
+ // REMOVING - burn words until we have data to
+ // start sending again
+ // *****************************************************
+ ST_REMOVING: begin
+ //defaults
+ s2.tuser = 0;
+ s2.tlast = 0;
+ s2.tvalid = 0;
+ s1.tready = 1;
+
+ //reached removal point
+ if (s1.tvalid && s1_reached_end) begin
+
+ // if we didn't get enough bytes to fill the word
+ // squelch the packet
+ if (s1.tlast && s1_byte_count <= BYTE_SHIFT) begin
+ s2.tlast = 1;
+ s2.tvalid = 0;
+ s1.tready = 1;
+ // ending in top half of word
+ end else if (s0.tlast && s0_byte_count <= BYTE_SHIFT) begin
+ s2.tlast = 1;
+ s2.tuser = uwrite(s1_error_bit || s0_error_bit,s0_byte_count + BYTE_CARRY);
+ s2.tvalid = s0.tvalid && s1.tvalid;
+ s2.tlast = 1;
+ s1.tready = s2.tready && s0.tvalid;
+ // ending in bottom half of word
+ end else if (s1.tlast) begin
+ s2.tlast = 1;
+ s2.tuser = uwrite(s1_error_bit,s1_byte_count - BYTE_SHIFT);
+ s2.tvalid = s1.tvalid;
+ s1.tready = s2.tready;
+ // end of removal start sending data!
+ end else begin
+ s2.tuser = uwrite(s1_error_bit || s0_error_bit,s0.BYTES_PER_WORD);
+ s2.tvalid = s0.tvalid && s1.tvalid;
+ s2.tlast = 0;
+ s1.tready = s2.tready && s0.tvalid;
+ if (s2.tready && s0.tvalid && s1.tvalid) begin
+ next_remove_state = ST_POST_REMOVE;
+ end
+ end
+ end
+
+ end
+
+ // *****************************************************
+ // POST_REMOVAL waiting for end
+ // *****************************************************
+ ST_POST_REMOVE: begin
+
+ //defaults
+ s2.tuser = uwrite(s1_error_bit || s0_error_bit,s0.BYTES_PER_WORD);
+ s2.tvalid = s0.tvalid && s1.tvalid;
+ s2.tlast = 0;
+ s1.tready = s2.tready && s0.tvalid;
+
+ // word is {i[BS-1:0],a[MSB:BS]}
+ // 32 bit example
+ // BS1 i[0] : a[3:1] ->
+ // BS2 i[1:0]: a[3:2]
+ // BS3 i[2:0]: a[3]
+ // ending in top half of word
+ if (s0.tlast && s0_byte_count <= BYTE_SHIFT) begin
+ s2.tuser = uwrite(s1_error_bit || s0_error_bit,s0_byte_count + BYTE_CARRY);
+ s2.tvalid = s0.tvalid && s1.tvalid;
+ s2.tlast = 1;
+ if (s2.tready && s0.tvalid && s1.tvalid) begin
+ next_remove_state = ST_REMOVING;
+ end
+ // ending in bottom half of word
+ end else if (s1.tlast) begin
+ s2.tuser = uwrite(s1_error_bit,s1_byte_count - BYTE_SHIFT);
+ s2.tvalid = s1.tvalid;
+ s2.tlast = 1;
+ if (s2.tready && s1.tvalid) begin
+ next_remove_state = ST_REMOVING;
+ end
+ // ending in second half of shift word
+ end
+ end
+
+ // We should never get here
+ default: begin
+ next_remove_state = ST_REMOVING;
+ end
+ endcase
+ end
+
+ always_ff @(posedge s1.clk) begin
+ if (s1.rst) begin
+ remove_state <= ST_REMOVING;
+ end else begin
+ remove_state <= next_remove_state;
+ end
+ end
+
+ // move from AxiStreamPacketIf to AxiStreamIfc
+ always_comb begin
+ `AXI4S_ASSIGN(o,s2)
+ end
+
+endmodule : axi4s_remove_bytes_start
diff --git a/fpga/usrp3/lib/axi4s_sv/axi4s_remove_bytes_tb/Makefile b/fpga/usrp3/lib/axi4s_sv/axi4s_remove_bytes_tb/Makefile
new file mode 100644
index 000000000..e017851ed
--- /dev/null
+++ b/fpga/usrp3/lib/axi4s_sv/axi4s_remove_bytes_tb/Makefile
@@ -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
diff --git a/fpga/usrp3/lib/axi4s_sv/axi4s_remove_bytes_tb/axi4s_remove_bytes_all_tb.sv b/fpga/usrp3/lib/axi4s_sv/axi4s_remove_bytes_tb/axi4s_remove_bytes_all_tb.sv
new file mode 100644
index 000000000..129c5e65e
--- /dev/null
+++ b/fpga/usrp3/lib/axi4s_sv/axi4s_remove_bytes_tb/axi4s_remove_bytes_all_tb.sv
@@ -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
diff --git a/fpga/usrp3/lib/axi4s_sv/axi4s_remove_bytes_tb/axi4s_remove_bytes_tb.sv b/fpga/usrp3/lib/axi4s_sv/axi4s_remove_bytes_tb/axi4s_remove_bytes_tb.sv
new file mode 100644
index 000000000..c7b2587c7
--- /dev/null
+++ b/fpga/usrp3/lib/axi4s_sv/axi4s_remove_bytes_tb/axi4s_remove_bytes_tb.sv
@@ -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
diff --git a/fpga/usrp3/lib/axi4s_sv/axi4s_width_conv.sv b/fpga/usrp3/lib/axi4s_sv/axi4s_width_conv.sv
new file mode 100644
index 000000000..f05f70438
--- /dev/null
+++ b/fpga/usrp3/lib/axi4s_sv/axi4s_width_conv.sv
@@ -0,0 +1,137 @@
+//
+// Copyright 2020 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Description:
+// System Verilog wrapper for axis_width_conv that accepts a AxiStreamIfc
+// with slave_user/master_user interface. This block requires that the
+// bottom bits of user contain the number of bytes in the last word.
+//
+// If TKEEP - tkeep contains byte_enables
+// If USER_TRAILING_BYTES - tuser contains byte_enables
+// Else everything is a full word
+//
+// Parameters:
+// PIPELINE - Add pipelining for timing.
+// SYNC_CLKS - Input and output clock are the same.
+// I_USER_TRAILING_BYTES - input byte_enable is set by user
+// O_USER_TRAILING_BYTES - output byte_enable is set in user
+//
+
+module axi4s_width_conv #(
+ PIPELINE = "NONE",
+ bit I_USER_TRAILING_BYTES = 0,
+ bit O_USER_TRAILING_BYTES = 0,
+ bit SYNC_CLKS = 1
+) (
+ interface i, // AxiStreamIf or AxiStreamPacketIf
+ interface o // AxiStreamIf or AxiStreamPacketIf
+);
+
+ localparam IWIDTH =i.DATA_WIDTH;
+ localparam OWIDTH =o.DATA_WIDTH;
+
+ `include "axi4s.vh"
+ // Parameter Checks
+ initial begin
+ if(i.TKEEP) begin
+ assert (!I_USER_TRAILING_BYTES) else
+ $fatal("I_USER_TRAILING_BYTE set at the same time as TKEEP");
+ assert (!i.TUSER) else
+ $fatal("i.TUSER set- This module does not pass user");
+ end else if(I_USER_TRAILING_BYTES) begin
+ assert (i.USER_WIDTH >= i.TRAILING_WIDTH ) else
+ $fatal("i.USER_WIDTH does not match TRAILING_WIDTH");
+ end else begin
+ assert (!i.TUSER) else
+ $fatal("This module does not pass generic user_data");
+ end
+
+ if(o.TKEEP) begin
+ assert (!O_USER_TRAILING_BYTES) else
+ $fatal("O_USER_TRAILING_BYTE set at the same time as TKEEP");
+ assert (!o.TUSER) else
+ $fatal("O.TUSER set- This module does not pass user");
+ end else if(O_USER_TRAILING_BYTES) begin
+ assert (o.USER_WIDTH >= o.TRAILING_WIDTH) else
+ $fatal("o.USER_WIDTH does not match TRAILING_WIDTH");
+ end else begin
+ assert (!o.TUSER) else
+ $fatal("This module does not pass generic user_data");
+ end
+
+ assert (i.TLAST == 1) else
+ $fatal("i.TLAST not present");
+ assert (o.TLAST == 1) else
+ $fatal("o.TLAST not present");
+ end
+
+ AxiStreamPacketIf #(.DATA_WIDTH(i.DATA_WIDTH),.USER_WIDTH(i.USER_WIDTH),
+ .TDATA(i.TDATA),.TKEEP(i.TKEEP),.TUSER(i.TUSER),.TLAST(i.TLAST),
+ .MAX_PACKET_BYTES(i.MAX_PACKET_BYTES))
+ s0(i.clk,i.rst);
+ AxiStreamPacketIf #(.DATA_WIDTH(o.DATA_WIDTH),.USER_WIDTH(o.USER_WIDTH),
+ .TDATA(o.TDATA),.TKEEP(o.TKEEP),.TUSER(o.TUSER),.TLAST(o.TLAST),
+ .MAX_PACKET_BYTES(o.MAX_PACKET_BYTES))
+ s1(o.clk,o.rst);
+
+ // move from AxiStreamIfc to AxiStreamPacketIf
+ always_comb begin
+ `AXI4S_ASSIGN(s0,i)
+ end
+ // move from AxiStreamPacketIf to AxiStreamIfc
+ always_comb begin
+ `AXI4S_ASSIGN(o,s1)
+ end
+
+ logic [IWIDTH/8-1:0] s0_tkeep;
+
+ if (s0.TKEEP) begin
+ always_comb s0_tkeep = s0.tkeep;
+ end else if (I_USER_TRAILING_BYTES) begin
+ always_comb s0_tkeep = s0.get_trailing_bytes();
+ end else begin
+ always_comb s0_tkeep = '1;
+ end
+
+ logic [OWIDTH/8-1:0] s1_tkeep;
+ logic [15:0] s1_bytes;
+
+ if (s1.TKEEP) begin
+ always_comb s1.tkeep = s1_tkeep;
+ always_comb s1.tuser = 'X;
+ end else if (O_USER_TRAILING_BYTES) begin
+ always_comb s1.tkeep = 'X;
+ always_comb begin : assign_s1_tuser
+ s1.tuser = 0;
+ // MODELSIM_BUG - deleting the s1_bytes assignment causes modelsim failures.
+ s1_bytes = s1.keep2trailing(s1_tkeep);
+ s1.set_trailing_bytes(s1_tkeep);
+ end
+ end else begin
+ always_comb s1.tkeep = 'X;
+ always_comb s1.tuser = 'X;
+ end
+
+ logic s0_ready, s1_valid, s1_last;
+ logic [s1.DATA_WIDTH-1:0] s1_data;
+ always_comb s0.tready = s0_ready;
+ always_comb s1.tvalid = s1_valid;
+ always_comb s1.tlast = s1_last;
+ always_comb s1.tdata = s1_data;
+
+ axis_width_conv #(
+ .IN_WORDS(IWIDTH/8), .OUT_WORDS(OWIDTH/8),
+ .SYNC_CLKS(SYNC_CLKS), .PIPELINE(PIPELINE)
+ ) axis_width_conv (
+ .s_axis_aclk(s0.clk), .s_axis_rst(s0.rst),
+ .s_axis_tdata(s0.tdata), .s_axis_tkeep(s0_tkeep), .s_axis_tlast(s0.tlast),
+ .s_axis_tvalid(s0.tvalid), .s_axis_tready(s0_ready),
+
+ .m_axis_aclk(s1.clk), .m_axis_rst(s1.rst),
+ .m_axis_tdata(s1_data), .m_axis_tkeep(s1_tkeep), .m_axis_tlast(s1_last),
+ .m_axis_tvalid(s1_valid), .m_axis_tready(s1.tready)
+ );
+
+endmodule : axi4s_width_conv