Why this is different from a FIFO (and why that matters)
A FIFO’s contract is “never lose data — if the consumer can’t keep up, make the producer wait.” That’s right for a bus interface or a DMA queue. But plenty of real hardware wants the opposite contract: a debug trace buffer that always holds the most recent N cycles of history, a sensor sample window for a moving-average filter, or a “last N events” log for postmortem debugging. In all of these, losing the oldest data when the buffer is full is correct behavior, and blocking the producer would be actively wrong (you’d stall the pipeline just to protect data nobody needs anymore).
That’s the whole design delta from the Synchronous FIFO problem: wr_en is always accepted. When the buffer is already full, a write overwrites the oldest entry and the read pointer silently advances along with it, keeping the window exactly DEPTH entries wide.
Interface
| Signal | Direction | Width | Description |
|---|---|---|---|
clk |
input | 1 | Clock. |
rst |
input | 1 | Synchronous, active-high reset. |
wr_en |
input | 1 | Write wr_data this cycle — always succeeds. |
wr_data |
input | WIDTH |
Data to push. |
rd_en |
input | 1 | Pop the oldest entry this cycle (ignored while empty). |
rd_data |
output | WIDTH |
Current oldest entry (fall-through). |
full |
output | 1 | High when the window already holds DEPTH entries. |
empty |
output | 1 | High when there is nothing to read. |
overwrite |
output | 1 | Pulses high the cycle a write evicts the oldest entry. |
Cycle-by-cycle example (DEPTH = 4)
| Cycle | wr_en |
wr_data |
Buffer contents (oldest → newest) | overwrite |
|---|---|---|---|---|
| 0 | 1 | A |
A |
0 |
| 1 | 1 | B |
A, B |
0 |
| 2 | 1 | C |
A, B, C |
0 |
| 3 | 1 | D |
A, B, C, D (now full) |
0 |
| 4 | 1 | E |
B, C, D, E (A dropped) |
1 |
| 5 | 1 | F |
C, D, E, F (B dropped) |
1 |
Correctness constraints
wr_enmust never be blocked or gated byfull— a write always writes.- When a write happens while
full, the oldest entry is dropped andoverwritepulses; the buffer’s occupancy stays at exactlyDEPTH. - A read while
emptymust have no effect (no pointer movement, no false data). fullandemptymust never be asserted simultaneously.- If a consumer read and an evicting write land in the same cycle, the read must observe the value being evicted (it was still the oldest live entry going into that cycle), and the read pointer must advance by exactly one — not two — for that cycle.