Description
The udp_checksum_gen_64 module drops a header when the header fifo becomes full.
This happens due to ready latency in the header_fifo_ready path back to s_udp_hdr_ready.
On the first clock cycle the state machine is in STATE_IDLE after STATE_FINISH_SUM_2, hdr_valid_reg will be 1 due to the registered header commit in STATE_FINISH_SUM_2. If this commit fills the fifo, the fifo will be full on the next clock cycle. However, header_fifo_ready and thus s_udp_hdr_ready_next is still 1 in this clock cycle, thus s_udp_hdr_ready will be 1 in the next clock cycle even though the fifo will be full. This causes the state machine to register a new header and progress from STATE_IDLE on the next clock cycle if s_udp_hdr_valid = '1'. When the state machine finishes, it writes a final hdr_valid_next even though the fifo is full, and the header is then essentially dropped.
Picked this up in system. Replicated this in test_udp_checksum_64.py by running the following test (currently commented out other tests...)
It sends a bunch of packets into the fifo with the sink paused to force a fifo overflow condition. Not the best test, but it illustrates the point.
yield clk.posedge
payload_len = 8
print("test 4: header fifo overflow with back-to-back packets, length %d" % payload_len)
current_test.next = 4
test_frame1 = udp_ep.UDPFrame()
test_frame1.eth_dest_mac = 0xDAD1D2D3D4D5
test_frame1.eth_src_mac = 0x5A5152535455
test_frame1.eth_type = 0x0800
test_frame1.ip_version = 4
test_frame1.ip_ihl = 5
test_frame1.ip_length = None
test_frame1.ip_identification = 0
test_frame1.ip_flags = 2
test_frame1.ip_fragment_offset = 0
test_frame1.ip_ttl = 64
test_frame1.ip_protocol = 0x11
test_frame1.ip_header_checksum = None
test_frame1.ip_source_ip = 0xc0a80164
test_frame1.ip_dest_ip = 0xc0a80165
test_frame1.udp_source_port = 1
test_frame1.udp_dest_port = 2
test_frame1.udp_length = None
test_frame1.udp_checksum = None
test_frame1.payload = bytearray(range(payload_len))
test_frame1.build()
cnt = 50
sink_pause.next = True
yield clk.posedge
for i in range(cnt):
source.send(test_frame1)
i = 8*cnt
while i > 0:
i = max(0, i-1)
yield clk.posedge
sink_pause.next = False
yield clk.posedge
for i in range(cnt):
yield sink.wait()
rx_frame = sink.recv()
yield delay(100)