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; | 
