aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/lib/rfnoc/xport
diff options
context:
space:
mode:
Diffstat (limited to 'fpga/usrp3/lib/rfnoc/xport')
-rw-r--r--fpga/usrp3/lib/rfnoc/xport/Makefile.srcs17
-rw-r--r--fpga/usrp3/lib/rfnoc/xport/chdr_xport_adapter_generic.v397
-rw-r--r--fpga/usrp3/lib/rfnoc/xport/eth_interface.v252
-rw-r--r--fpga/usrp3/lib/rfnoc/xport/eth_internal.v433
-rw-r--r--fpga/usrp3/lib/rfnoc/xport/eth_ipv4_chdr64_adapter.v397
-rw-r--r--fpga/usrp3/lib/rfnoc/xport/eth_ipv4_chdr64_dispatch.v472
-rw-r--r--fpga/usrp3/lib/rfnoc/xport/liberio_chdr64_adapter.v120
-rw-r--r--fpga/usrp3/lib/rfnoc/xport/rfnoc_xport_types.vh11
8 files changed, 2099 insertions, 0 deletions
diff --git a/fpga/usrp3/lib/rfnoc/xport/Makefile.srcs b/fpga/usrp3/lib/rfnoc/xport/Makefile.srcs
new file mode 100644
index 000000000..12582750b
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/xport/Makefile.srcs
@@ -0,0 +1,17 @@
+#
+# Copyright 2018 Ettus Research, A National Instruments Company
+#
+# SPDX-License-Identifier: LGPL-3.0-or-later
+#
+
+##################################################
+# RFNoC Utility Sources
+##################################################
+RFNOC_XPORT_SRCS = $(abspath $(addprefix $(BASE_DIR)/../lib/rfnoc/xport/, \
+chdr_xport_adapter_generic.v \
+eth_ipv4_chdr64_adapter.v \
+eth_ipv4_chdr64_dispatch.v \
+eth_interface.v \
+eth_internal.v \
+liberio_chdr64_adapter.v \
+))
diff --git a/fpga/usrp3/lib/rfnoc/xport/chdr_xport_adapter_generic.v b/fpga/usrp3/lib/rfnoc/xport/chdr_xport_adapter_generic.v
new file mode 100644
index 000000000..6de298530
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/xport/chdr_xport_adapter_generic.v
@@ -0,0 +1,397 @@
+//
+// Copyright 2019 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: chdr_xport_adapter_generic
+// Description: A generic transport adapter module that can be used in
+// a veriety of transports. It does the following:
+// - Exposes a configuration port for mgmt packets to configure the node
+// - Implements a return-address map for packets with metadata other than
+// the CHDR. Additional metadata can be passed as a tuser to this module
+// which will store it in a map indexed by the SrcEPID in a management
+// packet. For all returning packets, the metadata will be looked up in
+// the map and attached as the outgoing tuser.
+// - Implements a loopback path for node-info discovery
+// - Converts data stream to/from "RFNoC Network Order" (64-bit-Big-Endian)
+//
+// Parameters:
+// - PROTOVER: RFNoC protocol version {8'd<major>, 8'd<minor>}
+// - CHDR_W: Width of the CHDR bus in bits
+// - USER_W: Width of the tuser bus in bits
+// - TBL_SIZE: Log2 of the depth of the routing table
+// - NODE_TYPE: The node type to return for a node-info discovery
+// - NODE_INST: The node type to return for a node-info discovery
+//
+// Signals:
+// - device_id : The ID of the device that has instantiated this module
+// - s_axis_xport_*: The input CHDR stream from the transport (plus tuser metadata)
+// - m_axis_xport_*: The output CHDR stream to transport (plus tuser metadata)
+// - s_axis_rfnoc_*: The input CHDR stream from the rfnoc infrastructure
+// - m_axis_rfnoc_*: The output CHDR stream to the rfnoc infrastructure
+// - ctrlport_* : The ctrlport interface for the configuration port
+//
+
+module chdr_xport_adapter_generic #(
+ parameter [15:0] PROTOVER = {8'd1, 8'd0},
+ parameter CHDR_W = 256,
+ parameter USER_W = 16,
+ parameter TBL_SIZE = 6,
+ parameter [7:0] NODE_SUBTYPE = 8'd0,
+ parameter NODE_INST = 0
+)(
+ // Clock and reset
+ input wire clk,
+ input wire rst,
+ // Device info
+ input wire [15:0] device_id,
+ // Transport stream in (AXI-Stream)
+ input wire [CHDR_W-1:0] s_axis_xport_tdata,
+ input wire [USER_W-1:0] s_axis_xport_tuser,
+ input wire s_axis_xport_tlast,
+ input wire s_axis_xport_tvalid,
+ output wire s_axis_xport_tready,
+ // Transport stream out (AXI-Stream)
+ output wire [CHDR_W-1:0] m_axis_xport_tdata,
+ output wire [USER_W-1:0] m_axis_xport_tuser,
+ output wire m_axis_xport_tlast,
+ output wire m_axis_xport_tvalid,
+ input wire m_axis_xport_tready,
+ // RFNoC stream in (AXI-Stream)
+ input wire [CHDR_W-1:0] s_axis_rfnoc_tdata,
+ input wire s_axis_rfnoc_tlast,
+ input wire s_axis_rfnoc_tvalid,
+ output wire s_axis_rfnoc_tready,
+ // RFNoC stream out (AXI-Stream)
+ output wire [CHDR_W-1:0] m_axis_rfnoc_tdata,
+ output wire m_axis_rfnoc_tlast,
+ output wire m_axis_rfnoc_tvalid,
+ input wire m_axis_rfnoc_tready,
+ // Control port endpoint
+ output wire ctrlport_req_wr,
+ output wire ctrlport_req_rd,
+ output wire [15:0] ctrlport_req_addr,
+ output wire [31:0] ctrlport_req_data,
+ input wire ctrlport_resp_ack,
+ input wire [31:0] ctrlport_resp_data
+);
+
+ // ---------------------------------------------------
+ // RFNoC Includes
+ // ---------------------------------------------------
+ `include "../core/rfnoc_chdr_utils.vh"
+ `include "../core/rfnoc_chdr_internal_utils.vh"
+
+ // ---------------------------------------------------
+ // Reverse groups of 64-bit words to translate
+ // stream to "RFNoC Network Order" i.e. Big-Endian
+ // in groups of 8 bytes
+ // ---------------------------------------------------
+ wire [CHDR_W-1:0] i_xport_tdata;
+ wire [USER_W-1:0] i_xport_tuser;
+ wire i_xport_tlast, i_xport_tvalid, i_xport_tready;
+ wire [CHDR_W-1:0] o_xport_tdata;
+ wire [USER_W-1:0] o_xport_tuser;
+ wire o_xport_tlast, o_xport_tvalid, o_xport_tready;
+
+ localparam [$clog2(CHDR_W)-1:0] SWAP_LANES = ((CHDR_W / 64) - 1) << 6;
+
+ axis_data_swap #(
+ .DATA_W(CHDR_W), .USER_W(USER_W), .STAGES_EN(SWAP_LANES), .DYNAMIC(0)
+ ) xport_in_swap_i (
+ .clk(clk), .rst(rst),
+ .s_axis_tdata(s_axis_xport_tdata), .s_axis_tswap('h0),
+ .s_axis_tuser(s_axis_xport_tuser), .s_axis_tlast(s_axis_xport_tlast),
+ .s_axis_tvalid(s_axis_xport_tvalid), .s_axis_tready(s_axis_xport_tready),
+ .m_axis_tdata (i_xport_tdata), .m_axis_tuser(i_xport_tuser),
+ .m_axis_tlast (i_xport_tlast),
+ .m_axis_tvalid(i_xport_tvalid), .m_axis_tready(i_xport_tready)
+ );
+
+ axis_data_swap #(
+ .DATA_W(CHDR_W), .USER_W(USER_W), .STAGES_EN(SWAP_LANES), .DYNAMIC(0)
+ ) xport_out_swap_i (
+ .clk(clk), .rst(rst),
+ .s_axis_tdata(o_xport_tdata), .s_axis_tswap('h0),
+ .s_axis_tuser(o_xport_tuser), .s_axis_tlast(o_xport_tlast),
+ .s_axis_tvalid(o_xport_tvalid), .s_axis_tready(o_xport_tready),
+ .m_axis_tdata (m_axis_xport_tdata), .m_axis_tuser (m_axis_xport_tuser),
+ .m_axis_tlast (m_axis_xport_tlast),
+ .m_axis_tvalid(m_axis_xport_tvalid), .m_axis_tready(m_axis_xport_tready)
+ );
+
+
+ wire [CHDR_W-1:0] x2d_tdata; // Xport => Demux
+ reg [USER_W-1:0] x2d_tuser;
+ wire [1:0] x2d_tid;
+ wire x2d_tlast, x2d_tvalid, x2d_tready;
+ wire [CHDR_W-1:0] x2x_tdata; // Xport => Xport (loopback)
+ wire [USER_W-1:0] x2x_tuser;
+ wire x2x_tlast, x2x_tvalid, x2x_tready;
+ wire [CHDR_W-1:0] m2x_tdata; // Mux => Xport
+ wire m2x_tdest; // 1: Return to src, 0: CHDR input
+ wire [USER_W-1:0] m2x_tuser;
+ wire m2x_tlast, m2x_tvalid, m2x_tready;
+
+ // ---------------------------------------------------
+ // Transport => DEMUX
+ // ---------------------------------------------------
+ wire op_stb;
+ wire [15:0] op_src_epid;
+ wire [USER_W-1:0] op_data;
+ wire lookup_stb, lookup_done_stb, lookup_result_match;
+ wire [15:0] lookup_epid;
+ wire [USER_W-1:0] lookup_result_value;
+
+ chdr_mgmt_pkt_handler #(
+ .PROTOVER(PROTOVER), .CHDR_W(CHDR_W), .USER_W(USER_W), .MGMT_ONLY(0)
+ ) mgmt_ep_i (
+ .clk(clk), .rst(rst),
+ .node_info(chdr_mgmt_build_node_info({10'h0, NODE_SUBTYPE}, NODE_INST, NODE_TYPE_TRANSPORT, device_id)),
+ .s_axis_chdr_tdata(i_xport_tdata), .s_axis_chdr_tlast(i_xport_tlast),
+ .s_axis_chdr_tvalid(i_xport_tvalid), .s_axis_chdr_tready(i_xport_tready),
+ .s_axis_chdr_tuser(i_xport_tuser),
+ .m_axis_chdr_tdata(x2d_tdata), .m_axis_chdr_tlast(x2d_tlast),
+ .m_axis_chdr_tdest(/* unused */), .m_axis_chdr_tid(x2d_tid),
+ .m_axis_chdr_tvalid(x2d_tvalid), .m_axis_chdr_tready(x2d_tready),
+ .ctrlport_req_wr(ctrlport_req_wr), .ctrlport_req_rd(ctrlport_req_rd),
+ .ctrlport_req_addr(ctrlport_req_addr), .ctrlport_req_data(ctrlport_req_data),
+ .ctrlport_resp_ack(ctrlport_resp_ack), .ctrlport_resp_data(ctrlport_resp_data),
+ .op_stb(op_stb), .op_dst_epid(/* unused */), .op_src_epid(op_src_epid), .op_data(op_data)
+ );
+
+ kv_map #(
+ .KEY_WIDTH(16), .VAL_WIDTH(USER_W), .SIZE(TBL_SIZE)
+ ) kv_map_i (
+ .clk(clk), .reset(rst),
+ .insert_stb(op_stb), .insert_key(op_src_epid), .insert_val(op_data),
+ .insert_busy(/* Time between op_stb > Insertion time */),
+ .find_key_stb(lookup_stb), .find_key(lookup_epid),
+ .find_res_stb(lookup_done_stb),
+ .find_res_match(lookup_result_match), .find_res_val(lookup_result_value),
+ .count(/* unused */)
+ );
+
+ reg i_xport_hdr = 1'b1;
+ always @(posedge clk) begin
+ if (rst)
+ i_xport_hdr <= 1'b1;
+ else if (i_xport_tvalid && i_xport_tready)
+ i_xport_hdr <= i_xport_tlast;
+ end
+
+ // chdr_mgmt_pkt_handler does not buffer packets and has at least one cycle of delay
+ // TODO: The tuser caching logic could be more robust
+ always @(posedge clk) begin
+ if (i_xport_tvalid && i_xport_tready && i_xport_hdr)
+ x2d_tuser <= i_xport_tuser;
+ end
+
+ // ---------------------------------------------------
+ // MUX and DEMUX for return path
+ // ---------------------------------------------------
+
+ wire [USER_W-1:0] dummy_tuser;
+ axis_switch #(
+ .DATA_W(CHDR_W+USER_W), .DEST_W(1), .IN_PORTS(1), .OUT_PORTS(2), .PIPELINE(0)
+ ) rtn_demux_i (
+ .clk(clk), .reset(rst),
+ .s_axis_tdata({x2d_tuser, x2d_tdata}), .s_axis_alloc(1'b0),
+ .s_axis_tdest(x2d_tid == CHDR_MGMT_RETURN_TO_SRC ? 2'b01 : 2'b00),
+ .s_axis_tlast(x2d_tlast), .s_axis_tvalid(x2d_tvalid), .s_axis_tready(x2d_tready),
+ .m_axis_tdata({x2x_tuser, x2x_tdata, dummy_tuser, m_axis_rfnoc_tdata}),
+ .m_axis_tdest(/* unused */),
+ .m_axis_tlast({x2x_tlast, m_axis_rfnoc_tlast}),
+ .m_axis_tvalid({x2x_tvalid, m_axis_rfnoc_tvalid}),
+ .m_axis_tready({x2x_tready, m_axis_rfnoc_tready})
+ );
+
+ axi_mux #(
+ .WIDTH(CHDR_W+USER_W+1), .SIZE(2), .PRE_FIFO_SIZE(0), .POST_FIFO_SIZE(0)
+ ) rtn_mux_i (
+ .clk(clk), .reset(rst), .clear(1'b0),
+ .i_tdata({1'b1, x2x_tuser, x2x_tdata, 1'b0, {USER_W{1'b0}}, s_axis_rfnoc_tdata}),
+ .i_tlast({x2x_tlast, s_axis_rfnoc_tlast}),
+ .i_tvalid({x2x_tvalid, s_axis_rfnoc_tvalid}), .i_tready({x2x_tready, s_axis_rfnoc_tready}),
+ .o_tdata({m2x_tdest, m2x_tuser, m2x_tdata}), .o_tlast(m2x_tlast),
+ .o_tvalid(m2x_tvalid), .o_tready(m2x_tready)
+ );
+
+ // ---------------------------------------------------
+ // MUX => Transport
+ // ---------------------------------------------------
+
+ // In this section we must determine what value to put in tuser. If tdest is
+ // 1 then tuser is passed through unchanged. If tdest is 0 then the tuser
+ // value is looked up in the KV map using the EPID in the packet header.
+ //
+ // To do this we split the data (tdata, tlast) and the routing information
+ // (tdest, tuser, and the EPID) into two FIFOs. This allows us to perform a
+ // routing lookup and decide what to do while we continue to buffer data.
+ //
+ // With small packets, multiple routing lookups might be enqueued in the
+ // lookup_fifo, but we can only do one lookup at a time. Output logic
+ // controls release of packets from the data FIFO to ensure we only output
+ // one packet per lookup after the lookup is complete.
+
+ wire data_fifo_i_tready;
+ wire [CHDR_W-1:0] data_fifo_o_tdata;
+ wire data_fifo_o_tlast;
+ wire data_fifo_o_tvalid;
+ wire data_fifo_o_tready;
+ wire lookup_fifo_i_tready;
+ wire lookup_fifo_tdest;
+ wire [USER_W-1:0] lookup_fifo_tuser;
+ wire [ 15:0] lookup_fifo_tepid;
+ wire lookup_fifo_o_tvalid;
+ wire lookup_fifo_o_tready;
+
+ wire non_lookup_done_stb;
+ reg data_fifo_o_hdr = 1'b1;
+ reg pass_packet;
+ reg [USER_W-1:0] result_tuser;
+ reg result_tuser_valid;
+ reg [USER_W-1:0] reg_o_tuser;
+
+
+ // Track when the next m2x word contains is the start of a new packet
+ reg m2x_hdr = 1'b1;
+ always @(posedge clk) begin
+ if (rst)
+ m2x_hdr <= 1'b1;
+ else if (m2x_tvalid && m2x_tready)
+ m2x_hdr <= m2x_tlast;
+ end
+
+ // We can only accept data from the mux when when both the data_fifo and
+ // lookup_fifo are ready.
+ assign m2x_tready = data_fifo_i_tready && lookup_fifo_i_tready;
+
+ // The data_fifo only takes the packet data (tdata, tlast). We use an
+ // axi_fifo_short module for the data_fifo because it can tolerate tvalid
+ // going low before a transfer completes.
+ axi_fifo_short #(
+ .WIDTH (1+CHDR_W)
+ ) data_fifo (
+ .clk (clk),
+ .reset (rst),
+ .clear (1'b0),
+ .i_tdata ({m2x_tlast, m2x_tdata}),
+ .i_tvalid (m2x_tvalid && m2x_tready),
+ .i_tready (data_fifo_i_tready),
+ .o_tdata ({data_fifo_o_tlast, data_fifo_o_tdata}),
+ .o_tvalid (data_fifo_o_tvalid),
+ .o_tready (data_fifo_o_tready),
+ .space (),
+ .occupied ()
+ );
+
+ // The lookup FIFO only takes the header routing info (tdest, tuser, epid).
+ // We use axi_fifo_short since it can tolerate tvalid going low before a
+ // transfer completes.
+ axi_fifo_short #(
+ .WIDTH (1+USER_W+16)
+ ) lookup_fifo (
+ .clk (clk),
+ .reset (rst),
+ .clear (1'b0),
+ .i_tdata ({m2x_tdest, m2x_tuser, chdr_get_dst_epid(m2x_tdata[63:0])}),
+ .i_tvalid (m2x_tvalid && m2x_tready && m2x_hdr),
+ .i_tready (lookup_fifo_i_tready),
+ .o_tdata ({lookup_fifo_tdest, lookup_fifo_tuser, lookup_fifo_tepid}),
+ .o_tvalid (lookup_fifo_o_tvalid),
+ .o_tready (lookup_fifo_o_tready),
+ .space (),
+ .occupied ()
+ );
+
+ // Keep track of when we are busy doing a lookup in the KV map.
+ reg lookup_busy = 1'b0;
+ always @(posedge clk) begin
+ if (rst)
+ lookup_busy <= 1'b0;
+ else begin
+ if (lookup_stb)
+ lookup_busy <= 1'b1;
+ else if (lookup_done_stb)
+ lookup_busy <= 1'b0;
+ end
+ end
+
+ // Determine if we can use the output of the lookup_fifo to do a KV map
+ // lookup. We only perform a KV map lookup if tdest is 0 and we can only do
+ // so if the KV map is free and the holding register for the tuser value is
+ // available.
+ assign lookup_epid = lookup_fifo_tepid;
+ assign lookup_stb = lookup_fifo_o_tvalid && !lookup_busy &&
+ !lookup_fifo_tdest && !result_tuser_valid;
+
+ // Determine if we can use the output of the lookup FIFO directly (no lookup
+ // is needed). We can only use it if we're not already doing a KV lookup and
+ // if the holding register for the tuser value is available.
+ assign non_lookup_done_stb = lookup_fifo_o_tvalid && !lookup_busy &&
+ lookup_fifo_tdest && !result_tuser_valid;
+
+ // Pop the routing info off of the lookup_fifo if we've started its lookup
+ assign lookup_fifo_o_tready = lookup_stb || non_lookup_done_stb;
+
+ // Track when the next data_fifo_o word is the start of a new packet
+ always @(posedge clk) begin
+ if (rst)
+ data_fifo_o_hdr <= 1'b1;
+ else if (data_fifo_o_tvalid && data_fifo_o_tready && pass_packet)
+ data_fifo_o_hdr <= data_fifo_o_tlast;
+ end
+
+ // Store the lookup result in a holding register. This can come from the KV
+ // map or the incoming tuser.
+ always @(posedge clk) begin
+ if (rst) begin
+ result_tuser <= {USER_W{1'bX}}; // Don't care
+ result_tuser_valid <= 1'b0;
+ end else begin
+ // The tuser holding register becomes available as soon as we start
+ // transmitting the corresponding packet.
+ if (data_fifo_o_tvalid && data_fifo_o_tready && data_fifo_o_hdr && pass_packet) begin
+ result_tuser_valid <= 1'b0;
+ end
+
+ // Load the result of the lookup
+ if (lookup_done_stb) begin
+ result_tuser <= lookup_result_match ? lookup_result_value : {USER_W{1'b0}};
+ result_tuser_valid <= 1'b1;
+ end else if (non_lookup_done_stb) begin
+ result_tuser <= lookup_fifo_tuser;
+ result_tuser_valid <= 1'b1;
+ end
+ end
+ end
+
+ // Control when the packet from the data_fifo can be passed through. Put the
+ // tuser value into a register for the duration of the packet.
+ always @(posedge clk) begin
+ if (rst) begin
+ pass_packet <= 1'b0;
+ reg_o_tuser <= {USER_W{1'bX}}; // Don't care
+ end else begin
+ // We're done passing through a packet when tlast goes out
+ if (data_fifo_o_tvalid && data_fifo_o_tready && data_fifo_o_tlast && pass_packet) begin
+ pass_packet <= 1'b0;
+ end
+
+ // We can pass the next packet through when we're at the start of a
+ // packet and we have the tuser value waiting in the holding register.
+ if (data_fifo_o_hdr && result_tuser_valid && !pass_packet) begin
+ reg_o_tuser <= result_tuser;
+ pass_packet <= 1'b1;
+ end
+ end
+ end
+
+ assign o_xport_tdata = data_fifo_o_tdata;
+ assign o_xport_tuser = reg_o_tuser;
+ assign o_xport_tlast = data_fifo_o_tlast;
+ assign o_xport_tvalid = data_fifo_o_tvalid & pass_packet;
+ assign data_fifo_o_tready = o_xport_tready & pass_packet;
+
+endmodule // chdr_xport_adapter_generic \ No newline at end of file
diff --git a/fpga/usrp3/lib/rfnoc/xport/eth_interface.v b/fpga/usrp3/lib/rfnoc/xport/eth_interface.v
new file mode 100644
index 000000000..21e8b809d
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/xport/eth_interface.v
@@ -0,0 +1,252 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Adapts from internal VITA to ethernet packets. Also handles CPU and ethernet crossover interfaces.
+
+module eth_interface #(
+ parameter [15:0] PROTOVER = {8'd1, 8'd0},
+ parameter MTU = 10,
+ parameter NODE_INST = 0,
+ parameter RT_TBL_SIZE = 6,
+ parameter REG_AWIDTH = 14,
+ parameter BASE = 0
+) (
+ input clk,
+ input reset,
+ input [15:0] device_id,
+
+ // Register port: Write port (domain: clk)
+ input reg_wr_req,
+ input [REG_AWIDTH-1:0] reg_wr_addr,
+ input [31:0] reg_wr_data,
+
+ // Register port: Read port (domain: clk)
+ input reg_rd_req,
+ input [REG_AWIDTH-1:0] reg_rd_addr,
+ output reg reg_rd_resp,
+ output reg [31:0] reg_rd_data,
+
+ // Status ports (domain: clk)
+ output [47:0] my_mac,
+ output [31:0] my_ip,
+ output [15:0] my_udp_port,
+
+ // Ethernet ports
+ output [63:0] eth_tx_tdata,
+ output [3:0] eth_tx_tuser,
+ output eth_tx_tlast,
+ output eth_tx_tvalid,
+ input eth_tx_tready,
+ input [63:0] eth_rx_tdata,
+ input [3:0] eth_rx_tuser,
+ input eth_rx_tlast,
+ input eth_rx_tvalid,
+ output eth_rx_tready,
+
+ // Vita router interface
+ output [63:0] e2v_tdata,
+ output e2v_tlast,
+ output e2v_tvalid,
+ input e2v_tready,
+ input [63:0] v2e_tdata,
+ input v2e_tlast,
+ input v2e_tvalid,
+ output v2e_tready,
+
+ // CPU
+ output [63:0] e2c_tdata,
+ output [3:0] e2c_tuser,
+ output e2c_tlast,
+ output e2c_tvalid,
+ input e2c_tready,
+ input [63:0] c2e_tdata,
+ input [3:0] c2e_tuser,
+ input c2e_tlast,
+ input c2e_tvalid,
+ output c2e_tready
+);
+
+ localparam [47:0] DEFAULT_MAC_ADDR = {8'h00, 8'h80, 8'h2f, 8'h16, 8'hc5, 8'h2f};
+ localparam [31:0] DEFAULT_IP_ADDR = {8'd192, 8'd168, 8'd10, 8'd2};
+ localparam [31:0] DEFAULT_UDP_PORT = 16'd49153;
+
+ //---------------------------------------------------------
+ // Registers
+ //---------------------------------------------------------
+
+ // Allocate one full page for MAC
+ localparam [REG_AWIDTH-1:0] REG_MAC_LSB = BASE + 'h0000;
+ localparam [REG_AWIDTH-1:0] REG_MAC_MSB = BASE + 'h0004;
+
+ // Source IP address
+ localparam [REG_AWIDTH-1:0] REG_IP = BASE + 'h1000;
+ // Source UDP Port
+ localparam [REG_AWIDTH-1:0] REG_UDP = BASE + 'h1004;
+
+ // Registers for Internal/Bridge Network Mode in CPU
+ localparam [REG_AWIDTH-1:0] REG_BRIDGE_MAC_LSB = BASE + 'h1010;
+ localparam [REG_AWIDTH-1:0] REG_BRIDGE_MAC_MSB = BASE + 'h1014;
+ localparam [REG_AWIDTH-1:0] REG_BRIDGE_IP = BASE + 'h1018;
+ localparam [REG_AWIDTH-1:0] REG_BRIDGE_UDP = BASE + 'h101c;
+ localparam [REG_AWIDTH-1:0] REG_BRIDGE_ENABLE = BASE + 'h1020;
+
+ // MAC address for the dispatcher module.
+ // This value is used to determine if the packet is meant
+ // for this device should be consumed
+ // IP address for the dispatcher module.
+ // This value is used to determine if the packet is addressed
+ // to this device
+ // This module supports two destination ports
+ reg [47:0] mac_reg;
+ reg [31:0] ip_reg;
+ reg [15:0] udp_port;
+ reg [47:0] bridge_mac_reg;
+ reg [31:0] bridge_ip_reg;
+ reg [15:0] bridge_udp_port;
+ reg bridge_en;
+
+ assign my_mac = bridge_en ? bridge_mac_reg : mac_reg;
+ assign my_ip = bridge_en ? bridge_ip_reg : ip_reg;
+ assign my_udp_port = bridge_en ? bridge_udp_port : udp_port;
+
+ always @(posedge clk) begin
+ if (reset) begin
+ mac_reg <= DEFAULT_MAC_ADDR;
+ ip_reg <= DEFAULT_IP_ADDR;
+ udp_port <= DEFAULT_UDP_PORT;
+ bridge_en <= 1'b0;
+ bridge_mac_reg <= DEFAULT_MAC_ADDR;
+ bridge_ip_reg <= DEFAULT_IP_ADDR;
+ bridge_udp_port <= DEFAULT_UDP_PORT;
+ end
+ else begin
+ if (reg_wr_req)
+ case (reg_wr_addr)
+
+ REG_MAC_LSB:
+ mac_reg[31:0] <= reg_wr_data;
+
+ REG_MAC_MSB:
+ mac_reg[47:32] <= reg_wr_data[15:0];
+
+ REG_IP:
+ ip_reg <= reg_wr_data;
+
+ REG_UDP:
+ udp_port <= reg_wr_data[15:0];
+
+ REG_BRIDGE_MAC_LSB:
+ bridge_mac_reg[31:0] <= reg_wr_data;
+
+ REG_BRIDGE_MAC_MSB:
+ bridge_mac_reg[47:32] <= reg_wr_data[15:0];
+
+ REG_BRIDGE_IP:
+ bridge_ip_reg <= reg_wr_data;
+
+ REG_BRIDGE_UDP:
+ bridge_udp_port <= reg_wr_data[15:0];
+
+ REG_BRIDGE_ENABLE:
+ bridge_en <= reg_wr_data[0];
+ endcase
+ end
+ end
+
+ always @ (posedge clk) begin
+ // No reset handling required for readback
+ if (reg_rd_req) begin
+ // Assert read response one cycle after read request
+ reg_rd_resp <= 1'b1;
+ case (reg_rd_addr)
+ REG_MAC_LSB:
+ reg_rd_data <= mac_reg[31:0];
+
+ REG_MAC_MSB:
+ reg_rd_data <= {16'b0,mac_reg[47:32]};
+
+ REG_IP:
+ reg_rd_data <= ip_reg;
+
+ REG_UDP:
+ reg_rd_data <= {16'b0, udp_port};
+
+ REG_BRIDGE_MAC_LSB:
+ reg_rd_data <= bridge_mac_reg[31:0];
+
+ REG_BRIDGE_MAC_MSB:
+ reg_rd_data <= {16'b0,bridge_mac_reg[47:32]};
+
+ REG_BRIDGE_IP:
+ reg_rd_data <= bridge_ip_reg;
+
+ REG_BRIDGE_UDP:
+ reg_rd_data <= {16'b0, bridge_udp_port};
+
+ REG_BRIDGE_ENABLE:
+ reg_rd_data <= {31'b0,bridge_en};
+
+ default:
+ reg_rd_resp <= 1'b0;
+ endcase
+ end
+ // Deassert read response after one clock cycle
+ if (reg_rd_resp) begin
+ reg_rd_resp <= 1'b0;
+ end
+ end
+
+ // In AXI Stream, tkeep is the byte qualifier that indicates
+ // whether the content of the associated byte
+ // of TDATA is processed as part of the data stream.
+ // tuser as used in eth_switch is the numbier of valid bytes
+
+ eth_ipv4_chdr64_adapter #(
+ .PROTOVER (PROTOVER),
+ .MTU (MTU),
+ .CPU_FIFO_SIZE (MTU),
+ .RT_TBL_SIZE (RT_TBL_SIZE),
+ .NODE_INST (NODE_INST),
+ .DROP_UNKNOWN_MAC(0),
+ .IS_CPU_ARM (1)
+ ) eth_adapter_i (
+ .clk (clk ),
+ .rst (reset ),
+ .device_id (device_id ),
+ .s_mac_tdata (eth_rx_tdata ),
+ .s_mac_tuser (eth_rx_tuser ),
+ .s_mac_tlast (eth_rx_tlast ),
+ .s_mac_tvalid (eth_rx_tvalid),
+ .s_mac_tready (eth_rx_tready),
+ .m_mac_tdata (eth_tx_tdata ),
+ .m_mac_tuser (eth_tx_tuser ),
+ .m_mac_tlast (eth_tx_tlast ),
+ .m_mac_tvalid (eth_tx_tvalid),
+ .m_mac_tready (eth_tx_tready),
+ .s_chdr_tdata (v2e_tdata ),
+ .s_chdr_tlast (v2e_tlast ),
+ .s_chdr_tvalid (v2e_tvalid ),
+ .s_chdr_tready (v2e_tready ),
+ .m_chdr_tdata (e2v_tdata ),
+ .m_chdr_tlast (e2v_tlast ),
+ .m_chdr_tvalid (e2v_tvalid ),
+ .m_chdr_tready (e2v_tready ),
+ .s_cpu_tdata (c2e_tdata ),
+ .s_cpu_tuser (c2e_tuser ),
+ .s_cpu_tlast (c2e_tlast ),
+ .s_cpu_tvalid (c2e_tvalid ),
+ .s_cpu_tready (c2e_tready ),
+ .m_cpu_tdata (e2c_tdata ),
+ .m_cpu_tuser (e2c_tuser ),
+ .m_cpu_tlast (e2c_tlast ),
+ .m_cpu_tvalid (e2c_tvalid ),
+ .m_cpu_tready (e2c_tready ),
+ .my_eth_addr (my_mac ),
+ .my_ipv4_addr (my_ip ),
+ .my_udp_chdr_port(my_udp_port )
+ );
+
+
+endmodule // eth_interface
diff --git a/fpga/usrp3/lib/rfnoc/xport/eth_internal.v b/fpga/usrp3/lib/rfnoc/xport/eth_internal.v
new file mode 100644
index 000000000..49cf838a5
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/xport/eth_internal.v
@@ -0,0 +1,433 @@
+///////////////////////////////////////////////////////////////////
+//
+// Copyright 2019 Ettus Research, a National Instruments brand
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: eth_internal
+// Description:
+// Reduces clutter at top level.
+// - FPGA-internal Ethernet port
+// - ARP responder instead of other CPU connection
+//
+//////////////////////////////////////////////////////////////////////
+
+`default_nettype none
+module eth_internal #(
+ parameter DWIDTH = 32,
+ parameter AWIDTH = 14,
+ parameter [7:0] PORTNUM = 0,
+ parameter [15:0] RFNOC_PROTOVER = {8'd1, 8'd0}
+)(
+ // Resets
+ input wire bus_rst,
+
+ // Clocks
+ input wire bus_clk,
+
+ //Axi-lite
+ input wire s_axi_aclk,
+ input wire s_axi_aresetn,
+ input wire [AWIDTH-1:0] s_axi_awaddr,
+ input wire s_axi_awvalid,
+ output wire s_axi_awready,
+
+ input wire [DWIDTH-1:0] s_axi_wdata,
+ input wire [DWIDTH/8-1:0] s_axi_wstrb,
+ input wire s_axi_wvalid,
+ output wire s_axi_wready,
+
+ output wire [1:0] s_axi_bresp,
+ output wire s_axi_bvalid,
+ input wire s_axi_bready,
+
+ input wire [AWIDTH-1:0] s_axi_araddr,
+ input wire s_axi_arvalid,
+ output wire s_axi_arready,
+
+ output wire [DWIDTH-1:0] s_axi_rdata,
+ output wire [1:0] s_axi_rresp,
+ output wire s_axi_rvalid,
+ input wire s_axi_rready,
+
+ // Host-Ethernet DMA interface
+ output wire [63:0] e2h_tdata,
+ output wire [7:0] e2h_tkeep,
+ output wire e2h_tlast,
+ output wire e2h_tvalid,
+ input wire e2h_tready,
+
+ input wire [63:0] h2e_tdata,
+ input wire [7:0] h2e_tkeep,
+ input wire h2e_tlast,
+ input wire h2e_tvalid,
+ output wire h2e_tready,
+
+ // RFNoC interface
+ output wire [63:0] e2v_tdata,
+ output wire e2v_tlast,
+ output wire e2v_tvalid,
+ input wire e2v_tready,
+
+ input wire [63:0] v2e_tdata,
+ input wire v2e_tlast,
+ input wire v2e_tvalid,
+ output wire v2e_tready,
+
+ // MISC
+ output wire [31:0] port_info,
+ input wire [15:0] device_id,
+
+ output wire link_up,
+ output reg activity
+
+);
+
+ localparam REG_BASE_ETH_IO = 14'h0;
+ localparam REG_BASE_ETH_SWITCH = 14'h1000;
+
+ // AXI4-Lite to RegPort (PS to PL Register Access)
+ wire reg_wr_req;
+ wire [AWIDTH-1:0] reg_wr_addr;
+ wire [DWIDTH-1:0] reg_wr_data;
+ wire reg_rd_req;
+ wire [AWIDTH-1:0] reg_rd_addr;
+ wire reg_rd_resp, reg_rd_resp_eth_if;
+ reg reg_rd_resp_io = 1'b0;
+ wire [DWIDTH-1:0] reg_rd_data, reg_rd_data_eth_if;
+ reg [DWIDTH-1:0] reg_rd_data_io = 'd0;
+
+ axil_regport_master #(
+ .DWIDTH (DWIDTH), // Width of the AXI4-Lite data bus (must be 32 or 64)
+ .AWIDTH (AWIDTH), // Width of the address bus
+ .WRBASE (0), // Write address base
+ .RDBASE (0), // Read address base
+ .TIMEOUT (10) // log2(timeout). Read will timeout after (2^TIMEOUT - 1) cycles
+ ) eth_dma_reg_mst_i (
+ // Clock and reset
+ .s_axi_aclk (s_axi_aclk),
+ .s_axi_aresetn (s_axi_aresetn),
+ // AXI4-Lite: Write address port (domain: s_axi_aclk)
+ .s_axi_awaddr (s_axi_awaddr),
+ .s_axi_awvalid (s_axi_awvalid),
+ .s_axi_awready (s_axi_awready),
+ // AXI4-Lite: Write data port (domain: s_axi_aclk)
+ .s_axi_wdata (s_axi_wdata),
+ .s_axi_wstrb (s_axi_wstrb),
+ .s_axi_wvalid (s_axi_wvalid),
+ .s_axi_wready (s_axi_wready),
+ // AXI4-Lite: Write response port (domain: s_axi_aclk)
+ .s_axi_bresp (s_axi_bresp),
+ .s_axi_bvalid (s_axi_bvalid),
+ .s_axi_bready (s_axi_bready),
+ // AXI4-Lite: Read address port (domain: s_axi_aclk)
+ .s_axi_araddr (s_axi_araddr),
+ .s_axi_arvalid (s_axi_arvalid),
+ .s_axi_arready (s_axi_arready),
+ // AXI4-Lite: Read data port (domain: s_axi_aclk)
+ .s_axi_rdata (s_axi_rdata),
+ .s_axi_rresp (s_axi_rresp),
+ .s_axi_rvalid (s_axi_rvalid),
+ .s_axi_rready (s_axi_rready),
+ // Register port: Write port (domain: reg_clk)
+ .reg_clk (bus_clk),
+ .reg_wr_req (reg_wr_req),
+ .reg_wr_addr (reg_wr_addr),
+ .reg_wr_data (reg_wr_data),
+ // Register port: Read port (domain: reg_clk)
+ .reg_rd_req (reg_rd_req),
+ .reg_rd_addr (reg_rd_addr),
+ .reg_rd_resp (reg_rd_resp),
+ .reg_rd_data (reg_rd_data)
+ );
+
+ // Regport Mux for response
+ regport_resp_mux #(
+ .WIDTH (DWIDTH),
+ .NUM_SLAVES (2)
+ ) reg_resp_mux_i (
+ .clk(bus_clk), .reset(bus_rst),
+ .sla_rd_resp({reg_rd_resp_eth_if, reg_rd_resp_io}),
+ .sla_rd_data({reg_rd_data_eth_if, reg_rd_data_io}),
+ .mst_rd_resp(reg_rd_resp), .mst_rd_data(reg_rd_data)
+ );
+
+ // ARP responder
+ wire [63:0] e2c_tdata;
+ wire [7:0] e2c_tkeep;
+ wire e2c_tlast;
+ wire e2c_tvalid;
+ wire e2c_tready;
+
+ wire [63:0] c2e_tdata;
+ wire [7:0] c2e_tkeep;
+ wire c2e_tlast;
+ wire c2e_tvalid;
+ wire c2e_tready;
+
+ wire [3:0] e2c_tuser;
+ wire [3:0] c2e_tuser;
+
+ // ARM Host-to-Ethernet
+ wire [3:0] e2h_tuser;
+ wire [3:0] h2e_tuser;
+
+ // Host Ethernet-to-CHDR
+ wire [63:0] h2e_chdr_tdata;
+ wire [3:0] h2e_chdr_tuser;
+ wire h2e_chdr_tlast;
+ wire h2e_chdr_tvalid;
+ wire h2e_chdr_tready;
+ wire [63:0] e2h_chdr_tdata;
+ wire [3:0] e2h_chdr_tuser;
+ wire e2h_chdr_tlast;
+ wire e2h_chdr_tvalid;
+ wire e2h_chdr_tready;
+
+
+ // In AXI Stream, tkeep is the byte qualifier that indicates
+ // whether the content of the associated byte
+ // of TDATA is processed as part of the data stream.
+ // tuser as used in eth_interface is the number of valid bytes
+
+ // Converting tuser to tkeep for ingress packets
+ assign e2c_tkeep = ~e2c_tlast ? 8'b1111_1111
+ : (e2c_tuser == 4'd0) ? 8'b1111_1111
+ : (e2c_tuser == 4'd1) ? 8'b0000_0001
+ : (e2c_tuser == 4'd2) ? 8'b0000_0011
+ : (e2c_tuser == 4'd3) ? 8'b0000_0111
+ : (e2c_tuser == 4'd4) ? 8'b0000_1111
+ : (e2c_tuser == 4'd5) ? 8'b0001_1111
+ : (e2c_tuser == 4'd6) ? 8'b0011_1111
+ : 8'b0111_1111;
+
+ // Converting tkeep to tuser for egress packets
+ assign c2e_tuser = ~c2e_tlast ? 4'd0
+ : (c2e_tkeep == 8'b1111_1111) ? 4'd0
+ : (c2e_tkeep == 8'b0111_1111) ? 4'd7
+ : (c2e_tkeep == 8'b0011_1111) ? 4'd6
+ : (c2e_tkeep == 8'b0001_1111) ? 4'd5
+ : (c2e_tkeep == 8'b0000_1111) ? 4'd4
+ : (c2e_tkeep == 8'b0000_0111) ? 4'd3
+ : (c2e_tkeep == 8'b0000_0011) ? 4'd2
+ : (c2e_tkeep == 8'b0000_0001) ? 4'd1
+ : 4'd0;
+
+ // Converting tuser to tkeep for ingress packets
+ assign e2h_tkeep = ~e2h_tlast ? 8'b1111_1111
+ : (e2h_tuser == 4'd0) ? 8'b1111_1111
+ : (e2h_tuser == 4'd1) ? 8'b0000_0001
+ : (e2h_tuser == 4'd2) ? 8'b0000_0011
+ : (e2h_tuser == 4'd3) ? 8'b0000_0111
+ : (e2h_tuser == 4'd4) ? 8'b0000_1111
+ : (e2h_tuser == 4'd5) ? 8'b0001_1111
+ : (e2h_tuser == 4'd6) ? 8'b0011_1111
+ : 8'b0111_1111;
+
+ // Converting tkeep to tuser for egress packets
+ assign h2e_tuser = ~h2e_tlast ? 4'd0
+ : (h2e_tkeep == 8'b1111_1111) ? 4'd0
+ : (h2e_tkeep == 8'b0111_1111) ? 4'd7
+ : (h2e_tkeep == 8'b0011_1111) ? 4'd6
+ : (h2e_tkeep == 8'b0001_1111) ? 4'd5
+ : (h2e_tkeep == 8'b0000_1111) ? 4'd4
+ : (h2e_tkeep == 8'b0000_0111) ? 4'd3
+ : (h2e_tkeep == 8'b0000_0011) ? 4'd2
+ : (h2e_tkeep == 8'b0000_0001) ? 4'd1
+ : 4'd0;
+
+ // FPGA-side addresses for the ARP responder
+ wire [47:0] my_mac;
+ wire [31:0] my_ip;
+ wire [15:0] my_udp_port;
+
+ arm_deframer arm_deframer_i (
+ .clk(bus_clk),
+ .reset(bus_rst),
+ .clear(1'b0),
+ .s_axis_tdata(h2e_tdata),
+ .s_axis_tuser(h2e_tuser),
+ .s_axis_tlast(h2e_tlast),
+ .s_axis_tvalid(h2e_tvalid),
+ .s_axis_tready(h2e_tready),
+ .m_axis_tdata(h2e_chdr_tdata),
+ .m_axis_tuser(h2e_chdr_tuser),
+ .m_axis_tlast(h2e_chdr_tlast),
+ .m_axis_tvalid(h2e_chdr_tvalid),
+ .m_axis_tready(h2e_chdr_tready)
+ );
+
+ axi64_to_xge64 arm_framer (
+ .clk(bus_clk),
+ .reset(bus_rst),
+ .clear(1'b0),
+ .s_axis_tdata(e2h_chdr_tdata),
+ .s_axis_tuser(e2h_chdr_tuser),
+ .s_axis_tlast(e2h_chdr_tlast),
+ .s_axis_tvalid(e2h_chdr_tvalid),
+ .s_axis_tready(e2h_chdr_tready),
+ .m_axis_tdata(e2h_tdata),
+ .m_axis_tuser(e2h_tuser),
+ .m_axis_tlast(e2h_tlast),
+ .m_axis_tvalid(e2h_tvalid),
+ .m_axis_tready(e2h_tready)
+ );
+
+ eth_interface #(
+ .PROTOVER(RFNOC_PROTOVER),
+ .MTU(10),
+ .NODE_INST(0),
+ .REG_AWIDTH (AWIDTH),
+ .BASE(REG_BASE_ETH_SWITCH)
+ ) eth_interface (
+ .clk (bus_clk),
+ .reset (bus_rst),
+ .device_id (device_id),
+ .reg_wr_req (reg_wr_req),
+ .reg_wr_addr (reg_wr_addr),
+ .reg_wr_data (reg_wr_data),
+ .reg_rd_req (reg_rd_req),
+ .reg_rd_addr (reg_rd_addr),
+ .reg_rd_resp (reg_rd_resp_eth_if),
+ .reg_rd_data (reg_rd_data_eth_if),
+ .my_mac (my_mac),
+ .my_ip (my_ip),
+ .my_udp_port (my_udp_port),
+ .eth_tx_tdata (e2h_chdr_tdata),
+ .eth_tx_tuser (e2h_chdr_tuser),
+ .eth_tx_tlast (e2h_chdr_tlast),
+ .eth_tx_tvalid (e2h_chdr_tvalid),
+ .eth_tx_tready (e2h_chdr_tready),
+ .eth_rx_tdata (h2e_chdr_tdata),
+ .eth_rx_tuser (h2e_chdr_tuser),
+ .eth_rx_tlast (h2e_chdr_tlast),
+ .eth_rx_tvalid (h2e_chdr_tvalid),
+ .eth_rx_tready (h2e_chdr_tready),
+ .e2v_tdata (e2v_tdata),
+ .e2v_tlast (e2v_tlast),
+ .e2v_tvalid (e2v_tvalid),
+ .e2v_tready (e2v_tready),
+ .v2e_tdata (v2e_tdata),
+ .v2e_tlast (v2e_tlast),
+ .v2e_tvalid (v2e_tvalid),
+ .v2e_tready (v2e_tready),
+ .e2c_tdata (e2c_tdata),
+ .e2c_tuser (e2c_tuser),
+ .e2c_tlast (e2c_tlast),
+ .e2c_tvalid (e2c_tvalid),
+ .e2c_tready (e2c_tready),
+ .c2e_tdata (c2e_tdata),
+ .c2e_tuser (c2e_tuser),
+ .c2e_tlast (c2e_tlast),
+ .c2e_tvalid (c2e_tvalid),
+ .c2e_tready (c2e_tready)
+ );
+
+ arp_responder arp_responder_i (
+ .aclk (bus_clk),
+ .aresetn (~bus_rst),
+ .mac_addr (my_mac),
+ .ip_addr (my_ip),
+ .s_axis_tdata (e2c_tdata),
+ .s_axis_tvalid (e2c_tvalid),
+ .s_axis_tready (e2c_tready),
+ .s_axis_tkeep (e2c_tkeep),
+ .s_axis_tlast (e2c_tlast),
+ .s_axis_tuser (1'b0),
+ .m_axis_tdata (c2e_tdata),
+ .m_axis_tvalid (c2e_tvalid),
+ .m_axis_tready (c2e_tready),
+ .m_axis_tkeep (c2e_tkeep),
+ .m_axis_tlast (c2e_tlast),
+ .m_axis_tuser ()
+ );
+
+ //-----------------------------------------------------------------
+ // "I/O" Registers
+ //-----------------------------------------------------------------
+ localparam [7:0] COMPAT_NUM = 8'd2;
+ localparam [7:0] MGT_PROTOCOL = 8'd4; // 10 GbE Internal (8'd2 is 10 GbE External)
+
+ // Common registers
+ localparam REG_PORT_INFO = REG_BASE_ETH_IO + 'h0;
+ localparam REG_MAC_CTRL_STATUS = REG_BASE_ETH_IO + 'h4;
+ localparam REG_PHY_CTRL_STATUS = REG_BASE_ETH_IO + 'h8;
+ localparam REG_MAC_LED_CTL = REG_BASE_ETH_IO + 'hC;
+
+ // Protocol specific constants
+ localparam [1:0] MAC_LED_CTL_RST_VAL = 2'h0;
+
+ localparam [31:0] MAC_CTRL_RST_VAL = {31'h0, 1'b1}; // tx_enable on reset
+ localparam [31:0] PHY_CTRL_RST_VAL = 32'h0;
+
+ // Writable registers
+ reg [31:0] mac_ctrl_reg = MAC_CTRL_RST_VAL;
+ reg [31:0] phy_ctrl_reg = PHY_CTRL_RST_VAL;
+ reg [1:0] mac_led_ctl = MAC_LED_CTL_RST_VAL;
+
+ always @(posedge bus_clk) begin
+ if (bus_rst) begin
+ mac_ctrl_reg <= MAC_CTRL_RST_VAL;
+ phy_ctrl_reg <= PHY_CTRL_RST_VAL;
+ mac_led_ctl <= MAC_LED_CTL_RST_VAL;
+ end else if (reg_wr_req) begin
+ case(reg_wr_addr)
+ REG_MAC_CTRL_STATUS:
+ mac_ctrl_reg <= reg_wr_data;
+ REG_PHY_CTRL_STATUS:
+ phy_ctrl_reg <= reg_wr_data;
+ REG_MAC_LED_CTL:
+ mac_led_ctl <= reg_wr_data[1:0];
+ endcase
+ end
+ end
+
+ // Readable registers
+ wire [31:0] mac_status, phy_status;
+
+ assign port_info = {COMPAT_NUM, 6'h0, activity, link_up, MGT_PROTOCOL, PORTNUM};
+
+ always @(posedge bus_clk) begin
+ // No reset handling needed for readback
+ if (reg_rd_req) begin
+ reg_rd_resp_io <= 1'b1;
+ case(reg_rd_addr)
+ REG_PORT_INFO:
+ reg_rd_data_io <= port_info;
+ REG_MAC_CTRL_STATUS:
+ reg_rd_data_io <= mac_status;
+ REG_PHY_CTRL_STATUS:
+ reg_rd_data_io <= phy_status;
+ REG_MAC_LED_CTL:
+ reg_rd_data_io <= {30'd0, mac_led_ctl};
+ default:
+ reg_rd_resp_io <= 1'b0;
+ endcase
+ end if (reg_rd_resp_io) begin
+ reg_rd_resp_io <= 1'b0;
+ end
+ end
+
+ assign mac_status = 'd0;
+ assign phy_status[31:8] = 24'h0;
+ assign link_up = 1'b1;
+
+ wire identify_enable = mac_led_ctl[0];
+ wire identify_value = mac_led_ctl[1];
+
+ //-----------------------------------------------------------------
+ // Activity detector
+ //-----------------------------------------------------------------
+ wire activity_int;
+
+ pulse_stretch act_pulse_str_i (
+ .clk(bus_clk),
+ .rst(bus_rst | ~link_up),
+ .pulse((h2e_tvalid & h2e_tready) | (e2h_tvalid & e2h_tready)),
+ .pulse_stretched(activity_int)
+ );
+
+ always @ (posedge bus_clk) activity <= identify_enable ? identify_value : activity_int;
+
+endmodule
+`default_nettype wire
diff --git a/fpga/usrp3/lib/rfnoc/xport/eth_ipv4_chdr64_adapter.v b/fpga/usrp3/lib/rfnoc/xport/eth_ipv4_chdr64_adapter.v
new file mode 100644
index 000000000..66c1b521e
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/xport/eth_ipv4_chdr64_adapter.v
@@ -0,0 +1,397 @@
+//
+// Copyright 2019 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: eth_ipv4_chdr64_adapter
+// Description: A generic transport adapter module that can be used in
+// a veriety of transports. It does the following:
+// - Exposes a configuration port for mgmt packets to configure the node
+// - Implements a return-address map for packets with metadata other than
+// the CHDR. Additional metadata can be passed as a tuser to this module
+// which will store it in a map indexed by the SrcEPID in a management
+// packet. For all returning packets, the metadata will be looked up in
+// the map and attached as the outgoing tuser.
+// - Implements a loopback path for node-info discovery
+//
+// Parameters:
+// - PROTOVER: RFNoC protocol version {8'd<major>, 8'd<minor>}
+// - MTU: Log2 of the MTU of the packet in 64-bit words
+// - CPU_FIFO_SIZE: Log2 of the FIFO depth (in 64-bit words) for the CPU egress path
+// - RT_TBL_SIZE: Log2 of the depth of the return-address routing table
+// - NODE_INST: The node type to return for a node-info discovery
+// - DROP_UNKNOWN_MAC: Drop packets not addressed to us?
+//
+// Signals:
+// - device_id : The ID of the device that has instantiated this module
+// - s_mac_*: The input Ethernet stream from the MAC (plus tuser for trailing bytes + err)
+// - m_mac_*: The output Ethernet stream to the MAC (plus tuser for trailing bytes + err)
+// - s_chdr_*: The input CHDR stream from the rfnoc infrastructure
+// - m_chdr_*: The output CHDR stream to the rfnoc infrastructure
+// - s_cpu_*: The input Ethernet stream from the CPU (plus tuser for trailing bytes + err)
+// - m_cpu_*: The output Ethernet stream to the CPU (plus tuser for trailing bytes + err)
+// - my_eth_addr: The Ethernet (MAC) address of this endpoint
+// - my_ipv4_addr: The IPv4 address of this endpoint
+// - my_udp_chdr_port: The UDP port allocated for CHDR traffic on this endpoint
+//
+
+`default_nettype none
+module eth_ipv4_chdr64_adapter #(
+ parameter [15:0] PROTOVER = {8'd1, 8'd0},
+ parameter MTU = 10,
+ parameter CPU_FIFO_SIZE = MTU,
+ parameter RT_TBL_SIZE = 6,
+ parameter NODE_INST = 0,
+ parameter [0:0] DROP_UNKNOWN_MAC = 1,
+ parameter [0:0] IS_CPU_ARM = 0
+)(
+ // Clocking and reset interface
+ input wire clk,
+ input wire rst,
+ // Device info
+ input wire [15:0] device_id,
+ // AXI-Stream interface to/from MAC
+ input wire [63:0] s_mac_tdata,
+ input wire [3:0] s_mac_tuser,
+ input wire s_mac_tlast,
+ input wire s_mac_tvalid,
+ output wire s_mac_tready,
+ output wire [63:0] m_mac_tdata,
+ output wire [3:0] m_mac_tuser,
+ output wire m_mac_tlast,
+ output wire m_mac_tvalid,
+ input wire m_mac_tready,
+ // AXI-Stream interface to/from CHDR infrastructure
+ input wire [63:0] s_chdr_tdata,
+ input wire s_chdr_tlast,
+ input wire s_chdr_tvalid,
+ output wire s_chdr_tready,
+ output wire [63:0] m_chdr_tdata,
+ output wire m_chdr_tlast,
+ output wire m_chdr_tvalid,
+ input wire m_chdr_tready,
+ // AXI-Stream interface to/from CPU
+ input wire [63:0] s_cpu_tdata,
+ input wire [3:0] s_cpu_tuser,
+ input wire s_cpu_tlast,
+ input wire s_cpu_tvalid,
+ output wire s_cpu_tready,
+ output wire [63:0] m_cpu_tdata,
+ output wire [3:0] m_cpu_tuser,
+ output wire m_cpu_tlast,
+ output wire m_cpu_tvalid,
+ input wire m_cpu_tready,
+ // Device addresses
+ input wire [47:0] my_eth_addr,
+ input wire [31:0] my_ipv4_addr,
+ input wire [15:0] my_udp_chdr_port
+);
+
+ `include "../core/rfnoc_chdr_utils.vh"
+ `include "../core/rfnoc_chdr_internal_utils.vh"
+ `include "rfnoc_xport_types.vh"
+
+ //-----------------------------------------------------------------------
+ // Byte-swapping function
+ // Ethernet fields we wrote out left-to-right, but AXI-Stream time-orders
+ // its data right-to-left.
+ //-----------------------------------------------------------------------
+ function [63:0] bswap64(
+ input [63:0] din
+ );
+ begin
+ bswap64 = {din[0 +: 8], din[8 +: 8], din[16 +: 8], din[24 +: 8],
+ din[32+: 8], din[40+: 8], din[48 +: 8], din[56 +: 8]};
+ end
+ endfunction
+
+ //---------------------------------------
+ // E2X and E2C DEMUX
+ //---------------------------------------
+ wire [63:0] e2x_chdr_tdata;
+ wire [95:0] e2x_chdr_tuser;
+ wire e2x_chdr_tlast, e2x_chdr_tvalid, e2x_chdr_tready;
+ wire [63:0] e2c_chdr_tdata;
+ wire [3:0] e2c_chdr_tuser;
+ wire e2c_chdr_tlast, e2c_chdr_tvalid, e2c_chdr_tready;
+
+ // Ethernet sink. Inspects packet and dispatches
+ // to the correct port.
+ eth_ipv4_chdr64_dispatch #(
+ .DROP_UNKNOWN_MAC(DROP_UNKNOWN_MAC)
+ ) eth_dispatch_i (
+ .clk (clk),
+ .rst (rst),
+ .s_mac_tdata (s_mac_tdata),
+ .s_mac_tuser (s_mac_tuser),
+ .s_mac_tlast (s_mac_tlast),
+ .s_mac_tvalid (s_mac_tvalid),
+ .s_mac_tready (s_mac_tready),
+ .m_chdr_tdata (e2x_chdr_tdata),
+ .m_chdr_tuser (e2x_chdr_tuser),
+ .m_chdr_tlast (e2x_chdr_tlast),
+ .m_chdr_tvalid (e2x_chdr_tvalid),
+ .m_chdr_tready (e2x_chdr_tready),
+ .m_cpu_tdata (e2c_chdr_tdata),
+ .m_cpu_tuser (e2c_chdr_tuser),
+ .m_cpu_tlast (e2c_chdr_tlast),
+ .m_cpu_tvalid (e2c_chdr_tvalid),
+ .m_cpu_tready (e2c_chdr_tready),
+ .my_eth_addr (my_eth_addr),
+ .my_ipv4_addr (my_ipv4_addr),
+ .my_udp_chdr_port (my_udp_chdr_port)
+ );
+
+ //---------------------------------------
+ // CHDR Transport Adapter
+ //---------------------------------------
+
+ wire [63:0] x2e_chdr_tdata;
+ wire [95:0] x2e_chdr_tuser;
+ wire x2e_chdr_tlast, x2e_chdr_tvalid, x2e_chdr_tready;
+ wire [63:0] e2x_fifo_tdata;
+ wire e2x_fifo_tlast, e2x_fifo_tvalid, e2x_fifo_tready;
+ wire [63:0] e2c_fifo_tdata;
+ wire [3:0] e2c_fifo_tuser;
+ wire e2c_fifo_tlast, e2c_fifo_tvalid, e2c_fifo_tready;
+
+ chdr_xport_adapter_generic #(
+ .PROTOVER(PROTOVER), .CHDR_W(64),
+ .USER_W(96), .TBL_SIZE(RT_TBL_SIZE),
+ .NODE_SUBTYPE(NODE_SUBTYPE_XPORT_IPV4_CHDR64), .NODE_INST(NODE_INST)
+ ) xport_adapter_gen_i (
+ .clk (clk),
+ .rst (rst),
+ .device_id (device_id),
+ .s_axis_xport_tdata (e2x_chdr_tdata),
+ .s_axis_xport_tuser (e2x_chdr_tuser),
+ .s_axis_xport_tlast (e2x_chdr_tlast),
+ .s_axis_xport_tvalid(e2x_chdr_tvalid),
+ .s_axis_xport_tready(e2x_chdr_tready),
+ .m_axis_xport_tdata (x2e_chdr_tdata),
+ .m_axis_xport_tuser (x2e_chdr_tuser),
+ .m_axis_xport_tlast (x2e_chdr_tlast),
+ .m_axis_xport_tvalid(x2e_chdr_tvalid),
+ .m_axis_xport_tready(x2e_chdr_tready),
+ .s_axis_rfnoc_tdata (s_chdr_tdata),
+ .s_axis_rfnoc_tlast (s_chdr_tlast),
+ .s_axis_rfnoc_tvalid(s_chdr_tvalid),
+ .s_axis_rfnoc_tready(s_chdr_tready),
+ .m_axis_rfnoc_tdata (e2x_fifo_tdata),
+ .m_axis_rfnoc_tlast (e2x_fifo_tlast),
+ .m_axis_rfnoc_tvalid(e2x_fifo_tvalid),
+ .m_axis_rfnoc_tready(e2x_fifo_tready),
+ .ctrlport_req_wr (/* unused */),
+ .ctrlport_req_rd (/* unused */),
+ .ctrlport_req_addr (/* unused */),
+ .ctrlport_req_data (/* unused */),
+ .ctrlport_resp_ack (/* unused */),
+ .ctrlport_resp_data (/* unused */)
+ );
+
+ generate
+ if (IS_CPU_ARM == 1'b1) begin
+ //---------------------------------------
+ // Ethernet framer for ARM
+ //---------------------------------------
+
+ // Strip the 6 octet ethernet padding we used internally
+ // before sending to ARM.
+ // Put SOF into bit[3] of tuser.
+ axi64_to_xge64 arm_framer (
+ .clk(clk),
+ .reset(rst),
+ .clear(1'b0),
+ .s_axis_tdata(e2c_chdr_tdata),
+ .s_axis_tuser(e2c_chdr_tuser),
+ .s_axis_tlast(e2c_chdr_tlast),
+ .s_axis_tvalid(e2c_chdr_tvalid),
+ .s_axis_tready(e2c_chdr_tready),
+ .m_axis_tdata(e2c_fifo_tdata),
+ .m_axis_tuser(e2c_fifo_tuser),
+ .m_axis_tlast(e2c_fifo_tlast),
+ .m_axis_tvalid(e2c_fifo_tvalid),
+ .m_axis_tready(e2c_fifo_tready)
+ );
+ end else begin
+ assign e2c_fifo_tdata = e2c_chdr_tdata;
+ assign e2c_fifo_tuser = e2c_chdr_tuser;
+ assign e2c_fifo_tlast = e2c_chdr_tlast;
+ assign e2c_fifo_tvalid = e2c_chdr_tvalid;
+ assign e2c_chdr_tready = e2c_fifo_tready;
+ end
+ endgenerate
+
+ //---------------------------------------
+ // E2X and E2C Output Buffering
+ //---------------------------------------
+
+ // The CPU can be slow to respond (relative to packet wirespeed) so
+ // extra buffer for packets destined there so it doesn't back up.
+ axi_fifo #(
+ .WIDTH(64+4+1),.SIZE(CPU_FIFO_SIZE)
+ ) cpu_fifo_i (
+ .clk(clk), .reset(rst), .clear(1'b0),
+ .i_tdata({e2c_fifo_tlast, e2c_fifo_tuser, e2c_fifo_tdata}),
+ .i_tvalid(e2c_fifo_tvalid), .i_tready(e2c_fifo_tready),
+ .o_tdata({m_cpu_tlast, m_cpu_tuser, m_cpu_tdata}),
+ .o_tvalid(m_cpu_tvalid), .o_tready(m_cpu_tready),
+ .occupied(), .space()
+ );
+
+ // The transport should hook up to a crossbar downstream, which
+ // may backpressure this module because it is in the middle of
+ // transferring a packet. To ensure that upstream logic is not
+ // blocked, we instantiate one packet worth of buffering here.
+ axi_fifo #(
+ .WIDTH(64+1),.SIZE(MTU)
+ ) chdr_fifo_i (
+ .clk(clk), .reset(rst), .clear(1'b0),
+ .i_tdata({e2x_fifo_tlast, e2x_fifo_tdata}),
+ .i_tvalid(e2x_fifo_tvalid), .i_tready(e2x_fifo_tready),
+ .o_tdata({m_chdr_tlast, m_chdr_tdata}),
+ .o_tvalid(m_chdr_tvalid), .o_tready(m_chdr_tready),
+ .occupied(), .space()
+ );
+
+ //---------------------------------------
+ // Ethernet Framer for X2E
+ //---------------------------------------
+ wire [63:0] x2e_framed_tdata;
+ wire [3:0] x2e_framed_tuser;
+ wire x2e_framed_tlast, x2e_framed_tvalid, x2e_framed_tready;
+
+ localparam [2:0] ST_IDLE = 3'd0;
+ localparam [2:0] ST_ETH_L0 = 3'd1;
+ localparam [2:0] ST_ETH_L1 = 3'd2;
+ localparam [2:0] ST_ETH_L2_IPV4_L0 = 3'd3;
+ localparam [2:0] ST_IPV4_L1 = 3'd4;
+ localparam [2:0] ST_IPV4_L2 = 3'd5;
+ localparam [2:0] ST_IPV4_UDP_HDR = 3'd6;
+ localparam [2:0] ST_CHDR_PAYLOAD = 3'd7;
+
+ reg [2:0] frame_state = ST_IDLE;
+ reg [15:0] chdr_len = 16'd0;
+ reg [63:0] frame_tdata;
+
+ always @(posedge clk) begin
+ if(rst) begin
+ frame_state <= ST_IDLE;
+ chdr_len <= 16'd0;
+ end else begin
+ case(frame_state)
+ ST_IDLE: begin
+ if (x2e_chdr_tvalid) begin
+ frame_state <= ST_ETH_L0;
+ chdr_len <= chdr_get_length(x2e_chdr_tdata);
+ end
+ end
+ ST_CHDR_PAYLOAD: begin
+ if (x2e_chdr_tvalid & x2e_framed_tready)
+ if (x2e_chdr_tlast)
+ frame_state <= ST_IDLE;
+ end
+ default: begin
+ if(x2e_framed_tready)
+ frame_state <= frame_state + 3'd1;
+ end
+ endcase
+ end
+ end
+
+ assign x2e_chdr_tready = (frame_state == ST_CHDR_PAYLOAD) ? x2e_framed_tready : 1'b0;
+ assign x2e_framed_tvalid = (frame_state == ST_CHDR_PAYLOAD) ? x2e_chdr_tvalid : (frame_state == ST_IDLE) ? 1'b0 : 1'b1;
+ assign x2e_framed_tlast = (frame_state == ST_CHDR_PAYLOAD) ? x2e_chdr_tlast : 1'b0;
+ assign x2e_framed_tuser = ((frame_state == ST_CHDR_PAYLOAD) & x2e_chdr_tlast) ? {1'b0, chdr_len[2:0]} : 4'b0000;
+ assign x2e_framed_tdata = frame_tdata;
+
+ wire [47:0] pad = 48'h0;
+ wire [47:0] mac_dst = x2e_chdr_tuser[47:0]; // Extract from router lookup results
+ wire [15:0] eth_type = 16'h0800; // IPv4
+ wire [15:0] misc_ip = { 4'd4 /* IPv4 */, 4'd5 /* IP HDR Len */, 8'h00 /* DSCP and ECN */};
+ wire [15:0] ip_len = (16'd28 + chdr_len); // 20 for IP, 8 for UDP
+ wire [15:0] ident = 16'h0;
+ wire [15:0] flag_frag = { 3'b010 /* don't fragment */, 13'h0 };
+ wire [15:0] ttl_prot = { 8'h10 /* TTL */, 8'h11 /* UDP */ };
+ wire [15:0] iphdr_checksum;
+ wire [31:0] ip_dst = x2e_chdr_tuser[79:48]; // Extract from router lookup results
+ wire [15:0] udp_dst = x2e_chdr_tuser[95:80]; // Extract from router lookup results
+ wire [15:0] udp_len = (16'd8 + chdr_len);
+ wire [15:0] udp_checksum = 16'h0;
+
+ ip_hdr_checksum ip_hdr_checksum (
+ .clk(clk), .in({misc_ip, ip_len, ident, flag_frag, ttl_prot, 16'd0, my_ipv4_addr, ip_dst}),
+ .out(iphdr_checksum)
+ );
+
+ always @(*) begin
+ case(frame_state)
+ ST_ETH_L0 : frame_tdata <= bswap64({pad[47:0], mac_dst[47:32]});
+ ST_ETH_L1 : frame_tdata <= bswap64({mac_dst[31:0], my_eth_addr[47:16]});
+ ST_ETH_L2_IPV4_L0 : frame_tdata <= bswap64({my_eth_addr[15:0], eth_type[15:0], misc_ip[15:0], ip_len[15:0]});
+ ST_IPV4_L1 : frame_tdata <= bswap64({ident[15:0], flag_frag[15:0], ttl_prot[15:0], iphdr_checksum[15:0]});
+ ST_IPV4_L2 : frame_tdata <= bswap64({my_ipv4_addr[31:0], ip_dst[31:0]});
+ ST_IPV4_UDP_HDR : frame_tdata <= bswap64({my_udp_chdr_port[15:0], udp_dst[15:0], udp_len[15:0], udp_checksum[15:0]});
+ default : frame_tdata <= x2e_chdr_tdata;
+ endcase
+ end
+
+ wire [63:0] c2e_tdata;
+ wire [3:0] c2e_tuser;
+ wire c2e_tlast;
+ wire c2e_tvalid;
+ wire c2e_tready;
+
+ generate
+ if (IS_CPU_ARM == 1'b1) begin
+ //---------------------------------------
+ // Ethernet deframer for ARM
+ //---------------------------------------
+
+ // Add pad of 6 empty bytes to the ethernet packet going from the CPU to the
+ // SFP. This padding added before MAC addresses aligns the source and
+ // destination IP addresses, UDP headers etc.
+ // Note that the xge_mac_wrapper strips this padding to recreate the ethernet
+ // packet
+ arm_deframer inst_arm_deframer
+ (
+ .clk(clk),
+ .reset(rst),
+ .clear(1'b0),
+
+ .s_axis_tdata(s_cpu_tdata),
+ .s_axis_tuser(s_cpu_tuser),
+ .s_axis_tlast(s_cpu_tlast),
+ .s_axis_tvalid(s_cpu_tvalid),
+ .s_axis_tready(s_cpu_tready),
+
+ .m_axis_tdata(c2e_tdata),
+ .m_axis_tuser(c2e_tuser),
+ .m_axis_tlast(c2e_tlast),
+ .m_axis_tvalid(c2e_tvalid),
+ .m_axis_tready(c2e_tready)
+ );
+ end else begin
+ assign c2e_tdata = s_cpu_tdata;
+ assign c2e_tuser = s_cpu_tuser;
+ assign c2e_tlast = s_cpu_tlast;
+ assign c2e_tvalid = s_cpu_tvalid;
+ assign s_cpu_tready = c2e_tready;
+ end
+ endgenerate
+
+ //---------------------------------------
+ // X2E and C2E MUX
+ //---------------------------------------
+ axi_mux #(
+ .SIZE(2), .PRIO(0), .WIDTH(64+4), .PRE_FIFO_SIZE(0), .POST_FIFO_SIZE(1)
+ ) eth_mux_i (
+ .clk(clk), .reset(rst), .clear(1'b0),
+ .i_tdata({c2e_tuser, c2e_tdata, x2e_framed_tuser, x2e_framed_tdata}), .i_tlast({c2e_tlast, x2e_framed_tlast}),
+ .i_tvalid({c2e_tvalid, x2e_framed_tvalid}), .i_tready({c2e_tready, x2e_framed_tready}),
+ .o_tdata({m_mac_tuser, m_mac_tdata}), .o_tlast(m_mac_tlast),
+ .o_tvalid(m_mac_tvalid), .o_tready(m_mac_tready)
+ );
+
+endmodule // eth_ipv4_chdr64_adapter
+`default_nettype wire
diff --git a/fpga/usrp3/lib/rfnoc/xport/eth_ipv4_chdr64_dispatch.v b/fpga/usrp3/lib/rfnoc/xport/eth_ipv4_chdr64_dispatch.v
new file mode 100644
index 000000000..b954a8100
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/xport/eth_ipv4_chdr64_dispatch.v
@@ -0,0 +1,472 @@
+//
+// Copyright 2019 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: eth_ipv4_chdr64_dispatch
+// Description:
+// This module serves as an Ethernet endpoint for CHDR traffic.
+// Ethernet frames arrive on the s_mac port where they are
+// inspected and classified as CHDR or !CHDR. A frame contains
+// CHDR payload if it is addressed to us (Eth and IP), is a UDP
+// packet and the destination port is one of the CHDR ports.
+// The UDP payload for CHDR frame is sent out of the m_chdr port
+// in addition to source information for Eth, IP and UDP. All
+// other traffic address to us (Eth) is sent to the m_cpu port.
+// Traffic not addressed (Eth) to us is dropped.
+//
+// Parameters:
+// - DROP_UNKNOWN_MAC: Drop packets not addressed to us?
+//
+// Signals:
+// - s_mac_*: The input Ethernet stream from the MAC (plus tuser for trailing bytes + err)
+// The tuser bits are the values defined in xge_mac_wrapper. Most
+// relevant is tuser[3], which signals a bad packet that must be
+// dropped.
+// - m_chdr_*: The output CHDR stream to the rfnoc infrastructure
+// - m_cpu_*: The output Ethernet stream to the CPU (plus tuser for trailing bytes + err)
+// - my_eth_addr: The Ethernet (MAC) address of this endpoint
+// - my_ipv4_addr: The IPv4 address of this endpoint
+// - my_udp_chdr_port: The UDP port allocated for CHDR traffic on this endpoint
+//
+
+`default_nettype none
+module eth_ipv4_chdr64_dispatch #(
+ parameter [0:0] DROP_UNKNOWN_MAC = 1
+)(
+ // Clocking and reset interface
+ input wire clk,
+ input wire rst,
+ // Input 68bit AXI-Stream interface (from MAC)
+ input wire [63:0] s_mac_tdata,
+ input wire [3:0] s_mac_tuser,
+ input wire s_mac_tlast,
+ input wire s_mac_tvalid,
+ output wire s_mac_tready,
+ // Output AXI-Stream interface to CHDR infrastructure
+ output wire [63:0] m_chdr_tdata,
+ output wire [95:0] m_chdr_tuser,
+ output wire m_chdr_tlast,
+ output wire m_chdr_tvalid,
+ input wire m_chdr_tready,
+ // Output AXI-Stream interface to CPU
+ output wire [63:0] m_cpu_tdata,
+ output wire [3:0] m_cpu_tuser,
+ output wire m_cpu_tlast,
+ output wire m_cpu_tvalid,
+ input wire m_cpu_tready,
+ // Device addresses
+ input wire [47:0] my_eth_addr,
+ input wire [31:0] my_ipv4_addr,
+ input wire [15:0] my_udp_chdr_port
+);
+
+ //---------------------------------------
+ // Ethernet/IP/UDP Magic Numbers
+ //---------------------------------------
+
+ localparam [47:0] ETH_ADDR_BCAST = {48{1'b1}};
+ localparam [15:0] ETH_TYPE_IPV4 = 16'h0800;
+ localparam [7:0] IPV4_PROTO_UDP = 8'h11;
+
+ //---------------------------------------
+ // Byte-swapping function
+ //---------------------------------------
+ function [15:0] bswap16(
+ input [15:0] din
+ );
+ begin
+ bswap16 = {din[0 +: 8], din[8 +: 8]};
+ end
+ endfunction
+
+ function [31:0] bswap32(
+ input [31:0] din
+ );
+ begin
+ bswap32 = {din[0 +: 8], din[8 +: 8],
+ din[16+: 8], din[24+: 8]};
+ end
+ endfunction
+
+ //---------------------------------------
+ // Input pipeline stage
+ //---------------------------------------
+
+ wire [63:0] in_tdata;
+ wire [3:0] in_tuser;
+ wire in_tlast, in_tvalid;
+ reg in_tready;
+
+ wire [63:0] cpu_tdata;
+ wire [3:0] cpu_tuser;
+ reg cpu_tlast, cpu_terror, cpu_tvalid;
+ wire cpu_tready;
+
+ wire [63:0] chdr_tdata;
+ wire [95:0] chdr_tuser;
+ wire chdr_tlast, chdr_tvalid, chdr_tready;
+ wire chdr_terror;
+
+ axi_fifo #(
+ .WIDTH(64+4+1),.SIZE(1)
+ ) in_reg_i (
+ .clk(clk), .reset(rst), .clear(1'b0),
+ .i_tdata({s_mac_tlast, s_mac_tuser, s_mac_tdata}),
+ .i_tvalid(s_mac_tvalid), .i_tready(s_mac_tready),
+ .o_tdata({in_tlast, in_tuser, in_tdata}),
+ .o_tvalid(in_tvalid), .o_tready(in_tready),
+ .space(), .occupied()
+ );
+
+ //---------------------------------------
+ // Classification state machine
+ //---------------------------------------
+
+ localparam [3:0] ST_IDLE_ETH_L0 = 4'd0;
+ localparam [3:0] ST_ETH_L1 = 4'd1;
+ localparam [3:0] ST_ETH_L2_IPV4_L0 = 4'd2;
+ localparam [3:0] ST_IPV4_L1 = 4'd3;
+ localparam [3:0] ST_IPV4_L2 = 4'd4;
+ localparam [3:0] ST_IPV4_UDP_HDR = 4'd5;
+ localparam [3:0] ST_FWD_CHDR = 4'd6;
+ localparam [3:0] ST_FWD_CPU = 4'd7;
+ localparam [3:0] ST_DROP_PKT = 4'd8;
+
+ // State info
+ reg [3:0] state = ST_IDLE_ETH_L0;
+ reg discard_cpu_pkt = 1'b0;
+
+ // Cached fields
+ reg [47:0] eth_dst_addr_cached, eth_src_addr_cached;
+ reg [31:0] ipv4_src_addr_cached;
+ reg [15:0] udp_src_port_cached;
+
+ always @(posedge clk) begin
+ if (rst) begin
+ state <= ST_IDLE_ETH_L0;
+ discard_cpu_pkt <= 1'b0;
+ end else if (in_tvalid && in_tready) begin
+ case (state)
+ // Idle or First line of Eth frame
+ // ----------------------------------
+ // | DstMAC_HI (16) | Preamble (48) |
+ // ----------------------------------
+ ST_IDLE_ETH_L0: begin
+ discard_cpu_pkt <= 1'b0;
+ if (!in_tlast) begin
+ // Just cache addresses. No decisions to be made.
+ eth_dst_addr_cached[47:32] <= bswap16(in_tdata[48 +: 16]);
+ if (in_tuser[3]) begin
+ state <= ST_DROP_PKT;
+ discard_cpu_pkt <= 1'b1;
+ end else begin
+ state <= ST_ETH_L1;
+ end
+ end else begin
+ // Short packet: Violates min eth size of 64 bytes
+ state <= ST_IDLE_ETH_L0;
+ end
+ end
+
+ // Second line of Eth frame
+ // -----------------------------------
+ // | SrcMAC_HI (32) | DstMAC_LO (32) |
+ // -----------------------------------
+ ST_ETH_L1: begin
+ if (!in_tlast) begin
+ // Just cache addresses. No decisions to be made.
+ eth_dst_addr_cached[31:0] <= bswap32(in_tdata[0 +: 32]);
+ eth_src_addr_cached[47:16] <= bswap32(in_tdata[32 +: 32]);
+ if (in_tuser[3]) begin
+ state <= ST_DROP_PKT;
+ discard_cpu_pkt <= 1'b1;
+ end else begin
+ state <= ST_ETH_L2_IPV4_L0;
+ end
+ end else begin
+ // Short packet: Violates min eth size of 64 bytes
+ state <= ST_IDLE_ETH_L0;
+ end
+ end
+
+ // End Eth frame and start of IP
+ // --------------------------------------------------
+ // | IPv4_Line0 (32)| EthType (16) | SrcMAC_LO (16) |
+ // --------------------------------------------------
+ ST_ETH_L2_IPV4_L0: begin
+ if (!in_tlast) begin
+ eth_src_addr_cached[15:0] <= bswap16(in_tdata[0 +: 16]);
+ if (in_tuser[3]) begin
+ state <= ST_DROP_PKT;
+ discard_cpu_pkt <= 1'b1;
+ end else if (eth_dst_addr_cached == ETH_ADDR_BCAST) begin
+ // If Eth destination is bcast then fwd to CPU
+ state <= ST_FWD_CPU;
+ end else if (eth_dst_addr_cached != my_eth_addr && DROP_UNKNOWN_MAC) begin
+ // If Eth destination is not us then drop the packet
+ state <= ST_DROP_PKT;
+ discard_cpu_pkt <= 1'b1;
+ end else if (bswap16(in_tdata[16 +: 16]) != ETH_TYPE_IPV4) begin
+ // If this is not an IPv4 frame then fwd to CPU
+ state <= ST_FWD_CPU;
+ end else begin
+ // Otherwise continue classification
+ // We know this is an IPv4 frame
+ state <= ST_IPV4_L1;
+ end
+ end else begin
+ // Short packet: Violates min eth size of 64 bytes
+ state <= ST_IDLE_ETH_L0;
+ end
+ end
+
+ // Continue IPv4 header
+ // -------------------------------------
+ // | IPv4_Line2 (32) | IPv4_Line1 (32) |
+ // -------------------------------------
+ ST_IPV4_L1: begin
+ if (!in_tlast) begin
+ if (in_tuser[3]) begin
+ state <= ST_DROP_PKT;
+ discard_cpu_pkt <= 1'b1;
+ end else if (in_tdata[40 +: 8] != IPV4_PROTO_UDP) begin
+ // If this is not a UDP frame then fwd to CPU
+ state <= ST_FWD_CPU;
+ end else begin
+ // Otherwise continue classification
+ // We know this is a UDP frame
+ state <= ST_IPV4_L2;
+ end
+ end else begin
+ // Short packet: Violates min eth size of 64 bytes
+ state <= ST_IDLE_ETH_L0;
+ end
+ end
+
+ // Continue IPv4 header
+ // -----------------------------------
+ // | IPDstAddr (32) | IPSrcAddr (32) |
+ // -----------------------------------
+ ST_IPV4_L2: begin
+ if (!in_tlast) begin
+ ipv4_src_addr_cached <= bswap32(in_tdata[0 +: 32]);
+ if (in_tuser[3]) begin
+ state <= ST_DROP_PKT;
+ discard_cpu_pkt <= 1'b1;
+ end else if (bswap32(in_tdata[32 +: 32]) != my_ipv4_addr) begin
+ // If IPv4 destination is not us then fwd to CPU
+ state <= ST_FWD_CPU;
+ end else begin
+ // Otherwise continue classification
+ // We know this is a UDP frame for us
+ state <= ST_IPV4_UDP_HDR;
+ end
+ end else begin
+ // Short packet: Violates min eth size of 64 bytes
+ state <= ST_IDLE_ETH_L0;
+ end
+ end
+
+ // UDP header
+ // -----------------------------------------------------------
+ // | Chksum (16) | Length (16) | DstPort (16) | SrcPort (16) |
+ // -----------------------------------------------------------
+ ST_IPV4_UDP_HDR: begin
+ if (!in_tlast) begin
+ udp_src_port_cached <= bswap16(in_tdata[0 +: 16]);
+ if (in_tuser[3]) begin
+ state <= ST_DROP_PKT;
+ discard_cpu_pkt <= 1'b1;
+ end else if (bswap16(in_tdata[16 +: 16]) == my_udp_chdr_port) begin
+ // The UDP port matches CHDR port
+ state <= ST_FWD_CHDR;
+ discard_cpu_pkt <= 1'b1;
+ end else begin
+ // Not the CHDR port. Forward to CPU
+ state <= ST_FWD_CPU;
+ end
+ end else begin
+ // Short packet: Violates min eth size of 64 bytes
+ state <= ST_IDLE_ETH_L0;
+ end
+ end
+
+ // CHDR Payload
+ ST_FWD_CHDR: begin
+ discard_cpu_pkt <= 1'b0;
+ if (in_tlast)
+ state <= ST_IDLE_ETH_L0;
+ end
+
+ // NotCHDR Payload: Send to CPU
+ ST_FWD_CPU: begin
+ if (in_tlast)
+ state <= ST_IDLE_ETH_L0;
+ end
+
+ // Unwanted Payload: Drop
+ ST_DROP_PKT: begin
+ discard_cpu_pkt <= 1'b0;
+ if (in_tlast)
+ state <= ST_IDLE_ETH_L0;
+ end
+
+ // We should never get here
+ default: begin
+ state <= ST_IDLE_ETH_L0;
+ end
+ endcase
+ end
+ end
+
+ always @(*) begin
+ case (state)
+ ST_IDLE_ETH_L0: begin
+ in_tready = cpu_tready;
+ cpu_tvalid = in_tvalid;
+ cpu_tlast = in_tlast;
+ cpu_terror = in_tlast; // Illegal short packet: Drop it
+ end
+ ST_ETH_L1: begin
+ in_tready = cpu_tready;
+ cpu_tvalid = in_tvalid;
+ cpu_tlast = in_tlast;
+ cpu_terror = in_tlast; // Illegal short packet: Drop it
+ end
+ ST_ETH_L2_IPV4_L0: begin
+ in_tready = cpu_tready;
+ cpu_tvalid = in_tvalid;
+ cpu_tlast = in_tlast;
+ cpu_terror = in_tlast; // Illegal short packet: Drop it
+ end
+ ST_IPV4_L1: begin
+ in_tready = cpu_tready;
+ cpu_tvalid = in_tvalid;
+ cpu_tlast = in_tlast;
+ cpu_terror = in_tlast; // Illegal short packet: Drop it
+ end
+ ST_IPV4_L2: begin
+ in_tready = cpu_tready;
+ cpu_tvalid = in_tvalid;
+ cpu_tlast = in_tlast;
+ cpu_terror = in_tlast; // Illegal short packet: Drop it
+ end
+ ST_IPV4_UDP_HDR: begin
+ in_tready = cpu_tready;
+ cpu_tvalid = in_tvalid;
+ cpu_tlast = in_tlast;
+ cpu_terror = in_tlast; // Illegal short packet: Drop it
+ end
+ ST_FWD_CHDR: begin
+ in_tready = chdr_tready & (discard_cpu_pkt ? cpu_tready : 1'b1);
+ cpu_tvalid = discard_cpu_pkt;
+ cpu_tlast = discard_cpu_pkt;
+ cpu_terror = discard_cpu_pkt;
+ end
+ ST_FWD_CPU: begin
+ in_tready = cpu_tready;
+ cpu_tvalid = in_tvalid;
+ cpu_tlast = in_tlast;
+ cpu_terror = 1'b0;
+ end
+ ST_DROP_PKT: begin
+ in_tready = discard_cpu_pkt ? cpu_tready : 1'b1;
+ cpu_tvalid = discard_cpu_pkt;
+ cpu_tlast = discard_cpu_pkt;
+ cpu_terror = discard_cpu_pkt;
+ end
+ default: begin
+ in_tready = 1'b0;
+ cpu_tvalid = 1'b0;
+ cpu_tlast = 1'b0;
+ cpu_terror = 1'b0;
+ end
+ endcase
+ end
+
+ assign cpu_tdata = in_tdata;
+ assign cpu_tuser = in_tuser;
+
+ assign chdr_tdata = in_tdata;
+ assign chdr_tuser = {udp_src_port_cached, ipv4_src_addr_cached, eth_src_addr_cached};
+ assign chdr_tlast = in_tlast;
+ assign chdr_tvalid = in_tvalid && (state == ST_FWD_CHDR);
+ assign chdr_terror = in_tuser[3];
+
+ //---------------------------------------
+ // Output processing
+ //---------------------------------------
+
+ wire [63:0] o_cpu_tdata;
+ wire [3:0] o_cpu_tuser;
+ wire o_cpu_terror, o_cpu_tlast, o_cpu_tvalid, o_cpu_tready;
+
+ axi_fifo #(
+ .WIDTH(64+4+1+1),.SIZE(1)
+ ) out_reg_cpu_i (
+ .clk(clk), .reset(rst), .clear(1'b0),
+ .i_tdata({cpu_tlast, cpu_terror, cpu_tuser, cpu_tdata}),
+ .i_tvalid(cpu_tvalid), .i_tready(cpu_tready),
+ .o_tdata({o_cpu_tlast, o_cpu_terror, o_cpu_tuser, o_cpu_tdata}),
+ .o_tvalid(o_cpu_tvalid), .o_tready(o_cpu_tready),
+ .space(), .occupied()
+ );
+
+ // We cannot make a CHDR/noCHDR routing decision until we are in the middle
+ // of a packet so we use a packet gate for the CPU path because we can rewind
+ // the write pointer and drop the packet in case it's destined for the CHDR
+ // path.
+ // NOTE: The SIZE of this FIFO must be 11 to accomodate a 9000 byte jumbo frame
+ // regardless of the CHDR MTU
+ axi_packet_gate #( .WIDTH(64+4), .SIZE(11), .USE_AS_BUFF(0) ) cpu_out_gate_i (
+ .clk(clk), .reset(rst), .clear(1'b0),
+ .i_tdata({o_cpu_tuser, o_cpu_tdata}), .i_tlast(o_cpu_tlast), .i_terror(o_cpu_terror | o_cpu_tuser[3]),
+ .i_tvalid(o_cpu_tvalid), .i_tready(o_cpu_tready),
+ .o_tdata({m_cpu_tuser, m_cpu_tdata}), .o_tlast(m_cpu_tlast),
+ .o_tvalid(m_cpu_tvalid), .o_tready(m_cpu_tready)
+ );
+
+ wire [63:0] o_chdr_tdata;
+ wire [95:0] o_chdr_tuser;
+ wire o_chdr_tlast, o_chdr_tvalid, o_chdr_tready;
+ wire o_chdr_data_tvalid, o_chdr_user_tvalid;
+ wire o_chdr_data_tready, o_chdr_user_tready;
+
+ axi_fifo #(
+ .WIDTH(96),.SIZE(8)
+ ) chdr_user_fifo_i (
+ .clk(clk), .reset(rst), .clear(1'b0),
+ .i_tdata(chdr_tuser),
+ .i_tvalid(chdr_tvalid & chdr_tready & chdr_tlast & ~chdr_terror), .i_tready(/* Always ready */),
+ .o_tdata(o_chdr_tuser),
+ .o_tvalid(o_chdr_user_tvalid), .o_tready(o_chdr_user_tready),
+ .space(), .occupied()
+ );
+
+ axi_packet_gate #(
+ .WIDTH(64), .SIZE(11), .USE_AS_BUFF(1), .MIN_PKT_SIZE(1)
+ ) chdr_out_gate_i (
+ .clk(clk), .reset(rst), .clear(1'b0),
+ .i_tdata(chdr_tdata), .i_tlast(chdr_tlast), .i_terror(chdr_terror),
+ .i_tvalid(chdr_tvalid), .i_tready(chdr_tready),
+ .o_tdata(o_chdr_tdata), .o_tlast(o_chdr_tlast),
+ .o_tvalid(o_chdr_data_tvalid), .o_tready(o_chdr_data_tready)
+ );
+
+ assign o_chdr_tvalid = o_chdr_data_tvalid & o_chdr_user_tvalid;
+ assign o_chdr_user_tready = o_chdr_tready & o_chdr_data_tvalid & o_chdr_tlast;
+ assign o_chdr_data_tready = o_chdr_tready & o_chdr_user_tvalid;
+
+ chdr_trim_payload #(
+ .CHDR_W(64), .USER_W(96)
+ ) chdr_trim_i (
+ .clk(clk), .rst(rst),
+ .s_axis_tdata(o_chdr_tdata), .s_axis_tuser(o_chdr_tuser),
+ .s_axis_tlast(o_chdr_tlast), .s_axis_tvalid(o_chdr_tvalid), .s_axis_tready(o_chdr_tready),
+ .m_axis_tdata(m_chdr_tdata), .m_axis_tuser(m_chdr_tuser),
+ .m_axis_tlast(m_chdr_tlast), .m_axis_tvalid(m_chdr_tvalid), .m_axis_tready(m_chdr_tready)
+ );
+
+endmodule // eth_ipv4_chdr64_dispatch
+`default_nettype wire
diff --git a/fpga/usrp3/lib/rfnoc/xport/liberio_chdr64_adapter.v b/fpga/usrp3/lib/rfnoc/xport/liberio_chdr64_adapter.v
new file mode 100644
index 000000000..2800c46bb
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/xport/liberio_chdr64_adapter.v
@@ -0,0 +1,120 @@
+//
+// Copyright 2019 Ettus Research, A National Instruments brand
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: liberio_chdr64_adapter
+// Description: The transport adapter for a liberio transport with CHDR_W of
+// 64. A tuser field is used to identify the DMA engine for return routes.
+// The stream for a given SrcEPID with ID s_dma_tuser will return packets to
+// that EPID with same ID on m_dma_tuser (after an Advertise management
+// operation).
+//
+// Parameters:
+// - PROTOVER: RFNoC protocol version {8'd<major>, 8'd<minor>}
+// - RT_TBL_SIZE: Log2 of the depth of the return-address routing table
+// - NODE_INST: The node type to return for a node-info discovery
+// - DMA_ID_WIDTH: The width of the tuser signal that identifies the DMA engine)
+//
+// Signals:
+// - device_id : The ID of the device that has instantiated this module
+// - s_dma_*: The input DMA stream from the CPU (plus tuser for source DMA engine ID)
+// - m_dma_*: The output DMA stream to the CPU (plus tuser for dest DMA engine ID)
+// - s_chdr_*: The input CHDR stream from the rfnoc infrastructure
+// - m_chdr_*: The output CHDR stream to the rfnoc infrastructure
+//
+
+module liberio_chdr64_adapter #(
+ parameter [15:0] PROTOVER = {8'd1, 8'd0},
+ parameter RT_TBL_SIZE = 6,
+ parameter NODE_INST = 0,
+ parameter DMA_ID_WIDTH = 8
+)(
+ // Clocking and reset interface
+ input wire clk,
+ input wire rst,
+ // Device info
+ input wire [15:0] device_id,
+ // AXI-Stream interface to/from DMA engines
+ input wire [63:0] s_dma_tdata,
+ input wire [DMA_ID_WIDTH-1:0] s_dma_tuser,
+ input wire s_dma_tlast,
+ input wire s_dma_tvalid,
+ output wire s_dma_tready,
+ output wire [63:0] m_dma_tdata,
+ output wire [DMA_ID_WIDTH-1:0] m_dma_tuser,
+ output wire m_dma_tlast,
+ output wire m_dma_tvalid,
+ input wire m_dma_tready,
+ // AXI-Stream interface to/from CHDR infrastructure
+ input wire [63:0] s_chdr_tdata,
+ input wire s_chdr_tlast,
+ input wire s_chdr_tvalid,
+ output wire s_chdr_tready,
+ output wire [63:0] m_chdr_tdata,
+ output wire m_chdr_tlast,
+ output wire m_chdr_tvalid,
+ input wire m_chdr_tready
+);
+
+ `include "../core/rfnoc_chdr_utils.vh"
+ `include "../core/rfnoc_chdr_internal_utils.vh"
+ `include "rfnoc_xport_types.vh"
+
+ //---------------------------------------
+ // CHDR Transport Adapter
+ //---------------------------------------
+ wire [DMA_ID_WIDTH-1:0] m_axis_xport_tuser;
+
+ chdr_xport_adapter_generic #(
+ .PROTOVER(PROTOVER), .CHDR_W(64),
+ .USER_W(DMA_ID_WIDTH), .TBL_SIZE(RT_TBL_SIZE),
+ .NODE_SUBTYPE(NODE_SUBTYPE_XPORT_LIBERIO_CHDR64), .NODE_INST(NODE_INST)
+ ) xport_adapter_gen_i (
+ .clk (clk),
+ .rst (rst),
+ .device_id (device_id),
+ .s_axis_xport_tdata (s_dma_tdata),
+ .s_axis_xport_tuser (s_dma_tuser),
+ .s_axis_xport_tlast (s_dma_tlast),
+ .s_axis_xport_tvalid(s_dma_tvalid),
+ .s_axis_xport_tready(s_dma_tready),
+ .m_axis_xport_tdata (m_dma_tdata),
+ .m_axis_xport_tuser (m_axis_xport_tuser),
+ .m_axis_xport_tlast (m_dma_tlast),
+ .m_axis_xport_tvalid(m_dma_tvalid),
+ .m_axis_xport_tready(m_dma_tready),
+ .s_axis_rfnoc_tdata (s_chdr_tdata),
+ .s_axis_rfnoc_tlast (s_chdr_tlast),
+ .s_axis_rfnoc_tvalid(s_chdr_tvalid),
+ .s_axis_rfnoc_tready(s_chdr_tready),
+ .m_axis_rfnoc_tdata (m_chdr_tdata),
+ .m_axis_rfnoc_tlast (m_chdr_tlast),
+ .m_axis_rfnoc_tvalid(m_chdr_tvalid),
+ .m_axis_rfnoc_tready(m_chdr_tready),
+ .ctrlport_req_wr (/* unused */),
+ .ctrlport_req_rd (/* unused */),
+ .ctrlport_req_addr (/* unused */),
+ .ctrlport_req_data (/* unused */),
+ .ctrlport_resp_ack (/* unused */ 1'b0),
+ .ctrlport_resp_data (/* unused */ 32'd0)
+ );
+
+ // Ensure tdest does not change for entire packet
+ reg m_hdr = 1'b1;
+ always @(posedge clk) begin
+ if (rst)
+ m_hdr <= 1'b1;
+ else if (m_dma_tvalid && m_dma_tready)
+ m_hdr <= m_dma_tlast;
+ end
+
+ reg [DMA_ID_WIDTH-1:0] cached_dest = {DMA_ID_WIDTH{1'b0}};
+ always @(posedge clk) begin
+ if (m_hdr)
+ cached_dest <= m_axis_xport_tuser;
+ end
+
+ assign m_dma_tuser = m_hdr ? m_axis_xport_tuser : cached_dest;
+
+endmodule // liberio_chdr64_adapter
diff --git a/fpga/usrp3/lib/rfnoc/xport/rfnoc_xport_types.vh b/fpga/usrp3/lib/rfnoc/xport/rfnoc_xport_types.vh
new file mode 100644
index 000000000..5b273a4b4
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/xport/rfnoc_xport_types.vh
@@ -0,0 +1,11 @@
+//
+// Copyright 2019 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+
+// Add all new transport types here
+localparam [7:0] NODE_SUBTYPE_XPORT_GENERIC = 8'd0;
+localparam [7:0] NODE_SUBTYPE_XPORT_IPV4_CHDR64 = 8'd1;
+localparam [7:0] NODE_SUBTYPE_XPORT_LIBERIO_CHDR64 = 8'd2;
+localparam [7:0] NODE_SUBTYPE_XPORT_NIRIO_CHDR64 = 8'd3;