Your First Proof: The Same SVA, No Testbench

Run the error-response rule with free inputs, read the trace the solver writes, and see why the same assertion passed eighteen thousand times in simulation.

Lesson
F1.2
Time
15 min
Runs on
SymbiYosys
Check
4 questions

The error-response rule from A1.3 Implication and vacuous success (after a read that returns SLVERR, rvalid must drop) passed eighteen thousand times per regression. Nobody noticed that every pass was vacuous: the tests never addressed the error range, so the antecedent never fired. The team bound the same assertion into a formal run, and it failed in three cycles, with an address the test plan had never listed. The tool is not given addresses; it takes all of them.

The counterexample SBY wrote for the read channel
01234clkrstfreearvalidfreearaddr00F000rvalidfreerreadyrresp0010Free address: F0SLVERR completedrvalid still highSBY reports FAIL

Measured with smtbmc yices at depth 20. The sampled violation is step 3; SBY reports the clocked check at step 4.

Two Benches, One Rule

Who Chooses the Inputs?On the left, the A1.3 simulation bench: a driver chooses one sequence of addresses. On the right, the formal bench: nothing drives araddr, arvalid or rready; the solver may give them any value at every step. Arrows mean "supplies the inputs".
Who chooses the inputs?On the left, the A1.3 simulation bench: a driver chooses one sequence of addresses. On the right, the formal bench: nothing drives `araddr`, `arvalid` or `rready`; the solver may give them any value at every step. Arrows mean "supplies the inputs". component Simulation driver clock, reset, 18,000addresses below F0 component rd_slave +assertion one driven sequence component Free inputs araddr, arvalid,rready: any value,every step component rd_slave +property reset assumed in step0 the chosen stimulusevery allowedsequence
Read the diagram as text
  • Simulation driver: clock, reset, 18,000 addresses below F0
  • rd_slave + assertion: one driven sequence
  • Free inputs: araddr, arvalid, rready: any value, every step
  • rd_slave + property: reset assumed in step 0
  • Simulation driver → rd_slave + assertion: the chosen stimulus
  • Free inputs → rd_slave + property: every allowed sequence

The RTL and the error-response rule are the same text on both sides. Only the source of the inputs changes.

A simulation bench drives one sequence. The A1.3 driver walked 18,000 addresses below F0, so rresp was never SLVERR, the antecedent never held, and every attempt of the assertion passed without checking anything. A formal bench has no driver. The design, the property and a reset assumption are the whole setup; the inputs are free, and at every step the solver may give them any value.

Three words from here on. A property is the rule. A counterexample is a trace that breaks it. A proof is the tool's statement that no counterexample exists within what it checked, bounded or unbounded (F1.5).

Read the Counterexample

The counterexample, one frame at a time

Each frame holds the state and the free input values at one formal step. The obligation starts when the SLVERR handshake completes.

  1. arvalid=0, araddr=0x00

    rvalid=0, rresp=00

    No SLVERR obligation

    No obligation yet
  2. arvalid=1, araddr=0xF0

    rvalid=0, rresp=00

    No SLVERR obligation

    No obligation yet
  3. arvalid=0, araddr=0x00

    rvalid=1, rresp=10

    SLVERR handshake starts the obligation

    No obligation yet
  4. arvalid=0, araddr=0x00

    rvalid=1, rresp=10

    Required: rvalid = 0

    × Fails
  5. arvalid=0, araddr=0x00

    rvalid=1, rresp=10

    SBY reports the sampled failure

    × Fails

Select a frame to inspect its inputs and assertion.

Step 3 violates the next-cycle rule; step 4 is where the clocked check is reported. Select a frame to inspect it.

The tool found the one sequence that breaks the rule and wrote it down: a .vcd with every signal, step by step. The shaded rows are the inputs it chose; the marked step is where the assertion fails. A formal counterexample is a directed test the tool wrote for you, and SBY also writes trace_tb.v, a testbench that replays it in any simulator.

RunMeasured resultTime on this machine
Verilator, the A1.3 bench18,000 attempts; 0 SLVERR antecedents; 18,000 vacuous attempts227 ms
sby bmc, clocked immediate assertion, yicesFAIL at reported step 4372 ms
sby bmc, the concurrent SVA through the subset compiler, yicesFAIL at reported step 4351 ms
sby bmc, the same SVA with |-> instead of |=>FAIL at reported step 3383 ms
sby bmc, the fixed RTLBMC PASS to depth 20354 ms
sby bmc, the concurrent SVA, z3FAIL at reported step 4450 ms

Same RTL, same rule. Yices chose address F0; Z3 chose F1. Both are in the error range, and the exact value is the solver's choice, not a fact about the design.

The |-> row fails one step earlier because the overlapping implication asks for !rvalid in the same cycle as the handshake, while rvalid is still high by definition. That is the wrong property, not a second bug; F1.4 sorts the two apart.

The Property in Two Forms

Concurrent SVA (what a JasperGold or VC Formal user writes)

assert property (@(posedge clk) disable iff (rst) rvalid && rready && rresp == 2'b10 |=> !rvalid);

An SLVERR completion requires rvalid low on the next clock.

Clocked immediate assertion (what this engine runs)

reg err_done=0;
always @(posedge clk) begin
  err_done <= !rst && rvalid && rready && rresp == 2'b10;
  if (!rst && err_done) assert(!rvalid);
end

err_done remembers the antecedent; the guard checks the next sample.

Both forms say the same thing. |=> becomes a flop that remembers "an SLVERR completed last cycle" and an assert guarded by it. The page shows the SVA because that is what the industry writes, and the engine form because that is what runs here; F3.1 teaches the translation table. In a real project the formal file is bound to the design, never edited into it (A7.1 Packaging and binding).

The whole task. The page leaves depth at the default, 20.
[options]
mode bmc
depth 20
[engines]
smtbmc yices
[script]
read_verilog -formal -sv design.sv
prep -top rd_slave
[files]
design.sv

What the Result Establishes

The bound and the first failure

Depth 20 checks clocked samples 0 to 18. The buggy read channel fails at reported step 4 (sampled state 3), well inside the bound; the fixed one passes to the bound.

Linear scale. 0 to 25 steps.

Checked bound
20
Required bound
20
First reported failure
4
Claim
Counterexample found

Required bound here means the task target. The fixed twin passes BMC at depth 20 (samples 0 to 18); no unbounded proof is claimed.

The fixed twin passes BMC at depth 20: no input sequence of up to 20 cycles from reset breaks the rule. That is a bounded result with its depth attached, not a proof for all time. The buggy twin fails at reported step 4; a FAIL is a reachable counterexample from the assumed reset.

A reachable failure and an induction candidate differ

A separate two-bit teaching model: reset reaches 00 and 11. An induction step may start at 01, a state reset never reaches, and step into the bad state 10.

A reachable failure and an induction candidate differA separate two-bit teaching model: reset reaches 00 and 11. An induction step may start at 01, a state reset never reaches, and step into the bad state 10.Reachable from resetInduction00Depth 011Depth 101Unreachable10Bad state

The helper invariant a == b excludes 01 and 10. A counterexample to induction does not by itself show a reachable bug; F2.4 returns to this picture.

Read the states and transitions
  • 00: initial, depth 0.
  • 11: reachable, depth 1.
  • 01: unreachable, depth 0.
  • 10: bad, depth 1.
  • 00 → 11.
  • 11 → 00.
  • 01 → 10 (induction candidate).

Illustration. A teaching drawing for the induction lesson, not a state enumeration of rd_slave. The counterexample on this page is reachable from reset.

Asked in Interviews

QuestionAnswer
Does formal need a testbench? What replaces the stimulus?No driver; the inputs are free and the solver chooses them. Assumptions (F1.3) shape what it may choose.
Why did my assertion pass in simulation and fail in formal?Simulation checked only the inputs the tests produced; formal checked all of them and found one the tests never sent.

Try It

  1. Run and read

    Run as it is: FAIL at reported step 4. In the trace find the address the solver chose; any value at or above F0 breaks the rule, and the exact value may differ between solvers.

  2. Change the implication

    Clear the err_done block, paste the concurrent property into props.sv, change |=> to |-> and run. FAIL one step earlier: the same-cycle check asks for !rvalid while the handshake is happening.

  3. Fix the RTL

    Replace the condition rresp == 2'b10 && !extra with 1'b0, leave props.sv blank, and run: BMC PASS to depth 20.

design.sv

props.sv

Use assert property (@(posedge clk) ...);. Leave blank for immediate assertions in the design.

Inputs are free unless constrained. Run to find a counterexample or check a proof.

Model: one positive-edge clock, binary data. Sampled history requires declaration-initialized variables; primary-input history is unsupported.

SBY summary and log

Check Yourself

Predict the outputrd_slave with the bug, sby bmc, default depth 20, reset assumed in step 0, smtbmc yices. SBY reports the failing assertion at which step?
module rd_slave(input clk, rst, arvalid, input [7:0] araddr,
  input rready, output arready, output reg rvalid=0,
  output reg [1:0] rresp=0, output reg [7:0] rdata=0);
  reg extra=0;
  assign arready = !rvalid;
  always @(posedge clk) begin
    if (rst) begin rvalid<=0; rresp<=0; rdata<=0; extra<=0; end
    else if (rvalid && rready) begin
      if (rresp == 2'b10 && !extra) extra<=1;
      else begin rvalid<=0; extra<=0; end
    end else if (arvalid && arready) begin
      rvalid<=1; rresp <= araddr >= 8'hf0 ? 2'b10 : 2'b00;
      rdata<=araddr; extra<=0;
    end
  end
  always @(posedge clk) begin
    if ($initstate) begin assume(rst); end else begin assume(!rst); end
  end
  reg err_done=0;
  always @(posedge clk) begin
    err_done <= !rst && rvalid && rready && rresp == 2'b10;
    if (!rst && err_done) assert(!rvalid);
  end
endmodule
ConceptWhich statement about the formal bench is wrong?
ConceptWhy did the A1.3 simulation pass?
Read the waveformAt which step did the solver first raise arvalid with an address in the error range?
The counterexample SBY wrote for the read channel
01234clkrstfreearvalidfreearaddr00F000rvalidfreerreadyrresp0010Free address: F0SLVERR completedrvalid still highSBY reports FAIL

Measured with smtbmc yices at depth 20. The sampled violation is step 3; SBY reports the clocked check at step 4.

Remember

  • A property is the rule; a counterexample is a trace that breaks it; a proof says none exists within what was checked.
  • Inputs are free; the solver chooses them, and assumptions shape what it may choose.
  • A formal counterexample is a directed test the tool wrote; trace_tb.v replays it.
  • At depth 20 this flow checks clocked samples 0 to 18; a PASS there is bounded, not a proof.