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

AboutContactPrivacyTerms
Skip to lesson

Course contents

  • How to Use This Course
  • Reading Paths
  • Concept Map
  • How to Read the Diagrams
  • Part 0: What Is a Protocol?10
  • Part 1: Timing and Clocking11
  • Part 2: Encoding and Framing12
  • Part 3: Handshakes13
  • Part 4: Flow Control17
  • Part 5: Transactions19
    • Reads and Writes Are Not Symmetric
    • The Request/Response Model
    • Address Maps and Decoding
    • Register Semantics
    • Combined and Split Transactions
    • Independent Channels
    • Posted and Non-Posted Writes
    • Bursts
    • Alignment, Strobes and Narrow Transfers
    • Boundary Rules
    • Width and Endianness Conversion
    • Transaction Attributes
    • Atomics
    • Sideband or In-Band
    • Interrupts and Notification
    • Descriptors, Rings and Doorbells
    • Bridges and Protocol Conversion
    • Protocols Compared: Transactions
    • Drills: Transactions
  • Part 6: Concurrency and Performance17
  • Part 7: Ordering13
  • Part 8: Sharing and Arbitration12
  • Part 9: Deadlock and Liveness13
  • Part 10: Reliability and Recovery20
  • Part 11: Bring-Up and Link Management10
  • Part 12: Power8
  • Part 13: Shared State and Coherence9
  • Part 14: Security and Isolation7
  • Part 15: Specifying and Verifying11
  • Part 16: Protocols Dissected17
  • Part 17: Capstones5
  • Part 18: Interview Preparation6
Loading lesson

‹Part 5: Transactions
  1. Topics
  2. First Principles of Hardware Protocols
  3. Part 5: Transactions
  4. Address Maps and Decoding

Part 5 · Transactions · Foundation (F)

Address Maps and Decoding

How an address map sends each request to one target, what holes and aliases are, and why an address with no target must still get an answer from a default target.

On this page

    Note

    Level: Foundation (F) · Answers: What exactly is being asked?

    Before this: The Request/Response Model

    A software bug writes to address 0x1800, a gap between the UART and the timer where nothing is mapped. On one chip the write is quietly ignored. On another, the processor reports a precise error that points at the bad instruction. On a third, the system hangs. The software bug is identical. The difference is how each chip's decoder treats an address that belongs to nobody.

    How does an address find its target, and what should happen when no target owns it?

    The obvious answer, and why it breaks

    The obvious decoder compares the address with each target's range and enables the one that matches. For mapped addresses that is all it needs to do. For an unmapped address no target is enabled, so no target responds, and a requester waiting for its one response (lesson 5.2) waits for ever. A decoder that only knows about the targets that exist is incomplete; it must also decide what happens for every address that does not.

    The idea: a complete map, with a default target

    An address map assigns ranges of addresses to targets. The decoder turns each request's address into exactly one select signal. To make the map complete, every address that matches no range selects a default target, which accepts the request and answers with a decode error. Now every request, mapped or not, gets exactly one response.

    Range

    Target

    Notes

    0x0000–0x0FFF

    RAM

    4 KB

    0x1000–0x10FF

    UART

    256 B; registers at the start

    0x1100–0x1FFF

    (hole)

    Default target: decode error

    0x2000–0x20FF

    Timer

    256 B

    0x2100–0xFFFF

    (hole)

    Default target: decode error

    Every address selects exactly one target; addresses in the holes select the default target, which answers with a decode error.

    Build it: a decoder that always answers

    This is DCLink step 4's decoder, cut down to three targets and 16-bit addresses. The requester sends six addresses, two of them in holes. The checker counts selects and responses.

    tb.sv
    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
    // DCLink step 4: an address decoder with a default target. Three regions are mapped:
    // RAM at 0x0000-0x0FFF, a UART at 0x1000-0x10FF and a timer at 0x2000-0x20FF.
    // Every other address selects the error responder, so every request gets exactly one
    // response: data from a real target, or an error from the default one.
    module tb;
      logic clk = 0;
      always #5 clk = ~clk;
      initial begin $dumpfile("wave.vcd"); $dumpvars(0, tb); end
    
      // Requests: one per cycle, each answered on the next edge
      logic        req = 0;
      logic [15:0] addr = 0;
      // Decode (combinational): exactly one select is high whenever req is high
      wire sel_ram   = req && (addr[15:12] == 4'h0);
      wire sel_uart  = req && (addr[15:8]  == 8'h10);
      wire sel_timer = req && (addr[15:8]  == 8'h20);
      wire sel_err   = req && !(sel_ram || sel_uart || sel_timer);
      // Response, one cycle later
      logic        rsp = 0, rsp_err = 0;
      logic [7:0]  rdata = 0;
      always @(posedge clk) begin
        rsp     <= req;
        rsp_err <= sel_err;
        rdata   <= sel_ram ? addr[7:0] : sel_uart ? 8'h55 : sel_timer ? 8'h7A : 8'h00;
      end
    
      // Stimulus: RAM, UART, an unmapped hole, timer, another hole, RAM
      logic [15:0] seq [0:5];
      initial begin
        seq[0] = 16'h0042; seq[1] = 16'h1004; seq[2] = 16'h1800;
        seq[3] = 16'h2010; seq[4] = 16'hF000; seq[5] = 16'h0FFF;
      end
      int i = 0;
      always @(posedge clk) begin
        if (i < 6) begin req <= 1; addr <= seq[i]; i <= i + 1; end
        else req <= 0;
      end
    
      // Checker: one select per request, one response per request, errors only for holes
      int errors = 0, responses = 0, errs = 0;
      always @(posedge clk) begin
        if (req && (sel_ram + sel_uart + sel_timer + sel_err) != 1) errors++;
        if (rsp) begin responses++; if (rsp_err) errs++; end
      end
      initial begin
        #100;
        if (errors == 0 && responses == 6 && errs == 2) $display("PASS: 6 requests, 6 responses; the 2 unmapped addresses got error responses");
        else $display("FAIL: errors %0d responses %0d error responses %0d", errors, responses, errs);
        $finish;
      end
    endmodule

    The four selects are mutually exclusive by construction: sel_err is high exactly when a request matches none of the others.

    Watch it run

    How to read it, slot by slot:

    • Slot 0: address 0x0042 selects the RAM (sel_ram). One slot later (rsp, slot 1) the RAM's data comes back: 0x42, its low address byte.

    • Slot 1: 0x1004 selects the UART; its answer, 0x55, arrives in slot 2.

    • Slot 2: 0x1800 is in the hole between the UART and the timer. Only sel_err is high. In slot 3, rsp and rsp_err are both high: an answer, and it is an error.

    • Slots 3 to 5: the timer (0x2010), another hole (0xF000, error in slot 5), and the last RAM byte (0x0FFF, data 0xFF in slot 6). Six requests, six responses, two of them errors.

    Holes, aliases and partial decoding

    • A hole is an unmapped range. It should always reach the default target.

    • An alias appears when a decoder compares fewer address bits than it could. A UART decoded only on bits 15:12 = 0x1 answers at 0x1000, 0x1100, 0x1200 and so on: sixteen copies. Partial decoding saves a few gates and causes bugs in which a stray pointer writes a device register instead of faulting.

    • Overlaps (two targets claiming one address) must be impossible. Check the map, and assert one-hot selects in simulation.

    How real systems chose

    System

    Unmapped access

    AXI interconnects

    A default target returns DECERR, the decode-error response; SoC tools provide an error-response target for exactly this.

    DCLink (step 4)

    Any address outside memory gets a completion with ERR status and no data.

    What breaks

    The opening story: the chip that ignored the write had a default target that returned success, so the bug hid for months. The chip that hung had no default target at all. The one that reported a precise error had a default target returning a decode error, and a processor that turned it into an exception at the faulting instruction. The last design is the one to copy: a bad address should fail loudly, at once, and say where.

    Why this matters

    Senior lens. Treat the address map as a specification with two checks: every address belongs to exactly one target (no overlaps, holes go to the default target), and every decoder compares all the address bits it owns (no accidental aliases). Generate the decoder, the map documentation and the software header from one source so they cannot disagree, and make the default target return an error, never fake success.

    Interview

    Asked: "What happens on an access to an unmapped address?"

    Weak answer: Nothing, it's ignored.

    Strong answer: It should go to a default target that accepts it and returns a decode error, so the requester still gets its one response and software gets a precise fault. In AXI that's DECERR, usually generated by the interconnect. Without a default target nothing answers and the requester hangs; with one that returns success, the bug is hidden. I'd also watch for aliases from partial decoding, where a device answers at many addresses.

    Follow-ups: (1) What is an alias, and why is it dangerous? (2) How would you check a decoder for overlaps?

    Check yourself A UART is decoded on address bits 15:12 only. At how many addresses in a 64 KB space does it appear?

    It answers anywhere in 0x1000 to 0x1FFF. If its registers use only the low 8 address bits, the same registers appear every 256 bytes: sixteen copies, at 0x1000, 0x1100, … 0x1F00.

    Check yourself Why should a default target return an error rather than success?

    Success hides the bug: software reads a plausible value or believes its write landed. An error turns a bad pointer into a precise fault.

    Key takeaways

    • An address map must be complete: every address selects exactly one target.

    • Unmapped addresses go to a default target that answers with a decode error, so nothing hangs and nothing is hidden.

    • Decode every address bit you own; partial decoding creates aliases.

    Keep going

    Next: Lesson 5.4, Register Semantics. Read-only, write-1-to-clear, read-to-clear: register behaviours, and the traps hidden in each.

    Related topics

    • Part 5 overview: Transactions

    Previous

    70 / 234

    Next: Register Semantics

    Up next

    Up nextRegister Semantics

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Reads and Writes Are Not Symmetric
    • Combined and Split Transactions
    • Independent Channels
    clkreqaddr42100418002010f000fffsel_ramsel_uartsel_timersel_errrsprsp_errrdata0425507a0ff0
    Simulation output: six requests, two of them to unmapped addresses. Every request gets one response; the holes get errors.