Sign in on the Premium hub to save lesson progress. This lesson is open to read so the page is a complete article, not an empty login wall.
Lesson 2.3

Overflow and underflow

Overflow is a write that commits when the FIFO already holds DEPTH unread beats. Underflow is a read that commits when occupancy is 0. Both are functional bugs, not β€œretry later”. A correct FIFO may stall (full/empty); it must never overwrite or pop garbage.

Definitions

Overflow: wr_fire while occupancy is already DEPTH β€” new wdata overwrites an unread location (or the write pointer laps the read pointer). Lost data, often silent.
Underflow: rd_fire while occupancy is 0 β€” rdata is stale, X, or another beat’s leftover. Downstream FSM consumes a fake packet.

1

What actually breaks

OverflowUnderflow
WhenWrite while fullRead while empty
MemoryUnread word destroyedNo valid word at rptr
Pointerswptr advances anyway β†’ occupancy looks legal, data is notrptr advances past wptr β†’ later β€œvalid” reads are wrong
Typical causeProducer ignores full; combo-loop flag late; Gray CDC too optimisticConsumer ignores empty; FWFT vs standard mismatch; empty registered one cycle late without gating
Not the same as backpressure

A blocked write (wr_en=1 and full=1 and wr_fire=0) is healthy. Occupancy stays at DEPTH. Overflow is only if the RAM write and pointer increment still happen.

2

The only legal fires

hard gate
wire wr_fire = wr_en & ~full;   // no RAM write, no wptr++ if full
wire rd_fire = rd_en & ~empty;  // no rptr++, no rdata capture if empty

Upstream must treat full as β€œdo not present a new beat” (or keep wdata stable and wait). Downstream must treat empty as β€œrdata is not a new beat”.

3

Async FIFO β€” conservative flags vs overflow

The 2FF on Gray pointers hides recent opposite-side traffic. That must make flags pessimistic:

  • Write domain may assert full while true occupancy is still < DEPTH (wasted slots, never overflow).
  • Read domain may assert empty while RAM still holds unread writes that have not synced yet (extra latency, never underflow).

If you compared live binary pointers across clocks, you could think the FIFO is not full when it is β€” that is how CDC invents overflow.

A read completed on rd_clk. Until that Gray pointer finishes 2FF on wr_clk, the writer still believes the FIFO is full and blocks wr_fire. That is stall, same as lesson 2.1 β€” never a wrap of the write pointer into live data.

Golden check in any testbench: a software queue of the same DEPTH. Every wr_fire push, every rd_fire pop. If the model queue overflows or underflows, the RTL is wrong. Lesson 5.1’s lab tracks blocked writes/reads; those counters must rise instead of the golden queue breaking.

4

SVA β€” occupancy never illegal

fifo_ovf_unf.sv
// Sync FIFO (one clock). Track occupancy in the TB, not with a DUT count
// if you already use extra-MSB pointers.
int occ;
always @(posedge clk or negedge rst_n) begin
  if (!rst_n) occ <= 0;
  else occ <= occ + wr_fire - rd_fire;
end

assert property (@(posedge clk) disable iff (!rst_n)
  !(wr_fire && (occ == DEPTH)));          // overflow
assert property (@(posedge clk) disable iff (!rst_n)
  !(rd_fire && (occ == 0)));             // underflow
assert property (@(posedge clk) disable iff (!rst_n)
  occ >= 0 && occ <= DEPTH);

// Dual-clock: keep two occupancy views, or a mailbox in the TB.
// Never sample both pointers in one always @(posedge wr_clk) without Gray.
Try a fill-past-full test in the simulator β†’
5

Debug checklist

  1. full never 1, but occupancy hits DEPTH β†’ pointer/flag formula bug (wrong MSB invert on Gray).
  2. empty 0 at reset β†’ pointers not cleared together.
  3. Overflow only at a specific count (7β†’8) β†’ binary CDC or Gray XOR-order on the pointer you synchronize.
  4. Underflow after FWFT conversion β†’ you captured rdata on rd_en instead of on a valid pop.
5

Debug checklist when silicon corrupts a stream

Capture wr_en, full, wr_fire, rd_en, empty, rd_fire, and both pointers. If wr_en is high while full is high and wptr still increments, the gate is broken. If pointers wrap and RAM contents look rotated, you overflowed. If the reader gets old beats after empty pulsed, you underflowed or used FWFT timing in a standard-FIFO TB.

On async FIFOs also dump the 2FF outputs. If full is 0 but converted occupancy from live pointers (illegal in RTL, OK in a well-timed TB model that cheats with a hierarchical force) says DEPTH, you have a CDC compare bug.

6

Protocol overflow vs RTL overflow

AXI-Stream: if the master keeps tvalid high when tready is low, the master is protocol-illegal; the FIFO should not have sampled. If your wrapper samples tdata on tvalid alone, you built overflow into the wrapper even though the FIFO core is correct. Always AND with tready (i.e. !full).

Q

Frequently asked questions

What is FIFO overflow?

A write commit (wr_fire=1) while occupancy is already DEPTH. Unread data is overwritten. It is a bug. Stall (wr_en && full && !wr_fire) is the correct behavior.

What is FIFO underflow?

A read commit while occupancy is 0, returning stale or X data. Stall on empty is correct.

How can an async FIFO overflow without a gating bug?

If you compare live binary pointers across clocks, reconvergence can clear full while the RAM is still full. Gray + conservative 2FF flags exist to prevent that class of overflow.

How do you verify no overflow?

A golden queue of DEPTH, SVA that forbids wr_fire at occ==DEPTH and rd_fire at occ==0, and a blocked-write counter that must rise instead of the golden queue growing past DEPTH.

Key takeaways

  • Overflow = committed write when full. Underflow = committed read when empty.
  • Stall is required. Loss of data is not.
  • Async flags must be conservative; optimistic flags are overflow/underflow waiting to happen.
  • Prove both with a golden queue + SVA, not by staring at one waveform.