A script that works for one design on one day is a recording. A flow script is a program: the design name, the clock period and the effort level are inputs, each step reports whether it worked, and a failed step does not leave you guessing. This lesson builds the pieces: a settings file, commands built from data, a few small library commands, command-line arguments, a step runner and checkpoints.
Reading time: about 9 minutes.
11.1 Keep settings in one file
Put the values that change from run to run in a file of their own, and load it with source.
# config.tcl
set period 2.0
set effort medium
source config.tcl
puts "$period $effort"
This prints 2.0 medium. Anyone who wants a different run edits the config file and not the flow. For several designs, keep one config file per design and pass its name as an argument, as Section 11.6 shows.
11.2 Building a command from data
Sometimes the command to run is assembled by the script. The safe way is to build it as a list, because list quotes each element.
set cmd [list report_timing -from ff_a -to "u b"]
puts $cmd
puts [llength $cmd]
This prints:
report_timing -from ff_a -to {u b}
5
The name u b has a space and stays one argument, which it would not if you had glued the text together with string operations. A list built this way can be run with eval $cmd, and in Tcl 8.5 and later the brace expansion in Section 11.4 is the neater way.
subst is the second tool. It performs variable and command substitution on a string that you have kept in braces, which makes a template.
set tpl {period=$period effort=$effort}
puts [subst $tpl]
This prints period=2.0 effort=medium. Templates suit report headers and file names.
11.3 Evaluating
eval joins its arguments into one script and runs it. It is rarely needed now, and it has a danger: if the pieces are not lists, a space inside a value splits it. Prefer {*} or a procedure. When you do use eval, pass a list that was built with list.
11.4 Brace expansion: {*}
The expansion operator {*} turns a list into separate arguments. It is the right way to pass stored options to a command.
set opts {-from ff_a -to ff_b}
puts [llength [list report_timing {*}$opts]]
puts [llength [list report_timing $opts]]
This prints 5 and then 2. With {*} the four options are separate words. Without it, the whole list is one argument, and a command that expects -from as its own word would reject it. The same operator is how a flow script keeps one list of common options and adds it to several commands. Lesson 4 introduced it, and this is where it earns its place.
11.5 Small library commands
A few commands appear in almost every flow script.
puts [clock format 0 -gmt 1 -format {%Y-%m-%d %H:%M:%S}]
puts [package require Tcl 8.5-]
puts [catch {package require nosuchpkg} err]
puts $err
This prints:
1970-01-01 00:00:00
8.6.12
1
can't find package nosuchpkg
clock format turns a time in seconds into text, and clock seconds gives the current time, which names a log file or stamps a report. The example uses time zero with -gmt 1 so the output does not change between runs. package require Tcl 8.5- returns the version of the interpreter, here 8.6.12 in the test run, and raises an error if it is older than the version you ask for. That makes it a good first line for a script that uses lmap or try.
11.6 Taking arguments
When a script is started with tclsh script.tcl arg1 arg2, the arguments are in argv, their number is in argc, and the script name is in argv0. A script that checks them can be reused.
puts $argc
puts $argv
puts $argv0
Run as tclsh argtest.tcl in.rpt 0.1, it prints 2, in.rpt 0.1 and argtest.tcl. Check argc first and print a usage line, as Lesson 9 did. Whether a tool sets argv for a script depends on how it was started, so print the variable once in your environment before depending on it. For a design tool, the more usual route is to set the variables in a small wrapper file that sources the flow.
11.7 A step runner
A flow is a list of steps. A runner executes each one, catches errors, and prints a table at the end. A failing step is recorded and the flow decides what to do next.
proc run_step {name body} {
if {[catch {uplevel 1 $body} msg]} { return [list $name FAIL $msg] }
return [list $name OK $msg]
}
set steps {
{step_a {string cat a { done}}}
{step_b {error "library missing"}}
}
set failed 0
foreach s $steps {
lassign $s name body
set r [run_step $name $body]
puts [format "%-8s %-5s %s" {*}$r]
if {[lindex $r 1] eq "FAIL"} { incr failed }
}
puts "failed=$failed"
This prints:
step_a OK a done
step_b FAIL library missing
failed=1
The body runs in the caller's scope with uplevel, so steps can use the flow's variables. The runner combines catch from Lesson 6, lassign from Lesson 4, format from Lesson 5 and {*} from this lesson. At the end, a non-zero exit when failed is above zero tells the shell script or scheduler that the flow did not finish cleanly. Appendix B has an exercise that extends it.
11.8 A timing wrapper
A wrapper takes a script as an argument, runs it in the caller, and reports around it.
proc timed {label body} {
set t0 [clock milliseconds]
set rc [catch {uplevel 1 $body} res]
set dt [expr {[clock milliseconds] - $t0}]
puts "$label done"
return -code $rc $res
}
set x 0
timed loop { for {set i 0} {$i < 1000} {incr i} { incr x } }
puts $x
This prints loop done and 1000. The elapsed time is in dt. A real version would print it, but the number differs on every run, so the example leaves it out. return -code $rc $res passes on whatever the body did, including an error, so the wrapper does not hide failures.
11.9 A small Innovus example
A flow can save a checkpoint after each major step so a failed step does not mean a restart from the beginning. The reference states that saveDesign writes a restore script with the name you give plus a data directory, and that you source that script later, or use restoreDesign. To be run at the Innovus prompt. Check the options with saveDesign -help and restoreDesign -help.
Legacy UI.
source -echo config.tcl
redirect flow.log "timeDesign -postCTS" -append -tee
if {[catch {saveDesign after_cts.enc} msg]} {
puts "checkpoint failed: $msg"
exit 1
}
The first line echoes the config file into the log, which records the settings used. The redirect pattern comes from the Legacy reference and sends the timing step to the screen and to a log. The catch around saveDesign is the one from Lesson 6. In a later run, source after_cts.enc or restoreDesign brings the saved state back, and the options are listed in the reference. Keep a checkpoint before every large step.
11.10 Why an interviewer asks about this
Typical questions: "how do you make a flow script reusable across designs", "what does {*} do" and "how do you keep going when one step fails". A strong answer describes a config file loaded by source, arguments from argv, {*} for option lists, and a step runner with catch that records failures and exits non-zero at the end. A follow-up may ask what eval does and why list is safer than string gluing, and the answer is that list quotes each element.
Key takeaways
- Keep run-to-run settings in a config file and
sourceit, or pass its name as an argument. - Build commands as lists, expand stored options with
{*}, and usesubstfor templates. package require Tcl 8.5-at the top documents the version a script needs.- A step runner with
catchanduplevelrecords failures, and the flow ends with a non-zeroexitwhen any step failed.