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

  • How to Use This Course
  • The NanoSoC Tour
  • Reading Path
  • SoC Glossary10
  • SoC Metrics Reference
  • SoC Cheatsheet
  • What SoC Integration Is12
  • Architecture and Budgets21
    • Partitioning and Hierarchy
    • Area Budgets
    • Timing Budgets Across Hierarchy
      • Cycle Budgets for Crossing Paths
      • I/O Delay Budgeting
      • Margin Allocation and Handoff
    • Power Budgets
    • Bandwidth Budgets
    • Budget Governance
    • Multi-Die and Chiplet View
  • Memory Maps and Address Decode26
  • Interconnect and Bus Fabrics26
  • Clock and Reset Architecture24
  • Power Architecture21
  • Interrupts, DMA and Peripherals15
  • Memories, Boot and Security21
  • IP Integration and Handoff21
  • Top-Level Verification and Bring-up21
  • Package, Pads and I/O18
  • Chip Signoff and Tapeout18
  • SoC Interview Prep16
  • Capstone: Integrate NanoSoC14
Loading lesson

←Timing Budgets Across Hierarchy
  1. Topics
  2. SoC Design
  3. Architecture and Budgets
  4. Timing Budgets Across Hierarchy
  5. Margin Allocation and Handoff
On this page

    Part 2 of 14, lesson 9 of 21Part 2 of 14/Architecture and Budgets/Lesson 9 of 21All levels

    Margin Allocation and Handoff

    Holding timing margin at the top, handing blocks budgets they can close, and what happens on a miss.

    Why it matters

    A budget that spends every cycle on day one has nowhere to go when a block returns late. NanoSoC's timing closes with room to spare at these frequencies, which is exactly why it is the right place to learn the discipline cheaply: hold margin at the top, hand each block a budget tighter than the requirement, and spend the margin only through the change process.

    The core idea

    Margin is unallocated budget held by the integrator, not slack left inside a block. The method: set a cycle allowance for the path, hold back spare cycles at the top, and split the remainder among the blocks with a little hand-out slack, so that each block is handed a budget tighter than the requirement but not exactly its measured cycles. A block that closes with room keeps its room against its own budget; it does not donate it silently to a neighbour. A block that misses by less than the top margin can be covered by a governed margin spend; a miss beyond the margin reopens the partition or the frequency, never the requirement.

    Step

    NanoSoC fetch-path example

    Value

    Allowance (illustrative requirement)

    Fetch must complete within

    9 sys + 4 cpu cycles

    Top-held margin

    One spare sys cycle, teaching value

    1 sys cycle (10.4167 ns, 11.1% of the sys allowance)

    Handed to blocks

    Allowance minus top margin

    8 sys + 4 cpu cycles

    Measured path

    Fetch table rows added

    7 sys + 4 cpu

    Hand-out slack

    Handed minus measured

    1 sys cycle, held by the memory wrapper

    Handed per block (sys)

    Bridge crossing 3, fabric 2, memory 3

    8 sys cycles in total

    The calculator

    The script prices the margin policy in cycles and checks that the measured fetch fits the handable total with the spare cycle untouched.

    example.py
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    allow_sys, allow_cpu = 9, 4  # illustrative requirement, in cycles of the named clocks
    spare_sys = 1  # top-held margin, NanoSoC teaching value
    hand_sys = allow_sys - spare_sys
    handed = {"bridge crossing": 3, "fabric": 2, "memory wrapper": 3}  # sys cycles
    measured = {"bridge crossing": 3, "fabric": 1 + 1, "memory wrapper": 2}  # fetch table rows
    assert sum(handed.values()) == hand_sys
    assert sum(measured.values()) == 7 and 1 + 3 == allow_cpu  # 7 sys + 4 cpu measured
    assert all(measured[b] <= handed[b] for b in handed)  # every block closes
    slack = sum(handed.values()) - sum(measured.values())
    assert slack == 1  # hand-out slack: a budget tighter than the requirement, not zero
    def covered(miss_sys):
        # a miss is covered by hand-out slack first, then by the top margin
        return miss_sys <= slack + spare_sys
    assert covered(2) and not covered(3)  # the policy can fail: a 3-cycle miss reopens the plan
    t_sys = 1 / 96e6 * 1e9
    assert abs(spare_sys * t_sys - 10.4167) < 0.001  # margin held in cycles, converted once
    margin_share = spare_sys / allow_sys
    assert abs(margin_share - 1 / 9) < 1e-9
    print(f"PASS: measured 7 sys + 4 cpu inside handed 8 sys + 4 cpu; slack {slack}, margin {spare_sys} sys cycle ({margin_share*100:.1f}%)")

    What the review looks for

    • The margin share is written down once per project and applied to every handed budget.

    • Handed budgets name cycles and clocks; the margin is held in cycles of the named clock too, and converted to nanoseconds once.

    • A miss procedure exists before the first miss: cover from margin, restructure, or replan — in that order.

    Verify and debug

    Recompute every handed budget from requirement minus margin whenever the requirement moves: a frequency change that forgets the margin silently spends it. For the link to analysis mechanics — how the handed numbers become constraints and how signoff proves them — see the STA course's constraint and signoff material, which this topic consumes but does not repeat.

    Margin kept inside the blocks. Each of three owners hands back a budget with a little room 'just in case', the top holds nothing, and the first block that returns one cycle late has no cover: the whole path must be replanned. Hold the spare cycle in one place under one owner, and let blocks keep only the hand-out slack that was written into their budget.

    If asked where margin lives, answer at the top, in one number, under one owner. Margin distributed silently into blocks is not margin; it is optimism.

    Connect the handed budgets to the constraint set explicitly: each block's cycle allowance becomes its boundary constraints, owned by the top level and merged with the block's own mode set. The audit that closes the loop lists every handed budget against its constraint — an allowance with no constraint is unmanaged, and a constraint with no allowance is unbudgeted optimism. That consistency check between the budget table and the constraint merge is the chip-level discipline the timing signoff flow consumes.

    Check yourself A block closes 3% over its handed budget but within the top margin. What happens?

    The integrator covers it with a governed margin spend: record the draw, shrink the remaining margin, and continue. No replan needed, but the spend is visible in the ledger.

    Related topics

    • I/O Delay Budgeting

    • PD: Budgeting Partitions and Their Interfaces

    • STA: Interface Budgets

    Previous

    26 / 289

    Next: Power Budgets

    Up next

    Up nextPower Budgets

    Sign in to save progress

    Practice this lessonPrevious lessonReport an error

    See also

    • Cycle Budgets for Crossing Paths