Skip to content
VLSI Mentor

Wishbone · Module 2

Data Transfer

Selection routes a request; it does not move a value. Write data fans out, read data fans in through a multiplexer that every target widens, byte lanes decide which parts of a word participate, and a register narrower than the bus has to be placed on a lane rather than merely connected.

Chapter 2.2 produced a one-hot selection and a local offset. That routes a request. It does not move a single bit of data.

This chapter is the datapath: what carries the value out, what carries it back, and what happens when the bus and the register at the other end are not the same width — which is the normal case, not an exception.

Once a target is selected, how does information actually move between initiator and target?

1. Write Data Fans Out; Read Data Fans In

The two paths look symmetric on a block diagram and cost completely different amounts.

The write path and the read path have different structures. On the write path, a single write data bus from the initiator is broadcast to all four targets at once; each target ignores it unless it is the selected one, so adding a target costs only wiring. On the read path, all four targets each produce a read data value, and a multiplexer driven by the one-hot target select picks exactly one of them to return to the initiator. The multiplexer gains an input for every target added, on every bit of the data width, which is why the read path and not the write path is the structure that limits how many targets a flat fabric can carry.Initiatordrives wdata oncewdata broadcastwiring only — freerdata multiplexergrows with every targetSRAMignores unless selectedGPIOignores unless selectedUARTignores unless selectedTimerignores unless selectedone value12
Figure 1 — one write bus broadcast to every target; one read multiplexer collecting from every target.

Why the asymmetry is unavoidable. Write data has one producer and many possible consumers, and a wire can have many consumers for free. Read data has many producers and one consumer, and a wire cannot have many producers at all — something must choose. On-chip that something is a multiplexer, because tri-state nets inside a chip cost more than they save.

A consequence worth carrying to Chapter 2.7: when a fabric runs out of timing, the read path is almost always where it ran out.

2. RTL 1 — The Read-Data Multiplexer

Two forms, and the difference between them is not style.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────────
// bus_rdata_mux — collect one value from the selected target.
//
// PURPOSE. Turn N targets' read data into the one value the initiator sees,
// using the one-hot select from Chapter 2.2's decoder.
//
// Two implementations are shown because they fail differently when the
// one-hot property is violated, and that difference is worth knowing before
// choosing one.
//
// Generic educational interface — not any bus's signal names.
// ─────────────────────────────────────────────────────────────────────────
module bus_rdata_mux #(
  parameter int unsigned DW      = 32,
  parameter int unsigned NTARGET = 5,      // 4 real targets + default
  parameter bit          USE_AND_OR = 1'b1
) (
  input  logic [NTARGET-1:0]         target_sel,   // one-hot, from the decoder
  input  logic [NTARGET*DW-1:0]      t_rdata_flat, // each target's read data
  input  logic [NTARGET-1:0]         t_ready,
  output logic [DW-1:0]              rdata,
  output logic                       ready
);
  function automatic logic [DW-1:0] rdata_of(input int unsigned i);
    return t_rdata_flat[i*DW +: DW];
  endfunction

  generate
    if (USE_AND_OR) begin : g_and_or
      // FORM A — AND-OR reduction. Maps directly onto LUTs and does not ask
      // the synthesiser to re-derive mutual exclusion, because the one-hot
      // encoding is already available.
      //
      // CORRECT ONLY BECAUSE target_sel IS ONE-HOT. If two bits were ever
      // high, this ORs two targets' values into a number belonging to
      // neither — plausible-looking data with no error anywhere. The
      // dependence is on Chapter 2.2's assertion P1, and stating it at the
      // point of dependence is deliberate.
      always_comb begin
        rdata = '0;
        ready = 1'b0;
        for (int unsigned i = 0; i < NTARGET; i++) begin
          rdata |= {DW{target_sel[i]}} & rdata_of(i);
          ready |= target_sel[i] & t_ready[i];
        end
      end
    end else begin : g_case
      // FORM B — priority-free case over the one-hot vector. `unique case`
      // asks the tool to CHECK at simulation time that exactly one branch
      // matches, so a one-hot violation is reported rather than silently
      // merged. It costs nothing in synthesis and buys a real diagnostic.
      always_comb begin
        rdata = '0;
        ready = 1'b0;
        unique case (1'b1)
          target_sel[0]: begin rdata = rdata_of(0); ready = t_ready[0]; end
          target_sel[1]: begin rdata = rdata_of(1); ready = t_ready[1]; end
          target_sel[2]: begin rdata = rdata_of(2); ready = t_ready[2]; end
          target_sel[3]: begin rdata = rdata_of(3); ready = t_ready[3]; end
          target_sel[4]: begin rdata = rdata_of(4); ready = t_ready[4]; end
          default:       begin rdata = '0;          ready = 1'b0;       end
        endcase
      end
    end
  endgenerate
endmodule

Reading this module

Purpose. Reduce N candidate values to one, under the control of a one-hot vector.

Interface contract. target_sel is one-hot, per Chapter 2.2. t_rdata_flat carries every target's read data concatenated; the flat-vector form is the portability idiom introduced with the decoder.

Combinational behaviour, Form A. For each bit of the data width, an N-input OR of N two-input ANDs. On an FPGA with 6-input LUTs, five targets fit in about one LUT level per bit; a seventh forces a second level on all DW bits at once — the step-function cost Chapter 1.5 predicted.

Combinational behaviour, Form B. unique case (1'b1) selects the branch whose condition is true. The unique qualifier is the point: it instructs the simulator to report an error if zero or more than one branch matches. Form A silently merges a one-hot violation; Form B reports it.

No sequential behaviour. Both forms are pure combinational. rdata and ready are assigned before the loop or in the default, so every path assigns them and no latch is inferred.

Deliberate simplifications. The case form is written out rather than generated, because a generated unique case over a parameterised width is awkward and the explicit form is what an engineer actually reads. NTARGET is therefore effectively fixed at 5 for Form B.

How it could fail. Form A with a non-one-hot select produces merged data. Either form with a target that drives non-zero rdata while unselected produces the same merge in Form A — which is why Chapter 2.1's assertion P3 puts the obligation on the target as well.

Scaling. This is the structure that limits flat fabrics. Hierarchy — two narrow multiplexers instead of one wide one — is the answer, and it constrains the address map to contiguous groups.

3. Byte Lanes — Which Parts of the Word Participate

A 32-bit data bus is four byte lanes. A transfer does not necessarily use all four.

LaneBitsByte address within the word
0data[7:0]addr[1:0] == 0
1data[15:8]addr[1:0] == 1
2data[23:16]addr[1:0] == 2
3data[31:24]addr[1:0] == 3

The lane-to-byte-address mapping above is the little-endian convention, and it is a convention rather than a law: a big-endian system maps lane 0 to the highest byte address in the word. The important engineering point is not which convention is better — it is that both ends must use the same one, and that a mismatch produces byte-reversed data rather than an error.

This is the right level of endianness detail for Module 2. A bus specification fixes lane numbering; how a processor interprets a multi-byte value it has loaded is an architecture question, and the two are frequently confused.

Why byte enables exist at all. Without them, every write is a full-word write, so writing one byte requires software to read the word, modify one byte, and write it back. That is three problems: it costs two bus accesses instead of one, it is not atomic, and on a peripheral it is actively wrong — a read-modify-write of a register with read-to-clear bits destroys state the read consumed.

4. RTL 2 — A Register Bank That Honours Byte Enables

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────────
// reg_bank_be — a target whose registers honour byte-lane enables.
//
// PURPOSE. Show what a byte-granular write actually costs in RTL, and what
// a narrower-than-bus register requires beyond simply being connected.
//
// Two registers:
//   CTRL   at offset 0x00 — full 32 bits, byte-writable
//   STATUS at offset 0x04 — read-only, hardware-updated
//   NARROW at offset 0x08 — an 8-bit register living on lane 0 only
//
// Generic educational interface — not any bus's signal names.
// ─────────────────────────────────────────────────────────────────────────
module reg_bank_be #(
  parameter int unsigned AW = 8,
  parameter int unsigned DW = 32
) (
  input  logic          clk,
  input  logic          rst_n,
  input  logic          sel,
  input  logic          valid,
  input  logic          write,
  input  logic [AW-1:0] addr,
  input  logic [DW-1:0] wdata,
  input  logic [3:0]    byte_en,
  output logic          ready,
  output logic [DW-1:0] rdata,
  output logic          error,
  input  logic [7:0]    hw_status          // produced by surrounding logic
);
  localparam logic [AW-1:0] OFF_CTRL   = 'h00;
  localparam logic [AW-1:0] OFF_STATUS = 'h04;
  localparam logic [AW-1:0] OFF_NARROW = 'h08;

  logic [DW-1:0] ctrl_q;
  logic [7:0]    narrow_q;

  logic access, addr_legal;
  assign access = sel & valid;

  always_comb begin
    unique case (addr)
      OFF_CTRL, OFF_STATUS, OFF_NARROW: addr_legal = 1'b1;
      default:                          addr_legal = 1'b0;
    endcase
  end

  // A write to a read-only offset is a legal access with an illegal
  // operation, and it is reported as an error rather than ignored.
  logic write_to_ro;
  assign write_to_ro = access & write & (addr == OFF_STATUS);

  assign ready = access;
  assign error = access & (~addr_legal | write_to_ro);

  // ── Sequential: byte-lane merge ──────────────────────────────────────
  // Each lane is written independently. This is the whole mechanism: a
  // byte-granular write updates one lane and leaves the other three at
  // their previous value, with no read-modify-write anywhere.
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      ctrl_q   <= '0;
      narrow_q <= '0;
    end else if (access && write && addr_legal && !write_to_ro) begin
      unique case (addr)
        OFF_CTRL: begin
          if (byte_en[0]) ctrl_q[7:0]   <= wdata[7:0];
          if (byte_en[1]) ctrl_q[15:8]  <= wdata[15:8];
          if (byte_en[2]) ctrl_q[23:16] <= wdata[23:16];
          if (byte_en[3]) ctrl_q[31:24] <= wdata[31:24];
        end
        OFF_NARROW: begin
          // An 8-bit register is PLACED on lane 0. It is written only when
          // lane 0 participates — a byte store to offset 0x09 asserts
          // byte_en[1] and must leave this register alone.
          if (byte_en[0]) narrow_q <= wdata[7:0];
        end
        default: ;
      endcase
    end
  end

  // ── Combinational read path ──────────────────────────────────────────
  // A narrow register is ZERO-EXTENDED into the lane it occupies, and the
  // other lanes read as zero. Sign extension would be wrong here: these are
  // control bits, not a signed quantity, and a tool that inferred sign
  // extension from a signed type would corrupt every read with bit 7 set.
  always_comb begin
    rdata = '0;
    if (access && !write && addr_legal) begin
      unique case (addr)
        OFF_CTRL:   rdata = ctrl_q;
        OFF_STATUS: rdata = {{(DW-8){1'b0}}, hw_status};
        OFF_NARROW: rdata = {{(DW-8){1'b0}}, narrow_q};
        default:    rdata = '0;
      endcase
    end
  end
endmodule

Reading this module

Purpose. Demonstrate that byte granularity is per-lane logic, not a flag, and that a narrow register must be placed rather than merely connected.

Combinational decisions. Whether this is an access; whether the offset exists; whether a write targets a read-only register; and on a read, which value to present, zero-extended into the full width.

Sequential behaviour. Four independent conditional assignments for ctrl_q — one per lane. This is the mechanism that makes a byte store cost one access instead of three, and it is why byte enables are worth their four wires.

Timing. Everything completes in the cycle it is requested; ready is combinational. Wait states arrive in Chapter 2.5.

Deliberate simplifications. No wait states, no write buffering, and hw_status is an input rather than real hardware.

How it could fail — and these are the chapter's real content:

  • Swapped lane order. Writing ctrl_q[31:24] under byte_en[0] reverses every byte-granular write. Word writes still work, so it survives any test that only writes words.
  • Ignoring byte_en entirely. Every byte write becomes a word write, so a byte store to NARROW also clobbers whatever the adjacent lanes were meant to hold.
  • Sign extension instead of zero extension. If narrow_q were declared signed and the concatenation replaced with an assignment, values with bit 7 set would read back as 0xFFFF_FFxx.
  • Placing a narrow register on the wrong lane. It then responds to byte stores at the wrong byte address, which reads as an off-by-one in the driver.

5. Waveform — Write, Then Read

Byte write to CTRL lane 0, then a read of CTRL

8 cycles
A generic educational bus transfer over eight clock cycles. In cycle one the initiator raises valid with write high, an address of offset zero, write data of A5 in the low byte and a byte enable of one, meaning only lane zero participates. The target raises ready in the same cycle, so the transfer is accepted on the clock edge at the end of cycle one and only the low byte of the control register changes. Cycles two and three are idle with valid low. In cycle four the initiator raises valid with write low for a read of the same offset and all four byte enables asserted. Ready is high, and read data is valid only in that cycle, showing the previous contents of the upper three bytes unchanged alongside the newly written low byte.write accepted: valid AND ready on this edgewrite accepted: valid ANDready on this edgebyte_en=0x1 — only lane 0 is writtenbyte_en=0x1 — only lane 0is writtenread accepted; rdata valid ONLY this cycleread accepted; rdata validONLY this cyclerdata is not held — capture it or lose itrdata is not held — captureit or lose itclkvalidwriteaddr--0x00----0x00------byte_en00x1000xF000wdata--0x..A5------------readyrdata--------0x1234_56A5------t0t1t2t3t4t5t6t7
Figure 2 — a byte write followed by a word read on the Module 2 teaching interface. This is a generic educational handshake, not any bus's timing.

Three things to read off the waveform, each of which is a rule rather than an observation.

The transfer happens on the edge where valid and ready are both high. Not when valid rises, and not when the initiator feels finished. That single rule is what makes the two sides agree on when.

byte_en is part of the request, and it changes what the write means. The same address, same write data and same direction with byte_en = 0xF would have overwritten all four bytes. The value on wdata[31:8] in cycle 1 is not zero, and it is not written — it simply does not participate.

Read data has a validity window of exactly one cycle. Before it and after it, rdata means nothing. An initiator that captures a cycle late gets the next thing on the bus, which is Chapter 2.1's stale-read failure and looks like an off-by-one in software.

6. Width Mismatch — the Normal Case

A 32-bit bus talking to an 8-bit register is not an edge case; it is most peripheral registers.

Reading a narrow register. The value must be placed on a defined lane and the remaining bits given a defined value. Zero is the usual choice and it must be chosen — leaving the upper bits unassigned is a latch, and leaving them at whatever the previous value was is a bug that reads as intermittent.

Writing a narrow register. Only the lane the register occupies may participate, so the write must be gated on that lane's enable. reg_bank_be's NARROW shows this, and omitting the gate is the failure in Chapter 2.1 §9's exercise.

A bus narrower than the register is the opposite problem and needs more than lane placement: the register must be split across several transfers, which raises a question this module cannot answer — what does the hardware do between the two halves? A 32-bit counter read over two 16-bit accesses can change between them, returning a value that never existed. The standard answer is a shadow register that latches the whole value on the first access, and it is a peripheral-design decision rather than a bus one.

A signedness trap worth naming. Zero extension is right for control and status bits. Sign extension is right for a genuinely signed narrow quantity — an ADC sample, say. Getting this wrong produces values that are correct for small magnitudes and wildly wrong above the sign-bit threshold, which is a pattern that sends people looking at the ADC rather than at the bus interface.

7. Failure Modes and Discriminating Evidence

Symptom: reads return a value that looks like two registers merged.

Candidates. Two selects high, ORed by Form A. Or an unselected target driving non-zero rdata.

Discriminating evidence. Compare the returned value against each target's rdata in the same cycle. A bitwise OR of two of them is conclusive. Then probe target_sel: two bits high is decode; one bit high with another target still driving is that target failing Chapter 2.1's P3.

Property that catches it. $onehot(target_sel) for the first; !sel |-> (rdata == '0) for the second.

Symptom: byte writes corrupt neighbouring bytes.

Candidates. byte_en ignored in the target's write branch; lane order swapped.

Discriminating evidence. Write 0xFF to one byte address and read the whole word. If all four bytes changed, byte_en is ignored. If the wrong single byte changed, the lane order is reversed — and which one changed tells you the permutation.

Likely RTL location. The target's per-lane write conditions.

Symptom: reads are correct for small values and wrong for large ones.

Candidates. Sign extension where zero extension was intended.

Discriminating evidence. Check whether the threshold is exactly the sign bit of the narrow width — values at or above 0x80 for an 8-bit register reading back as 0xFFFF_FFxx.

Likely RTL location. The read path's extension, or a signed declaration that made the tool infer it.

Symptom: reads return the previous access's data.

Candidates. The initiator captures rdata outside its validity window.

Discriminating evidence. Put ready and the initiator's capture strobe on one waveform. If the capture is one cycle after ready, the initiator is late and the target is blameless.

Likely RTL location. The initiator's read-data register — which is Chapter 2.5's subject.

Symptom: multi-byte values arrive byte-reversed.

Candidates. Endianness convention mismatch between the two ends.

Discriminating evidence. Write a known word such as 0x0102_0304 and read it back byte by byte at the four byte addresses. The order tells you the mapping directly, with no ambiguity.

8. Common Mistakes

"Read data just needs to be correct on the bus."

Wrong mental model: the target presents a value and the initiator takes it.

Concrete failure: the target drives valid data in the completion cycle; the initiator registers it a cycle later and gets whatever came next.

Observable evidence: every read returns the previous read's value — a perfect off-by-one that looks like a software bug.

Correct model: read data has a validity window both sides agree on, and here it is the cycle ready is high.

"Byte enables are an optimisation."

Wrong mental model: they save a little time on narrow writes.

Concrete failure: without them, a byte write becomes read-modify-write. On a register with read-to-clear bits, the read destroys state; on a shared register, the sequence is not atomic.

Observable evidence: interrupt flags disappearing when an unrelated field in the same register is written.

Correct model: byte enables make a partial write expressible. Their absence changes what operations exist, not how fast they are.

"An 8-bit register just connects to the low bits."

Wrong mental model: narrow means fewer wires.

Concrete failure: the upper 24 bits of the read path are unassigned, inferring latches; or the write ignores lane enables, so a store to a neighbouring byte overwrites the register.

Observable evidence: reads whose upper bytes carry stale values from an unrelated access.

Correct model: a narrow register is placed on a lane. Placement fixes which byte address reaches it, which enable gates it, and what the other lanes read.

"The one-hot select is guaranteed, so AND-OR is safe."

Wrong mental model: the decoder's encoding makes the multiplexer's precondition true.

Concrete failure: an aliasing decode produces two selects, and the AND-OR form merges two targets' data with no error.

Observable evidence: a read value sharing bits with two different registers.

Correct model: AND-OR depends on a property proved elsewhere. Use it, and assert the property separately — the dependence belongs in a comment where the multiplexer is.

9. Interview Reasoning

Because the two directions have opposite fan-in.

Write data has one producer and many possible consumers. The initiator drives one value; every target sees it and ignores it unless selected. A wire supports many consumers for free, so broadcasting costs wiring and nothing else.

Read data has many producers and one consumer. Every target has a value; exactly one must arrive. A wire cannot have many producers, so something has to choose, and on-chip that something is a multiplexer because tri-state nets inside a chip cost more than they save.

The consequence that matters: the read multiplexer gains an input for every target added, on every bit of the data width. It is the structure that limits how many targets a flat fabric can carry, and on an FPGA it grows in steps as the LUT input count is exceeded — so a functionally empty change can break timing.

The answer that shows depth adds where this leads: when a fabric misses timing, the read path is where it missed, and the fixes are registering the response or going hierarchical — both of which require the initiator to tolerate variable latency.

10. Understanding Check

11. What's Next

Data now moves in both directions: broadcast out, multiplexed back, with byte lanes deciding which parts of a word participate and a defined validity window for read data.

Every example so far has quietly relied on signals whose only job is to say what the values mean. valid said a request was real. write said which direction. byte_en said which lanes. ready said it was over. None of those carries data, and without them the address and data buses carry numbers with no interpretation.

Address and data say what values are involved. What tells the hardware what operation those values represent, and when they matter?

Chapter 2.4 — Control Signals separates payload from control, derives why an unqualified data bus is meaningless, and builds a complete target around the distinction. 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.