Tcl · Module 21
The Phases of a Synthesis Script
Synthesis scripts differ enormously in detail and almost not at all in shape. Learn the six phases once and every later chapter is a phase of a script you can already picture.
Intermediate16 min readtcledapart-iisynthesisflowscripts
Part II · Module 21 · Chapter 21.1 · The Phases of a Synthesis Script
A synthesis script turns RTL into a gate-level netlist. They vary a great deal between projects and tools, and almost not at all in shape.
Six phases, always in this order. Learn them once and the rest of this module is a chapter per phase.
1. The six phases
| Phase | What happens | Chapter |
|---|---|---|
| Read | sources and libraries enter the tool | 21.2, 21.4 |
| Elaborate | the hierarchy is built and references resolved | 21.5 |
| Constrain | the SDC is applied | 21.6 |
| Compile | logic is optimised and mapped to library cells | 21.7 |
| Report | timing, area, power and quality are captured | 21.8 |
| Write | the netlist leaves the tool | 21.9 |
2. Why the order is fixed
It is not convention. Each phase needs the previous one's result:
- You cannot elaborate a design whose sources have not been read.
- You cannot constrain a port that does not exist until elaboration created it.
- Compile has nothing to optimise toward without constraints — it would produce a netlist optimised against no requirement, which is Chapter 20.1 §1's empty-report problem in a more expensive form.
- Reports describe the compiled result, so they come after it.
- Writing the netlist is last because that is the artefact everything else was for.
The one that trips people is constrain before compile. Applying constraints to an already-compiled design does not retroactively change how it was optimised.
3. Every phase can fail quietly
This is the thread running through the module, and it is why it ends with a review chapter rather than a summary.
A tool command applied to nothing succeeds (Chapter 18.8). So:
- A file list that silently lost a file elaborates a partial design — 21.2.
- An unresolved reference leaves a black box the tool happily optimises around — 21.5.
- A constraint file that was sourced but applied nothing leaves the design unconstrained — 21.6.
- A report that was never written leaves a directory somebody assumes is complete — 21.8.
Each failure lets the next phase proceed. A run can pass through all six and exit successfully having produced a netlist nobody should use — which is the module blurb's "run which finished but produced nothing you can sign off", and Chapter 21.10 is the check for it.
4. The scaffolding
Because every phase can fail, the script needs a shape that notices. This is ordinary Tcl and it is the frame every later chapter plugs into:
proc run_phase {name body} {
puts " --- $name"
if {[catch {uplevel 1 $body} err]} {
puts stderr " FAILED $name: $err"
return 1
}
puts " ok $name"
return 0
}
set failures 0
incr failures [run_phase "read" { read_sources $files }]
incr failures [run_phase "elaborate" { elaborate_top $top }]
incr failures [run_phase "constrain" { apply_sdc $sdc }]
incr failures [run_phase "compile" { compile_design }]
incr failures [run_phase "report" { write_reports $rundir }]
incr failures [run_phase "write" { write_netlist $rundir }]
exit [expr {$failures > 0 ? 1 : 0}]With one phase failing, the shape is visible:
--- read
ok read
--- elaborate
FAILED elaborate: cannot resolve reference to 'missing_mod'
--- constrain
ok constrain
---
1 phase(s) failedFive Part I decisions, each from Chapter 15.9:
- A
catchper phase, so a failure is attributed to the phase that caused it rather than appearing as a trace from somewhere deep inside. - Failures counted, not fatal immediately — though §5 qualifies that.
- Diagnostics on stderr, progress on stdout (Chapter 14.9 §2).
- An honest exit status so the surrounding automation stops.
uplevel 1so each phase body runs in the caller's scope and can see$files,$topand the rest (Chapter 11.5).
5. Continue or stop?
Chapter 15.9 §4 argued that a batch loop should continue past one bad input. Synthesis is the opposite case, and it is worth being clear why.
The inputs there were independent — one malformed file says nothing about the next. Here the phases are dependent: if elaboration failed, compiling is not merely pointless, it burns an hour and produces a misleading artefact.
So the right structure is to stop at the first failure:
foreach {name body} $phases {
if {[run_phase $name $body]} {
puts stderr " stopping: '$name' failed"
exit 1
}
}The rule generalises: continue when the items are independent, stop when each depends on the last.
6. What varies and what does not
Between tools, the command names change completely — read_verilog versus something else, and the compile command especially. Chapter 17.5's adapter idea applies if you need more than one tool.
Between projects, the options change — which libraries, which effort level, which reports.
What does not change is the order, the fact that each phase can fail quietly, and the scaffolding around them. That is why learning the shape once is worth more than learning any particular tool's commands.
7. What a run must leave behind
Stated here because it shapes every later chapter: a synthesis run is not finished when the tool exits. It is finished when it has left
- a netlist,
- a report set somebody can read,
- a record of what produced them — 21.9 §4,
- and an exit status that tells the truth.
A run missing any of those is one somebody will have to repeat, usually when they are short of time.
8. Exercises
Exercise 1. Name the six phases in order.
Exercise 2. Why must constraints be applied before compile rather than after?
Exercise 3. Give two examples of a phase failing quietly.
Exercise 4. Why does run_phase use uplevel 1?
Exercise 5. Why stop at the first failed phase, when 15.9's batch loop continued past a bad input?
Exercise 6. What changes between tools, and what does not?
Exercise 7. Name the four things a finished run must leave behind.
Where this fits
Part of the Tcl curriculum.
