fpga: lib: Add width agnostic version of Ethernet Interface
The rnfoc/xport section is refactored in System Verilog to allow the
following improvements
(1) CPU_W - Sets the size of the c2e and e2c pipes. This can be run
at a different clock rate than the main ethernet pipe
(2) CHDR_W - Sets the size of the v2e and e2v pipes. This can be run
at a different clock rate than the main ethernet pipe
(3) ENET_W - Sets the size of the eth_tx and eth_rx pipes.
eth_interface_tb runs traffic from e2c,e2v,v2e,c2e simultaneously
against the original xport_sv implementation, and against the new
implementation with widths of 64/128/512. A chdr_management node
info request queries the port info of the node0 in the eth_interface.
eth_ifc_synth_test.sv can be compiled with the make xsim target to test
out the size of various configurations.
Original-commit: eed4988cc266a63370a4332351d02fadedde3a3b
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
#
|
||||
# Copyright 2020 Ettus Research, a National Instruments Brand
|
||||
#
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
#
|
||||
|
||||
##################################################
|
||||
# RFNoC Utility Sources
|
||||
##################################################
|
||||
RFNOC_XPORT_SV_SRCS = $(abspath $(addprefix $(BASE_DIR)/../lib/rfnoc/xport_sv/, \
|
||||
chdr_xport_adapter.sv \
|
||||
eth_ipv4_add_udp.sv \
|
||||
eth_ipv4_chdr_adapter.sv \
|
||||
eth_ipv4_chdr_dispatch.sv \
|
||||
eth_ipv4_interface.sv \
|
||||
))
|
||||
@@ -0,0 +1,529 @@
|
||||
//
|
||||
// Copyright 2020 Ettus Research, A National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: chdr_xport_adapter (Ethernet IPV4)
|
||||
//
|
||||
// Description: An Xport transport adapter module that does the following:
|
||||
// - Exposes a configuration port for mgmt packets to configure the node.
|
||||
// (chdr_mgmt_pkt_handler)
|
||||
// - 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. (kv_map)
|
||||
// - Implements a loopback path for node-info discovery (axi_switch/axi_mux)
|
||||
// - Strip UDP headers and extract mac/ip/udp src addresses
|
||||
// - Add UDP header for outgoing ethernet traffic
|
||||
//
|
||||
// Parameters:
|
||||
// - PROTOVER: RFNoC protocol version {8'd<major>, 8'd<minor>}
|
||||
// - TBL_SIZE: Log2 of the depth of the routing table
|
||||
// - NODE_SUBTYPE: The node subtype to return for a node-info discovery
|
||||
// - NODE_INST: The node type to return for a node-info discovery
|
||||
// - ALLOW_DISC: Controls if the external transport network should be
|
||||
// discoverable by management packets from RFNoC side.
|
||||
//
|
||||
// Signals:
|
||||
// - device_id : The ID of the device that has instantiated this module
|
||||
// - my_* : MAC address, IP address, and UDP port that responds/accepts CHDR traffic
|
||||
// - eth_rx : The input CHDR stream from the transport
|
||||
// - eth_tx : The output CHDR stream to transport
|
||||
// - v2e : The input CHDR stream from the rfnoc infrastructure
|
||||
// - e2v : The output CHDR stream to the rfnoc infrastructure
|
||||
//
|
||||
|
||||
`include "../xport/rfnoc_xport_types.vh"
|
||||
|
||||
module chdr_xport_adapter #(
|
||||
int PREAMBLE_BYTES = 6,
|
||||
int MAX_PACKET_BYTES = 2**16,
|
||||
logic [15:0] PROTOVER = {8'd1, 8'd0},
|
||||
int TBL_SIZE = 6,
|
||||
logic [7:0] NODE_SUBTYPE = NODE_SUBTYPE_XPORT_IPV4_CHDR64,
|
||||
int NODE_INST = 0,
|
||||
bit ALLOW_DISC = 1
|
||||
)(
|
||||
// Device info
|
||||
input logic [15:0] device_id,
|
||||
// Device addresses
|
||||
input logic [47:0] my_mac,
|
||||
input logic [31:0] my_ip,
|
||||
input logic [15:0] my_udp_chdr_port,
|
||||
|
||||
// Ethernet
|
||||
AxiStreamIf.slave eth_rx, // tUser={*not used*}
|
||||
AxiStreamIf.master eth_tx, // tUser={1'b0,trailing bytes}
|
||||
|
||||
// CHDR
|
||||
AxiStreamIf.slave v2e, // tUser={*not used*}
|
||||
AxiStreamIf.master e2v // tUser={*not used*}
|
||||
);
|
||||
|
||||
//used to store {udp, ipv4, mac}
|
||||
localparam USER_META_W = 96;
|
||||
localparam ENET_USER_W = $clog2(eth_rx.DATA_WIDTH/8)+1;
|
||||
// ---------------------------------------------------
|
||||
// RFNoC Includes
|
||||
// ---------------------------------------------------
|
||||
`include "../core/rfnoc_chdr_utils.vh"
|
||||
`include "../core/rfnoc_chdr_internal_utils.vh"
|
||||
`include "eth_constants.vh"
|
||||
`include "../../axi4s_sv/axi4s.vh"
|
||||
|
||||
// tUser={None}
|
||||
AxiStreamPacketIf #(.DATA_WIDTH(eth_rx.DATA_WIDTH),.TKEEP(0),.TUSER(0),
|
||||
.MAX_PACKET_BYTES(MAX_PACKET_BYTES))
|
||||
ru1(eth_rx.clk,eth_rx.rst);// Packet handler input
|
||||
// tUser={None}
|
||||
AxiStreamIf #(.DATA_WIDTH(eth_rx.DATA_WIDTH),.TKEEP(0),.TUSER(0),
|
||||
.MAX_PACKET_BYTES(MAX_PACKET_BYTES))
|
||||
ru2(eth_rx.clk,eth_rx.rst);// Packet handler input
|
||||
// tUser={udp_src_port,ipv4_src_addr,eth_src_addr}
|
||||
AxiStreamIf #(.DATA_WIDTH(eth_rx.DATA_WIDTH),.USER_WIDTH(USER_META_W),.TKEEP(0))
|
||||
ru3(eth_rx.clk,eth_rx.rst);// Packet handler input
|
||||
AxiStreamIf #(.DATA_WIDTH(eth_rx.DATA_WIDTH),.USER_WIDTH(USER_META_W),.TKEEP(0))
|
||||
ru4(eth_rx.clk,eth_rx.rst);// Packet handler input
|
||||
AxiStreamIf #(.DATA_WIDTH(eth_rx.DATA_WIDTH),.USER_WIDTH(USER_META_W),.TKEEP(0))
|
||||
ph(eth_rx.clk,eth_rx.rst);// Packet handler input
|
||||
// tUser={udp_src_port,ipv4_src_addr,eth_src_addr}
|
||||
AxiStreamIf #(.DATA_WIDTH(eth_rx.DATA_WIDTH),.USER_WIDTH(USER_META_W),.TKEEP(0))
|
||||
e2d(eth_rx.clk,eth_rx.rst);// Eth => Demux
|
||||
logic [1:0] e2d_tid;
|
||||
// tUser={udp_src_port,ipv4_src_addr,eth_src_addr}
|
||||
AxiStreamIf #(.DATA_WIDTH(eth_rx.DATA_WIDTH),.USER_WIDTH(USER_META_W),.TKEEP(0))
|
||||
e2e(eth_rx.clk,eth_rx.rst);// Eth => Eth (loopback)
|
||||
// tUser={udp_dst_port, ipv4_dst_addr, eth_dst_addr}
|
||||
AxiStreamIf #(.DATA_WIDTH(eth_rx.DATA_WIDTH),.USER_WIDTH(USER_META_W),.TKEEP(0))
|
||||
m2e(eth_rx.clk,eth_rx.rst);// Mux => Eth
|
||||
logic m2e_tdest; // 1: Return to src, 0: CHDR input
|
||||
|
||||
// ---------------------------------------------------
|
||||
// Strip UDP and grab {udp_src_port_old, ipv4_src_addr_old, eth_src_addr_old}
|
||||
// ---------------------------------------------------
|
||||
|
||||
always_comb begin : assign_ru1
|
||||
`AXI4S_ASSIGN(ru1,eth_rx)
|
||||
end
|
||||
|
||||
// Cached fields
|
||||
logic [47:0] eth_src_addr_new, eth_src_addr_old;
|
||||
logic [31:0] ipv4_src_addr_new, ipv4_src_addr_old;
|
||||
logic [15:0] udp_src_port_new, udp_src_port_old;
|
||||
|
||||
// save the fields
|
||||
always_ff @(posedge eth_rx.clk) begin : field_ff
|
||||
if (eth_rx.rst) begin
|
||||
eth_src_addr_old <= '0;
|
||||
ipv4_src_addr_old <= '0;
|
||||
udp_src_port_old <= '0;
|
||||
end else begin
|
||||
eth_src_addr_old <= eth_src_addr_new;
|
||||
ipv4_src_addr_old <= ipv4_src_addr_new;
|
||||
udp_src_port_old <= udp_src_port_new;
|
||||
end
|
||||
end
|
||||
|
||||
// get the fields - don't use assign. assign will not activate with changes to eth_rx.
|
||||
always_comb begin : get_fields
|
||||
eth_src_addr_new = ru1.get_packet_field48(eth_src_addr_old,SRC_MAC_BYTE,.NETWORK_ORDER(1));
|
||||
ipv4_src_addr_new = ru1.get_packet_field32(ipv4_src_addr_old,SRC_IP_BYTE,.NETWORK_ORDER(1));
|
||||
udp_src_port_new = ru1.get_packet_field16(udp_src_port_old,SRC_PORT_BYTE,.NETWORK_ORDER(1));
|
||||
end
|
||||
|
||||
// Strip the udp header
|
||||
axi4s_remove_bytes #(.REM_START(0),.REM_END(UDP_END)
|
||||
) strip_udp (
|
||||
.i(ru1), .o(ru2)
|
||||
);
|
||||
|
||||
// start driving the port information
|
||||
always_comb begin : assign_ru3
|
||||
`AXI4S_ASSIGN(ru3,ru2)
|
||||
ru3.tuser = {udp_src_port_old, ipv4_src_addr_old, eth_src_addr_old};
|
||||
end
|
||||
|
||||
|
||||
chdr_trim_payload #(
|
||||
.CHDR_W(eth_rx.DATA_WIDTH), .USER_W(USER_META_W)
|
||||
) chdr_trim_i (
|
||||
.clk(eth_rx.clk), .rst(eth_rx.rst),
|
||||
.s_axis_tdata(ru3.tdata), .s_axis_tuser(ru3.tuser),
|
||||
.s_axis_tlast(ru3.tlast), .s_axis_tvalid(ru3.tvalid), .s_axis_tready(ru3.tready),
|
||||
.m_axis_tdata(ru4.tdata), .m_axis_tuser(ru4.tuser),
|
||||
.m_axis_tlast(ru4.tlast), .m_axis_tvalid(ru4.tvalid), .m_axis_tready(ru4.tready)
|
||||
);
|
||||
|
||||
// Pay close attention to when ph.tuser swtiches versus when it is needed!
|
||||
always_comb begin : assign_ph
|
||||
`AXI4S_ASSIGN(ph,ru4)
|
||||
end
|
||||
|
||||
// ---------------------------------------------------
|
||||
// Transport => DEMUX
|
||||
// ---------------------------------------------------
|
||||
logic op_stb;
|
||||
logic [15:0] op_src_epid;
|
||||
logic [USER_META_W-1:0] op_data;
|
||||
logic lookup_stb, lookup_done_stb, lookup_result_match;
|
||||
logic [15:0] lookup_epid;
|
||||
logic [USER_META_W-1:0] lookup_result_value;
|
||||
logic [47:0] node_info;
|
||||
|
||||
always_comb node_info = chdr_mgmt_build_node_info(
|
||||
{ 10'h0, NODE_SUBTYPE},
|
||||
NODE_INST, NODE_TYPE_TRANSPORT, device_id);
|
||||
|
||||
chdr_mgmt_pkt_handler #(
|
||||
.PROTOVER(PROTOVER), .CHDR_W(eth_rx.DATA_WIDTH), .USER_W(USER_META_W), .MGMT_ONLY(0)
|
||||
) mgmt_ep_i (
|
||||
.clk(eth_rx.clk), .rst(eth_rx.rst),
|
||||
.node_info(node_info),
|
||||
//ph in
|
||||
.s_axis_chdr_tdata(ph.tdata), .s_axis_chdr_tlast(ph.tlast),
|
||||
.s_axis_chdr_tvalid(ph.tvalid), .s_axis_chdr_tready(ph.tready),
|
||||
.s_axis_chdr_tuser(ph.tuser),
|
||||
//e2d out
|
||||
.m_axis_chdr_tdata(e2d.tdata), .m_axis_chdr_tlast(e2d.tlast),
|
||||
.m_axis_chdr_tdest(/* unused */), .m_axis_chdr_tid(e2d_tid),
|
||||
.m_axis_chdr_tvalid(e2d.tvalid), .m_axis_chdr_tready(e2d.tready),
|
||||
//unused ctrlport
|
||||
.ctrlport_req_wr (/* unused */),
|
||||
.ctrlport_req_rd (/* unused */),
|
||||
.ctrlport_req_addr (/* unused */),
|
||||
.ctrlport_req_data (/* unused */),
|
||||
.ctrlport_resp_ack (1'b0 /* unused */),
|
||||
.ctrlport_resp_data (32'b0 /* unused */),
|
||||
// kv_map lookups
|
||||
.op_stb(op_stb),
|
||||
.op_dst_epid(/* unused */),
|
||||
.op_src_epid(op_src_epid),
|
||||
.op_data(op_data)
|
||||
);
|
||||
|
||||
// Key/Value map.
|
||||
// Stores the destination address information for UDP
|
||||
// -- storage is controlled from the chdr_managment_node
|
||||
// -- lookup is done on each packet passing out
|
||||
kv_map #(
|
||||
.KEY_WIDTH(16), .VAL_WIDTH(USER_META_W), .SIZE(TBL_SIZE)
|
||||
) kv_map_i (
|
||||
.clk(eth_rx.clk), .reset(eth_rx.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 */)
|
||||
);
|
||||
|
||||
logic ph_hdr = 1'b1;
|
||||
always_ff @(posedge eth_rx.clk) begin
|
||||
if (eth_rx.rst)
|
||||
ph_hdr <= 1'b1;
|
||||
else if (ph.tvalid && ph.tready)
|
||||
ph_hdr <= ph.tlast;
|
||||
end
|
||||
|
||||
// chdr_mgmt_pkt_handler does not buffer packets and has at least one cycle of delay.
|
||||
// The tuser caching logic could be more robust.
|
||||
always_ff @(posedge eth_rx.clk) begin
|
||||
if (ph.tvalid && ph.tready && ph_hdr)
|
||||
e2d.tuser <= ph.tuser;
|
||||
end
|
||||
|
||||
// ---------------------------------------------------
|
||||
// Optional management filter
|
||||
// ---------------------------------------------------
|
||||
// tUser={*not used*}
|
||||
AxiStreamIf #(.DATA_WIDTH(eth_rx.DATA_WIDTH),.TUSER(0),.TKEEP(0))
|
||||
f2m(eth_rx.clk,eth_rx.rst);
|
||||
|
||||
if (ALLOW_DISC) begin : gen_no_mgmt_filter
|
||||
// Allow all packets to pass through
|
||||
always_comb begin
|
||||
f2m.tdata = v2e.tdata;
|
||||
f2m.tlast = v2e.tlast;
|
||||
f2m.tvalid = v2e.tvalid;
|
||||
v2e.tready = f2m.tready;
|
||||
end
|
||||
|
||||
end else begin : gen_mgmt_filter
|
||||
// Disallow forwarding of management discovery packets from RFNoC to the
|
||||
// transport interface for transports that don't support them.
|
||||
//vhook_nowarn unused_*
|
||||
logic [eth_rx.DATA_WIDTH-1:0] unused_tdata;
|
||||
logic unused_tlast, unused_tvalid;
|
||||
logic [eth_rx.DATA_WIDTH-1:0] s_header;
|
||||
logic dispose_pkt;
|
||||
|
||||
// We identify discovery packets by the fact that they are management
|
||||
// packets and that they use the null EPID as the destination.
|
||||
always_comb dispose_pkt = (chdr_get_pkt_type(s_header[63:0]) == CHDR_PKT_TYPE_MGMT) &&
|
||||
(chdr_get_dst_epid(s_header[63:0]) == NULL_EPID);
|
||||
|
||||
|
||||
axi_demux #(
|
||||
.WIDTH (eth_rx.DATA_WIDTH),
|
||||
.SIZE (2),
|
||||
.PRE_FIFO_SIZE (0),
|
||||
.POST_FIFO_SIZE (1)
|
||||
) axi_demux_mgmt_filter_i (
|
||||
.clk (eth_rx.clk),
|
||||
.reset (eth_rx.rst),
|
||||
.clear (1'b0),
|
||||
.header (s_header),
|
||||
.dest (dispose_pkt),
|
||||
.i_tdata (v2e.tdata),
|
||||
.i_tlast (v2e.tlast),
|
||||
.i_tvalid (v2e.tvalid),
|
||||
.i_tready (v2e.tready),
|
||||
.o_tdata ({unused_tdata, f2m.tdata}),
|
||||
.o_tlast ({unused_tlast, f2m.tlast}),
|
||||
.o_tvalid ({unused_tvalid, f2m.tvalid}),
|
||||
.o_tready ({1'b1, f2m.tready})
|
||||
);
|
||||
end
|
||||
|
||||
// ---------------------------------------------------
|
||||
// MUX and DEMUX for return path
|
||||
// ---------------------------------------------------
|
||||
|
||||
logic [USER_META_W-1:0] unused_tuser;
|
||||
|
||||
axis_switch #(
|
||||
.DATA_W(eth_rx.DATA_WIDTH+USER_META_W), .DEST_W(1), .IN_PORTS(1), .OUT_PORTS(2), .PIPELINE(0)
|
||||
) rtn_demux_i (
|
||||
.clk(eth_rx.clk), .reset(eth_rx.rst),
|
||||
.s_axis_tdata({e2d.tuser, e2d.tdata}), .s_axis_alloc(1'b0),
|
||||
.s_axis_tdest(e2d_tid == CHDR_MGMT_RETURN_TO_SRC ? 2'b01 : 2'b00),
|
||||
.s_axis_tlast(e2d.tlast), .s_axis_tvalid(e2d.tvalid), .s_axis_tready(e2d.tready),
|
||||
.m_axis_tdata({e2e.tuser, e2e.tdata, unused_tuser, e2v.tdata}),
|
||||
.m_axis_tdest(/* unused */),
|
||||
.m_axis_tlast({e2e.tlast, e2v.tlast}),
|
||||
.m_axis_tvalid({e2e.tvalid, e2v.tvalid}),
|
||||
.m_axis_tready({e2e.tready, e2v.tready})
|
||||
);
|
||||
|
||||
axi_mux #(
|
||||
.WIDTH(eth_rx.DATA_WIDTH+USER_META_W+1), .SIZE(2), .PRE_FIFO_SIZE(0), .POST_FIFO_SIZE(0)
|
||||
) rtn_mux_i (
|
||||
.clk(eth_rx.clk), .reset(eth_rx.rst), .clear(1'b0),
|
||||
.i_tdata({1'b1, e2e.tuser, e2e.tdata, 1'b0, {USER_META_W{1'b0}}, f2m.tdata}),
|
||||
.i_tlast({e2e.tlast, f2m.tlast}),
|
||||
.i_tvalid({e2e.tvalid, f2m.tvalid}), .i_tready({e2e.tready, f2m.tready}),
|
||||
.o_tdata({m2e_tdest, m2e.tuser, m2e.tdata}), .o_tlast(m2e.tlast),
|
||||
.o_tvalid(m2e.tvalid), .o_tready(m2e.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.
|
||||
|
||||
AxiStreamIf #(.DATA_WIDTH(eth_rx.DATA_WIDTH),.TUSER(0),.TKEEP(0))
|
||||
data_fifo_o(eth_rx.clk,eth_rx.rst);
|
||||
AxiStreamIf #(.DATA_WIDTH(eth_rx.DATA_WIDTH),.TUSER(0),.TKEEP(0))
|
||||
data_fifo_i(eth_rx.clk,eth_rx.rst);
|
||||
|
||||
AxiStreamIf #(.DATA_WIDTH(1+USER_META_W+16),.TUSER(0),.TKEEP(0))
|
||||
lookup_fifo_o(eth_rx.clk,eth_rx.rst);
|
||||
AxiStreamIf #(.DATA_WIDTH(1+USER_META_W+16),.TUSER(0),.TKEEP(0))
|
||||
lookup_fifo_i(eth_rx.clk,eth_rx.rst);
|
||||
|
||||
logic lookup_fifo_tdest;
|
||||
logic [USER_META_W-1:0] lookup_fifo_tuser;
|
||||
logic [ 15:0] lookup_fifo_tepid;
|
||||
logic non_lookup_done_stb;
|
||||
logic data_fifo_o_hdr = 1'b1;
|
||||
logic pass_packet;
|
||||
logic [USER_META_W-1:0] result_tuser;
|
||||
logic result_tuser_valid;
|
||||
logic [USER_META_W-1:0] reg_o_tuser;
|
||||
|
||||
// Track when the next m2e word contains is the start of a new packet
|
||||
logic m2e_hdr = 1'b1;
|
||||
always_ff @(posedge eth_rx.clk) begin : m2e_hdr_ff
|
||||
if (eth_rx.rst)
|
||||
m2e_hdr <= 1'b1;
|
||||
else if (m2e.tvalid && m2e.tready)
|
||||
m2e_hdr <= m2e.tlast;
|
||||
end
|
||||
|
||||
// We can only accept data from the mux when when both the data_fifo and
|
||||
// lookup_fifo are ready.
|
||||
always_comb data_fifo_i.tdata = m2e.tdata;
|
||||
always_comb data_fifo_i.tlast = m2e.tlast;
|
||||
always_comb data_fifo_i.tvalid = m2e.tvalid && m2e.tready;
|
||||
always_comb m2e.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+eth_rx.DATA_WIDTH)
|
||||
) data_fifo (
|
||||
.clk (eth_rx.clk),
|
||||
.reset (eth_rx.rst),
|
||||
.clear (1'b0),
|
||||
.i_tdata ({data_fifo_i.tlast, data_fifo_i.tdata}),
|
||||
.i_tvalid (data_fifo_i.tvalid),
|
||||
.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.
|
||||
|
||||
always_comb lookup_fifo_i.tdata = {m2e_tdest, m2e.tuser, chdr_get_dst_epid(m2e.tdata[63:0])};
|
||||
always_comb {lookup_fifo_tdest, lookup_fifo_tuser, lookup_fifo_tepid} = lookup_fifo_o.tdata;
|
||||
always_comb lookup_fifo_i.tvalid = m2e.tvalid && m2e.tready && m2e_hdr;
|
||||
|
||||
axi_fifo_short #(
|
||||
.WIDTH (1+USER_META_W+16)
|
||||
) lookup_fifo (
|
||||
.clk (eth_rx.clk),
|
||||
.reset (eth_rx.rst),
|
||||
.clear (1'b0),
|
||||
.i_tdata (lookup_fifo_i.tdata),
|
||||
.i_tvalid (lookup_fifo_i.tvalid),
|
||||
.i_tready (lookup_fifo_i.tready),
|
||||
.o_tdata (lookup_fifo_o.tdata),
|
||||
.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.
|
||||
logic lookup_busy = 1'b0;
|
||||
always_ff @(posedge eth_rx.clk) begin : lookup_busy_ff
|
||||
if (eth_rx.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.
|
||||
always_comb lookup_epid = lookup_fifo_tepid;
|
||||
always_comb 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.
|
||||
always_comb 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
|
||||
always_comb 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_ff @(posedge eth_rx.clk) begin : data_fifo_o_hdr_ff
|
||||
if (eth_rx.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_ff @(posedge eth_rx.clk) begin : result_tuser_ff
|
||||
if (eth_rx.rst) begin
|
||||
result_tuser <= {USER_META_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_META_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_ff @(posedge eth_rx.clk) begin : pass_packet_ff
|
||||
if (eth_rx.rst) begin
|
||||
pass_packet <= 1'b0;
|
||||
reg_o_tuser <= {USER_META_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
|
||||
|
||||
// Device addresses
|
||||
logic [15:0] au_udp_dst;
|
||||
logic [31:0] au_ip_dst;
|
||||
logic [47:0] au_mac_dst;
|
||||
|
||||
AxiStreamIf #(.DATA_WIDTH(eth_rx.DATA_WIDTH),.TKEEP(0),.TUSER(0))
|
||||
au(eth_rx.clk,eth_rx.rst);// Add UDP input
|
||||
always_comb begin
|
||||
{au_udp_dst,au_ip_dst,au_mac_dst} = reg_o_tuser;
|
||||
au.tdata = data_fifo_o.tdata;
|
||||
au.tlast = data_fifo_o.tlast;
|
||||
au.tvalid = data_fifo_o.tvalid & pass_packet;
|
||||
data_fifo_o.tready = au.tready & pass_packet;
|
||||
end
|
||||
|
||||
// add the UDP header back on before sending to EthTx
|
||||
eth_ipv4_add_udp #(
|
||||
.PREAMBLE_BYTES(PREAMBLE_BYTES),
|
||||
.MAX_PACKET_BYTES(MAX_PACKET_BYTES)
|
||||
) add_udp_i (
|
||||
.i(au), .o(eth_tx),
|
||||
.mac_src(my_mac),
|
||||
.ip_src(my_ip),
|
||||
.udp_src(my_udp_chdr_port),
|
||||
.mac_dst(au_mac_dst),
|
||||
.ip_dst(au_ip_dst),
|
||||
.udp_dst(au_udp_dst)
|
||||
);
|
||||
|
||||
|
||||
endmodule : chdr_xport_adapter
|
||||
@@ -0,0 +1,76 @@
|
||||
//
|
||||
// Copyright 2020 Ettus Research, a National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: eth_constants (Header File)
|
||||
//
|
||||
// Description:
|
||||
// Constants for ethernet
|
||||
|
||||
//---------------------------------------
|
||||
// Ethernet constants
|
||||
//---------------------------------------
|
||||
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;
|
||||
localparam [7:0] IPV4_LEN5 = 8'h45;
|
||||
|
||||
// tUser conventions
|
||||
localparam BYTES_MSB = ENET_USER_W-2;
|
||||
localparam ERROR_BIT = ENET_USER_W-1;
|
||||
localparam SOF_BIT = ENET_USER_W-1;
|
||||
|
||||
//---------------------------------------
|
||||
// Ethernet byte positions
|
||||
//---------------------------------------
|
||||
// Bytes 7-0-------------------------
|
||||
// | DstMAC_HI (16) | Preamble (48) |
|
||||
// ----------------------------------
|
||||
localparam PREAMBLE_BYTE = 0;
|
||||
localparam PREAMBLE_END = 5;
|
||||
localparam ETH_HDR_BYTE = 0;
|
||||
localparam DST_MAC_BYTE = ETH_HDR_BYTE+0;
|
||||
// Bytes 15-8-------------------------
|
||||
// | SrcMAC_HI (32) | DstMAC_LO (32) |
|
||||
// -----------------------------------
|
||||
localparam SRC_MAC_BYTE = ETH_HDR_BYTE+6;
|
||||
// Bytes 23-16---------------------------------------
|
||||
// | IPv4_Line0 (32)| EthType (16) | SrcMAC_LO (16) |
|
||||
// --------------------------------------------------
|
||||
localparam ETH_TYPE_BYTE = ETH_HDR_BYTE+12;
|
||||
localparam ETH_PAYLOAD_BYTE = ETH_HDR_BYTE+14;
|
||||
// Bytes 31-24--------------------------
|
||||
// | IPv4_Line2 (32) | IPv4_Line1 (32) |
|
||||
// -------------------------------------
|
||||
localparam IPV4_HDR_BYTE = ETH_PAYLOAD_BYTE;
|
||||
localparam IP_VERSION_BYTE = IPV4_HDR_BYTE+0;
|
||||
localparam IP_DSCP_BYTE = IPV4_HDR_BYTE+1;
|
||||
localparam IP_LENGTH_BYTE = IPV4_HDR_BYTE+2;
|
||||
localparam IP_ID_BYTE = IPV4_HDR_BYTE+4;
|
||||
localparam IP_FRAG_BYTE = IPV4_HDR_BYTE+6;
|
||||
localparam IP_TTL_BYTE = IPV4_HDR_BYTE+8;
|
||||
localparam PROTOCOL_BYTE = IPV4_HDR_BYTE+9;
|
||||
localparam IP_CHECKSUM_BYTE = IPV4_HDR_BYTE+10;
|
||||
// Bytes 39-32------------------------
|
||||
// | IPDstAddr (32) | IPSrcAddr (32) |
|
||||
// -----------------------------------
|
||||
localparam SRC_IP_BYTE = IPV4_HDR_BYTE+12;
|
||||
localparam DST_IP_BYTE = IPV4_HDR_BYTE+16;
|
||||
localparam IPV4_PAYLOAD_BYTE = IPV4_HDR_BYTE+20;
|
||||
// Bytes 48-40------------------------------------------------
|
||||
// | Chksum (16) | Length (16) | DstPort (16) | SrcPort (16) |
|
||||
// -----------------------------------------------------------
|
||||
localparam UDP_HDR_BYTE = IPV4_PAYLOAD_BYTE;
|
||||
localparam SRC_PORT_BYTE = UDP_HDR_BYTE+0;
|
||||
localparam DST_PORT_BYTE = UDP_HDR_BYTE+2;
|
||||
localparam UDP_LENGTH_BYTE = UDP_HDR_BYTE+4;
|
||||
localparam UDP_CHECKSUM_BYTE = UDP_HDR_BYTE+6;
|
||||
localparam UDP_END = UDP_CHECKSUM_BYTE+1; // last byte in the UDP header
|
||||
|
||||
localparam MIN_PACKET_SIZE_BYTE = 63;
|
||||
//---------------------------------------
|
||||
// CHDR_BYTE_POSITION
|
||||
//---------------------------------------
|
||||
localparam CHDR_HDR_BYTE = 0;
|
||||
localparam CHDR_LENGTH_BYTE = CHDR_HDR_BYTE+2;
|
||||
@@ -0,0 +1,3 @@
|
||||
wave.do
|
||||
*.dcp
|
||||
*.rpt
|
||||
@@ -0,0 +1,81 @@
|
||||
#
|
||||
# Copyright 2020 Ettus Research, a National Instruments Brand
|
||||
#
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
#
|
||||
|
||||
#-------------------------------------------------
|
||||
# Top-of-Makefile
|
||||
#-------------------------------------------------
|
||||
# Define BASE_DIR to point to the "top" dir
|
||||
BASE_DIR = $(abspath ../../../../top)
|
||||
# Include viv_sim_preamble after defining BASE_DIR
|
||||
include $(BASE_DIR)/../tools/make/viv_sim_preamble.mak
|
||||
|
||||
#-------------------------------------------------
|
||||
# Design Specific
|
||||
#-------------------------------------------------
|
||||
SYNTH_DUT = eth_ifc_synth_test
|
||||
|
||||
# Include makefiles and sources for the DUT and its dependencies
|
||||
include $(BASE_DIR)/../lib/control/Makefile.srcs
|
||||
include $(BASE_DIR)/../lib/axi/Makefile.srcs
|
||||
include $(BASE_DIR)/../lib/axi4s_sv/Makefile.srcs
|
||||
include $(BASE_DIR)/../lib/packet_proc/Makefile.srcs
|
||||
include $(BASE_DIR)/../lib/xge_interface/Makefile.srcs
|
||||
include $(BASE_DIR)/../lib/fifo/Makefile.srcs
|
||||
include $(BASE_DIR)/../lib/rfnoc/utils/Makefile.srcs
|
||||
include $(BASE_DIR)/../lib/rfnoc/xport/Makefile.srcs
|
||||
include $(BASE_DIR)/../lib/rfnoc/xport_sv/Makefile.srcs
|
||||
include $(BASE_DIR)/../lib/rfnoc/crossbar/Makefile.srcs
|
||||
include $(BASE_DIR)/../lib/rfnoc/core/Makefile.srcs
|
||||
|
||||
BUILD_IP_DIR = $(abspath $(BASE_DIR)/x300/build-ip/xc7k410tffg900-2)
|
||||
|
||||
IP_SRC = \
|
||||
$(abspath $(BUILD_IP_DIR)/fifo_short_2clk/fifo_short_2clk_sim_netlist.v)
|
||||
# Xilinx IP wants lots of libraries
|
||||
MODELSIM_LIBS += secureip unimacro_ver unisims_ver xilinx_vip xpm
|
||||
MODELSIM_ARGS += glbl -t 1fs
|
||||
|
||||
DESIGN_SRCS = $(abspath \
|
||||
eth_ifc_synth_test.sv \
|
||||
$(FIFO_SRCS) \
|
||||
$(CONTROL_LIB_SRCS) \
|
||||
$(AXI_SRCS) \
|
||||
$(AXI4S_SV_SRCS) \
|
||||
$(XGE_INTERFACE_SRCS) \
|
||||
$(PACKET_PROC_SRCS) \
|
||||
$(RFNOC_UTIL_SRCS) \
|
||||
$(RFNOC_XPORT_SRCS) \
|
||||
$(RFNOC_XPORT_SV_SRCS) \
|
||||
$(RFNOC_XBAR_SRCS) \
|
||||
$(RFNOC_CORE_SRCS) \
|
||||
$(IP_SRC) \
|
||||
)
|
||||
|
||||
#-------------------------------------------------
|
||||
# Testbench Specific
|
||||
#-------------------------------------------------
|
||||
# Define only one toplevel module
|
||||
TB_TOP_MODULE ?= eth_ifc_all_tb
|
||||
#TB_TOP_MODULE ?= eth_ifc_synth_test
|
||||
SIM_TOP = $(TB_TOP_MODULE)
|
||||
|
||||
SIM_SRCS = \
|
||||
$(abspath eth_ifc_tb.sv) \
|
||||
$(abspath $(TB_TOP_MODULE).sv)
|
||||
|
||||
# supressing the following worthless reminder.
|
||||
#* Warning: M:/usrp4-hw/oss-repo/fpga/usrp3/lib/axi4s_sv/axi4s_remove_bytes.sv(228): (vlog-2583) [SVCHK] -
|
||||
# Extra checking for conflicts with always_comb and always_latch variables is done at vopt time
|
||||
SVLOG_ARGS = -suppress 2583 -keep_delta
|
||||
VLOG_ARGS = -keep_delta
|
||||
|
||||
#-------------------------------------------------
|
||||
# Bottom-of-Makefile
|
||||
#-------------------------------------------------
|
||||
# Include all simulator specific makefiles here
|
||||
# Each should define a unique target to simulate
|
||||
# e.g. xsim, vsim, etc and a common "clean" target
|
||||
include $(BASE_DIR)/../tools/make/viv_simulator.mak
|
||||
@@ -0,0 +1,24 @@
|
||||
//
|
||||
// Copyright 2020 Ettus Research, a National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: eth_ifc_all_tb
|
||||
//
|
||||
// Description: Testbench for eth_ifc
|
||||
//
|
||||
|
||||
module eth_ifc_all_tb #(
|
||||
/* no PARAM */
|
||||
)(
|
||||
/* no IO */
|
||||
);
|
||||
|
||||
eth_ifc_tb #(.TEST_NAME("ORIGINAL"),.ENET_W(64),.CPU_W(64),.CHDR_W(64),.SV_ETH_IFC(0)) ORIGINAL ();
|
||||
|
||||
eth_ifc_tb #(.TEST_NAME("64B"),.ENET_W(64),.CPU_W(64),.CHDR_W(64)) ETH64 ();
|
||||
eth_ifc_tb #(.TEST_NAME("512_64B"),.ENET_W(512),.CPU_W(64),.CHDR_W(64)) ETH512_CHDR64 ();
|
||||
eth_ifc_tb #(.TEST_NAME("512_128B"),.ENET_W(512),.CPU_W(64),.CHDR_W(128)) ETH512_CHDR128 ();
|
||||
eth_ifc_tb #(.TEST_NAME("512B"),.ENET_W(512),.CPU_W(64),.CHDR_W(512)) ETH512_CHDR512 ();
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,161 @@
|
||||
//
|
||||
// Copyright 2020 Ettus Research, a National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
//
|
||||
// Module: eth_ifc_synth_test
|
||||
//
|
||||
// Description: Wrapper to test size of 64 bit version
|
||||
//
|
||||
// Parameters: -- get descriptions from eth_ipv4_interface
|
||||
// PROTOVER -
|
||||
// MTU - Max Packet Size
|
||||
// NODE_INST -
|
||||
// RT_TBL_SIZE -
|
||||
// REG_AWIDTH -
|
||||
// BASE -
|
||||
// EWIDTH - Ethernet Width
|
||||
// CWIDTH - CPU Width
|
||||
// VWIDTH - CHDR Width
|
||||
|
||||
module eth_ifc_synth_test #(
|
||||
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,
|
||||
parameter EWIDTH = 512,
|
||||
parameter CWIDTH = 64,
|
||||
parameter VWIDTH = 128
|
||||
) (
|
||||
input logic bus_clk,
|
||||
input logic bus_rst,
|
||||
input logic [15:0] device_id,
|
||||
|
||||
// Register port: Write port (domain: clk)
|
||||
input logic reg_wr_req,
|
||||
input logic [REG_AWIDTH-1:0] reg_wr_addr,
|
||||
input logic [31:0] reg_wr_data,
|
||||
|
||||
// Register port: Read port (domain: clk)
|
||||
input logic reg_rd_req,
|
||||
input logic [REG_AWIDTH-1:0] reg_rd_addr,
|
||||
output logic reg_rd_resp,
|
||||
output logic [31:0] reg_rd_data,
|
||||
|
||||
// Status ports (domain: clk)
|
||||
output logic [47:0] my_mac,
|
||||
output logic [31:0] my_ip,
|
||||
output logic [15:0] my_udp_chdr_port,
|
||||
|
||||
// Ethernet ports
|
||||
output logic [EWIDTH-1:0] eth_tx_tdata,
|
||||
output logic [$clog2(EWIDTH/8):0] eth_tx_tuser,
|
||||
output logic eth_tx_tlast,
|
||||
output logic eth_tx_tvalid,
|
||||
input logic eth_tx_tready,
|
||||
input logic [EWIDTH-1:0] eth_rx_tdata,
|
||||
input logic [$clog2(EWIDTH/8):0] eth_rx_tuser,
|
||||
input logic eth_rx_tlast,
|
||||
input logic eth_rx_tvalid,
|
||||
output logic eth_rx_tready,
|
||||
|
||||
// CHDR router interface
|
||||
output logic [VWIDTH-1:0] e2v_tdata,
|
||||
output logic e2v_tlast,
|
||||
output logic e2v_tvalid,
|
||||
input logic e2v_tready,
|
||||
input logic [VWIDTH-1:0] v2e_tdata,
|
||||
input logic v2e_tlast,
|
||||
input logic v2e_tvalid,
|
||||
output logic v2e_tready,
|
||||
|
||||
// CPU
|
||||
output logic [CWIDTH-1:0] e2c_tdata,
|
||||
output logic [$clog2(CWIDTH/8):0] e2c_tuser,
|
||||
output logic e2c_tlast,
|
||||
output logic e2c_tvalid,
|
||||
input logic e2c_tready,
|
||||
input logic [CWIDTH-1:0] c2e_tdata,
|
||||
input logic [$clog2(CWIDTH/8):0] c2e_tuser,
|
||||
input logic c2e_tlast,
|
||||
input logic c2e_tvalid,
|
||||
output logic c2e_tready
|
||||
|
||||
);
|
||||
|
||||
localparam MAX_PACKET_BYTES = 2**16;
|
||||
localparam ENET_W=EWIDTH;
|
||||
localparam ENET_USER_W=$clog2(EWIDTH/8)+1;
|
||||
localparam CHDR_W=VWIDTH;
|
||||
localparam CHDR_USER_W=$clog2(VWIDTH/8)+1;
|
||||
localparam CPU_W=CWIDTH;
|
||||
localparam CPU_USER_W=$clog2(CWIDTH/8)+1;
|
||||
|
||||
AxiStreamIf #(.DATA_WIDTH(ENET_W),.USER_WIDTH(ENET_USER_W),.TKEEP(0),
|
||||
.MAX_PACKET_BYTES(MAX_PACKET_BYTES))
|
||||
eth_tx (bus_clk, bus_reset);
|
||||
AxiStreamIf #(.DATA_WIDTH(ENET_W),.USER_WIDTH(ENET_USER_W),.TKEEP(0),
|
||||
.MAX_PACKET_BYTES(MAX_PACKET_BYTES))
|
||||
eth_rx (bus_clk, bus_reset);
|
||||
|
||||
AxiStreamIf #(.DATA_WIDTH(CHDR_W),.USER_WIDTH(CHDR_USER_W),.TKEEP(0),.TUSER(0))
|
||||
v2e (bus_clk, bus_reset);
|
||||
AxiStreamIf #(.DATA_WIDTH(CHDR_W),.USER_WIDTH(CHDR_USER_W),.TKEEP(0),.TUSER(0))
|
||||
e2v (bus_clk, bus_reset);
|
||||
|
||||
AxiStreamIf #(.DATA_WIDTH(CPU_W),.USER_WIDTH(CPU_USER_W),.TKEEP(0))
|
||||
c2e (bus_clk, bus_reset);
|
||||
AxiStreamIf #(.DATA_WIDTH(CPU_W),.USER_WIDTH(CPU_USER_W),.TKEEP(0))
|
||||
e2c (bus_clk, bus_reset);
|
||||
|
||||
assign eth_tx_tdata = eth_tx.tdata;
|
||||
assign eth_tx_tuser = eth_tx.tuser;
|
||||
assign eth_tx_tlast = eth_tx.tlast;
|
||||
assign eth_tx_tvalid = eth_tx.tvalid;
|
||||
assign eth_tx.tready = eth_tx_tready;
|
||||
|
||||
assign eth_rx.tdata = eth_rx_tdata;
|
||||
assign eth_rx.tuser = eth_rx_tuser;
|
||||
assign eth_rx.tlast = eth_rx_tlast;
|
||||
assign eth_rx.tvalid = eth_rx_tvalid;
|
||||
assign eth_rx_tready = eth_rx.tready;
|
||||
|
||||
assign e2v_tdata = e2v.tdata;
|
||||
assign e2v_tlast = e2v.tlast;
|
||||
assign e2v_tvalid = e2v.tvalid;
|
||||
assign e2v.tready = e2v_tready;
|
||||
|
||||
assign v2e.tdata = v2e_tdata;
|
||||
assign v2e.tlast = v2e_tlast;
|
||||
assign v2e.tvalid = v2e_tvalid;
|
||||
assign v2e_tready = v2e.tready;
|
||||
|
||||
assign e2c_tdata = e2c.tdata;
|
||||
assign e2c_tuser = e2c.tuser;
|
||||
assign e2c_tlast = e2c.tlast;
|
||||
assign e2c_tvalid = e2c.tvalid;
|
||||
assign e2c.tready = e2c_tready;
|
||||
|
||||
assign c2e.tdata = c2e_tdata;
|
||||
assign c2e.tuser = c2e_tuser;
|
||||
assign c2e.tlast = c2e_tlast;
|
||||
assign c2e.tvalid = c2e_tvalid;
|
||||
assign c2e_tready = c2e.tready;
|
||||
|
||||
eth_ipv4_interface #(
|
||||
.PROTOVER(PROTOVER), .MTU(MTU), .NODE_INST(NODE_INST),
|
||||
.REG_AWIDTH(REG_AWIDTH), .RT_TBL_SIZE(RT_TBL_SIZE),
|
||||
.BASE(BASE),
|
||||
.DROP_UNKNOWN_MAC(0),
|
||||
.DROP_MIN_PACKET(0),
|
||||
.PREAMBLE_BYTES(0),
|
||||
.ADD_SOF(0),
|
||||
.SYNC(0),
|
||||
.ENET_W(EWIDTH),.CPU_W(CWIDTH),.CHDR_W(VWIDTH)
|
||||
) eth_interface (
|
||||
.* );
|
||||
|
||||
endmodule
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,200 @@
|
||||
//
|
||||
// Copyright 2020 Ettus Research, A National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: eth_ipv4_add_udp.sv
|
||||
// Description: Add a UDP header onto an incoming CHDR stream
|
||||
//
|
||||
// Parameters:
|
||||
// - PREAMBLE_BYTES: Number of bytes of Preamble expected
|
||||
// - MAX_PACKET_BYTES: Maximum expected packet size
|
||||
// Constant fields in the added packet header
|
||||
// - ETH_TYPE: EthType in Ethernet Frame header
|
||||
// - MISC_IP: IP version / IP header Length / DSCP and ECN in IpV4 header
|
||||
// - FLAG_FRAG: Identification in IPv4 header
|
||||
// - TTL_PROT: Time to Live / Protocol in IPv4 header
|
||||
|
||||
module eth_ipv4_add_udp #(
|
||||
int PREAMBLE_BYTES = 6,
|
||||
int MAX_PACKET_BYTES = 2**16,
|
||||
logic [15:0] ETH_TYPE = 16'h0800, // IPv4
|
||||
logic [15:0] MISC_IP = { 4'd4 /* IPv4 */, 4'd5 /* IP HDR Len */, 8'h00 /* DSCP and ECN */},
|
||||
logic [15:0] IDENT = 16'h0,
|
||||
logic [15:0] FLAG_FRAG = { 3'b010 /* don't fragment */, 13'h0 },
|
||||
logic [15:0] TTL_PROT = { 8'h10 /* TTL */, 8'h11 /* UDP */ }
|
||||
)(
|
||||
// Device addresses
|
||||
input logic [47:0] mac_src,
|
||||
input logic [31:0] ip_src,
|
||||
input logic [15:0] udp_src,
|
||||
|
||||
input logic [47:0] mac_dst,
|
||||
input logic [31:0] ip_dst,
|
||||
input logic [15:0] udp_dst,
|
||||
|
||||
// Ethernet Stream - CHDR ONLY
|
||||
AxiStreamIf.slave i, // tUser = {*not used*}
|
||||
AxiStreamIf.master o // tUser = {1'b0,trailing bytes};
|
||||
|
||||
);
|
||||
|
||||
localparam ENET_USER_W = $clog2(i.DATA_WIDTH/8)+1;
|
||||
//---------------------------------------
|
||||
// Include for byte positions
|
||||
//---------------------------------------
|
||||
`include "eth_constants.vh"
|
||||
`include "../../axi4s_sv/axi4s.vh"
|
||||
|
||||
// tUser = {1'b0,trailing bytes(always full)}
|
||||
AxiStreamPacketIf #(.DATA_WIDTH(i.DATA_WIDTH),.USER_WIDTH(ENET_USER_W),.TKEEP(0),
|
||||
.MAX_PACKET_BYTES(MAX_PACKET_BYTES))
|
||||
s0(i.clk,i.rst);
|
||||
// tUser = {1'b0,trailing bytes(after add)}
|
||||
AxiStreamPacketIf #(.DATA_WIDTH(i.DATA_WIDTH),.USER_WIDTH(ENET_USER_W),.TKEEP(0),
|
||||
.MAX_PACKET_BYTES(MAX_PACKET_BYTES))
|
||||
s1(i.clk,i.rst);
|
||||
// tUser = {HeaderInfo,trailing bytes(after add)}
|
||||
AxiStreamPacketIf #(.DATA_WIDTH(i.DATA_WIDTH),.USER_WIDTH(ENET_USER_W+96+48),.TKEEP(0),
|
||||
.MAX_PACKET_BYTES(MAX_PACKET_BYTES))
|
||||
s2(i.clk,i.rst);
|
||||
// tUser = {1'b0,trailing bytes}
|
||||
AxiStreamPacketIf #(.DATA_WIDTH(i.DATA_WIDTH),.USER_WIDTH(ENET_USER_W+96+48),.TKEEP(0),
|
||||
.MAX_PACKET_BYTES(MAX_PACKET_BYTES))
|
||||
s3(i.clk,i.rst);
|
||||
// tUser = {1'b0,trailing bytes}
|
||||
AxiStreamPacketIf #(.DATA_WIDTH(i.DATA_WIDTH),.USER_WIDTH(ENET_USER_W),.TKEEP(0),
|
||||
.MAX_PACKET_BYTES(MAX_PACKET_BYTES))
|
||||
s4(i.clk,i.rst);
|
||||
|
||||
//---------------------------------------
|
||||
// Ethernet Framer
|
||||
//---------------------------------------
|
||||
logic [15:0] chdr_len_new, chdr_len_old, chdr_len_reg;
|
||||
|
||||
logic [15:0] ip_len, ip_len_reg;
|
||||
logic [15:0] udp_len, udp_len_reg;
|
||||
//delayed one clock cycle to pipeline
|
||||
logic [47:0] mac_dst_reg;
|
||||
logic [31:0] ip_dst_reg;
|
||||
logic [15:0] udp_dst_reg;
|
||||
logic [ENET_USER_W-1:0] trailing_bytes;
|
||||
|
||||
always_comb begin : s0_assign
|
||||
`AXI4S_ASSIGN(s0,i)
|
||||
s0.tuser = 0; // all full words going in
|
||||
end
|
||||
|
||||
localparam BYTES_TO_ADD = UDP_END+1+PREAMBLE_BYTES;
|
||||
axi4s_add_bytes #(.ADD_START(0),.ADD_BYTES(BYTES_TO_ADD)
|
||||
) add_header (
|
||||
.i(s0), .o(s1)
|
||||
);
|
||||
|
||||
// try to calculate what tuser should be on the final word
|
||||
logic [15:0] total_len;
|
||||
logic [15:0] rem_bytes;
|
||||
|
||||
always_ff @(posedge i.clk) begin : chdr_len_ff
|
||||
if (i.rst) begin
|
||||
chdr_len_old <= '0;
|
||||
end else begin
|
||||
if (s0.tvalid && s0.tready) begin
|
||||
chdr_len_old <= chdr_len_new;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
always_comb begin : calc_length_fields
|
||||
// exract fields
|
||||
chdr_len_new = s0.get_packet_field16(chdr_len_old,CHDR_LENGTH_BYTE);
|
||||
ip_len = (16'd28 + chdr_len_new); // 20 for IP, 8 for UDP
|
||||
udp_len = (16'd8 + chdr_len_new);
|
||||
end
|
||||
|
||||
logic [15:0] iphdr_checksum;
|
||||
|
||||
always_comb begin : pack_unpack_header_info
|
||||
`AXI4S_ASSIGN(s2,s1)
|
||||
s2.tuser = { udp_len, ip_len, chdr_len_new, udp_dst, ip_dst, mac_dst, s1.tuser};
|
||||
{ udp_len_reg, ip_len_reg, chdr_len_reg, udp_dst_reg, ip_dst_reg,
|
||||
mac_dst_reg,trailing_bytes} = s3.tuser;
|
||||
end
|
||||
|
||||
axi4s_fifo #(
|
||||
.SIZE(1)
|
||||
) s3_pipline_reg_ (
|
||||
.clear(1'b0),.space(),.occupied(),
|
||||
.i(s2),.o(s3)
|
||||
);
|
||||
|
||||
logic chdr_end_early;
|
||||
|
||||
// track when we cut off the outgoing packet
|
||||
always_ff @(posedge i.clk) begin : chdr_end_early_ff
|
||||
if (i.rst) begin
|
||||
chdr_end_early <= 0;
|
||||
end else begin
|
||||
if (s3.tvalid && s3.tready && s3.tlast)
|
||||
chdr_end_early <= 0;
|
||||
else if (s4.tvalid && s4.tready &&s4.tlast)
|
||||
chdr_end_early <= 1;
|
||||
end
|
||||
end
|
||||
|
||||
localparam [15:0] udp_checksum = 16'h0;
|
||||
|
||||
ip_hdr_checksum #(
|
||||
.LATENCY(1)
|
||||
) ip_hdr_checksum_i (
|
||||
.clk(i.clk),
|
||||
.in({MISC_IP, ip_len, IDENT, FLAG_FRAG, TTL_PROT, 16'd0, ip_src, ip_dst}),
|
||||
.clken(s2.tready && s2.tvalid),
|
||||
.out(iphdr_checksum)
|
||||
);
|
||||
|
||||
always_comb begin : calc_rem_bytes
|
||||
total_len = (BYTES_TO_ADD + chdr_len_reg);
|
||||
rem_bytes = total_len - s4.word_count*i.DATA_WIDTH/8;
|
||||
end
|
||||
|
||||
// zero value fields are commented out because the fill goes to zero
|
||||
// if those fields become non zero uncomment them
|
||||
always_comb begin : set_header_fields
|
||||
// assign bus
|
||||
s4.tvalid = s3.tvalid && !chdr_end_early;
|
||||
s3.tready = s4.tready || chdr_end_early;
|
||||
if (rem_bytes > i.DATA_WIDTH/8 || chdr_end_early) begin
|
||||
s4.tlast = s3.tvalid && s3.tlast; // s0metimes packets end early
|
||||
s4.tuser = 0;
|
||||
end else if (rem_bytes == i.DATA_WIDTH/8) begin
|
||||
s4.tlast = s3.tvalid;
|
||||
s4.tuser = 0;
|
||||
end else begin
|
||||
s4.tlast = s3.tvalid;
|
||||
s4.tuser = rem_bytes;
|
||||
end
|
||||
s4.tdata = s3.tdata;
|
||||
s4.put_packet_field48(mac_dst_reg, PREAMBLE_BYTES+DST_MAC_BYTE, .NETWORK_ORDER(1));
|
||||
s4.put_packet_field48(mac_src, PREAMBLE_BYTES+SRC_MAC_BYTE, .NETWORK_ORDER(1));
|
||||
s4.put_packet_field16(ETH_TYPE, PREAMBLE_BYTES+ETH_TYPE_BYTE, .NETWORK_ORDER(1));
|
||||
s4.put_packet_field16(MISC_IP, PREAMBLE_BYTES+IP_VERSION_BYTE, .NETWORK_ORDER(1));
|
||||
s4.put_packet_field16(ip_len_reg, PREAMBLE_BYTES+IP_LENGTH_BYTE, .NETWORK_ORDER(1));
|
||||
// s4.put_packet_field16(IDENT, PREAMBLE_BYTES+IP_ID_BYTE, .NETWORK_ORDER(1));
|
||||
s4.put_packet_field16(FLAG_FRAG, PREAMBLE_BYTES+IP_FRAG_BYTE, .NETWORK_ORDER(1));
|
||||
s4.put_packet_field16(TTL_PROT, PREAMBLE_BYTES+IP_TTL_BYTE, .NETWORK_ORDER(1));
|
||||
s4.put_packet_field16(iphdr_checksum, PREAMBLE_BYTES+IP_CHECKSUM_BYTE, .NETWORK_ORDER(1));
|
||||
s4.put_packet_field32(ip_src, PREAMBLE_BYTES+SRC_IP_BYTE, .NETWORK_ORDER(1));
|
||||
s4.put_packet_field32(ip_dst_reg, PREAMBLE_BYTES+DST_IP_BYTE, .NETWORK_ORDER(1));
|
||||
s4.put_packet_field16(udp_src, PREAMBLE_BYTES+SRC_PORT_BYTE, .NETWORK_ORDER(1));
|
||||
s4.put_packet_field16(udp_dst_reg, PREAMBLE_BYTES+DST_PORT_BYTE, .NETWORK_ORDER(1));
|
||||
s4.put_packet_field16(udp_len_reg, PREAMBLE_BYTES+UDP_LENGTH_BYTE, .NETWORK_ORDER(1));
|
||||
// s4.put_packet_field16(udp_checksum, PREAMBLE_BYTES+UDP_CHECKSUM_BYTE,.NETWORK_ORDER(1));
|
||||
|
||||
end
|
||||
|
||||
always_comb begin : assign_output
|
||||
`AXI4S_ASSIGN(o,s4)
|
||||
end
|
||||
|
||||
endmodule // eth_ipv4_add_udp
|
||||
@@ -0,0 +1,473 @@
|
||||
//
|
||||
// Copyright 2020 Ettus Research, A National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: eth_ipv4_chdr_adapter
|
||||
// Description: A generic transport adapter module that can be used in
|
||||
// a variety 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?
|
||||
// - DROP_MIN_PACKET: Drop packets smaller than 64 bytes?
|
||||
// - PREAMBLE_BYTES: Number of bytes of Preamble expected
|
||||
// - ADD_SOF: Add a SOF indication into the tuser field
|
||||
// If false use TKEEP instead of USER
|
||||
// - SYNC: Set if MAC is not the same as bus_clk
|
||||
// - ENET_W: Width of the link to the Ethernet MAC
|
||||
// - CPU_W: Width of the CPU interface
|
||||
// - CHDR_W: Width of the CHDR interface
|
||||
//
|
||||
// Signals:
|
||||
// - device_id : The ID of the device that has instantiated this module
|
||||
// - eth_rx : The input Ethernet stream from the MAC
|
||||
// - eth_tx : The output Ethernet stream to the MAC
|
||||
// - v2e : The input CHDR stream from the rfnoc infrastructure
|
||||
// - e2v : The output CHDR stream to the rfnoc infrastructure
|
||||
// - c2e : The input Ethernet stream from the CPU
|
||||
// - e2c : The output Ethernet stream to the CPU
|
||||
// - my_mac: The Ethernet (MAC) address of this endpoint
|
||||
// - my_ip: The IPv4 address of this endpoint
|
||||
// - my_udp_chdr_port: The UDP port allocated for CHDR traffic on this endpoint
|
||||
//
|
||||
|
||||
module eth_ipv4_chdr_adapter #(
|
||||
logic [15:0] PROTOVER = {8'd1, 8'd0},
|
||||
int MTU = 10,
|
||||
int CPU_FIFO_SIZE = MTU,
|
||||
int RT_TBL_SIZE = 6,
|
||||
int NODE_INST = 0,
|
||||
bit DROP_UNKNOWN_MAC = 0,
|
||||
bit DROP_MIN_PACKET = 0,
|
||||
int PREAMBLE_BYTES = 6,
|
||||
bit ADD_SOF = 1,
|
||||
bit SYNC = 0,
|
||||
int ENET_W = 64,
|
||||
int CPU_W = 64,
|
||||
int CHDR_W = 64
|
||||
)(
|
||||
// Device info
|
||||
input logic [15:0] device_id,
|
||||
// Device addresses
|
||||
input logic [47:0] my_mac,
|
||||
input logic [31:0] my_ip,
|
||||
input logic [15:0] my_udp_chdr_port,
|
||||
|
||||
// Ethernet MAC
|
||||
AxiStreamIf.master eth_tx, // tUser = {1'b0,trailing bytes};
|
||||
AxiStreamIf.slave eth_rx, // tUser = {error,trailing bytes};
|
||||
// CHDR router interface
|
||||
AxiStreamIf.master e2v, // tUser = {*not used*};
|
||||
AxiStreamIf.slave v2e, // tUser = {*not used*};
|
||||
// CPU DMA
|
||||
AxiStreamIf.master e2c, // tUser = {sof,trailing bytes};
|
||||
AxiStreamIf.slave c2e // tUser = {1'b0,trailing bytes};
|
||||
|
||||
);
|
||||
|
||||
`include "../core/rfnoc_chdr_utils.vh"
|
||||
`include "../core/rfnoc_chdr_internal_utils.vh"
|
||||
`include "../../axi4s_sv/axi4s.vh"
|
||||
|
||||
|
||||
localparam ENET_USER_W = $clog2(ENET_W/8)+1;
|
||||
localparam CPU_USER_W = $clog2(CPU_W/8)+1;
|
||||
localparam CHDR_USER_W = $clog2(CHDR_W/8);
|
||||
localparam MAX_PACKET_BYTES = 2**16;
|
||||
localparam DEBUG = 1;
|
||||
|
||||
`include "eth_constants.vh"
|
||||
|
||||
//---------------------------------------
|
||||
// E2V and E2C DEMUX
|
||||
//---------------------------------------
|
||||
// tUser = {*not used*}
|
||||
AxiStreamIf #(.DATA_WIDTH(ENET_W),.TKEEP(0),.TUSER(0)) e2v1(eth_rx.clk,eth_rx.rst);
|
||||
// tUser = {*not used*}
|
||||
AxiStreamIf #(.DATA_WIDTH(CHDR_W),.TKEEP(0),.TUSER(0)) e2v2(e2v.clk,e2v.rst);
|
||||
// tUser = {*not used*}
|
||||
AxiStreamIf #(.DATA_WIDTH(CHDR_W),.TKEEP(0),.TUSER(0)) e2v4(e2v.clk,e2v.rst);
|
||||
// tUser = {*not used*}
|
||||
AxiStreamIf #(.DATA_WIDTH(CHDR_W),.TKEEP(0),.TUSER(0)) e2v5(e2v.clk,e2v.rst);
|
||||
|
||||
// tUser = {1'b0,trailing bytes}
|
||||
AxiStreamIf #(.DATA_WIDTH(ENET_W),.USER_WIDTH(ENET_USER_W),.TKEEP(0)) e2c1(eth_rx.clk,eth_rx.rst);
|
||||
// tUser = {sof,trailing bytes} IF ADD_SOF
|
||||
AxiStreamIf #(.DATA_WIDTH(CPU_W),.USER_WIDTH(CPU_USER_W),
|
||||
.TKEEP(!ADD_SOF), .TUSER(ADD_SOF))
|
||||
e2c2(e2c.clk,e2c.rst);
|
||||
|
||||
logic [47:0] e_my_mac;
|
||||
logic [31:0] e_my_ip;
|
||||
logic [15:0] e_my_udp_chdr_port;
|
||||
// crossing clock boundaries.
|
||||
// my_mac, my_ip,,my_udp_chdr_port must be written
|
||||
// prior to traffic, or an inconsistent version will
|
||||
// exist for a clock period or 2. This would be better
|
||||
// done with a full handshake.
|
||||
synchronizer #(.WIDTH(96),.STAGES(1))
|
||||
e_info_sync (.clk(eth_rx.clk),.rst(eth_rx.rst),
|
||||
.in({my_mac,my_ip,my_udp_chdr_port}),
|
||||
.out({e_my_mac,e_my_ip,e_my_udp_chdr_port}));
|
||||
|
||||
// Ethernet sink. Inspects packet and dispatches
|
||||
// to the correct port.
|
||||
eth_ipv4_chdr_dispatch #(
|
||||
.CPU_FIFO_SIZE(CPU_FIFO_SIZE),
|
||||
.PREAMBLE_BYTES(PREAMBLE_BYTES),
|
||||
.MAX_PACKET_BYTES(MAX_PACKET_BYTES),
|
||||
.DROP_UNKNOWN_MAC(DROP_UNKNOWN_MAC),
|
||||
.DROP_MIN_PACKET(DROP_MIN_PACKET),
|
||||
.ENET_W(ENET_W)
|
||||
) eth_dispatch_i (
|
||||
.eth_rx (eth_rx),
|
||||
.e2v (e2v1),
|
||||
.e2c (e2c1),
|
||||
.my_mac (e_my_mac),
|
||||
.my_ip (e_my_ip),
|
||||
.my_udp_chdr_port (e_my_udp_chdr_port)
|
||||
);
|
||||
|
||||
//---------------------------------------
|
||||
// E2C Path
|
||||
//---------------------------------------
|
||||
if (ENET_W != CPU_W || !SYNC) begin : gen_e2c_width_conv
|
||||
axi4s_width_conv #(.I_USER_TRAILING_BYTES(1),.O_USER_TRAILING_BYTES(ADD_SOF),.SYNC_CLKS(0))
|
||||
e2c_width_conv (.i(e2c1), .o(e2c2));
|
||||
end else begin : gen_e2c_width_match
|
||||
always_comb begin : e2c_assign
|
||||
`AXI4S_ASSIGN(e2c2,e2c1)
|
||||
end
|
||||
end
|
||||
|
||||
if (ADD_SOF) begin : add_sof
|
||||
logic sof = 1'b1;
|
||||
|
||||
// Add SOF
|
||||
always_ff @(posedge e2c.clk) begin : cpu3_find_sof
|
||||
if (e2c.rst) begin
|
||||
sof <= 1'b1;
|
||||
end else if (e2c2.tvalid && e2c2.tready) begin
|
||||
sof <= e2c2.tlast;
|
||||
end
|
||||
end
|
||||
always_comb begin : e2c2_sof_assign
|
||||
`AXI4S_ASSIGN(e2c,e2c2)
|
||||
e2c.tuser = {sof,e2c2.tuser[CPU_USER_W-2:0]};
|
||||
end
|
||||
end else begin : no_sof
|
||||
if (DEBUG) begin
|
||||
`AXI4S_DEBUG_ASSIGN(e2c,e2c2)
|
||||
end else begin
|
||||
always_comb begin : e2c_nodebug_assign
|
||||
`AXI4S_ASSIGN(e2c,e2c2)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
//---------------------------------------
|
||||
// E2V Path
|
||||
//---------------------------------------
|
||||
if (ENET_W != CHDR_W || !SYNC) begin : gen_e2v_width_conv
|
||||
// assumes full words on input
|
||||
axi4s_width_conv #(.SYNC_CLKS(0))
|
||||
e2v_width_conv (.i(e2v1), .o(e2v2));
|
||||
end else begin : gen_e2v_width_match
|
||||
always_comb begin : e2v_assign
|
||||
`AXI4S_ASSIGN(e2v2,e2v1)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
//---------------------------------------
|
||||
// CHDR Transport Adapter
|
||||
//---------------------------------------
|
||||
|
||||
// tUser = {*not used*}
|
||||
AxiStreamIf #(.DATA_WIDTH(CHDR_W),.USER_WIDTH(ENET_USER_W),.TKEEP(0))
|
||||
v2e1D(v2e.clk,v2e.rst);
|
||||
// tUser = {*not used*}
|
||||
AxiStreamIf #(.DATA_WIDTH(CHDR_W),.USER_WIDTH(ENET_USER_W),.TKEEP(0))
|
||||
v2e1(v2e.clk,v2e.rst);
|
||||
// tUser = {*not used*}
|
||||
AxiStreamIf #(.DATA_WIDTH(ENET_W),.USER_WIDTH(ENET_USER_W),.TKEEP(0))
|
||||
v2e2(eth_rx.clk,eth_rx.rst);
|
||||
// tUser = {*not used*}
|
||||
AxiStreamIf #(.DATA_WIDTH(ENET_W),.USER_WIDTH(ENET_USER_W),.TKEEP(0))
|
||||
v2e3(eth_rx.clk,eth_rx.rst);
|
||||
|
||||
chdr_xport_adapter #(
|
||||
.PREAMBLE_BYTES (PREAMBLE_BYTES),
|
||||
.MAX_PACKET_BYTES (MAX_PACKET_BYTES),
|
||||
.PROTOVER (PROTOVER),
|
||||
.TBL_SIZE (RT_TBL_SIZE),
|
||||
.NODE_INST (NODE_INST),
|
||||
.ALLOW_DISC (1)
|
||||
) xport_adapter_gen_i (
|
||||
.device_id (device_id),
|
||||
.my_mac (my_mac),
|
||||
.my_ip (my_ip),
|
||||
.my_udp_chdr_port (my_udp_chdr_port),
|
||||
|
||||
.eth_rx (e2v2), // from ethernet
|
||||
.e2v (e2v4), // to CHDR
|
||||
// optional loop from ethernet to ethernet to talk to node
|
||||
.v2e (v2e), // from CHDR
|
||||
.eth_tx (v2e1D) // to ethernet
|
||||
);
|
||||
|
||||
if (DEBUG) begin
|
||||
`AXI4S_DEBUG_ASSIGN(v2e1,v2e1D)
|
||||
end else begin
|
||||
always_comb begin : v2e_nodebug_assign
|
||||
`AXI4S_ASSIGN(v2e1,v2e1D)
|
||||
end
|
||||
end
|
||||
|
||||
// Convert incoming CHDR_W
|
||||
if (ENET_W != CHDR_W || !SYNC) begin : gen_v2e_width_conv
|
||||
axi4s_width_conv #(.SYNC_CLKS(0),.I_USER_TRAILING_BYTES(1),.O_USER_TRAILING_BYTES(1))
|
||||
v2e_width_conv (.i(v2e1), .o(v2e2));
|
||||
end else begin : gen_v2e_width_match
|
||||
always_comb begin : v2e1_assign
|
||||
`AXI4S_ASSIGN(v2e2,v2e1)
|
||||
end
|
||||
end
|
||||
|
||||
// Adding so packet will be contiguous going out
|
||||
// The MAC needs bandwidth feeding it to be greater than the line rate
|
||||
if (ENET_W > CHDR_W || !SYNC) begin : gen_v2e_packet_gate
|
||||
axi4s_packet_gate #(.SIZE(17-$clog2(ENET_W)), .USE_AS_BUFF(0))
|
||||
v2e_gate_i (.clear(1'b0),.error(1'b0),.i(v2e2),.o(v2e3));
|
||||
end else begin : gen_v2e_no_packet_gate
|
||||
always_comb begin : v2e1_assign
|
||||
`AXI4S_ASSIGN(v2e3,v2e2)
|
||||
end
|
||||
end
|
||||
|
||||
//---------------------------------------
|
||||
// E2V Output Buffering
|
||||
//---------------------------------------
|
||||
|
||||
// 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.
|
||||
axi4s_fifo #(
|
||||
.SIZE(MTU)
|
||||
) chdr_fifo_i (
|
||||
.clear(1'b0),.space(),.occupied(),
|
||||
.i(e2v4),.o(e2v5)
|
||||
);
|
||||
|
||||
if (DEBUG) begin
|
||||
`AXI4S_DEBUG_ASSIGN(e2v,e2v5)
|
||||
end else begin
|
||||
always_comb begin : e2v_direct_assign
|
||||
`AXI4S_ASSIGN(e2v,e2v5)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
//---------------------------------------
|
||||
// C2E Path
|
||||
//---------------------------------------
|
||||
// tUser = {1'b0,trailing bytes}
|
||||
AxiStreamIf #(.DATA_WIDTH(c2e.DATA_WIDTH),.USER_WIDTH(c2e.USER_WIDTH),
|
||||
.TKEEP(c2e.TKEEP),.TUSER(c2e.TUSER))
|
||||
c2eD(c2e.clk,c2e.rst);
|
||||
// tUser = {1'b0,trailing bytes}
|
||||
AxiStreamIf #(.DATA_WIDTH(ENET_W),.USER_WIDTH(ENET_USER_W),.TKEEP(0)) c2e1(eth_rx.clk,eth_rx.rst);
|
||||
// tUser = {1'b0,trailing bytes}
|
||||
AxiStreamIf #(.DATA_WIDTH(ENET_W),.USER_WIDTH(ENET_USER_W),.TKEEP(0),.MAX_PACKET_BYTES(MAX_PACKET_BYTES)) c2e2(eth_rx.clk,eth_rx.rst);
|
||||
// tUser = {1'b0,trailing bytes}
|
||||
AxiStreamPacketIf #(.DATA_WIDTH(ENET_W),.USER_WIDTH(ENET_USER_W),.TKEEP(0),.MAX_PACKET_BYTES(MAX_PACKET_BYTES)) c2e3(eth_rx.clk,eth_rx.rst);
|
||||
|
||||
|
||||
if (DEBUG) begin
|
||||
`AXI4S_DEBUG_ASSIGN(c2eD,c2e)
|
||||
end else begin
|
||||
always_comb begin : c2e_nodebug_assign
|
||||
`AXI4S_ASSIGN(c2eD,c2e)
|
||||
end
|
||||
end
|
||||
|
||||
if (ENET_W != CPU_W || !SYNC) begin : gen_c2e_width_conv
|
||||
AxiStreamIf #(.DATA_WIDTH(ENET_W),.USER_WIDTH(ENET_USER_W),.TKEEP(0)) c2e1_0(eth_rx.clk,eth_rx.rst);
|
||||
axi4s_width_conv #(.I_USER_TRAILING_BYTES(c2eD.TUSER),.O_USER_TRAILING_BYTES(1),.SYNC_CLKS(0))
|
||||
c2e_width_conv (.i(c2eD), .o(c2e1_0));
|
||||
|
||||
if (ENET_W > CPU_W || !SYNC) begin : gen_c2e_packet_gate
|
||||
// Adding so packet will be contiguous going out
|
||||
// I think the MAC needs bandwdith feeding it to
|
||||
// be greater than the line rate
|
||||
axi4s_packet_gate #(.SIZE(17-$clog2(ENET_W)), .USE_AS_BUFF(0))
|
||||
c2e_gate_i (.clear(1'b0),.error(1'b0),.i(c2e1_0),.o(c2e1));
|
||||
end else begin : gen_c2e_no_packet_gate
|
||||
always_comb begin : c2e1_assign
|
||||
`AXI4S_ASSIGN(c2e1,c2e1_0)
|
||||
end
|
||||
end
|
||||
end else begin : gen_c2e_width_match
|
||||
always_comb begin : c2e1_assign
|
||||
`AXI4S_ASSIGN(c2e1,c2eD)
|
||||
end
|
||||
end
|
||||
|
||||
if (PREAMBLE_BYTES > 0) begin : gen_add_preamble
|
||||
// Add pad of PREAMBLE_BYTES 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
|
||||
axi4s_add_bytes #(.ADD_START(0),.ADD_BYTES(PREAMBLE_BYTES)
|
||||
) add_header (
|
||||
.i(c2e1), .o(c2e2)
|
||||
);
|
||||
end else begin : gen_no_preamble
|
||||
always_comb begin : c2e2_assign
|
||||
`AXI4S_ASSIGN(c2e2,c2e1)
|
||||
end
|
||||
end
|
||||
|
||||
localparam FORCE_MIN_PACKET = 1;
|
||||
if (FORCE_MIN_PACKET) begin : gen_force_min
|
||||
// add extra zero bytes to the end of a packet if it is less
|
||||
// than the minimum packet size.
|
||||
typedef enum logic {
|
||||
ST_IDLE,
|
||||
ST_AFTER
|
||||
} pad_state_t;
|
||||
pad_state_t pad_state = ST_IDLE;
|
||||
logic clk_before_minpacket;
|
||||
logic pad_last;
|
||||
|
||||
always_comb clk_before_minpacket = c2e3.reached_packet_byte(MIN_PACKET_SIZE_BYTE);
|
||||
|
||||
always_ff @(posedge eth_rx.clk) begin : pad_state_ff
|
||||
if (eth_rx.rst) begin
|
||||
pad_state <= ST_IDLE;
|
||||
pad_last <= 0;
|
||||
end else begin
|
||||
if (c2e3.tready && c2e3.tvalid && c2e3.tlast) begin
|
||||
pad_state <= ST_IDLE;
|
||||
end else if (c2e3.tready && c2e3.tvalid && clk_before_minpacket) begin
|
||||
pad_state <= ST_AFTER;
|
||||
end
|
||||
if (c2e3.tready && c2e3.tvalid && c2e3.tlast) begin
|
||||
pad_last <= 0;
|
||||
end else if (c2e3.tready && c2e3.tvalid && c2e2.tlast) begin
|
||||
pad_last <= 1;
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
always_comb begin : c2e3_pad
|
||||
if (pad_state == ST_IDLE) begin
|
||||
//force to a full word
|
||||
// preserve SOF if it's there, but force
|
||||
// trailing bytes to zero (full word)
|
||||
c2e3.tuser = 0;
|
||||
c2e3.tuser[ENET_USER_W-1] = c2e2.tuser[ENET_USER_W-1];
|
||||
c2e3.tvalid = c2e2.tvalid;
|
||||
|
||||
// force any tdata bytes that we pad with zero
|
||||
// SW recommended forcing the padding bytes to zero
|
||||
// but I suspect we could save logic by just allowing
|
||||
// trash data.
|
||||
foreach (c2e2.tkeep[i]) begin
|
||||
if (pad_last || (i >= c2e2.tuser[ENET_USER_W-2:0] &&
|
||||
c2e2.tuser[ENET_USER_W-2:0] != 0)) begin
|
||||
c2e3.tdata[i*8 +:8] = 0;
|
||||
end else begin
|
||||
c2e3.tdata[i*8 +:8] = c2e2.tdata[i*8 +:8];
|
||||
end
|
||||
end
|
||||
|
||||
if (ENET_W < 512) begin
|
||||
// hold off input if we reach the end early
|
||||
if (c2e2.tlast) begin
|
||||
c2e2.tready = clk_before_minpacket && c2e3.tready;
|
||||
end else begin
|
||||
c2e2.tready = c2e3.tready;
|
||||
end
|
||||
// add tlast at end of idle
|
||||
c2e3.tlast = clk_before_minpacket && c2e2.tlast;
|
||||
end else begin
|
||||
c2e2.tready = c2e3.tready;
|
||||
c2e3.tlast = c2e2.tlast;
|
||||
end
|
||||
|
||||
end else begin
|
||||
`AXI4S_ASSIGN(c2e3,c2e2)
|
||||
end
|
||||
|
||||
end
|
||||
end else begin : gen_no_force_min
|
||||
always_comb begin : c2e3_assign
|
||||
`AXI4S_ASSIGN(c2e3,c2e2)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
//---------------------------------------
|
||||
// V2E and C2E MUX
|
||||
//---------------------------------------
|
||||
logic c2e3_tready;
|
||||
logic eth_tx1_tlast;
|
||||
logic eth_tx1_tvalid;
|
||||
logic eth_tx1_tready;
|
||||
logic [ENET_W-1:0] eth_tx1_tdata;
|
||||
logic [ENET_USER_W-1:0] eth_tx1_tuser;
|
||||
always_comb begin
|
||||
c2e3.tready = c2e3_tready;
|
||||
end
|
||||
axi_mux #(
|
||||
.SIZE(2), .PRIO(0), .WIDTH(ENET_W+ENET_USER_W), .PRE_FIFO_SIZE(0), .POST_FIFO_SIZE(1)
|
||||
) eth_mux_i (
|
||||
.clk(eth_rx.clk), .reset(eth_rx.rst), .clear(1'b0),
|
||||
.i_tdata({c2e3.tuser, c2e3.tdata, v2e3.tuser, v2e3.tdata}), .i_tlast({c2e3.tlast, v2e3.tlast}),
|
||||
.i_tvalid({c2e3.tvalid, v2e3.tvalid}), .i_tready({c2e3_tready, v2e3.tready}),
|
||||
.o_tdata({eth_tx1_tuser, eth_tx1_tdata}), .o_tlast(eth_tx1_tlast),
|
||||
.o_tvalid(eth_tx1_tvalid), .o_tready(eth_tx1_tready)
|
||||
);
|
||||
|
||||
// Clean up the noisy mux output. I suspect it is annoying
|
||||
// the xilinx cores that tlast and tuser(tkeep) flop around
|
||||
// when tvalid isn't true.
|
||||
always_comb begin : eth_tx_assign
|
||||
if (eth_tx1_tvalid) begin
|
||||
eth_tx.tvalid = 1'b1;
|
||||
eth_tx.tdata = eth_tx1_tdata;
|
||||
eth_tx.tlast = eth_tx1_tlast;
|
||||
if (eth_tx1_tlast) begin
|
||||
eth_tx.tuser = eth_tx1_tuser;
|
||||
end else begin
|
||||
eth_tx.tuser = '0;
|
||||
end
|
||||
end else begin
|
||||
eth_tx.tvalid = 1'b0;
|
||||
eth_tx.tdata = 'X; // use X so synth will optimize
|
||||
eth_tx.tlast = 0;
|
||||
eth_tx.tuser = '0;
|
||||
end
|
||||
eth_tx1_tready = eth_tx.tready;
|
||||
end
|
||||
|
||||
endmodule // eth_ipv4_chdr_adapter
|
||||
@@ -0,0 +1,435 @@
|
||||
//
|
||||
// Copyright 2020 Ettus Research, A National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: eth_ipv4_chdr_dispatch
|
||||
//
|
||||
// Description:
|
||||
// This module serves as an Ethernet endpoint for CHDR traffic.
|
||||
// Ethernet frames arrive on the eth_rx 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 e2v
|
||||
// in addition to source information for Eth, IP and UDP. All
|
||||
// other traffic address to us (Eth) is sent to the e2c port.
|
||||
// Traffic not addressed (Eth) to us is dropped(optionally).
|
||||
//
|
||||
// Parameters:
|
||||
// - CPU_FIFO_SIZE: log2 size of CPU RX fifo
|
||||
// - PREAMBLE_BYTES: Number of bytes in the Preamble
|
||||
// - DROP_UNKNOWN_MAC: Drop packets not addressed to us?
|
||||
// - DROP_MIN_PACKET: Drop packets smaller than 64 bytes?
|
||||
// - ENET_W: Width of AXI bus going to Ethernet Mac
|
||||
//
|
||||
// Signals:
|
||||
// - eth_rx : The input Ethernet stream from the MAC
|
||||
// tUser={error,trailing bytes}
|
||||
// - e2v : The output CHDR stream to the rfnoc infrastructure
|
||||
// - e2c : The output Ethernet stream to the CPU
|
||||
//
|
||||
// - my_mac : The Ethernet (MAC) address of this endpoint
|
||||
// - my_ip : The IPv4 address of this endpoint
|
||||
// - my_udp_chdr_port : The UDP port allocated for CHDR traffic on this endpoint
|
||||
//
|
||||
|
||||
module eth_ipv4_chdr_dispatch #(
|
||||
int CPU_FIFO_SIZE = $clog2(1558),
|
||||
int PREAMBLE_BYTES = 6,
|
||||
int MAX_PACKET_BYTES = 2**16-1,
|
||||
bit DROP_UNKNOWN_MAC = 0,
|
||||
bit DROP_MIN_PACKET = 0,
|
||||
int ENET_W = 64
|
||||
)(
|
||||
|
||||
// AXI-Stream interfaces
|
||||
AxiStreamIf.slave eth_rx, // tUser={error,trailing bytes};
|
||||
AxiStreamIf.master e2v, // tUser={1'b0,trailing bytes};
|
||||
AxiStreamIf.master e2c, // tUser={1'b0,trailing bytes};
|
||||
|
||||
// Device addresses
|
||||
input logic [47:0] my_mac,
|
||||
input logic [31:0] my_ip,
|
||||
input logic [15:0] my_udp_chdr_port
|
||||
);
|
||||
|
||||
localparam ENET_USER_W = $clog2(ENET_W/8)+1;
|
||||
//---------------------------------------
|
||||
// Include for byte positions
|
||||
//---------------------------------------
|
||||
`include "eth_constants.vh"
|
||||
// example macro to handle interface assignment
|
||||
`define AXI4S_ASSIGN(O,I) \
|
||||
``O.tdata = ``I.tdata;\
|
||||
``O.tuser = ``I.tuser;\
|
||||
``O.tlast = ``I.tlast;\
|
||||
``O.tvalid = ``I.tvalid;\
|
||||
``I.tready = ``O.tready;
|
||||
|
||||
// axi_remov_bytes (PREAMBLE Strip)
|
||||
// tUser = {error,trailing bytes};
|
||||
AxiStreamPacketIf #(.DATA_WIDTH(ENET_W),.USER_WIDTH(ENET_USER_W),
|
||||
.TKEEP(0),.MAX_PACKET_BYTES(MAX_PACKET_BYTES))
|
||||
in0(eth_rx.clk,eth_rx.rst);
|
||||
// in_reg
|
||||
// tUser = {error,trailing bytes};
|
||||
AxiStreamPacketIf #(.DATA_WIDTH(ENET_W),.USER_WIDTH(ENET_USER_W),
|
||||
.TKEEP(0),.MAX_PACKET_BYTES(MAX_PACKET_BYTES))
|
||||
in1(eth_rx.clk,eth_rx.rst);
|
||||
// STATEMACHINE
|
||||
// tUser = {error,trailing bytes};
|
||||
AxiStreamIf #(.DATA_WIDTH(ENET_W),.USER_WIDTH(ENET_USER_W),.TKEEP(0))
|
||||
in2(eth_rx.clk,eth_rx.rst);
|
||||
|
||||
// CPU_BRANCH
|
||||
// tUser = {error,trailing bytes};
|
||||
AxiStreamIf #(.DATA_WIDTH(ENET_W),.USER_WIDTH(ENET_USER_W),.TKEEP(0))
|
||||
cpu0(eth_rx.clk,eth_rx.rst);
|
||||
// out_reg_cpu
|
||||
// tUser = {error,trailing bytes};
|
||||
AxiStreamIf #(.DATA_WIDTH(ENET_W),.USER_WIDTH(ENET_USER_W),.TKEEP(0))
|
||||
cpu1(eth_rx.clk,eth_rx.rst);
|
||||
// cpu_out_gate - throw away error packets
|
||||
// tUser = {1'b0,trailing bytes};
|
||||
AxiStreamIf #(.DATA_WIDTH(ENET_W),.USER_WIDTH(ENET_USER_W),.TKEEP(0))
|
||||
cpu2(eth_rx.clk,eth_rx.rst);
|
||||
// cpu_out_fifo
|
||||
// e2c (OUTPUT)
|
||||
|
||||
// CHDR_Branch
|
||||
// tUser = {error,trailing bytes};
|
||||
AxiStreamIf #(.DATA_WIDTH(ENET_W),.TKEEP(0),.TUSER(0))
|
||||
chdr0(eth_rx.clk,eth_rx.rst);
|
||||
// e2v(OUTPUT)
|
||||
|
||||
//---------------------------------------
|
||||
// Strip Bytes
|
||||
//---------------------------------------
|
||||
if (PREAMBLE_BYTES > 0) begin : gen_strip_preamble
|
||||
// Strip the preamble
|
||||
axi4s_remove_bytes #(.REM_START(0),.REM_END(PREAMBLE_BYTES-1)
|
||||
) strip_preamble (
|
||||
.i(eth_rx),.o(in0)
|
||||
);
|
||||
end else begin : gen_no_preamble
|
||||
always_comb begin
|
||||
`AXI4S_ASSIGN(in0,eth_rx);
|
||||
end
|
||||
end
|
||||
|
||||
//---------------------------------------
|
||||
// Input pipeline stage
|
||||
//---------------------------------------
|
||||
axi4s_fifo #(
|
||||
.SIZE(1)
|
||||
) in_reg_i (
|
||||
.clear(1'b0),.space(),.occupied(),
|
||||
.i(in0), .o(in1)
|
||||
);
|
||||
|
||||
//---------------------------------------
|
||||
// Classification state machine
|
||||
//---------------------------------------
|
||||
typedef enum logic [2:0] {
|
||||
ST_IDLE_ETH_L0 = 3'd0,
|
||||
ST_FWD_CHDR = 3'd1,
|
||||
ST_FWD_CPU = 3'd2,
|
||||
ST_DROP_TERM = 3'd3,
|
||||
ST_DROP_WAIT = 3'd4
|
||||
} dispatch_state_t;
|
||||
|
||||
// State info
|
||||
dispatch_state_t dispatch_state,next_dispatch_state = ST_IDLE_ETH_L0;
|
||||
logic cpu_error = 1'b0;
|
||||
logic chdr_error = 1'b0;
|
||||
logic chdr0_error = 1'b0;
|
||||
logic mac_error, mac_error_old = 1'b0;
|
||||
logic min_packet_error, min_packet_error_old = 1'b0;
|
||||
logic reached_min_packet;
|
||||
|
||||
// Cached fields
|
||||
logic [47:0] eth_dst_addr_new, eth_src_addr_new;
|
||||
logic [31:0] ipv4_dst_addr_new, ipv4_src_addr_new;
|
||||
logic [15:0] udp_dst_port_new, udp_src_port_new, eth_type_new;
|
||||
logic [7:0] ip_protocol_new, ip_version_new;
|
||||
logic [47:0] eth_dst_addr_old, eth_src_addr_old;
|
||||
logic [31:0] ipv4_dst_addr_old, ipv4_src_addr_old;
|
||||
logic [15:0] udp_dst_port_old, udp_src_port_old, eth_type_old;
|
||||
logic [7:0] ip_protocol_old, ip_version_old;
|
||||
logic reached_min_packet_new, reached_min_packet_old;
|
||||
logic reached_end_of_udp;
|
||||
|
||||
logic eth_dst_is_broadcast;
|
||||
logic eth_dst_is_me;
|
||||
logic udp_dst_is_me;
|
||||
logic ipv4_dst_is_me;
|
||||
logic ipv4_protocol_is_udp;
|
||||
logic eth_type_is_ipv4;
|
||||
|
||||
// save the fields
|
||||
always_ff @(posedge eth_rx.clk) begin : field_ff
|
||||
if (eth_rx.rst) begin
|
||||
|
||||
eth_dst_addr_old <= '0;
|
||||
eth_src_addr_old <= '0;
|
||||
ip_protocol_old <= '0;
|
||||
ip_version_old <= '0;
|
||||
ipv4_src_addr_old <= '0;
|
||||
ipv4_dst_addr_old <= '0;
|
||||
udp_src_port_old <= '0;
|
||||
udp_dst_port_old <= '0;
|
||||
eth_type_old <= '0;
|
||||
mac_error_old <= 1'b0;
|
||||
min_packet_error_old <= 1'b0;
|
||||
reached_min_packet_old <= 1'b0;
|
||||
|
||||
// Statemachine Decisions
|
||||
eth_dst_is_broadcast <=1'b0;
|
||||
eth_dst_is_me <=1'b0;
|
||||
udp_dst_is_me <=1'b0;
|
||||
ipv4_dst_is_me <=1'b0;
|
||||
ipv4_protocol_is_udp <=1'b0;
|
||||
eth_type_is_ipv4 <=1'b0;
|
||||
end else begin
|
||||
eth_dst_addr_old <= eth_dst_addr_new;
|
||||
eth_src_addr_old <= eth_src_addr_new;
|
||||
ip_protocol_old <= ip_protocol_new;
|
||||
ip_version_old <= ip_version_new;
|
||||
ipv4_src_addr_old <= ipv4_src_addr_new;
|
||||
ipv4_dst_addr_old <= ipv4_dst_addr_new;
|
||||
udp_src_port_old <= udp_src_port_new;
|
||||
udp_dst_port_old <= udp_dst_port_new;
|
||||
eth_type_old <= eth_type_new;
|
||||
|
||||
if (in0.tvalid && in0.tready) begin
|
||||
eth_dst_is_broadcast <= eth_dst_addr_new == ETH_ADDR_BCAST;
|
||||
eth_dst_is_me <= eth_dst_addr_new == my_mac;
|
||||
udp_dst_is_me <= udp_dst_port_new == my_udp_chdr_port;
|
||||
ipv4_dst_is_me <= ipv4_dst_addr_new == my_ip;
|
||||
ipv4_protocol_is_udp <= ip_protocol_new == IPV4_PROTO_UDP;
|
||||
eth_type_is_ipv4 <= eth_type_new == ETH_TYPE_IPV4;
|
||||
end
|
||||
if (in1.tvalid && in1.tready) begin
|
||||
if (in1.tlast) begin
|
||||
mac_error_old <= 1'b0;
|
||||
min_packet_error_old <= 1'b0;
|
||||
reached_min_packet_old <= 1'b0;
|
||||
end else begin
|
||||
if (mac_error)
|
||||
mac_error_old <= 1'b1;
|
||||
if(min_packet_error)
|
||||
min_packet_error_old <= 1'b1;
|
||||
if(reached_min_packet_new)
|
||||
reached_min_packet_old <= 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// get the fields - don't use assign. assign will not activate with changes to in0.
|
||||
always_comb begin : get_fields
|
||||
eth_dst_addr_new = in0.get_packet_field48(eth_dst_addr_old,DST_MAC_BYTE,.NETWORK_ORDER(1));
|
||||
eth_src_addr_new = in0.get_packet_field48(eth_src_addr_old,SRC_MAC_BYTE,.NETWORK_ORDER(1));
|
||||
ip_version_new = in0.get_packet_byte(ip_version_old,IP_VERSION_BYTE);
|
||||
ip_protocol_new = in0.get_packet_byte(ip_protocol_old,PROTOCOL_BYTE);
|
||||
ipv4_src_addr_new = in0.get_packet_field32(ipv4_src_addr_old,SRC_IP_BYTE,.NETWORK_ORDER(1));
|
||||
ipv4_dst_addr_new = in0.get_packet_field32(ipv4_dst_addr_old,DST_IP_BYTE,.NETWORK_ORDER(1));
|
||||
udp_src_port_new = in0.get_packet_field16(udp_src_port_old,SRC_PORT_BYTE,.NETWORK_ORDER(1));
|
||||
udp_dst_port_new = in0.get_packet_field16(udp_dst_port_old,DST_PORT_BYTE,.NETWORK_ORDER(1));
|
||||
eth_type_new = in0.get_packet_field16(eth_type_old,ETH_TYPE_BYTE,.NETWORK_ORDER(1));
|
||||
end
|
||||
|
||||
|
||||
always_comb begin : reached_bytes
|
||||
reached_min_packet_new = in1.reached_packet_byte(MIN_PACKET_SIZE_BYTE);
|
||||
reached_end_of_udp = in1.reached_packet_byte(DST_PORT_BYTE+3);// we have enough to decide
|
||||
end
|
||||
assign mac_error = in1.tuser[ERROR_BIT] || mac_error_old;
|
||||
if (DROP_MIN_PACKET) begin
|
||||
assign reached_min_packet = (reached_min_packet_new && in1.tuser[BYTES_MSB:0] ==0) || reached_min_packet_old;
|
||||
assign min_packet_error = (in1.tlast && !reached_min_packet) || min_packet_error_old;
|
||||
end else begin
|
||||
assign reached_min_packet = 1'b1;
|
||||
assign min_packet_error = 1'b0;
|
||||
end
|
||||
|
||||
always_ff @(posedge eth_rx.clk) begin : dispatch_sm_ff
|
||||
if (eth_rx.rst) begin
|
||||
dispatch_state <= ST_IDLE_ETH_L0;
|
||||
end else begin
|
||||
if (in1.tvalid && in1.tready) begin
|
||||
if (in1.tlast)
|
||||
dispatch_state <= ST_IDLE_ETH_L0;
|
||||
else
|
||||
dispatch_state <= next_dispatch_state;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
always_comb begin : dispatch_sm_next_state
|
||||
//defaults
|
||||
next_dispatch_state = dispatch_state;
|
||||
`AXI4S_ASSIGN(in2,in1);
|
||||
in2.tuser[ERROR_BIT] = mac_error || min_packet_error;
|
||||
cpu_error = 1'b0;
|
||||
chdr_error = 1'b0;
|
||||
|
||||
// Statemachine always returns to ST_IDLE_ETH_L0 when tlast is set
|
||||
case (dispatch_state)
|
||||
ST_IDLE_ETH_L0: begin
|
||||
cpu_error = 1'b0;
|
||||
chdr_error = 1'b0;
|
||||
if (mac_error || min_packet_error) begin
|
||||
cpu_error = 1'b1;
|
||||
chdr_error = 1'b1;
|
||||
next_dispatch_state = ST_DROP_TERM;
|
||||
end else if (reached_end_of_udp) begin
|
||||
// all header values are decoded
|
||||
if (eth_dst_is_broadcast) begin
|
||||
// If Eth destination is bcast then fwd to CPU
|
||||
cpu_error = 1'b0;
|
||||
chdr_error = 1'b1;
|
||||
next_dispatch_state = ST_FWD_CPU;
|
||||
end else if (!eth_dst_is_me && DROP_UNKNOWN_MAC) begin
|
||||
// If Eth destination is not us then drop the packet
|
||||
cpu_error = 1'b1;
|
||||
chdr_error = 1'b1;
|
||||
next_dispatch_state = ST_DROP_TERM;
|
||||
end else if (udp_dst_is_me &&
|
||||
ipv4_dst_is_me &&
|
||||
//ip_version_new == IPV4_LEN5 && // NEW CHECK --verify if this is ok
|
||||
ipv4_protocol_is_udp &&
|
||||
eth_type_is_ipv4) begin
|
||||
// The conditions matches CHDR port
|
||||
cpu_error = 1'b1;
|
||||
chdr_error = 1'b0;
|
||||
next_dispatch_state = ST_FWD_CHDR;
|
||||
end else begin
|
||||
// Not the CHDR port. Forward to CPU
|
||||
cpu_error = 1'b0;
|
||||
chdr_error = 1'b1;
|
||||
next_dispatch_state = ST_FWD_CPU;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// CHDR Payload
|
||||
ST_FWD_CHDR: begin
|
||||
cpu_error = 1'b1;
|
||||
chdr_error = 1'b0;
|
||||
if (mac_error || min_packet_error) begin
|
||||
cpu_error = 1'b1;
|
||||
chdr_error = 1'b1;
|
||||
next_dispatch_state = ST_DROP_TERM;
|
||||
end
|
||||
end
|
||||
|
||||
// NotCHDR Payload: Send to CPU
|
||||
ST_FWD_CPU: begin
|
||||
cpu_error = 1'b0;
|
||||
chdr_error = 1'b1;
|
||||
if (mac_error || min_packet_error) begin
|
||||
cpu_error = 1'b1;
|
||||
chdr_error = 1'b1;
|
||||
next_dispatch_state = ST_DROP_TERM;
|
||||
end
|
||||
end
|
||||
|
||||
// Unwanted Payload: Drop
|
||||
ST_DROP_TERM: begin
|
||||
cpu_error = 1'b1;
|
||||
chdr_error = 1'b1;
|
||||
in2.tlast = 1'b1;
|
||||
in2.tvalid = 1'b1;
|
||||
in1.tready = in2.tready;
|
||||
next_dispatch_state = ST_DROP_WAIT;
|
||||
end
|
||||
|
||||
// Unwanted Payload: wait
|
||||
ST_DROP_WAIT: begin
|
||||
cpu_error = 1'b0;
|
||||
chdr_error = 1'b0;
|
||||
in2.tlast = 1'b0;
|
||||
in2.tvalid = 1'b0;
|
||||
in1.tready = 1'b1;
|
||||
end
|
||||
|
||||
// We should never get here
|
||||
default: begin
|
||||
cpu_error = 1'b0;
|
||||
chdr_error = 1'b0;
|
||||
in1.tready = 1'b1;
|
||||
in2.tvalid = 1'b0;
|
||||
in2.tlast = 1'b0;
|
||||
next_dispatch_state = ST_IDLE_ETH_L0;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
|
||||
//---------------------------------------
|
||||
// SPLIT
|
||||
//---------------------------------------
|
||||
always_comb begin : cpu0_assign
|
||||
cpu0.tdata = in2.tdata;
|
||||
cpu0.tuser = in2.tuser;
|
||||
cpu0.tlast = in2.tlast;
|
||||
cpu0.tvalid = in2.tvalid && chdr0.tready;
|
||||
cpu0.tuser[ERROR_BIT] = in2.tuser[ERROR_BIT] || cpu_error;
|
||||
|
||||
chdr0.tdata = in2.tdata;
|
||||
chdr0.tuser = in2.tuser;
|
||||
chdr0.tlast = in2.tlast;
|
||||
chdr0.tvalid = in2.tvalid && cpu0.tready;
|
||||
chdr0_error = in2.tuser[ERROR_BIT] || chdr_error;
|
||||
|
||||
in2.tready = cpu0.tready && chdr0.tready;
|
||||
end
|
||||
|
||||
//---------------------------------------
|
||||
// CPU Output processing
|
||||
//---------------------------------------
|
||||
axi4s_fifo #(
|
||||
.SIZE(1)
|
||||
) out_reg_cpu_i (
|
||||
.clear(),.space(),.occupied(),
|
||||
.i(cpu0),.o(cpu1)
|
||||
);
|
||||
|
||||
// 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: This also rejects packets with FCS failures.
|
||||
// NOTE: The SIZE of this FIFO must accommodate a 9000 byte jumbo frame
|
||||
// regardless of the CHDR MTU
|
||||
// SIZED for 11 bit address when using a 64 bit word -> 16KByte
|
||||
// SIZED for 8 bit address when using a 512 bit word -> 16KByte
|
||||
axi4s_packet_gate #(
|
||||
.SIZE(17-$clog2(ENET_W)), .USE_AS_BUFF(0)
|
||||
) cpu_out_gate_i (
|
||||
.clear(1'b0), .error(cpu1.tuser[ERROR_BIT]),
|
||||
.i(cpu1),.o(cpu2)
|
||||
);
|
||||
|
||||
// The CPU can be slow to respond (relative to packet wirespeed) so
|
||||
// extra buffer for packets destined there so it doesn't back up.
|
||||
axi4s_fifo #(
|
||||
.SIZE(CPU_FIFO_SIZE)
|
||||
) cpu_fifo_i (
|
||||
.clear(),.space(),.occupied(),
|
||||
.i(cpu2),.o(e2c)
|
||||
);
|
||||
|
||||
// CHDR DATA GATE
|
||||
// SIZED for 11 bit address when using a 64 bit word -> 16KByte
|
||||
// SIZED for 8 bit address when using a 512 bit word -> 16KByte
|
||||
axi4s_packet_gate #(
|
||||
.SIZE(17-$clog2(ENET_W))
|
||||
) chdr_out_gate_i (
|
||||
.clear(1'b0),.error(chdr0_error),
|
||||
.i(chdr0),.o(e2v)
|
||||
);
|
||||
|
||||
endmodule // eth_ipv4_chdr_dispatch
|
||||
@@ -0,0 +1,228 @@
|
||||
//
|
||||
// Copyright 2020 Ettus Research, a National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// Module: eth_ipv4_interface
|
||||
//
|
||||
// Description:
|
||||
// Adapts from internal CHDR to UDP/IPV4 Ethernet packets.
|
||||
// Packets not specifically addressed to CHDR are routed
|
||||
// to the CPU
|
||||
//
|
||||
// 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?
|
||||
// - DROP_MIN_PACKET: Drop packets smaller than 64 bytes?
|
||||
// - PREAMBLE_BYTES: Number of bytes of Preamble expected
|
||||
// - ADD_SOF: Add a SOF indication into the tuser field
|
||||
// - SYNC: Set if MAC is not the same as bus_clk
|
||||
// - ENET_W: Width of the link to the Ethernet MAC
|
||||
// - CPU_W: Width of the CPU interface
|
||||
// - CHDR_W: Width of the CHDR interface
|
||||
//
|
||||
|
||||
module eth_ipv4_interface #(
|
||||
logic [15:0] PROTOVER = {8'd1, 8'd0},
|
||||
int MTU = 10,
|
||||
int CPU_FIFO_SIZE = MTU,
|
||||
int NODE_INST = 0,
|
||||
int RT_TBL_SIZE = 6,
|
||||
int REG_AWIDTH = 14,
|
||||
int BASE = 0,
|
||||
bit DROP_UNKNOWN_MAC = 0,
|
||||
bit DROP_MIN_PACKET = 0,
|
||||
int PREAMBLE_BYTES = 6,
|
||||
bit ADD_SOF = 1,
|
||||
bit SYNC = 0,
|
||||
int ENET_W = 64,
|
||||
int CPU_W = 64,
|
||||
int CHDR_W = 64
|
||||
) (
|
||||
input logic bus_clk,
|
||||
input logic bus_rst,
|
||||
input logic [15:0] device_id,
|
||||
|
||||
// Register port: Write port (domain: bus_clk)
|
||||
input logic reg_wr_req,
|
||||
input logic [REG_AWIDTH-1:0] reg_wr_addr,
|
||||
input logic [31:0] reg_wr_data,
|
||||
|
||||
// Register port: Read port (domain: bus_clk)
|
||||
input logic reg_rd_req,
|
||||
input logic [REG_AWIDTH-1:0] reg_rd_addr,
|
||||
output logic reg_rd_resp,
|
||||
output logic [31:0] reg_rd_data,
|
||||
|
||||
// Status ports (domain: bus_clk)
|
||||
output logic [47:0] my_mac,
|
||||
output logic [31:0] my_ip,
|
||||
output logic [15:0] my_udp_chdr_port,
|
||||
|
||||
// Ethernet MAC
|
||||
AxiStreamIf.master eth_tx, // tUser = {1'b0,trailing bytes};
|
||||
AxiStreamIf.slave eth_rx, // tUser = {error,trailing bytes};
|
||||
// CHDR router interface
|
||||
AxiStreamIf.master e2v, // tUser = {*not used*};
|
||||
AxiStreamIf.slave v2e, // tUser = {*not used*};
|
||||
// CPU DMA
|
||||
AxiStreamIf.master e2c, // tUser = {sof,trailing bytes};
|
||||
AxiStreamIf.slave c2e // tUser = {1'b0,trailing bytes};
|
||||
|
||||
);
|
||||
|
||||
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
|
||||
//---------------------------------------------------------
|
||||
// Include for register offsets
|
||||
`include "eth_regs.vh"
|
||||
// Allocate one full page for M
|
||||
// mac_reg: MAC address for the dispatcher module. This value is used to
|
||||
// determine if the packet is meant for this device and should be consumed.
|
||||
//
|
||||
// ip_reg: 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.
|
||||
logic [47:0] mac_reg = DEFAULT_MAC_ADDR;
|
||||
logic [31:0] ip_reg = DEFAULT_IP_ADDR;
|
||||
logic [15:0] udp_port = DEFAULT_UDP_PORT;
|
||||
logic [47:0] bridge_mac_reg = DEFAULT_MAC_ADDR;
|
||||
logic [31:0] bridge_ip_reg = DEFAULT_IP_ADDR;
|
||||
logic [15:0] bridge_udp_port = DEFAULT_UDP_PORT;
|
||||
logic bridge_en;
|
||||
|
||||
always_comb begin : bridge_mux
|
||||
my_mac = bridge_en ? bridge_mac_reg : mac_reg;
|
||||
my_ip = bridge_en ? bridge_ip_reg : ip_reg;
|
||||
my_udp_chdr_port = bridge_en ? bridge_udp_port : udp_port;
|
||||
end
|
||||
|
||||
always_ff @(posedge bus_clk) begin : reg_wr_ff
|
||||
if (bus_rst) 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_ff @ (posedge bus_clk) begin : reg_rd_ff
|
||||
if (bus_rst) begin
|
||||
reg_rd_resp <= 1'b0;
|
||||
reg_rd_data <= 32'd0;
|
||||
end
|
||||
else begin
|
||||
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
|
||||
end
|
||||
|
||||
eth_ipv4_chdr_adapter #(
|
||||
.PROTOVER (PROTOVER),
|
||||
.MTU (MTU),
|
||||
.CPU_FIFO_SIZE (CPU_FIFO_SIZE),
|
||||
.RT_TBL_SIZE (RT_TBL_SIZE),
|
||||
.NODE_INST (NODE_INST),
|
||||
.DROP_UNKNOWN_MAC(DROP_UNKNOWN_MAC),
|
||||
.DROP_MIN_PACKET (DROP_MIN_PACKET),
|
||||
.PREAMBLE_BYTES (PREAMBLE_BYTES),
|
||||
.ADD_SOF (ADD_SOF),
|
||||
.SYNC (SYNC),
|
||||
.ENET_W (ENET_W),
|
||||
.CPU_W (CPU_W),
|
||||
.CHDR_W (CHDR_W)
|
||||
) eth_adapter_i (
|
||||
.eth_rx (eth_rx ),
|
||||
.eth_tx (eth_tx ),
|
||||
.v2e (v2e ),
|
||||
.e2v (e2v ),
|
||||
.c2e (c2e ),
|
||||
.e2c (e2c ),
|
||||
.device_id (device_id),
|
||||
.my_mac (my_mac ),
|
||||
.my_ip (my_ip ),
|
||||
.my_udp_chdr_port(my_udp_chdr_port )
|
||||
);
|
||||
|
||||
|
||||
endmodule : eth_ipv4_interface
|
||||
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// Copyright 2020 Ettus Research, a National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: eth_constants (Header File)
|
||||
//
|
||||
// Description:
|
||||
// Holds register offsets for the Ethernet Interface
|
||||
// NOTE: These depend on the following parameters
|
||||
// Parameters:
|
||||
// REG_AWIDTH - How wide the register window is in bits
|
||||
// BASE - Base address added to the offsets here
|
||||
// REGISTER OFFSETS
|
||||
// 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;
|
||||
Reference in New Issue
Block a user