Under-Constraint and Counterexample Triage

Watch a correct design fail with free inputs, walk the trace through four questions, and fix the environment with one assumption from the spec.

Lesson
F3.3
Time
15 min
Runs on
SymbiYosys
Check
4 questions

A read slave captured the address in the first cycle arvalid was high, to save a cycle when arready came late. The formal run failed with a trace in which the master changed araddr mid-stall, so the designer rewrote the capture logic. A week later the lead pointed at the AXI rule: a master holds its payload while VALID waits for READY. The trace was illegal, and the design had been right.

Run A: a correct slave fails with free inputs
012345clkfreerstfreearvalidfreearaddr00FE7E00arreadyrvalidfreerreadyrdata00FFread FEstall: arready lowaraddr now 7Erdata FFSBY reports FAIL

Measured with smtbmc yices at depth 20. The addresses are the solver's choice. The response at step 4 carries the captured FE plus one; the rule expects 7E plus one.

Too Few Assumptions

Triage a Counterexample in Four QuestionsAsk in order and stop at the first "no". Each question has one fix. Arrows mean "if so, go to".
Triage a counterexample in four questionsAsk in order and stop at the first "no". Each question has one fix. Arrows mean "if so, go to". component 1. Is step 0 areset state? no: fix the resetassumption (F1.6) component 2. Is every shadedinput legal? check each valueagainst the spec row,step by step component 3. Does the checksay what the specsays? implication, delay, thesignal it reads component 4. RTL bug only now is the designwrong component Add oneassumption on inputs, with thespec row in itscomment component Fix the property the design may beright yesyesyesnono
Read the diagram as text
  • 1. Is step 0 a reset state?: no: fix the reset assumption (F1.6)
  • 2. Is every shaded input legal?: check each value against the spec row, step by step
  • 3. Does the check say what the spec says?: implication, delay, the signal it reads
  • 4. RTL bug: only now is the design wrong
  • Add one assumption: on inputs, with the spec row in its comment
  • Fix the property: the design may be right
  • 1. Is step 0 a reset state? → 2. Is every shaded input legal?: yes
  • 2. Is every shaded input legal? → 3. Does the check say what the spec says?: yes
  • 3. Does the check say what the spec says? → 4. RTL bug: yes
  • 2. Is every shaded input legal? → Add one assumption: no
  • 3. Does the check say what the spec says? → Fix the property: no

Run A stops at question 2: at step 3 araddr changed while step 2 had arvalid high and arready low. The flow is F1.4's table in order.

With no assumptions the solver may drive input sequences no legal neighbour produces. A FAIL built on one is a false failure: the design misbehaved in a world the spec forbids. That is under-constraint, the other end of the dial from F3.2.

The flow keeps you from fixing the wrong thing. Start state first, then inputs, then the check, and only then the RTL. Run A stops at question 2, and the hook's designer skipped straight to question 4.

The Handshake Rule People Get Backwards

Signal, before the handshakeMay it change?So the assumption says
ARVALID once highNo: it stays high until ARREADY is sampled higharvalid && !arready |=> arvalid
ARADDR while ARVALID waitsNo: the payload holds until the transfer... |=> araddr == araddr_q
ARREADYYes: it may rise and fall again before ARVALID arrivesnothing: never assume the receiver holds READY

IHI 0022L A2.3, the Valid-Ready handshake (P1.3 draws it). A slave that relies on a held payload is correct; one that relies on a held READY is not.

One assumption, on the inputs arvalid and araddr, written from that row. The engine refuses $stable(araddr) on a primary input (it reports UNSUPPORTED_INPUT_HISTORY: araddr for $past(araddr) too), so the bench compares with araddr_q, last cycle's address. The response rule reads it the same way.

RunMeasured resultTime
A: no assumptionFAIL at reported step 5644 ms
B: the handshake assumptionBMC PASS to depth 20687 ms
A and B, clocked immediate formFAIL at reported step 5; BMC PASS to depth 20516 ms; 627 ms
Capture bug, under the assumptionFAIL at reported step 3620 ms

rd_slave_sp; mode bmc, depth 20, smtbmc yices, reset assumed in step 0.

The capture bug fails under the honest assumption too, at reported step 3: a read accepted in its first cycle returns the stale copy. Walk that trace through the flow: step 0 is reset, every input is legal, the check matches the spec. Box 4: the RTL.

Did the Assumption Go Too Far?

CoverHandshake assumptionassume(!arvalid)araddr never changes
arvalid && !arready (a stall)reached, step 3unreachedreached, step 3
arvalid && arready (a handshake)reached, step 2unreachedreached, step 2
arvalid && arready && araddr == 8'hFFreached, step 2unreachedunreached

Mode cover, depth 20, the correct RTL. All three assumptions turn run A green in bmc: BMC PASS to depth 20, BMC PASS to depth 20, BMC PASS to depth 20.

Two wrong fixes silence run A just as well. assume(!arvalid) kills every cover: nothing happens. Freezing the address keeps the stall and the handshake but loses the top address, so a cover at each extreme of a data input earns its place.

Asked in Interviews

QuestionAnswer
What is under-constraining?Too few assumptions: the tool drives sequences the environment never produces and reports false failures.
How do you decide a failure is the environment's fault?Reset state, then each shaded input against the spec row, then the check against the spec, and only then the RTL.
Must READY stay high once asserted?No. VALID must stay high and the payload stable until the handshake; READY may rise and fall freely.

Try It

As IEEE SVA writes it

assume property (@(posedge clk) disable iff (rst) arvalid && !arready |=> arvalid && $stable(araddr));
assert property (@(posedge clk) disable iff (rst) arvalid && arready |=> rvalid && rdata == $past(araddr) + 8'd1);
assert property (@(posedge clk) disable iff (rst) rvalid && !rready |=> rvalid && $stable(rdata));

History on the inputs araddr: this engine refuses it.

props.sv, with the registered copy

assume property (@(posedge clk) disable iff (rst) arvalid && !arready |=> arvalid && araddr == araddr_q);
assert property (@(posedge clk) disable iff (rst) arvalid && arready |=> rvalid && rdata == araddr_q + 8'd1);
assert property (@(posedge clk) disable iff (rst) rvalid && !rready |=> rvalid && $stable(rdata));

araddr_q is last cycle's araddr, so the questions are the same from step 1.

The task. Depth is the default, 20; set mode cover for the cover runs.
[options]
mode bmc
depth 20
[engines]
smtbmc yices
[script]
read_verilog -formal -sv design.sv props.sv
prep -top rd_slave_sp
[files]
design.sv
props.sv
  1. Run free

    Run as it is: FAIL at reported step 5. Walk the trace through the four questions and stop at the first "no".

  2. Write one assumption

    Add the handshake assumption as the first line of props.sv and run: BMC PASS to depth 20.

  3. Check it with covers

    Replace the two assertions with the three covers, mode cover: all reached.

  4. Find a real bug

    Make the response always use addr_cap and run under the assumption: FAIL at reported step 3. The flow ends at box 4.

  5. Try a wrong fix

    Swap the assumption for assume(!arvalid) in design.sv. Green in bmc; now run the covers.

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_sp, correct RTL, mode bmc, default depth 20, smtbmc yices, reset assumed in step 0, no assumption on the address channel. SBY reports the failure at which step?
module rd_slave_sp(input clk, rst, arvalid, input [7:0] araddr, input rready,
  output arready, output reg rvalid=0, output reg [7:0] rdata=0);
  reg [7:0] addr_cap=0;   // the speculative copy
  reg waited=0;           // arvalid was high last cycle without a handshake
  assign arready = !rvalid;
  always @(posedge clk)
    if (rst) begin rvalid <= 0; waited <= 0; end
    else begin
      if (arvalid && !waited) addr_cap <= araddr;   // capture in the first arvalid cycle
      waited <= arvalid && !arready;
      if (rvalid && rready) rvalid <= 0;
      else if (arvalid && arready) begin
        rvalid <= 1;
        rdata  <= (waited ? addr_cap : araddr) + 8'd1;
      end
    end
`ifdef FORMAL
  always @(posedge clk) begin
    if ($initstate) begin assume(rst); end else begin assume(!rst); end
  end
  reg [7:0] araddr_q=0;   // last cycle's araddr, the registered form of $past(araddr)
  always @(posedge clk) araddr_q <= araddr;
`endif
endmodule
Read included file: props.sv
assert property (@(posedge clk) disable iff (rst) arvalid && arready |=> rvalid && rdata == araddr_q + 8'd1);
assert property (@(posedge clk) disable iff (rst) rvalid && !rready |=> rvalid && $stable(rdata));
ConceptWhich statement about the AXI handshake is wrong?
ConceptTriaging a counterexample, what do you check before the RTL?
Read the waveformIn run A, which free input changes illegally, and at which step?
Run A: a correct slave fails with free inputs
012345clkfreerstfreearvalidfreearaddr00FE7E00arreadyrvalidfreerreadyrdata00FFread FEstall: arready lowaraddr now 7Erdata FFSBY reports FAIL

Measured with smtbmc yices at depth 20. The addresses are the solver's choice. The response at step 4 carries the captured FE plus one; the rule expects 7E plus one.

Remember

  • Under-constraint: free inputs drive sequences no legal neighbour sends, and the FAIL is false.
  • Triage in order: reset state, each shaded input against the spec, the check, and only then the RTL.
  • Write the missing assumption on inputs, from one spec row, and cite it in a comment.
  • Then cover the stall, the handshake and each extreme of the payload, so the assumption did not go too far.