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
  • Logic Optimisation and Mapping20
  • Constraints and Timing15
    • SDC for Synthesis
    • Delay Models and Slack
    • Timing-Driven Synthesis
      • Critical-Path Focus
      • Restructuring and Duplication
      • Sizing and Path Groups
      • Reading WNS and TNS
      • Margin and When to Stop
  • Sequential, FSM and Memory19
  • Datapath Synthesis14
  • Area, Power and Test14
  • Verification, ECO and Handoff15
  • Flows and Debugging15
  • Interview and Judgment11
Loading lesson

←Timing-Driven Synthesis
  1. Topics
  2. Logic Synthesis
  3. Constraints and Timing
  4. Timing-Driven Synthesis
  5. Restructuring and Duplication
On this page

    Part 3 of 9, lesson 12 of 15Part 3 of 9/Constraints and Timing/Lesson 12 of 15Intermediate

    Restructuring and Duplication

    Restructuring shortens logic depth and duplication isolates the critical path.

    Depth is cheaper than drive, and a copy the tool can prove identical is area you wrote and the tool deleted. Knowing which fix survives which pass keeps the area bill honest.

    After this lesson you can: read logic depth off an unoptimised netlist, explain why a hand-written duplicate disappears during mapping, and price cloning against buffering from recorded reports.

    Depth sets the arrival time

    Arrival time grows with the number of gate stages on a path: every stage adds its own delay, so a shorter path arrives earlier for the same function. An 8-input AND makes the point concrete. Written as a chain of two-input AND gates, the last input waits through seven stages; written as a balanced tree of pairs, no input waits through more than three. The two scripts below synthesise each spelling twice: first with a light flow that preserves the written structure, so the recorded netlist shows the shape as written, then with the full flow and ABC so the recorded report shows what the tool makes of each shape. The light flow is hierarchy, proc, flatten, techmap and clean: no optimisation pass, so nothing rebalances the chain before it is written out.

    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
    read_verilog <<EOV
    module chain8(input [7:0] x, output y);
    wire c0 = x[0] & x[1];
    wire c1 = c0 & x[2];
    wire c2 = c1 & x[3];
    wire c3 = c2 & x[4];
    wire c4 = c3 & x[5];
    wire c5 = c4 & x[6];
    assign y = c5 & x[7];
    endmodule
    EOV
    hierarchy -top chain8
    proc
    flatten
    techmap
    clean
    write_json chain_raw.json
    design -reset
    read_verilog <<EOV
    module chain8(input [7:0] x, output y);
    wire c0 = x[0] & x[1];
    wire c1 = c0 & x[2];
    wire c2 = c1 & x[3];
    wire c3 = c2 & x[4];
    wire c4 = c3 & x[5];
    wire c5 = c4 & x[6];
    assign y = c5 & x[7];
    endmodule
    EOV
    synth -top chain8 -flatten
    design -save chain
    tee -o chain_loose.txt abc -liberty sized.lib -constr typical.constr -D 100000
    tee -o chain_loose_stat.txt stat -liberty sized.lib
    write_json chain_net.json
    select -assert-none t:$and t:$or t:$xor t:$xnor t:$mux t:$not
    select -assert-none t:$_AND_ t:$_OR_ t:$_XOR_ t:$_XNOR_ t:$_MUX_ t:$_NOT_ t:$_NAND_ t:$_NOR_
    check
    design -load chain
    tee -o chain_tight.txt abc -liberty sized.lib -constr typical.constr -D 250
    tee -o chain_tight_stat.txt stat -liberty sized.lib
    select -assert-none t:$and t:$or t:$xor t:$xnor t:$mux t:$not
    select -assert-none t:$_AND_ t:$_OR_ t:$_XOR_ t:$_XNOR_ t:$_MUX_ t:$_NOT_ t:$_NAND_ t:$_NOR_
    select -assert-min 1 t:*_X2 t:*_X4
    check
    log PASS: a chain maps the way a tree maps
    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
    read_verilog <<EOV
    module tree8(input [7:0] x, output y);
    wire a0 = x[0] & x[1];
    wire a1 = x[2] & x[3];
    wire a2 = x[4] & x[5];
    wire a3 = x[6] & x[7];
    wire b0 = a0 & a1;
    wire b1 = a2 & a3;
    assign y = b0 & b1;
    endmodule
    EOV
    hierarchy -top tree8
    proc
    flatten
    techmap
    clean
    write_json tree_raw.json
    design -reset
    read_verilog <<EOV
    module tree8(input [7:0] x, output y);
    wire a0 = x[0] & x[1];
    wire a1 = x[2] & x[3];
    wire a2 = x[4] & x[5];
    wire a3 = x[6] & x[7];
    wire b0 = a0 & a1;
    wire b1 = a2 & a3;
    assign y = b0 & b1;
    endmodule
    EOV
    synth -top tree8 -flatten
    tee -o tree_loose.txt abc -liberty sized.lib -constr typical.constr -D 100000
    tee -o tree_loose_stat.txt stat -liberty sized.lib
    write_json tree_net.json
    select -assert-none t:$and t:$or t:$xor t:$xnor t:$mux t:$not
    select -assert-none t:$_AND_ t:$_OR_ t:$_XOR_ t:$_XNOR_ t:$_MUX_ t:$_NOT_ t:$_NAND_ t:$_NOR_
    check
    log PASS: a tree maps the way a chain maps

    The assertions prove the generic gates are gone from the mapped netlists. The raw netlists are unoptimised, so their depth is the depth as written, checked by the program further down.

    Predict first The chain is 7 stages deep and the tree is 3. Will ABC report a slower delay for the chain's netlist than for the tree's?

    No: ABC rebalances the chain into a balanced cover on its own, so the RTL spelling stops mattering once the full flow runs. The mapping table below shows both spellings landing on identical reports.

    Spelling (recorded raw netlist, hand-derived depth)

    Generic cells

    Depth in stages

    Chain of 7 AND gates

    7

    7

    Balanced tree of 7 AND gates

    7

    3

    The chain spelling as written: seven AND gates in series, depth 7.
    The tree spelling as written: the same seven AND gates in three levels, depth 3.

    Two shapes in, one shape out

    The mapped reports erase the difference completely. The chain maps to 5 cells at area 13 and 382 ps; the tree maps to 5 cells at area 13 and 382 ps. Same function, same library, same target, same cover: the mapper's restructuring already did the balancing the tree spelling did by hand. That is timing-driven synthesis spending restructuring on depth before it spends any area on bigger cells.

    Mapping (recorded ABC report)

    Cells

    Area

    Delay reached

    Chain, loose target

    5

    13

    382 ps

    Tree, loose target

    5

    13

    382 ps

    Chain, tight -D 250 target

    5

    27.4

    230 ps

    The tight row shows the second half. At -D 250 the chain's cover cannot get shallower, so the mapper buys speed with drive strength instead: area rises from 13 to 27.4 while delay falls from 382 ps to 230 ps. Restructure first, size second: depth is cheaper than drive, and the tool tries them in that order.

    The depth rules, checked

    The program below checks the depth rules against the two depths parsed from the recorded raw netlists (7 and 3): a chain of n inputs needs n minus 1 stages, a balanced tree needs the ceiling of log base 2 of n. Its checks fail on any other pair, so the raw table above cannot drift from the recordings without the run going red.

    example.py
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    import sys
    
    # Depth rules for an n-input AND of two-input gates, checked against the
    # depths parsed from the recorded raw netlists: 7 and 3.
    def chain_depth(n):
        return n - 1
    
    def tree_depth(n):
        depth = 0
        span = 1
        while span < n:
            span *= 2
            depth += 1
        return depth
    
    if chain_depth(8) != 7:
        print("FAIL: chain depth " + str(chain_depth(8)) + " want 7")
        sys.exit(1)
    if tree_depth(8) != 3:
        print("FAIL: tree depth " + str(tree_depth(8)) + " want 3")
        sys.exit(1)
    print("PASS: an 8-input AND is 7 stages as a chain and 3 as a balanced tree")

    Cloning versus buffering

    The third script maps the shared cone three ways, loose, tight, and under a heavy load, then maps the kept-duplicate design once. Every assertion in it proves the generic gates are gone, the tight runs use upsized cells, and the heavy run inserts a buffer.

    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
    read_verilog <<EOV
    module sharedrv(input a, b, c, d, e, f, output y1, y2);
    wire t = (a & b) | (c & d);
    assign y1 = t ^ e;
    assign y2 = t ^ f;
    endmodule
    EOV
    synth -top sharedrv -flatten
    write_json shr_gen.json
    design -save shr
    tee -o shr_loose.txt abc -liberty sized.lib -constr typical.constr -D 100000
    tee -o shr_loose_stat.txt stat -liberty sized.lib
    write_json shr_net.json
    select -assert-none t:$and t:$or t:$xor t:$xnor t:$mux t:$not
    select -assert-none t:$_AND_ t:$_OR_ t:$_XOR_ t:$_XNOR_ t:$_MUX_ t:$_NOT_ t:$_NAND_ t:$_NOR_
    check
    design -load shr
    tee -o shr_tight.txt abc -liberty sized.lib -constr typical.constr -D 300
    tee -o shr_tight_stat.txt stat -liberty sized.lib
    select -assert-none t:$and t:$or t:$xor t:$xnor t:$mux t:$not
    select -assert-none t:$_AND_ t:$_OR_ t:$_XOR_ t:$_XNOR_ t:$_MUX_ t:$_NOT_ t:$_NAND_ t:$_NOR_
    select -assert-min 1 t:*_X2 t:*_X4
    check
    design -load shr
    tee -o shr_heavy.txt abc -liberty sized.lib -constr heavy.constr -D 100000
    tee -o shr_heavy_stat.txt stat -liberty sized.lib
    write_json shr_heavy_net.json
    select -assert-min 1 t:BUF*
    select -assert-none t:$and t:$or t:$xor t:$xnor t:$mux t:$not
    select -assert-none t:$_AND_ t:$_OR_ t:$_XOR_ t:$_XNOR_ t:$_MUX_ t:$_NOT_ t:$_NAND_ t:$_NOR_
    check
    design -reset
    read_verilog <<EOV
    module dupkeep(input a, b, c, d, e, f, output y1, y2);
    (* keep *) wire t1 = (a & b) | (c & d);
    (* keep *) wire t2 = (a & b) | (c & d);
    assign y1 = t1 ^ e;
    assign y2 = t2 ^ f;
    endmodule
    EOV
    synth -top dupkeep -flatten
    write_json dup_gen.json
    tee -o dup_loose.txt abc -liberty sized.lib -constr typical.constr -D 100000
    tee -o dup_loose_stat.txt stat -liberty sized.lib
    write_json dup_net.json
    select -assert-none t:$and t:$or t:$xor t:$xnor t:$mux t:$not
    select -assert-none t:$_AND_ t:$_OR_ t:$_XOR_ t:$_XNOR_ t:$_MUX_ t:$_NOT_ t:$_NAND_ t:$_NOR_
    check
    log PASS: a kept duplicate still maps to one shared cone

    A shared cone serves several paths at once, so its shape compromises between all of them. Duplication gives the critical path its own private copy shaped for speed alone. One design in that script shares a single AND-OR cone between two XOR outputs, while the changed variant computes the same cone twice behind keep attributes. The keep attribute saves the wire, but boolean optimisation still proves the two cones identical and shares almost everything, so the duplicate costs a single extra cell before mapping.

    Mapping then erases even that. Both designs map to 5 cells at area 16: the shared run at 366 ps, the duplicated run at 382 ps. ABC's default script runs fraig and strash, which merge functionally identical nodes no matter what the RTL said, so a naive RTL copy never survives to the netlist; the tool un-duplicates the keep-attribute copy here. A private copy has to survive the tool: separate it with a register, or let the mapper replicate during covering.

    Shared cone (recorded)

    Generic cells

    Mapped cells

    Mapped area

    Mapped delay

    One cone, two outputs

    5

    5

    16

    366 ps

    One cone, tight -D 300 target

    5

    5

    32

    295 ps

    Duplicated cone, keep attributes

    6

    5

    16

    382 ps

    One cone, heavy 0.2 pF load

    5

    7

    20

    506 ps

    The heavy-load row shows the other answer to a struggling fanout: buffering. Against a 0.2 pF load the same shared design maps to 7 cells at area 20, and the two extra cells are buffers the lighter runs never needed. Yet the delay still rises from 366 ps to 506 ps. Buffering and sizing answer load and drive; restructuring answers depth: buffering repairs the drive the heavy capacitance degrades, but it cannot shorten the logic the signal passes through. Sizing costs 16 area units for speed here, as the new tight row shows; buffering costs 4 and still arrives later.

    The shared design under a 0.2 pF load: 7 cells at area 20, delay 506 ps.

    Break it: duplicate the cone and watch the tool undo it

    The duplicated design is the break-it: the same function, library and loose target, with the shared cone written twice. Everything about the setup favours the duplicate, and the recorded result still refuses it: 6 generic cells collapse to the same 5 mapped cells at the same area 16 as the shared design. The only trace of the copy is a 16 ps wobble in the reported delay, noise from a different starting point, not a faster structure. An RTL copy that the tool can prove identical is area you wrote and the tool deleted.

    Common mistakes

    Duplicating a wide cone to fix a small slack buys little and doubles that cone area. Restructure or resize first, and duplicate only when sharing is the actual limiter.

    The worst path is already all upsized cells and still misses, and tighter targets change nothing. What now?

    • Weak answer: keep tightening the target or add margin until it closes.

    • Strong answer: sizing is spent at the library floor, so restructure the RTL algorithm next, since ABC already balanced this cover: the 7-to-3 rebalance here holds 382 ps at the same area 13 before any sizing, and duplicate the cone after that only if sharing is the limiter.

    • Follow-up: when is duplication better? When depth is already shallow and sharing forces the compromise.

    Check yourself The chain and the tree map to identical reports. Where did the 7-stage depth go?

    Into the mapper's restructuring: ABC rebalances the chain into a balanced cover on its own, so both spellings land on the same cells, area and delay. The RTL depth only shows in the unoptimised raw netlists.

    Check yourself The duplicated design maps to the same 5 cells at area 16 as the shared one. Was the copy ever built?

    Yes, at the generic level: the recorded generic counts read 5 against 6. Mapping merged it back, because fraig proves the copies identical. The copy cost one cell and bought no delay.

    • ABC rebalances a chain into a balanced cover on its own; RTL spelling stops mattering after mapping.

    • A keep attribute saves the wire, not the cone: fraig merges provably identical logic.

    • Buffering and sizing answer load and drive; restructuring answers depth.

    Rule of thumb: restructure for depth before sizing for drive, and duplicate only what the tool cannot prove identical.

    How this was checked

    Every number and picture on this page comes from Yosys 0.70 runs of the three scripts shown, recorded by the course checker (scripts/tools/run-rtl-testbenches.mjs with COURSE=synth): unoptimised chain and tree netlists from a light hierarchy-proc-flatten-techmap-clean flow, ABC mappings of both against sized.lib loose and the chain tight, and shared, kept-duplicate and heavy-load mappings of a shared-cone design. Depths are hand-derived from the recorded raw netlists; the depth program checks the same two numbers from the depth rules. The library is fictional, so absolute figures are teaching units.

    • Interview: run a mock interview on this topic

    Related topics

    • Up: Timing-Driven Synthesis

    • Next: Sizing and Path Groups

    • Structuring and Buffering

    Previous

    48 / 139

    Next: Sizing and Path Groups

    Up next

    Up nextSizing and Path Groups

    Sign in to save progress

    Practice this lessonPrevious lessonReport an error

    See also

    • Reading WNS and TNS
    • Margin and When to Stop