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
  • Memory Maps and Address Decode26
  • Interconnect and Bus Fabrics26
    • Topologies and Bridges
    • AXI Integration Details
    • Ordering and Deadlock at the Top
    • QoS and Arbitration
    • NoC Basics
    • Coherent Interconnect View
    • Firewalls and Protection
    • Bandwidth and Latency Verification
      • The Budget Table as Arithmetic
      • Checking Latency Link by Link
      • Performance Counters and What a Miss Looks Like
  • 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

←Bandwidth and Latency Verification
  1. Topics
  2. SoC Design
  3. Interconnect and Bus Fabrics
  4. Bandwidth and Latency Verification
  5. The Budget Table as Arithmetic
On this page

    Part 4 of 14, lesson 24 of 26Part 4 of 14/Interconnect and Bus Fabrics/Lesson 24 of 26All levels

    The Budget Table as Arithmetic

    From width times clock to sustained rates: every row of the link table recomputed and asserted.

    Why it matters

    A budget table that nobody can recompute is a rumour. The NanoSoC link table (in the NanoSoC tour) already shows its formulae, peak as width times clock and sustained as peak times burst efficiency, but formulae in prose drift from the numbers beside them. This lesson pins them together in one model: change a clock or a width and every dependent number must move, or the model, not the table, is wrong.

    The core idea

    Peak on any link is width in bytes times clock in megahertz, giving megabytes per second directly. Sustained multiplies by burst efficiency, beats over beats plus the one bubble per burst. The APB rows replace the bubble with clocks per access: three clocks for the bus alone, five with the bridge's accept and respond, and 208.33 ns per access behind the apb_cdc. The model below builds the whole table as a function of the three clocks, compares every peak and sustained value against the published numbers with a relative tolerance (so the 0.0115 MB/s UART row is checked as strictly as the 722.8 MB/s row), and is then run with a changed clock to prove the comparison can fail.

    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
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    # NanoSoC teaching values: the link table as a function of the three clocks.
    def link_table(f_cpu=192.0, f_sys=96.0, f_per=24.0):
        # name -> (peak MB/s, sustained MB/s)
        def sus(peak, burst):
            return peak * burst / (burst + 1)
        cdc_ns = 2 * (1e3 / f_per) + 2 * (1e3 / f_per) + 2 * (1e3 / f_sys) + 2 * (1e3 / f_sys)
        return {
            "cpu_to_cdc": (4 * f_cpu, 16 / (24 * 1e3 / f_cpu) * 1e3), # one 4-beat line fill = 24 clk_cpu
            "cdc_to_xbar": (4 * f_sys, sus(4 * f_sys, 16)),
            "sbr_read": (4 * f_sys, sus(4 * f_sys, 16)),
            "sbr_write": (4 * f_sys, sus(4 * f_sys, 16)),
            "sram_bank": (4 * f_sys, sus(4 * f_sys, 16)),
            "same_bank_each": (4 * f_sys, sus(4 * f_sys, 16) / 2),
            "rom": (4 * f_sys, sus(4 * f_sys, 16)),
            "both_banks": (2 * 4 * f_sys, 2 * sus(4 * f_sys, 16)),
            "dma_in": (4 * f_sys, sus(4 * f_sys, 16)),
            "dma_out": (4 * f_sys, sus(4 * f_sys, 16)),
            "burst8": (4 * f_sys, sus(4 * f_sys, 8)),
            "burst4": (4 * f_sys, sus(4 * f_sys, 4)),
            "burst1": (4 * f_sys, sus(4 * f_sys, 1)),
            "apb_fast": (4 * f_sys / 3, 4 * f_sys / 5), # 3 clocks bus, 5 with accept and respond
            "apb_fast_zero_wait": (4 * f_sys / 2, 4 * f_sys / 4),
            "apb_slow": (4 * f_per / 2, 4 / cdc_ns * 1e3), # 208.33 ns per access through the apb_cdc
            "uart": (f_per * 1e6 / (16 * 13) / 10 / 1e6,) * 2, # line rate: 10 bits per byte
            "spi": (f_per / (2 * (1 + 1)) / 8,) * 2, # 6 Mb/s line
        }
    
    EXPECT = {
        "cpu_to_cdc": (768.0, 128.0), "cdc_to_xbar": (384.0, 361.4), "sbr_read": (384.0, 361.4),
        "sbr_write": (384.0, 361.4), "sram_bank": (384.0, 361.4), "same_bank_each": (384.0, 180.7),
        "rom": (384.0, 361.4), "both_banks": (768.0, 722.8), "dma_in": (384.0, 361.4),
        "dma_out": (384.0, 361.4), "burst8": (384.0, 341.3), "burst4": (384.0, 307.2),
        "burst1": (384.0, 192.0), "apb_fast": (128.0, 76.8), "apb_fast_zero_wait": (192.0, 96.0),
        "apb_slow": (48.0, 19.2), "uart": (0.01154, 0.01154), "spi": (0.750, 0.750),
    }
    
    def mismatches(table):
        bad = []
        for name, want in EXPECT.items():
            for got, w in zip(table[name], want):
                if abs(got - w) > 1e-3 * w: # relative tolerance: tiny rows are checked as hard as big ones
                    bad.append(name)
                    break
        return bad
    
    assert mismatches(link_table()) == []
    assert len(EXPECT) * 2 == 36 # 18 rows, peak and sustained
    # the comparison can fail: a 100 MHz fabric moves every clk_sys row
    assert "sbr_read" in mismatches(link_table(f_sys=100.0))
    assert "uart" not in mismatches(link_table(f_sys=100.0)) # clk_per rows do not depend on clk_sys
    # removing one bridge accept cycle: 4 x 96 / 4 and 4 B / (208.33 - 10.4167 ns)
    assert abs(4 * 96 / 4 - 96.0) < 1e-9
    assert abs(4 / (208.3333 - 10.41667) * 1e3 - 20.2) < 0.05
    print("PASS: 36 link-table numbers reproduced, a changed clock is caught, one bridge cycle gives 96.0 and 20.2 MB/s")

    What the review looks for

    • The model script is versioned with the map and the clocks: new divider, new run, new table.

    • Every row states the burst length or clocks-per-access its sustained number assumes, and peak and sustained are never mixed in one row.

    • Illustrative numbers, where the table is silent, are labelled and kept consistent across lessons.

    Verify and debug

    Run the model before every simulation campaign and diff its output against the table in the specification: any drift means the spec or the clocks moved without the other. When a simulated link disagrees with its row, check the traffic shape first, burst lengths and idle gaps, exactly as the hub's check-yourself warns, before suspecting the RTL.

    Peak and sustained mixed in one row: a budget quotes 384.0 for a link in one column and 361.4 in another, or multiplies the peak by the whole run time, and a stream that meets the 361.4 row is reported as failing the 384.0 row. Caught by a model that carries both columns and a reviewer who checks the unit of each; fixed by one definition per column.

    If asked where a bandwidth number comes from, derive it live: width times clock, times efficiency, naming the burst length and the bubble. Then give the NanoSoC pair, 384 peak and 361.4 sustained, as the worked example.

    Check yourself The bridge accept cycle is removed by a redesign. Which rows change, and to what?

    The with-bridge APB rows: the fast segment rises from 76.8 to 4 x 96 / 4 = 96.0 MB/s (128.0 needs both bridge clocks gone), and 19.2 becomes 4 B / 197.92 ns = 20.2 MB/s. The bus-only rows do not move.

    Related topics

    • Protocol First Principles: Peak and Sustained Bandwidth

    • Next: Checking Latency Link by Link

    • Protocols: Bandwidth-Latency Budgeting

    Previous

    94 / 289

    Next: Checking Latency Link by Link

    Up next

    Up nextChecking Latency Link by Link

    Sign in to save progress

    Practice this lessonPrevious lessonReport an error

    See also

    • Performance Counters and What a Miss Looks Like