Hierarchy, fanout and naming answer three different questions that one report often mixes together: which instances exist, how heavily each net is loaded, and which names survive optimisation. This lesson separates them on a design with two instances of one AND submodule sharing an input, plus an OR at the top. Every count below is a recording from the script, including the fanout table and the mapped comparison, both counted from recorded netlists at build time rather than guessed from the RTL.
A hierarchical netlist keeps submodule instances, so its report counts instances alongside gates: the pre-flatten stat spans both the sub module and the top module, while the flattened report counts one flat module. The script pins the instance count first, flattens, then pins the dissolved result. Flattening moves gates between modules without creating or deleting any, which is exactly what the two reports confirm.
read_verilog <<EOV
module sub(input a, b, output y);
assign y = a & b;
endmodule
module top(input a, b, c, output y);
wire n1, n2;
sub u1 (.a(a), .b(b), .y(n1));
sub u2 (.a(a), .b(c), .y(n2));
assign y = n1 | n2;
endmodule
EOV
hierarchy -check -top top
proc
opt
tee -o stat_hier.txt stat
write_json net_hier.json
select -assert-count 2 t:sub
design -save hier
flatten
tee -o stat_flat.txt stat
write_json net_flat.json
select -assert-none t:sub
select -assert-count 2 t:$and
select -assert-count 1 t:$or
design -load hier
synth -top top
tee -o abc_hier.txt abc -liberty course.lib -constr typical.constr -D 1000
tee -o stat_hier_map.txt stat -liberty course.lib
select -assert-count 2 t:sub
select -assert-none t:$and t:$or t:$_AND_ t:$_OR_
select -assert-min 1 t:NAND2 t:NOR2 t:AND2 t:OR2 t:INV t:BUF %u
design -load hier
synth -top top -flatten
tee -o abc_flat.txt abc -liberty course.lib -constr typical.constr -D 1000
tee -o stat_flat_map.txt stat -liberty course.lib
write_json net_flat_map.json
select -assert-none t:sub t:$and t:$or t:$_AND_ t:$_OR_
select -assert-min 1 t:NAND2 t:NOR2 t:AND2 t:OR2 t:INV t:BUF %u
design -load hier
read_verilog <<EOV
module tieconst(input a, b, output y, output z);
assign y = a & 1'b1;
assign z = b | 1'b0;
endmodule
EOV
hierarchy -check -top tieconst
opt
tee -o stat_const.txt stat
select -assert-none t:$and
select -assert-none t:$or
log PASS: hierarchy, flatten, both mapped areas and folded constants recordedCommercial flows call this keep_hierarchy versus flattening: keep the instances for floorplanning and debugging, or dissolve them so optimisation sees across the boundary.
The gates match: 3 hierarchically ($and 2, $or 1) against 3 flat ($and 2, $or 1). The flat report's cell line reads 5 only because flattening leaves two $scopeinfo markers behind. The highest fanout sits on a with fanout 2: input a feeds both instances, so it drives twice the load of any other net.
Hierarchy keeps the gates, but the mapped area moves a lot: 9 library units hierarchical against 4 flat. Mapped per module, each sub instance becomes one AND2 (area 3 each, counted twice) and the top becomes one OR2 (area 3): ABC's per-module run covers a single AND2 at area 3. Mapped flat, ABC sees the whole cone at once and covers it as OAI21 plus INV, with ABC reporting 2 gates at delay 168.07 ps. Same gates, different covering: flattening lets ABC optimise across the boundary, which is the textbook reason to flatten before mapping.
8. Printing statistics.
=== top ===
+----------Local Count, excluding submodules.
|
12 wires
12 wire bits
12 public wires
12 public wire bits
4 ports
4 port bits
5 cells
2 $and
...The flat report above is one module: 5 cells on 12 wires with no sub instances left — 3 gates plus the two $scopeinfo markers. Past the ellipsis the file lists one $or and the two markers. Against 3 cells across two modules before flattening, the gates are unchanged; the grouping and the covering are not.
Fanout counts how many gate inputs one net drives. High fanout means heavy load, which later stages must buffer, so the number belongs to the netlist rather than to anyone's impression of the RTL. The table below is not typed: the lesson reads the recorded generic flat netlist (net_flat.json, before any library cell exists) and counts sinks per net at build time, exactly as a reporting script would.
Net | Fanout (loads driven) |
|---|---|
a | 2 |
b | 1 |
c | 1 |
n1 | 1 |
n2 | 1 |
Input a leads at fanout 2 because it is shared by both instances, while every other generic net drives exactly one load. Mapping changes the answer: in the recorded mapped flat netlist (net_flat_map.json, one OAI21 plus one INV) input a drives 1 pin, because both ANDs dissolved into a single cell and the sharing vanished. Fanout depends on the stage, so always quote the netlist you counted from.
Nets without a declared name get a tool-generated one, and those names shift with small edits. Scripts and constraints must therefore use stable ports and hierarchical paths, never generated names: anything the tool invents, the next edit may rename. The flat netlist above shows the declared nets n1 and n2 alongside its ports precisely because the RTL declared them; an unnamed net would appear under a generated stand-in instead.
Constant pins fold away during optimisation rather than building anything. Tying an input to 1 or 0 turns the gate into a wire, so the tied-constant design in the script reports no cells: both gates folded, no $and and no $or left, exactly as the assertions pin. Treat an undriven pin with the same suspicion in reverse: confirm it rather than assuming the tool understood the intent.
Fanout 2 on the generic flat netlist: it drives one input on each of the two $and cells. The table settles it because fanout is a netlist property counted from recorded sinks, and RTL sharing is only a hint until the netlist confirms the loads.
No. The gates are identical: 3 hierarchically ($and 2, $or 1) against 3 flat ($and 2, $or 1). The mapped area gap is the covering — per-module AND2s plus an OR2 against one OAI21 plus INV — not new hardware.
Every number, both fanout counts and the highlighted netlist on this page come from one Yosys 0.69 run of the script shown, recorded by the course checker (scripts/tools/run-rtl-testbenches.mjs with COURSE=synth): a two-instance hierarchical design read flat and hierarchical, mapped against the teaching library course.lib, plus a tied-constant design folded by opt. The library is fictional, so absolute values are teaching units.