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
    • Power Budgets
    • Bandwidth Budgets
      • Workload to Bandwidth
      • Link Table and Sustained Rates
      • Spotting the Bottleneck
    • 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

←Bandwidth Budgets
  1. Topics
  2. SoC Design
  3. Architecture and Budgets
  4. Bandwidth Budgets
  5. Spotting the Bottleneck
On this page

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

    Spotting the Bottleneck

    Comparing demand against supply on every link: the same-bank trap and the oversubscription check.

    Why it matters

    Demands summed per link meet the sustained supply of that link, and the tightest link names the bottleneck. NanoSoC hides two textbook cases: source and destination in the same SRAM bank halve each stream to 180.7 MB/s, and a workload mix that sums past the achievable rate misses its target no matter how each master looks alone.

    The core idea

    Walk every link the workload touches and divide the demand that lands on it by its sustained supply: the highest ratio is the bottleneck, and anything above 1.0 fails. The unit of comparison is the port, not the master. The DMA job at 341.3 MB/s against 361.4 is at 94% when the read stream sits on one SRAM port and the write stream on the other: fits, with little room. Put the CPU's instruction-fill traffic (128 MB/s) on the same port as the DMA read and that port carries 469.3 MB/s against 361.4: a ratio of 1.30, so the job stretches. Put source and destination in the same bank and one single-port SRAM carries both streams, 2 x 341.3 = 682.7 MB/s against 361.4: a ratio of 1.89, which is the same as each stream getting 180.7 MB/s against its 341.3 demand. Add the CPU fill to that same port and the demand is 810.7 MB/s against 361.4: a ratio of 2.24, so the port delivers only 45% of the ask. The fix order is fixed: place buffers on separate banks, shorten bursts or arbitrate, and only then widen the link.

    The calculator

    The script runs the bottleneck check on three placements and proves which fails and by how much.

    example.py
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    dma, fill, port = 341.3, 128.0, 361.4  # MB/s; port = sustained rate of one SRAM port
    # Demand that lands on the busiest SRAM port in each placement.
    cases = {"split banks": dma,
             "split banks, CPU fills from the DMA source bank": dma + fill,
             "same bank (both streams on one port)": 2 * dma,
             "same bank plus CPU fill": 2 * dma + fill}
    ratios = {}
    for name, demand in cases.items():
        ratios[name] = demand / port
        print(f"  {name}: demand {demand:.1f} vs supply {port:.1f} = {ratios[name]:.2f} "
              f"{'OK' if ratios[name] <= 1.0 else 'BOTTLENECK'}")
    assert abs(ratios["split banks"] - 0.944) < 0.001 and ratios["split banks"] <= 1.0
    assert abs(ratios["split banks, CPU fills from the DMA source bank"] - 1.30) < 0.005
    assert abs(ratios["same bank (both streams on one port)"] - 1.889) < 0.005
    assert abs(dma / (port / 2) - 1.889) < 0.005  # same number as 341.3 against 180.7 per stream
    assert abs(ratios["same bank plus CPU fill"] - 2.24) < 0.005
    assert abs(port / (2 * dma + fill) - 0.446) < 0.002  # the port delivers 45% of the ask
    # The check can fail: the worst placement must be the one flagged as the bottleneck.
    worst = max(ratios, key=ratios.get)
    assert worst == "same bank plus CPU fill" and ratios[worst] > 1.0
    print("PASS: split fits at 0.94; CPU on the DMA bank 1.30; same bank 1.89; same bank plus CPU 2.24")

    What the review looks for

    • One demand-over-supply ratio per touched link, with the bottleneck named and its ratio quoted.

    • Buffer placement stated as a rule (DMA buffers in the bank the CPU is not fetching from), not as advice.

    • Every ratio above 0.9 carries a mitigation: placement, arbitration or scope cut.

    Verify and debug

    When silicon runs slower than the budget, check placement before rates: read back the actual source and destination addresses and confirm they sit in different banks. The same-bank trap is a software placement bug with a hardware symptom, and no link upgrade fixes it. Then check the burst lengths the master really issued — short bursts cut the efficiency the table assumed (burst length 1 halves it).

    Checking the budget per master instead of per link. The DMA fits (0.94), the CPU fill fits (128 MB/s against 361.4), and the spreadsheet is green, yet both land on the SRAM0 port and the port is oversubscribed at 1.30. The shared port is the thing that fails, so the demand must be summed where it meets the supply. Sum worst cases per link, and list which masters share each one.

    If asked to find the bottleneck, sum the demands per link out loud and divide by sustained supply. Name the highest ratio first; everything else is commentary.

    Confirm the bottleneck with measurement, not only arithmetic: fabric performance monitors count beats per master per window, and the losing master shows stalled READY or longer grant waits while the winner saturates. If arbitration is round-robin with a one-cycle change penalty, the fix for a 0.94 ratio may be arbitration tuning or burst-length discipline rather than placement — but placement comes first because it costs software nothing. Re-run the ratio after each fix; a bottleneck that moves to the next link is progress, and the table says which link is next.

    Check yourself The DMA source and destination are both in SRAM0, and the CPU fills its instruction cache from SRAM0 at 128 MB/s. By how much is the one SRAM port oversubscribed?

    Demand is 2 x 341.3 + 128 = 810.7 MB/s against 361.4 MB/s on the single port: a ratio of 2.24. The port delivers 45% of the ask. Move the DMA destination to SRAM1 first (the demand on each port then falls to 469.3 and 341.3, ratios 1.30 and 0.94), then move the CPU's fill traffic off the DMA source bank.

    Related topics

    • Link Table and Sustained Rates

    • Arch: Memory Bandwidth and Latency

    • Protocol First Principles: Little's Law

    Previous

    32 / 289

    Next: Memory Maps and Address Decode

    Up next

    Up nextMemory Maps and Address Decode

    Sign in to save progress

    Practice this lessonPrevious lessonReport an error

    See also

    • Workload to Bandwidth