The longest topological path counts gates from an input to an output. It ignores cell delays and wire load, so it measures structure rather than time, and it only works where the tool can see the structure. This lesson runs ltp on the generic netlist, records the zero it prints on the mapped one, and shows the one-line fix that recovers a mapped depth. It then closes with a write, read-back and recount loop that checks the file rather than the synthesis.
The example is two ANDs feeding one OR, elaborated but not yet mapped. Its recorded structure holds 3 cells on 7 wires with depth 2: one AND level feeding one OR level.
read_verilog <<EOV
module top(input a, b, c, d, output y);
wire n1, n2;
assign n1 = a & b;
assign n2 = c & d;
assign y = n1 | n2;
endmodule
EOV
hierarchy -check -top top
proc
opt
tee -o stat_generic.txt stat
tee -o ltp_generic.txt ltp
write_json net_generic.json
select -assert-count 2 t:$and
select -assert-count 1 t:$or
design -save generic
techmap
opt
tee -o abc_mapped.txt abc -liberty course.lib -constr typical.constr -D 1000
tee -o stat_mapped.txt stat -liberty course.lib
tee -o ltp_mapped.txt ltp
write_json net_mapped.json
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
read_liberty -lib course.lib
tee -o ltp_liberty.txt ltp
read_verilog <<EOV
module inv1(input i, output o);
assign o = ~i;
endmodule
module twopaths(input a, b, c, d, output [2:0] w, output y);
inv1 u0(.i(a), .o(w[0]));
inv1 u1(.i(w[0]), .o(w[1]));
inv1 u2(.i(w[1]), .o(w[2]));
assign y = c ^ d;
endmodule
EOV
hierarchy -check -top twopaths
proc
opt
tee -o stat_twopaths.txt stat
tee -o ltp_twopaths.txt ltp
write_json net_twopaths.json
select -assert-count 3 t:inv1
select -assert-count 1 t:$xor
design -reset
read_verilog <<EOV
module inv1b(input i, output o);
assign o = ~i;
endmodule
module invchain(input a, output y);
wire w0, w1;
inv1b u0(.i(a), .o(w0));
inv1b u1(.i(w0), .o(w1));
inv1b u2(.i(w1), .o(y));
endmodule
EOV
hierarchy -check -top invchain
proc
opt
flatten
techmap
opt
tee -o abc_inv.txt abc -liberty course.lib -constr typical.constr -D 1000
tee -o stat_inv.txt stat -liberty course.lib
select -assert-min 1 t:INV t:BUF %u
design -reset
read_verilog <<EOV
module xor1(input c, d, output y);
assign y = c ^ d;
endmodule
EOV
hierarchy -check -top xor1
proc
opt
techmap
opt
tee -o abc_xor.txt abc -liberty course.lib -constr typical.constr -D 1000
tee -o stat_xor.txt stat -liberty course.lib
select -assert-count 1 t:XOR2
design -load generic
write_verilog netlist.v
design -reset
read_verilog netlist.v
hierarchy -check -top top
tee -o stat_readback.txt stat
select -assert-count 2 t:$and
select -assert-count 1 t:$or
log PASS: ltp before and after mapping recorded with a write and read back round tripA commercial flow treats this depth as the starting point for timing analysis, never the answer: later stages annotate the same path with delays.
6. Executing LTP pass (find longest path).
Longest topological path in top (length=2):
0: \a
1: \n1 (via $and$<inlined>:3$1)
2: \y (via $or$<inlined>:5$3)Nothing useful yet: the mapped run reports a zero-length path even though the netlist holds 3 library cells. Library cells arrive as unknown modules with no known pin directions, so the topological walk finds no gates to count. The script then loads the library and reruns ltp, which recovers depth 2. Read the zero as missing visibility, never as an empty design.
12. Executing LTP pass (find longest path).
Longest topological path in top (length=0):
0: \n2Compare the two excerpts. Before mapping, ltp walks 3 generic gates and reports its depth. After mapping, the same command faces 3 library cells and reports a zero-length path, while ABC's own summary of the same netlist still shows area 6 and delay 178.07 ps. The logic did not shrink; only the visibility did. The fix is one line the script records: read_liberty -lib course.lib teaches ltp the cells' pin directions, and the rerun below walks the mapped cells to depth 2.
15. Executing LTP pass (find longest path).
Longest topological path in top (length=2):
0: \d
1: $abc$78$new_n7 (via $abc$78$blif$78$1)
2: $abc$78$y (via $abc$78$blif$78$2)That recovered number is still a gate count, not timing: it says how many mapped cells the path crosses, nothing about how slow they are. The two-path run below makes the same point from the other side: ltp walks straight through the three user-module instances there, because their ports declare directions, while library cells stay opaque until the library loads.
The two-path design in the script is the deliberate breakage: three inverters in series on one output against a single XOR2 on another. Topologically the inverter chain is longer (3 gates against 1), so ltp ranks it first, as the recorded report below shows. Mapping disagrees about what survives: the script maps the chain and the XOR separately against course.lib under typical.constr, and each run's summary line reports its own cells and delay. The three inversions collapse to 1 cell while the XOR stays 1 cell, so the mapped race the delays describe is one cell against one cell, not three against one. Quoting the generic depth as timing misleads twice: ltp never looks at delay, and the structure it counted does not survive mapping.
21. Executing LTP pass (find longest path).
Longest topological path in inv1 (length=1):
0: \i
1: \o (via $not$<inlined>:2$79)
Longest topological path in twopaths (length=3):
0: \a
1: \w [0] (via u0)
2: \w [1] (via u1)
3: \w [2] (via u2)Path | Generic depth (recorded) | Mapped cells (recorded) | ABC delay (recorded) |
|---|---|---|---|
Three-inverter chain | 3 gates | 1 cell | 63.96 ps |
Single XOR2 | 1 gate | 1 cell | 193.96 ps |
Both delays are recorded ABC summaries: a mapper estimate for comparing options in this ABC script, never a timing sign-off, which needs STA. The generic ranking of 3 against 1 does not survive mapping, where both paths cover to a single cell. The program below passes only when the recorded numbers rank the XOR slower under this constraint, not as a general law about XOR gates.
import sys
# Delays below are the recorded ABC summary lines for this page's two mapping
# runs (abc_inv.txt, abc_xor.txt), injected at build time: never retype them.
INV_DELAY = 63.96
XOR_DELAY = 193.96
if not XOR_DELAY > INV_DELAY:
print("recorded ABC delays do not rank the XOR path slower: " + str(INV_DELAY) + " vs " + str(XOR_DELAY))
sys.exit(1)
print("PASS: recorded ABC delay ranks the single XOR2 slower than the mapped inverter chain")write_verilog prints the netlist to a file, and reading it back must restore the same cells, or the file is corrupt. The script writes the three-gate netlist, resets the design, reads the file back with the file form of read_verilog, and pins the same counts the generic stage asserted. It catches truncated writes, renamed modules, and version skew.
The read-back report holds 3 cells: two $and and one $or, the same 3 cells as before the write. Same cells, different design object in memory. What the loop proves is narrow but valuable: the file faithfully carries the cells. Wire naming may shift across the file boundary, so the recount compares cells, never wires. What the loop leaves open is whether the netlist matches the RTL, which needs an equivalence check rather than a recount.
43. Printing statistics.
=== top ===
+----------Local Count, excluding submodules.
|
10 wires
10 wire bits
7 public wires
7 public wire bits
5 ports
5 port bits
3 cells
2 $and
...It counts gates on the longest path: depth 2 through one AND level and the OR level. It ignores delays and load, so the number describes structure, not timing.
It proves the round trip preserved the cells: 3 before and after. Wires move from 7 to 10 across the file boundary, so cells are the check. It leaves open whether the netlist still matches the RTL, which needs an equivalence check rather than a recount.
Every report excerpt and all four ltp readings 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): ltp on a coarse three-gate netlist, ltp again after mapping plus a read_liberty rerun, ltp on a two-path design, separate ABC mappings of each path for the delay ranking plus mapped stat counts showing the chain collapsing to one cell, and a write_verilog plus read_verilog round trip. Delays are ABC's recorded mapper estimates; the library is fictional.