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. Fanout, Slew and Capacitance Rules
On this page

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

    Fanout, Slew and Capacitance Rules

    Max fanout, transition and capacitance limits with a recorded load comparison.

    A net that looks logically fine can still violate three electrical rules, and the tool will silently spend your area fixing them. Knowing where the rules live turns a surprise upsizing into a read-off.

    After this lesson you can: read max fanout, transition and capacitance off the library and the constraints, count load units instead of sinks, and split a repair into its buffers and its sizes from recorded runs.

    The question

    One net drives sixteen loads, and the tool quietly changes your cells without anything looking logically wrong. This lesson shows which three rules the net is checked against, where each is written down, and which repair the mapper reaches for as the load grows: buffers, or stronger cells. Every comparison below is a real Yosys and ABC run.

    Where the three rules live

    Each rule lives in two places. The library describes what its cells can drive: every X1 output pin in the teaching library carries at most 1.0 pF, 2.0 on X2 and 4.0 on X4, with a 1.0 ns default for the slowest allowed edge. The constraints say what this design may use: a maximum transition, capacitance and fanout, set in a commercial flow with the three commands sketched below. The sketch is illustrative: Yosys has no such commands, and only the ABC runs here are recorded.

    Rule

    Library side (sized.lib, copied exactly)

    Constraint side (commercial, illustrative)

    Max fanout

    default fanout load 1 per input pin

    set_max_fanout

    Max transition

    default maximum transition 1.0 ns

    set_max_transition

    Max capacitance

    X1 pins 1.0 pF, X2 2.0 pF, X4 4.0 pF

    set_max_capacitance

    Commercial max fanout counts load units, not sinks: each input pin contributes its fanout load attribute, defaulting to 1. Each load here is a NAND2 built as ~(n & c[i]), so only its n-side input touches the shared net; the c-side input is driven from outside. Sixteen loads present sixteen units, and a commercial limit of 16 would be exactly met. Counting sinks agrees here only by accident.

    One net, sixteen loads, two output loads

    The design below is a driver NAND2 feeding sixteen load NAND2 cells, each with its own second input; every load survives mapping while contributing one pin to the shared net: the sixteen units the Python check replays. The script maps the same generic netlist four times: loosely and tightly under the typical output load, then loosely and tightly under the heavy one. The typical constraint file asks for a BUF driver and 0.05 pF on every output; the heavy file keeps the driver and asks for 0.2 pF. A four-load sibling at the end feeds the break-it, and an eight-load half feeds the fanout-cap comparison.

    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
    read_verilog <<EOV
    module drv(input a, input b, input [15:0] c, output [15:0] y);
      wire n = ~(a & b);
      genvar i;
      generate for (i = 0; i < 16; i = i + 1) begin : g
        assign y[i] = ~(n & c[i]);
      end
      endgenerate
    endmodule
    EOV
    synth -top drv -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-count 17 t:NAND2 t:NAND2_X2 t:NAND2_X4
    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
    write_json net_loose_heavy.json
    select -assert-count 17 t:NAND2 t:NAND2_X2 t:NAND2_X4
    select -assert-none t:*_X2 t:*_X4
    design -load generic
    tee -o abc_typ.txt abc -liberty sized.lib -constr typical.constr -D 250
    tee -o stat_typ.txt stat -liberty sized.lib
    write_json net_typ.json
    select -assert-count 17 t:NAND2 t:NAND2_X2 t:NAND2_X4
    select -assert-min 1 t:*_X2 t:*_X4 %u
    design -load generic
    tee -o abc_heavy.txt abc -liberty sized.lib -constr heavy.constr -D 250
    tee -o stat_heavy.txt stat -liberty sized.lib
    write_json net_heavy.json
    select -assert-count 17 t:NAND2 t:NAND2_X2 t:NAND2_X4
    select -assert-min 1 t:*_X2 t:*_X4 %u
    design -reset
    read_verilog <<EOV
    module drv4(input a, input b, input [3:0] c, output [3:0] y);
      wire n = ~(a & b);
      genvar i;
      generate for (i = 0; i < 4; i = i + 1) begin : g
        assign y[i] = ~(n & c[i]);
      end
      endgenerate
    endmodule
    EOV
    synth -top drv4 -flatten
    design -save gen4
    design -load gen4
    tee -o abc_b4_typ.txt abc -liberty sized.lib -constr typical.constr -D 250
    tee -o stat_b4_typ.txt stat -liberty sized.lib
    select -assert-count 5 t:NAND2 t:NAND2_X2 t:NAND2_X4
    select -assert-none t:*_X2 t:*_X4 t:BUF t:BUF_X2 t:BUF_X4
    design -load gen4
    tee -o abc_b4_heavy.txt abc -liberty sized.lib -constr heavy.constr -D 250
    tee -o stat_b4_heavy.txt stat -liberty sized.lib
    write_json net_b4_heavy.json
    select -assert-count 5 t:NAND2 t:NAND2_X2 t:NAND2_X4
    select -assert-min 1 t:*_X2 t:*_X4 %u
    design -reset
    read_verilog <<EOV
    module drv8(input a, input b, input [7:0] c, output [7:0] y);
      wire n = ~(a & b);
      genvar i;
      generate for (i = 0; i < 8; i = i + 1) begin : g
        assign y[i] = ~(n & c[i]);
      end
      endgenerate
    endmodule
    EOV
    synth -top drv8 -flatten
    design -save gen8
    design -load gen8
    tee -o abc_cap8_heavy.txt abc -liberty sized.lib -constr heavy.constr -D 250
    tee -o stat_cap8_heavy.txt stat -liberty sized.lib
    write_json net_cap8_heavy.json
    select -assert-count 9 t:NAND2 t:NAND2_X2 t:NAND2_X4
    log PASS: load decides the inserted buffers and the target decides the sizes

    The select lines assert the structures: all seventeen NAND2-family cells in every 16-load run, all size 1 in both loose runs, at least one larger cell in both tight runs, all five with no buffers in the typical break-it, and all nine in the capped half.

    Diagram
    # illustrative sketch (not run): what the commercial tool calls this step
    set_max_fanout 16 [current_design]
    set_max_transition 1.0 [current_design]
    set_max_capacitance 1.0 [current_design]

    What the four runs printed

    Predict first The heavy runs change only the output load, from 0.05 to 0.2 pF. Will the mapper answer with stronger cells, with more cells, or with both?

    With both, split by axis: heavy runs add buffers, tight runs add sizes. The loose heavy run inserts 17 buffers and upsizes nothing; the tight heavy run keeps them and upsizes too.

    Run

    Cells

    Area

    Delay reached

    Buffers (outputs + shared net)

    Logic upsized

    loose, typical

    18

    36

    335 ps

    1 (0+1)

    0

    loose, heavy

    34

    68

    477 ps

    17 (16+1)

    0

    tight, typical

    18

    42.4

    248 ps

    1 (0+1)

    1

    tight, heavy

    34

    114.8

    336 ps

    17 (16+1)

    2

    Read the table one axis at a time; each buffer count splits into outputs versus the shared net. Down the load axis at the loose target the count moves from 18 to 34 with every logic cell still size 1: the heavier output load alone orders 17 (16+1) buffers against 1 (0+1). Down the target axis the count stays at 18 while one NAND2 hardens to X4. No rule is violated anywhere: the shared net carries 0.16 pF against the 1.0 pF pin limit, so buffers-for-load plus sizes-for-target is this ABC script's split of duties, not a general law. Scorecard: the tight heavy run lands at about 336 ps, short by about 86 ps, and the tight typical run at about 248 ps, inside; every loose run and both break-it runs meet their targets.

    The two tight netlists, side by side

    Both netlists come from the write_json files; orange marks inserted and upsized cells.

    Typical load: 1 buffer on the shared net, 1 upsized NAND2 cell.
    Tight run under the heavy load: 17 (16+1) buffers and 2 upsized NAND2 cells.

    Break it: shrink the fanout to four loads

    The script ends with a new variant: the same driver feeding four loads instead of sixteen, mapped tightly twice. At the typical load it records 5 cells and no buffer at all; under the heavy load 4 (4+0) buffers with 1 upsized logic cell, every buffer on an output. The shared-net repair vanishes with the fanout while per-output buffers follow the output load at any fanout. Which threshold fires the shared-net buffer is not isolated by these runs: ABC's buffer step takes a fanout cap (-N, default 10) and a gain target (-G, default 300) in this build.

    What a fanout cap buys

    The cap is structural: ABC takes no numeric limit through this runner: abc -script, the only way to pass buffer -N, is rejected, so the cap is structural. The script maps an 8-load half of the same netlist under the same heavy, tight conditions; two such halves are what a cap of 8 allows:

    Version

    Buffers (outputs + shared net)

    Area

    Delay reached

    uncapped, 16 loads

    17 (16+1)

    114.8

    336 ps

    one capped half, 8 loads

    8 (8+0)

    62.8

    249 ps

    two halves (doubled)

    16 (16+0)

    125.6

    about 249 ps per half

    The cap removes the shared-net buffer (1 to 0) while the per-output buffers stand (16 against 16): fanout sets the shared-net repair, output load the per-output one, for about 125.6 area against 114.8 uncapped: a second driver, each half still paying its heavy-load buffers.

    Common mistakes

    A net can pass max fanout yet fail max transition. Long wires add capacitance without adding sinks, so check all three rules on every repaired net, and count load units, not sinks, when you do.

    Q: your net drives sixteen NAND2 gates and the limit is set_max_fanout 16. Violation?

    Weak answer: sixteen sinks against sixteen, so it exactly meets. Strong answer: units, not sinks, decide it: only each n-side pin sits on the net, and 0.16 pF against the 1.0 pF limit passes with margin while the heavy runs still buffer for delay. Follow-up: in a fixed netlist where both inputs of every load tie to the shared net, the count is thirty-two units against sixteen: a real violation, and the repair is a buffer tree.

    Check yourself Sixteen NAND2 loads share one driver net. How many fanout load units is that, and why?

    Sixteen units: only each load's n-side pin touches the shared net, one unit per pin, with each c-side pin driven from outside.

    Check yourself The tight heavy run adds 17 buffers and 2 upsized logic cells next to the tight typical run. What did that area buy, and what rule was never violated?

    Per-output buffers carry the extra capacitance and the upsized pair holds the slow path: 0.16 pF on the shared net against the 1.0 pF limit, so the repair is delay-driven.

    • Commercial max fanout counts load units per input pin on the net, not sinks.

    • Design-rule repairs outrank timing in the cost function.

    • In this ABC script, load orders buffers at any target; only a tight target on top orders stronger cells.

    Rule of thumb: count load units on every shared net, and check all three rules after each repair.

    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 a driver plus sixteen NAND2 loads against sized.lib under typical.constr (0.05 pF) and heavy.constr (0.2 pF) at loose and tight delay targets, with stat and netlists saved so each buffer count splits into outputs versus the shared net, plus a four-load sibling at the tight target for the break-it and an eight-load half of the same netlist for the cap comparison. The set_max_* sketch is a commercial-tool illustration and was not run. The libraries are fictional teaching files, so the shape of the trade, not the digits, is the point.

    • Interview: run a mock interview on this topic

    Related topics

    • Delay of a driver with load

    • SDC for Synthesis

    Previous

    32 / 139

    Next: Delay of a Driver with Load

    Up next

    Up nextDelay of a Driver with Load

    Sign in to save progress

    Practice this lessonPrevious lessonReport an error

    See also

    • Buffer Trees
    • Sizing, Cloning and Restructuring
    • Fanout Constraints in Practice