PacketFIFO is a small first-in first-out buffer with a valid/ready port on each side. A producer pushes bytes in, a consumer pulls them out in the same order, and nothing may be lost, duplicated or reordered.
The bytes form packets. Each packet is a header byte, then len payload bytes, then a check byte. A ninth bit, last, marks the check byte so the consumer knows where the packet ends.
Field | Bits | Meaning |
|---|---|---|
kind | header[7:6] | 0 DATA, 1 CTRL, 2 ERR |
dest | header[5:4] | Destination port, 0 to 3 |
len | header[3:0] | Payload bytes that follow: 1 to 8 are legal; 0 and 9 to 15 are illegal |
check byte | 8 bits | XOR of the header and every payload byte |
The handshake rule is the one from RTL: a byte moves on a clock edge where valid and ready are both 1. While ready is 0 the producer must hold the same byte.
The first version needs nothing beyond Verilog. The bench sends one packet, fills the 4-entry FIFO exactly, reads the bytes back and checks order, the last bit and the check byte.
// PacketFIFO v0: a valid/ready FIFO of 9-bit entries {last, byte}.
module packet_fifo #(
parameter WIDTH = 9,
parameter DEPTH = 4,
parameter AW = 2
) (
input wire clk,
input wire rst_n,
input wire in_valid,
output wire in_ready,
input wire [WIDTH-1:0] in_data,
output wire out_valid,
input wire out_ready,
output wire [WIDTH-1:0] out_data
);
reg [WIDTH-1:0] mem [0:DEPTH-1];
reg [AW-1:0] wptr, rptr;
reg [AW:0] count;
wire push = in_valid && in_ready;
wire pop = out_valid && out_ready;
assign in_ready = (count != DEPTH);
assign out_valid = (count != 0);
assign out_data = mem[rptr];
always @(posedge clk or negedge rst_n)
if (!rst_n) begin
wptr <= 0;
rptr <= 0;
count <= 0;
end else begin
if (push) begin
mem[wptr] <= in_data;
wptr <= wptr + 1'b1;
end
if (pop) rptr <= rptr + 1'b1;
case ({push, pop})
2'b10: count <= count + 1'b1;
2'b01: count <= count - 1'b1;
default: count <= count;
endcase
end
endmodule
PASS: packet 22 a5 3c bb came out in order, last on the check byte, XOR 008'hBB. 22 XOR A5 is 87, and 87 XOR 3C is BB. XOR-ing all four bytes then gives 00, which is what the bench checks at the end.
PASS: packet 22 a5 3c bb came out in order, last on the check byte, XOR 00Version 0 checks one packet, written by hand. The rest of the course replaces each hand-written piece with something that scales.
Part | What you add to PacketFIFO |
|---|---|
1 · Foundations | A packed struct and enum for the header, a queue as the expected-byte model, an interface for the two ports |
2 · OOP | A |
3 · Randomization | Random legal packets, then deliberately illegal ones (length 0, bad check byte) |
4 · Assertions | Valid/ready rules on both ports, no overflow, no underflow |
5 · Coverage | Bins for kind, dest and length, including the illegal lengths |
6 · Advanced | A bind file that attaches the checkers without editing the RTL |
7 · Testbench | A complete class testbench with monitors and a scoreboard |
8 · Senior | The capstone: plan, regression and sign-off, with a planted bug to find |
Each step is a short Project step box at the end of the lesson it belongs to. The steps are optional: every lesson makes sense without them.
last bit next to each byte instead of storing whole packets?It keeps the RTL a plain byte FIFO that knows nothing about packets. The packet format lives in the testbench, so the same FIFO can be checked with any format, and it grows into the course capstone unchanged.
out_ready for three cycles in the middle of a packet. What must the FIFO do?Keep out_valid high and keep the same byte on out_data until a cycle where out_ready is 1. Nothing is lost, and the byte is not repeated.
Run with Icarus Verilog 13 (iverilog -g2012) and Verilator 5.048 (--binary --timing) on 2026-10-18; both printed the PASS line shown, with no warnings.
Version 0 checks one hand-written packet that exactly fills a depth-4 FIFO. It does not test back-pressure, simultaneous push and pop, reset in the middle of traffic or illegal packets; later parts add those.