Tcl · Module 17
Vendor Differences: Why a Script Is Not Portable
The Tcl is portable. The commands are not. Knowing exactly where that line falls is what lets you write a script that survives a tool change instead of being rewritten.
Intermediate18 min readtcledapart-iiportabilityvendorsadapter
Part II · Module 17 · Chapter 17.5 · Vendor Differences: Why a Script Is Not Portable
Chapter 16.4 drew the line between standard Tcl and tool commands. This chapter is about what happens on the far side of that line: the tool commands differ between vendors, and often between tools from the same vendor.
That is not a defect to complain about. It is a constraint to design around, and there is a good way to do it.
1. The same question, three command sets
One question: what is the design called, and which instances match u_*?
# alpha
puts " alpha : design=[current_design] cells=[get_cells u_*] count=[cell_count u_*]"
# beta
puts " beta : design=[get_design_name] cells=[find_instances -pattern u_*] count=[instance_count u_*]"
# gamma
puts " gamma : design=[design name] cells=[design cells u_*] count=[design count u_*]" alpha : design=cpu_top cells=u_fifo u_alu u_ctrl count=3
beta : design=cpu_top cells=u_fifo u_alu u_ctrl count=3
gamma : design=cpu_top cells=u_fifo u_alu u_ctrl count=3Identical answers. Not one command in common.
2. The three ways command sets differ
Those mocks were built to differ in the three dimensions that actually vary. Recognising which one you are facing tells you how much work a port is.
Different verbs for the same idea. get_cells versus find_instances. Both select instances by pattern; the names share nothing. This is the easy case — a rename.
Different option styles. get_cells u_* takes the pattern positionally; find_instances -pattern u_* takes it as a named option. Harder, because the call shape changes, and because a positional argument and a flag fail differently when you get them wrong.
Different command shapes. alpha and beta use flat commands; gamma uses one ensemble, design, with subcommands — design name, design cells, design count. This is the hardest case: there is no one-to-one mapping, and a script written against one shape has to be restructured, not renamed.
3. What is portable
Worth being precise, because the portable part is larger than people assume.
Standard Tcl — everything from Part I. The ninety-odd commands from Chapter 16.4 §3 behave identically in every tool. Your loops, your string handling, your error handling, your file I/O, your script structure: all portable.
SDC constraints, largely. As Chapter 16.2 §2 noted, constraint commands are a shared standard, which is why one .sdc is read by tools from different vendors. Not perfectly — tools add extensions and differ at the edges — but it is the one place where "will this run in the other tool" usually has a reassuring answer.
Your own procedures, if they take plain data. count_matching {names pattern} from Chapter 16.4 §6 works anywhere, because it never touches the tool.
That last one is the lever. The more of your logic takes plain lists and strings, the more of your script is portable by construction.
4. Say what you assume
The minimum, and it costs three lines at the top of the file:
# Requires: <tool name> <version or later>.
# Tool commands used: read_design, get_cells, report_area.
# Developed and tested against: <tool> <exact version>.Then enforce it, so the script fails in the first second rather than the fortieth minute — the preflight from Chapter 14.10 §8:
proc require_commands {args} {
set missing {}
foreach c $args {
if {![llength [info commands $c]]} { lappend missing $c }
}
if {[llength $missing]} {
error "this tool does not provide: [join $missing {, }]" {} \
[list TOOL MISSING $missing]
}
}
require_commands read_design get_cells report_area5. The adapter: one script, several tools
When a script genuinely has to run on more than one tool, do not scatter if {$tool eq "..."} through it. Put every tool-specific call behind a thin layer, and have the flow script call only that layer.
The adapter — one procedure per capability, with the differences isolated inside:
# adapter.tcl — one thin layer per tool. The flow script calls only these.
namespace eval ::flow {}
proc ::flow::detect {} {
if {[llength [info commands cell_count]]} { return alpha }
if {[llength [info commands instance_count]]} { return beta }
if {[llength [info commands design]]} { return gamma }
error "unrecognised tool: none of the expected commands are present" {} {FLOW NOTOOL}
}
proc ::flow::design_name {} {
switch -- [detect] {
alpha { return [current_design] }
beta { return [get_design_name] }
gamma { return [design name] }
}
}
proc ::flow::cells {pattern} {
switch -- [detect] {
alpha { return [get_cells $pattern] }
beta { return [find_instances -pattern $pattern] }
gamma { return [design cells $pattern] }
}
}The flow script, which contains no tool command at all:
# flow.tcl — the SAME script for every tool. It calls only ::flow::*.
source adapter.tcl
set tool [::flow::detect]
set name [::flow::design_name]
set list [::flow::cells u_*]
puts " tool detected : $tool"
puts " design : $name"
puts " cells u_* : $list"
puts " count : [llength $list]"Run unchanged against all three shells:
tool detected : alpha
design : cpu_top
cells u_* : u_fifo u_alu u_ctrl
count : 3
---
tool detected : beta
design : cpu_top
cells u_* : u_fifo u_alu u_ctrl
count : 3
---
tool detected : gamma
design : cpu_top
cells u_* : u_fifo u_alu u_ctrl
count : 3
---One flow script. Three command sets with nothing in common. No change to the script.
Four things that make this work:
- Detection is by capability, not by name.
::flow::detectasks which commands exist rather than reading a version string. That is Chapter 14.10'sinfo commandsdoing the one job no documentation can. - The adapter is the only file that mentions a tool command. Supporting a fourth tool means adding a branch to each adapter procedure and changing nothing else.
- It fails loudly on an unknown tool, with a structured error code, rather than falling through to a default that would be wrong.
- The flow script is testable — write a fourth mock adapter that returns canned lists, and the whole flow runs in
tclshwith no tool at all.
6. Realistic expectations
Three things worth being clear-eyed about.
Full portability is not achievable, and usually is not the goal. Tools differ because they do different things, and an abstraction broad enough to cover everything would hide the capabilities you bought the tool for. Most teams target one tool per stage and port when they change.
Porting is mostly mechanical. When the structure is right — tool calls at the edges, logic in standard Tcl — a port is a rewrite of the thin layer. When it is wrong, it is a rewrite of everything. That difference is decided when the script is first written.
Versions matter as much as vendors. Commands gain options, change defaults and are deprecated between releases of the same tool. # Developed and tested against: <tool> <exact version> is not bureaucracy; it is the single most useful line in the file when something behaves unexpectedly two years later.
7. Exercises
Exercise 1. Name the three ways tool command sets differ, in increasing order of difficulty.
Exercise 2. Which of those is hardest to port, and why?
Exercise 3. What is the fourth kind of difference, and why is it the most dangerous?
Exercise 4. Name three things that are portable between tools.
Exercise 5. Why does a script need both a requirements comment and a preflight?
Exercise 6. Why does ::flow::detect ask which commands exist rather than reading a version string?
Exercise 7. When should you not build an adapter layer?
Where this fits
Part of the Tcl curriculum.
