443 lines
15 KiB
Systemverilog
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
|