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
  • Clock and Reset Architecture24
  • Power Architecture21
  • Interrupts, DMA and Peripherals15
  • Memories, Boot and Security21
  • IP Integration and Handoff21
    • IP Collateral and Versioning
    • Wrappers and Adapters
      • Why Every Block Is Wrapped
      • Width and Protocol Adapters
      • The NanoSoC Wrappers
    • Integration Lint and Connectivity Checks
    • Top-Level SDC and Constraint Merge
    • DFT Integration
    • Debug Infrastructure
    • Safety and Fault Architecture
  • Top-Level Verification and Bring-up21
  • Package, Pads and I/O18
  • Chip Signoff and Tapeout18
  • SoC Interview Prep16
  • Capstone: Integrate NanoSoC14
Loading lesson

←Wrappers and Adapters
  1. Topics
  2. SoC Design
  3. IP Integration and Handoff
  4. Wrappers and Adapters
  5. Width and Protocol Adapters
On this page

    Part 9 of 14, lesson 5 of 21Part 9 of 14/IP Integration and Handoff/Lesson 5 of 21All levels

    Width and Protocol Adapters

    Narrow and wide transfers without corrupting neighbours: strobe mapping, ordering, and a compiling sketch.

    Why it matters

    NanoSoC needs no width converter: every AXI link is 32 bits wide. The strobe question does not go away, though. It moves to the AXI-to-APB bridge, where APB3 carries no strobes and the DMA has no PSTRB port, so a byte store to a peripheral register reaches the block as a word write unless the bridge says otherwise. A bigger chip adds the classic version of the same failure: a width converter that mishandles byte strobes corrupts neighbouring bytes on partial writes, green in full-word tests and red in the field. Both cases need the same discipline, so this lesson covers the narrowing pattern done correctly and then the one protocol adapter NanoSoC really has.

    The core idea

    A narrowing adapter maps each wide beat onto narrower beats with the strobes following the data: the high half of a 64-bit write with its upper four strobes becomes one 32-bit beat, the low half with its lower four strobes another, and the order of the two beats matches the address order so that per-ID ordering is preserved downstream. A widening adapter does the reverse and must synthesise strobes for narrow bursts explicitly: a single-byte write becomes a full-width beat with exactly one strobe set, never a read-modify-write the adapter invented, because an invented read changes ordering and fault behaviour. Endianness is fixed at the same boundary: NanoSoC is little endian throughout, and any future big-endian block gets its lane swizzle in its own wrapper, verified with byte and halfword accesses since full-word tests pass straight through a swizzle bug.

    narrow_adapter.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // Datapath slice of a narrowing adapter (64-bit producer to a 32-bit link).
    // One wide beat becomes two narrow beats in address order; strobes
    // follow their data half. The beat counter that drives hi_half, the
    // burst-length rewrite and the response merge are not shown.
    module narrow_adapter (
      input  logic [63:0] wdata_in,
      input  logic [7:0]  wstrb_in,
      input  logic        hi_half,
      output logic [31:0] wdata_out,
      output logic [3:0]  wstrb_out
    );
      assign wdata_out = hi_half ? wdata_in[63:32] : wdata_in[31:0];
      assign wstrb_out = hi_half ? wstrb_in[7:4] : wstrb_in[3:0];
    endmodule

    A real narrowing adapter also splits the burst (it rewrites AxLEN and AxSIZE), may skip a beat whose strobes are all zero, and keeps ordering; the sketch is only the datapath slice. The checker below proves the mapping that slice implements against an independent byte-lane model: for each of the 256 strobe patterns, byte lane i of the wide beat must appear as lane i mod 4 of narrow beat i div 4, with the same strobe bit, and nothing else. The test is not allowed to pass for the wrong reason, so it also runs a deliberately broken adapter (halves swapped) and requires that it is caught.

    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
    def narrow(wdata, wstrb, hi):
        # Mirrors the narrow_adapter sketch: split data and strobes by half.
        if hi:
            return (wdata >> 32) & 0xFFFF_FFFF, (wstrb >> 4) & 0xF
        return wdata & 0xFFFF_FFFF, wstrb & 0xF
    
    def narrow_swapped(wdata, wstrb, hi):
        # Broken variant: strobes of the other half (a classic wiring slip).
        d, _ = narrow(wdata, wstrb, hi)
        return d, (wstrb & 0xF) if hi else (wstrb >> 4) & 0xF
    
    WDATA = 0xDEAD_BEEF_1234_5678
    
    def check(fn):
        for wstrb in range(256):
            lanes = [(wstrb >> i) & 1 for i in range(8)]   # reference model
            for hi in (0, 1):
                d, s = fn(WDATA, wstrb, hi)
                for b in range(4):
                    lane = 4 * hi + b
                    assert ((s >> b) & 1) == lanes[lane], (hex(wstrb), hi, b)
                    want_byte = (WDATA >> (8 * lane)) & 0xFF
                    assert ((d >> (8 * b)) & 0xFF) == want_byte, (hex(wstrb), hi, b)
    
    check(narrow)
    caught = False
    try:
        check(narrow_swapped)
    except AssertionError:
        caught = True
    assert caught, "the broken adapter must be rejected"
    print("PASS: all 256 strobe patterns map lane by lane; swapped-strobe adapter is caught")

    The adapter NanoSoC really has: AXI to APB

    NanoSoC has one protocol adapter, nano_axi2apb with the slot decoder behind it. It serialises an AXI burst into single APB accesses, maps PSLVERR to an AXI SLVERR response, and takes three clocks per access for a slave with the DMA's one wait state. AxPROT[1] goes to the slot decoder as a side signal, because APB3 has no PPROT to carry the secure attribute any further. Strobes are lost at this bridge: APB3 has no PSTRB, and the DMA has no PSTRB port, so every write is a full 32-bit write. A byte or halfword store from the CPU therefore reaches the peripheral as a word write, and the bytes the CPU did not mean to change are overwritten with whatever the write data lanes carry. The bridge has three legal answers: complete it as a word write, merge it with a read first, or refuse it. The review must see one of them written down; refusing (SLVERR for any partial strobe) is the least surprising, and software that needs byte access to a register is rewritten to use whole words.

    example.py
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    # One 32-bit peripheral register behind nano_axi2apb. A byte store to
    # lane 0 arrives with WSTRB = 0b0001 and write data lanes 1..3 not meaningful.
    def bridge_write(reg, wdata, wstrb, policy):
        """Return (new_reg, response). APB3 has no strobes to forward."""
        if policy == "refuse":
            if wstrb != 0xF:
                return reg, "SLVERR"
            return wdata, "OKAY"
        if policy == "word":            # the silent behaviour: strobes dropped
            return wdata, "OKAY"
        raise ValueError(policy)
    
    REG = 0xAABB_CC00                    # neighbour bytes hold live data
    BYTE_STORE = 0x0000_0011             # software meant: low byte = 0x11
    new, resp = bridge_write(REG, BYTE_STORE, 0b0001, "word")
    assert new == 0x0000_0011 and resp == "OKAY"       # neighbours destroyed
    new, resp = bridge_write(REG, BYTE_STORE, 0b0001, "refuse")
    assert new == REG and resp == "SLVERR"              # nothing changes, error seen
    new, resp = bridge_write(REG, 0x1234_5678, 0xF, "refuse")
    assert new == 0x1234_5678 and resp == "OKAY"        # full words still work
    print("PASS: word policy loses 3 neighbour bytes; refuse policy returns SLVERR and keeps them")

    What the review looks for

    • The strobe-mapping proof above, or its widening equivalent, rerun for the exact widths shipped.

    • Byte, halfword and word accesses from software through every converter, with neighbours checked, not just the written bytes.

    • The written rule for partial strobes at the AXI-to-APB bridge, plus a software test that does a byte store to a register and then reads it and its neighbour back.

    • No converter invents reads: partial writes synthesise strobes, never a read-modify-write cycle.

    Verify and debug

    Randomise transfer size and alignment against a scoreboard that checks every byte lane: a strobe failure needs narrow random traffic with neighbour checking, and directed full-word tests will never find it. On NanoSoC add the directed byte-store test through the APB bridge. If neighbours corrupt, dump the strobe bits beside the data beats; the offender is the adapter whose output strobes enable bytes the input strobes did not.

    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 plus strobe assertions; fixed with a spec-compliant converter tested at every size and alignment.

    If asked how a wide-to-narrow adapter handles strobes, answer lane by lane: each strobe bit follows its byte, beats stay in address order, and the proof is a table over all strobe patterns. Then volunteer the widening rule: synthesise strobes for narrow writes, never invent reads.

    Check yourself NanoSoC has 32-bit links everywhere. Does that mean strobes cannot go wrong?

    No. No width converter is needed, but APB3 has no strobes, so a byte store through nano_axi2apb becomes a word write unless the bridge refuses it. The review needs the partial-strobe rule written down and a byte-store test, and the width table must still show 32 bits on every link so a future wider port is flagged.

    Related topics

    • Why Every Block Is Wrapped

    • Next: The NanoSoC Wrappers

    • Protocol First Principles: Width and Endianness Conversion

    Previous

    176 / 289

    Next: The NanoSoC Wrappers

    Up next

    Up nextThe NanoSoC Wrappers

    Sign in to save progress

    Practice this lessonPrevious lessonReport an error