Skip to content
VLSI Mentor

Wishbone · Module 2

Control Signals

Address and data are payload; they say what values are involved and nothing about what should happen to them. Control information is what makes a bus interpretable: a qualifier that says a request is real, a direction, lane enables, a completion and an error — each derived from a failure that occurs without it.

Chapter 2.3 moved values in both directions and leaned the whole time on signals that carry no data at all. valid decided whether a request existed. write decided the direction. byte_en decided which lanes counted. ready decided when it was over.

Those are the subject now, and the point is not a list of them. It is why each one has to exist, derived from what breaks in its absence.

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

1. Deriving Control From Failure

Take a bus that has payload only — an address bus and a bidirectional data path — and try to use it. Each thing that breaks introduces exactly one control signal.

Failure 1: the target cannot tell a request from leftover wires.

Between transfers the address bus is still driving something — the last address, or whatever the initiator's register happens to hold. A target watching only the address sees a stream of values with no way to know which are requests. It acts on all of them, or on none.

Introduces: a qualifier. One signal meaning what is on the payload wires right now is a real request. In this module it is valid.

Failure 2: the target cannot tell a read from a write.

The same address is frequently both readable and writable, so direction cannot be inferred from it. Nor can it be inferred from whether the data wires are driven, because on a shared path somebody is always driving them.

Introduces: a direction. In this module, write.

Failure 3: the initiator cannot express a partial write.

Chapter 2.3 established this. Without lane enables every write is a full-word write, so a byte store becomes a read-modify-write — not slower but different, and on a peripheral register frequently destructive.

Introduces: lane enables. In this module, byte_en.

Failure 4: the initiator cannot tell when the target is finished.

Without this the interface must fix a latency, and Chapter 1.5 showed the fixed-latency system failing in every direction — five targets with four latencies, an initiator carrying special cases, and no structural fix for a timing problem available.

Introduces: a completion. In this module, ready.

Failure 5: a failed access is indistinguishable from a successful one.

An access to an unimplemented offset, a write to a read-only register, a target in reset — all complete. Software sees success and acts on nothing.

Introduces: an error indication. In this module, error.

Nothing on that list was chosen for elegance. Each is the minimum answer to a specific way an unqualified bus fails, and that is why this same set appears — under different names — in every on-chip bus you will meet.

2. The Two Categories, and Why the Split Matters

PayloadControl
Examples hereaddr, wdata, rdatavalid, write, byte_en, ready, error
Carriesvaluesmeaning
Between transfersstill driving somethingmust be deasserted
Meaningful whencontrol says soalways
Wrong value causeswrong datawrong operation
Widthwide, data-sizednarrow, usually one bit

The row worth dwelling on is the third. Payload is allowed to be garbage between transfers — nobody is looking. Control is not, because control is what tells everyone whether to look. A qualifier that glitches high for one cycle manufactures a transfer that no initiator requested, out of whatever the payload wires happened to hold.

This is why control signals get the careful treatment in a design review. A wrong address produces a wrong access at a known moment. A wrong qualifier produces an access that nothing in the system asked for, and there is no software instruction to correlate it with.

3. RTL — A Complete Target Built on Control

Everything so far, in one block: a UART-style target that uses every control signal for something.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────────
// uart_regs — a target that exercises every control signal in the Module 2
// teaching interface.
//
// PURPOSE. Show what each control signal actually causes in RTL, including
// the two cases a simpler target does not have: a write to a read-only
// register, and an access to an offset that does not exist.
//
// Register map (local offsets):
//   0x00 DATA    write = transmit byte (lane 0); read = last received byte
//   0x04 STATUS  read-only, hardware-updated
//   0x08 CTRL    read/write, byte-writable
//
// Generic educational interface — NOT Wishbone signal names. Wishbone's own
// signals and their rules are Module 4's subject.
// ─────────────────────────────────────────────────────────────────────────
module uart_regs #(
  parameter int unsigned AW = 8,
  parameter int unsigned DW = 32
) (
  input  logic          clk,
  input  logic          rst_n,

  // ── Control + payload from the initiator ─────────────────────────────
  input  logic          sel,
  input  logic          valid,      // CONTROL: a request exists this cycle
  input  logic          write,      // CONTROL: direction
  input  logic [AW-1:0] addr,       // PAYLOAD: local offset
  input  logic [DW-1:0] wdata,      // PAYLOAD
  input  logic [3:0]    byte_en,    // CONTROL: which lanes participate

  // ── Control + payload back to the initiator ──────────────────────────
  output logic          ready,      // CONTROL: finished on this edge
  output logic [DW-1:0] rdata,      // PAYLOAD
  output logic          error,      // CONTROL: finished unsuccessfully

  // ── The hardware side ────────────────────────────────────────────────
  output logic [7:0]    tx_byte,
  output logic          tx_start,
  input  logic          tx_busy,
  input  logic [7:0]    rx_byte,
  input  logic          rx_valid
);
  localparam logic [AW-1:0] OFF_DATA   = 'h00;
  localparam logic [AW-1:0] OFF_STATUS = 'h04;
  localparam logic [AW-1:0] OFF_CTRL   = 'h08;

  logic [DW-1:0] ctrl_q;
  logic [7:0]    rx_hold_q;
  logic          rx_ready_q;

  // ── Qualification: both conditions, per Chapter 2.1 ──────────────────
  logic access;
  assign access = sel & valid;

  // ── Classify the access. Three outcomes, and they are mutually
  //    exclusive by construction: legal, wrong offset, wrong operation.
  logic addr_legal, write_to_ro;
  always_comb begin
    unique case (addr)
      OFF_DATA, OFF_STATUS, OFF_CTRL: addr_legal = 1'b1;
      default:                        addr_legal = 1'b0;
    endcase
  end
  assign write_to_ro = access & write & (addr == OFF_STATUS);

  // ── Completion and error. Both are CONTROL outputs owned by this block,
  //    and both are gated on `access` so this target can never complete a
  //    transfer belonging to somebody else.
  //
  //    This target is never busy, so `ready` is combinational. A target that
  //    COULD be busy would hold `ready` low instead — the mechanism Chapter
  //    2.5 builds the initiator side of.
  assign ready = access;
  assign error = access & (~addr_legal | write_to_ro);

  // An errored access must NOT change state. Folding that into one term
  // used by every write branch is what keeps the rule in a single place.
  logic write_ok;
  assign write_ok = access & write & addr_legal & ~write_to_ro;

  // ── Sequential state ─────────────────────────────────────────────────
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      ctrl_q     <= '0;
      rx_hold_q  <= '0;
      rx_ready_q <= 1'b0;
    end else begin
      // Hardware-driven: a newly received byte latches and sets the flag.
      if (rx_valid) begin
        rx_hold_q  <= rx_byte;
        rx_ready_q <= 1'b1;
      end

      if (write_ok && (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

      // READ-TO-CLEAR. A successful read of DATA consumes the byte. This is
      // a control-signal consequence people miss: the side effect is
      // conditioned on a completing READ, so it must not fire on a write to
      // the same offset, nor on a cycle where `access` is low.
      if (access && !write && addr_legal && (addr == OFF_DATA))
        rx_ready_q <= 1'b0;
    end
  end

  // ── Transmit trigger. A write to DATA is an ACTION, not storage: the
  //    value goes to the shifter and a one-cycle start pulse is issued.
  //    `tx_start` is gated on lane 0 because the byte lives there.
  assign tx_byte  = wdata[7:0];
  assign tx_start = write_ok & (addr == OFF_DATA) & byte_en[0] & ~tx_busy;

  // ── Read path. Zero-extended, fully assigned on every path.
  always_comb begin
    rdata = '0;
    if (access && !write && addr_legal) begin
      unique case (addr)
        OFF_DATA:   rdata = {{(DW-8){1'b0}}, rx_hold_q};
        OFF_STATUS: rdata = {{(DW-3){1'b0}}, rx_ready_q, tx_busy, ~tx_busy};
        OFF_CTRL:   rdata = ctrl_q;
        default:    rdata = '0;
      endcase
    end
  end
endmodule

Reading this module

Purpose. Every control signal is load-bearing here. Remove any one and a specific behaviour becomes inexpressible.

Interface contract. sel and valid together qualify; write directs; byte_en selects lanes; ready and error are this block's outputs and are asserted only inside an access.

Combinational decisions. Whether an access exists; whether the offset is legal; whether the operation is legal for that offset; whether a write may change state (write_ok); the read value; and the transmit trigger.

Sequential behaviour. Three pieces of state. ctrl_q changes only on a successful write, per lane. rx_hold_q and rx_ready_q are driven by hardware on receive and cleared by a bus read — the read-to-clear behaviour Chapter 1.2 warned about, now implemented.

Timing. Everything completes in the cycle it is requested. tx_start is one cycle wide because write_ok is, which matters: a held request would otherwise restart the transmitter every cycle.

Assumptions and simplifications. No wait states — a real UART would hold ready low while tx_busy, and Chapter 2.5 builds the other side of that. rx_valid is assumed synchronous to clk. There is no receive FIFO, no interrupt output, and ready is combinational from valid, which is the loop hazard Chapter 2.1 flagged.

How it could fail.

  • Drop ~write_to_ro from write_ok and a write to STATUS reports an error and changes state — the worst combination, because software was told it failed.
  • Drop the !write condition on the read-to-clear and a write to DATA also consumes the received byte.
  • Drop ~tx_busy from tx_start and a write during transmission corrupts the byte in flight.
  • Make tx_start a level instead of a pulse and every held request restarts the transmitter.

4. Waveform — A Generic Handshake

Control signals across four accesses

9 cycles
A generic educational bus over nine clock cycles showing four situations. In cycle one a write to the control register is qualified by valid and write both high with a byte enable of F, and ready is high so it is accepted on that edge. Cycle two is idle with valid low, during which the address and write data buses still carry stale values that no target acts on because valid is low. In cycle three a read of the status register is accepted and read data is valid only in that cycle. In cycle five an access to an offset that does not exist is accepted and the error output is high together with ready, showing that an errored access still completes rather than hanging. Cycles seven and eight are idle again.write to CTRL acceptedwrite to CTRL acceptedIDLE — addr drives, valid low, nobody actsIDLE — addr drives, validlow, nobody actsread of STATUS; rdata valid only hereread of STATUS; rdata validonly herebad offset: error AND ready togetherbad offset: error AND readytogetherclkvalidwriteaddr--0x080x080x040x040x400x400x40--byte_en00xF0xF0xF0xF0xF0xF0xF0readyrdata------0x0000_0003----------errort0t1t2t3t4t5t6t7t8
Figure 1 — four transfers: a write, a read, an errored access, and an idle gap. A generic educational handshake, not any bus's timing.

The two cycles worth studying are 2 and 5.

Cycle 2 is the argument for a qualifier. addr still carries 0x08 and write is still high — the payload wires are indistinguishable from cycle 1's real write. The only thing that stops every target from writing again is valid being low. That is what qualification means, and it is why a bus without it does nothing rather than less.

Cycle 5 is the argument for an error that completes. The access is illegal, and it still asserts ready. An errored access that failed to complete would hang the initiator — turning a recoverable software fault into a dead system. Error and completion are different facts: one says it is over, the other says it did not work.

5. Verification — Properties About Control

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────────
// Control-signal properties for a target on the Module 2 teaching interface.
// These encode the rules THIS chapter states. They are not any bus's
// protocol rules.
// ─────────────────────────────────────────────────────────────────────────
module control_checker #(
  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          ready,
  input logic          error,
  input logic [DW-1:0] ctrl_q,          // white-box: the writable register
  input logic [DW-1:0] status_shadow    // white-box: expected read-only value
);
  default disable iff (!rst_n);

  // P1 — completion only inside a qualified access. The ownership rule from
  //      Chapter 2.1, restated now that `valid` has been derived properly.
  property p_ready_qualified;
    @(posedge clk) ready |-> (sel && valid);
  endproperty
  a_ready_qualified : assert property (p_ready_qualified)
    else $error("ready asserted outside a qualified access");

  // P2 — an error is a KIND of completion, never an alternative to it. A
  //      target that raised error without ready would hang the initiator,
  //      which is the cycle-5 argument in Section 4.
  property p_error_implies_ready;
    @(posedge clk) error |-> ready;
  endproperty
  a_error_implies_ready : assert property (p_error_implies_ready)
    else $error("error asserted without completing the transfer");

  // P3 — a read-only register is not modified by a bus write. Expressed on
  //      the state rather than on the interface, because that is where the
  //      damage would be.
  property p_ro_immutable;
    @(posedge clk) $changed(status_shadow) |-> !(ready && write);
  endproperty
  a_ro_immutable : assert property (p_ro_immutable)
    else $error("read-only state changed on a completing write");

  // P4 — the writable register changes ONLY on an accepted, non-errored
  //      write. This catches the `write_ok` term being wrong in either
  //      direction: a write that should have been rejected and landed, or a
  //      register that moves when no write occurred at all.
  property p_ctrl_only_on_good_write;
    @(posedge clk) $changed(ctrl_q) |-> $past(ready && write && !error);
  endproperty
  a_ctrl_only_on_good_write : assert property (p_ctrl_only_on_good_write)
    else $error("ctrl_q changed without an accepted, error-free write");
endmodule

Why these four. P1 and P2 are interface rules; P3 and P4 are state rules, and the pairing is deliberate. An interface-only property cannot catch a target that reports an error and changes state anyway — the worst failure in Section 3's list, because software has been told the access failed.

$past in P4 refers to the previous clock cycle's values, which is what makes the property expressible: ctrl_q changes at an edge, and the conditions that permitted it were true before that edge.

What is not asserted. Nothing about how long a target may take, because this target is never busy. That is the next chapter.

6. Failure Modes and Discriminating Evidence

Symptom: registers change with no corresponding software access.

Candidates. A target not gating on valid, so it acts on stale payload between transfers. A qualifier that glitches. A held request that a target re-applies every cycle.

Discriminating evidence. Trigger on the register changing and look at valid in that cycle. Low means the target is not qualifying. High for many consecutive cycles with one intended access means the target is re-applying a held request — it needs edge detection, or the initiator needs to drop valid after acceptance.

Property that catches it. P4.

Symptom: a write reports an error and takes effect anyway.

Candidates. The write-enable term does not include the error condition.

Discriminating evidence. Access a read-only offset with a write; check error and then read the register back. Both true is conclusive.

Likely RTL location. The write_ok term — specifically a missing ~write_to_ro or addr_legal.

Property that catches it. P3 and P4 together.

Symptom: the system hangs on an access to a non-existent offset.

Candidates. The target raises error instead of ready rather than with it.

Discriminating evidence. Probe both. error high with ready low is the bug, and it is visible in a single cycle.

Property that catches it. P2.

Symptom: a received byte disappears when a debugger is attached.

Candidates. Read-to-clear on the DATA register, consumed by the debugger's register view.

Discriminating evidence. Detach the debugger and see whether the problem stops. Then check whether the read-to-clear is correctly gated on !write and on access.

Correct conclusion: usually not a bug at all, but the documented behaviour interacting with a tool — which is why a register map must mark read-to-clear registers explicitly.

Symptom: the UART transmits the same byte repeatedly.

Candidates. tx_start is a level rather than a pulse, so a held request restarts it every cycle.

Discriminating evidence. Put valid, ready and tx_start on one waveform. A tx_start wider than one cycle is conclusive.

Likely RTL location. The tx_start expression — it must be derived from the one-cycle acceptance term, not from valid alone.

7. Common Mistakes

"The address bus tells the target what to do."

Wrong mental model: the address is the request.

Concrete failure: a target acts on whatever the address bus holds between transfers, writing registers nobody asked to write.

Observable evidence: register state changing with no software access, often correlated with the previous access's address.

Correct model: the address is payload. Qualification is what turns a value on a wire into a request, and it is a separate signal by necessity.

"Error and completion are alternatives."

Wrong mental model: a transfer either succeeds, or errors, and those are two different endings.

Concrete failure: a target raises error and never raises ready. The initiator waits forever. A recoverable software fault becomes a dead system.

Observable evidence: a reproducible hang on a specific bad address, with error high in the waveform.

Correct model: error is a property of a completion, not a substitute for it. Every access ends; error says how it ended.

"A control signal can be held for as long as convenient."

Wrong mental model: holding a request steady is harmless.

Concrete failure: a target that treats the request as level-sensitive applies it every cycle — writing a register repeatedly, or restarting a transmitter mid-byte.

Observable evidence: an action that happens many times for one software access; a transmitter that never finishes a byte.

Correct model: the interface must define when a request takes effect. Here it is the single edge where valid and ready are both high, and every side effect must be derived from that term rather than from valid alone.

"Read-to-clear is a peripheral quirk, not a bus concern."

Wrong mental model: side effects live inside the peripheral and do not interact with control signals.

Concrete failure: the clear fires on a write to the same offset, or on a cycle where the target was not selected — consuming an event nothing read.

Observable evidence: receive data lost when an unrelated register is written.

Correct model: a side effect is conditioned on a completing access of a particular direction, which is a control-signal expression. Getting it wrong is a control bug wearing a peripheral costume.

8. Interview Reasoning

Because the wires are never blank, so a target has no way to distinguish a request from residue.

Between transfers the address bus is still driving something — the last address, or whatever the initiator's register holds. A target watching only payload sees a continuous stream of values and cannot tell which represent requests. It either acts on all of them or on none, and both are useless.

A qualifier is the minimum fix, and everything else follows the same pattern: direction cannot be inferred from an address, because the same address is usually both readable and writable; partial writes cannot be expressed without lane enables; completion cannot be inferred without a fixed latency, which reintroduces every problem Module 1 catalogued; and a failed access cannot be distinguished from a successful one without an error indication.

The framing that distinguishes a strong answer: payload says what values are involved, control says what operation they constitute. Payload without control is not an incomplete bus — it is a bus that cannot do anything, because nothing on it can be interpreted.

9. Understanding Check

10. What's Next

Control has been derived rather than listed: a qualifier because payload wires are never blank, a direction because an address cannot carry one, lane enables because partial writes must be expressible, a completion because latency must be local, and an error because failure must be distinguishable from success.

Each has been treated as an individual signal. But the waveform in Section 4 shows something none of them describes alone: cycle 1 and cycle 3 are not five independent signal events each — they are two operations, each with a beginning, a middle and an end.

What turns a collection of address, data and control signals into one coherent operation — and what must stay true for as long as that operation is in flight?

Chapter 2.5 — Bus Transactions makes the transaction the unit: its lifecycle, what the initiator must hold stable while waiting, what a target that is genuinely busy forces the other side to build, and how an operation ends when it does not succeed. 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.