aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/lib/rfnoc/utils
diff options
context:
space:
mode:
authorWade Fife <wade.fife@ettus.com>2021-02-16 19:38:48 -0600
committerWade Fife <wade.fife@ettus.com>2021-11-04 11:04:54 -0500
commita94ea11f00bba2c4227c9ab173eb4b6ef1049bad (patch)
treeb0fcc3dcc26c42a2427b22a74cd6c0b903acb1b9 /fpga/usrp3/lib/rfnoc/utils
parentf6ec1496486f14ce16042062bdddcec38fa316a5 (diff)
downloaduhd-a94ea11f00bba2c4227c9ab173eb4b6ef1049bad.tar.gz
uhd-a94ea11f00bba2c4227c9ab173eb4b6ef1049bad.tar.bz2
uhd-a94ea11f00bba2c4227c9ab173eb4b6ef1049bad.zip
fpga: rfnoc: Add RFNoC CHDR resize module
Diffstat (limited to 'fpga/usrp3/lib/rfnoc/utils')
-rw-r--r--fpga/usrp3/lib/rfnoc/utils/Makefile.srcs3
-rw-r--r--fpga/usrp3/lib/rfnoc/utils/chdr_convert_down.v451
-rw-r--r--fpga/usrp3/lib/rfnoc/utils/chdr_convert_up.v448
-rw-r--r--fpga/usrp3/lib/rfnoc/utils/chdr_resize.v378
4 files changed, 1280 insertions, 0 deletions
diff --git a/fpga/usrp3/lib/rfnoc/utils/Makefile.srcs b/fpga/usrp3/lib/rfnoc/utils/Makefile.srcs
index c8fb9648f..fe4135f17 100644
--- a/fpga/usrp3/lib/rfnoc/utils/Makefile.srcs
+++ b/fpga/usrp3/lib/rfnoc/utils/Makefile.srcs
@@ -10,6 +10,9 @@
RFNOC_UTIL_SRCS = $(abspath $(addprefix $(BASE_DIR)/../lib/rfnoc/utils/, \
chdr_trim_payload.v \
chdr_pad_packet.v \
+chdr_resize.v \
+chdr_convert_up.v \
+chdr_convert_down.v \
context_handler_sync.v \
context_builder.v \
context_parser.v \
diff --git a/fpga/usrp3/lib/rfnoc/utils/chdr_convert_down.v b/fpga/usrp3/lib/rfnoc/utils/chdr_convert_down.v
new file mode 100644
index 000000000..e3322bdda
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/utils/chdr_convert_down.v
@@ -0,0 +1,451 @@
+//
+// Copyright 2021 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: chdr_convert_down
+//
+// Description:
+//
+// Takes a CHDR packet data stream that was generated using a CHDR width
+// (I_CHDR_W) that is wider than the current bus width (DATA_W) and reformats
+// the packet stream to use the CHDR_W equal to that of the current bus width
+// (DATA_W). It does not resize the bus, but rather only changes the CHDR_W
+// of the encoded packets.
+//
+// Packets with different CHDR width have a different maximum number of
+// metadata bytes. This module repacks the the metadata into the new word
+// size, little-endian ordered. If there is too much metadata for the smaller
+// DATA_W packet, then the excess metadata will be discarded.
+//
+// Parameters:
+//
+// I_CHDR_W : CHDR_W for the input data stream on i_chdr. Must be larger than
+// DATA_W.
+// DATA_W : Width of the data bus, and the new CHDR_W for the output data
+// stream on o_chdr.
+// PIPELINE : Indicates whether to add pipeline stages to the input and/or
+// output. This can be: "NONE", "IN", "OUT", or "INOUT".
+
+`default_nettype none
+
+
+module chdr_convert_down #(
+ parameter I_CHDR_W = 512,
+ parameter DATA_W = 64,
+ parameter PIPELINE = "NONE"
+) (
+ input wire clk,
+ input wire rst,
+
+ // Input
+ input wire [DATA_W-1:0] i_chdr_tdata,
+ input wire i_chdr_tlast,
+ input wire i_chdr_tvalid,
+ output wire i_chdr_tready,
+
+ // Output
+ output wire [DATA_W-1:0] o_chdr_tdata,
+ output wire o_chdr_tlast,
+ output wire o_chdr_tvalid,
+ input wire o_chdr_tready
+);
+
+ `include "../core/rfnoc_chdr_utils.vh"
+ `include "../core/rfnoc_chdr_internal_utils.vh"
+
+ // Calculate ceiling(N/D)
+ `define DIV_CEIL(N,D) (((N)+(D)-1)/(D))
+
+
+ //---------------------------------------------------------------------------
+ // Check Parameters
+ //---------------------------------------------------------------------------
+
+ generate
+ if (!(
+ // Must be reducing the CHDR width
+ (I_CHDR_W > DATA_W) &&
+ // CHDR widths must be valid (at least 64 and powers of 2)
+ (I_CHDR_W >= 64) &&
+ (DATA_W >= 64) &&
+ (2**$clog2(I_CHDR_W) == I_CHDR_W) &&
+ (2**$clog2(DATA_W) == DATA_W) &&
+ // I_CHDR_W must be a multiple of DATA_W
+ (I_CHDR_W % DATA_W == 0)
+ )) begin : gen_error
+ ERROR__Invalid_CHDR_or_data_width_parameters();
+ end
+ endgenerate
+
+
+ //---------------------------------------------------------------------------
+ // Input Register
+ //---------------------------------------------------------------------------
+
+ wire [DATA_W-1:0] i_pipe_tdata;
+ wire i_pipe_tlast;
+ wire i_pipe_tvalid;
+ reg i_pipe_tready;
+
+ if (PIPELINE == "IN" || PIPELINE == "INOUT") begin : gen_in_pipeline
+ // Add a pipeline stage
+ axi_fifo_flop2 #(
+ .WIDTH (1 + DATA_W)
+ ) axi_fifo_flop2_i (
+ .clk (clk),
+ .reset (rst),
+ .clear (1'b0),
+ .i_tdata ({i_chdr_tlast, i_chdr_tdata}),
+ .i_tvalid (i_chdr_tvalid),
+ .i_tready (i_chdr_tready),
+ .o_tdata ({i_pipe_tlast, i_pipe_tdata}),
+ .o_tvalid (i_pipe_tvalid),
+ .o_tready (i_pipe_tready),
+ .space (),
+ .occupied ()
+ );
+ end else begin : gen_no_in_pipeline
+ assign i_pipe_tdata = i_chdr_tdata;
+ assign i_pipe_tlast = i_chdr_tlast;
+ assign i_pipe_tvalid = i_chdr_tvalid;
+ assign i_chdr_tready = i_pipe_tready;
+ end
+
+
+ //---------------------------------------------------------------------------
+ // Downsize State Machine
+ //---------------------------------------------------------------------------
+ //
+ // This state machine does the translation from the larger CHDR_W to the
+ // smaller CHDR_W by updating the header and dropping empty words.
+ //
+ //---------------------------------------------------------------------------
+
+ // States
+ localparam [2:0] ST_HDR = 3'd0; // CHDR header
+ localparam [2:0] ST_TS = 3'd1; // CHDR timestamp
+ localparam [2:0] ST_HDR_DROP = 3'd2; // CHDR header, drop unused words
+ localparam [2:0] ST_MDATA = 3'd3; // CHDR metadata words
+ localparam [2:0] ST_MDATA_DROP = 3'd4; // CHDR metadata, drop unused words
+ localparam [2:0] ST_PYLD = 3'd5; // CHDR payload words
+ localparam [2:0] ST_PYLD_DROP = 3'd6; // CHDR payload, drop unused words
+ localparam [2:0] ST_MGMT_PYLD = 3'd7; // CHDR management payload words
+
+ reg [2:0] state = ST_HDR;
+
+ // Determine the number of bits needed to represent the new number of
+ // metadata words, which might be bigger than the allowed value of 31.
+ localparam NUM_MDATA_W = $clog2(31*I_CHDR_W/DATA_W + 1);
+
+ // Number of output words per input word
+ localparam NUM_WORDS = I_CHDR_W/DATA_W;
+
+ // Determine the number of bits needed to represent a counter to track which
+ // CHDR words are valid and which are unused and need to be dropped.
+ localparam COUNT_W = $clog2(NUM_WORDS);
+
+ // Determine the maximum number DATA_W-sized payload words. The maximum
+ // packet size is 2**16-1 bytes, then subtract one word for the smallest
+ // possible header and convert that to a number of whole CHDR words.
+ localparam NUM_PYLD_WORDS = `DIV_CEIL((2**16-1) - (DATA_W/8), DATA_W/8);
+
+ // Determine the number of bits needed to represent a counter to track which
+ // O_DATA_W payload word we are processing.
+ localparam PYLD_COUNT_W = $clog2(NUM_PYLD_WORDS + 1);
+
+ // Header info we need to save
+ reg [ NUM_MDATA_W-1:0] i_num_mdata_reg; // Input packet NumMData in terms of DATA_W words
+ reg [ 4:0] o_num_mdata_reg; // Output packet NumMData to keep
+ reg [ 2:0] pkt_type_reg; // Packet type
+ reg [PYLD_COUNT_W-1:0] pyld_len_reg; // Packet payload length in DATA_W words
+ reg [PYLD_COUNT_W-1:0] mgmt_pyld_len_reg; // Management payload length in DATA_W words
+
+ // Counters (number of DATA_W sized words processed on the input)
+ reg [ NUM_MDATA_W-1:0] mdata_count;
+ reg [PYLD_COUNT_W-1:0] pyld_count;
+ reg [ COUNT_W-1:0] word_count; // Zero based (starts at 0)
+
+ // Shortcuts for CHDR header info
+ wire [ 2:0] pkt_type = chdr_get_pkt_type(i_pipe_tdata[63:0]);
+ wire [15:0] pyld_len_bytes = chdr_calc_payload_length(I_CHDR_W, i_pipe_tdata[63:0]);
+
+ // Calculate the payload length in DATA_W words
+ wire [PYLD_COUNT_W-1:0] pyld_len = `DIV_CEIL(pyld_len_bytes, DATA_W/8);
+
+ // Calculate the payload length of a management packet in words (management
+ // packets have the same number of payload words, regardless of CHDR width).
+ wire [PYLD_COUNT_W-1:0] mgmt_pyld_len =
+ `DIV_CEIL(chdr_calc_payload_length(I_CHDR_W, i_pipe_tdata), I_CHDR_W/8);
+
+ // Calculate NumMData from input packet in terms of DATA_W words
+ wire [NUM_MDATA_W-1:0] i_num_mdata =
+ chdr_get_num_mdata(i_pipe_tdata[63:0]) * (I_CHDR_W/DATA_W);
+ // Calculate NumMData for output packet (limit to max of 31)
+ wire [4:0] o_num_mdata = (i_num_mdata <= 31) ? i_num_mdata : 31;
+
+ // Generate packet headers with updated NumMData and Length fields
+ reg [DATA_W-1:0] new_header;
+ always @(*) begin
+ new_header = i_pipe_tdata;
+ // Update NumMData
+ new_header[63:0] = chdr_set_num_mdata(new_header, o_num_mdata);
+ // Update packet length
+ new_header[63:0] = chdr_update_length(DATA_W, new_header,
+ (pkt_type == CHDR_PKT_TYPE_MGMT) ? mgmt_pyld_len * (DATA_W/8) : pyld_len_bytes);
+ end
+
+ reg [DATA_W-1:0] new_mgmt_header;
+ always @(*) begin
+ // Update the CHDRWidth field in the management header.
+ new_mgmt_header = i_pipe_tdata;
+ new_mgmt_header[63:0] =
+ chdr_mgmt_set_chdr_w(i_pipe_tdata[63:0], chdr_w_to_enum(DATA_W));
+ end
+
+
+ always @(posedge clk) begin
+ if (rst) begin
+ state <= ST_HDR;
+ mdata_count <= 'bX;
+ pyld_count <= 'bX;
+ word_count <= 'bX;
+ pkt_type_reg <= 'bX;
+ pyld_len_reg <= 'bX;
+ mgmt_pyld_len_reg <= 'bX;
+ i_num_mdata_reg <= 'bX;
+ o_num_mdata_reg <= 'bX;
+ end else if (i_pipe_tvalid & i_pipe_tready) begin
+ // Default assignment
+ word_count <= word_count + 1;
+
+ case (state)
+
+ // ST_HDR: CHDR Header
+ ST_HDR: begin
+ mdata_count <= 1; // The first metadata word will be word 1
+ pyld_count <= 1; // The first payload word will be word 1
+ word_count <= 1; // Word 0 is the current word (header)
+ pkt_type_reg <= pkt_type;
+ pyld_len_reg <= pyld_len;
+ mgmt_pyld_len_reg <= mgmt_pyld_len;
+ // Save number of DATA_W words of mdata we expect
+ i_num_mdata_reg <= i_num_mdata;
+ // Save the number of DATA_W words of mdata we can keep
+ o_num_mdata_reg <= o_num_mdata;
+ if (DATA_W == 64) begin
+ if (pkt_type == CHDR_PKT_TYPE_DATA_TS) begin
+ // Next word must be the timestamp
+ state <= ST_TS;
+ end else begin
+ // Next word(s) must be empty, so drop it
+ state <= ST_HDR_DROP;
+ end
+ end else begin
+ // DATA_W >= 128. We should have received the header word and
+ // timestamp (if present) this clock cycle. Since I_CHDR_W >
+ // DATA_W, there must be extra words with the header that we need
+ // to drop.
+ state <= ST_HDR_DROP;
+ end
+ end
+
+ // ST_TS: Timestamp (DATA_W == 64 only)
+ ST_TS: begin
+ if (I_CHDR_W > 128) begin
+ state <= ST_HDR_DROP;
+ end else if (o_num_mdata_reg != 0) begin
+ state <= ST_MDATA;
+ end else begin
+ state <= ST_PYLD;
+ end
+ end
+
+ // ST_HDR_DROP: CHDR header, drop unused words
+ ST_HDR_DROP: begin
+ if (word_count == NUM_WORDS-1) begin
+ if (o_num_mdata_reg != 0) begin
+ state <= ST_MDATA;
+ end else if(pkt_type_reg == CHDR_PKT_TYPE_MGMT) begin
+ state <= ST_MGMT_PYLD;
+ end else begin
+ state <= ST_PYLD;
+ end
+ end
+ end
+
+ // ST_MDATA: Metadata words
+ ST_MDATA: begin
+ mdata_count <= mdata_count + 1;
+ if (mdata_count == o_num_mdata_reg) begin
+ if (mdata_count < i_num_mdata_reg) begin
+ // There are more MDATA words to deal with than we can fit, so we
+ // need to drop the rest.
+ state <= ST_MDATA_DROP;
+ end else if (pkt_type_reg == CHDR_PKT_TYPE_MGMT) begin
+ state <= ST_MGMT_PYLD;
+ end else begin
+ state <= ST_PYLD;
+ end
+ end
+ end
+
+ // ST_MDATA_DROP: Drop excess metadata words
+ ST_MDATA_DROP: begin
+ mdata_count <= mdata_count + 1;
+ if (mdata_count == i_num_mdata_reg) begin
+ if (pkt_type_reg == CHDR_PKT_TYPE_MGMT) begin
+ state <= ST_MGMT_PYLD;
+ end else begin
+ state <= ST_PYLD;
+ end
+ end
+ end
+
+ // ST_PYLD: Payload words
+ ST_PYLD: begin
+ pyld_count <= pyld_count + 1;
+ if (i_pipe_tlast) begin
+ state <= ST_HDR;
+ end else if (pyld_count == pyld_len_reg) begin
+ state <= ST_PYLD_DROP;
+ end
+ end
+
+ // ST_PYLD_DROP: Payload, drop unused words
+ ST_PYLD_DROP: begin
+ // The input packet may have had empty words at the end if the
+ // payload didn't fill the last CHDR word. We remove those here.
+ if (i_pipe_tlast) begin
+ state <= ST_HDR;
+ end
+ end
+
+ // ST_MGMT_PYLD: Management words
+ ST_MGMT_PYLD: begin
+ // Management packets are different from other packet types in that
+ // the payload is not serialized. In the new DATA_W, we'll have empty
+ // words we need to discard. When word_count is zero, that's when we
+ // have a valid word. For all other counts, we want to discard words.
+ if (word_count == 0) begin
+ pyld_count <= pyld_count + 1;
+ end
+ if (i_pipe_tlast) begin
+ state <= ST_HDR;
+ end
+ end
+
+ endcase
+ end
+ end
+
+
+ //-----------------------------
+ // State machine output logic
+ //-----------------------------
+
+ reg [DATA_W-1:0] o_pipe_tdata;
+ reg o_pipe_tlast;
+ reg o_pipe_tvalid;
+ wire o_pipe_tready;
+
+ always @(*) begin
+ case (state)
+ ST_HDR : begin
+ o_pipe_tdata = new_header;
+ o_pipe_tlast = i_pipe_tlast;
+ o_pipe_tvalid = i_pipe_tvalid;
+ i_pipe_tready = o_pipe_tready;
+ end
+ ST_TS : begin
+ o_pipe_tdata = i_pipe_tdata;
+ o_pipe_tlast = i_pipe_tlast;
+ o_pipe_tvalid = i_pipe_tvalid;
+ i_pipe_tready = o_pipe_tready;
+ end
+ ST_HDR_DROP : begin
+ o_pipe_tdata = { DATA_W {1'bX} };
+ o_pipe_tlast = 1'bX;
+ o_pipe_tvalid = 1'b0;
+ i_pipe_tready = 1'b1;
+ end
+ ST_MDATA : begin
+ o_pipe_tdata = i_pipe_tdata;
+ o_pipe_tlast = i_pipe_tlast;
+ o_pipe_tvalid = i_pipe_tvalid;
+ i_pipe_tready = o_pipe_tready;
+ end
+ ST_MDATA_DROP : begin
+ o_pipe_tdata = { DATA_W {1'bX} };
+ o_pipe_tlast = 1'bX;
+ o_pipe_tvalid = 1'b0;
+ i_pipe_tready = 1'b1;
+ end
+ ST_PYLD : begin
+ o_pipe_tdata = i_pipe_tdata;
+ o_pipe_tlast = (pyld_count == pyld_len_reg);
+ o_pipe_tvalid = i_pipe_tvalid;
+ i_pipe_tready = o_pipe_tready;
+ end
+ ST_PYLD_DROP : begin
+ o_pipe_tdata = { DATA_W {1'bX} };
+ o_pipe_tlast = 1'bX;
+ o_pipe_tvalid = 1'b0;
+ i_pipe_tready = 1'b1;
+ end
+ ST_MGMT_PYLD : begin
+ if (word_count == 0) begin
+ o_pipe_tdata = (pyld_count == 1) ? new_mgmt_header : i_pipe_tdata;
+ o_pipe_tlast = (pyld_count == mgmt_pyld_len_reg);
+ o_pipe_tvalid = i_pipe_tvalid;
+ i_pipe_tready = o_pipe_tready;
+ end else begin
+ // Drop unused management payload words
+ o_pipe_tdata = { DATA_W {1'bX} };
+ o_pipe_tlast = 1'bX;
+ o_pipe_tvalid = 1'b0;
+ i_pipe_tready = 1'b1;
+ end
+ end
+ default : begin
+ o_pipe_tdata = { DATA_W {1'bX} };
+ o_pipe_tlast = 1'bX;
+ o_pipe_tvalid = 1'bX;
+ i_pipe_tready = 1'bX;
+ end
+ endcase
+ end
+
+
+ //---------------------------------------------------------------------------
+ // Output Register
+ //---------------------------------------------------------------------------
+
+ if (PIPELINE == "OUT" || PIPELINE == "INOUT") begin : gen_out_pipeline
+ // Add a pipeline stage
+ axi_fifo_flop2 #(
+ .WIDTH (1 + DATA_W)
+ ) axi_fifo_flop2_i (
+ .clk (clk),
+ .reset (rst),
+ .clear (1'b0),
+ .i_tdata ({ o_pipe_tlast, o_pipe_tdata }),
+ .i_tvalid (o_pipe_tvalid),
+ .i_tready (o_pipe_tready),
+ .o_tdata ({ o_chdr_tlast, o_chdr_tdata }),
+ .o_tvalid (o_chdr_tvalid),
+ .o_tready (o_chdr_tready),
+ .space (),
+ .occupied ()
+ );
+ end else begin : gen_no_out_pipeline
+ assign o_chdr_tdata = o_pipe_tdata;
+ assign o_chdr_tlast = o_pipe_tlast;
+ assign o_chdr_tvalid = o_pipe_tvalid;
+ assign o_pipe_tready = o_chdr_tready;
+ end
+
+endmodule
+
+
+`default_nettype wire
diff --git a/fpga/usrp3/lib/rfnoc/utils/chdr_convert_up.v b/fpga/usrp3/lib/rfnoc/utils/chdr_convert_up.v
new file mode 100644
index 000000000..7d7b79a96
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/utils/chdr_convert_up.v
@@ -0,0 +1,448 @@
+//
+// Copyright 2021 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: chdr_convert_up
+//
+// Description:
+//
+// Takes a CHDR packet data stream that was generated using a CHDR width
+// equal to the current bust width (DATA_W) and reformats the packet stream
+// to use a wider width (O_CHDR_W). It does not resize the bus, but rather
+// only changes the CHDR_W of the encoded packets.
+//
+// The metadata might not be a nice multiple of O_CHDR_W sized words. This
+// module repacks the metadata into the new word size, little-endian ordered,
+// and pads the last metadata word with zeros if necessary.
+//
+// Parameters:
+//
+// DATA_W : The width of the data bus and the input CHDR width for the
+// input data stream on i_chdr.
+// O_CHDR_W : CHDR_W for the output data stream on o_chdr. Must be larger
+// than DATA_W.
+// PIPELINE : Indicates whether to add pipeline stages to the input and/or
+// output. This can be: "NONE", "IN", "OUT", or "INOUT".
+//
+
+`default_nettype none
+
+
+module chdr_convert_up #(
+ parameter DATA_W = 64,
+ parameter O_CHDR_W = 512,
+ parameter PIPELINE = "NONE"
+) (
+ input wire clk,
+ input wire rst,
+
+ // Input
+ input wire [DATA_W-1:0] i_chdr_tdata,
+ input wire i_chdr_tlast,
+ input wire i_chdr_tvalid,
+ output wire i_chdr_tready,
+
+ // Output
+ output wire [DATA_W-1:0] o_chdr_tdata,
+ output wire o_chdr_tlast,
+ output wire o_chdr_tvalid,
+ input wire o_chdr_tready
+);
+
+ `include "../core/rfnoc_chdr_utils.vh"
+ `include "../core/rfnoc_chdr_internal_utils.vh"
+
+ // Calculate ceiling(N/D)
+ `define DIV_CEIL(N,D) (((N)+(D)-1)/(D))
+
+
+ //---------------------------------------------------------------------------
+ // Check Parameters
+ //---------------------------------------------------------------------------
+
+ generate
+ if (!(
+ // Must be up-sizing
+ (DATA_W < O_CHDR_W) &&
+ // CHDR widths must be valid (at least 64 and powers of 2)
+ (DATA_W >= 64) &&
+ (O_CHDR_W >= 64) &&
+ (2**$clog2(DATA_W) == DATA_W) &&
+ (2**$clog2(O_CHDR_W) == O_CHDR_W) &&
+ // O_CHDR_W must be a multiple of DATA_W
+ (O_CHDR_W % DATA_W == 0)
+ )) begin : gen_error
+ ERROR__Invalid_CHDR_W_parameters();
+ end
+ endgenerate
+
+
+ //---------------------------------------------------------------------------
+ // Input Register
+ //---------------------------------------------------------------------------
+
+ wire [DATA_W-1:0] i_pipe_tdata;
+ wire i_pipe_tlast;
+ wire i_pipe_tvalid;
+ reg i_pipe_tready;
+
+ if (PIPELINE == "IN" || PIPELINE == "INOUT") begin : gen_in_pipeline
+ // Add a pipeline stage
+ axi_fifo_flop2 #(
+ .WIDTH (1 + DATA_W)
+ ) axi_fifo_flop2_i (
+ .clk (clk),
+ .reset (rst),
+ .clear (1'b0),
+ .i_tdata ({i_chdr_tlast, i_chdr_tdata}),
+ .i_tvalid (i_chdr_tvalid),
+ .i_tready (i_chdr_tready),
+ .o_tdata ({i_pipe_tlast, i_pipe_tdata}),
+ .o_tvalid (i_pipe_tvalid),
+ .o_tready (i_pipe_tready),
+ .space (),
+ .occupied ()
+ );
+ end else begin : gen_no_in_pipeline
+ assign i_pipe_tdata = i_chdr_tdata;
+ assign i_pipe_tlast = i_chdr_tlast;
+ assign i_pipe_tvalid = i_chdr_tvalid;
+ assign i_chdr_tready = i_pipe_tready;
+ end
+
+
+ //---------------------------------------------------------------------------
+ // Up-size State Machine
+ //---------------------------------------------------------------------------
+ //
+ // This state machine does the translation from the smaller CHDR_W to the
+ // larger CHDR_W by updating the header and padding words as needed.
+ //
+ //---------------------------------------------------------------------------
+
+ // States
+ localparam [3:0] ST_HDR = 4'd0; // CHDR header
+ localparam [3:0] ST_TS = 4'd1; // CHDR timestamp
+ localparam [3:0] ST_HDR_PAD = 4'd2; // CHDR header padding
+ localparam [3:0] ST_MDATA = 4'd3; // CHDR metadata words
+ localparam [3:0] ST_MDATA_PAD = 4'd4; // CHDR metadata padding
+ localparam [3:0] ST_PYLD = 4'd5; // CHDR payload words
+ localparam [3:0] ST_MGMT_HDR = 4'd6; // CHDR management header word
+ localparam [3:0] ST_MGMT_PYLD = 4'd7; // CHDR management payload words
+ localparam [3:0] ST_MGMT_PAD = 4'd8; // CHDR management word padding
+ localparam [3:0] ST_LAST_PAD = 4'd9; // Pad the last CHDR word
+
+ reg [3:0] state = ST_HDR;
+
+ // Number of input words per output word
+ localparam NUM_WORDS = O_CHDR_W/DATA_W;
+
+ // Determine the number of bits needed to represent a counter to track
+ // which CHDR words are valid and which are padding.
+ localparam COUNT_W = $clog2(NUM_WORDS);
+
+ // Determine the maximum number DATA_W-sized payload words. The maximum
+ // packet size is 2**16-1 bytes, then subtract one word for the smallest
+ // possible header and convert that to a number of whole CHDR words.
+ localparam NUM_PYLD_WORDS = `DIV_CEIL((2**16-1) - (DATA_W/8), DATA_W/8);
+
+ // Determine the number of bits needed to represent a counter to track which
+ // I_DATA_W payload word we are processing.
+ localparam PYLD_COUNT_W = $clog2(NUM_PYLD_WORDS + 1);
+
+ // Header info we need to save
+ reg [4:0] num_mdata_reg;
+ reg [2:0] pkt_type_reg;
+
+ // Counters (number of DATA_W sized words processed on the input)
+ reg [ 4:0] mdata_count;
+ reg [COUNT_W-1:0] word_count; // Zero based (starts at 0)
+
+ // Shortcuts for CHDR header info
+ wire [2:0] pkt_type = chdr_get_pkt_type(i_pipe_tdata[63:0]);
+ wire [4:0] num_mdata = chdr_get_num_mdata(i_pipe_tdata[63:0]);
+
+ // Calculate payload length in bytes
+ wire [15:0] pyld_len_bytes = chdr_calc_payload_length(DATA_W, i_pipe_tdata[63:0]);
+
+ // Calculate the payload length of a management packet in words (management
+ // packets have the same number of payload words, regardless of CHDR width).
+ wire [PYLD_COUNT_W-1:0] mgmt_pyld_len = `DIV_CEIL(pyld_len_bytes, DATA_W/8);
+
+ // Determine the number of metadata words for the output packet
+ wire [4:0] o_num_mdata = `DIV_CEIL(num_mdata, O_CHDR_W/DATA_W);
+
+ // Generate packet headers with updated NumMData and Length fields
+ reg [DATA_W-1:0] new_header;
+ always @(*) begin
+ // Pass through upper bits unchanged (e.g., timestamp)
+ new_header = i_pipe_tdata;
+ // Update NumMData
+ new_header[63:0] = chdr_set_num_mdata(new_header, o_num_mdata);
+ // Update packet length
+ new_header[63:0] = chdr_update_length(O_CHDR_W, new_header,
+ (pkt_type == CHDR_PKT_TYPE_MGMT) ? mgmt_pyld_len * (O_CHDR_W/8) : pyld_len_bytes);
+ end
+
+ reg [DATA_W-1:0] new_mgmt_header;
+ always @(*) begin
+ // Update the CHDRWidth field in the management header.
+ new_mgmt_header = i_pipe_tdata;
+ new_mgmt_header[63:0] =
+ chdr_mgmt_set_chdr_w(i_pipe_tdata[63:0], chdr_w_to_enum(O_CHDR_W));
+ end
+
+ reg [DATA_W-1:0] o_pipe_tdata;
+ reg o_pipe_tlast;
+ reg o_pipe_tvalid;
+ wire o_pipe_tready;
+
+ always @(posedge clk) begin
+ if (rst) begin
+ state <= ST_HDR;
+ mdata_count <= 'bX;
+ word_count <= 'bX;
+ num_mdata_reg <= 'bX;
+ pkt_type_reg <= 'bX;
+ end else if (o_pipe_tvalid & o_pipe_tready) begin
+ // Default assignment
+ word_count <= word_count + 1;
+
+ case (state)
+
+ // ST_HDR: CHDR Header
+ ST_HDR: begin
+ mdata_count <= 1; // The first metadata word will be word 1
+ word_count <= 1; // Word 0 is the current word (header)
+ pkt_type_reg <= pkt_type;
+ // Save the number of DATA_W sized metadata words
+ num_mdata_reg <= num_mdata;
+ if (DATA_W == 64) begin
+ // When CHDR_W == 64, the timestamp comes after the header.
+ if (pkt_type == CHDR_PKT_TYPE_DATA_TS) begin
+ state <= ST_TS;
+ end else begin
+ // O_CHDR_W must be at least 128, so there must be at least one
+ // word of header padding.
+ state <= ST_HDR_PAD;
+ end
+ end else begin
+ // If DATA_W > 64 then O_CHDR_W must be at least 256, so we know
+ // there must be some header padding needed.
+ state <= ST_HDR_PAD;
+ end
+ end
+
+ // ST_TS: Timestamp (DATA_W == 64 only)
+ ST_TS: begin
+ if (O_CHDR_W > 128) begin
+ state <= ST_HDR_PAD;
+ end else begin
+ if (num_mdata_reg != 0) begin
+ state <= ST_MDATA;
+ end else begin
+ state <= ST_PYLD;
+ end
+ end
+ end
+
+ // ST_HDR_PAD: CHDR header padding to fill out the last O_CHDR_W
+ ST_HDR_PAD: begin
+ if (word_count == NUM_WORDS-1) begin
+ if (num_mdata_reg != 0) begin
+ state <= ST_MDATA;
+ end else if (pkt_type_reg == CHDR_PKT_TYPE_MGMT) begin
+ state <= ST_MGMT_HDR;
+ end else begin
+ state <= ST_PYLD;
+ end
+ end
+ end
+
+ // ST_MDATA: Metadata words
+ ST_MDATA: begin
+ mdata_count <= mdata_count + 1;
+ if (mdata_count == num_mdata_reg) begin
+ // If we've input a multiple of O_CHDR_W, then we're done with
+ // metadata. Otherwise, we need to add some padding words.
+ if (word_count == NUM_WORDS-1) begin
+ if (pkt_type_reg == CHDR_PKT_TYPE_MGMT) begin
+ state <= ST_MGMT_HDR;
+ end else begin
+ state <= ST_PYLD;
+ end
+ end else begin
+ state <= ST_MDATA_PAD;
+ end
+ end
+ end
+
+ // ST_MDATA_PAD: Add metadata padding to fill out the last O_CHDR_W
+ ST_MDATA_PAD: begin
+ if (word_count == NUM_WORDS-1) begin
+ if (pkt_type_reg == CHDR_PKT_TYPE_MGMT) begin
+ state <= ST_MGMT_HDR;
+ end else begin
+ state <= ST_PYLD;
+ end
+ end
+ end
+
+ // ST_PYLD: Payload words
+ ST_PYLD: begin
+ if (i_pipe_tlast) begin
+ // We don't pad data words because unused bytes are not sent or
+ // expected on the transport.
+ state <= ST_HDR;
+ end
+ end
+
+ // ST_MGMT_HDR: Management header
+ ST_MGMT_HDR: begin
+ // Management packets are different from other packet types in that
+ // the payload is not serialized. So we need to pad each word to make
+ // it a full O_CHDR_W size.
+ if (i_pipe_tlast) begin
+ state <= ST_LAST_PAD;
+ end else begin
+ state <= ST_MGMT_PAD;
+ end
+ end
+
+ // ST_MGMT_PYLD: Management operation words
+ ST_MGMT_PYLD: begin
+ if (i_pipe_tlast) begin
+ state <= ST_LAST_PAD;
+ end else begin
+ state <= ST_MGMT_PAD;
+ end
+ end
+
+ // ST_MGMT_PAD: Management word padding
+ ST_MGMT_PAD: begin
+ if (word_count == NUM_WORDS-1) begin
+ state <= ST_MGMT_PYLD;
+ end
+ end
+
+ // ST_LAST_PAD: Pad the last word so output is a multiple of O_CHDR_W
+ ST_LAST_PAD : begin
+ if (word_count == NUM_WORDS-1) begin
+ state <= ST_HDR;
+ end
+ end
+
+ endcase
+ end
+ end
+
+
+ //-----------------------------
+ // State machine output logic
+ //-----------------------------
+
+ always @(*) begin
+ case (state)
+ ST_HDR : begin
+ o_pipe_tdata = new_header;
+ o_pipe_tvalid = i_pipe_tvalid;
+ o_pipe_tlast = i_pipe_tlast;
+ i_pipe_tready = o_pipe_tready;
+ end
+ ST_TS : begin
+ o_pipe_tdata = i_pipe_tdata;
+ o_pipe_tvalid = i_pipe_tvalid;
+ o_pipe_tlast = i_pipe_tlast;
+ i_pipe_tready = o_pipe_tready;
+ end
+ ST_HDR_PAD : begin
+ o_pipe_tdata = { DATA_W {1'b0} };
+ o_pipe_tvalid = 1'b1;
+ o_pipe_tlast = 1'b0;
+ i_pipe_tready = 1'b0;
+ end
+ ST_MDATA : begin
+ o_pipe_tdata = i_pipe_tdata;
+ o_pipe_tvalid = i_pipe_tvalid;
+ o_pipe_tlast = 1'b0;
+ i_pipe_tready = o_pipe_tready;
+ end
+ ST_MDATA_PAD : begin
+ o_pipe_tdata = { DATA_W {1'b0} };
+ o_pipe_tvalid = 1'b1;
+ o_pipe_tlast = 1'b0;
+ i_pipe_tready = 1'b0;
+ end
+ ST_PYLD : begin
+ o_pipe_tdata = i_pipe_tdata;
+ o_pipe_tvalid = i_pipe_tvalid;
+ o_pipe_tlast = i_pipe_tlast;
+ i_pipe_tready = o_pipe_tready;
+ end
+ ST_MGMT_HDR : begin
+ o_pipe_tdata = new_mgmt_header;
+ o_pipe_tvalid = i_pipe_tvalid;
+ o_pipe_tlast = 1'b0;
+ i_pipe_tready = o_pipe_tready;
+ end
+ ST_MGMT_PYLD : begin
+ o_pipe_tdata = i_pipe_tdata;
+ o_pipe_tvalid = i_pipe_tvalid;
+ o_pipe_tlast = 1'b0;
+ i_pipe_tready = o_pipe_tready;
+ end
+ ST_MGMT_PAD : begin
+ o_pipe_tdata = { DATA_W {1'b0} };
+ o_pipe_tvalid = 1'b1;
+ o_pipe_tlast = 1'b0;
+ i_pipe_tready = 1'b0;
+ end
+ ST_LAST_PAD : begin
+ o_pipe_tdata = { DATA_W {1'b0} };
+ o_pipe_tvalid = 1'b1;
+ o_pipe_tlast = (word_count == NUM_WORDS-1);
+ i_pipe_tready = 1'b0;
+ end
+ default : begin
+ o_pipe_tdata = 'bX;
+ o_pipe_tvalid = 1'bX;
+ o_pipe_tlast = 1'bX;
+ i_pipe_tready = 1'bX;
+ end
+ endcase
+ end
+
+
+ //---------------------------------------------------------------------------
+ // Output Register
+ //---------------------------------------------------------------------------
+
+ if (PIPELINE == "OUT" || PIPELINE == "INOUT") begin : gen_out_pipeline
+ // Add a pipeline stage
+ axi_fifo_flop2 #(
+ .WIDTH (1 + DATA_W)
+ ) axi_fifo_flop2_i (
+ .clk (clk),
+ .reset (rst),
+ .clear (1'b0),
+ .i_tdata ({ o_pipe_tlast, o_pipe_tdata }),
+ .i_tvalid (o_pipe_tvalid),
+ .i_tready (o_pipe_tready),
+ .o_tdata ({ o_chdr_tlast, o_chdr_tdata }),
+ .o_tvalid (o_chdr_tvalid),
+ .o_tready (o_chdr_tready),
+ .space (),
+ .occupied ()
+ );
+ end else begin : gen_no_out_pipeline
+ assign o_chdr_tdata = o_pipe_tdata;
+ assign o_chdr_tlast = o_pipe_tlast;
+ assign o_chdr_tvalid = o_pipe_tvalid;
+ assign o_pipe_tready = o_chdr_tready;
+ end
+
+endmodule
+
+
+`default_nettype wire
diff --git a/fpga/usrp3/lib/rfnoc/utils/chdr_resize.v b/fpga/usrp3/lib/rfnoc/utils/chdr_resize.v
new file mode 100644
index 000000000..888d196f6
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/utils/chdr_resize.v
@@ -0,0 +1,378 @@
+//
+// Copyright 2021 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: chdr_resize
+//
+// Description:
+//
+// Takes a CHDR packet data stream and converts it from one CHDR width to a
+// different CHDR width. It can also do CHDR width conversion without
+// changing the bus width, if the bus width is the same size as the smaller
+// CHDR width.
+//
+// For example, to convert from a 64-bit CHDR_W to a 256-bit CHDR_W, you
+// would set I_CHDR_W to 64 and O_CHDR_W to 256 (by default, I_DATA_W will be
+// set to 64 and O_DATA_W will be set to 256).
+//
+// But you could also convert from 64-bit CHDR to 256-bit CHDR while keeping
+// the bus width at 64 bits. In this case you would set I_CHDR_W to 64 and
+// O_CHDR_W to 256, but set both I_DATA_W and O_DATA_W to 64.
+//
+// There are some restrictions, including the requirement that I_CHDR_W ==
+// I_DATA_W or O_CHDR_W == O_DATA_W, and that MIN(I_DATA_W, O_DATA_W) ==
+// MIN(I_CHDR_W, O_CHDR_W). Basically, it can't do CHDR width conversion
+// where the smaller CHDR width is smaller than the bus width(s). For
+// example, you could not do conversion from 64-bit to 256-bit CHDR with
+// input and output bus widths of 256.
+//
+// TUSER is supported, but is not resized. TUSER is sampled along with the
+// first word of the input packet and is assumed to be the same for the
+// duration of the packet.
+//
+// Also, note that packets with different CHDR_W have a different maximum
+// number of metadata bytes. This module repacks the metadata in
+// little-endian order in the new word size. If there is too much metadata
+// for a smaller CHDR_W packet, the extra data will be discarded.
+//
+// Parameters:
+//
+// I_CHDR_W : CHDR_W for the input data stream on i_chdr.
+// O_CHDR_W : CHDR_W for the output data stream on o_chdr.
+// I_DATA_W : Bus width for i_chdr_tdata.
+// O_DATA_W : Bus width for o_chdr_tdata.
+// USER_W : Width for i_chdr_tuser and o_chdr_tuser.
+// PIPELINE : Indicates whether to add pipeline stages to the input and/or
+// output. This can be: "NONE", "IN", "OUT", or "INOUT".
+//
+
+`default_nettype none
+
+
+module chdr_resize #(
+ parameter I_CHDR_W = 64,
+ parameter O_CHDR_W = 512,
+ parameter I_DATA_W = I_CHDR_W,
+ parameter O_DATA_W = O_CHDR_W,
+ parameter USER_W = 1,
+ parameter PIPELINE = "NONE"
+) (
+ input wire clk,
+ input wire rst,
+
+ // Input
+ input wire [I_DATA_W-1:0] i_chdr_tdata,
+ input wire [ USER_W-1:0] i_chdr_tuser,
+ input wire i_chdr_tlast,
+ input wire i_chdr_tvalid,
+ output wire i_chdr_tready,
+
+ // Input
+ output wire [O_DATA_W-1:0] o_chdr_tdata,
+ output wire [ USER_W-1:0] o_chdr_tuser,
+ output wire o_chdr_tlast,
+ output wire o_chdr_tvalid,
+ input wire o_chdr_tready
+);
+
+ `define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
+
+ // Determine the bus width of the CHDR converter, which is always the smaller
+ // bus width of the input and output.
+ localparam CONVERT_W = `MIN(I_DATA_W, O_DATA_W);
+ // Determine if we need the bus down-sizer
+ localparam DO_DOWNSIZE = (I_DATA_W > O_DATA_W);
+ // Determine if we need the CHDR width converter
+ localparam DO_CONVERT = (I_CHDR_W != O_CHDR_W);
+ // Determine if we need the bus up-sizer
+ localparam DO_UPSIZE = (I_DATA_W < O_DATA_W);
+
+ // Determine the pipeline settings. We want pipeline stages on the input
+ // and/or output, depending on the PIPELINE parameter, as well as between the
+ // up-sizer and converter, and between the converter and down-sizer, any of
+ // which may or may not be present. We don't, however, want back-to-back
+ // pipeline stages (e.g., on the output of the down-sizer and the input to
+ // the converter). If both an up/down-sizer and converter are used, the
+ // settings below will turn on the adjacent pipeline stage in the converter
+ // and turn off the corresponding pipeline stage in the up/down-sizer.
+ localparam DOWNSIZE_PIPELINE =
+ (PIPELINE == "IN" && DO_CONVERT) ? "IN" :
+ (PIPELINE == "IN" && !DO_CONVERT) ? "IN" :
+ (PIPELINE == "INOUT" && DO_CONVERT) ? "IN" :
+ (PIPELINE == "INOUT" && !DO_CONVERT) ? "INOUT" :
+ (PIPELINE == "OUT" && DO_CONVERT) ? "NONE" :
+ (PIPELINE == "OUT" && !DO_CONVERT) ? "OUT" :
+ "NONE" ;
+ localparam CONVERT_PIPELINE =
+ (PIPELINE == "IN" && DO_DOWNSIZE) ? "IN" :
+ (PIPELINE == "IN" && DO_UPSIZE ) ? "INOUT" :
+ (PIPELINE == "IN" /* neither */) ? "IN" :
+ (PIPELINE == "INOUT" && DO_DOWNSIZE) ? "INOUT" :
+ (PIPELINE == "INOUT" && DO_UPSIZE ) ? "INOUT" :
+ (PIPELINE == "INOUT" /* neither */) ? "INOUT" :
+ (PIPELINE == "OUT" && DO_DOWNSIZE) ? "INOUT" :
+ (PIPELINE == "OUT" && DO_UPSIZE ) ? "OUT" :
+ (PIPELINE == "OUT" /* neither */) ? "OUT" :
+ "NONE" ;
+ localparam UPSIZE_PIPELINE =
+ (PIPELINE == "IN" && DO_CONVERT) ? "NONE" :
+ (PIPELINE == "IN" && !DO_CONVERT) ? "IN" :
+ (PIPELINE == "INOUT" && DO_CONVERT) ? "OUT" :
+ (PIPELINE == "INOUT" && !DO_CONVERT) ? "INOUT" :
+ (PIPELINE == "OUT" && DO_CONVERT) ? "OUT" :
+ (PIPELINE == "OUT" && !DO_CONVERT) ? "OUT" :
+ "NONE" ;
+
+
+ generate
+
+ //-------------------------------------------------------------------------
+ // Check Parameters
+ //-------------------------------------------------------------------------
+
+ if (!(
+ // All widths must be valid CHDR widths (at least 64 and powers of 2)
+ (2**$clog2(I_CHDR_W) == I_CHDR_W) &&
+ (2**$clog2(O_CHDR_W) == O_CHDR_W) &&
+ (2**$clog2(I_DATA_W) == I_DATA_W) &&
+ (2**$clog2(O_DATA_W) == O_DATA_W) &&
+ (I_CHDR_W >= 64) &&
+ (O_CHDR_W >= 64) &&
+ (I_DATA_W >= 64) &&
+ (O_DATA_W >= 64) &&
+ // The converter width must match the smaller bus width. It doesn't work
+ // on buses wider than the CHDR width.
+ (CONVERT_W == `MIN(I_CHDR_W, O_CHDR_W))
+ )) begin : gen_error
+ ERROR__Invalid_CHDR_or_data_width_parameters();
+ end
+
+
+ //-------------------------------------------------------------------------
+ // TUSER Data Path
+ //-------------------------------------------------------------------------
+ //
+ // Sample TUSER at the beginning of the input packet and output it for the
+ // duration of the output packet.
+ //
+ //-------------------------------------------------------------------------
+
+ if (DO_DOWNSIZE || DO_UPSIZE || DO_CONVERT || PIPELINE == "INOUT") begin : gen_tuser_buffer
+ if (!DO_DOWNSIZE && !DO_UPSIZE && DO_CONVERT && PIPELINE == "NONE") begin : gen_tuser_reg
+ // In this case, there's a combinatorial path from i_chdr to o_chdr, so
+ // we can't use a FIFO to buffer TUSER.
+
+ // Track start of packet on o_chdr
+ reg o_chdr_sop = 1;
+ always @(posedge clk) begin
+ if (rst) begin
+ o_chdr_sop <= 1;
+ end else if (o_chdr_tvalid && o_chdr_tready) begin
+ o_chdr_sop <= o_chdr_tlast;
+ end
+ end
+
+ reg [USER_W-1:0] o_tuser_reg;
+ always @(posedge clk) begin
+ if (rst) begin
+ o_tuser_reg <= {USER_W{1'bX}};
+ end else if (o_chdr_tvalid && o_chdr_tready && o_chdr_sop) begin
+ o_tuser_reg <= i_chdr_tuser;
+ end
+ end
+
+ // Pass through TUSER for first word in the packet, then use a holding
+ // register for the rest of the packet.
+ assign o_chdr_tuser = (o_chdr_sop) ? i_chdr_tuser : o_tuser_reg;
+
+ end else begin : gen_tuser_fifo
+ // In this case we use a FIFO to buffer TUSER.
+
+ // Track start of packet on i_chdr
+ reg i_chdr_sop = 1;
+ always @(posedge clk) begin
+ if (rst) begin
+ i_chdr_sop <= 1;
+ end else if (i_chdr_tvalid && i_chdr_tready) begin
+ i_chdr_sop <= i_chdr_tlast;
+ end
+ end
+
+ axi_fifo_short #(
+ .WIDTH (USER_W)
+ ) axi_fifo_short_i (
+ .clk (clk),
+ .reset (rst),
+ .clear (1'b0),
+ .i_tdata (i_chdr_tuser),
+ .i_tvalid (i_chdr_tvalid && i_chdr_tready && i_chdr_sop),
+ .i_tready (),
+ .o_tdata (o_chdr_tuser),
+ .o_tvalid (),
+ .o_tready (o_chdr_tready && o_chdr_tvalid && o_chdr_tlast),
+ .space (),
+ .occupied ()
+ );
+ end
+ end else begin : gen_tuser_pass_through
+ // In this case there's no logic on the data path, so we can pass TUSER
+ // through directly.
+ assign o_chdr_tuser = i_chdr_tuser;
+ end
+
+
+ //-------------------------------------------------------------------------
+ // Down-Size Input Bus Width
+ //-------------------------------------------------------------------------
+
+ wire [CONVERT_W-1:0] resized_tdata;
+ wire resized_tlast;
+ wire resized_tvalid;
+ wire resized_tready;
+
+ if (DO_DOWNSIZE) begin : gen_bus_downsize
+ axis_width_conv #(
+ .WORD_W (CONVERT_W),
+ .IN_WORDS (I_DATA_W / CONVERT_W),
+ .OUT_WORDS (1),
+ .SYNC_CLKS (1),
+ .PIPELINE (DOWNSIZE_PIPELINE)
+ ) axis_width_conv_i (
+ .s_axis_aclk (clk),
+ .s_axis_rst (rst),
+ .s_axis_tdata (i_chdr_tdata),
+ .s_axis_tkeep ({(I_DATA_W / CONVERT_W){1'b1}}),
+ .s_axis_tlast (i_chdr_tlast),
+ .s_axis_tvalid (i_chdr_tvalid),
+ .s_axis_tready (i_chdr_tready),
+ .m_axis_aclk (clk),
+ .m_axis_rst (rst),
+ .m_axis_tdata (resized_tdata),
+ .m_axis_tkeep (),
+ .m_axis_tlast (resized_tlast),
+ .m_axis_tvalid (resized_tvalid),
+ .m_axis_tready (resized_tready)
+ );
+ end else begin : gen_no_bus_downsize
+ assign resized_tdata = i_chdr_tdata;
+ assign resized_tlast = i_chdr_tlast;
+ assign resized_tvalid = i_chdr_tvalid;
+ assign i_chdr_tready = resized_tready;
+ end
+
+
+ //-------------------------------------------------------------------------
+ // CHDR Width Protocol Conversion
+ //-------------------------------------------------------------------------
+
+ wire [CONVERT_W-1:0] converted_tdata;
+ wire converted_tlast;
+ wire converted_tvalid;
+ wire converted_tready;
+
+ if (DO_CONVERT) begin : gen_convert
+ if (I_CHDR_W > O_CHDR_W) begin : gen_chdr_convert_down
+ chdr_convert_down #(
+ .I_CHDR_W (I_CHDR_W),
+ .DATA_W (CONVERT_W),
+ .PIPELINE (CONVERT_PIPELINE)
+ ) chdr_convert_down_i (
+ .clk (clk),
+ .rst (rst),
+ .i_chdr_tdata (resized_tdata),
+ .i_chdr_tlast (resized_tlast),
+ .i_chdr_tvalid (resized_tvalid),
+ .i_chdr_tready (resized_tready),
+ .o_chdr_tdata (o_chdr_tdata),
+ .o_chdr_tlast (o_chdr_tlast),
+ .o_chdr_tvalid (o_chdr_tvalid),
+ .o_chdr_tready (o_chdr_tready)
+ );
+ end else if (I_CHDR_W < O_CHDR_W) begin : gen_chdr_convert_up
+ chdr_convert_up #(
+ .DATA_W (CONVERT_W),
+ .O_CHDR_W (O_CHDR_W),
+ .PIPELINE (PIPELINE)
+ ) chdr_convert_up_i (
+ .clk (clk),
+ .rst (rst),
+ .i_chdr_tdata (resized_tdata),
+ .i_chdr_tlast (resized_tlast),
+ .i_chdr_tvalid (resized_tvalid),
+ .i_chdr_tready (resized_tready),
+ .o_chdr_tdata (converted_tdata),
+ .o_chdr_tlast (converted_tlast),
+ .o_chdr_tvalid (converted_tvalid),
+ .o_chdr_tready (converted_tready)
+ );
+ end
+ end else begin : gen_no_convert
+ if (PIPELINE == "INOUT" && !DO_DOWNSIZE && !DO_UPSIZE) begin : gen_pipeline
+ // In this case there's no conversion or up-size/down-size, so we're
+ // just passing the data through unchanged. However, if PIPELINE is set
+ // to INOUT then we should have a pipeline stage, so we add that here.
+ axi_fifo_flop2 #(
+ .WIDTH (1 + CONVERT_W)
+ ) axi_fifo_flop2_i (
+ .clk (clk),
+ .reset (rst),
+ .clear (1'b0),
+ .i_tdata ({ resized_tlast, resized_tdata }),
+ .i_tvalid (resized_tvalid),
+ .i_tready (resized_tready),
+ .o_tdata ({ converted_tlast, converted_tdata }),
+ .o_tvalid (converted_tvalid),
+ .o_tready (converted_tready),
+ .space (),
+ .occupied ()
+ );
+ end else begin : gen_convert_bypass
+ assign converted_tdata = resized_tdata;
+ assign converted_tlast = resized_tlast;
+ assign converted_tvalid = resized_tvalid;
+ assign resized_tready = converted_tready;
+ end
+ end
+
+
+ //-------------------------------------------------------------------------
+ // Up-Size Output Bus Width
+ //-------------------------------------------------------------------------
+
+ if (DO_UPSIZE) begin : gen_bus_upsize
+ axis_width_conv #(
+ .WORD_W (CONVERT_W),
+ .IN_WORDS (1),
+ .OUT_WORDS (O_DATA_W / CONVERT_W),
+ .SYNC_CLKS (1),
+ .PIPELINE (UPSIZE_PIPELINE)
+ ) axis_width_conv_i (
+ .s_axis_aclk (clk),
+ .s_axis_rst (rst),
+ .s_axis_tdata (converted_tdata),
+ .s_axis_tkeep (1'b1),
+ .s_axis_tlast (converted_tlast),
+ .s_axis_tvalid (converted_tvalid),
+ .s_axis_tready (converted_tready),
+ .m_axis_aclk (clk),
+ .m_axis_rst (rst),
+ .m_axis_tdata (o_chdr_tdata),
+ .m_axis_tkeep (),
+ .m_axis_tlast (o_chdr_tlast),
+ .m_axis_tvalid (o_chdr_tvalid),
+ .m_axis_tready (o_chdr_tready)
+ );
+ end else begin : gen_no_bus_upsize
+ assign o_chdr_tdata = converted_tdata;
+ assign o_chdr_tlast = converted_tlast;
+ assign o_chdr_tvalid = converted_tvalid;
+ assign converted_tready = o_chdr_tready;
+ end
+
+ endgenerate
+
+endmodule
+
+
+`default_nettype wire