Skip to content
VLSI Mentor

Wishbone · Module 13

Alignment

The two address bits the bus does not carry become the select pattern. Measured across every offset and size, including the misaligned operand that fits in one transfer.

Chapter 13.3 proved partial writes correct across every select pattern. Every pattern arrived ready-made.

Software does not have select patterns. It has a pointer and a type.

Where does SEL come from, and what happens when the bytes software wants do not fit in one word?

1. The Split

One byte address, two destinations:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  byte address  0x0000_1002
        |
        +-- bits 31..2  ->  ADR   = 0x0000_0400      which word
        |
        +-- bits 1..0   ->  offset = 2               where in it
                                |
                                +-- with the size  ->  SEL = 0100

No information is lost and none is duplicated. P9 and P10 in Section 6 state exactly that: the word address is the byte address shifted, and the offset is the bits the shift discarded.

The offset alone is not SEL. A byte at offset 2 gives 0100; a halfword at offset 2 gives 1100. The size decides how many lanes; the offset decides where the run starts.

2. Which Lane the Offset Picks Is an Endianness Question

Chapter 13.1 separated two layers and this is where the separation pays.

Layer one is fixed. RULE 3.100 binds SEL(n) to DAT(8n+7..8n). Wiring. It never moves.

Layer two is chosen. Which byte number sits on which lane is the endianness of the system, and the same normative figure gives both:

byte offset in the wordlittle endianbig endian
0SEL[0]SEL[3]
1SEL[1]SEL[2]
2SEL[2]SEL[1]
3SEL[3]SEL[0]

RULE 2.15 requires a core's datasheet to indicate the data transfer ordering as BIG ENDIAN or LITTLE ENDIAN. This course is little endian throughout — a SOC CONVENTION, stated rather than assumed, and Chapter 4.7 declared it.

A slave never converts. It receives lane numbers and applies them to its own storage. A mixed-endian system needs an explicit converter, placed deliberately — a slave that tries to compensate applies the correction twice.

3. RTL — The Adapter

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────────
// wb_byte_address_adapter — software's byte address and operand size become
// a Wishbone word address plus a select pattern.
//
// THREE NUMBERS, KEPT APART. Module 12 insisted on the first two; this
// module adds the third.
//
//   byte_adr_i   what a C pointer holds. Byte units.
//   adr_o        what appears on ADR. WORD units — the organization32
//                figure lists ADR_I/ADR_O(63..02) for a 32-bit port with
//                8-bit granularity, so the low two bits are not on the
//                wires at all (Chapter 4.3).
//   sel_o        which lanes of that one word carry the operand.
//
// The low two bits of the byte address do not vanish. They become sel_o.
// That is the whole content of this module: ADR says WHICH WORD, SEL says
// WHERE IN IT, and the bits that stopped being address became select.
//
// ENDIANNESS. The lane binding SEL[n] <-> DAT[8n+7:8n] is normative
// (RULE 3.100) and fixed. Which BYTE NUMBER of an operand sits on which
// lane is NOT fixed — the same organization32 figure gives BIG and LITTLE
// ENDIAN as two different rows. RULE 2.15 requires a core's datasheet to
// state which it uses.
//
//   LITTLE ENDIAN: byte offset 0 -> lane 0 -> SEL[0]
//   BIG ENDIAN:    byte offset 0 -> lane 3 -> SEL[3]
//
// THIS ADAPTER IS LITTLE ENDIAN, matching the convention Chapter 4.7
// declared for the whole course. That is a SOC CONVENTION, not a Wishbone
// requirement, and LITTLE_ENDIAN can be cleared to get the other one.
//
// ALIGNMENT IS LOCAL POLICY. Nothing in Wishbone forbids a misaligned
// access. What the specification does say (RULE 2.15) is that a datasheet
// states the port's granularity and maximum operand size; whether a given
// operand at a given offset can be carried is then a property of THIS
// system. This adapter declares its policy in ok_o/reason_o and rejects
// nothing silently.
// ─────────────────────────────────────────────────────────────────────────
module wb_byte_address_adapter #(
  parameter int unsigned BYTE_AW       = 32,
  parameter int unsigned DW            = 32,
  parameter int unsigned GRAN          = 8,
  parameter bit          LITTLE_ENDIAN = 1'b1,
  localparam int unsigned SELW  = DW / GRAN,
  localparam int unsigned SHIFT = $clog2(DW / 8),
  localparam int unsigned WAW   = BYTE_AW - SHIFT
) (
  input  logic [BYTE_AW-1:0] byte_adr_i,
  input  logic [2:0]         size_i,     // operand size in BYTES: 1, 2 or 4
  output logic [WAW-1:0]     adr_o,      // WORD address
  output logic [SELW-1:0]    sel_o,
  output logic [1:0]         off_o,      // byte offset within the word
  output logic               ok_o,       // representable in ONE transfer
  output logic [1:0]         reason_o    // 0 ok, 1 bad size, 2 crosses word
);
  initial begin
    if (DW % GRAN != 0)
      $fatal(1, "wb_byte_address_adapter: DW not a multiple of GRAN");
    if (GRAN != 8)
      $fatal(1, "wb_byte_address_adapter: assumes byte granularity");
  end

  localparam int unsigned BPW = DW / 8;          // bytes per bus word

  logic [SHIFT-1:0] off;
  assign off   = byte_adr_i[SHIFT-1:0];
  assign off_o = 2'(off);
  assign adr_o = byte_adr_i[BYTE_AW-1 -: WAW];   // == byte_adr_i >> SHIFT

  // A contiguous run of `size` lanes starting at the byte offset. Built as
  // a run over LANE indices, then flipped if the system is big endian —
  // because the flip is about byte NUMBERING, and doing it in one named
  // place is what keeps it from leaking into the mask expansion.
  logic [SELW-1:0] run_lo;
  always_comb begin
    run_lo = '0;
    for (int unsigned n = 0; n < SELW; n++)
      if ((32'(n) >= 32'(off)) &&
          (32'(n) <  32'(off) + 32'(size_i)))
        run_lo[n] = 1'b1;
  end

  logic [SELW-1:0] run_be;
  always_comb begin
    run_be = '0;
    for (int unsigned n = 0; n < SELW; n++)
      run_be[SELW-1-n] = run_lo[n];
  end

  // Policy, stated once. Size must be a supported operand, and the operand
  // must lie inside one bus word — a byte set that straddles the boundary
  // is not one word's worth of lanes, so no single SEL pattern names it.
  logic size_ok, fits;
  assign size_ok = (size_i == 3'd1) || (size_i == 3'd2) || (size_i == 3'd4);
  assign fits    = (32'(off) + 32'(size_i)) <= 32'(BPW);

  assign ok_o     = size_ok && fits;
  assign reason_o = !size_ok ? 2'd1 : (!fits ? 2'd2 : 2'd0);

  // A rejected request emits no selects. Driving a partial pattern for a
  // request the adapter has refused would invite a caller to use it.
  assign sel_o = !ok_o ? '0 : (LITTLE_ENDIAN ? run_lo : run_be);
endmodule

Reading it

adr_o and off_o are the two halves of one number, taken from the same input with no arithmetic between them. There is no opportunity for them to disagree.

run_lo is built over lane indices, then mirrored once for big endian. The flip lives in one named place. Putting it anywhere else — in the mask expansion, or in the target — is how a system ends up correcting twice.

ok_o and reason_o are the policy, and the policy is declared rather than enforced silently. A caller is told whether the request is representable and why not when it is not.

A refused request emits sel_o = '0'. Driving a partial pattern for an operand the adapter has already refused would invite a caller to use it — and the partial pattern would name the wrong bytes.

4. Simulation — SIM E: A Byte Address Becomes ADR Plus SEL

Nine translations, walking one byte at a time across a word boundary and then taking halfwords and words at their offsets.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  === SIM E - a byte address becomes ADR plus SEL ===
    32-bit port, byte granularity. ADR is a WORD address, so
    the two low bits of the byte address are not on the wires
    - they become the byte offset, and the offset picks lanes.
    Little endian: byte offset 0 is lane 0.

    byte adr    size        ADR (word)    off   SEL  classification
    0x00001000  byte        0x00000400    0     0001   one transfer
    0x00001001  byte        0x00000400    1     0010   one transfer
    0x00001002  byte        0x00000400    2     0100   one transfer
    0x00001003  byte        0x00000400    3     1000   one transfer
    0x00001004  byte        0x00000401    0     0001   one transfer

    0x00001000  halfword    0x00000400    0     0011   one transfer
    0x00001002  halfword    0x00000400    2     1100   one transfer

    0x00001000  word        0x00000400    0     1111   one transfer
    0x00001004  word        0x00000401    0     1111   one transfer

    The first five rows walk one byte at a time. The ADR column
    changes only at 0x1004, where the byte address crossed into
    the next word - four byte addresses share one ADR, and SEL
    is what tells them apart.

  --- the same byte addresses, big endian ---

    byte adr    off   SEL little   SEL big
    0x00001000   0    0001         1000
    0x00001001   1    0010         0100
    0x00001002   2    0100         0010
    0x00001003   3    1000         0001

    Same address, same lane wiring, different lane chosen.
    RULE 3.100 fixes SEL(n) to DAT(8n+7..8n) in both columns;
    what moved is which byte NUMBER sits on which lane.
    RULE 2.15 requires a datasheet to say which ordering a
    core uses. This course is little endian throughout.

Reading it

The first four rows share one ADR and differ only in SEL. 0x1000, 0x1001, 0x1002, 0x1003 all give ADR = 0x00000400, with select 0001, 0010, 0100, 1000. Four distinct byte addresses, one word, four lanes — which is the split of Section 1, measured.

Row five is the boundary. 0x1004 is the first byte of the next word: ADR steps to 0x00000401 and SEL returns to 0001. The ADR column changes exactly once in five rows, and that is the only place it can change.

Then the halfwords. Offset 0 gives 0011 — lanes 1 and 0. Offset 2 gives 1100 — lanes 3 and 2. The size sets the run length and the offset sets where it starts.

The word rows give 1111 at both word-aligned addresses, which is the configuration everything before this module used implicitly.

The big-endian block is the same four byte addresses through an adapter differing in one parameter. Byte offset 0 selects lane 0 in one column and lane 3 in the other; the columns are mirror images. Neither is more correct, and the lane wiring is identical in both — RULE 3.100 is not what changed.

5. Simulation — SIM F: What One Transfer Cannot Represent

A halfword at offset 3 needs byte 3 of this word and byte 0 of the next. One transfer presents one ADR, so no select pattern names both.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  === SIM F - operands one bus word cannot carry ===
    a halfword at offset 3 needs bytes 3 and 4. Byte 4 is in
    the NEXT word, and one transfer presents one ADR.

    byte adr    size        ADR (word)    off   SEL  classification
    0x00001003  halfword    0x00000400    3     0000   crosses word
    0x00001001  word        0x00000400    1     0000   crosses word
    0x00001002  word        0x00000400    2     0000   crosses word
    0x00001003  word        0x00000400    3     0000   crosses word
    0x00001000  unsupported 0x00000400    0     0000   bad size

    SEL reads 0000 on every rejected row. The adapter emits no
    lanes for a request it refused, so a caller cannot use a
    partial pattern for an operand that was never representable.

    MISALIGNED AND CROSS-WORD ARE NOT THE SAME TEST:

    0x00001001  halfword    0x00000400    1     0110   one transfer

    A halfword at offset 1 is misaligned by the usual software
    rule - its address is not a multiple of its size - and it
    still fits inside one bus word, so one transfer carries it
    with SEL = 0110. What one transfer cannot represent is a
    byte set spanning TWO words, because a transfer presents
    one ADR. Cross-word is the constraint; alignment is a
    separate convention that this adapter does not enforce.

  --- every offset and size, classified ---

    size       off 0        off 1        off 2        off 3
    byte       single       single       single       single
    halfword   single       single       single       cross-word
    word       single       cross-word   cross-word   cross-word

    8 of the 12 combinations fit in one transfer; 4 span two.

    NOTHING IN WISHBONE FORBIDS THE 4 THAT SPAN. The spec has
    no alignment rule; it requires a datasheet to state the
    port's granularity and maximum operand size (RULE 2.15), and
    leaves what a core accepts to the core. Rejecting them is
    THIS SoC'S POLICY. Splitting them into two transfers would
    be equally conformant, and RECOMMENDATION 3.20 advises the
    low-address half first if a design chooses to split.

    cases probed 15   single transfer 10   crosses word 4   bad size 1

Reading it

Four rejected rows, all with SEL = 0000. The adapter refuses and emits no lanes, so a caller cannot take a partial pattern and use it for an operand that was never representable.

Then the row that separates two ideas people routinely fuse.

A halfword at offset 1 is misaligned — its address is not a multiple of its size, which is the usual software definition and what a processor with alignment requirements would fault on. And it fits in one bus word, so this adapter carries it with SEL = 0110.

What one transfer cannot represent is a byte set spanning two words. That is the constraint, and it is structural: one transfer, one ADR.

misalignedcross-word
halfword at offset 1yesno — SEL = 0110
halfword at offset 3yesyes — refused
word at offset 0nono
word at offset 1yesyes — refused

The two tests coincide often enough to look like one test. They are not, and the halfword at offset 1 is the counterexample.

The matrix then classifies the whole space. Twelve combinations of three sizes and four offsets; eight fit in one transfer and four span two. Every byte fits — a single byte can never cross a boundary. Halfwords fail only at offset 3. Words fail everywhere but offset 0.

And the count comes from the design, not from the prose. The testbench classifies each cell by asking the adapter and tallies the result, so the sentence below the matrix cannot drift from the table above it.

6. Verification

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────────
// wb_adapter_props — the address/lane translation and the alignment policy.
// ─────────────────────────────────────────────────────────────────────────
module wb_adapter_props #(
  parameter int unsigned DW   = 32,
  parameter int unsigned GRAN = 8,
  localparam int unsigned SELW = DW / GRAN
) (
  input logic            clk_i,
  input logic [31:0]     byte_adr_i,
  input logic [2:0]      size_i,
  input logic [29:0]     adr_i,
  input logic [SELW-1:0] sel_i,
  input logic [1:0]      off_i,
  input logic            ok_i
);
  // P9 — SPEC-DERIVED (DESC ADR_O, and the organization32 figure).
  // The word address is the byte address with the granularity bits removed.
  // The figure lists ADR_I/ADR_O(63..02) for a 32-bit byte-granular port,
  // so those two bits are not carried and cannot be recovered from ADR.
  property p_word_address;
    @(posedge clk_i) adr_i == byte_adr_i[31:2];
  endproperty
  a_word_address: assert property (p_word_address);

  // P10 — SPEC-DERIVED, same source.
  // The bits ADR does not carry are exactly the byte offset. Together with
  // P9 this says no information was lost: the address split in two.
  property p_offset_is_low_bits;
    @(posedge clk_i) off_i == byte_adr_i[1:0];
  endproperty
  a_offset_is_low_bits: assert property (p_offset_is_low_bits);

  // P11 — LOCAL SOC POLICY (little endian, declared by this course).
  // For an accepted operand the selected lanes are exactly the run of
  // `size` lanes starting at the byte offset. Under BIG ENDIAN the same
  // adapter produces the mirrored run, which is why this property names
  // its endianness instead of assuming one.
  property p_lane_run;
    @(posedge clk_i) (ok_i) |->
      (sel_i == (SELW'((1 << size_i) - 1) << off_i));
  endproperty
  a_lane_run: assert property (p_lane_run);

  // P12 — LOCAL SOC POLICY.
  // A refused request selects nothing. Wishbone forbids neither misaligned
  // nor cross-word operands; this adapter declines to represent the ones it
  // cannot carry in a single transfer, and emits no partial pattern that a
  // caller might mistake for a usable one.
  property p_rejected_selects_nothing;
    @(posedge clk_i) (!ok_i) |-> (sel_i == '0);
  endproperty
  a_rejected_selects_nothing: assert property (p_rejected_selects_nothing);
endmodule

P9 and P10 together say the address split loses nothing. One takes the high bits, the other takes the low bits, and between them they account for every bit of the input. Either alone would be satisfied by an implementation that dropped information.

P11 names its endianness in the property text, because the property is false under the other one. A conditional property that does not state its condition is worse than no property — it reads as a proof of something it does not prove, which is the same discipline Chapter 12.3's P14 needed.

P12 is the policy, and it is the only one here that another conformant SoC could legitimately fail. A design that splits cross-word operands would produce a partial pattern and a second transfer. That design is not wrong; it has a different policy, and P12 belongs to this one.

7. Failure Modes and Discriminating Evidence

Symptom: every access is off by a factor of four.

Candidate causes. A byte address wired directly to ADR, or a word address used as a byte address.

Discriminating evidence. The factor itself. Exactly 4, or exactly 1/4 — not an arbitrary displacement. Compare the byte address shifted right by two against the observed ADR. This is Chapter 12.1's units audit at a different layer.

Likely RTL location: the adapter's adr_o assignment, or a caller bypassing the adapter.

Symptom: a byte write works at offset 0 and fails at offset 3.

Candidate causes. A lane run that does not shift with the offset, or an offset taken from the wrong bits.

Discriminating evidence. SEL at each of the four offsets. If it is 0001 regardless of offset, the shift is missing. If it is right at 0 and 1 but wrong at 2 and 3, the offset field is too narrow. SIM E's first four rows are this test.

Symptom: byte accesses land on the wrong byte, consistently mirrored.

Candidate causes. An endianness disagreement between software and the adapter.

Discriminating evidence. Whether offset 0 selects lane 0 or lane 3. Mirroring across the word is endianness; an arbitrary permutation is a wiring bug. RULE 2.15 says the datasheet should have settled which the core uses, and the fix belongs in a converter placed deliberately, not inside a slave.

Symptom: an operand at a legal-looking address is refused.

Candidate causes. It crosses a word boundary.

Discriminating evidence. offset + size against the bytes per word. Greater than four means no single transfer can carry it. This is correct behaviour under this SoC's policy — the bug, if any, is in whatever generated an unaligned operand for a system that does not split them.

Symptom: a cross-word access silently reads or writes the wrong bytes.

Candidate causes. An adapter that truncates the lane run at the word boundary instead of refusing.

Discriminating evidence. A non-zero SEL on a request whose offset plus size exceeds the word. That pattern names a subset of the intended bytes — a partial operand presented as a whole one, which is worse than a refusal because nothing reports it.

8. Common Mistakes

"A byte address can be connected to ADR directly."

Wrong mental model: the address is the address.

What is true: ADR is a word address on this port. The specification's example for a 32-bit byte-granular port is ADR_O(n..2), and the normative organization figure confirms it. The low bits are not dropped — they become SEL.

"Misaligned means illegal Wishbone."

Wrong mental model: the protocol has alignment rules.

What is true: it has none. RULE 2.15 requires a datasheet to state granularity and maximum operand size; what a core accepts follows from that. This SoC refuses cross-word operands as LOCAL POLICY, and another could split them.

"Misaligned and cross-word are the same test."

Wrong mental model: one condition.

What is true: a halfword at offset 1 is misaligned and fits in one transfer, with SEL = 0110. SIM F measures it beside the ones that do not fit. Alignment is a software convention; fitting in one word is a structural constraint.

"SEL[0] is always the lowest-addressed byte."

Wrong mental model: lane number is byte number.

What is true: only under little endian, which SIM E shows by running both adapters on the same four addresses. The lane wiring is identical in both columns.

"A cross-word access just needs a wider SEL."

Wrong mental model: more lanes would fix it.

What is true: one transfer presents one ADR. The bytes are in two different words, so no select pattern of any width names them. Two transfers, or a refusal — and which one is the architecture's choice.

9. Interview Reasoning

The address splits; it does not shrink.

The high bits become ADR. On a 32-bit port with byte granularity the address array is ADR_O(n..2) — the specification's own example — so ADR = byte_address >> 2. Four byte addresses share one ADR.

The low bits become the offset, which with the operand size determines the lane run: size consecutive lanes starting at the offset. A byte at offset 2 gives 0100; a halfword at offset 2 gives 1100.

Nothing is lost. The two bits the bus does not carry are exactly the two bits SEL needs.

The part worth volunteering is the endianness layer. Which lane the offset picks is a system convention — little endian puts offset 0 on lane 0, big endian on lane 3 — and RULE 2.15 requires the datasheet to say which. The lane-to-wire binding underneath is fixed by RULE 3.100 and does not move.

10. Understanding Check

No — it moved to SEL.

ADR is a word address on this port, so 0x1000, 0x1001, 0x1002 and 0x1003 all name word 0x00000400. That much is genuinely the same.

What distinguishes them is the select pattern: 0001, 0010, 0100, 1000. The two bits that stopped being address became four bits of lane selection.

Count it. Two bits of byte address can express four positions; four lanes of select can express those four positions and eleven more combinations besides. Nothing was dropped.

Where information would genuinely be lost is SEL = 0000 — a transfer naming a word and no lanes within it. Chapter 13.1 covers that; it is permitted, and it accomplishes nothing.

11. What's Next

The translation is complete: a byte address splits into ADR and SEL, the endianness layer is named rather than assumed, and every offset and size in the configuration is classified.

Every register in this module so far has done the same thing with a selected lane: taken the data. Real peripherals do not all agree about that.

What does "this lane participates" mean to a status register, or to a command port?

Chapter 13.5 — Data Masking applies one mask to three different register semantics, and measures a command that fires from a lane the transfer never delivered. The full path is on the Wishbone curriculum index.

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.