← Formal Verification Interviews
Interview Question · Code Reading
End-to-End vs Glue Properties
The Question
The interviewer asks
Your FIFO bench proves the four properties below. Is the FIFO verified? What would you write first?
always @(posedge clk) if (!rst) begin
if (empty) assert(wr_ptr == rd_ptr);
if (full) assert(wr_ptr == rd_ptr);
assert(count <= 3'd4);
assert(count == f_ptr_diff || count == 3'd4 && f_ptr_diff == 2'd0);
endYou have two and a half minutes. Speak or type, then press Get Feedback under your answer. No credits needed.
Speech-to-text runs in your browser. Chrome and Edge may send audio to their own speech service.
You can edit your answer before you send it. Feedback uses AI and checks your answer only against our written answer. No credits are used and your answer is not saved.
Reveal the Model Answer
Not yet: those are glue properties about the mechanism, and they miss bugs that leave the pointers and count intact. Write an end-to-end check at the ports first: every accepted word leaves once, in order and unchanged. In the lesson, this glue set passes depth 10 on a FIFO that reads the wrong slot. The end-to-end check fails at step 3. Keep the glue to localise failures and feed induction.
Read the diagram as text
- push, wdata: port
- wr_ptr, rd_ptr, count: glue: pointers equal when empty or full, count <= 4, count matches the pointers
- mem[0:3]: four bytes
- pop, rdata: port
- Scoreboard sb0..sb3: modelling code in the bench: every accepted byte, oldest first
- End-to-end check: pop && !empty |-> rdata == sb0
- push, wdata → wr_ptr, rd_ptr, count
- push, wdata → mem[0:3]: write
- mem[0:3] → pop, rdata: read
- push, wdata → Scoreboard sb0..sb3: record
- Scoreboard sb0..sb3 → End-to-end check: head
- pop, rdata → End-to-end check
Glue attaches inside the box. End-to-end attaches across it: what goes in comes out, in order, once, unchanged.
Measured at depth 10 with smtbmc yices. The solver needed no second push: the slot after the head was never written, so its power-up value (80) is the solver's choice.
Self-Check
Tick each key point you covered in your answer.
What Interviewers Listen For (Mid-Level)
A good mid-level answer starts with end-to-end checks and explains what glue properties miss. It uses glue properties to find where a bug is and to help induction.
What Interviewers Listen For (Senior)
A good senior answer weighs the modelling cost and the depth that data tracking needs. It prefers a symbolic tracker to a reference queue, and sizes the bound against the longest transaction.
Cross-Questions
Where the interviewer goes next. Open a node to practise that follow-up.
WhyWhat IfProve ItBreak ItCompareScale It
- End-to-End vs Glue Properties
-
WhyWhy Glue Properties Miss Data BugsL2 Mid-Level
Why can every pointer and count property hold while the FIFO returns the wrong data?
2:30You have two and a half minutes. Speak or type, then press Get Feedback under your answer. No credits needed.
Speech-to-text runs in your browser. Chrome and Edge may send audio to their own speech service.
You can edit your answer before you send it. Feedback uses AI and checks your answer only against our written answer. No credits are used and your answer is not saved.
Reveal the Model Answer
Glue properties check the mechanism: pointers stay consistent with the count and full and empty agree with them. A bug in what is read, such as the wrong slot or stale data, leaves all of that intact. Only a property that compares what leaves with what entered, the purpose of the block, can see it.
Self-Check
Tick each key point you covered in your answer.
-
Prove ItSolve F3.4.1: Prove the Data, Not Just the PointersSimulator-graded
Show it, don't just say it: this Silicon Lab problem is graded by the simulator.
-
CompareThe Formal Scoreboard with One Symbolic WordL2 Mid-LevelL3 Senior
Write a formal check of data integrity and order through a FIFO, without modelling the whole queue.
2:30You have two and a half minutes. Speak or type, then press Get Feedback under your answer. No credits needed.
Speech-to-text runs in your browser. Chrome and Edge may send audio to their own speech service.
You can edit your answer before you send it. Feedback uses AI and checks your answer only against our written answer. No credits are used and your answer is not saved.
Reveal the Model Answer
Track one word whose value comes from
$anyconst, so the solver picks it once, and let the cycle it enters be free. Count the words ahead of it, then assert that it is never dropped and that it leaves first in line, unchanged. The value and entry time are arbitrary. So a proof for this one word covers every word, as long as the design never inspects the data.One Symbolic Packet Through the FIFOThe solver picks a value and an entry time. The tracker follows that one packet from its write to its read. Read the diagram as text
- f_val = $anyconst: one value, fixed for the run, any value
- f_pick = $anyseq: free each cycle: which write to follow
- enters: records its slot and the words ahead
- waits: each read lowers the words ahead
- at the head: assert: rdata == f_val
- while inside: assert: the FIFO is not empty
- f_val = $anyconst → enters: wdata == f_val
- f_pick = $anyseq → enters
- enters → waits
- waits → at the head: none ahead
- waits → while inside
A drop, duplicate, reorder or corruption breaks one of the two assertions for some choice of packet.
module sfifo(input clk, rst, wr, rd, input [7:0] wdata, output [7:0] rdata, output empty, full); reg [7:0] mem [0:7]; reg [3:0] wr_ptr = 0, rd_ptr = 0; wire [3:0] used = wr_ptr - rd_ptr; assign empty = used == 0; assign full = used == 8; assign rdata = mem[rd_ptr[2:0]]; wire do_wr = wr && !full, do_rd = rd && !empty; always @(posedge clk) if (rst) begin wr_ptr <= 0; rd_ptr <= 0; end else begin if (do_wr) begin mem[wr_ptr[2:0]] <= wdata; wr_ptr <= wr_ptr + 1'b1; end if (do_rd) rd_ptr <= rd_ptr + 1'b1; end always @(posedge clk) begin if ($initstate) begin assume(rst); end else begin assume(!rst); end end // one symbolic packet: any value, entering at any accepted write the solver picks wire [7:0] f_val = $anyconst; wire f_pick = $anyseq; reg f_in = 0, f_done = 0; reg [3:0] f_addr = 0; // the slot it was written to reg [3:0] f_ahead = 0; // words in front of it wire f_enter = !f_in && !f_done && wr && !full && f_pick && wdata == f_val; always @(posedge clk) if (rst) begin f_in <= 0; f_done <= 0; end else if (f_enter) begin f_in <= 1; f_addr <= wr_ptr; f_ahead <= used - do_rd; end else if (f_in && do_rd) begin if (f_ahead == 0) begin f_in <= 0; f_done <= 1; end else f_ahead <= f_ahead - 1'b1; end always @(posedge clk) if (!rst && f_in) begin assert(!empty); // never dropped if (f_ahead == 0) assert(rdata == f_val); // leaves first in line, unchanged end // helpers: where the tracked word sits, and that it is still there wire [3:0] f_dist = f_addr - rd_ptr; always @(posedge clk) if (!rst) begin assert(used <= 8); if (f_in) begin assert(f_dist == f_ahead); assert(f_ahead < used); assert(mem[f_addr[2:0]] == f_val); end end endmodulePrints:
prove, depth 2 (smtbmc yices): PASSSelf-Check
Tick each key point you covered in your answer.
What Interviewers Listen For (Mid-Level)
A good mid-level answer describes the
$anyconstvalue, the free entry time and the two assertions.What Interviewers Listen For (Senior)
A good senior answer states the data-independence condition, and adds helper invariants that tie the tracked slot to memory. It narrows the data width only when no control logic reads the data.
Misconception
"One tracked value proves the FIFO only for that value."
The solver may choose any value and any entry time. If some word could be lost or reordered, the solver would pick that word, so the proof covers them all.
Source Seligman, Schubert and Kiran Kumar, Formal Verification (2nd ed.) ch. 10 "Exploiting symmetry with rigid free variables"
Missed Corner
"Shrink the data to one bit; a FIFO does not care about data."
Only true when no logic reads the data. This FIFO drops a second
8'hFFin a row. The tracker finds it with the value 255, which a one-bit path could never carry.Run It
A FIFO with fill-word compression: a repeated
8'hFFis not stored.module sfifo(input clk, rst, wr, rd, input [7:0] wdata, output [7:0] rdata, output empty, full); reg [7:0] mem [0:7]; reg [3:0] wr_ptr = 0, rd_ptr = 0; wire [3:0] used = wr_ptr - rd_ptr; assign empty = used == 0; assign full = used == 8; assign rdata = mem[rd_ptr[2:0]]; reg last_ff = 0; // fill-word compression: a second 8'hFF in a row is not stored always @(posedge clk) if (rst) last_ff <= 0; else if (wr && !full) last_ff <= wdata == 8'hFF; wire do_wr = wr && !full && !(wdata == 8'hFF && last_ff), do_rd = rd && !empty; always @(posedge clk) if (rst) begin wr_ptr <= 0; rd_ptr <= 0; end else begin if (do_wr) begin mem[wr_ptr[2:0]] <= wdata; wr_ptr <= wr_ptr + 1'b1; end if (do_rd) rd_ptr <= rd_ptr + 1'b1; end always @(posedge clk) begin if ($initstate) begin assume(rst); end else begin assume(!rst); end end // one symbolic packet: any value, entering at any accepted write the solver picks wire [7:0] f_val = $anyconst; wire f_pick = $anyseq; reg f_in = 0, f_done = 0; reg [3:0] f_addr = 0; // the slot it was written to reg [3:0] f_ahead = 0; // words in front of it wire f_enter = !f_in && !f_done && wr && !full && f_pick && wdata == f_val; always @(posedge clk) if (rst) begin f_in <= 0; f_done <= 0; end else if (f_enter) begin f_in <= 1; f_addr <= wr_ptr; f_ahead <= used - do_rd; end else if (f_in && do_rd) begin if (f_ahead == 0) begin f_in <= 0; f_done <= 1; end else f_ahead <= f_ahead - 1'b1; end always @(posedge clk) if (!rst && f_in) begin assert(!empty); // never dropped if (f_ahead == 0) assert(rdata == f_val); // leaves first in line, unchanged end // helpers: where the tracked word sits, and that it is still there wire [3:0] f_dist = f_addr - rd_ptr; always @(posedge clk) if (!rst) begin assert(used <= 8); if (f_in) begin assert(f_dist == f_ahead); assert(f_ahead < used); assert(mem[f_addr[2:0]] == f_val); end end endmodulePrints:
bmc, depth 12 (smtbmc yices): FAIL at step 4See the Fix
module sfifo(input clk, rst, wr, rd, input [7:0] wdata, output [7:0] rdata, output empty, full); reg [7:0] mem [0:7]; reg [3:0] wr_ptr = 0, rd_ptr = 0; wire [3:0] used = wr_ptr - rd_ptr; assign empty = used == 0; assign full = used == 8; assign rdata = mem[rd_ptr[2:0]]; wire do_wr = wr && !full, do_rd = rd && !empty; always @(posedge clk) if (rst) begin wr_ptr <= 0; rd_ptr <= 0; end else begin if (do_wr) begin mem[wr_ptr[2:0]] <= wdata; wr_ptr <= wr_ptr + 1'b1; end if (do_rd) rd_ptr <= rd_ptr + 1'b1; end always @(posedge clk) begin if ($initstate) begin assume(rst); end else begin assume(!rst); end end // one symbolic packet: any value, entering at any accepted write the solver picks wire [7:0] f_val = $anyconst; wire f_pick = $anyseq; reg f_in = 0, f_done = 0; reg [3:0] f_addr = 0; // the slot it was written to reg [3:0] f_ahead = 0; // words in front of it wire f_enter = !f_in && !f_done && wr && !full && f_pick && wdata == f_val; always @(posedge clk) if (rst) begin f_in <= 0; f_done <= 0; end else if (f_enter) begin f_in <= 1; f_addr <= wr_ptr; f_ahead <= used - do_rd; end else if (f_in && do_rd) begin if (f_ahead == 0) begin f_in <= 0; f_done <= 1; end else f_ahead <= f_ahead - 1'b1; end always @(posedge clk) if (!rst && f_in) begin assert(!empty); // never dropped if (f_ahead == 0) assert(rdata == f_val); // leaves first in line, unchanged end // helpers: where the tracked word sits, and that it is still there wire [3:0] f_dist = f_addr - rd_ptr; always @(posedge clk) if (!rst) begin assert(used <= 8); if (f_in) begin assert(f_dist == f_ahead); assert(f_ahead < used); assert(mem[f_addr[2:0]] == f_val); end end endmodulePrints:
prove, depth 2 (smtbmc yices): PASS -
WhyWhy the Tracker Proof Needs HelpersL2 Mid-LevelL3 Senior
Why does the symbolic tracker proof need helper assertions before k-induction (an induction proof over k steps) can close it?
2:30You have two and a half minutes. Speak or type, then press Get Feedback under your answer. No credits needed.
Speech-to-text runs in your browser. Chrome and Edge may send audio to their own speech service.
You can edit your answer before you send it. Feedback uses AI and checks your answer only against our written answer. No credits are used and your answer is not saved.
Reveal the Model Answer
Without helpers the induction step may start with the tracker claiming a word is stored in a slot that holds something else. Helpers tie the tracker to the design. Its distance from the read pointer equals the count of words ahead. Memory at the tracked slot still holds the value. Without them the proof stays UNKNOWN even at k = 8; with them it closes at k = 2.
module sfifo(input clk, rst, wr, rd, input [7:0] wdata, output [7:0] rdata, output empty, full); reg [7:0] mem [0:7]; reg [3:0] wr_ptr = 0, rd_ptr = 0; wire [3:0] used = wr_ptr - rd_ptr; assign empty = used == 0; assign full = used == 8; assign rdata = mem[rd_ptr[2:0]]; wire do_wr = wr && !full, do_rd = rd && !empty; always @(posedge clk) if (rst) begin wr_ptr <= 0; rd_ptr <= 0; end else begin if (do_wr) begin mem[wr_ptr[2:0]] <= wdata; wr_ptr <= wr_ptr + 1'b1; end if (do_rd) rd_ptr <= rd_ptr + 1'b1; end always @(posedge clk) begin if ($initstate) begin assume(rst); end else begin assume(!rst); end end // one symbolic packet: any value, entering at any accepted write the solver picks wire [7:0] f_val = $anyconst; wire f_pick = $anyseq; reg f_in = 0, f_done = 0; reg [3:0] f_addr = 0; // the slot it was written to reg [3:0] f_ahead = 0; // words in front of it wire f_enter = !f_in && !f_done && wr && !full && f_pick && wdata == f_val; always @(posedge clk) if (rst) begin f_in <= 0; f_done <= 0; end else if (f_enter) begin f_in <= 1; f_addr <= wr_ptr; f_ahead <= used - do_rd; end else if (f_in && do_rd) begin if (f_ahead == 0) begin f_in <= 0; f_done <= 1; end else f_ahead <= f_ahead - 1'b1; end always @(posedge clk) if (!rst && f_in) begin assert(!empty); // never dropped if (f_ahead == 0) assert(rdata == f_val); // leaves first in line, unchanged end endmodulePrints:
prove, depth 8 (smtbmc yices): UNKNOWNSelf-Check
Tick each key point you covered in your answer.
-
Prove ItSolve F4.4.1: Implement the Symbolic Packet TrackerSimulator-graded
Show it, don't just say it: this Silicon Lab problem is graded by the simulator.
-
What IfTwo Tracked Words for OrderingL2 Mid-Level
What if you also want to check that two different words never swap order?
2:30You have two and a half minutes. Speak or type, then press Get Feedback under your answer. No credits needed.
Speech-to-text runs in your browser. Chrome and Edge may send audio to their own speech service.
You can edit your answer before you send it. Feedback uses AI and checks your answer only against our written answer. No credits are used and your answer is not saved.
Reveal the Model Answer
Track two writes chosen freely by the solver, the first accepted before the second. Assert that the first leaves before the second and both arrive unchanged. Because both choices are free, the check covers every pair of words, which catches reordering that a single tracker cannot see.
Two Tracked Writes Stand for Every PairThe solver picks any accepted write as the first; the next accepted write is the second. The bench copies their data and counts the entries in front of the first. Read the diagram as text
- write side: write_en, write_data
- first: f_d1: any accepted write, chosen by the free input f_pick; f_ahead entries in front
- second: f_d2: the very next accepted write
- read side: read_valid, read_data one cycle after an accepted read
- checks: f_d1 leaves when f_ahead reaches 0; f_d2 is the very next read
- write side → first: f_d1
- write side → second: f_d2
- first: f_d1 → read side: f_ahead reads later
- second: f_d2 → checks
- read side → checks
The solver may pick any accepted write as the first, so the proof covers every pair of entries in a row.
Self-Check
Tick each key point you covered in your answer.
-
Prove ItSolve F8.1.1: Sign off a Four-Entry FIFOSimulator-graded
Show it, don't just say it: this Silicon Lab problem is graded by the simulator.
-
WhyWhy Two Free Words Cover Every PairL2 Mid-LevelL3 Senior
Why does choosing both words freely cover every pair, when the proof only ever tracks two?
2:30You have two and a half minutes. Speak or type, then press Get Feedback under your answer. No credits needed.
Speech-to-text runs in your browser. Chrome and Edge may send audio to their own speech service.
You can edit your answer before you send it. Feedback uses AI and checks your answer only against our written answer. No credits are used and your answer is not saved.
Reveal the Model Answer
The solver may pick any two writes, at any times, as the tracked pair. A proof must hold for every choice it could make. So if any pair could swap, some choice of the two tracked words shows it. Two trackers then stand for all pairs, with only two sets of state.
Self-Check
Tick each key point you covered in your answer.
Misconception
"Tracking two words only checks those two words."
The words are free variables, so the proof covers every value the solver could choose. One run checks all pairs.
Source Seligman, Schubert and Kiran Kumar, Formal Verification (2nd ed.) ch. 10 "Exploiting symmetry with rigid free variables"
-
CompareSymbolic Tracker vs Reference QueueL2 Mid-LevelL3 Senior
Asked if they propose a reference queue
Compare a symbolic tracker with a reference-model queue as the scoreboard of a formal bench.
2:30You have two and a half minutes. Speak or type, then press Get Feedback under your answer. No credits needed.
Speech-to-text runs in your browser. Chrome and Edge may send audio to their own speech service.
You can edit your answer before you send it. Feedback uses AI and checks your answer only against our written answer. No credits are used and your answer is not saved.
Reveal the Model Answer
A reference queue copies every word, so its state grows with depth times width. The solver must reason about all of it. The tracker holds one value, one slot and one count, yet covers every word through the free choice. The queue is closer to a simulation scoreboard and easier to read, so people pick it first. The tracker is what converges.
Self-Check
Tick each key point you covered in your answer.
-
Scale ItScaling the Tracker to a Packet RouterL3 Senior
How does the same idea scale to a two-by-two packet router with several flows?
2:30You have two and a half minutes. Speak or type, then press Get Feedback under your answer. No credits needed.
Speech-to-text runs in your browser. Chrome and Edge may send audio to their own speech service.
You can edit your answer before you send it. Feedback uses AI and checks your answer only against our written answer. No credits are used and your answer is not saved.
Reveal the Model Answer
Track one symbolic packet, tagged at the moment of acceptance. Count how many packets of its flow are owed ahead of it at its output. Assert that it leaves at its turn, on the right port, unchanged. Also assert that no output delivers a packet it does not owe. Fairness is a separate question: a fixed-priority arbiter passes every one of these safety checks.
rtr22 and the Tracked PacketTwo inputs, a grant per output, a two-entry queue per output, two outputs. The tracked packet is drawn as it moves: accepted, queued, at the head, gone. Read the diagram as text
- input 0: tvalid, tdata, tdest
- input 1: tvalid, tdata, tdest
- grant per output: round-robin when both want it
- queue 0: two entries {tid, tdata}
- queue 1: two entries {tid, tdata}
- output 0: tvalid, tdata, tid
- output 1: tvalid, tdata, tid
- input 0 → grant per output: 1 accepted
- input 1 → grant per output
- grant per output → queue 0: 2 queued
- grant per output → queue 1
- queue 0 → output 0: 3 its turn, 4 gone
- queue 1 → output 1
A two-by-two router with its tracked packet.
tidnames the input a packet came from.Self-Check
Tick each key point you covered in your answer.
-
Prove ItSolve F9.2.1: Prove Packet Routing End to EndSimulator-graded
Show it, don't just say it: this Silicon Lab problem is graded by the simulator.
-
Break ItWhat a Bounded End-to-End Check MissesL2 Mid-LevelL3 Senior
Your end-to-end FIFO check passes at depth 10. What bug could still be there?
2:30You have two and a half minutes. Speak or type, then press Get Feedback under your answer. No credits needed.
Speech-to-text runs in your browser. Chrome and Edge may send audio to their own speech service.
You can edit your answer before you send it. Feedback uses AI and checks your answer only against our written answer. No credits are used and your answer is not saved.
Reveal the Model Answer
Anything that needs more than ten steps to reach a port. Examples are a pointer that wraps wrongly only after many writes, or a count that drifts and overflows later. The end-to-end check sees the effect only once the bad word is read. Glue invariants on the count and pointers catch such drift early and also help an induction proof close.
The scoreboard's price in depth Correct FIFO, glue and end-to-end together. Depth 10 (samples 0 to 8) passed in 1.0 s. Depth 20 ended at the 20-second budget: time limit, no verdict.
Linear scale. 0 to 20 steps.
- Checked bound
- 10
- Required bound
- 20
- Claim
- Bounded check only
Glue and end-to-end checks together on a correct FIFO. Depth 10 passed in 1.0 s; depth 20 ran out of time.
Self-Check
Tick each key point you covered in your answer.
What People Get Wrong
Common wrong answers and why they fail. Run It shows the wrong belief failing on a real simulator.
Misconception
"If the pointers and the count are proven, the data must be right."
This FIFO reads mem[rd_ptr + 1]. Every pointer and count property holds, and only the check that the output equals the oldest stored word fails.
Run It
Glue properties only, on a FIFO that reads one slot ahead.
module fifo4sb(input clk, rst, push, pop, input [7:0] wdata,
output [7:0] rdata, output full, empty);
reg [7:0] mem [0:3];
reg [1:0] wr_ptr=0, rd_ptr=0;
reg [2:0] count=0;
assign full = count == 3'd4;
assign empty = count == 3'd0;
wire wr_en = push && !full, rd_en = pop && !empty;
assign rdata = mem[rd_ptr + 2'd1]; // bug: the pointer moves before the read
always @(posedge clk)
if (rst) begin wr_ptr <= 0; rd_ptr <= 0; count <= 0; end
else begin
if (wr_en) begin mem[wr_ptr] <= wdata; wr_ptr <= wr_ptr + 2'd1; end
if (rd_en) rd_ptr <= rd_ptr + 2'd1;
count <= count + wr_en - rd_en;
end
`ifdef FORMAL
always @(posedge clk) begin
if ($initstate) begin assume(rst); end else begin assume(!rst); end
end
wire [1:0] f_ptr_diff = wr_ptr - rd_ptr; // entries between the pointers, modulo 4
// Modelling code: a shadow copy of the FIFO's contents, in order, oldest in sb0.
reg [7:0] sb0=0, sb1=0, sb2=0, sb3=0;
reg [2:0] sb_n=0;
wire [2:0] slot = sb_n - (rd_en ? 3'd1 : 3'd0); // where an accepted push lands
always @(posedge clk)
if (rst) sb_n <= 0;
else begin
if (rd_en) begin sb0 <= sb1; sb1 <= sb2; sb2 <= sb3; end
if (wr_en) case (slot)
3'd0: sb0 <= wdata; 3'd1: sb1 <= wdata; 3'd2: sb2 <= wdata; default: sb3 <= wdata;
endcase
sb_n <= sb_n + wr_en - rd_en;
end
always @(posedge clk) if (!rst) begin
if (empty) assert(wr_ptr == rd_ptr); // glue
if (full) assert(wr_ptr == rd_ptr); // glue
assert(count <= 3'd4); // glue
assert(count == f_ptr_diff || count == 3'd4 && f_ptr_diff == 2'd0); // glue
end
`endif
endmodule
Prints: bmc, depth 10 (smtbmc yices): PASS
See the Fix
module fifo4sb(input clk, rst, push, pop, input [7:0] wdata,
output [7:0] rdata, output full, empty);
reg [7:0] mem [0:3];
reg [1:0] wr_ptr=0, rd_ptr=0;
reg [2:0] count=0;
assign full = count == 3'd4;
assign empty = count == 3'd0;
wire wr_en = push && !full, rd_en = pop && !empty;
assign rdata = mem[rd_ptr + 2'd1]; // bug: the pointer moves before the read
always @(posedge clk)
if (rst) begin wr_ptr <= 0; rd_ptr <= 0; count <= 0; end
else begin
if (wr_en) begin mem[wr_ptr] <= wdata; wr_ptr <= wr_ptr + 2'd1; end
if (rd_en) rd_ptr <= rd_ptr + 2'd1;
count <= count + wr_en - rd_en;
end
`ifdef FORMAL
always @(posedge clk) begin
if ($initstate) begin assume(rst); end else begin assume(!rst); end
end
wire [1:0] f_ptr_diff = wr_ptr - rd_ptr; // entries between the pointers, modulo 4
// Modelling code: a shadow copy of the FIFO's contents, in order, oldest in sb0.
reg [7:0] sb0=0, sb1=0, sb2=0, sb3=0;
reg [2:0] sb_n=0;
wire [2:0] slot = sb_n - (rd_en ? 3'd1 : 3'd0); // where an accepted push lands
always @(posedge clk)
if (rst) sb_n <= 0;
else begin
if (rd_en) begin sb0 <= sb1; sb1 <= sb2; sb2 <= sb3; end
if (wr_en) case (slot)
3'd0: sb0 <= wdata; 3'd1: sb1 <= wdata; 3'd2: sb2 <= wdata; default: sb3 <= wdata;
endcase
sb_n <= sb_n + wr_en - rd_en;
end
always @(posedge clk) if (!rst) begin
if (empty) assert(wr_ptr == rd_ptr); // glue
if (full) assert(wr_ptr == rd_ptr); // glue
assert(count <= 3'd4); // glue
assert(count == f_ptr_diff || count == 3'd4 && f_ptr_diff == 2'd0); // glue
if (pop && !empty) assert(rdata == sb0); // end-to-end
end
`endif
endmodule
Prints: bmc, depth 10 (smtbmc yices): FAIL at step 3
Half-Truth
"A bounded end-to-end check catches everything glue catches."
A bounded run can miss state that has gone wrong but not yet reached a port. In the lesson glue caught the count bug at step 4, end-to-end at step 5.
Wrong Altitude
Builds a full reference queue in the bench for a 16-deep FIFO.
It works, but it costs depth and state. One symbolic tracked word checks order and integrity for every value far more cheaply.