Skip to content
DenseChip
Learn
PracticeCode labInterview

DenseChip

Navigate

Menu

LearnPracticeCode labInterview

Tools

All toolsSV editorCode repoResume
DenseChip

Practice, courses, and chip-design tools in one focused workspace.

LearnPracticeCode labInterviewToolsLearning paths

© 2026 DenseChip

AboutContactPrivacyTerms
Skip to lesson

Course contents

  • How to Use This Course
  • Reading Paths
  • Concept Map
  • Meet DCRouter
  • Glossary
  • Part 0: What Verification Is8
  • Part 1: What Can Go Wrong11
  • Part 2: Specifications as Checkable Rules8
  • Part 3: Observability and Controllability7
  • Part 4: Stimulus11
  • Part 5: Checking11
  • Part 6: Coverage10
  • Part 7: Formal and Simulation10
    • What Formal Does
    • Bounded Proofs
    • Induction and Full Proofs
    • Assumptions and Over-Constraint
    • Where Formal Wins
    • Where Simulation Wins
    • Formal Apps
    • Combining Formal and Simulation
    • Proving DCRouter
    • Drills: Formal and Simulation
  • Part 8: Verification Levels and Reuse8
  • Part 9: Planning and Risk9
  • Part 10: Emulation and Prototyping8
  • Part 11: Post-Silicon Validation8
  • Part 12: Bug Economics8
  • Part 13: Debug7
  • Part 14: Sign-Off7
  • Part 15: Verification in Context8
  • Part 16: Case Studies5
  • Part 17: Capstones5
  • Part 18: Interview Preparation6
Loading lesson

‹Part 7: Formal and Simulation
  1. Topics
  2. First Principles of Verification Strategy
  3. Part 7: Formal and Simulation
  4. Assumptions and Over-Constraint

Part 7 · Formal and Simulation · Senior (S)

Assumptions and Over-Constraint

Assumptions tell a formal tool what the environment may do. Too few and it reports counterexamples no real system could produce; too many and it proves properties by excluding the inputs that break them. A runnable over-constrained proof that passes on a buggy FIFO, and the cover check that exposes it, plus a case from building this course where a Yosys feature was silently ignored.

On this page

    Note

    Level: Senior (S) · Answers: When should we prove instead of test?

    Before this: Induction and Full Proofs, False Passes

    A formal engineer is fighting false counterexamples: the tool keeps finding traces where the producer pushes into a FIFO that is nearly full, and the downstream logic was never designed for that. The producer in this system never sends more than three words without a pop, so the engineer adds an assumption saying exactly that. The counterexamples disappear and every property passes. One of those properties was about the FIFO's full flag, which now can never be reached.

    How do assumptions go wrong, and how do you catch it?

    The obvious answer, and why it breaks

    The obvious use of assumptions is to silence counterexamples until the proof passes. Each assumption removes input sequences from the proof. An assumption that matches a guarantee of the real environment removes only impossible traces. One that is stricter than the environment removes possible traces too, and with them the bugs they would have exposed. A passing proof cannot tell you which kind you wrote.

    The idea: assumptions are a model of the environment

    Too few

    Right

    Too many (over-constraint)

    False counterexamples: traces no real environment produces

    Exactly what the neighbours' specification guarantees

    False proofs: real traces removed, bugs hidden

    Loud: you see them

    Reviewed against the interface specification

    Silent: everything passes

    The same principle as constraints in simulation (lesson 4.3), with sharper consequences: an over-constrained random test still runs other cases, but an over-constrained proof reports proved for a property it never examined.

    Build it: a false proof, and the cover that exposes it

    This is lesson 7.2's buggy FIFO, with an assumption that pushes stop once three words are held:

    fifo_over.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    // formal: bmc 12
    // Lesson 7.4: an over-constrained proof. This is the buggy FIFO of lesson 7.2 (full rises one
    // entry late), but the environment assumption says pushes stop once 3 words are held,
    // "because our producer never sends more than 3". Every assertion passes: the assumption has
    // removed the only inputs that reach the bug.
    module fifo_over (input logic clk, rst, push, pop, input logic [7:0] din);
      logic [7:0] mem [0:3];
      logic [1:0] wp, rp;
      logic [2:0] count;
      logic full, empty;
      assign full  = (count > 3'd4);                   // BUG: should be (count == 4)
      assign empty = (count == 3'd0);
      always @(posedge clk)
        if (rst) begin wp <= 0; rp <= 0; count <= 0; end
        else begin
          if (push && !full) begin mem[wp] <= din; wp <= wp + 2'd1; end
          if (pop && !empty) rp <= rp + 2'd1;
          count <= count + 3'(push && !full) - 3'(pop && !empty);
        end
      logic past = 1'b0;
      always @(posedge clk) past <= 1'b1;
      always @(*) assume (rst == !past);
      always @(*) if (count >= 3'd3) assume (!push);   // the over-constraint
      always @(posedge clk) if (past) assert (count <= 3'd4);
    endmodule

    The bounded check passes, as its first line says: the property "occupancy never exceeds 4" is safe because the FIFO never gets past 3. Now ask the opposite question. A cover asks whether a situation can occur; written as an assertion of its negation, a passing check means it cannot:

    fifo_cover.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    // formal: bmc 12
    // Lesson 7.4: the over-constraint exposed. A cover asks "can the FIFO ever be full?"; written
    // as an assertion of the opposite, a passing bounded check means "never, within 12 cycles".
    // It passes: under the assumption the FIFO never holds 4 words, so no check about full
    // behaviour was ever exercised.
    module fifo_cover (input logic clk, rst, push, pop, input logic [7:0] din);
      logic [7:0] mem [0:3];
      logic [1:0] wp, rp;
      logic [2:0] count;
      logic full, empty;
      assign full  = (count > 3'd4);                   // BUG: should be (count == 4)
      assign empty = (count == 3'd0);
      always @(posedge clk)
        if (rst) begin wp <= 0; rp <= 0; count <= 0; end
        else begin
          if (push && !full) begin mem[wp] <= din; wp <= wp + 2'd1; end
          if (pop && !empty) rp <= rp + 2'd1;
          count <= count + 3'(push && !full) - 3'(pop && !empty);
        end
      logic past = 1'b0;
      always @(posedge clk) past <= 1'b1;
      always @(*) assume (rst == !past);
      always @(*) if (count >= 3'd3) assume (!push);   // the over-constraint
      always @(posedge clk) if (past) assert (count != 3'd4);   // cover "full", negated
    endmodule

    It passes too, and that is the alarm: under the assumptions, the FIFO can never be full, so no property about full behaviour was ever exercised. Remove the over-constraint and lesson 7.2's counterexample comes straight back.

    A real case: a feature silently ignored

    While building lesson 7.9 for this course, the first plan put DCRouter's formal properties in a separate checker module attached with SystemVerilog bind. The proof passed, quickly. A deliberately false property also passed. The open-source Yosys version used here accepted the bind statement and ignored it: no property was checked at all. The properties were moved inside the design under ` ifdef FORMAL `` (fact V9), where a deliberately false one fails as it should. The lesson is the same as the FIFO's: a proof that has never failed has not shown that it checks anything.

    Guarding against over-constraint

    • Every assumption traces to the neighbour's specification, not to one neighbour's habit or to a counterexample you wanted gone.

    • Cover the important situations: full, empty, every state, every grant; an unreachable cover means the proof is vacuous there.

    • Plant a bug and expect a counterexample: as in lessons 7.2 and 7.9; if the proof still passes, it is not checking.

    • Prefer assume-guarantee: assumptions about block A's inputs are proved as assertions on block B's outputs.

    How real teams manage assumptions

    Practice

    Purpose

    Assumption reviews against interface specifications

    Each assumption justified by a guarantee

    Cover properties run with every proof

    Vacuity detected automatically

    Assumptions on one block proved on its neighbour

    No assumption left unverified

    What breaks

    The opening story is the model above. The illustrative team's assumption came from the producer they had, not from the interface specification; a later producer sent four words, filled the FIFO, and met the late full flag in the lab. A cover on "FIFO full" would have shown the over-constraint the day it was written.

    Why this matters

    Senior lens. Review assumptions with the same suspicion as waivers: each one removes behaviour from the proof. Require a source for every assumption, covers on the situations the properties care about, and a planted bug that the proof catches. A proof that passes too easily is a proof that is not looking.

    Interview

    Asked: "What is over-constraining?"

    Weak answer: Having too many properties.

    Strong answer: Writing assumptions stricter than the real environment, so the proof excludes legal input sequences and the bugs they would expose. It's silent: properties pass. I guard against it by tracing every assumption to the neighbour's specification, running cover properties on the situations the assertions care about (an unreachable cover means a vacuous proof), and planting bugs the proof must catch. In a small example, assuming a producer never sends more than three words made a buggy FIFO's properties pass, and a cover on 'full' immediately showed full was unreachable.

    Follow-ups: (1) What is a vacuous proof? (2) How does assume-guarantee help?

    Check yourself Why do all the properties pass on the buggy FIFO here, when lesson 7.2's run of the same FIFO returns a counterexample?

    The assumption stops pushes at 3 words, so the FIFO never reaches 4 and the late full flag never matters.

    Check yourself A cover written as assert (count != 4) passes. What does that tell you?

    Under the current assumptions the FIFO can never hold 4 words within the bound, so every property about the full condition was checked vacuously.

    Key takeaways

    • Assumptions model the environment: too few give false counterexamples, too many give false proofs.

    • Over-constraint is silent; covers on key situations and planted bugs expose it.

    • A proof that has never failed has not shown it checks anything.

    Keep going

    Next: Lesson 7.5, Where Formal Wins. The kinds of logic where formal beats simulation.

    Related topics

    • Part 7 overview: Formal and Simulation

    Previous

    75 / 160

    Next: Where Formal Wins

    Up next

    Up nextWhere Formal Wins

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • What Formal Does
    • Bounded Proofs
    • Where Simulation Wins