Complex Packet Decoder

Building packet decoders isn’t fun

You recently got a new task to code a packet decoder module for a network processing unit. The packet decoder is responsible for taking in a stream of packets and classifying them into two categories: big packets and small packets. Looking at the task, you think how hard a packet decoder can be and decide to hand-over this task to your intern, who graciously accepts it. You give the intern the specifications for the packet decoder and they start working on it.

After a few hours and spending $$ amount on burning tokens, the intern comes back to you with the code for the packet decoder. The code seems to be functionally correct, you review the testbench and the code and it seems to be doing what it is supposed to do. Thinking that the task is complete, you review the code and let it be merged on a FRIDAY evening.

You come back to office on Monday morning and see that you have been getting bombarded with comments about how the timing degradation caused by the packet decoder module. You are not sure what to do, but you know that you need to fix the timing issues as soon as possible.

Given the specifications for the packet decoder, identify the potential timing issues in the code and suggest possible solutions to fix them.

All the flops should be positive edge triggered with active high resets (if any).

Interface Definition

pkts_i       => A 512-bit organised as 32x16b input vector representing the incoming packets
pkt_valid_i  => A 32b valid signal indicating which 16 packets are valid
big_pkt_o    => The output vector indicating big packets
small_pkt_o  => The output vector indicating small packets

A packet is represented by a 2-bit pattern in the input vector. The first 2 bits of each packet indicate the type of the packet. The remaining bits of the packet is the payload of the packet. The packet decoder needs to classify the packets based on the first 2 bits and output the corresponding big and small packet vectors.

Small Packet

A small packet is only 16 bits long and is represented by the following 2-bit patterns in the LSB of the packet:

  • 00: Small Packet Type 0
  • 01: Small Packet Type 1
  • 10: Small Packet Type 2

Big Packet

A big packet is 32 bits long and is represented by the following 2-bit patterns in the LSB of the packet:

  • 11: Big Packet

Packet Valid

The 32b packet valid input can atmost have 16b set and hence the input stream of 512b vector can thus contain atmost 16 valid packets.

Interface Requirements

  • The 16 bits output vectors for big packets and small packets should indicate which packets in the input vector are big and small respectively.
  • The output should be valid in the same cycle as the input packet vector is asserted.
  • The packets can be in any order in the input vector with the only constraint that out of reset the first 16b in the input vector corresponds to the first valid packet
  • It is possible to have the last 16b in the input vector to mark the beginning of a big packet, such that the big packet straddles across the boundary of the input vector. In such a case, the upper 16b of the input vector would be the first 16b of the incoming vector in the next cycle.
  • The packet valid can only have 16b set with all the set bits being next to each other. This implies that there will never be a 0 between two or more set bits in the packet valid vector however the set bits can start at any position in the vector. If the start position is such that the 16b can't fit in the vector then the bits will rollover.
  • The first and the last packet can then be figured out using the packet valid input vector. Given the above requirement, the first packet would be at the index where the previous index is a 0 and the last packet would be at the index where the next index is a 0

Example

Given the following input vector of packets (pkts_i assuming 128 bits for simplicity and 8 bits of pkt_valid with atmost 4 packets being valid in a cycle):

[7:0] pkt_valid_i = {1, 1, 0, 0, 0, 0, 1, 1}
[7:0][15:0] pkts_i = {16'hFEE3, 16'hBEEF, 16'h1234, 16'hDEAD, 16'hC0DF, 16'h5678, 16'h9ABF, 16'hDEF0}

The output vectors for big packets and small packets would be:
big_pkt_o[7:0]   = 4'b1001
small_pkt_o[7:0] = 4'b0100

Explanation:

Given the input pkt_valid_i = 8'b1100_0011, the first packet in the stream would be at index 6 and the last packet would be at index 1 Hence, the aligned input packet stream now becomes:

pkt_valid_i = {0, 0, 0, 0, 1, 1, 1, 1}
pkts_i = {16'h1234, 16'hDEAD, 16'hC0DF, 16'h5678, 16'h9ABF, 16'hDEF0, 16'hFEE3, 16'hBEEF}
  • The first packet (16'hBEEF) is a big packet (11 in the LSB[1:0]) and is valid, so big_pkt_o[0] is set to 1.
  • The second packet (16'hFEE3) is the upper part of a big packet and hence is not a valid packet, so both big_pkt_o[1] and small_pkt_o[1] are set to 0.
  • The third packet (16'hDEF0) is a small packet (00 in the LSB[1:0]) and is valid, so small_pkt_o[2] is set to 1.
  • The fourth packet (16'h9ABF) is a big packet (11 in the LSB[1:0]) and is valid, so big_pkt_o[3] is set to 1.
NOTE: The above example is also the case where the upper 16b of the big packet straddles across two cycles. Hence the first packet valid in the next cycle would then represent the upper 16b of the big packet.

Loading the interactive workspace...