7.2.6 The read that was valid on an empty FIFO

EasyFixVerilator · local serverReview the topic

Fix a FIFO that reports read_valid for a read it never accepted.

Hardware context: Parameterized synchronous FIFO RTL. Repair FIFO RTL that reports read_valid for a read refused at empty.

This is a Fix problem: the FIFO in the editor has one realistic defect. Find it and fix it.

The scoreboard of the FIFO testbench fails a test that drains the FIFO one read too far, as the test plan asks. After the last real entry has come out, the FIFO raises read_valid once more, and the scoreboard sees a response that nobody asked for. The data, the flags and the pointers all look right.

Every other test passes, which tells you the defect only shows when a read is requested but not accepted.

Signals in the timeline

SignalWidthRole
depth8input of each check
scenario8input of each check
Full specification
The module (keep these ports)
module fifo #(parameter int DEPTH = 4, parameter int WIDTH = 8) (
  input  bit             clk, reset, write_en, read_en,
  input  bit [WIDTH-1:0] write_data,
  output bit             full, empty, read_valid,
  output bit [WIDTH-1:0] read_data
);
  // your design
endmodule
RuleAt each rising edge
Resetreset = 1 empties the FIFO and clears read_valid and read_data. It wins over both enables.
WriteAccepted when write_en = 1 and the FIFO was not full before the edge.
ReadAccepted when read_en = 1 and the FIFO was not empty before the edge. read_data holds the oldest entry after the edge, with read_valid = 1; after an edge with no accepted read, read_valid = 0.
Flagsfull = occupancy is DEPTH, empty = occupancy is 0, both from the current occupancy.

Your module is built at depths 1, 3 and 4 on the same pins. Each scenario starts with a reset edge (edge 1); 1 ns after every edge the selected FIFO’s full, empty, read_valid and read_data (when valid, and 0 after a reset edge) are compared with a queue model.

Examples

Example 1

Drain past empty at depth 1
Input
DEPTH = 1; edges after the reset edge: WWRR-
(W write, R read, B both, X reset with both enables, - idle)
Output
mismatched_edges = 0, accepted_reads = 1, final_occupancy = 0

The last read comes at empty and is refused, so read_valid must be 0 after it.

Example 2

Drain past empty at depth 3
Input
DEPTH = 3; edges after the reset edge: WWWWRRRR-
(W write, R read, B both, X reset with both enables, - idle)
Output
mismatched_edges = 0, accepted_reads = 3, final_occupancy = 0

Three reads are accepted; the fourth is not, and read_valid must drop.

Example 3

Both enables at empty
Input
DEPTH = 3; edges after the reset edge: BWBBWWWBBRRRRB-
(W write, R read, B both, X reset with both enables, - idle)
Output
mismatched_edges = 0, accepted_reads = 6, final_occupancy = 1

At empty the read is refused even though a write arrives on the same edge.

Constraints and contract

  • DEPTH is 1 or more and need not be a power of two; WIDTH is 8 in the tests.
  • All inputs are known bits and change only between edges.
  • Keep the module name, parameters and ports.

Hints

Hint 1

Run the first example. At which edge does read_valid first disagree with the model, and what is the occupancy just before it?

Hint 2

read_valid should describe what happened at the edge, not what was asked for. Which signal in the design says a read really happened?

Hint 3

Compare how read_data and read_valid are updated: one of them is guarded by the accept decision and one is not.

What this judge verifies: Your FIFO runs at depths 1, 3 and 4 through fills, drains past empty, simultaneous operations, wraps, resets and seeded random traffic, checked edge by edge against a queue model. Passing shows the defect is gone on that traffic.

21 checks on Verilator · local server. Run the examples first, then check the full regression. The suite is local and inspectable. A passing result establishes the tested cases, not a proof for all possible inputs.

See all 6 steps and the final capstone
Your code SystemVerilogSaved locally
Ln 1, Col 1Tab indents · Ctrl/⌘ + Enter runs samples
Not run yet

Drain past empty at depth 1

Input
DEPTH = 1; edges after the reset edge: WWRR-
(W write, R read, B both, X reset with both enables, - idle)
Expected output
mismatched_edges = 0, accepted_reads = 1, final_occupancy = 0

The last read comes at empty and is refused, so read_valid must be 0 after it.

Run samples executes all 3 example checks. Check all tests runs 21 checks.

Verilator · local server