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
      • IDs, Outstanding Counts and No WID
      • The 4 KB Rule and Burst Splitting
      • Width, Clock and Endianness Conversion
    • Ordering and Deadlock at the Top
    • QoS and Arbitration
    • NoC Basics
    • Coherent Interconnect View
    • Firewalls and Protection
    • Bandwidth and Latency Verification
  • 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

←AXI Integration Details
  1. Topics
  2. SoC Design
  3. Interconnect and Bus Fabrics
  4. AXI Integration Details
  5. Width, Clock and Endianness Conversion
On this page

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

    Width, Clock and Endianness Conversion

    Crossing 192 to 96 MHz with dual-clock FIFOs, and why NanoSoC converts clocks everywhere but widths nowhere.

    Why it matters

    The CPU runs at 192 MHz and the fabric at 96 MHz, and signoff treats the two as asynchronous despite the shared PLL: different divider roots mean uncontrolled skew. Every fetch the CPU makes crosses that boundary twice. If the crossing is a bare synchroniser on a multi-bit bus, the destination occasionally samples a mixed value and executes garbage. If it is a FIFO, a full FIFO does not overflow: with valid and ready, it drops ready and the sender waits. The question this lesson answers is therefore how much the FIFO depth costs in throughput, and whether the CPU ever notices.

    The core idea

    The nano_axi_cdc_m crosses with one 4-deep dual-clock FIFO per channel, gray-coded pointers and two-flop pointer synchronisers: multi-bit data never crosses without the FIFO, and no single-bit control crosses without two flops. That is the standing rule for all ten NanoSoC crossings. The clock ratio is fixed at 2 to 1, so there is no ratio to sweep; the numbers below are exact.

    Sizing starts from the rates. The source side can offer 4 x 192 = 768.0 MB/s, the fabric side takes 4 x 96 = 384.0 MB/s, and the slow side limits. At the full source rate and a 7 clk_sys loop (request crossing 3, xbar address 1, memory 2, xbar data 1) there are 0.875 of a 16-beat burst in flight, less than one burst. A FIFO that could swallow a whole burst without back-pressure would need about 16 x (1 - 384/768) = 8 entries plus a synchroniser allowance, and NanoSoC deliberately does not provide that. With 4 entries the sender may have 4 words outstanding per round trip, which bounds a streaming master at about 4 x 4 B / 72.9 ns = 219 MB/s (an illustrative window estimate). The CPU is a blocking master that never asks for more than 128 MB/s (line fills) or 42.67 MB/s (single words), so 4 entries never slow it. A streaming master added later would be limited by this FIFO before it was limited by the fabric.

    Width conversion is the dog that does not bark here: every fabric link is 32 bits, the DMA stream is 32 bits and APB is 32 bits, so WSTRB passes through untouched and no upsizer or downsizer can corrupt neighbouring bytes. Endianness is fixed the same way: the whole chip is little endian, and any future big-endian block would get its lane swizzle in its own wrapper, verified with byte and halfword accesses because full-word tests pass straight through a swizzle bug.

    example.py
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    # NanoSoC teaching values: what the 4-deep FIFOs in the CPU bridge can sustain.
    T_SYS = 1e9 / 96e6 # ns
    SRC_MBS = 4 * 192.0 # CPU side, 32 bits at 192 MHz = 768.0 MB/s
    SINK_MBS = 4 * 96.0 # fabric side, 384.0 MB/s: the slow side limits
    LOOP = 7 * T_SYS # ns: request crossing, fabric and memory (7 clk_sys)
    BURST_BYTES = 64 # 16 beats of 4 bytes
    
    in_flight_bursts = SRC_MBS * 1e6 * LOOP * 1e-9 / BURST_BYTES
    assert abs(in_flight_bursts - 0.875) < 0.001 # less than one burst in flight
    
    def window_limit_mbs(depth_words):
        # a full FIFO drops ready: depth words per round trip bounds the rate
        return min(SINK_MBS, depth_words * 4 / (LOOP * 1e-9) / 1e6)
    
    CPU_LINE_FILL = 128.0 # MB/s, the fastest the blocking CPU ever asks for
    CPU_WORD = 42.67 # MB/s, single-word reads
    assert window_limit_mbs(4) > CPU_LINE_FILL > CPU_WORD # 4 entries never throttle the CPU
    assert window_limit_mbs(4) < SINK_MBS # but would cap a streaming master below peak
    assert window_limit_mbs(1) < CPU_LINE_FILL # a 1-deep FIFO would throttle the CPU
    absorb = 16 * (1 - SINK_MBS / SRC_MBS) # entries to absorb a whole burst, not required
    assert absorb == 8.0
    print("PASS: 0.875 bursts in flight, 4 entries cap a stream near 219 MB/s but never throttle the CPU")

    What the review looks for

    • Every crossing names its mechanism: FIFO depth with the throughput arithmetic, or handshake with the hold-stable rule.

    • The width table shows 32 bits on every link, so the review confirms conversion logic is absent rather than assumed correct.

    • Endianness is stated once for the chip, and every block datasheet records its agreement or its wrapper swizzle.

    Verify and debug

    Drive back-to-back 16-beat line fills through the bridge with random stalls on valid and ready on both sides, and check with a scoreboard that no beat is dropped, duplicated or reordered: pointer-synchroniser and full or empty bugs show up only when both sides stall at awkward moments. Check that the FIFO never accepts a write while full (ready must be low) and never presents data while empty. Run byte, halfword and word accesses from software through any converter; a width bug corrupts neighbours on partial writes while full-word tests stay green.

    Width-converter WSTRB and narrow-burst errors: upsizing or downsizing mishandles strobes or narrow bursts and corrupts neighbouring bytes on partial writes. Caught by random narrow-transfer tests with a scoreboard and strobe assertions; fixed with a spec-compliant converter tested at every size and alignment. NanoSoC avoids the class by converting nothing, which the review must confirm rather than assume.

    If asked to size an async FIFO, start from the rates on each side and the round-trip loop, then say what happens when it fills: ready drops, throughput falls, nothing is lost. Quote the NanoSoC result: 0.875 of a burst in flight, 4 entries, enough for a blocking CPU.

    Check yourself Why can the 192 and 96 MHz clocks not be treated as synchronous when one PLL makes both?

    They leave through different divider roots with separate clock trees, so the skew between them is uncontrolled. Signoff treats them as asynchronous and the crossing uses FIFOs either way.

    Related topics

    • The 4 KB Rule and Burst Splitting

    • CDC and RDC: Async FIFO CDC

    • Protocol First Principles: Width and Endianness Conversion

    • Protocol First Principles: Crossing at the Interface

    Previous

    68 / 289

    Next: Memory-Map Verification

    Up next

    Up nextMemory-Map Verification

    Sign in to save progress

    Practice this lessonPrevious lessonReport an error

    See also

    • IDs, Outstanding Counts and No WID