Files
b210-k7-fpga/lib/rfnoc/xport_sv/eth_ipv4_add_udp.sv
T
Andrew Moch 05eb1bc2f1 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
2020-06-30 10:29:35 -05:00

201 lines
7.1 KiB
Systemverilog

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