diff options
Diffstat (limited to 'fpga/usrp3/lib/rfnoc/xport')
-rw-r--r-- | fpga/usrp3/lib/rfnoc/xport/Makefile.srcs | 17 | ||||
-rw-r--r-- | fpga/usrp3/lib/rfnoc/xport/chdr_xport_adapter_generic.v | 397 | ||||
-rw-r--r-- | fpga/usrp3/lib/rfnoc/xport/eth_interface.v | 252 | ||||
-rw-r--r-- | fpga/usrp3/lib/rfnoc/xport/eth_internal.v | 433 | ||||
-rw-r--r-- | fpga/usrp3/lib/rfnoc/xport/eth_ipv4_chdr64_adapter.v | 397 | ||||
-rw-r--r-- | fpga/usrp3/lib/rfnoc/xport/eth_ipv4_chdr64_dispatch.v | 472 | ||||
-rw-r--r-- | fpga/usrp3/lib/rfnoc/xport/liberio_chdr64_adapter.v | 120 | ||||
-rw-r--r-- | fpga/usrp3/lib/rfnoc/xport/rfnoc_xport_types.vh | 11 |
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; |