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
      • Arrival, Required Time and Slack
      • Setup, Hold and Clock-to-Q
      • Cell Delay Models
      • Wire Delay at Synthesis Time
      • A Static Timing Calculator
    • Timing-Driven Synthesis
  • Sequential, FSM and Memory19
  • Datapath Synthesis14
  • Area, Power and Test14
  • Verification, ECO and Handoff15
  • Flows and Debugging15
  • Interview and Judgment11
Loading lesson

←Delay Models and Slack
  1. Topics
  2. Logic Synthesis
  3. Constraints and Timing
  4. Delay Models and Slack
  5. Wire Delay at Synthesis Time
On this page

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

    Wire Delay at Synthesis Time

    The shared path under two output loads, and what a wire estimate hides.

    Synthesis quotes slack without knowing a single wire length, and engineers sign off on it anyway. Knowing exactly what the load number stands for is what keeps that estimate honest.

    After this lesson you can: map one path under two output loads, explain the cover change, and price a wire segment with an Elmore model.

    The question

    Synthesis runs before placement, so it cannot know wire lengths. Yet every net has capacitance, and capacitance is delay. This lesson measures what the tool does about that contradiction on the shared vipexp path: it maps the same block under two output loads, watches the cover change, and prices the one wire model small enough to check by hand.

    The two loads are the course constraint files. typical.constr puts a 0.05 pF load on every output; heavy.constr puts 0.2 pF there instead. Both files also name a BUF driving cell for the inputs. Those numbers are lumped pin loads, not a wire model, and saying plainly what they are, and are not, is half of this lesson.

    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
    read_verilog <<EOV
    module vipexp(input clk, input a, b, c, d, input din, output reg qout, output t1, t2, t3, t4);
      reg q1;
      wire s1, s2, s3, s4;
      always @(posedge clk) q1 <= din;
      assign s1 = ~(q1 & a);
      assign s2 = ~(s1 | b);
      assign s3 = s2 ^ c;
      assign s4 = ~(s3 ^ d);
      always @(posedge clk) qout <= s4;
      assign t1 = s1;
      assign t2 = s2;
      assign t3 = s3;
      assign t4 = s4;
    endmodule
    EOV
    hierarchy -check -top vipexp
    synth -top vipexp -flatten
    dfflegalize -cell $_DFF_P_ 01 -cell $_DFF_PN0_ 01
    design -save generic
    dfflibmap -liberty course.lib
    tee -o abc_typ.txt abc -liberty course.lib -constr typical.constr -D 1000
    clean
    tee -o stat_typ.txt stat -liberty course.lib
    write_json net_typ.json
    select -assert-none t:$not t:$and t:$or t:$xor t:$mux t:$_NOT_ t:$_AND_ t:$_OR_ t:$_XOR_ t:$_NAND_ t:$_NOR_ t:$_DFF_P_ t:$_MUX_
    select -assert-count 2 t:DFF
    select -assert-count 1 t:NAND2
    select -assert-count 1 t:NOR2
    select -assert-count 1 t:XOR2
    select -assert-count 1 t:XNOR2
    check
    design -load generic
    dfflibmap -liberty course.lib
    tee -o abc_heavy.txt abc -liberty course.lib -constr heavy.constr -D 1000
    clean
    tee -o stat_heavy.txt stat -liberty course.lib
    write_json net_heavy.json
    select -assert-count 2 t:DFF
    select -assert-min 4 t:BUF
    check
    log PASS: one path under a light load and a heavy load

    The script is the familiar vipexp mapping, run twice from the saved generic netlist with only the constraint file changed. The typical run is the cover every other lesson prices: 4 cells at area 14 with arrival 610 ps. The heavy run is the break-it and the result in one: 8 cells at area 22 with arrival 1230 ps. Same RTL, same target, same library; only the load number moved.

    Predict first Four times the output load must slow the taps. Will ABC keep the cover and accept slower taps, or spend cells to protect the path?

    It spends cells. The recorded heavy histogram reads BUF x4, DFF x2, NAND2 x1, NOR2 x1, XNOR2 x1, XOR2 x1: the four logic stages survive unchanged and a BUF lands between every stage and the next, each driving its tap port as well as the following stage, isolating the logic from the heavy load at a cost of 8 area units and about 620 ps of arrival. The cover change is the answer, not a side effect.

    What the two runs print

    Load

    Cells

    Area

    Arrival

    Typical (0.05 pF)

    4

    14

    610 ps

    Heavy (0.20 pF)

    8

    22

    1230 ps

    Read the table as cause and effect. The heavy cover holds 4 BUF cells the typical cover never needed, one between every stage and the next, and the recorded histogram confirms it: BUF x4, DFF x2, NAND2 x1, NOR2 x1, XNOR2 x1, XOR2 x1 against DFF x2, NAND2 x1, NOR2 x1, XNOR2 x1, XOR2 x1. Each BUF drives its tap load while the logic stage behind it sees only the BUF input pin, so the logic delays barely move; but the same BUF output also feeds the next stage, so the flop-to-flop spine passes through one BUF per stage and pays all four. The arrival roughly doubles, from 610 ps to 1230 ps, and the area rises by 8 units for the four buffers.

    The recorded heavy-load netlist: the same four logic stages between the two flops, now with a BUF cell between every stage and the next, each driving its tap output.

    Compare this drawing with the typical-load netlist two lessons back: the logic spine is identical, and each new BUF sits between its stage and the next, driving both the tap port and the following stage. That placement is the tool telling you where it thinks the load hurts: the buffers shield the logic from the tap loads, and the spine passes through all four. ABC's delay is the maximum over all its outputs, and the recorded netlist shows the worst path ends on the net the t4 port shares with the capture flop's D input, so the 1230 ps path is the capture path. Against the 1.00 ns period from the earlier slack arithmetic, the heavy arrival plus model clock-to-Q reaches about 1.39 ns against a 0.94 ns requirement: negative slack, failing, on the same clock that met timing under the typical load.

    What a wire-load model adds, and what it hides

    A wire-load model assigns each net a capacitance from its fanout or from the size of the block, before any wire exists. What it adds over the lumped pin load is fanout awareness: a net driving four pins gets more capacitance than a net driving one, so net-heavy paths stop looking free. What it hides is everything placement decides: length, layer, coupling to neighbours, and detours around congestion. It is a statistical guess with the right average shape, wrong in both directions on any particular net.

    ABC's constr load is simpler than even that: one fixed capacitor per output port, no fanout term, no length term. Treat it as what it is, a load the mapper must drive, never as a prediction of routed wire. The heavy run shows the honest use: sweep the load number and watch the cover respond. The dishonest use is quoting the resulting slack as signoff: post-layout extraction replaces every one of these guesses with measured nets.

    example.py
    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
    import sys  # stdlib only: no files, one PASS line
    
    # Elmore delay of a wire segment, stated as a model (not a recording).
    # A 0.05 pF wire is split into two RC lumps: R1, C1 near the driver and R2, C2
    # near the load. Elmore: each resistor sees its downstream capacitance.
    R1, C1 = 1.0, 0.025  # kohm, pF
    R2, C2 = 1.0, 0.025
    
    def elmore(r1, c1, r2, c2):
        return r1 * (c1 + c2) + r2 * c2
    
    def close(got, want, tol=1e-9):
        return abs(got - want) <= tol
    
    bad = 0
    def check(name, cond):
        global bad
        if not cond:
            print("FAIL: " + name)
            bad += 1
    
    check("single lump RC", close(elmore(1.0, 0.05, 0.0, 0.0), 0.05))
    check("zero resistance is zero", close(elmore(0.0, 0.025, 0.0, 0.025), 0.0))
    check("zero capacitance is zero", close(elmore(1.0, 0.0, 1.0, 0.0), 0.0))
    check("two lumps add downstream", close(elmore(R1, C1, R2, C2), 0.075))
    check("doubling R doubles delay", close(elmore(2 * R1, C1, 2 * R2, C2), 2 * 0.075))
    check("wider split, same total", close(elmore(1.0, 0.04, 1.0, 0.01), 0.06))
    if bad != 0:
        sys.exit(1)
    print("PASS: Elmore model of a 0.05 pF wire gives 0.075 ns in two lumps")

    The program prices the typical tap load as two RC lumps: 0.075 ns of model delay, on the order of one NAND2 stage. That is the scale synthesis guesses at and layout measures. The asserts pin the model's structure, not silicon: zero resistance gives zero delay, doubling resistance doubles the delay, and splitting the same capacitance differently moves the total exactly as the downstream rule says. A model that cannot fail these checks cannot be trusted with the ones that matter.

    Common mistakes

    Quoting synthesis slack as signoff timing over-reads an estimate. The slack guides optimisation: only the post-layout check with extracted nets closes timing. A path that meets timing here can still fail there, and the heavy run shows how little load it takes to flip the verdict.

    Why is wire delay guessed at synthesis, and what does the guess cost here?

    Weak: wires are unknown until layout, so the tool estimates. Strong: the constr load is lumped per output with no fanout or length term, so quadrupling it forces one BUF between every stage and the next: the recorded arrival roughly doubles and the area rises from 14 to 22 units, and the same 1.00 ns clock flips from a plus 0.16 ns setup slack to a negative one. Follow-up: the estimate says the path meets by 0.05 ns. Do you sign off?

    Answer: never on an estimate. The wire-load guess hides length and coupling in both directions; only extracted post-layout timing closes the check.

    Check yourself Same RTL, same target, two loads. What changed in the cover, and what does each new cell protect?

    Four BUF cells appeared, one between every stage and the next: BUF x4, DFF x2, NAND2 x1, NOR2 x1, XNOR2 x1, XOR2 x1. Each buffer shields its logic stage from the 0.2 pF tap load, so the logic delays barely move; the same buffer output feeds the next stage, so the spine pays four buffer delays instead, about 620 ps of extra arrival in total.

    Check yourself A colleague quotes the heavy-load slack as proof the block fails timing. What is wrong with that argument?

    The heavy load is still a lumped guess, not extracted wire: it proves the cover responds to load, not that any routed net matches the guess. Failing on a guess means re-examine the assumption and the cover, then close timing after layout.

    • ABC's constr load is a lumped pin load per output, with no fanout, length or coupling term.

    • A wire-load model adds fanout awareness but still hides everything placement decides.

    • Only post-layout extraction closes timing; synthesis slack guides optimisation.

    Rule of thumb: sweep the load and watch the cover before believing any synthesis slack.

    How this was checked

    Areas, arrivals, histograms and both netlists on this page come from one Yosys run of the script shown, recorded by the course checker (scripts/tools/run-rtl-testbenches.mjs with COURSE=synth): the vipexp block mapped with ABC against course.lib at a 1000 ps target with typical.constr (0.05 pF) and heavy.constr (0.2 pF). The library is fictional, so absolute delays are teaching values; the cover change is the point. The Elmore wire delay is a hand-built model computed by the stdlib-only program shown, labelled so on the page.

    • Interview: run a mock interview on this topic

    Related topics

    • Cell Delay Models

    • A Static Timing Calculator

    • Timing-Driven Synthesis

    Previous

    45 / 139

    Next: A Static Timing Calculator

    Up next

    Up nextA Static Timing Calculator

    Sign in to save progress

    Practice this lessonPrevious lessonReport an error

    See also

    • Arrival, Required Time and Slack
    • Setup, Hold and Clock-to-Q