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

AboutRoadmapCareersInternshipsFAQContactPrivacyTerms
Skip to lesson

Course contents

  • How to Use This Course
  • Reading Paths
  • Glossary
  • Metrics Reference
  • Formal Cheat Sheet
  • Formal Foundations19
    • Formal versus Simulation
    • Transition Systems, Invariants and the Three Property Roles
      • State, Inputs and the Transition Relation
      • Safety Properties as Invariants
      • Assume, Assert, Cover: the Three Roles
      • State-Space Arithmetic
    • Reset, Initial State and the First Cycle
    • Cone of Influence
    • The Formal Tool Flow, Vendor-Neutral
  • Properties and SVA16
  • Property Development and Constraints19
  • Proof Engines and Convergence23
  • Formal Applications20
  • Equivalence Checking14
  • Formal Coverage and Signoff15
  • Methodology and Debug13
  • Interview Prep15
  • Capstone: A Chip-Block Verification Lab24
Loading lesson

←Transition Systems, Invariants and the Three Property Roles
  1. Topics
  2. Formal Verification
  3. Formal Foundations
  4. Transition Systems, Invariants and the Three Property Roles
  5. State-Space Arithmetic
On this page

    Part 1 of 10, lesson 8 of 19Part 1 of 10/Formal Foundations/Lesson 8 of 19All levels

    State-Space Arithmetic

    How big the spaces are, what visiting them would cost, and how assumptions shrink the input space.

    Why it matters

    A newcomer proposes to verify the FIFO by visiting every state. The FIFO has 39 state bits, and visiting 2 to the 39 states at a billion a second takes under ten minutes, so the idea sounds fine until the inputs and the storage are counted. The same newcomer then assumes away much of the input space without recording it, and the proof passes on a slice nobody can name. Both mistakes are arithmetic, and both are cheap to prevent once the numbers are written down.

    Job of this lesson: compute state counts, show what explicit enumeration really costs, and quantify how assumptions shrink the input space.

    The core idea

    N state bits mean 2 to the N states, and every extra bit doubles the space. Visiting the states is not the whole cost: every state has one successor for each input value (11 free input bits for the FIFO, so 2,048 successors), and the visited set must be stored. The tool therefore reasons about sets of states symbolically. Assumptions shrink the input space; for independent input fields the kept fractions multiply, and each excluded class must be verified elsewhere or the proof has a hole shaped exactly like the exclusion.

    Worked example: counting the spaces

    The first script derives the FIFO figures: 2 to the 39 states is 549,755,813,888, visited in about 550 seconds at a billion a second; times 2,048 input choices it is 1.1 times 10 to the 15 transitions, about 13 days; one bit per state of storage is 64 GiB. The 158-bit example (three 8-bit registers, a 4-bit counter, a 2-bit machine and a 16 by 8 memory) needs about 10 to the 31 years. The check is applied to the correct functions and to a mutant that squares the bit count, which it catches.

    example.py
    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
    27
    28
    def states(bits):
        return 2 ** bits
    
    def years(bits):
        return states(bits) / 1e9 / 31_557_600
    
    def check(states_fn, years_fn):
        assert states_fn(39) == 549755813888
        assert states_fn(24) == 16777216 and states_fn(18) == 262144
        seconds = states_fn(39) / 1e9
        assert 549 < seconds < 550
        inputs = 1 + 1 + 8 + 1
        assert 2 ** inputs == 2048
        days = states_fn(39) * 2 ** inputs / 1e9 / 86400
        assert 13.0 < days < 13.1
        assert states_fn(39) // 8 == 64 * 2 ** 30
        bits = 24 + 4 + 2 + 128
        assert bits == 158
        assert 1.1e31 < years_fn(bits) < 1.2e31
    
    check(states, years)
    caught = False
    try:
        check(lambda b: b * b, years)
    except AssertionError:
        caught = True
    assert caught
    print("PASS: 2^39 is 550 s of visiting, 13 days with the input choices, 64 GiB of storage; 158 bits need about 1e31 years; a wrong power is caught")

    How assumptions shrink the input space

    A 4-bit opcode with the assumption “opcodes above 10 never occur” keeps 11 of 16 values. A word-alignment assumption on the two low address bits keeps 1 of 4. For independent fields the kept fractions multiply: 11/16 times 1/4 is 11/64, about 17.2 percent, so 82.8 percent of the combined space is excluded and every bug that hides there is invisible. The product holds only for independent fields: an assumption that couples them, such as “an unaligned address is allowed only with opcode 0”, keeps 14/64, not 11/64. The script enumerates the combinations, checks the product, and catches the calculator that forgets to multiply.

    example.py
    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
    27
    from fractions import Fraction as F
    
    def kept(opcode_ok, address_ok):
        ok = total = 0
        for opcode in range(16):
            for low in range(4):
                total += 1
                ok += 1 if (opcode_ok(opcode) and address_ok(opcode, low)) else 0
        return F(ok, total)
    
    independent = kept(lambda o: o <= 10, lambda o, low: low == 0)
    coupled = kept(lambda o: o <= 10, lambda o, low: low == 0 or o == 0)
    
    def check(combine):
        assert independent == combine(F(11, 16), F(1, 4)) == F(11, 64)
        assert abs(float(independent) - 0.171875) < 1e-12
        assert 1 - independent == F(53, 64)
        assert coupled == F(14, 64) != combine(F(11, 16), F(1, 4))
    
    check(lambda a, b: a * b)
    caught = False
    try:
        check(lambda a, b: a)
    except AssertionError:
        caught = True
    assert caught
    print("PASS: 11 of 64 combinations remain for independent fields, 14 of 64 when the fields are coupled; forgetting the product is caught")

    What the review looks for

    • State counts are quoted as powers of two with the reachable share beside the total.

    • No plan proposes enumeration without counting the input choices and the storage.

    • Every assumption that removes input space names the excluded class and where it is verified instead.

    Verify and debug

    Recompute the kept fraction whenever an assumption is added: the excluded share is where the next bug hides. If a proof is suspiciously quick after a new assumption, suspect the fraction moved, not the engine. Record each excluded class in the assumption register, the list of every assumption with the reason it is safe and where it is checked.

    Judging a design by what the outputs can show. Symptom: a clean range check beside a wrong design. Cause: one planted bug (the count drifts away from the pointers when a push and a pop meet) leaves the outputs plausible, so the range check never fails within 12 steps; only an internal consistency property on the pointers fails, at step 3. Fix: add invariants over internal state, and score properties by mutation, not by state count. Found by running every planted bug against every property.

    “How many states?” Answer: total from the bits, reachable from reset, and the share. Then the punchline: a billion states a second needs about 10 to the 31 years for 158 bits, so the tool must work symbolically. Quote the FIFO: 2 to the 39 in total, 15.6 percent reachable.

    Check yourself Two independent assumptions keep 11/16 and 1/4 of the space. What share remains, and what must the reviewer demand?

    11/64, about 17.2 percent. The reviewer demands each excluded class named with where it is verified instead: opcodes above 10 and unaligned addresses are now invisible here.

    Check yourself Why is forgetting to multiply the classic miscount, and when does the product itself fail?

    Each assumption compounds, so 68.8 percent kept and then a quarter of that leaves 17.2 percent, not 68.8. The product assumes independent fields; coupled assumptions must be enumerated.

    Related topics

    • Earlier: The Formal Engineer's Split

    • Later: Abstraction Techniques

    Previous

    13 / 183

    Next: Reset, Initial State and the First Cycle

    Up next

    Up nextReset, Initial State and the First Cycle

    Sign in to save progress

    Practice this lessonPrevious lessonReport an error

    See also

    • State, Inputs and the Transition Relation
    • Safety Properties as Invariants