Skip to content
VLSI Mentor

Tcl · Module 29

Making a Flow Fail Loudly

A broken run and a broken sign-off cost the same compute. Only one of them produces a number somebody puts their name to, and gates are what decides which you get.

Advanced21 min readtcledapart-iidebuggingerror-handlingsignoff

Part II · Module 29 · Chapter 29.4 · Making a Flow Fail Loudly

Chapter 29.1 catalogued the failures and the assertions that catch them. This chapter is about the structure those assertions live in: gates between phases.

1. A broken run versus a broken sign-off

The registry's phrase for this chapter is worth taking literally.

2. Ungated: every phase runs

The mock below has five phases, each producing something the next one needs: read, elaborate, constrain, optimise, report.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
proc run_ungated {files sdc} {
    ::g::reset
    phase_read $files
    phase_elaborate
    phase_constrain $sdc
    phase_optimise
    set viol [phase_report]
    return [expr {$viol == 0 ? "PASS" : "FAIL"}]
}

Nothing in that is unusual, and nothing in it is checked.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== ungated: every phase runs, verdict is the last one's ===
    CASE                 VERDICT  WHAT THE PHASES DID
    everything-present   FAIL     read 1 of 1 file(s) | elaborated 4 cell(s) | constraints applied: 1 | optimised 4 cell(s) | reported 2 violation(s)
    rtl-path-wrong       PASS     read 0 of 1 file(s) | elaborated 0 cell(s) | constraints applied: 1 | optimised 0 cell(s) | reported 0 violation(s)
    sdc-path-wrong       PASS     read 1 of 1 file(s) | elaborated 4 cell(s) | constraints applied: 0 | optimised 4 cell(s) | reported 0 violation(s)
    no-files-at-all      PASS     read 0 of 0 file(s) | elaborated 0 cell(s) | constraints applied: 1 | optimised 0 cell(s) | reported 0 violation(s)

Three of four broken runs report PASS, and the phase log says precisely why each one did:

  • rtl-path-wrong — read 0 of 1, so elaborated 0 cell(s). There is no design, so there is nothing to violate.
  • sdc-path-wrong — constraints applied: 0. There is a design and no requirement, so nothing is violated.
  • no-files-at-all — read 0 of 0. The flow was given an empty file list and found all of it.

Every phase "succeeded". phase_read returned a count, and a count of zero is a count. phase_constrain returned 0, and nobody looked. The verdict is the last phase's output, and the last phase correctly reported that an empty unconstrained design has no violations.

Note read 0 of 1 and read 0 of 0 are different failures wearing the same clothes: a wrong path, and a caller that passed nothing. The of N denominator is what separates them — Chapter 22.7 §4's rule, earning its keep here.

3. Gated: stop at the phase that failed

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
proc gate {name value message} {
    if {!$value} {
        error "gate after '$name' failed: $message" {} [list GATE $name]
    }
    return $value
}

proc run_gated {files sdc} {
    ::g::reset
    gate read      [phase_read $files]    "no input file was readable"
    gate elaborate [phase_elaborate]      "the design elaborated to zero cells"
    gate constrain [phase_constrain $sdc] "no constraints were applied"
    gate optimise  [phase_optimise]       "optimisation touched nothing"
    set viol [phase_report]
    gate report [dict exists $::g::state violations] "no report was produced"
    return [expr {$viol == 0 ? "PASS" : "FAIL"}]
}
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== gated: the flow stops at the phase that failed ===
    CASE                 RESULT
    everything-present   FAIL     (5 phases)
    rtl-path-wrong       STOPPED: gate after 'read' failed: no input file was readable
                                  phases completed: 1
    sdc-path-wrong       STOPPED: gate after 'constrain' failed: no constraints were applied
                                  phases completed: 3
    no-files-at-all      STOPPED: gate after 'read' failed: no input file was readable
                                  phases completed: 1

Four properties of that output are worth naming.

The correct case still runs all five phases and still reports FAIL — correctly, because the design has two real violations. A gate set that breaks the working case is one somebody removes (Chapter 23.1 §6).

Each stop names its phase. gate after 'constrain' is the location, in the first line, without reading a log.

The phases-completed count locates it independently. 1 for a read failure, 3 for a constraint failure — two numbers that say which phase failed even if the message is lost, truncated or buried. That is a second, independent signal about the same fact, which is Chapter 24.7 §5's principle applied to a failure report.

gate takes the value, not an expression. gate read [phase_read $files] runs the phase and hands its result to the gate, so the phase cannot be skipped by a short-circuit and the gate cannot be written without the phase. Two things that belong together are syntactically together.

4. The gate that must not be a truth test

5. Where gates go, and where they do not

Between phases, immediately after the phase they are about. Chapter 29.1 §6's locality argument: a gate at the end reports "the report is empty", three phases from the cause.

Not inside a phase. A phase that gates its own internals is a phase whose contract is unclear — the gate belongs at the seam, where the question is "did this produce what the next phase needs".

Not on every intermediate value. Gating each of a thousand cells is both slow and noise. Gates belong where a phase hands something to the next phase, and that is a handful of places.

And one gate per claim, not one per phase. The constraint phase has two claims — the call was made, and the state changed — and Chapter 29.1 §5 checks both. Collapsing them loses the distinction between "the file was unreadable" and "the file was read and contained nothing applicable".

6. Turning a gate into an exit status

A gate that raises is only half the mechanism. The flow has to exit with something the caller can act on, which is Chapter 27.1 §7's split:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
if {[catch {run_gated $files $sdc} result]} {
    puts stderr "flow stopped: $result"
    puts stderr "phases completed: [llength $::g::log]"
    foreach line $::g::log { puts stderr "  $line" }
    # a GATE error is a flow failure; anything else is a tool or script fault
    if {[lindex $::errorCode 0] eq "GATE"} { exit 1 }
    exit 2
}
puts $result
exit 0

Three decisions there.

The phase log goes to stderr on failure. Not just the message — the whole sequence of what the phases did, because that is the §2 table and it is what makes the failure diagnosable without re-running. On success it is not printed, because nobody reads it then.

The errorCode distinguishes a gate from everything else. GATE means a phase did not produce what the next needed — a flow failure, exit 1. Anything else is a bug in the script or a fault in the tool — exit 2. Those go to different people, per Chapter 25.4 §7, and the distinction is free because Chapter 29.1 §5 put a code on every error.

exit 0 only on the success path, and note that FAIL — a design with real violations — also exits 0 here. That is deliberate and worth being explicit about: the flow worked. Whether the design meets timing is the verdict's business, and conflating "the flow broke" with "the design has violations" is how a regression driver comes to retry a genuine timing failure.

7. Exercises

Exercise 1. State the difference between a broken run and a broken sign-off in terms of cost.

Exercise 2. Three of four ungated cases reported PASS. Give the mechanism for each.

Exercise 3. Why did every phase "succeed" in those three cases?

Exercise 4. read 0 of 1 and read 0 of 0 are different failures. What distinguishes them and why does that matter?

Exercise 5. Why does the gated flow's first row matter?

Exercise 6. What does the phases-completed count give you that the message does not?

Exercise 7. Why does gate take a value rather than an expression?

Exercise 8. Why can the report gate not assert that violations exist?

Exercise 9. State the distinction between the two questions a gate and a verdict ask.

Exercise 10. Why one gate per claim rather than one per phase?

Exercise 11. Why does a design that fails timing exit 0 in §6, and when would you want otherwise?

Exercise 12. Why should the block-the-merge policy live in the caller rather than the flow?

Where this fits

Part of the Tcl curriculum.