Files
b210-k7-fpga/lib/rfnoc/xport_sv/eth_ipv4_internal.sv
T
Andrew Moch d7acf31a38 fpga: lib: add pause support to ethernet xport
Original-commit: d099fc3b032250bcc70e4c24f78d5eb6508850e1
2021-06-03 11:26:54 -05:00

443 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
// 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 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)
) 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