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
  • Properties and SVA16
  • Property Development and Constraints19
  • Proof Engines and Convergence23
  • Formal Applications20
  • Equivalence Checking14
  • Formal Coverage and Signoff15
  • Methodology and Debug13
    • The Formal Testplan
    • Managing Complexity in a Project
      • Diagnosing a Slow Proof
      • Splitting Properties and Parameters
      • Runtime Budgets and Timeouts
    • Debugging Formal Results
    • Formal in Regression
  • Interview Prep15
  • Capstone: A Chip-Block Verification Lab24
Loading lesson

←Managing Complexity in a Project
  1. Topics
  2. Formal Verification
  3. Methodology and Debug
  4. Managing Complexity in a Project
  5. Runtime Budgets and Timeouts
On this page

    Part 8 of 10, lesson 6 of 13Part 8 of 10/Methodology and Debug/Lesson 6 of 13All levels

    Runtime Budgets and Timeouts

    Budgets per property and per run, parallel tasks, and a timeout reported as inconclusive, never as a pass.

    Why it matters

    A nightly run hit its time limit on three properties and the dashboard counted them as not-failed, which the team read as proven for a month. The properties covered the reset values of a data register. The bug that shipped was a wrong reset value. The failure was not the timeout; timeouts happen. The failure was the reporting.

    Job of this lesson: set budgets that fit the work, and make a timeout impossible to misread.

    The core idea

    Every property gets a time budget and every run gets a total budget; small bounded checks and covers run per commit in minutes, full proofs run nightly. Parallel tasks split the set so one slow property cannot starve the rest. The result vocabulary has exactly three outcomes, proven, counterexample and inconclusive, and a timeout or an undecided engine lands in the third. The ledger records the outcome plus the depth, so a bounded result is never filed where a proof belongs.

    Set

    Contents

    Budget (illustrative)

    On timeout

    Per-commit

    Small bounded checks and covers

    Minutes

    Block the commit, file inconclusive

    Nightly

    Full proofs to the planned depth

    Hours across parallel tasks

    File inconclusive, triage by morning

    Release

    Rerun of every proof on final RTL

    As needed

    No signoff until clear

    Worked example: the planner and the reporting rule

    The planner below assigns budgets from the property list and places them on the parallel tasks inside the nightly window. A total that fits is necessary but not sufficient: three 100-minute properties on two tasks with a 150-minute window total 300 minutes against 300 available, and cannot be placed, so the planner packs them first-fit rather than only adding. Budgets are illustrative planning numbers, not measured runtimes. The second half is the reporting rule: a function that takes a result line, as a flow writes it, and rejects a proven line whose reason is a timeout, a line with no depth, and any status outside the three outcomes. It is applied to a clean line and to three mislabelled ones:

    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
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    props = [
        {"name": "a_id_const", "budget_min": 5},
        {"name": "a_w1c_done", "budget_min": 5},
        {"name": "a_conserve", "budget_min": 60},
        {"name": "a_data", "budget_min": 120},
    ]
    
    def total_fits(props, window_min, tasks):
        return sum(p["budget_min"] for p in props) <= window_min * tasks
    
    def pack(props, window_min, tasks):
        loads = [0] * tasks
        for p in sorted(props, key=lambda p: -p["budget_min"]):
            for i in range(tasks):
                if loads[i] + p["budget_min"] <= window_min:
                    loads[i] += p["budget_min"]
                    break
            else:
                return False
        return True
    
    assert pack(props, 240, 2)
    greedy = [dict(p) for p in props]
    greedy[3]["budget_min"] = 500
    assert not pack(greedy, 240, 2)
    three = [{"name": "p" + str(i), "budget_min": 100} for i in range(3)]
    assert total_fits(three, 150, 2) and not pack(three, 150, 2)    # necessary, not sufficient
    
    OUTCOMES = ("proven", "counterexample", "inconclusive")
    
    def check_line(line):
        if line.get("status") not in OUTCOMES:
            raise ValueError("status is not one of the three outcomes")
        if line.get("depth") is None:
            raise ValueError("no depth recorded")
        if line["status"] == "proven" and line.get("reason") in ("timeout", "budget", "killed"):
            raise ValueError("a timeout cannot be filed as proven")
        return line
    
    def rejects(line):
        try:
            check_line(line)
        except ValueError:
            return True
        return False
    
    def file_result(raw, depth):
        status = {"pass": "proven", "fail": "counterexample"}.get(raw, "inconclusive")
        return {"status": status, "depth": depth, "reason": raw}
    
    assert not rejects({"status": "proven", "depth": 8, "reason": "induction closed"})
    assert file_result("timeout", 40)["status"] == "inconclusive"
    assert not rejects(file_result("timeout", 40))
    assert rejects({"status": "proven", "depth": 40, "reason": "timeout"})
    assert rejects({"status": "proven", "reason": "induction closed"})
    assert rejects({"status": "timeout-pass", "depth": 40})
    print("PASS: budgets are packed, not just added; a timeout filed as proven is rejected")

    What the review looks for

    • Every property has a budget, and the nightly plan packs onto the tasks inside the window, not merely adds up.

    • The ledger shows three outcomes only, each with a depth; no timeout is filed as proven.

    • A property that times out has a triage note: reduce, split, strengthen, or a justified bound.

    Verify and debug

    Inject a tiny time limit once per quarter and read the summary: if anything downstream counts it as a pass, the reporting chain is broken. When a property first exceeds its budget, triage it as a slow proof before raising anything; budgets are set from history, and history is allowed to say the proof needs work.

    A timeout reported as a pass: the summary counts not-failed as proven and the dashboard shows no failures over stalled coverage. Symptom is a release with unproven properties nobody triaged. Cause is a parser or a habit that treats anything without a failure as a pass. Fix: three-way results with a bounded-depth column, plus a check that refuses the mislabelled line. Found by injecting a tiny time limit and reading the summary.

    “Your nightly run times out on two properties. What do you do?” File both inconclusive, keep the release blocked on them, and triage as slow proofs: measure cone, depth and engine progress, then reduce, split or strengthen. Raising the limit needs observed progress; tightening assumptions needs a written justification. Never re-label them as passes.

    Check yourself The per-commit set takes twenty minutes and engineers start skipping it. What changes?

    Shrink the set, not the standard: keep small bounded checks and covers per commit, move full proofs to nightly, and parallelise so the commit set finishes in minutes.

    Check yourself A proof needs a larger depth than its budget allows. What are the honest options?

    Split or reduce so it fits, justify a bound at the depth reached, or record it inconclusive and carry it openly. Filing it as proven at a depth it never reached is not an option.

    Related topics

    • Earlier: Diagnosing a Slow Proof

    • Later: CI for Formal

    Previous

    137 / 183

    Next: Debugging Formal Results

    Up next

    Up nextDebugging Formal Results

    Sign in to save progress

    Practice this lessonPrevious lessonReport an error

    See also

    • Diagnosing a Slow Proof