Files
b210-k7-fpga/lib/rfnoc/xport_sv/eth_ipv4_internal.sv
T
Wade Fife 032b9677aa fpga: lib: Add NET_CHDR_W parameter to transport adapters
The NET_CHDR_W parameter tells the transport adapter what CHDR width
is used in transport packets, such as Ethernet packets. By default the
CHDR width used by the transport and RFNoC core will be the same. The
NET_CHDR_W parameter can be used in situations where the RFNoC core
expects a CHDR width from the transport adapter that is different from
that used over the network. In this situation, the transport adapter
will rewrite the CHDR packets so that each side gets the expected CHDR
width.


Original-commit: a9e49b7c587ef778ee0fe327e8bdcb15e0b0fc95
2022-08-29 17:01:45 -05:00

446 lines
15 KiB
Systemverilog

//
// Copyright 2020 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: eth_ipv4_internal
//
// Description:
//
// This internal Ethernet port is responsible for routing CHDR data between
// the ARM CPU and RFNoC. Treating the RFNoC interface to the CPU like an
// internal Ethernet device allows the ARM processor to take advantage of
// highly optimized DMA engines and software designed for Ethernet. This
// block also includes an ARP responder for IP address discovery.
//
// Prefixes are used to distinguish the various AXI-Stream buses:
//
// - e2h : Ethernet to Host (Ethernet transport adapter to ARM)
// - h2e : Host to Ethernet (ARM to Ethernet transport adapter)
// - e2v : Ethernet to CHDR (Ethernet transport to RFNoC)
// - v2e : CHDR to Ethernet (RFNoC to Ethernet transport adapter)
// - e2c : Ethernet to CPU (Ethernet transport adapter to ARP responder)
// - c2e : CPU to Ethernet (ARP responder to Ethernet transport adapter)
//
// Parameters:
//
// CHDR_W : CHDR width used by RFNoC on the FPGA
// NET_CHDR_W : CHDR width used over the network connection
// BYTE_MTU : Sets the MTU to 2^BYTE_MTU bytes
// DWIDTH : Data width for AXI-Lite interface (32 or 64)
// AWIDTH : Address width for AXI-Lite interface
// PORTNUM : Ethernet port number
// RFNOC_PROTOVER : 16-bit RFNoC protocol version (major[7:0], minor[7:0])
//
`default_nettype none
module eth_ipv4_internal #(
parameter CHDR_W = 64,
parameter NET_CHDR_W = CHDR_W,
parameter BYTE_MTU = 10,
parameter DWIDTH = 32,
parameter AWIDTH = 14,
parameter [ 7:0] PORTNUM = 0,
parameter [15:0] RFNOC_PROTOVER = {8'd1, 8'd0}
) (
input wire bus_clk,
input wire bus_rst,
// 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 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 reg [CHDR_W-1:0] e2v_tdata,
output reg e2v_tlast,
output reg e2v_tvalid,
input wire e2v_tready,
input wire [CHDR_W-1:0] v2e_tdata,
input wire v2e_tlast,
input wire v2e_tvalid,
output reg v2e_tready,
// Misc
input wire [15:0] device_id
);
// The CPU (host DMA) interface is currently fixed at 64 bits, due to the
// arp_responder and arm_framer/deframer only supporting 64 bits.
localparam CPU_W = 64;
//---------------------------------------------------------------------------
// AXI-Lite to RegPort Register Access Bridge
//---------------------------------------------------------------------------
localparam REG_BASE_ETH_IO = 14'h0;
localparam REG_BASE_ETH_SWITCH = 14'h1000;
logic reg_wr_req;
logic [AWIDTH-1:0] reg_wr_addr;
logic [DWIDTH-1:0] reg_wr_data;
logic reg_rd_req;
logic [AWIDTH-1:0] reg_rd_addr;
logic reg_rd_resp;
logic [DWIDTH-1:0] reg_rd_data;
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) // 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),
.reg_wr_keep (),
// 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)
);
logic reg_rd_resp_eth_if;
logic reg_rd_resp_io = 1'b0;
logic [DWIDTH-1:0] reg_rd_data_eth_if;
logic [DWIDTH-1:0] reg_rd_data_io = 'd0;
// RegPort mux for responses
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)
);
//---------------------------------------------------------------------------
// ARM Framer/Deframer
//---------------------------------------------------------------------------
//
// The arm_deframer removes bytes from the beginning of every packet sent by
// the ARM processor to give the packets a specific alignment that will be
// used later. The framer does the opposite, padding the packet before
// sending it to the ARM CPU.
//
//---------------------------------------------------------------------------
// Host Ethernet-to-CHDR
logic [63:0] h2e_chdr_tdata;
logic [3:0] h2e_chdr_tuser;
logic h2e_chdr_tlast;
logic h2e_chdr_tvalid;
logic h2e_chdr_tready;
//
logic [63:0] e2h_chdr_tdata;
logic [3:0] e2h_chdr_tuser;
logic e2h_chdr_tlast;
logic e2h_chdr_tvalid;
logic e2h_chdr_tready;
logic [3:0] e2h_tuser;
logic [3:0] h2e_tuser;
// 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;
// Convert 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;
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_i (
.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)
);
//---------------------------------------------------------------------------
// Ethernet Interface
//---------------------------------------------------------------------------
// FPGA-side addresses for the ARP responder
logic [47:0] my_mac;
logic [31:0] my_ip;
// ARP responder signals
logic [63:0] e2c_tdata;
logic [7:0] e2c_tkeep;
logic e2c_tlast;
logic e2c_tvalid;
logic e2c_tready;
//
logic [63:0] c2e_tdata;
logic [7:0] c2e_tkeep;
logic c2e_tlast;
logic c2e_tvalid;
logic c2e_tready;
localparam CPU_USER_W = $clog2(CPU_W/8)+1; // SOF + trailing bytes
// Host DMA interfaces
AxiStreamIf #(.DATA_WIDTH(CPU_W), .USER_WIDTH(CPU_USER_W), .TUSER(1), .TKEEP(0))
e2h_chdr(bus_clk, bus_rst);
AxiStreamIf #(.DATA_WIDTH(CPU_W), .USER_WIDTH(CPU_USER_W), .TUSER(1), .TKEEP(0))
h2e_chdr(bus_clk, bus_rst);
// RFNoC Interfaces
AxiStreamIf #(.DATA_WIDTH(CHDR_W), .TUSER(0), .TKEEP(0))
e2v_chdr(bus_clk, bus_rst);
AxiStreamIf #(.DATA_WIDTH(CHDR_W), .TUSER(0), .TKEEP(0))
v2e_chdr(bus_clk, bus_rst);
// ARP Responder Interfaces
AxiStreamIf #(.DATA_WIDTH(CPU_W), .TUSER(0), .TKEEP(1))
e2c_chdr(bus_clk, bus_rst);
AxiStreamIf #(.DATA_WIDTH(CPU_W), .TUSER(0), .TKEEP(1))
c2e_chdr(bus_clk, bus_rst);
// Translate between SystemVerilog interfaces and Verilog signals
always_comb begin
e2h_chdr_tdata = e2h_chdr.tdata;
e2h_chdr_tlast = e2h_chdr.tlast;
e2h_chdr_tvalid = e2h_chdr.tvalid;
e2h_chdr_tuser = e2h_chdr.tuser;
e2h_chdr.tready = e2h_chdr_tready;
h2e_chdr.tdata = h2e_chdr_tdata;
h2e_chdr.tlast = h2e_chdr_tlast;
h2e_chdr.tvalid = h2e_chdr_tvalid;
h2e_chdr.tuser = h2e_chdr_tuser;
h2e_chdr_tready = h2e_chdr.tready;
e2v_tdata = e2v_chdr.tdata;
e2v_tlast = e2v_chdr.tlast;
e2v_tvalid = e2v_chdr.tvalid;
e2v_chdr.tready = e2v_tready;
v2e_chdr.tdata = v2e_tdata;
v2e_chdr.tlast = v2e_tlast;
v2e_chdr.tvalid = v2e_tvalid;
v2e_tready = v2e_chdr.tready;
e2c_tdata = e2c_chdr.tdata;
e2c_tlast = e2c_chdr.tlast;
e2c_tkeep = e2c_chdr.tkeep;
e2c_tvalid = e2c_chdr.tvalid;
e2c_chdr.tready = e2c_tready;
c2e_chdr.tdata = c2e_tdata;
c2e_chdr.tlast = c2e_tlast;
c2e_chdr.tkeep = c2e_tkeep;
c2e_chdr.tvalid = c2e_tvalid;
c2e_tready = c2e_chdr.tready;
end
eth_ipv4_interface #(
.PROTOVER (RFNOC_PROTOVER),
.CPU_FIFO_SIZE (BYTE_MTU),
.CHDR_FIFO_SIZE (BYTE_MTU),
.NODE_INST (0),
.BASE (REG_BASE_ETH_SWITCH),
.PREAMBLE_BYTES (6),
.ADD_SOF (1),
.ENET_W (CPU_W),
.CPU_W (CPU_W),
.CHDR_W (CHDR_W),
.NET_CHDR_W (NET_CHDR_W)
) eth_ipv4_interface_i (
.bus_clk (bus_clk),
.bus_rst (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),
.eth_pause_req (),
.eth_tx (e2h_chdr),
.eth_rx (h2e_chdr),
.e2v (e2v_chdr),
.v2e (v2e_chdr),
.e2c (e2c_chdr),
.c2e (c2e_chdr),
.my_udp_chdr_port (),
.my_ip (my_ip),
.my_mac (my_mac)
);
//---------------------------------------------------------------------------
// ARP Responder
//---------------------------------------------------------------------------
//
// This block sends replies to ARP IPv4 frames.
//
//---------------------------------------------------------------------------
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 ()
);
//---------------------------------------------------------------------------
// NIXGE Registers
//---------------------------------------------------------------------------
//
// Implement the minimum subset of registers needed by the NIXGE driver for
// our internal Ethernet port. Only the NIXGE_REG_LED_CTL register is
// actually used, but the internal adapter doesn't need LED control. So all
// registers read as 0 and all writes are ignored.
//
//---------------------------------------------------------------------------
// NIXGE 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;
always @(posedge bus_clk) begin
if (reg_rd_req) begin
case(reg_rd_addr[AWIDTH-1:2])
REG_PORT_INFO [AWIDTH-1:2] |
REG_MAC_CTRL_STATUS[AWIDTH-1:2] |
REG_PHY_CTRL_STATUS[AWIDTH-1:2] |
REG_MAC_LED_CTL [AWIDTH-1:2]:
reg_rd_resp_io <= 1'b1;
default:
reg_rd_resp_io <= 1'b0;
endcase
end
end
endmodule
`default_nettype wire