Every synthesis tool runs the same five ideas in roughly this order, even when it iterates, merges steps, or renames them. This topic uses one stage list on every page: read, elaborate, optimise, map, report. Read the model once and every later lesson has somewhere to hang: each new pass or report belongs to exactly one stage, and each stage owns a specific netlist shape.
Stage | Command here | What the netlist contains |
|---|---|---|
1. Read |
| RTL loaded and checked, top set; the netlist starts here. |
2. Elaborate |
| Word-level cells: operators, muxes, and flops as the RTL wrote them. |
3. Optimise |
| Word-level cells simplified, then lowered to bit-level generic gates; technology-independent, still no library, still no area. |
4. Map |
| Real library cells chosen for the constraints; area exists only from here on. |
5. Report |
| Counts and area for review. Reporting never changes the netlist. |
Elaboration builds word-level cells and nothing else: after proc and coarse opt the design still holds one $add and one $mux with zero generic gates, and bit-level gates appear only once techmap runs the lowering half of optimisation. The script below pins both halves of that boundary, so read the assertions.
The script runs the saturating adder from the previous lesson through three recording stops: after proc and opt, after techmap, and after abc -liberty with a loose 1000 ps target. Each stop writes a stat report and a JSON netlist under its own name, and design -save freezes the netlist so a later stage cannot disturb an earlier recording. The assertions then reload each frozen stage and pin exactly what may and may not be present.
read_verilog <<EOV
module satadd(input [3:0] a, b, output [3:0] y, output sat);
wire [4:0] s = a + b;
assign sat = s[4];
assign y = s[4] ? 4'hF : s[3:0];
endmodule
EOV
hierarchy -check -top satadd
proc
opt
tee -o stat_opt.txt stat
write_json net_opt.json
design -save after_opt
techmap
tee -o stat_tech.txt stat
write_json net_tech.json
design -save after_tech
tee -o abc_stage.txt abc -liberty course.lib -constr typical.constr -D 1000
tee -o stat_mapped.txt stat -liberty course.lib
write_json net_mapped.json
design -save after_map
design -load after_opt
select -assert-count 1 t:$add
select -assert-count 1 t:$mux
select -assert-none t:$_AND_ t:$_OR_ t:$_NOT_ t:$_MUX_ t:$_XOR_ t:$fa %u
design -load after_tech
select -assert-none t:$add
select -assert-none t:$mux
select -assert-min 1 t:$_AND_ t:$_OR_ t:$_XOR_ t:$_NOT_ t:$_MUX_ t:$fa %u
design -load after_map
select -assert-none t:$and t:$or t:$xor t:$mux t:$not t:$add t:$sub %u
select -assert-none t:$_AND_ t:$_OR_ t:$_NOT_ t:$_MUX_ t:$_XOR_ t:$_NAND_ t:$_NOR_ %u
select -assert-min 1 t:INV t:BUF t:NAND2 t:NOR2 t:AND2 t:OR2 t:NAND3 t:NOR3 t:XOR2 t:XNOR2 t:AOI21 t:OAI21 t:MUX2 %u
check
log PASS: adder word cells survive opt, become bit cells at techmap, library cells after abc$add and one $mux. What happens to the cell count at the second stop, and what happens again at the third?It jumps, then it falls well below the peak. Lowering one 5-bit adder and one 4-bit mux to bit-level gates creates many gates from two cells, so the second stop counts 58 cells against 2 word-level cells at the first. Mapping then merges and re-covers those gates into 24 library cells: wider cells such as AOI21 and OAI21 each absorb several generic gates, and ABC re-optimises while covering.
Stage | Command | Cells recorded | Area recorded |
|---|---|---|---|
Stages 1–3 (coarse) |
| 2 word-level cells | None: no library attached yet |
Stage 3 (lowering) |
| 58 bit-level gates | None: generic gates have no prices |
Stages 4–5 |
| 24 library cells | 65 library units of course.lib |
Read the table left to right. Area appears only in the last row, priced at 65 library units of course.lib. The first two rows cannot quote an area at any price: word-level cells and generic gates are library-neutral by construction, so any area attached to them would be fiction. That is why quoting a stage-3 count as final area misleads, and why the only area this topic ever quotes comes from a stat -liberty report after mapping. Do not confuse this generic-gate total with the previous page's area, which happens to share its digits: there the number is library units after mapping, here it is a gate count before any library is involved.
The boundary is pinned twice. After proc; opt the script asserts one $add, one $mux, and zero generic gates: elaboration builds word-level cells, full stop. After techmap it asserts the reverse: no $add, no $mux, and at least one bit-level gate. Bit-level gates appear at techmap, the lowering tail of optimisation, never before.
A learner comparing with the previous lesson will notice a different covering there: the previous lesson maps this same adder through synth and records fewer cells at a smaller area, while this page runs proc, opt, techmap, and abc as separate passes and records its own covering. Same RTL, two scripts, two coverings: synth runs its own optimisation and abc first, so its techmap input already differs. Every number on a page belongs to the script beside it.
This variant converges by design rather than breaking anything. The second script uses a small cone with a duplicated product term, (a & b) | (a & b) | (c & d), and runs it twice: once through proc and opt, once with opt deleted. Nothing else changes, so any difference in the recordings belongs to the missing pass alone.
read_verilog <<EOV
module or_redundant(input a, b, c, d, output y);
assign y = (a & b) | (a & b) | (c & d);
endmodule
EOV
hierarchy -check -top or_redundant
proc
opt
tee -o stat_red_opt.txt stat
write_json net_red_opt.json
design -save red_opt
techmap
tee -o abc_red_opt.txt abc -liberty course.lib -constr typical.constr -D 1000
tee -o stat_red_opt_mapped.txt stat -liberty course.lib
write_json net_red_opt_mapped.json
design -save red_opt_mapped
design -load red_opt
select -assert-count 2 t:$and
select -assert-count 1 t:$or
select -assert-none t:$_AND_ t:$_OR_ t:$_NOT_ t:$_MUX_ t:$_XOR_ t:$fa %u
design -load red_opt_mapped
select -assert-none t:$and t:$or t:$xor t:$mux t:$not t:$add t:$sub %u
select -assert-none t:$_AND_ t:$_OR_ t:$_NOT_ t:$_MUX_ t:$_XOR_ t:$_NAND_ t:$_NOR_ %u
select -assert-min 1 t:INV t:BUF t:NAND2 t:NOR2 t:AND2 t:OR2 t:NAND3 t:NOR3 t:XOR2 t:XNOR2 t:AOI21 t:OAI21 t:MUX2 %u
design -reset
read_verilog <<EOV
module or_redundant(input a, b, c, d, output y);
assign y = (a & b) | (a & b) | (c & d);
endmodule
EOV
hierarchy -check -top or_redundant
proc
tee -o stat_red_noopt.txt stat
write_json net_red_noopt.json
design -save red_noopt
techmap
tee -o abc_red_noopt.txt abc -liberty course.lib -constr typical.constr -D 1000
tee -o stat_red_noopt_mapped.txt stat -liberty course.lib
write_json net_red_noopt_mapped.json
design -save red_noopt_mapped
design -load red_noopt
select -assert-count 3 t:$and
select -assert-count 2 t:$or
select -assert-none t:$_AND_ t:$_OR_ t:$_NOT_ t:$_MUX_ t:$_XOR_ t:$fa %u
design -load red_noopt_mapped
select -assert-none t:$and t:$or t:$xor t:$mux t:$not t:$add t:$sub %u
select -assert-none t:$_AND_ t:$_OR_ t:$_NOT_ t:$_MUX_ t:$_XOR_ t:$_NAND_ t:$_NOR_ %u
select -assert-min 1 t:INV t:BUF t:NAND2 t:NOR2 t:AND2 t:OR2 t:NAND3 t:NOR3 t:XOR2 t:XNOR2 t:AOI21 t:OAI21 t:MUX2 %u
check
log PASS: opt merges the duplicated product term that proc leaves behindWith optimisation the design elaborates to 3 coarse cells: opt merges the two identical product terms into one, leaving two $and cells feeding one $or. Without optimisation the merger never runs and 5 coarse cells reach techmap. In this ABC script mapping reconverges both halves to 3 library cells at area 6 library units: abc re-optimises while covering, so the duplicated term costs nothing at the mapped endpoint. Optimisation earns its keep before mapping, handing techmap a smaller coarse netlist even when the mapped endpoint coincides.
In stage 4, map. Before it the page asserts one $add, one $mux, and zero generic gates, so stages 1 and 2 work only with word-level cells that carry no area.
Those 2 are word-level cells with no library attached, so they have no prices. The area is the mapped row's figure in library units of course.lib, quoted only after mapping; read it from the table, never from a stage-2 count.
Every number and the bar chart on this page come from two Yosys 0.69 runs of the shown scripts, recorded by the course checker (scripts/tools/run-rtl-testbenches.mjs with COURSE=synth): the saturating adder recorded after proc and opt, after techmap, and after abc against course.lib, plus a redundant cone run twice, with and without opt. The library is fictional, so absolute areas are teaching units.