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
| Signal | Width | Role |
|---|---|---|
depth | 8 | input of each check |
scenario | 8 | input of each check |
Full specification
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
| Rule | At each rising edge |
|---|---|
| Reset | reset = 1 empties the FIFO and clears read_valid and read_data. It wins over both enables. |
| Write | Accepted when write_en = 1 and the FIFO was not full before the edge. |
| Read | Accepted 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. |
| Flags | full = 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