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
    • Boolean Optimisation
    • Technology Mapping
    • Cell Libraries and Liberty
    • Structuring and Buffering
      • Fanout, Slew and Capacitance Rules
      • Delay of a Driver with Load
      • Buffer Trees
      • Sizing, Cloning and Restructuring
      • Fanout Constraints in Practice
  • 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

←Structuring and Buffering
  1. Topics
  2. Logic Synthesis
  3. Logic Optimisation and Mapping
  4. Structuring and Buffering
  5. Sizing, Cloning and Restructuring
On this page

    Part 2 of 9, lesson 19 of 20Part 2 of 9/Logic Optimisation and Mapping/Lesson 19 of 20Intermediate

    Sizing, Cloning and Restructuring

    Recorded X1/X2/X4 choices against target and load, plus hand-priced cloning and restructuring.

    Sizing, cloning and restructuring all cost area but fix different stages. Picking the wrong one pays twice: once for the repair and once for the violation it creates upstream.

    After this lesson you can: read a size histogram off a sweep, price cloning on both sides of the driver, and separate depth fixes from load fixes.

    The question

    Real flows let the mapper spend area three ways: sizing a cell up, cloning a driver so each copy feeds fewer sinks, and restructuring logic to cut depth. This lesson records the first across target and load, then prices the other two with hand arithmetic that names its sources; the break-it is a recorded chain/tree pair.

    Sizing: the recorded sweep

    Sizing swaps a cell for a stronger sibling with the same function: plain, X2 and X4 in the teaching library, each step faster and larger. The script below maps one decoder five times: loosely and tightly at the typical output load, loosely and tightly at the heavy load, and once more at the tight target against the single-size library, which has no X2 or X4 to buy. The loose runs must use only X1 cells and the tight sized runs at least one bigger one, all asserted.

    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
    read_verilog <<EOV
    module decode (input [11:0] x, output y, output z);
      assign y = ((x[0] & x[1]) | (x[2] & x[3] & x[4]) | (x[5] ^ x[6]) | (x[7] & ~x[8])) & (x[9] | x[10] | ~x[11]);
      assign z = ^x[7:0] ^ (x[8] & x[9]);
    endmodule
    EOV
    synth -top decode -flatten
    design -save generic
    tee -o abc_loose.txt abc -liberty sized.lib -constr typical.constr -D 1000
    tee -o stat_loose.txt stat -liberty sized.lib
    write_json net_loose.json
    select -assert-none t:*_X2 t:*_X4
    design -load generic
    tee -o abc_loose_heavy.txt abc -liberty sized.lib -constr heavy.constr -D 1000
    tee -o stat_loose_heavy.txt stat -liberty sized.lib
    select -assert-none t:*_X2 t:*_X4
    design -load generic
    tee -o abc_tight.txt abc -liberty sized.lib -constr typical.constr -D 600
    tee -o stat_tight.txt stat -liberty sized.lib
    write_json net_tight.json
    select -assert-min 1 t:*_X2 t:*_X4 %u
    design -load generic
    tee -o abc_tight_heavy.txt abc -liberty sized.lib -constr heavy.constr -D 600
    tee -o stat_tight_heavy.txt stat -liberty sized.lib
    select -assert-min 1 t:*_X2 t:*_X4 %u
    design -load generic
    tee -o abc_tight_1size.txt abc -liberty course.lib -constr typical.constr -D 600
    tee -o stat_tight_1size.txt stat -liberty course.lib
    select -assert-none t:*_X2 t:*_X4
    design -reset
    read_verilog <<EOV
    module chain8(input [7:0] x, output y);
      wire s1 = x[0] & x[1];
      wire s2 = s1 & x[2];
      wire s3 = s2 & x[3];
      wire s4 = s3 & x[4];
      wire s5 = s4 & x[5];
      wire s6 = s5 & x[6];
      assign y = s6 & x[7];
    endmodule
    EOV
    synth -top chain8 -flatten
    design -save chain
    design -reset
    read_verilog <<EOV
    module tree8(input [7:0] x, output y);
      wire t0 = x[0] & x[1];
      wire t1 = x[2] & x[3];
      wire t2 = x[4] & x[5];
      wire t3 = x[6] & x[7];
      wire u0 = t0 & t1;
      wire u1 = t2 & t3;
      assign y = u0 & u1;
    endmodule
    EOV
    synth -top tree8 -flatten
    design -save tree
    design -load chain
    tee -o abc_chain.txt abc -liberty sized.lib -constr typical.constr -D 1000
    tee -o stat_chain.txt stat -liberty sized.lib
    select -assert-count 5 t:AND2 t:NAND2 t:NAND3 t:NOR3
    design -load tree
    tee -o abc_tree.txt abc -liberty sized.lib -constr typical.constr -D 1000
    tee -o stat_tree.txt stat -liberty sized.lib
    select -assert-count 5 t:AND2 t:NAND2 t:NAND3 t:NOR3
    log PASS: the target buys sizes and the load buys buffers and one size buys nothing
    Predict first The tight run asks for 600 ps where the loose run asked for 1000. Will the mapper rewrite the logic, or keep the structure and buy drive?

    It keeps the structure and buys drive: the same 18 cells with 2 upsized logic cells at the tight target, unchanged when the heavy load is added on top.

    Run

    Cells

    Area

    Delay reached

    Buffers

    Logic upsized

    loose, typical

    18

    65

    750 ps

    0

    0

    loose, heavy

    20

    69

    890 ps

    2

    0

    tight, typical

    18

    81

    594 ps

    0

    2

    tight, heavy

    20

    88.2

    678 ps

    2

    2

    tight, one size

    18

    65

    750 ps

    0

    0 (none exist)

    Read the table along its two axes against the 600 ps the tight runs asked for. Down the target axis at the typical load, the count holds at 18 cells while 2 XNOR2 cells harden to X4: same structure, stronger slow path, landing at about 594 ps against the 600 asked, inside by about 6 ps. Down the load axis at the loose target, the count moves from 18 to 20 with every logic cell still size 1: the heavier output load inserts 2 buffers with no target pressure. The tight heavy run stacks both repairs yet still misses, about 78 ps short; the single-size tight run misses further, about 150 ps short: with nothing stronger to buy, the target buys nothing.

    Recorded size histogram, every cell accounted for

    Tight typical

    Tight heavy

    NAND2 at X1 (never moves)

    2

    2

    XNOR2 at X1

    3

    3

    XNOR2 at X4 (the slow path)

    2

    2

    BUF at X1

    0

    1

    BUF at X4 (upsized with the load)

    0

    1

    all other cells (stay X1)

    11

    11

    The histogram accounts for every cell: both tight sized runs upsize XNOR2 only, with 11 other cells staying X1 in the typical run and 11 other cells in the heavy one, and no buffer appears until the load grows. It also names the tight heavy run's extra area over the loose heavy run: 19.2 units, of which 16 come from the two XNOR2 cells growing from 5 to 13 each and 3.2 from the one BUF hardening from 2 to 5.2, all four digits copied exactly from the sized library.

    The two netlists, side by side

    Both netlists come from the saved write_json files. The loose netlist is all size 1; the tight one keeps the structure with 2 XNOR2 cells upsized to X4, drawn in orange.

    The decoder mapped loosely: all size 1, no buffers, no upsized cells.
    The same decoder mapped tightly: 2 XNOR2 cells upsized to X4, drawn in orange.

    Cloning, including the input-net bill

    Cloning duplicates the driver so each copy feeds a subset of the sinks: less load per copy and sharper edges, at the price of a second driver. The other side sits upstream: the feeding net now drives two input pins. The arithmetic below uses the library's own digits: an X1 inverter input is 0.01 pF and an X1 inverter cell is area 1, both copied exactly, with the inverter rise row giving 0.05 ns at 0.01 pF and 0.07 ns at 0.1 pF.

    Diagram
    hand arithmetic from the copied inverter row (0.05 ns at 0.01 pF, 0.07 at 0.1 pF):
    slope k = (0.07 - 0.05) / 0.09 = 0.2222 ns per pF
    before: driver sees wire 0.02 pF plus 12 loads of 0.01 pF = 0.14 pF (extrapolated: past the 0.1 pF table edge)
    D = 0.05 + 0.2222 x 0.13 = 0.0789 ns, about 79 ps (extrapolated)
    after 2-way cloning (6 sinks per copy):
    each copy sees 0.02 + 6 x 0.01 = 0.08 pF
    D = 0.05 + 0.2222 x 0.07 = 0.0656 ns, about 66 ps
    saving = 79 - 66 = 13 ps for twice the driver area (1 + 1 = 2 units)
    upstream bill: the feeding net gains one more 0.01 pF input pin, 0.01 to 0.02 pF

    Thirteen picoseconds saved against a doubled driver and a heavier upstream net. When the upstream net already runs near its own slew limit, the clone files another violation while fixing one, so a buffer tree or stronger upstream driver belongs in the same repair.

    Restructuring for depth

    Balanced trees cut logic depth without touching load: an 8-input AND needs seven two-input gates however arranged, but a chain of seven has depth 7 against 3 for the balanced tree of 4 plus 2 plus 1. Restructuring pairs with buffering rather than replacing it: one cuts stages of logic, the other cuts load per stage.

    Diagram
    8-input AND from 2-input AND gates: 8 - 1 = 7 gates either way
    chain: 7 gates in series -> depth 7
    balanced tree: 4 + 2 + 1 gates per level -> depth 3
    same count, depth 3 against 7

    Break it: chain versus tree

    Restructuring has had no recorded run so far; this break-it adds the pair. The script maps two eight-input ANDs: one written as a chain of seven two-input gates, one as a balanced tree of 4 plus 2 plus 1. The abc -D script's rewriting canonicalizes both RTLs to the same cell mix at the same area and arrival, before any size or buffer decision:

    RTL

    Cells

    Cell mix

    Area

    Delay reached

    chain of seven

    5

    1 AND2, 2 NAND2, 1 NAND3, 1 NOR3

    13

    382 ps

    balanced tree

    5

    1 AND2, 2 NAND2, 1 NAND3, 1 NOR3

    13

    382 ps

    Restructuring is the one repair the mapper does unasked; sizes and buffers wait for pressure. The hand depth arithmetic, 7 against 3, prices what the RTL author controls, not what the mapper keeps. The fifth row above still shows the other side: the same tight target against the single-size library matches the loose typical run exactly, since a constraint steers the tool only among choices the library provides.

    Common mistakes

    A clone that still drives every sink fixes nothing: each copy must own its subset of loads with no shared tail net rejoining them. And a clone that overloads its own input net trades one violation for another, so price both sides every time.

    Q: when do you clone a driver, and when do you size it instead?

    Weak answer: cloning splits the load, sizing strengthens the driver; both cost area. Strong answer: restructure when depth dominates, as in the recorded chain/tree pair reaching the same mix either way; clone when the load is many sinks that partition cleanly, and only after checking the upstream bill, here an extra 0.01 pF input pin per copy; size when one stage is slow but the sink count is small, as in the recorded sweep, with 2 XNOR2_X4 cells on the same 18-cell structure while the NAND2 cells never move. Follow-up: the clone still misses timing. What is wrong? Either the copies share a tail net, or the upstream net, now heavier, is the new slow stage.

    Check yourself Cloning halves each copy's sinks from 12 to 6 in the worked arithmetic. What are the two delays, and what does the feeding net pay?

    About 79 ps before and about 66 ps after, saving 13 ps for twice the driver area. The feeding net pays one more 0.01 pF input pin.

    Check yourself The tight typical row holds the cell count while the size mix moves. Why is that sizing rather than restructuring?

    Same logic, stronger cells on the slow path: the arrangement is unchanged and only the drive strengths moved. Restructuring would change the gate arrangement and depth at a similar count.

    • Tighter targets first buy bigger cells on the slow path, not new logic.

    • Every clone adds its input pin to the upstream net; price both sides.

    • Restructuring cuts depth, buffering cuts load per stage; slow paths need both.

    Rule of thumb: restructure deep logic first, then size the slow stage, then clone partitionable loads.

    How this was checked

    Every number and picture on this page comes from one Yosys 0.69 run of the script shown, recorded by the course checker (scripts/tools/run-rtl-testbenches.mjs with COURSE=synth): ABC maps one decoder against sized.lib at loose and tight delay targets under typical.constr and heavy.constr, plus one tight run against the single-size library and a chain-RTL versus tree-RTL pair, with stat and netlists saved. The cloning arithmetic is hand-derived from pin digits copied exactly from sized.lib and labelled so, as are the 7-against-3 depth counts. The libraries are fictional teaching files.

    • Interview: run a mock interview on this topic

    Related topics

    • Fanout constraints in practice

    • Area, power and timing tradeoffs

    Previous

    35 / 139

    Next: Fanout Constraints in Practice

    Up next

    Up nextFanout Constraints in Practice

    Sign in to save progress

    Practice this lessonPrevious lessonReport an error

    See also

    • Fanout, Slew and Capacitance Rules
    • Delay of a Driver with Load