Skip to content
VLSI Mentor

Wishbone · Module 23

Address Decoder RTL

B3 never describes a decoder but does say whose job it is. A one-digit mask error steals sixteen addresses, and probing inside the overlap shows nothing wrong — the damage is at an address that used to be a hole.

Chapter 16.4 built wb_split2: it decodes one address bit into two slaves. Module 19's SoC built a tree of them, which works, and does not generalise. A five-slave map with unequal region sizes is not a balanced binary tree, and forcing it into one is how address maps acquire holes.

This chapter builds the general case: N regions, any size, any alignment, with the two failure modes a real map actually has — addresses that match nothing, and addresses that match twice.

1. B3 Does Not Describe A Decoder. It Does Say Whose Job It Is.

Search Wishbone B3 for an address decoder and you will not find one. What you will find is the specification quietly assigning the work to somebody, in its Partial Address Decoding section:

"each SLAVE decodes only the range of addresses that it requires... The remaining address bits are decoded by the interconnection system."

That sentence is the entire normative basis for this chapter. It establishes three things and no more:

  1. A slave decodes only the bits it needs. Chapter 23.3's bank uses [ADR_I][3:0] and ignores everything above — that is not laziness, it is the model B3 describes.
  2. Somebody else decodes the rest. That somebody is the interconnect.
  3. Nothing about how.

Everything below — the table format, first-match priority, what happens to an unmapped address — is local policy, and this chapter names each one as it arrives rather than presenting them as though the specification required them.

2. Base And Mask, And Why The Mask Is The Size

A region matches when:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
//     (adr & mask) == (base & mask)

The mask says which bits must match, and its zeros are the region's size. A mask of 0xF00 with base 0x200 selects 0x2000x2FF — 256 words, because eight bits are free.

This is why base/mask generalises where a split tree does not. The four regions in the running example are deliberately unequal:

regionrangemasksize
0 — ROM0x0000x0FF0xF00256 words
1 — RAM0x4000x4FF0xF00256 words
2 — timer0x8000x80F0xFF016 words
3 — gpio0x8100x81F0xFF016 words

A 256-word region and a 16-word region, adjacent regions and distant ones, in one flat table. wb_split2 nested to any depth cannot express that map.

The table itself is packed, for the same portability reason as Chapter 23.3's policy table:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Packed rather than unpacked because Icarus -g2012 rejects unpacked
// arrays as parameters.

And the match is a bounded loop over all N regions in parallel — every region is tested, not just until one hits:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  logic [7:0] raw;      // every region that matches, not just the winner
  logic [7:0] onehot;   // first match only

raw is the load-bearing variable in this module. Keeping every match, rather than stopping at the first, is what makes Section 6 possible.

3. First Match Wins — And That Is A Choice

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  // first match wins - the lowest set bit of raw
  always_comb begin
    onehot = 8'd0;
    for (i = 7; i >= 0; i = i - 1)
      if (raw[i]) onehot = (8'd1 << i);
  end

Deterministic, cheap, and it makes an overlap silent. The second region simply never sees those addresses. So the decoder reports overlaps as well as surviving them:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  // more than one region claimed this address
  logic multi;
  assign multi = any && ((raw & (raw - 8'd1)) != 8'd0);

That expression is the standard "more than one bit set" idiom: subtracting one from a value clears its lowest set bit, so a non-zero AND means at least two were set.

An alternative policy is to make overlap an elaboration error. That is stronger, and it cannot describe a legitimately shadowed region — a debug aperture deliberately placed over part of RAM, for instance. This module reports rather than refuses, and says so, which is a different answer from the one many real interconnect generators give.

4. RULE 3.30 Belongs Here, Once

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  // RULE 3.30 lives here too: a decoder that selects a slave when
  // [CYC_I] is negated would make that slave respond outside a cycle.
  // The gate is applied once, here, so no downstream slave has to
  // remember it.
  logic active;
  assign active = cyc_i && stb_i;

RULE 3.30"SLAVE interfaces MAY NOT respond to any SLAVE signals when [CYC_I] is negated." — is a slave's obligation, and Chapter 23.2 showed how easily a slave gets it wrong.

Applying the gate in the decoder does not relieve the slave of the rule. It means a correctly-written slave and a carelessly-written one behave identically behind this interconnect, which is worth having. Section 7 of Chapter 23.2 showed that the careless slave's bug is invisible until something puts a stray strobe in front of it — and one of the things that can do that is a decoder without this line.

5. Every Address Must Go Somewhere

B3 says nothing about an address matching no slave. Three policies exist:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
//   1. let it hang        - no termination ever arrives. B3 has no
//                           timeout, so the master waits forever.
//   2. acknowledge it     - the write vanishes, the read returns garbage,
//                           and nothing reports anything.
//   3. ERROR it           - a default port answers ERR_O.

Policy 3 is the only one that tells anybody, and it is what default_sel_o exists for. Policy 1 is what you get by accident if you never think about it, and it is a hang — which Chapter 12.6 has to clean up from the master's side because the bus has no timeout of its own.

Sweeping the entire 4096-address space:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    THE WHOLE 4096-ADDRESS SPACE, SWEPT
      decoded to a region   544
      reached the default   3552
      matched two regions   0
      total                 4096
      -> EVERY ADDRESS DECODES SOMEWHERE. That is not
         automatic: it is what the default port is for.

544 mapped, 3552 holes, and the two sum to 4096 exactly. That total is the point of the sweep — it is a completeness proof, not a statistic. 256 + 256 + 16 + 16 = 544, so the arithmetic of the map itself is checked at the same time.

Boundary by boundary:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
      address  what                   decodes to  verdict
      0x000  ROM first word         region 0  PASS
      0x0ff  ROM last word          region 0  PASS
      0x100  one past ROM           DEFAULT   PASS
      0x3ff  just below RAM         DEFAULT   PASS
      0x400  RAM first word         region 1  PASS
      0x4ff  RAM last word          region 1  PASS
      0x800  timer first            region 2  PASS
      0x80f  timer last             region 2  PASS
      0x810  gpio first (adjacent)  region 3  PASS
      0x81f  gpio last              region 3  PASS
      0x820  one past gpio          DEFAULT   PASS

0x80F and 0x810 are the interesting pair — adjacent addresses in adjacent regions, the classic place for an off-by-one in a mask.

The N-way address decoder. The address arrives together with CYC_I and STB_I. Every region is compared in parallel against its own base and mask, and all matches are collected into a raw match vector rather than stopping at the first hit. Three things are then derived from that vector. A priority encoder picks the lowest-indexed match, which becomes the one-hot slave select and the winning region index. A population check detects whether more than one region matched, which drives the overlap output. A zero check detects whether no region matched, which drives the default select that routes the access to the default port for an error response. All three outputs are gated by the AND of CYC_I and STB_I, so that RULE 3.30 is satisfied once in the interconnect rather than separately in every slave.ADR_Iwith CYC_I and STB_IN parallel comparators(adr AND mask) == (base ANDmask)raw match vectorevery match, not just thefirstfirst match winslowest index — local policymore than one bit setoverlap_o — reports, doesnot refuseno bit setdefault_sel_o — the ERR port12

6. The Default Port Is A Slave Somebody Has To Write

default_sel_o is an output, not a behaviour. Policy 3 only reports the unmapped access if something is attached to that wire, and that something is the smallest legal Wishbone slave there is:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// The default responder. RULE 3.40's slave minimum is [ACK_O], [CLK_I],
// [CYC_I], [STB_I], [RST_I] - and this one does not even use [ACK_O].
module wb_default_slave (
  input  logic cyc_i, stb_i,
  output logic ack_o,
  output logic err_o
);
  // RULE 3.45: one-hot. ACK is never asserted, so ERR alone is trivially
  // one-hot. RULE 3.30: both outputs are gated by cyc_i, so this slave
  // is silent outside a cycle like any other.
  assign ack_o = 1'b0;
  assign err_o = cyc_i && stb_i;
endmodule

Two continuous assigns, no state, no clock. It answers combinationally, which PERMISSION 3.30 allows and which B3 §4.1 warns about — but a default responder is the one slave where the combinational path is hard to object to, because it is on the path only for accesses that are already broken.

Driven from the decoder's own default_sel_o, it answers:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    THE DEFAULT PORT, ACTUALLY ANSWERING
      address  decodes to  default ACK_O  default ERR_O
      0x000    region 0           0              0
      0x850    DEFAULT            0              1

      -> ERR_O asserted, ACK_O silent. RULE 3.45 is
         satisfied trivially because this slave never
         asserts ACK_O at all, and RULE 3.30 because
         both outputs are gated by the decoder's own
         CYC/STB gate.

0x000 is mapped, so the default port stays silent; 0x850 is not, and gets [ERR_O].

If you forget to instantiate it, the decoder is still correct and the system still hangs, because the unmapped access now reaches nothing at all. That is policy 1 arriving by omission, and the failure looks identical to a dead slave.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
      WITHOUT THIS MODULE INSTANTIATED the decoder is
      still correct and the unmapped access still hangs,
      because nothing answers it. default_sel_o is an
      OUTPUT, not a behaviour.

7. A Loop That Icarus Refused, And What It Taught

The winning region index started life as the obvious loop and did not survive:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  // The winning index, as a ternary chain over CONSTANT bit selects.
  //
  // A loop with `onehot[i]` would be shorter and makes Icarus warn that
  // the process becomes sensitive to every bit of i. Constant selects
  // have no such problem, and for a bounded N the chain is no less
  // readable than the loop it replaces.
  assign index_o = onehot[0] ? 3'd0 :
                   onehot[1] ? 3'd1 :
                   onehot[2] ? 3'd2 :

The warning is "constant selects in always_* processes are not fully supported", and it fires for any variable bit-select or part-select inside an always_* block — not only in the decoder. It appeared four separate times while building this module, in the register bank's policy lookup, its read multiplexer, this index encoder, and the interconnect's forward route.

The fix is the same every time: move the indexing into a continuous assign, which has no sensitivity list to get wrong. It is a simulator limitation rather than a language rule, and a design that avoids it is portable to more tools than one that does not.

8. One Digit, Sixteen Stolen Addresses

Now break it the way real maps break. Region 3's mask becomes 0xF00 instead of 0xFF0one hexadecimal digit — so gpio now claims all of 0x8000x8FF, including the timer.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  === SIM H - overlap detection ===
      correct map    addresses matching two regions  0
      overlapping    addresses matching two regions  16

      -> 16 addresses are claimed twice. FIRST MATCH WINS,
         so region 2 keeps 0x800..0x80F and region 3
         silently never sees them.

Here is the part that catches people. The instinct is to probe an address inside the overlap:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
         0x805  broken map -> region 2
                correct map -> region 2   (unchanged:
                first match wins, so the timer keeps it
                and gpio is the one that lost)

0x805 behaves identically in both maps. First match wins, the timer has the lower index, and nothing appears wrong. An engineer checking the overlap by probing inside it will conclude the map is fine.

The damage is somewhere else entirely:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
         0x850  broken map -> region 3
                correct map -> DEFAULT (an ERR)

         THAT is the address that changed meaning. A
         write that used to be reported as an error now
         lands silently on gpio.

An address that used to be a hole is now a peripheral. The access that previously came back as [ERR_O] — visible, logged, debuggable — now completes successfully against the wrong device.

And the map still passes the completeness check:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
        map           mapped  unmapped  double-claimed
        clean            544      3552              0
        overlapping      768      3328             16

768 mapped and 3328 unmapped still sum to 4096. A design rule that only asked "does every address go somewhere?" would pass this map without comment. That is why the decoder reports overlap as a separate output rather than folding it into a general health check.

9. The Design Rules This Chapter Leaves You With

checkwhat it catcheswhat it misses
mapped + unmapped = totala region that decodes nowhereoverlap — both maps pass
overlap count = 0two regions claiming one addressa region at the wrong base entirely
every boundary probedoff-by-one in a maska hole between two correct regions
unmapped reaches the defaulta silent hangwhether the default is wired up

No single one of these is sufficient, which is the general shape of every checker in this module: Chapter 23.2's conformance monitor caught its own five rules and was blind to a controller that lost twelve transfers.

10. What This Decoder Does Not Do

  • No runtime reconfiguration. The table is a parameter; there is no way to move a region after elaboration.
  • No more than 8 regions. The vectors are 8 bits wide. Widening them is mechanical; making the priority encoder scale gracefully past that is not.
  • No security or privilege checking. Every master sees every region.
  • No elaboration-time overlap error. Stated in Section 3 as a deliberate choice, not an omission.

Next: Chapter 23.6 — Interconnect RTL puts the decoder to work routing two masters to four slaves, and finds that the choice between a shared bus and a crossbar is settled by something other than the reason everybody gives for it.

Continue learning

Standards & specifications

Governing standard
Wishbone SoC Interconnection Architecture (OpenCores)(opens OpenCores in a new tab)

Defines the Wishbone signal set, the bus cycles built from it and the interface rules a portable IP core must follow. It deliberately leaves interconnect topology, address map and arbitration policy to the integrator, so those are system decisions rather than requirements of the specification.

This page also covers RTL structure, verification approach and debugging technique. Those are engineering practice built on the standard, not requirements the standard itself imposes.

Where this fits

Part of the Wishbone curriculum.