Files
b210-k7-fpga/lib/rfnoc/xport/eth_ipv4_chdr64_adapter.v
T
Wade Fife 3810f5a4d9 rfnoc: Add management filter to generic xport
Original-commit: bdcb95c56bdd213af534d312a1abd8ab69dcc214
2020-02-19 13:27:16 -06:00

402 lines
15 KiB
Verilog

//
// 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),
.ALLOW_DISC (1)
) 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