Skip to content
DenseChip
Learn
PracticeCode labInterview

DenseChip

Navigate

Menu

LearnPracticeCode labInterview

Tools

All toolsSV editorCode repoResume
DenseChip

Practice, courses, and chip-design tools in one focused workspace.

LearnPracticeCode labInterviewToolsLearning paths

© 2026 DenseChip

AboutRoadmapCareersInternshipsFAQContactPrivacyTerms
Skip to lesson

Course contents

  • Logic Synthesis Glossary
  • Logic Synthesis Cheatsheet
  • Synthesis Foundations14
    • Synthesis Flow Overview
    • Synthesizable RTL and Inference
    • Reading Netlists and Reports
      • Cell Counts from stat
      • Area by Cell Type
      • Hierarchy, Fanout and Net Names
      • Longest Path, Write and Read Back
  • Logic Optimisation and Mapping20
  • Constraints and Timing15
  • Sequential, FSM and Memory19
  • Datapath Synthesis14
  • Area, Power and Test14
  • Verification, ECO and Handoff15
  • Flows and Debugging15
  • Interview and Judgment11
Loading lesson

←Reading Netlists and Reports
  1. Topics
  2. Logic Synthesis
  3. Synthesis Foundations
  4. Reading Netlists and Reports
  5. Longest Path, Write and Read Back
On this page

    Part 1 of 9, lesson 14 of 14Part 1 of 9/Synthesis Foundations/Lesson 14 of 14Intermediate

    Longest Path, Write and Read Back

    Longest topological path with ltp, plus write, read back, and recount.

    The longest topological path is not the slowest path, and a bare mapped ltp reports zero. Quoting either number as timing has caused real schedule arguments.

    After this lesson you can: run ltp where it works, recover a mapped depth with read_liberty, show a longer path beaten on delay by a shorter one, and round-trip a netlist through write and read.

    The question

    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.

    ltp on the coarse netlist

    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.

    yosys
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    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 trip

    A commercial flow treats this depth as the starting point for timing analysis, never the answer: later stages annotate the same path with delays.

    All four ltp reports below are shown exactly as the tool printed them. The generic depths quoted in prose are computed from the recorded write_json netlists at build time, and the mapped depth is read straight from the recorded rerun, so each excerpt and its number corroborate each other.

    Diagram
    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)
    Predict first Before reading on: the same ltp command runs again after abc maps the design against course.lib. Will it report the same depth, a larger one, or nothing useful?

    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.

    ltp after mapping reports zero until the library loads

    Diagram
    12. Executing LTP pass (find longest path).
    
    Longest topological path in top (length=0):
        0: \n2

    Compare 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.

    Diagram
    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.

    Break it: the longer path collapses in mapping

    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.

    Diagram
    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.

    example.py
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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")
    The two-path design from its recorded generic netlist: a three-inverter chain (highlighted, the topologically longer path) beside a single XOR2. Mapping collapses the chain to one cell while the XOR stays one, as the table above records.

    Write, read back, recount

    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.

    Diagram
    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
    ...

    Common mistakes

    The longest topological path is not the critical timing path. ltp counts gates and ignores delay, and the generic structure it counts may not survive mapping: three inversions collapse to one cell in this ABC script, so the mapped race is one cell against one cell. Quote mapped cells, never generic depth. Quoting a mapped zero is the companion trap: it reports missing library visibility, fixed by one read_liberty line, not an empty design.

    When does ltp help, and what does the round trip prove?

    Weak: ltp finds the slowest path, and writing the netlist saves your work. Strong: ltp ranks the generic three-gate example at depth 2 and the inverter chain at 3, but mapping collapses the chain to 1 cell while the XOR stays 1 cell, and the recorded ABC delays rank the survivors (193.96 ps against 63.96 ps) in this ABC script, so ltp is structure only; on a mapped netlist it reports zero until read_liberty loads the pin directions and recovers a mapped depth, and the write plus read-back restoring 3 cells proves the file loop, not the synthesis result — the wire count shifts across the file, so cells are the check.

    Follow-up: the mapped ltp reports zero while ABC reports delay. Which number do you trust for timing, and why? Neither for sign-off: ABC's delay is the mapper's estimate for ranking options in this script, while ltp never looks at delay at all. Only STA signs off timing.

    Check yourself The coarse example has a recorded depth. What does ltp count, and what does it ignore?

    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.

    Check yourself The written netlist reads back with the same cell count. What does that prove, and what does it leave open?

    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.

    • Generic depth can collapse in mapping: three inversions become one cell, so quote mapped cells, never generic depth.

    • A bare mapped ltp reports zero because library cells are unknown modules; read_liberty restores pin directions and a mapped depth (cells along the path), never delay.

    • Write plus read-back with identical cell counts checks the file loop, not the synthesis result.

    Rule of thumb: never quote a bare mapped ltp zero, and never quote ltp as timing.

    How this was checked

    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.

    • Interview: run a mock interview on this topic

    Related topics

    • Hierarchy, fanout and net names

    • Delay Models and Slack

    Previous

    16 / 139

    Next: Logic Optimisation and Mapping

    Up next

    Up nextLogic Optimisation and Mapping

    Sign in to save progress

    Practice this lessonPrevious lessonReport an error

    See also

    • Cell Counts from stat
    • Area by Cell Type