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. Induction and Full Proofs

Part 7 · Formal and Simulation · Senior (S)

Induction and Full Proofs

Induction proves a property for every depth: if it holds for k consecutive cycles from any state, it holds in the next. A true property can still fail induction, because the step may start in an unreachable state. Two runnable proofs of the same FIFO property, one failing induction and one passing once a helper invariant ties the count to the pointers.

On this page

    Note

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

    Before this: Bounded Proofs

    A FIFO's designer writes a simple property: when the FIFO is empty, the read and write pointers are equal. It is true; every bounded check passes, at every depth anyone tries. Then the formal tool is asked for a full proof, and it returns a counterexample to the induction step: a FIFO with a count of 1 and equal pointers, which pops and ends up empty with unequal pointers. No such FIFO can exist. The tool knows that only if someone tells it.

    How do you prove a property for all depths, and why does a true property sometimes fail?

    The obvious answer, and why it breaks

    The obvious path to a full proof is a very deep bounded check. No depth is enough for a design that can run forever, and cost grows steeply (lesson 7.2). Induction avoids the bound, but it reasons from arbitrary states, including states the design can never reach. A property that holds in every reachable state can fail from an unreachable one, and then the proof is inconclusive until the unreachable states are ruled out.

    The idea: base case and induction step

    Part

    Checks

    Tool step

    Base case

    The property holds for the first k cycles from reset

    A bounded check of depth k

    Induction step

    From any state where the property held for k consecutive cycles, it holds in the next

    A check starting from an arbitrary state (yosys-smtbmc -i, fact V9)

    Both pass

    The property holds at every depth

    A full proof

    Step fails

    Either a real bug deeper than k, or an unreachable starting state

    Inconclusive: add a helper invariant or increase k

    A helper invariant is an extra property, true in every reachable state, that rules out the unreachable states the induction step would start from. It must itself be proved, which is why it is written as an assertion, not an assumption.

    Build it: the same property, without and with a helper

    The FIFO keeps its count and its pointers in separate registers. Without help, induction is allowed to start from count 1 with equal pointers:

    fifo_ind.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    // formal: prove 4 expect unproven
    // Lesson 7.3: induction without a helper invariant. The FIFO keeps its count and its pointers
    // separately. The property "empty means the pointers are equal" holds in every reachable
    // state, so the bounded check passes, but induction can start from an unreachable state
    // (count 1 with equal pointers) where one pop breaks it.
    module fifo_ind (input logic clk, rst, push, pop);
      logic [1:0] wp, rp;
      logic [2:0] count;
      logic full, empty;
      assign full  = (count == 3'd4);
      assign empty = (count == 3'd0);
      always @(posedge clk)
        if (rst) begin wp <= 0; rp <= 0; count <= 0; end
        else begin
          if (push && !full) wp <= wp + 2'd1;
          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 @(posedge clk) if (past && !rst) assert (!empty || wp == rp);
    endmodule

    The first line says what happens, and the course's checker confirms it: the bounded check passes and the induction step fails. Now with a helper invariant, "the count equals the distance between the pointers, and is at most 4":

    fifo_ind_helper.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
    26
    // formal: prove 4
    // Lesson 7.3: the same proof with a helper invariant tying the count to the pointers. The FIFO keeps its count and its pointers
    // separately. Without help, induction can start from an unreachable state (count 1 with
    // equal pointers). With the helper invariant asserted, those states are excluded and both
    // the base case and the induction step pass.
    module fifo_ind_helper (input logic clk, rst, push, pop);
      logic [1:0] wp, rp;
      logic [2:0] count;
      logic full, empty;
      assign full  = (count == 3'd4);
      assign empty = (count == 3'd0);
      always @(posedge clk)
        if (rst) begin wp <= 0; rp <= 0; count <= 0; end
        else begin
          if (push && !full) wp <= wp + 2'd1;
          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 @(posedge clk) if (past && !rst) begin
        assert (!empty || wp == rp);                   // the property
        assert (count <= 3'd4 && count[1:0] == 2'(wp - rp));   // the helper invariant
      end
    endmodule

    Both the base case and the induction step pass: the property is proved for every depth. The helper ruled out every state where count and pointers disagree, and in all remaining states one more cycle preserves the property. Note that the helper is what a designer would call the FIFO's defining relationship; DCRouter's FIFO carries the same invariant in its formal properties, and with it the whole router's FIFO, arbiter and credit properties are proved by a 4-step induction (lesson 7.9).

    When induction fails

    • Read the induction counterexample's first state: if it cannot happen in reality, write the invariant that forbids it.

    • Typical helpers: counters agree with pointers; one-hot states are one-hot; a lock implies an owner; credits plus outstanding items equal the total.

    • Increase k: a larger induction depth rules out states that cannot persist for k cycles, at higher cost.

    • Suspect a real bug: a failing step can also start from a reachable state deeper than the base case; check reachability before writing a helper.

    How real teams get full proofs

    Practice

    Purpose

    Designers provide invariants with their RTL

    The relationships they designed in become proof helpers

    Engines that find invariants automatically (IC3/PDR)

    Full proofs without hand-written helpers in many cases (fact V2)

    Abstraction of large memories and counters

    Smaller state makes proofs converge

    What breaks

    A team (illustrative) made a stubborn induction pass by writing the missing relationship as an assumption instead of an assertion. The proof passed. The assumption was false in one corner of the design, so it had silently removed real behaviour from the proof, and a bug lived there. Helpers must be asserted and proved; assuming them is over-constraining (lesson 7.4).

    Why this matters

    Senior lens. Treat an inconclusive induction as information: its counterexample names a state the design's own invariants should exclude. Ask designers for those invariants early, assert them, and never fix a proof by assuming what should have been proved.

    Interview

    Asked: "Why does induction fail without invariants?"

    Weak answer: Because the design has a bug.

    Strong answer: Because the induction step starts from an arbitrary state where the property held for k cycles, including states the design can never reach. A true property can fail from such a state. In a FIFO with a separate count and pointers, 'empty implies equal pointers' fails induction from count 1 with equal pointers, which can't happen. Adding the helper invariant 'count equals the pointer distance', asserted and proved, rules those states out and the full proof passes. A failing step can also be a real deep bug, so I check whether its starting state is reachable before writing a helper.

    Follow-ups: (1) What is a helper invariant? (2) Why must a helper be asserted, not assumed?

    Check yourself Why does the bounded check pass for the property without the helper?

    Every state reachable from reset satisfies it; the bounded check only explores reachable states, so it never sees the bad starting state the induction step uses.

    Check yourself What would go wrong if the helper invariant were written as an assume?

    If it were false anywhere reachable, it would silently remove those behaviours from the proof, so a bug there could pass; asserting it makes the tool prove it.

    Key takeaways

    • Induction proves a property at every depth: base case from reset, step from any state.

    • A true property can fail induction from an unreachable state; a helper invariant rules such states out.

    • Helpers must be asserted and proved, never assumed.

    Keep going

    Next: Lesson 7.4, Assumptions and Over-Constraint. Assumptions model the environment; too many hide bugs.

    Related topics

    • Part 7 overview: Formal and Simulation

    • Formal course: Property Development

    Previous

    74 / 160

    Next: Assumptions and Over-Constraint

    Up next

    Up nextAssumptions and Over-Constraint

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • What Formal Does
    • Where Formal Wins
    • Where Simulation Wins