Skip to content
VLSI Mentor

Tcl · Module 29

Debugging Inside the Tool Shell

A flow log records what the script did. A live session holds what the tool believes. For the failures in this module those differ, which makes the session the only instrument that works.

Advanced20 min readtcledapart-iidebugginginteractiveverification

Part II · Module 29 · Chapter 29.3 · Debugging Inside the Tool Shell

Chapter 29.1 showed a flow reporting PASS on a design with two real violations. The log for that run contains nothing wrong — every step reported success, because every step succeeded.

So the log cannot be the instrument. This chapter is about the one that can.

1. Why a log is the wrong instrument for this class of failure

And the corollary: once you have an assertion (Chapter 29.1 §5), the log does carry the fact, because the assertion put it there. Interactive debugging is how you find out what to assert.

2. Three helpers

Chapter 27.2 argued for one-letter names at the prompt. These are the three that matter for this class:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
# s — the session state, which is what you actually want to see.
proc s {} {
    if {[dict size $::g::state] == 0} { puts "  (no state)" ; return "empty" }
    dict for {k v} $::g::state { puts [format "  %-12s %s" $k $v] }
    return "[dict size $::g::state] key(s)"
}
# p — what the phases did, in order.
proc p {} {
    set i 0
    foreach l $::g::log { puts [format "  %d. %s" [incr i] $l] }
    return "$i phase(s)"
}
# f — does this path exist? The question behind most flow failures.
proc f {path} {
    return [format "%s  exists=%d isfile=%d" $path [file exists $path] [file isfile $path]]
}

s is the important one. In a real tool it is the handful of queries that summarise the session — how many cells, which clocks are defined, whether the timing graph is current, what the current design is. The principle is the same: one word that prints what the tool believes, so you can compare it against what the script intended.

3. A real session

Everything below is a genuine interactive session over a pty — expect spawned a real tclsh and typed these commands. Verbatim from the first prompt.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
% source /private/tmp/m29/flow/dbg.tcl
% run_ungated {rtl/core.sv} constraints/missing.sdc
PASS
% p
  1. read 1 of 1 file(s)
  2. elaborated 4 cell(s)
  3. constraints applied: 0
  4. optimised 4 cell(s)
  5. reported 0 violation(s)
5 phase(s)
% s
  files        rtl/core.sv
  cells        4
  constrained  0
  optimised    4
  violations   0
5 key(s)
% f constraints/missing.sdc
constraints/missing.sdc  exists=0 isfile=0
% f constraints/c.sdc
constraints/c.sdc  exists=1 isfile=1
% run_ungated {rtl/core.sv} constraints/c.sdc
FAIL
% s
  files        rtl/core.sv
  cells        4
  constrained  1
  optimised    4
  violations   2
5 key(s)
% exit

4. Reading that session

Six commands take a wrong verdict to a located cause. The sequence is worth walking, because it is the method.

run_ungated … missing.sdc → PASS. The reported symptom. Nothing else is known yet.

p → constraints applied: 0. The first real information. Five phases ran and all five "succeeded", but phase 3 applied zero constraints. Notice that this line was in the log too — and in a 40,000-line log, between elaborated 4 cell(s) and optimised 4 cell(s), neither of which is wrong, it reads as ordinary progress.

s → constrained 0, violations 0. This is the step the log cannot do. The state says the session has no constraints, which explains the verdict completely: zero violations is the honest answer for an unconstrained design. The verdict is no longer mysterious; it is arithmetic.

f constraints/missing.sdc → exists=0. The cause. One command, and it is a path problem rather than a tool problem, a constraint problem or a design problem.

f constraints/c.sdc → exists=1. The confirmation — the file the flow should have been given is there. Without this step the conclusion is "the path is wrong" and the fix is a guess; with it, the fix is known.

Re-run → FAIL, and s → constrained 1, violations 2. The real answer, and the proof that the diagnosis was right. Two violations, which is what Chapter 29.1's design actually has.

5. What a live session cannot tell you

The honest half, because an interactive session is persuasive and its results do not always transfer.

A live session is a different run. You sourced helpers, you ran a phase twice, you possibly changed a variable. The session's state is the result of your sequence, not of the batch run you are investigating — which is why Chapter 27.7's idempotency matters: a helper that is not safe to run twice makes your second experiment measure something new.

State accumulates across experiments. In the transcript above, run_ungated calls ::g::reset first, so each run starts clean. A real tool session does not reset itself, and an experiment run after another experiment carries the first one's state — which is Chapter 26.5 §3's leak, met interactively instead of in a loop. The discipline is the same: reset deliberately, or restart the session.

The inputs may have moved since the run. You are debugging last night's failure this morning, and the tree has changed. Chapter 29.2's identity check is what tells you whether your session is even looking at the same inputs — and if it is not, you are debugging a different problem.

A fix verified interactively is not a fix verified in batch. The session had your helpers loaded, your working directory, and whatever else you had done. Chapter 27.1 §4's lesson applies directly: a path that resolves at your prompt may not resolve from the batch driver's directory. The fix has to be re-verified by running the flow.

6. Getting into a session in the first place

The practical obstacle, which is worth naming because it determines whether any of this happens.

A flow that only runs end to end cannot be debugged interactively. If the only entry point is a six-hour driver, reproducing phase 3 means waiting for phases 1 and 2 — every time. The flows that are debuggable are the ones where a stage can be entered on its own, which is Chapter 27.1's -stage argument doing a second job: it is a debugging interface as much as a scheduling one.

The state has to be reachable. A tool session that has exited takes its state with it. The usual answers are to save a database at each stage boundary so a later stage can be entered without re-running the earlier ones, and to have the flow drop into an interactive prompt on failure rather than exiting — guarded by a flag, because in batch that is a hung job (Chapter 25.4 §9).

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
if {[catch {run_stage $stage} err]} {
    ::flow::log ERROR "stage $stage failed: $err"
    if {$::INTERACTIVE_ON_FAIL} {
        puts stderr "dropping to a prompt; the session state is intact"
        # the tool's own interactive loop here
    } else {
        exit 1
    }
}

The flag defaults to 0, and the comment matters: what makes the prompt useful is that the state is intact — the failure has happened and nothing has been cleaned up, so s shows the session exactly as the failure left it.

7. Exercises

Exercise 1. What does a flow log record, and what does a live session hold?

Exercise 2. When do those two diverge, and why is that this module's whole subject?

Exercise 3. Once an assertion exists, does the log carry the fact? What does that make interactive debugging for?

Exercise 4. p showed constraints applied: 0. Why was that line not noticed in the log?

Exercise 5. What did s establish that p could not?

Exercise 6. Why run f on the correct path as well as the wrong one?

Exercise 7. Why is the final re-run the step that makes the session worth having?

Exercise 8. What does the re-run give you that a diagnosis without it does not?

Exercise 9. Why does a helper that is not idempotent undermine interactive debugging?

Exercise 10. Why is a fix verified at the prompt not yet verified?

Exercise 11. What makes a flow debuggable at all, and what does -stage have to do with it?

Where this fits

Part of the Tcl curriculum.