aboutsummaryrefslogtreecommitdiffstats
path: root/fpga
diff options
context:
space:
mode:
authorAndrew Moch <Andrew.Moch@ni.com>2020-07-29 19:17:49 +0100
committerWade Fife <wade.fife@ettus.com>2020-08-05 17:18:12 -0500
commit7f86724ec3387f75d39d683b2ac5f5152e714c74 (patch)
treefe78329b0810cd67e6a4431590b17e2f6b232a8f /fpga
parent4ddd6530895a895772e629281b88145ba304338a (diff)
downloaduhd-7f86724ec3387f75d39d683b2ac5f5152e714c74.tar.gz
uhd-7f86724ec3387f75d39d683b2ac5f5152e714c74.tar.bz2
uhd-7f86724ec3387f75d39d683b2ac5f5152e714c74.zip
fpga: lib: Update xport_sv
- Detect dropped words at the dispatch level. This prevents an overflow on CHDR from block CPU. - Dropped packets are recorded as CPU or CHDR drop count - Refactor to put chdr_xport_adapter.sv in different clock domain to improve timing - Unwrinkle tkeep/trailing transitions
Diffstat (limited to 'fpga')
-rw-r--r--fpga/usrp3/lib/rfnoc/xport_sv/chdr_xport_adapter.sv20
-rw-r--r--fpga/usrp3/lib/rfnoc/xport_sv/eth_interface_tb/eth_ifc_tb.sv169
-rw-r--r--fpga/usrp3/lib/rfnoc/xport_sv/eth_ipv4_chdr_adapter.sv143
-rw-r--r--fpga/usrp3/lib/rfnoc/xport_sv/eth_ipv4_chdr_dispatch.sv222
-rw-r--r--fpga/usrp3/lib/rfnoc/xport_sv/eth_ipv4_interface.sv62
-rw-r--r--fpga/usrp3/lib/rfnoc/xport_sv/eth_regs.vh3
6 files changed, 437 insertions, 182 deletions
diff --git a/fpga/usrp3/lib/rfnoc/xport_sv/chdr_xport_adapter.sv b/fpga/usrp3/lib/rfnoc/xport_sv/chdr_xport_adapter.sv
index b898c1fe2..f757ddf5e 100644
--- a/fpga/usrp3/lib/rfnoc/xport_sv/chdr_xport_adapter.sv
+++ b/fpga/usrp3/lib/rfnoc/xport_sv/chdr_xport_adapter.sv
@@ -511,15 +511,29 @@ module chdr_xport_adapter #(
data_fifo_o.tready = au.tready & pass_packet;
end
+ // Clock Crossing to the ethernet clock domain
+ 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}));
+
// 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_src(e_my_mac),
+ .ip_src(e_my_ip),
+ .udp_src(e_my_udp_chdr_port),
.mac_dst(au_mac_dst),
.ip_dst(au_ip_dst),
.udp_dst(au_udp_dst)
diff --git a/fpga/usrp3/lib/rfnoc/xport_sv/eth_interface_tb/eth_ifc_tb.sv b/fpga/usrp3/lib/rfnoc/xport_sv/eth_interface_tb/eth_ifc_tb.sv
index 6241da9a5..b82d84315 100644
--- a/fpga/usrp3/lib/rfnoc/xport_sv/eth_interface_tb/eth_ifc_tb.sv
+++ b/fpga/usrp3/lib/rfnoc/xport_sv/eth_interface_tb/eth_ifc_tb.sv
@@ -48,7 +48,7 @@ module eth_ifc_tb #(
// Include for register offsets
`include "../eth_regs.vh"
// allows the DUT to push full words and tb does not check tuser/tkeep of packets it's transmitting
- localparam IGNORE_EXTRA_DATA = 1;
+ localparam IGNORE_EXTRA_DATA = 0;
//---------------------------------------------------------------------------
// Clocks
@@ -68,10 +68,10 @@ module eth_ifc_tb #(
TestExec test = new();
localparam MAX_PACKET_BYTES = 2**16;
- AxiStreamIf #(.DATA_WIDTH(ENET_W),.USER_WIDTH(ENET_USER_W),.TKEEP(0),
+ AxiStreamIf #(.DATA_WIDTH(ENET_W),.USER_WIDTH(ENET_USER_W),
.MAX_PACKET_BYTES(MAX_PACKET_BYTES))
eth_tx (eth_clk, eth_reset);
- AxiStreamIf #(.DATA_WIDTH(ENET_W),.USER_WIDTH(ENET_USER_W),.TKEEP(0),
+ AxiStreamIf #(.DATA_WIDTH(ENET_W),.USER_WIDTH(ENET_USER_W),
.MAX_PACKET_BYTES(MAX_PACKET_BYTES))
eth_rx (eth_clk, eth_reset);
@@ -86,7 +86,7 @@ module eth_ifc_tb #(
e2c (clk, reset);
// Bus functional model for a axi_stream controller
- AxiStreamBfm #(.DATA_WIDTH(ENET_W),.USER_WIDTH(ENET_USER_W),.TKEEP(0),
+ AxiStreamBfm #(.DATA_WIDTH(ENET_W),.USER_WIDTH(ENET_USER_W),
.MAX_PACKET_BYTES(MAX_PACKET_BYTES)) eth =
new(.master(eth_rx), .slave(eth_tx));
AxiStreamBfm #(.DATA_WIDTH(CHDR_W),.USER_WIDTH(CHDR_USER_W),.TKEEP(0),.TUSER(0)) v =
@@ -123,8 +123,12 @@ module eth_ifc_tb #(
eth_ipv4_interface #(
.PREAMBLE_BYTES(PREAMBLE_BYTES),
- .PROTOVER(PROTOVER), .MTU(MTU), .NODE_INST(NODE_INST),
- .REG_AWIDTH(REG_AWIDTH), .RT_TBL_SIZE(RT_TBL_SIZE),
+ .CPU_FIFO_SIZE(MTU),
+ .CHDR_FIFO_SIZE(MTU),
+ .PROTOVER(PROTOVER),
+ .NODE_INST(NODE_INST),
+ .REG_AWIDTH(REG_AWIDTH),
+ .RT_TBL_SIZE(RT_TBL_SIZE),
.BASE(BASE),.SYNC(SYNC),
.ENET_W(ENET_W),.CPU_W(CPU_W),.CHDR_W(CHDR_W)
) eth_interface (
@@ -168,6 +172,7 @@ module eth_ifc_tb #(
always_comb begin
eth_tx.tdata = eth_tx_tdata;
eth_tx.tuser = eth_tx_tuser;
+ eth_tx.tkeep = eth_tx.trailing2keep(eth_tx_tuser);
eth_tx.tlast = eth_tx_tlast;
eth_tx.tvalid = eth_tx_tvalid;
eth_tx_tready = eth_tx.tready;
@@ -202,7 +207,7 @@ module eth_ifc_tb #(
end
eth_interface #(
- .PROTOVER(PROTOVER), .MTU(MTU), .NODE_INST(NODE_INST),
+ .PROTOVER(PROTOVER), .NODE_INST(NODE_INST), .MTU(MTU),
.REG_AWIDTH(REG_AWIDTH), .RT_TBL_SIZE(RT_TBL_SIZE),
.BASE(BASE)
) eth_interface (
@@ -210,9 +215,6 @@ module eth_ifc_tb #(
.my_udp_port (my_udp_chdr_port)
);
end
- always_comb begin
- eth_tx.tkeep = '1;
- end
task automatic reg_wr (
// Register port: Write port (domain: clk)
@@ -310,7 +312,10 @@ module eth_ifc_tb #(
reg_rd_check(REG_BRIDGE_MAC_MSB,DEF_BRIDGE_MAC_ADDR[47:32]);
reg_rd_check(REG_BRIDGE_IP,DEF_BRIDGE_IP_ADDR);
reg_rd_check(REG_BRIDGE_UDP,DEF_BRIDGE_UDP_PORT);
-
+ if (SV_ETH_IFC) begin
+ reg_rd_check(REG_CHDR_DROPPED,0);
+ reg_rd_check(REG_CPU_DROPPED,0);
+ end
test.end_test();
endtask : test_registers
@@ -330,7 +335,7 @@ module eth_ifc_tb #(
typedef AxiStreamPacket #(CHDR_W,CHDR_USER_W) ChdrAxisPacket_t;
typedef ChdrPacket #(CHDR_W,CHDR_USER_W) ChdrPacket_t;
- task automatic test_ethcpu(int num_samples[$], int ERROR_PROB=2);
+ task automatic test_ethcpu(int num_samples[$], int ERROR_PROB=2, int EXPECT_DROPS=0);
TestExec test_e2c = new();
automatic EthXportPacket_t send[$];
automatic CpuXportPacket_t expected[$];
@@ -402,20 +407,41 @@ module eth_ifc_tb #(
end
end
begin //rx_thread
- foreach(expected[i]) begin
- automatic CpuAxisPacket_t actual_a;
- automatic CpuXportPacket_t actual = new();
- cpu.get(actual_a);
- actual.import_axis(actual_a);
- actual.tuser_to_tkeep();
-// $display({"****************::%s::**********************\n",
-// "**send** \n%s\n**expected**\n%s\n**actual**\n%s \n".
-// "%d byte with error %d"},
-// TEST_NAME,
-// send[i].sprint(),expected[i].sprint(),actual.sprint(),
-// num_samples[i],send[i].has_error());
-
- `ASSERT_ERROR(!actual.compare_w_sof(expected[i]),"failed to send packet to e2c");
+ if (EXPECT_DROPS > 0) begin
+ automatic int pkt_num = 0;
+ automatic int drop_count = 0;
+ while (expected.size() > 0) begin
+ automatic CpuAxisPacket_t actual_a;
+ automatic CpuXportPacket_t actual = new();
+ cpu.get(actual_a);
+ actual.import_axis(actual_a);
+ actual.tuser_to_tkeep();
+ while (expected.size > 0 && actual.compare_no_user(expected[0],.PRINT_LVL(0))) begin
+ void'(expected.pop_front());
+ ++drop_count;
+ ++pkt_num;
+ $display("Droped packet %d",pkt_num);
+ `ASSERT_ERROR(drop_count < EXPECT_DROPS,"Exceeded anticipated number of dropped packets e2c");
+ end
+ if (expected.size() > 0) begin
+ ++pkt_num;
+ $display("Rcvd packet %d",pkt_num);
+ void'(expected.pop_front());
+ end
+ end
+ if (SV_ETH_IFC) begin
+ $display("Verify drop count is %d",drop_count);
+ reg_rd_check(REG_CPU_DROPPED,drop_count);
+ end
+ end else begin
+ foreach(expected[i]) begin
+ automatic CpuAxisPacket_t actual_a;
+ automatic CpuXportPacket_t actual = new();
+ cpu.get(actual_a);
+ actual.import_axis(actual_a);
+ actual.tuser_to_tkeep();
+ `ASSERT_ERROR(!actual.compare_w_sof(expected[i]),"failed to send packet to e2c");
+ end
end
end
join
@@ -512,7 +538,9 @@ module eth_ifc_tb #(
wait_for_udp_packets(DEF_DEST_UDP_PORT);
eth.get(actual_a);
actual.import_axis(actual_a);
- actual.tuser_to_tkeep();
+ if (!SV_ETH_IFC) begin
+ actual.tuser_to_tkeep();
+ end
`ASSERT_ERROR(!actual.compare_w_pad(expected[i],!SV_ETH_IFC),"failed to send packet to c2e");
end
end
@@ -556,7 +584,7 @@ module eth_ifc_tb #(
return chdr_pkt;
endfunction : unflatten_chdr
- task automatic test_ethchdr(int num_samples[$], int ERROR_PROB=2);
+ task automatic test_ethchdr(int num_samples[$], int ERROR_PROB=2, int EXPECT_DROPS=0);
TestExec test_e2v = new();
automatic EthXportPacket_t send[$];
automatic ChdrXportPacket_t expected[$];
@@ -655,13 +683,41 @@ module eth_ifc_tb #(
end
end
begin //rx_thread
- foreach(expected[i]) begin
- automatic ChdrAxisPacket_t actual_a;
- automatic ChdrXportPacket_t actual = new();
- v.get(actual_a);
- actual.import_axis(actual_a);
- actual.tuser_to_tkeep();
- `ASSERT_ERROR(!actual.compare_no_user(expected[i]),"failed to send packet e2v");
+ if (EXPECT_DROPS > 0) begin
+ automatic int pkt_num = 0;
+ automatic int drop_count = 0;
+ while (expected.size() > 0) begin
+ automatic ChdrAxisPacket_t actual_a;
+ automatic ChdrXportPacket_t actual = new();
+ v.get(actual_a);
+ actual.import_axis(actual_a);
+ actual.tuser_to_tkeep();
+ while (expected.size > 0 && actual.compare_no_user(expected[0],.PRINT_LVL(0))) begin
+ void'(expected.pop_front());
+ ++drop_count;
+ ++pkt_num;
+ $display("Droped packet %d",pkt_num);
+ `ASSERT_ERROR(drop_count < EXPECT_DROPS,"Exceeded anticipated number of dropped packets e2v");
+ end
+ if (expected.size() > 0) begin
+ ++pkt_num;
+ $display("Rcvd packet %d",pkt_num);
+ void'(expected.pop_front());
+ end
+ end
+ if (SV_ETH_IFC) begin
+ $display("Verify drop count is %d",drop_count);
+ reg_rd_check(REG_CHDR_DROPPED,drop_count);
+ end
+ end else begin
+ foreach(expected[i]) begin
+ automatic ChdrAxisPacket_t actual_a;
+ automatic ChdrXportPacket_t actual = new();
+ v.get(actual_a);
+ actual.import_axis(actual_a);
+ actual.tuser_to_tkeep();
+ `ASSERT_ERROR(!actual.compare_no_user(expected[i]),"failed to send packet e2v");
+ end
end
end
join
@@ -785,9 +841,8 @@ module eth_ifc_tb #(
wait_for_udp_packets(.udp_dest_port(0));
eth.get(actual_a);
actual.import_axis(actual_a);
- actual.tuser_to_tkeep();
actual_raw = actual.dump_bytes();
- repeat(PREAMBLE_BYTES) actual_raw.pop_front();
+ repeat(PREAMBLE_BYTES) void'(actual_raw.pop_front());
decode_udp_pkt(actual_raw,eth_hdr,ipv4_hdr,udp_hdr,chdr_raw);
chdr_pkt = unflatten_chdr(chdr_raw);
// fills remainder of packet with zeros
@@ -939,9 +994,8 @@ module eth_ifc_tb #(
eth.get(actual_a);
actual.import_axis(actual_a);
- actual.tuser_to_tkeep();
actual_raw = actual.dump_bytes();
- repeat(PREAMBLE_BYTES) actual_raw.pop_front();
+ repeat(PREAMBLE_BYTES) void'(actual_raw.pop_front());
decode_udp_pkt(actual_raw,eth_hdr,ipv4_hdr,udp_hdr,chdr_raw);
chdr_pkt = unflatten_chdr(chdr_raw);
// fills remainder of packet with zeros
@@ -966,6 +1020,7 @@ module eth_ifc_tb #(
initial begin : tb_main
automatic int num_samples[$];
automatic int cpu_num_samples[$];
+ automatic int expected_drops;
localparam QUICK = 0;
test.start_test({TEST_NAME,"Wait for Reset"}, 10us);
clk_gen.reset();
@@ -981,6 +1036,42 @@ module eth_ifc_tb #(
test_chdr_endpoint();
+ // Check what happens if the input bandwidth exceeds
+ // the devices ability to consume packets
+ // This can happen in matched bandwidth cases
+ // if there is hold off from upstream
+ // Dropped packets exceed the drop count cause an error
+ // The actual droped count is compared versus the real count
+ test.start_test({TEST_NAME,"::Input overrun"}, 200us);
+
+ eth.set_master_stall_prob(0);
+ eth.set_slave_stall_prob(0);
+ cpu.set_master_stall_prob(0);
+ cpu.set_slave_stall_prob(0);
+ v.set_master_stall_prob(0);
+ v.set_slave_stall_prob(0);
+
+ num_samples = {7936,7936,7936,7936,7936,320,
+ 7936,7936,7936,7936,7936,320};
+
+ // The actual number of expected drops depends on the
+ // bus width difference between ENET_W and CHDR/CPU_W
+
+ // in this SIM unlimited etherent bandwidth is coming in at over 300 MHZ
+ // and output runs at 200 MHZ. This causes excess BW on transmitter even when matched.
+ expected_drops = 9;
+
+ test_ethchdr(num_samples,.EXPECT_DROPS(expected_drops),.ERROR_PROB(0));
+ test_ethcpu(num_samples,.EXPECT_DROPS(expected_drops),.ERROR_PROB(0));
+ test.end_test();
+
+ eth.set_master_stall_prob(38);
+ eth.set_slave_stall_prob(38);
+ cpu.set_master_stall_prob(38);
+ cpu.set_slave_stall_prob(38);
+ v.set_master_stall_prob(38);
+ v.set_slave_stall_prob(38);
+
num_samples = {1,2,3,4,5,6,7,8,
ENET_W/8-1,ENET_W/8,ENET_W/8+1,
2*ENET_W/8-1,2*ENET_W/8,2*ENET_W/8+1,
diff --git a/fpga/usrp3/lib/rfnoc/xport_sv/eth_ipv4_chdr_adapter.sv b/fpga/usrp3/lib/rfnoc/xport_sv/eth_ipv4_chdr_adapter.sv
index 8505836bf..892e0a497 100644
--- a/fpga/usrp3/lib/rfnoc/xport_sv/eth_ipv4_chdr_adapter.sv
+++ b/fpga/usrp3/lib/rfnoc/xport_sv/eth_ipv4_chdr_adapter.sv
@@ -16,15 +16,15 @@
//
// 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
+// - CPU_FIFO_SIZE: Log2 of the FIFO depth (in bytes) for the CPU egress path
+// - CHDR_FIFO_SIZE: Log2 of the FIFO depth (in bytes) for the CHDR 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
+// - ADD_SOF: Add a SOF indication into the tuser field of the e2c path.
+// 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
@@ -45,8 +45,8 @@
module eth_ipv4_chdr_adapter #(
logic [15:0] PROTOVER = {8'd1, 8'd0},
- int MTU = 10,
- int CPU_FIFO_SIZE = MTU,
+ int CPU_FIFO_SIZE = $clog2(8*1024),
+ int CHDR_FIFO_SIZE = $clog2(8*1024),
int RT_TBL_SIZE = 6,
int NODE_INST = 0,
bit DROP_UNKNOWN_MAC = 0,
@@ -58,6 +58,7 @@ module eth_ipv4_chdr_adapter #(
int CPU_W = 64,
int CHDR_W = 64
)(
+
// Device info
input logic [15:0] device_id,
// Device addresses
@@ -65,6 +66,9 @@ module eth_ipv4_chdr_adapter #(
input logic [31:0] my_ip,
input logic [15:0] my_udp_chdr_port,
+ output logic chdr_dropped,
+ output logic cpu_dropped,
+
// Ethernet MAC
AxiStreamIf.master eth_tx, // tUser = {1'b0,trailing bytes};
AxiStreamIf.slave eth_rx, // tUser = {error,trailing bytes};
@@ -90,6 +94,10 @@ module eth_ipv4_chdr_adapter #(
`include "eth_constants.vh"
+
+ // tUser = {error,trailing_bytes}
+ AxiStreamIf #(.DATA_WIDTH(ENET_W),.USER_WIDTH(ENET_USER_W),.TKEEP(0)) eth_rx1(eth_rx.clk,eth_rx.rst);
+
//---------------------------------------
// E2V and E2C DEMUX
//---------------------------------------
@@ -98,9 +106,8 @@ module eth_ipv4_chdr_adapter #(
// 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);
+ AxiStreamIf #(.DATA_WIDTH(CHDR_W),.TKEEP(0),.TUSER(0)) e2v3(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);
@@ -109,35 +116,35 @@ module eth_ipv4_chdr_adapter #(
.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}));
+ // The older implementation connected with tUser containing trailing bytes
+ // The newer implementation brings in TKEEP
+ // inside this block we expect trailing bytes.
+ always_comb begin
+ `AXI4S_ASSIGN(eth_rx1,eth_rx)
+ if (eth_rx.TKEEP) begin
+ eth_rx1.tuser = {eth_rx.tuser[ENET_USER_W-1],eth_rx.keep2trailing(eth_rx.tkeep)};
+ end
+ end
// Ethernet sink. Inspects packet and dispatches
// to the correct port.
eth_ipv4_chdr_dispatch #(
.CPU_FIFO_SIZE(CPU_FIFO_SIZE),
+ .CHDR_FIFO_SIZE(CHDR_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),
+ .eth_rx (eth_rx1),
.e2v (e2v1),
.e2c (e2c1),
- .my_mac (e_my_mac),
- .my_ip (e_my_ip),
- .my_udp_chdr_port (e_my_udp_chdr_port)
+ .my_mac (my_mac),
+ .my_ip (my_ip),
+ .my_udp_chdr_port (my_udp_chdr_port),
+ .chdr_dropped (chdr_dropped),
+ .cpu_dropped (cpu_dropped)
);
//---------------------------------------
@@ -222,7 +229,7 @@ module eth_ipv4_chdr_adapter #(
.my_udp_chdr_port (my_udp_chdr_port),
.eth_rx (e2v2), // from ethernet
- .e2v (e2v4), // to CHDR
+ .e2v (e2v3), // to CHDR
// optional loop from ethernet to ethernet to talk to node
.v2e (v2e), // from CHDR
.eth_tx (v2e1D) // to ethernet
@@ -261,28 +268,14 @@ module eth_ipv4_chdr_adapter #(
// 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)
+ `AXI4S_DEBUG_ASSIGN(e2v,e2v3)
end else begin
always_comb begin : e2v_direct_assign
- `AXI4S_ASSIGN(e2v,e2v5)
+ `AXI4S_ASSIGN(e2v,e2v3)
end
end
-
-
-
//---------------------------------------
// C2E Path
//---------------------------------------
@@ -291,7 +284,7 @@ module eth_ipv4_chdr_adapter #(
.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);
+ AxiStreamIf #(.DATA_WIDTH(ENET_W),.USER_WIDTH(ENET_USER_W),.TKEEP(0),.MAX_PACKET_BYTES(MAX_PACKET_BYTES)) 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}
@@ -332,8 +325,8 @@ module eth_ipv4_chdr_adapter #(
// 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
+ // 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)
@@ -380,7 +373,7 @@ module eth_ipv4_chdr_adapter #(
always_comb begin : c2e3_pad
if (pad_state == ST_IDLE) begin
- //force to a full word
+ // force to a full word
// preserve SOF if it's there, but force
// trailing bytes to zero (full word)
c2e3.tuser = 0;
@@ -429,12 +422,13 @@ module eth_ipv4_chdr_adapter #(
//---------------------------------------
// V2E and C2E MUX
//---------------------------------------
+ // tUser = {1'b0,trailing bytes}
+ AxiStreamIf #(.DATA_WIDTH(ENET_W),.USER_WIDTH(ENET_USER_W),.TKEEP(0)) eth_tx1 (eth_rx.clk,eth_rx.rst);
+ // tUser = {1'b0,trailing bytes}
+ AxiStreamIf #(.DATA_WIDTH(ENET_W),.USER_WIDTH(ENET_USER_W)) eth_tx2 (eth_rx.clk,eth_rx.rst);
+
+
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
@@ -444,30 +438,47 @@ module eth_ipv4_chdr_adapter #(
.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)
+ .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
+ // 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;
+ if (eth_tx1.tvalid) begin
+ eth_tx2.tvalid = 1'b1;
+ eth_tx2.tdata = eth_tx1.tdata;
+ eth_tx2.tlast = eth_tx1.tlast;
+ // driving both tuser and tkeep
+ if (eth_tx1.tlast) begin
+ eth_tx2.tuser = eth_tx1.tuser;
+ eth_tx2.tkeep = eth_tx1.trailing2keep(eth_tx1.tuser);
end else begin
- eth_tx.tuser = '0;
+ eth_tx2.tuser = '0;
+ eth_tx2.tkeep = '1;
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;
+ eth_tx2.tvalid = 1'b0;
+ eth_tx2.tdata = 'X; // use X so synth will optimize
+ eth_tx2.tlast = 0;
+ eth_tx2.tuser = '0;
+ eth_tx2.tkeep = '1;
end
- eth_tx1_tready = eth_tx.tready;
+ eth_tx1.tready = eth_tx2.tready;
end
+ //---------------------------------------
+ // Output pipeline stage
+ //---------------------------------------
+ axi4s_fifo #(
+ .SIZE(1)
+ ) in_reg_i (
+ .clear(1'b0),.space(),.occupied(),
+ .i(eth_tx2), .o(eth_tx)
+ );
+
+
endmodule // eth_ipv4_chdr_adapter
diff --git a/fpga/usrp3/lib/rfnoc/xport_sv/eth_ipv4_chdr_dispatch.sv b/fpga/usrp3/lib/rfnoc/xport_sv/eth_ipv4_chdr_dispatch.sv
index 36f887190..578646640 100644
--- a/fpga/usrp3/lib/rfnoc/xport_sv/eth_ipv4_chdr_dispatch.sv
+++ b/fpga/usrp3/lib/rfnoc/xport_sv/eth_ipv4_chdr_dispatch.sv
@@ -17,7 +17,8 @@
// Traffic not addressed (Eth) to us is dropped(optionally).
//
// Parameters:
-// - CPU_FIFO_SIZE: log2 size of CPU RX fifo
+// - CPU_FIFO_SIZE: Log2 of the FIFO depth (in bytes) for the CPU egress path
+// - CHDR_FIFO_SIZE: Log2 of the FIFO depth (in bytes) for the CHDR egress path
// - 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?
@@ -35,7 +36,8 @@
//
module eth_ipv4_chdr_dispatch #(
- int CPU_FIFO_SIZE = $clog2(1558),
+ int CPU_FIFO_SIZE = $clog2(8*1024),
+ int CHDR_FIFO_SIZE = $clog2(8*1024),
int PREAMBLE_BYTES = 6,
int MAX_PACKET_BYTES = 2**16-1,
bit DROP_UNKNOWN_MAC = 0,
@@ -51,9 +53,27 @@ module eth_ipv4_chdr_dispatch #(
// Device addresses
input logic [47:0] my_mac,
input logic [31:0] my_ip,
- input logic [15:0] my_udp_chdr_port
+ input logic [15:0] my_udp_chdr_port,
+
+ output logic chdr_dropped,
+ output logic cpu_dropped
);
+ // Clock Crossing to the ethernet clock domain
+ 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}));
+
+
localparam ENET_USER_W = $clog2(ENET_W/8)+1;
//---------------------------------------
// Include for byte positions
@@ -68,6 +88,9 @@ module eth_ipv4_chdr_dispatch #(
``I.tready = ``O.tready;
// axi_remov_bytes (PREAMBLE Strip)
+ AxiStreamIf #(.DATA_WIDTH(ENET_W),.USER_WIDTH(ENET_USER_W),
+ .TKEEP(0),.MAX_PACKET_BYTES(MAX_PACKET_BYTES))
+ inp(eth_rx.clk,eth_rx.rst);
// tUser = {error,trailing bytes};
AxiStreamPacketIf #(.DATA_WIDTH(ENET_W),.USER_WIDTH(ENET_USER_W),
.TKEEP(0),.MAX_PACKET_BYTES(MAX_PACKET_BYTES))
@@ -86,21 +109,21 @@ module eth_ipv4_chdr_dispatch #(
// 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
+ // cpu_out_gate - throw away error packets
// 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};
+ // tUser = {not used};
AxiStreamIf #(.DATA_WIDTH(ENET_W),.TKEEP(0),.TUSER(0))
chdr0(eth_rx.clk,eth_rx.rst);
+ // tUser = {not used};
+ AxiStreamIf #(.DATA_WIDTH(ENET_W),.TKEEP(0),.TUSER(0))
+ chdr1(eth_rx.clk,eth_rx.rst);
+ // chdr_out_fifo
// e2v(OUTPUT)
//---------------------------------------
@@ -108,9 +131,12 @@ module eth_ipv4_chdr_dispatch #(
//---------------------------------------
if (PREAMBLE_BYTES > 0) begin : gen_strip_preamble
// Strip the preamble
+ always_comb begin
+ `AXI4S_ASSIGN(inp,eth_rx);
+ end
axi4s_remove_bytes #(.REM_START(0),.REM_END(PREAMBLE_BYTES-1)
) strip_preamble (
- .i(eth_rx),.o(in0)
+ .i(inp),.o(in0)
);
end else begin : gen_no_preamble
always_comb begin
@@ -143,10 +169,11 @@ module eth_ipv4_chdr_dispatch #(
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 mac_error;
+ logic min_packet_error;
logic reached_min_packet;
+ logic cpu_push_error;
+ logic chdr_push_error;
// Cached fields
logic [47:0] eth_dst_addr_new, eth_src_addr_new;
@@ -180,8 +207,6 @@ module eth_ipv4_chdr_dispatch #(
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
@@ -204,26 +229,12 @@ module eth_ipv4_chdr_dispatch #(
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;
+ eth_dst_is_me <= eth_dst_addr_new == e_my_mac;
+ udp_dst_is_me <= udp_dst_port_new == e_my_udp_chdr_port;
+ ipv4_dst_is_me <= ipv4_dst_addr_new == e_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
@@ -240,15 +251,19 @@ module eth_ipv4_chdr_dispatch #(
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;
+
+ // calculate error conditions
+ assign mac_error = in1.tuser[ERROR_BIT] && in1.tvalid;
+ assign cpu_push_error = (in2.tvalid && !cpu0.tready);
+ assign chdr_push_error = (in2.tvalid && !chdr0.tready);
+
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;
+ assign min_packet_error = (in1.tlast && !reached_min_packet);
end else begin
assign reached_min_packet = 1'b1;
assign min_packet_error = 1'b0;
@@ -271,16 +286,16 @@ module eth_ipv4_chdr_dispatch #(
//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;
+ in1.tready = 1'b1; // never hold off
// 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
+ if (mac_error || min_packet_error || cpu_push_error || chdr_push_error) begin
cpu_error = 1'b1;
chdr_error = 1'b1;
next_dispatch_state = ST_DROP_TERM;
@@ -318,7 +333,7 @@ module eth_ipv4_chdr_dispatch #(
ST_FWD_CHDR: begin
cpu_error = 1'b1;
chdr_error = 1'b0;
- if (mac_error || min_packet_error) begin
+ if (mac_error || min_packet_error || chdr_push_error) begin
cpu_error = 1'b1;
chdr_error = 1'b1;
next_dispatch_state = ST_DROP_TERM;
@@ -329,7 +344,7 @@ module eth_ipv4_chdr_dispatch #(
ST_FWD_CPU: begin
cpu_error = 1'b0;
chdr_error = 1'b1;
- if (mac_error || min_packet_error) begin
+ if (mac_error || min_packet_error || cpu_push_error) begin
cpu_error = 1'b1;
chdr_error = 1'b1;
next_dispatch_state = ST_DROP_TERM;
@@ -342,7 +357,6 @@ module eth_ipv4_chdr_dispatch #(
chdr_error = 1'b1;
in2.tlast = 1'b1;
in2.tvalid = 1'b1;
- in1.tready = in2.tready;
next_dispatch_state = ST_DROP_WAIT;
end
@@ -352,14 +366,12 @@ module eth_ipv4_chdr_dispatch #(
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;
@@ -371,65 +383,143 @@ module eth_ipv4_chdr_dispatch #(
//---------------------------------------
// SPLIT
//---------------------------------------
+ // differentiating push_errors for reporting
+ logic cpu0_push_error, cpu0_push_error_old= 1'b0;
+ logic chdr0_push_error, chdr0_push_error_old= 1'b0;
+ logic chdr0_error, chdr0_error_old = 1'b0;
+ logic cpu0_error, cpu0_error_old = 1'b0;
+
always_comb begin : cpu0_assign
+ cpu0_error = (cpu_error && in2.tvalid) || cpu0_error_old;
+ cpu0_push_error = (cpu_push_error && in2.tvalid)|| cpu0_push_error_old;
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;
+ cpu0.tlast = in2.tlast || cpu0_error;
+ cpu0.tvalid = in2.tvalid || cpu0_error;
+ chdr0_error = (chdr_error && in2.tvalid) || chdr0_error_old;
+ chdr0_push_error = (chdr_push_error && in2.tvalid) || chdr0_push_error_old;
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;
+ chdr0.tlast = in2.tlast || chdr0_error;
+ chdr0.tvalid = in2.tvalid || chdr0_error;
+
+ // If the downstream sections are not ready, then the packet is dropped
+ // there isn't really any buffer up stream, so a hold off here would
+ // mean pushing back on a mac that doesn't have the capability to slow
+ // down, and a data word would be lost.
+ in2.tready = 1'b1;
- 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)
- );
+ // hold the error bits until the end of the packet
+ always_ff @(posedge eth_rx.clk) begin : error_ff
+ if (eth_rx.rst) begin
+ cpu0_error_old <= 1'b0;
+ chdr0_error_old <= 1'b0;
+ cpu0_push_error_old <= 1'b0;
+ chdr0_push_error_old <= 1'b0;
+ chdr_dropped <= 1'b0;
+ cpu_dropped <= 1'b0;
+ end else begin
+
+ // report dropped back at the end of packet when push_error is detected.
+ // 1st term counts the drop if ready lets up before the end of the packet
+ // 2nd term counts the drop if ready is held through then end of the packet
+ // NOTE: Drop counts don't have to be perfect. This gets pretty close though.
+ // I.e. Don't sweat this more in the future.
+ chdr_dropped <= (chdr0_push_error && chdr0.tlast && chdr0.tvalid && chdr0.tready) ||
+ (chdr0_push_error && in1.tlast && in1.tvalid);
+ cpu_dropped <= (cpu0_push_error && cpu0.tlast && cpu0.tvalid && cpu0.tready) ||
+ (cpu0_push_error && in1.tlast && in1.tvalid);
+
+ // don't clear till we discard a packet
+ if (cpu0.tlast && cpu0.tvalid && cpu0.tready) begin
+ cpu0_push_error_old <= 1'b0;
+ // remember if we saw an error
+ end else if (cpu0_push_error && cpu0.tvalid) begin
+ cpu0_push_error_old <= 1'b1;
+ end
+
+ // don't clear till we discard a packet
+ if (cpu0.tlast && cpu0.tvalid && cpu0.tready) begin
+ cpu0_error_old <= 1'b0;
+ // remember if we saw an error
+ end else if (cpu0_error && cpu0.tvalid) begin
+ cpu0_error_old <= 1'b1;
+ end
+
+ // don't clear till we discard a packet
+ if (chdr0.tlast && chdr0.tvalid && chdr0.tready) begin
+ chdr0_push_error_old <= 1'b0;
+ // remember if we saw an error
+ end else if (chdr0_push_error && chdr0.tvalid) begin
+ chdr0_push_error_old <= 1'b1;
+ end
+
+ // don't clear till we discard a packet
+ if (chdr0.tlast && chdr0.tvalid && chdr0.tready) begin
+ chdr0_error_old <= 1'b0;
+ // remember if we saw an error
+ end else if (chdr0_error && chdr0.tvalid) begin
+ chdr0_error_old <= 1'b1;
+ end
+
+ end
+ end
// 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.
+ // path
+
+ //---------------------------------------
+ // CPU Output processing
+ //---------------------------------------
// 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)
+ .SIZE(14-$clog2(ENET_W/8)), .USE_AS_BUFF(0)
) cpu_out_gate_i (
- .clear(1'b0), .error(cpu1.tuser[ERROR_BIT]),
- .i(cpu1),.o(cpu2)
+ .clear(1'b0), .error(cpu0_error),
+ .i(cpu0),.o(cpu1)
);
// 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)
+ .SIZE(CPU_FIFO_SIZE-$clog2(ENET_W/8))
) cpu_fifo_i (
.clear(),.space(),.occupied(),
- .i(cpu2),.o(e2c)
+ .i(cpu1),.o(e2c)
);
+ //---------------------------------------
+ // CHDR Output processing
+ //---------------------------------------
// 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))
+ .SIZE(14-$clog2(ENET_W/8))
) chdr_out_gate_i (
.clear(1'b0),.error(chdr0_error),
- .i(chdr0),.o(e2v)
+ .i(chdr0),.o(chdr1)
+ );
+
+ // 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 at laeast one packet of buffering here.
+ // The actual size is set by CHDR_FIFO_SIZE.
+ axi4s_fifo #(
+ .SIZE(CHDR_FIFO_SIZE-$clog2(ENET_W/8))
+ ) chdr_fifo_i (
+ .clear(1'b0),.space(),.occupied(),
+ .i(chdr1),.o(e2v)
);
endmodule // eth_ipv4_chdr_dispatch
diff --git a/fpga/usrp3/lib/rfnoc/xport_sv/eth_ipv4_interface.sv b/fpga/usrp3/lib/rfnoc/xport_sv/eth_ipv4_interface.sv
index 0c9f33f8c..80670e029 100644
--- a/fpga/usrp3/lib/rfnoc/xport_sv/eth_ipv4_interface.sv
+++ b/fpga/usrp3/lib/rfnoc/xport_sv/eth_ipv4_interface.sv
@@ -11,14 +11,14 @@
//
// 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
+// - CPU_FIFO_SIZE: Log2 of the FIFO depth (in bytes) for the CPU egress path
+// - CHDR_FIFO_SIZE: Log2 of the FIFO depth (in bytes) for the CHDR 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
+// - ADD_SOF: Add a SOF indication into the tuser field of e2c
// - 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
@@ -27,8 +27,8 @@
module eth_ipv4_interface #(
logic [15:0] PROTOVER = {8'd1, 8'd0},
- int MTU = 10,
- int CPU_FIFO_SIZE = MTU,
+ int CPU_FIFO_SIZE = $clog2(8*1024),
+ int CHDR_FIFO_SIZE = $clog2(8*1024),
int NODE_INST = 0,
int RT_TBL_SIZE = 6,
int REG_AWIDTH = 14,
@@ -98,6 +98,10 @@ module eth_ipv4_interface #(
logic [31:0] bridge_ip_reg = DEFAULT_IP_ADDR;
logic [15:0] bridge_udp_port = DEFAULT_UDP_PORT;
logic bridge_en;
+ logic cpu_dropped;
+ logic chdr_dropped;
+ logic [31:0] chdr_drop_count = 0;
+ logic [31:0] cpu_drop_count = 0;
always_comb begin : bridge_mux
my_mac = bridge_en ? bridge_mac_reg : mac_reg;
@@ -153,8 +157,16 @@ module eth_ipv4_interface #(
if (bus_rst) begin
reg_rd_resp <= 1'b0;
reg_rd_data <= 32'd0;
+ chdr_drop_count <= 32'd0;
+ cpu_drop_count <= 32'd0;
end
else begin
+ if (chdr_dropped) begin
+ chdr_drop_count <= chdr_drop_count+1;
+ end
+ if (cpu_dropped) begin
+ cpu_drop_count <= cpu_drop_count+1;
+ end
if (reg_rd_req) begin
// Assert read response one cycle after read request
reg_rd_resp <= 1'b1;
@@ -185,7 +197,19 @@ module eth_ipv4_interface #(
REG_BRIDGE_ENABLE:
reg_rd_data <= {31'b0,bridge_en};
-
+ // Drop counts are used to debug situations
+ // Where the incoming data goes faster than
+ // chdr can consume it
+ REG_CHDR_DROPPED:
+ begin
+ reg_rd_data <= chdr_drop_count;
+ chdr_drop_count <= 0; // clear when read
+ end
+ REG_CPU_DROPPED:
+ begin
+ reg_rd_data <= cpu_drop_count;
+ cpu_drop_count <= 0; // clear when read
+ end
default:
reg_rd_resp <= 1'b0;
endcase
@@ -197,10 +221,30 @@ module eth_ipv4_interface #(
end
end
+ logic b_dropped_valid;
+ logic e_cpu_dropped, b_cpu_dropped;
+ logic e_chdr_dropped, b_chdr_dropped;
+ // push over the clock domain
+ // Sized to fit into 2 SRL's
+ axi_fifo_2clk #(.WIDTH(2), .SIZE(4)) fifo_i (
+ .reset(eth_rx.rst),
+ .i_aclk(eth_rx.clk),
+ .i_tdata({e_cpu_dropped, e_chdr_dropped}),
+ .i_tvalid(e_cpu_dropped || e_chdr_dropped), .i_tready(/*not used*/),
+ .o_aclk(bus_clk),
+ .o_tdata({b_cpu_dropped, b_chdr_dropped}),
+ .o_tvalid(b_dropped_valid), .o_tready(1'b1)
+ );
+
+ always_comb begin
+ cpu_dropped = b_cpu_dropped && b_dropped_valid;
+ chdr_dropped = b_chdr_dropped && b_dropped_valid;
+ end
+
eth_ipv4_chdr_adapter #(
.PROTOVER (PROTOVER),
- .MTU (MTU),
.CPU_FIFO_SIZE (CPU_FIFO_SIZE),
+ .CHDR_FIFO_SIZE (CHDR_FIFO_SIZE),
.RT_TBL_SIZE (RT_TBL_SIZE),
.NODE_INST (NODE_INST),
.DROP_UNKNOWN_MAC(DROP_UNKNOWN_MAC),
@@ -221,7 +265,9 @@ module eth_ipv4_interface #(
.device_id (device_id),
.my_mac (my_mac ),
.my_ip (my_ip ),
- .my_udp_chdr_port(my_udp_chdr_port )
+ .my_udp_chdr_port(my_udp_chdr_port),
+ .chdr_dropped (e_chdr_dropped),
+ .cpu_dropped (e_cpu_dropped)
);
diff --git a/fpga/usrp3/lib/rfnoc/xport_sv/eth_regs.vh b/fpga/usrp3/lib/rfnoc/xport_sv/eth_regs.vh
index 354a19414..1deb35aee 100644
--- a/fpga/usrp3/lib/rfnoc/xport_sv/eth_regs.vh
+++ b/fpga/usrp3/lib/rfnoc/xport_sv/eth_regs.vh
@@ -27,3 +27,6 @@ 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;
+
+localparam [REG_AWIDTH-1:0] REG_CHDR_DROPPED = BASE + 'h1030;
+localparam [REG_AWIDTH-1:0] REG_CPU_DROPPED = BASE + 'h1034;