Hardware interview practice
How to Build a Synchronous FIFO in SystemVerilog
Design a parameterized synchronous FIFO, then write a small bus-functional model that can reset it, push one item, and pop one item without testbench races.
Example
cycle 0: rst_n=0
cycle 1: rst_n=1, push=1, data=0x2A, pop=0
cycle 2: push=1, data=0x7C, pop=1
cycle 3: push=0, pop=1cycle 0: empty=1, count=0
cycle 1: accept 0x2A, count=1
cycle 2: pop 0x2A and accept 0x7C, count remains 1
cycle 3: pop 0x7C, empty=1, count=0Explanation: The simultaneous push/pop cycle preserves occupancy, and the race-free BFM samples accepted transfers only on the active clock edge.
Reasoning requirements
- Expose full and empty, reject reads while empty, and reject writes while full unless a same-cycle read creates space.
- Define registered read-data semantics and handle simultaneous accepted reads and writes without corrupting occupancy.
- Support non-power-of-two depths with explicit pointer wrap logic.
- Use an interface clocking block so the BFM drives and samples on deterministic scheduler regions.
Concise answer
The answer and the key reason
Deeper explanation
Work through the implementation and tradeoffs
Define read_accept as rd_en while nonempty. Define write_accept as wr_en when capacity exists, or when a simultaneous accepted read frees one entry. Only these accepted events move pointers. The count increments for write-only, decrements for read-only, and remains constant when both occur, which prevents occupancy drift under concurrent traffic.
Non-power-of-two depths require an explicit comparison against the final legal index before returning a pointer to zero. Because read data is registered, the BFM should issue and hold a request through an acceptance edge, then wait until the register has updated before sampling. Clocking-block skews keep those actions out of DUT scheduling races.
What to remember
- Count accepted operations
- Wrap non-power-of-two pointers
- Sample registered reads later
Practice the complete prompt
