Tcl · Module 27
Keeping the Flow Tool-Agnostic
Put a seam between what the flow wants and what the tool calls it. The seam works, and the chapter spends as long on where it does not as on where it does.
Advanced22 min readtcledapart-iiinfrastructureportabilityarchitecture
Part II · Module 27 · Chapter 27.5 · Keeping the Flow Tool-Agnostic
Flow scripts get written against one tool and then live longer than the decision to use it. A tool is replaced, a second one is brought in for a particular block, a licence runs out, or a vendor is acquired.
The question is whether that is an afternoon or a quarter.
1. The seam
proc ::tool::optimise {effort} {
switch -- $::tool::BACKEND {
alpha { ::tool::issue "a_synthesize -effort $effort" }
beta { ::tool::issue "b_compile -opt $effort" }
}
}Every procedure in the seam is named for what the flow wants — read_design, apply_constraints, optimise, write_netlist — and the switch is the only place a vendor command appears.
The backends here record the command they would issue rather than issuing it, which is what makes the substitution demonstrable on a machine with no tool installed at all. alpha and beta are deliberately not real vendor names, for the reason Chapter 17.5 gives.
2. The flow script
# synth.tcl — ONE flow script. It names no vendor command anywhere.
proc run_synthesis {rtl top sdc outdir} {
::tool::read_design $rtl $top
::tool::apply_constraints $sdc
::tool::optimise high
::tool::report_timing_to [file join $outdir timing.rpt]
::tool::write_netlist [file join $outdir $top.v]
}Run it against each backend in turn:
=== backend alpha issued 7 command(s) ===
a_read_hdl -sv rtl/core.sv
a_read_hdl -sv rtl/fifo.sv
a_elaborate core_top
a_read_sdc constraints/core.sdc
a_synthesize -effort high
a_report_timing -max_paths 10 > work/timing.rpt
a_write_netlist -format verilog work/core_top.v
=== backend beta issued 8 command(s) ===
b_analyze -format sverilog rtl/core.sv rtl/fifo.sv
b_elaborate -top core_top
b_link
b_source_constraints constraints/core.sdc
b_check_constraints
b_compile -opt high
b_timing_report -paths 10 -out work/timing.rpt
b_write -hierarchy -format verilog -output work/core_top.v=== vendor command names anywhere in the flow script? ===
0 vendor token(s) in the flow scriptSeven commands against eight, entirely different names, from one unchanged script with zero vendor tokens in it.
3. What the seam buys
A flow script becomes reviewable by someone who does not know the tool. run_synthesis above says what synthesis is, in five lines, with no vendor knowledge required.
The vendor knowledge is in one file, so the question "what does this flow actually run" has a single answer rather than being distributed across thirty scripts.
Adding a backend is additive. A third tool is a third switch arm in each procedure, and no existing arm changes — so the work is bounded and the risk of breaking the working tool is near zero.
Testing without the tool becomes possible. The mock used here is a third backend in all but name: it records rather than issues, which means flow logic can be exercised in CI with no licence. That is the one benefit people do not anticipate and it is often the largest.
4. Leak one: reports
=== the command seam holds: one call, two command sets ===
alpha -> {a_synthesize -effort high}
beta -> {b_compile -opt high}Now the reports those tools produce:
=== but the REPORT formats are unrelated ===
--- alpha ---
ALPHA timing report
endpoint u_mem/addr_reg_7/D slack -0.0730 VIOLATED
endpoint u_mem/dout_reg_3/D slack 0.3120 MET
--- beta ---
# beta timing
u_mem/addr_reg_7/D,-0.0730,violated
u_mem/dout_reg_3/D,0.3120,met5. Leak two: a feature one tool does not have
proc ::tool::optimise_with_retiming {effort} {
switch -- $::tool::BACKEND {
alpha { ::tool::issue "a_synthesize -effort $effort -retime" }
beta { error "this backend has no retiming option" {} {TOOL NOFEATURE retime} }
}
}=== and an option one tool has and the other does not ===
alpha {a_synthesize -effort high -retime}
beta REFUSED: this backend has no retiming optionThe design decision is in that output: the backend refuses rather than silently dropping the option.
A backend that quietly ignored -retime would produce a netlist that was not retimed, with nothing anywhere saying so — the exact Part II failure shape, alongside Chapter 25.4 §2's test that failed and exited 0 and Chapter 23.1 §5's floorplan command that succeeded at an impossible utilisation.
Refusing forces a decision at the seam, where somebody can make it:
- implement the feature another way on that backend, and say in a comment that it is an approximation,
- declare the flow unsupported there, which is honest and sometimes correct,
- or make the option advisory, with the flow checking a capability flag rather than the backend pretending.
That third option is worth the extra machinery when it is common:
proc ::tool::supports {feature} {
set caps [dict create alpha {retime incremental} beta {incremental multicorner}]
return [expr {[lsearch -exact [dict get $caps $::tool::BACKEND] $feature] >= 0}]
}
if {[::tool::supports retime]} { ::tool::optimise_with_retiming high } else { ::tool::optimise high }Now the flow script contains a conditional about a capability, not about a tool name — which keeps it tool-agnostic while admitting that the tools differ.
6. Leak three: the same word meaning different things
The leak that cannot be demonstrated in a mock, and is the worst of the three.
Both backends take an effort option, and ::tool::optimise high maps to -effort high on one and -opt high on the other. The names translate cleanly. What does not translate is how much work "high" is — the runtime, the optimisations attempted, the quality of result, and whether it is the highest setting or the middle one.
So two runs through the seam are not comparable even though the flow script is identical. Chapter 24.2 §6's rule applies with full force: a result is comparable only against another result from the same tool, same version, same settings — and the seam, by making the scripts look identical, makes it easier to forget that.
There is no code fix. The honest handling is documentation at the seam:
# optimise: maps to each tool's main optimisation command.
# alpha -effort {low|medium|high} high ~= 2x medium runtime
# beta -opt {1..5}, high -> 4 beta's 5 has no alpha equivalent
# NOT comparable across backends. A QoR comparison must fix the backend.A comment is a weak mechanism, and it is the right one here because the alternative is pretending the abstraction is tighter than it is.
7. Where not to use a seam
Being complete about the honesty the registry asks for.
Do not abstract something you have one implementation of. A seam with a single backend is indirection with no payoff, and it is harder to review than the vendor command. Write the vendor command, and introduce the seam when a second tool actually arrives — the refactor is mechanical, and guessing the interface in advance is how you get an interface shaped like one tool.
Do not abstract the thing the tool is uniquely good at. If you chose a tool for a specific capability, hiding that capability behind a generic name defeats the choice. Name it for what it is and accept that this part of the flow is tool-specific.
Do not abstract interactive debugging. Chapter 27.2's helpers are typed by a human who knows which tool they are in. A seam there costs keystrokes and buys nothing.
Do not claim the abstraction is complete. The three leaks above are the ones in a four-procedure seam; a real one has more. "An afternoon rather than a rewrite" is the right order of magnitude for the commands and is optimistic once the parsers are counted.
8. Exercises
Exercise 1. What is each seam procedure named for, and where does a vendor command appear?
Exercise 2. Why is the 7-against-8 asymmetry more informative than a symmetric mapping?
Exercise 3. Why must the interface be shaped around the flow's intent rather than the first tool?
Exercise 4. Name the benefit of a seam that people do not anticipate.
Exercise 5. The seam abstracts issuing. What does it not abstract, and what does that cost?
Exercise 6. Why should a backend refuse a missing feature rather than drop it?
Exercise 7. Give the three ways to handle a feature one backend lacks.
Exercise 8. Why is "the same word meaning different things" the worst leak?
Exercise 9. Why does the seam make that leak easier to forget?
Exercise 10. Give two cases where a seam is the wrong choice.
Where this fits
Part of the Tcl curriculum.
