diff options
author | Wade Fife <wade.fife@ettus.com> | 2021-02-16 19:38:48 -0600 |
---|---|---|
committer | Wade Fife <wade.fife@ettus.com> | 2021-11-04 11:04:54 -0500 |
commit | a94ea11f00bba2c4227c9ab173eb4b6ef1049bad (patch) | |
tree | b0fcc3dcc26c42a2427b22a74cd6c0b903acb1b9 /fpga/usrp3/lib/rfnoc/utils/chdr_resize.v | |
parent | f6ec1496486f14ce16042062bdddcec38fa316a5 (diff) | |
download | uhd-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/chdr_resize.v')
-rw-r--r-- | fpga/usrp3/lib/rfnoc/utils/chdr_resize.v | 378 |
1 files changed, 378 insertions, 0 deletions
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 |