From 7f86724ec3387f75d39d683b2ac5f5152e714c74 Mon Sep 17 00:00:00 2001 From: Andrew Moch Date: Wed, 29 Jul 2020 19:17:49 +0100 Subject: 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 --- .../usrp3/lib/rfnoc/xport_sv/chdr_xport_adapter.sv | 20 +- .../rfnoc/xport_sv/eth_interface_tb/eth_ifc_tb.sv | 169 ++++++++++++---- .../lib/rfnoc/xport_sv/eth_ipv4_chdr_adapter.sv | 143 +++++++------ .../lib/rfnoc/xport_sv/eth_ipv4_chdr_dispatch.sv | 222 +++++++++++++++------ .../usrp3/lib/rfnoc/xport_sv/eth_ipv4_interface.sv | 62 +++++- fpga/usrp3/lib/rfnoc/xport_sv/eth_regs.vh | 3 + 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, 8'd} -// - 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, 8'd} -// - 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; -- cgit v1.2.3