Day 65: Building block: the synchronous FIFO with full/empty/almost flags
Building block: the synchronous FIFO
A FIFO (first-in-first-out) buffers data between a producer and consumer. A synchronous FIFO has both sides on the *same* clock. The core is a small memory with a write pointer and read pointer; the tricky part is generating full and empty flags correctly — the classic ambiguity is that write pointer == read pointer means *both* full and empty, resolved with an extra pointer bit or a count.
module sync_fifo #(parameter W=8, DEPTH=16, AW=4) (
input clk, rst_n,
input wr_en, rd_en,
input [W-1:0] wdata,
output reg [W-1:0] rdata,
output full, empty
);
reg [W-1:0] mem [0:DEPTH-1];
reg [AW-1:0] wptr, rptr;
reg [AW:0] count; // 0..DEPTH
assign full = (count == DEPTH);
assign empty = (count == 0);
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin wptr<=0; rptr<=0; count<=0; end
else begin
if (wr_en && !full) begin mem[wptr]<=wdata; wptr<=wptr+1'b1; end
if (rd_en && !empty) begin rdata<=mem[rptr]; rptr<=rptr+1'b1; end
case ({wr_en && !full, rd_en && !empty})
2'b10: count <= count + 1'b1;
2'b01: count <= count - 1'b1;
default: count <= count; // both or neither -> unchanged
endcase
end
end
endmoduleAlmost-full / almost-empty
Real FIFOs also expose almost-full/almost-empty (programmable-threshold) flags so a producer can throttle *before* overflow and a consumer can prefetch. They fall out of the same count. Add them — they're trivial here and expected in interviews and real peripherals (your UART/SPI will use FIFOs).
Ship for Day 65
Why is generating correct full/empty flags the tricky part of a FIFO?