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.
# 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 0The 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.
# 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# 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.5403. Plain Tcl Has No >
Look at the canonical example for this chapter — the kind of line found in real timing scripts:
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:
#!/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/^/ /' 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 directoryputs 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.
# 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
}#!/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/^/ /' 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.rptThree 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.
# 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).
#!/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 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 reportThe 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
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 provides | So that Perl can | Perl's check |
|---|---|---|
| one report per corner, named by the corner | map a file to its corner without parsing the content | the name matches the Corner : line inside |
| an end marker in every report | tell a finished report from a truncated one | the last non-blank line is the marker |
| reports written to a temporary name, then renamed | never read a half-written report | ignore *.tmp; a lingering .tmp is itself a finding |
| a manifest of every requested corner and its outcome | tell "failed" from "never requested" | every manifest corner has a report or a failure reason |
| layout options set in the script | parse the same shape every run | the header and field shapes match the expected version |
| a non-zero exit when anything failed | let the job go red without reading files | the 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. > fileafter a command is the tool shell's feature, not Tcl's. Measured in plaintclsh: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.
