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. Sizing and Path Groups
On this page

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

    Sizing and Path Groups

    Gate sizing on the worst path, plus path groups and weights that split effort.

    One delay target cannot say the datapath matters more than a peripheral bus. Violation-driven sizing already spends where it hurts; groups and weights add the priority for passes where everything hurts at once.

    After this lesson you can: show that a single target sizes only the violating cone, read a group_weight cost model, and state plainly that Yosys and ABC have no path groups.

    Sizing trades drive for delay, one gate at a time

    A larger gate drives its load faster and occupies more area: that is the whole trade behind sizing. The first script maps an arithmetic cone of AND-OR and XOR stages twice against sized.lib, once with a very loose target and once at -D 400. The loose run is the baseline: 6 cells at area 17 and 422 ps, not one of them upsized. Against -D 400 the same function maps to 6 cells at area 25 and 344 ps, and the histogram names the price: 1 XNOR2_X4 cell where a plain cell stood. Delay falls by 78 ps for 8 area units, spent gate by gate on the slowest stages.

    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
    read_verilog <<EOV
    module arithcone(input [7:0] x, output y);
    assign y = ((x[0] & x[1]) | (x[2] & x[3])) ^ ((x[4] & x[5]) | (x[6] & x[7]));
    endmodule
    EOV
    synth -top arithcone -flatten
    design -save arith
    tee -o arith_loose.txt abc -liberty sized.lib -constr typical.constr -D 100000
    tee -o arith_loose_stat.txt stat -liberty sized.lib
    write_json arith_net.json
    select -assert-none t:*_X2 t:*_X4
    design -load arith
    tee -o arith_tight.txt abc -liberty sized.lib -constr typical.constr -D 400
    tee -o arith_tight_stat.txt stat -liberty sized.lib
    write_json arith_tight_net.json
    select -assert-min 1 t:*_X2 t:*_X4 %u
    design -reset
    read_verilog <<EOV
    module ctrlcone(input [5:0] x, output y);
    assign y = (x[0] & x[1] & x[2]) | (x[3] & x[4]) | x[5];
    endmodule
    EOV
    synth -top ctrlcone -flatten
    design -save ctrl
    tee -o ctrl_loose.txt abc -liberty sized.lib -constr typical.constr -D 100000
    tee -o ctrl_loose_stat.txt stat -liberty sized.lib
    select -assert-none t:*_X2 t:*_X4
    design -load ctrl
    tee -o ctrl_tight.txt abc -liberty sized.lib -constr typical.constr -D 400
    tee -o ctrl_tight_stat.txt stat -liberty sized.lib
    select -assert-none t:*_X2 t:*_X4
    design -load ctrl
    tee -o ctrl_vtight.txt abc -liberty sized.lib -constr typical.constr -D 100
    tee -o ctrl_vtight_stat.txt stat -liberty sized.lib
    select -assert-min 1 t:*_X2 t:*_X4 %u
    design -reset
    read_verilog <<EOV
    module both(input [7:0] x, input [5:0] c, output y_arith, output y_ctrl);
    wire aa = ((x[0] & x[1]) | (x[2] & x[3])) ^ ((x[4] & x[5]) | (x[6] & x[7]));
    wire cc = (c[0] & c[1] & c[2]) | (c[3] & c[4]) | c[5];
    assign y_arith = aa;
    assign y_ctrl = cc;
    endmodule
    EOV
    synth -top both -flatten
    tee -o both_tight.txt abc -liberty sized.lib -constr typical.constr -D 400
    tee -o both_tight_stat.txt stat -liberty sized.lib
    write_json both_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
    select -assert-min 1 t:*_X2 t:*_X4 %u
    log PASS: one target sizes only the violating cone

    The select lines assert this: no upsized cell in the loose cone, at least one in the tight cone and in the combined design. In a commercial flow this loop runs inside the tool, guided by slack: upsize the slowest stage of the worst path, re-rank, repeat until the slack closes or the library runs out of sizes. Yosys and ABC run the same loop behind a single -D target shared by every output, which is why the lesson's second half matters: one number cannot say which path deserves the next upsize.

    The arithmetic cone mapped for a 400 ps target: 6 cells, area 25, the upsized stage in orange.

    The group that needed nothing

    The same script maps a second, smaller cone: three AND-OR stages behind one output. Its recorded reports read identically at both targets: 3 cells at area 8 and 208 ps, loose or tight, with no upsized cell either way. The cone already arrives in 208 ps against a 400 ps target, so there is no violation to spend on. Sizing is violation-driven: a path that meets its target draws no effort no matter how tight the rest of the design is.

    Cone (recorded ABC report)

    Cells

    Area

    Delay reached

    Upsized cells

    Arithmetic, loose target

    6

    17

    422 ps

    0

    Arithmetic, -D 400

    6

    25

    344 ps

    1

    Control, loose target

    3

    8

    208 ps

    0

    Control, -D 400

    3

    8

    208 ps

    0

    Predict first Both cones are placed under a single -D 400 target in one design. Which cone gets upsized cells, and what happens to the other?

    Only the arithmetic cone changes: 1 cell in its cone grows while the control cone keeps all plain cells. The tool sizes the violator and leaves the path that already meets its target alone, even though both share one constraint.

    One target for two cones

    The last design in the script joins both cones behind two outputs of one top module and maps them together at -D 400. Splitting the recorded netlist by output cone confirms the prediction: 1 upsized cell in the arithmetic cone, 0 upsized cells in the control cone. The figure shows it as an effort map: orange only where the violation was.

    Both cones under one target: upsized cells only in the arithmetic cone, the control cone untouched.

    Groups and weights order the effort

    Violation-driven effort has a blind spot: when two paths both fail, the tool must choose, and one number cannot express that the core datapath matters more than a slow peripheral bus. Commercial flows answer with path groups: named sets of paths with a shared target and a weight, so the cost function the tool minimises prices each group's violation by its weight. The SDC below is shown for reading only: the open tools in this course have no SDC reader, and Yosys and ABC have no path groups at all. Here every output shares one -D target and one load; there is no weight to set and no group to join. What follows is the idea those commands express, demonstrated with a model, not a measurement.

    Diagram
    # not-run: no SDC reader in the open tools (commercial syntax, shown for reading only)
    group_path -name core -weight 10 -to [get_cells data_reg*]
    group_path -name slow -weight 1 -to [get_ports status_*]
    # cost minimised: 10 * violation(core) + 1 * violation(slow)

    The program below runs that cost function on model numbers: two failing groups, a core group violating by 80 ps at weight 10 against a slow group violating by 40 ps at weight 1, and three effort units that erase 30 ps each. Each unit goes to the group with the largest weight times remaining violation, and the checks pin the order and the leftover: three units to core before slow sees any, because 10 times 80 outranks 1 times 40 from the first step. Labels matter here: every figure in this section is a model, while every area and delay in the tables above is a recording.

    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
    import sys
    
    # MODEL of group-weighted cost (illustrative numbers, not tool output):
    # two failing groups, weights 10 and 1, three effort units of 30 ps each.
    start = {"core": 80, "slow": 40}
    viol = dict(start)
    weight = {"core": 10, "slow": 1}
    STEP = 30
    order = []
    for _ in range(3):
        pick = max(viol, key=lambda g: weight[g] * viol[g])
        order.append(pick)
        viol[pick] = max(0, viol[pick] - STEP)
    if order != ["core", "core", "core"]:
        print("FAIL: effort order " + str(order) + " want all core")
        sys.exit(1)
    if viol != {"core": 0, "slow": 40}:
        print("FAIL: leftover " + str(viol) + " want core 0 slow 40")
        sys.exit(1)
    cost = lambda v: sum(weight[g] * v[g] for g in v)
    cost_before = cost(start)
    cost_after = cost(viol)
    if not (cost_after < cost_before):
        print("FAIL: weighted cost did not fall from " + str(cost_before))
        sys.exit(1)
    print("PASS: weights send all three effort units to core, cost " + str(cost_before) + " to " + str(cost_after))

    Read the model's lesson back onto the recordings: the arithmetic cone drew effort because it violated, the control cone drew none because it met. Weights would only change the order if both had violated. The starvation warning comes free with the model: a weight of 1 against a weight of 10 means the slow group waits several passes, which is correct when the project says so and a bug when nobody set out to say it.

    Break it: make the easy cone violate

    The script's final single-cone run constrains the control cone alone at -D 100, far tighter than its 208 ps natural arrival. Now the easy cone is the violator, and the recorded result flips: 3 cells at area 14.8, reaching 147 ps against -D 100, still a miss at 47 ps over, but upsized where the -D 400 runs stayed plain. Effort follows the violation, not the cone's reputation: whichever path misses draws the next upsize. That is the mechanism weights modulate: a single target already implements the greedy half, and weights add the priority half for passes where everything fails at once.

    Common mistakes

    Sizing every gate on a failing path instead of the slowest ones grows area for the same arrival. Rank the stages, upsize the worst, then re-rank before the next step.

    Two groups fail and you have budget for one transform. Where does it go?

    • Weak answer: the group with the bigger violation.

    • Strong answer: the bigger weight times violation, since the tool minimises cost as weight times violation: the arithmetic cone here trades 78 ps for 8 area units, and the model sends all three effort units to the 10x core before the 1x slow group sees any.

    • Follow-up: what if the weights are equal, or the tool has no groups? Equal weights fall back to pure violation order, and Yosys and ABC have no groups at all, so one target always sizes purely by violation.

    Check yourself The control cone reports the same 8 area at both targets. Why did it draw no effort?

    It already arrives in 208 ps against a 400 ps target, so its slack is positive and there is no violation to spend on. Sizing follows violations, and this cone has none.

    Check yourself At -D 100 the control cone finally gets upsized. What does that prove about the -D 400 result?

    That the -D 400 result was never about the cone being unsizable: the same cells upsize readily once the cone actually violates. Effort follows the violation, and weights only reorder violations that arrive together.

    • Sizing follows violations: a cone that meets its target draws no effort.

    • Weights multiply violations, so effort serves the highest weight-times-violation first.

    • Yosys and ABC share one -D target and one load across all outputs; the SDC groups here are reading-only.

    Rule of thumb: when several violations compete, price each by weight times violation before assigning effort.

    How this was checked

    Areas, delays, cell counts and netlist pictures on this page come from one Yosys 0.70 run of the script shown, recorded by the course checker (scripts/tools/run-rtl-testbenches.mjs with COURSE=synth): ABC maps an arithmetic cone and a control cone against sized.lib loose and tight, plus both cones together under one target. Cone splits are hand-derived from the recorded netlist files. The group-weight program is a labelled model with illustrative numbers, and the group_path listing is commercial syntax shown for reading only. 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: Reading WNS and TNS

    • Technology Mapping

    Previous

    49 / 139

    Next: Reading WNS and TNS

    Up next

    Up nextReading WNS and TNS

    Sign in to save progress

    Practice this lessonPrevious lessonReport an error

    See also

    • Critical-Path Focus
    • Margin and When to Stop