Skip to content
VLSI Mentor

Perl Part II · Module 26 · Perl with Bash, Tcl, and EDA Tools

Tcl Drives the Tool

In most signoff flows, Perl never talks to the timing tool. The tool is driven by a Tcl script running inside it, and that script writes the per-corner reports Perl later reads. Perl's input is therefore whatever that script decided to produce — which corners, under which names, in what format, and with what evidence of failure. This chapter looks at the Tcl half from the Perl engineer's side, using a small teaching mock because no timing tool is available here, and measures how the obvious corner loop hands Perl an incomplete directory with nothing to say so.

Intermediate21 min readPerlPart IIEDA Flows

Part II · Module 26 · Chapter 26.4 · Tcl Drives the Tool

1. Two Languages, One Hand-off

Timing tools, synthesis tools and place-and-route tools from the major vendors are all driven by Tcl. Each one embeds a Tcl interpreter and adds its own commands — reading a design, choosing an operating corner, reporting timing — to the standard language. A batch run starts the tool with a script, and the script does the work.

Perl's role is downstream of that. The Tcl script runs inside the tool, holding the design in memory; Perl runs afterwards, outside the tool, holding only the files the script wrote. Nothing passes between them except those files and the tool's exit status.

That makes the Tcl script the author of Perl's input. When the Perl parser in Chapter 26.5 finds two reports where it expected three, the cause is almost always here — and the decisions that make the difference between "Perl can tell what happened" and "Perl silently reports two clean corners" are made in this script, not in the parser.

So a Perl engineer in a signoff team needs to read and review this script even though they will rarely write it. This chapter is that review.

2. A Teaching Mock, Not a Timing Tool

No timing tool exists on the machine these examples were run on, and inventing output for a real vendor's command would publish something nobody can verify. So this chapter uses a teaching mock: a plain Tcl program, run with the standard tclsh, that adds three commands — read_design, set_corner and report_timing — and then runs a script, the way a tool started in batch mode does.

It is shown in full so that nothing about it is hidden. It is not a model of any real tool's behaviour beyond the general shape: commands added to Tcl, a design loaded once, a corner selected, a report produced. Its report is the synthetic format this module defined in Chapter 26.1.

Azvya Education Pvt. Ltd.VLSI Mentor
mock_sta.tcl
# mock_sta.tcl -- a TEACHING MOCK of a timing tool's Tcl shell.
#
# It is not PrimeTime or any other real tool, and the report it writes is the
# synthetic format defined for this module. It exists so that the Tcl half of
# a signoff flow can be run for real on a machine with no EDA tools: it adds
# three commands to a standard tclsh, then sources the script given with -f.
#
#   read_design NAME                      pretend to load a design
#   set_corner NAME                       load corners/NAME.tim, or fail
#   report_timing ?-max_paths N? ?> FILE? print the report, or write it to FILE
#
# Real tool shells provide `command > file` redirection themselves. Plain Tcl
# has no such syntax, so this mock implements it inside report_timing only.

set ::design ""
set ::corner ""
set ::paths  {}

proc read_design {name} {
    set ::design $name
    set f [open loads.log a]
    puts $f "loaded $name"
    close $f
    puts "mock_sta: design $name loaded"
}

proc set_corner {name} {
    set file corners/$name.tim
    if {![file exists $file]} {
        error "set_corner: no timing data for corner $name ($file)"
    }
    set ::corner $name
    set ::paths {}
    set f [open $file r]
    while {[gets $f line] >= 0} {
        if {[string trim $line] eq "" || [string index $line 0] eq "#"} continue
        lappend ::paths $line
    }
    close $f
}

proc report_timing {args} {
    if {$::corner eq ""} { error "report_timing: no corner set" }
    set max [llength $::paths]
    set out ""
    while {[llength $args]} {
        set a [lindex $args 0]
        set args [lrange $args 1 end]
        switch -- $a {
            -max_paths { set max [lindex $args 0]; set args [lrange $args 1 end] }
            >          { set out [lindex $args 0]; set args [lrange $args 1 end] }
            default    { error "report_timing: unknown option $a" }
        }
    }
    set text "Report : timing (synthetic teaching format)\n"
    append text "Design : $::design\nCorner : $::corner\n"
    foreach p [lrange [lsort -real -index 2 $::paths] 0 [expr {$max - 1}]] {
        foreach {start end slack} $p break
        if {$slack < 0} { set st "(VIOLATED)   " } else { set st "(MET)         " }
        append text "\nStartpoint: $start\nEndpoint  : $end\n"
        append text "  slack $st[format %.3f $slack]\n"
    }
    append text "\nEnd of report\n"
    if {$out eq ""} {
        puts -nonewline $text
    } else {
        set f [open $out w]
        puts -nonewline $f $text
        close $f
    }
}

# ---- batch mode, like a tool started with a script: tclsh mock_sta.tcl -f SCRIPT
if {[llength $argv] != 2 || [lindex $argv 0] ne "-f"} {
    puts stderr "usage: tclsh mock_sta.tcl -f SCRIPT.tcl"
    exit 2
}
if {[catch {source [lindex $argv 1]} msg]} {
    puts stderr "mock_sta: Error: $msg"
    exit 1
}
exit 0

The mock's "corner data" is a small text file per corner. A real tool reads libraries and parasitics for a corner; the mock reads three synthetic paths. There is deliberately no data file for the fast corner, ff_0p88v_m40c, which stands in for the most common real failure of a corner loop: a library or parasitics file that is missing for one corner.

Azvya Education Pvt. Ltd.VLSI Mentor
corners/ss_0p72v_125c.tim
# synthetic path data for the teaching mock: startpoint endpoint slack
u_core/r_reg[3]     u_core/acc_reg[7]   -0.043
u_core/cnt_reg[0]   u_core/cnt_reg[5]    0.120
u_core/a_reg[1]     u_core/acc_reg[2]    0.301
Azvya Education Pvt. Ltd.VLSI Mentor
corners/tt_0p80v_25c.tim
# synthetic path data for the teaching mock: startpoint endpoint slack
u_core/r_reg[3]     u_core/acc_reg[7]    0.212
u_core/cnt_reg[0]   u_core/cnt_reg[5]    0.388
u_core/a_reg[1]     u_core/acc_reg[2]    0.540

3. Plain Tcl Has No >

Look at the canonical example for this chapter — the kind of line found in real timing scripts:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
foreach corner $corners {
    report_timing -nosplit > reports/$corner.rpt
}

That > is not Tcl. In standard Tcl, > after a command is simply another argument, and the command receives it like any other word:

Azvya Education Pvt. Ltd.VLSI Mentor
plain_tcl_redirect.sh
#!/bin/bash
printf 'puts hello > out.txt\n' > redirect.tcl
tclsh redirect.tcl 2>&1 | sed 's/^/  /'
echo "  tclsh exit: ${PIPESTATUS[0]}"
ls out.txt 2>&1 | sed 's/^/  /'
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
  bad argument "out.txt": should be "nonewline"
      while executing
  "puts hello > out.txt"
      (file "redirect.tcl" line 1)
  tclsh exit: 1
  ls: out.txt: No such file or directory

puts received three arguments — hello, >, out.txt — and failed on them. No file was created.

Tool shells add this redirection themselves. That is the first concrete lesson about the Tcl half: a tool script is standard Tcl plus whatever the tool's shell adds, and the additions are not all commands. Redirection with >, and in many tools a redirect command as well, belong to the shell you are in. A script that works inside the tool may fail in a plain tclsh, and a fragment tested in tclsh may behave differently inside the tool. Confirm the redirection syntax in your own tool's documentation; the mock above implements only report_timing ... > FILE, because that is all this chapter needs.

4. The Obvious Corner Loop

Here is the script most people write first. It takes the list of corners from the environment — Chapter 26.2's channel for a setting the whole job shares — loads the design once, and writes one report per corner.

Azvya Education Pvt. Ltd.VLSI Mentor
sta_corners_v1.tcl
# sta_corners_v1.tcl -- the obvious corner loop
set corners $::env(CORNERS)
read_design chip_top
foreach corner $corners {
    set_corner $corner
    report_timing -max_paths 2 > reports/$corner.rpt
}
Azvya Education Pvt. Ltd.VLSI Mentor
run_v1.sh
#!/bin/bash
mkdir -p reports
export CORNERS="ss_0p72v_125c ff_0p88v_m40c tt_0p80v_25c"
tclsh mock_sta.tcl -f sta_corners_v1.tcl 2>&1 | sed 's/^/  /'
echo "  tool exit: ${PIPESTATUS[0]}"
echo "  reports written:"
ls reports | sed 's/^/    /'
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
  mock_sta: design chip_top loaded
  mock_sta: Error: set_corner: no timing data for corner ff_0p88v_m40c (corners/ff_0p88v_m40c.tim)
  tool exit: 1
  reports written:
    ss_0p72v_125c.rpt

Three corners requested, one report written. The fast corner failed — expected, its data is missing — and the typical corner, which had perfectly good data, was never attempted. An uncaught Tcl error stops the whole script, so everything after the failing corner is lost.

Now look at it from Perl's side, the next morning. The reports directory holds one file. Nothing in it says that two more were requested. If Chapter 26.5's parser simply globs reports/*.rpt, it reports one corner — with a violation, as it happens — and has no way to know that the other two exist only in the job's environment variable and the tool's log.

The mock's exit status was 1, so a wrapper that propagates it (Chapter 26.1) would at least mark the job as failed. But "the job failed" is not "the typical corner was never run". The information Perl needs is which corners exist, and the script never wrote it down.

5. The Loop Perl Can Rely On

The fixed version changes four things. Each one exists so that the Perl stage can tell what happened without reading the tool log.

Azvya Education Pvt. Ltd.VLSI Mentor
sta_corners_v2.tcl
# sta_corners_v2.tcl -- a corner loop whose output Perl can trust
set corners $::env(CORNERS)
read_design chip_top
file mkdir reports

set manifest [open reports/manifest.txt.tmp w]
set failed 0
foreach corner $corners {
    if {[catch {
        set_corner $corner
        report_timing -max_paths 2 > reports/$corner.rpt.tmp
        file rename -force reports/$corner.rpt.tmp reports/$corner.rpt
    } msg]} {
        incr failed
        puts $manifest "$corner FAILED $msg"
        puts "corner $corner FAILED: $msg"
        continue
    }
    puts $manifest "$corner OK reports/$corner.rpt"
}
close $manifest
file rename -force reports/manifest.txt.tmp reports/manifest.txt

if {$failed} {
    puts "$failed of [llength $corners] corners failed"
    exit 3
}

Each corner is wrapped in catch. A failure is recorded and the loop continues, so one missing library costs one corner, not every corner after it.

Each report is written to a temporary name and renamed when complete. If the tool dies part-way through writing a report — killed by the queue, out of memory — the half-written file keeps its .tmp name and is never mistaken for a finished report. This is Chapter 13.13's atomic write, in Tcl. The synthetic report also ends with an End of report line, so Perl has a second, independent check (Chapter 21.2's argument: the evidence of an unfinished file is a missing last line).

A manifest lists every requested corner and its outcome. This is the most important change. It is the only place where a corner that failed is recorded alongside the corners that succeeded, so it is the only thing that lets Perl distinguish "this corner failed" from "this corner was never requested". The manifest itself is written through a temporary name, so its presence means the loop finished.

The script exits non-zero when any corner failed, with its own documented code. The manifest says which corners; the exit status makes sure the job is not green. Two channels carrying the same fact is not redundancy for its own sake — they serve different readers (Chapter 26.1's CI job reads the number; Chapter 26.5's parser reads the file).

Azvya Education Pvt. Ltd.VLSI Mentor
run_v2.sh
#!/bin/bash
mkdir -p reports
export CORNERS="ss_0p72v_125c ff_0p88v_m40c tt_0p80v_25c"
tclsh mock_sta.tcl -f sta_corners_v2.tcl 2>&1 | sed 's/^/  /'
echo "  tool exit: ${PIPESTATUS[0]}"
echo "  reports directory:"
ls reports | sed 's/^/    /'
echo "  manifest:"
sed 's/^/    /' reports/manifest.txt
echo "  the typical corner's report:"
sed 's/^/    | /' reports/tt_0p80v_25c.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
  mock_sta: design chip_top loaded
  corner ff_0p88v_m40c FAILED: set_corner: no timing data for corner ff_0p88v_m40c (corners/ff_0p88v_m40c.tim)
  1 of 3 corners failed
  tool exit: 3
  reports directory:
    manifest.txt
    ss_0p72v_125c.rpt
    tt_0p80v_25c.rpt
  manifest:
    ss_0p72v_125c OK reports/ss_0p72v_125c.rpt
    ff_0p88v_m40c FAILED set_corner: no timing data for corner ff_0p88v_m40c (corners/ff_0p88v_m40c.tim)
    tt_0p80v_25c OK reports/tt_0p80v_25c.rpt
  the typical corner's report:
    | Report : timing (synthetic teaching format)
    | Design : chip_top
    | Corner : tt_0p80v_25c
    | 
    | Startpoint: u_core/r_reg[3]
    | Endpoint  : u_core/acc_reg[7]
    |   slack (MET)         0.212
    | 
    | Startpoint: u_core/cnt_reg[0]
    | Endpoint  : u_core/cnt_reg[5]
    |   slack (MET)         0.388
    | 
    | End of report

The fast corner still failed — nothing can fix a missing library from inside a loop — but the typical corner now has its report, the manifest records all three outcomes with the reason for the failure, and the exit status is 3 rather than 0 or 1.

That last report is exactly the shape Chapter 26.1's parse_sta.pl was written against, because both use the module's synthetic format. The Perl side of this hand-off is Chapter 26.5.

Figure 26.4.1 — What crosses from the tool to Perl

Three things leave the tool. Per-corner reports carry the numbers and are read by the Perl parser. A manifest lists every requested corner and its outcome, and the Perl parser uses it to check completeness. The exit status signals that a corner failed and is read by the wrapper and the CI job.reportsone file per cornermanifestevery corner, its outcomeexit statusnon-zero if one failedPerl parserreads the numbersPerl parserchecks completenesswrapper, CIred or green12
Three things leave the tool, and each answers a different question. The reports carry the numbers. The manifest says which corners were requested and what happened to each, which is the only way to tell a failed corner from one that was never asked for. The exit status tells the job that something went wrong, without saying what. Perl reads the first two; the wrapper and the CI job act on the third.

6. Options That Change the Report's Shape

The Tcl script decides more than which reports exist. Its options decide what the reports look like, and a Perl parser is written against one particular look.

The canonical example's report_timing -nosplit is the standard case. PrimeTime lays most report fields out in fixed-width columns, and when a value — typically a long hierarchical pin name — is wider than its column, the fields after it continue on a new line. -nosplit turns that line-splitting off, and PrimeTime's own documentation gives making the report easier for software to read as the reason to use it. A parser written against unsplit output, which expects a name and its numbers on one line, reads a split report as a line with a name and no numbers followed by a line with numbers and no name. Depending on how it is written, that is either a missed path or a mispaired one, and neither raises an error.

That gives a rule for the Tcl side: every option that affects layout belongs in the script, not in a user's interactive habits or a site-wide setup file, so that the reports Perl reads are produced the same way every time. Path counts (-max_paths here), significant digits, which columns are shown, how names are wrapped — each is a parser input. When a report option changes, the parser needs to know, which is the version-tolerance problem Chapter 18.8 addresses.

Names and options differ between tools and between versions of one tool, so treat every option named here as an example to confirm in your own tool's reference, not as a command to copy.

7. The Contract, as a Checklist

Put together, the Tcl half of the hand-off owes the Perl half six things. Each one is something a Perl stage can check, which is the point — a contract that cannot be checked is a hope.

The Tcl script providesSo that Perl canPerl's check
one report per corner, named by the cornermap a file to its corner without parsing the contentthe name matches the Corner : line inside
an end marker in every reporttell a finished report from a truncated onethe last non-blank line is the marker
reports written to a temporary name, then renamednever read a half-written reportignore *.tmp; a lingering .tmp is itself a finding
a manifest of every requested corner and its outcometell "failed" from "never requested"every manifest corner has a report or a failure reason
layout options set in the scriptparse the same shape every runthe header and field shapes match the expected version
a non-zero exit when anything failedlet the job go red without reading filesthe wrapper propagates it (26.1)

The first and fourth rows are the ones most often missing in practice, and they are cheap to add. Chapter 26.5 implements every check in the third column.

8. Exercises

1. Read the failure path. In sta_corners_v2.tcl, suppose report_timing itself fails half-way through writing reports/tt_0p80v_25c.rpt.tmp. List every file in reports afterwards, what the manifest says about the typical corner, and the exit status. Would any Perl check in section 7 be fooled? Then do the same for a tool killed by the queue during the loop, before manifest.txt.tmp is renamed.

2. Break the mock on purpose. Add a corner name containing a space — "tt 0p80v_25c" — to CORNERS and predict what each version of the script does with it. Then predict what happens if the environment variable is not set at all, and add the one-line check the script should have at the top. Which chapter in this module made the same argument for Perl?

3. Where does the corner list come from? The scripts read CORNERS from the environment. Give one advantage and one risk of that compared with writing the list into a small generated Tcl file that the corner script sources. What would you put in the run directory so that a reviewer six months later can see which corners were requested?

4. A second report per corner. A colleague adds report_constraint output for each corner, written to reports/$corner.cons. Extend the manifest format so Perl can tell, per corner, which of the two reports exist. Keep the format one line per corner and parseable with a single split.

5. Review a real script. Take a timing or synthesis script from a flow you have access to and check it against the table in section 7. For each missing row, write down the concrete failure it would allow — the specific corner, file or situation — rather than the general principle.

9. What To Remember

  • The tool runs the Tcl; Perl reads the files afterwards. Nothing else crosses but the exit status.
  • The Tcl script is the author of Perl's input, so a Perl engineer has to review it.
  • No timing tool exists here; every run on this page uses a labelled teaching mock in plain tclsh.
  • > file after a command is the tool shell's feature, not Tcl's. Measured in plain tclsh: bad argument "out.txt", no file.
  • The obvious corner loop stops at the first failing corner. Measured: three corners requested, one report written, the good typical corner never attempted.
  • A directory of reports cannot show what is missing from it.
  • Catch each corner, so one failure costs one corner.
  • Write each report to a temporary name and rename it (13.13), and keep an end marker in the format (21.2).
  • Write a manifest of every requested corner and its outcome — the only way to tell "failed" from "never requested".
  • Exit non-zero when anything failed, with a documented code. The manifest says which; the status says that.
  • Layout options are parser inputs. Set them in the script, every time (18.8).
  • Confirm every vendor option in your own tool's reference — names and behaviour vary by tool and version.

Chapter 26.5 is the other side of this hand-off: a Perl stage that reads the reports directory and the manifest, checks every row of section 7's table, and turns per-corner reports into one normalised CSV.

Where this fits

Part of the Perl curriculum.