Skip to content
VLSI Mentor

Wishbone · Module 9

Transaction Extension

Stretching a transfer from one clock to six changes its duration and nothing else. A measured matrix: same transfers, same terminations, same value, same single side effect.

Chapter 9.2 gave the slave its obligations and Chapter 9.3 gave the master its own. Each was demonstrated on one operation at a time, at one latency at a time.

This chapter runs the whole grid.

If a transfer can be stretched to any length, what exactly is preserved across all of those lengths?

1. Why This Is Not Obvious

It sounds like a tautology. Of course a longer transfer is the same transfer — what else would it be?

Four chapters of this course have measured designs for which it was false.

Chapter 6.2 — a master whose address advanced on elapsed clocks read the wrong register once latency appeared.

Chapter 7.4 — a write whose payload moved mid-transfer committed a value the client never asked for.

Chapter 8.3 — a block master that advanced on clocks rather than terminations silently lost half its words.

Chapter 9.2 — a slave that committed on every presented clock turned one command into four.

In each case the design was correct at zero wait states and wrong at three. The invariant is not automatic; it is a property that correct designs have and broken ones do not, and the only way to know which you have is to vary the latency and measure.

That is what makes the matrix worth running rather than reasoning about. A single-latency test suite cannot distinguish a design that preserves transaction identity from one that merely has not been given the opportunity to break it.

2. The Two Claims, Kept Apart

One distinction has to be made before the numbers, because conflating it turns a clean result into a confusing one.

Claim A — the transaction is invariant. One accepted request produces one transfer, one termination, one side effect and one completion report, at every latency. This is a protocol-level claim and it holds unconditionally for a correct design.

Claim B — the data is invariant. The value read is the same at every latency.

Claim B is not a protocol claim at all. It holds here because the operations read ID — a constant — and read back CONTROL, which nothing else writes. Reading a free-running register would return four different values at four different latencies, and that would say nothing whatever about the protocol.

Chapter 9.1 §10 raised this deliberately and the matrix keeps it separate: COUNT is excluded from these runs for exactly that reason. The target's state changing over time is not the transaction changing.

3. Waveform — The Same Read, Stretched

One transfer, two durations

10 cycles
Ten clock cycles comparing the same read against two slaves. Against the zero wait slave the cycle and strobe signals are asserted only at cycle two, the acknowledge is asserted in that same cycle, and the captured data appears at cycle three. Against the three wait slave the cycle and strobe signals are asserted from cycle two through cycle five, the acknowledge is asserted only at cycle five, and the captured data appears at cycle six. Both runs contain exactly one acknowledge and both capture the same identifier value.both present the same requestboth present the samerequestzero-wait: already capturedzero-wait: already capturedthree-wait: terminates herethree-wait: terminates hereCLK_IW=0 CYC+STBW=0 ACK_IW=0 rdat000IDIDIDIDIDIDIDW=3 CYC+STBW=3 ACK_IW=3 rdat000000IDIDIDIDt0t1t2t3t4t5t6t7t8t9
Figure 1 — one logical read of word 4, run simultaneously against a zero-wait slave and a three-wait slave. Both are one transfer; both return the same value. Traced from the simulation in Section 4.

Both runs start identically. At cycle 2 each master presents the same address with the same direction. Nothing at the presenting edge distinguishes a transfer that will take one clock from one that will take four — which is the point, because the master does not know and must not care.

The zero-wait run is over in that clock. ACK_I is asserted at cycle 2, the transfer terminates, and the value is captured and visible at cycle 3.

The three-wait run holds. CYC_O and STB_O stay asserted through cycles 3, 4 and 5 — three wait clocks — and ACK_I rises only at cycle 5. The value is captured at that edge and visible at cycle 6.

Count the acknowledges in each run: one. That is the whole invariant, visible. The lower group is the upper group with three clocks inserted in the middle, and nothing else has changed.

And the two rdat rows hold the same value. Different clocks, same data — because ID is a constant and the master captured at its own terminating edge in both cases.

4. Simulation — SIM I, the Extension Matrix

Four rigs, identical except for WAIT_CYCLES. Each performs the same three operations in the same order with the same master.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  === SIM I - transaction extension matrix ===
    three operations, four slave latencies, one master

    OPERATION 1 - read word 4 (ID)
      WAIT  transfers  waits  presented  value
        0        1        0        1      0x57420901
        1        1        1        2      0x57420901
        2        1        2        3      0x57420901
        5        1        5        6      0x57420901

    OPERATION 2 - write word 2 (CONTROL) = 0x1234ABCD
      WAIT  transfers  waits  presented
        0        1        0        1
        1        1        1        2
        2        1        2        3
        5        1        5        6
      read back      0x1234abcd (all four latencies agreed: 1)

    OPERATION 3 - write word 9 (COMMAND), a side-effecting register
      WAIT  side effects fired
        0            1
        1            1
        2            1
        5            1

    TOTALS after all four operations
      WAIT  transfers  terminations  CYC clks  STB clks  metadata changes
        0        4           4             4         4            0
        1        4           4             8         8            0
        2        4           4            12        12            0
        5        4           4            24        24            0

Reading the matrix

Operation 1, the read. transfers = 1 on every row. value = 0x57420901 on every row. Waits and presented clocks are the only columns that move, and they move exactly as WAIT_CYCLES and WAIT_CYCLES + 1.

Operation 2, the register write. Same shape: one transfer at every latency, and the read-back confirms all four rigs committed 0x1234ABCD. A write held for six clocks committed the same value as one held for one.

Operation 3, the side-effecting write. side effects fired = 1 at every latency. This is the row that Chapter 9.2's broken slave fails — it would read 1, 2, 3, 6 down this column, tracking WAIT_CYCLES + 1.

The totals are where the invariance becomes a single picture.

transfers = 4 and terminations = 4 on every row. Four operations, four transfers, four terminations, regardless of latency. Time passing does not create transfers.

CYC clks and STB clks go 4, 8, 12, 24. That is 4 × (WAIT_CYCLES + 1) — the duration, scaling linearly and exactly as predicted.

metadata changes = 0 on every row, which is what licenses the rest. The transfers were the same transfers because nothing about them moved.

And CYC clks == STB clks on every row, which is worth a note. This master presents one transfer per bus cycle and never inserts a gap, so the two counts coincide. That is a property of this master, not of the protocolChapter 8.3 showed CYC_O asserted with STB_O negated, and Chapter 8.5 made the general case. A slave wait state keeps both asserted; a master gap drops only STB_O. Distinguishing the two is what the separate counters are for.

What would break each row

The value of an invariance table is that each row names a defect class.

Row that changedWhat it would mean
transfers grows with latencythe master is re-presenting, or the monitor counts STB_O clocks
terminations growsthe slave acknowledges more than once per request
side effects growsthe slave commits per presented clock — 9.2's bug
value changesthe master captures before the terminating edge, or the target is stateful
metadata changes non-zerothe master violates RULE 3.60 — 9.3's bug
waitsWAIT_CYCLESan off-by-one in the latency counter — 9.5

Every one of those has been built and measured in this module, which is why the table is a diagnostic rather than a reassurance.

5. Verification — Properties That Hold At Any Latency

These are the module's summary properties. Each is written so that it does not mention a latency at all — which is the point.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Transaction-extension properties. The defining feature of this set is
// that none of them refers to a number of clocks. A property that had to
// know the latency would be describing a timing, not an invariant.
//
// NOTE ON EXECUTION: Icarus Verilog does not support SVA. These were
// reviewed by inspection and are NOT claimed to have been executed. The
// numbers in Section 3 come from procedural checks, which Icarus does run.
// ─────────────────────────────────────────────────────────────────────────
module wb_extension_props #(
  parameter int unsigned AW = 30,
  parameter int unsigned DW = 32
) (
  input logic            clk_i, rst_i,
  input logic            cyc_o, stb_o, we_o,
  input logic [AW-1:0]   adr_o,
  input logic [DW-1:0]   dat_o,
  input logic [DW/8-1:0] sel_o,
  input logic            ack_i, err_i, rty_i,
  input logic            req_accepted_i,   // master latched a client request
  input logic            done_o,           // master reported completion
  input logic            commit_i          // slave performed its side effect
);
  default clocking cb @(posedge clk_i); endclocking
  default disable iff (rst_i);

  logic outstanding, terminated;
  assign outstanding = cyc_o && stb_o && !(ack_i || err_i || rty_i);
  assign terminated  = cyc_o && stb_o &&  (ack_i || err_i || rty_i);

  // ── E1..E4 — SPECIFICATION (RULE 3.60). Identity is fixed at the
  //    presenting edge. These are the same four obligations as Chapter 9.3,
  //    restated here because they are what MAKES the extension safe.
  E1_adr_stable:   assert property ( outstanding |=> $stable(adr_o) );
  E2_we_stable:    assert property ( outstanding |=> $stable(we_o)  );
  E3_sel_stable:   assert property ( outstanding |=> $stable(sel_o) );
  E4_wdata_stable: assert property ( (outstanding && we_o) |=> $stable(dat_o) );

  // ── E5 — LOCAL POLICY. One accepted request produces at most one
  //    termination. Written as: between two acceptances there is at most
  //    one terminating edge.
  E5_one_termination: assert property (
    terminated |=> !terminated until_with req_accepted_i
  );

  // ── E6 — LOCAL POLICY, and the one Chapter 9.2's broken slave fails.
  //    A side effect happens only at a terminating edge. Not on a waiting
  //    clock, however many of those there are.
  E6_commit_at_termination: assert property ( commit_i |-> $past(terminated) );

  // ── E7 — LOCAL POLICY. One accepted client request, one completion
  //    report. This is the master's contract with its client; Wishbone
  //    does not govern that interface at all.
  E7_one_done: assert property ( done_o |-> $past(terminated) );

  // ── E8 — LOCAL POLICY. A presented transfer stays presented until it
  //    terminates: the master does not withdraw a request it has issued.
  //    Note this is a statement about THIS master's style. A block master
  //    may legitimately negate STB_O between transfers of a cycle
  //    (Chapter 8.3), so this property does not generalise to one.
  E8_no_withdrawal: assert property ( outstanding |=> (cyc_o && stb_o) );
endmodule

E1 through E4 are specification; E5 through E8 are policy. That split is the whole point of the labelling discipline this course has used since Chapter 5.8.

Notice what E1–E4 actually do here. They are the same stability properties as Chapter 9.3, but the framing has changed: they are not merely obligations, they are the mechanism by which extension is safe. A transfer can be stretched arbitrarily precisely because nothing that identifies it may move while it is stretched.

E8 is the property I would be most careful about exporting. It is true of a single-transfer master and false of a conformant block master, which negates STB_O between transfers while holding CYC_O — the master-side throttle Chapter 8.3 introduced. A property that is correct for one design and wrong for another is exactly what the LOCAL POLICY label exists to flag.

None of these properties mentions a number of clocks. That is the test for whether a property is about an invariant or about a timing. If you have to parameterise it by WAIT_CYCLES, it is describing the latency, not the transaction — which is a legitimate thing to check, and is what Chapter 9.1's P3 does, labelled as policy.

6. Failure Modes and Discriminating Evidence

Symptom: an operation produces the right result at one latency and the wrong result at another.

Candidate causes. Something in the design is counting clocks where it should be counting terminations.

Discriminating evidence. Run the matrix. Any column that should be invariant and is not names the defect class directly — the table in Section 4 maps each one. This is faster than reasoning about the RTL, because it turns "something is wrong" into "the side-effect count scales with latency".

Symptom: a design passes every unit test and fails in the system.

Candidate causes. The suite uses one latency — almost always zero.

Discriminating evidence. Add a second latency and re-run. If the failure appears, the defect is in this module's territory. Four of the seven measured bugs in this course are invisible at zero wait states.

Symptom: a read returns different data at different latencies and the design looks correct.

Candidate causes. Two, and they are easy to confuse.

Discriminating evidence. Ask whether the target is stateful. A free-running counter legitimately returns different values when sampled at different clocks, and that is Claim B failing while Claim A holds. If the target is a constant and the value still varies, the master is capturing outside the termination — a genuine bug.

This distinction is why the matrix reads ID rather than COUNT.

Symptom: CYC clks and STB clks diverge unexpectedly.

Candidate causes. The master is inserting gaps — holding the cycle with nothing presented.

Discriminating evidence. The difference is exactly the gap count. For a single-transfer master it should be zero; for a block master it is the master-side throttle and is legitimate. Neither number alone tells you which, which is why both are reported.

7. Common Mistakes

"A longer transfer is obviously the same transfer."

Wrong mental model: invariance is automatic.

What is true: it is a property of correct designs, and four chapters of this course have measured designs that lacked it.

Concrete bug: any of the four in Section 1 — each correct at zero wait states and wrong at three.

Observable evidence: a column in the matrix that should be flat and is not.

Correct model: the invariant is a claim to be tested, and the test is to vary the latency.

"If the value read is the same, the transaction was the same."

Wrong mental model: data equality proves protocol equality.

What is true: they are different claims. A constant register returns the same value at every latency whether or not the protocol behaved; a free-running one returns different values even when it did.

Concrete bug: concluding that a design is latency-safe because a read-back test passed, when the register happened to be idempotent — the trap Chapter 7.4 measured for W1C.

Observable evidence: the matrix reads ID deliberately and excludes COUNT.

Correct model: check transfers, terminations and side effects, not just data.

"CYC_O high for six clocks means the bus was busy with six things."

Wrong mental model: clocks are a proxy for work.

What is true: CYC clks went 4, 8, 12, 24 across the matrix while transfers stayed at 4. Duration and content are independent — the separation Chapter 8.1 established and Chapter 8.6 quantified.

Concrete bug: a performance model or a coverage metric keyed on asserted clocks.

Observable evidence: the totals table, where one pair of columns scales and the other does not.

Correct model: count terminations for content and clocks for cost.

"An invariant property can be written once and used everywhere."

Wrong mental model: a correct property is universally correct.

What is true: E8 — "a presented transfer stays presented" — is true of this master and false of a conformant block master, which negates STB_O between transfers.

Concrete bug: importing a property set into a block-capable design and "fixing" the design to satisfy it, removing the master-side throttle.

Observable evidence: the property fires on every gap in a legal block cycle.

Correct model: label each property specification or policy, and check the scope before reusing it.

8. Interview Reasoning

Everything that identifies the transaction; nothing that describes its cost.

Invariant: the address, the direction, the byte lanes, the write payload, and the counts — one transfer, one termination, one architectural side effect, one completion report to the client. Scaling: clocks presented, clocks CYC_O and STB_O are asserted, and total time.

I ran that as a matrix rather than arguing it: the same read, the same register write and the same side-effecting write at four latencies. Transfers and terminations were 4 and 4 on every row; the clock counts went 4, 8, 12, 24. The side-effect count was 1 at every latency, and the committed value was identical.

What makes it work is a single mechanism, and I would name it rather than list the outcomes: RULE 3.60 fixes the transaction's identity at the presenting edge. Address, direction, byte lanes and payload are qualified by STB_O, so none of them may move while the transfer is outstanding. The transfer can be stretched arbitrarily precisely because nothing that defines it is allowed to change.

And I would push back gently on the premise that this is obvious. It is a property correct designs have, not something the protocol enforces on its own. I have measured four designs that had it at zero wait states and lost it at three — a master advancing its address on clocks, a write whose payload moved, a block master that skipped words, a slave that committed once per presented clock. All four passed their zero-wait tests.

One distinction I would insist on. "The same value came back" is a weaker claim than "the same transaction happened". Reading a free-running counter at four latencies gives four different values and the protocol behaved perfectly. I would test invariance with a constant register and check transfer and side-effect counts, not data equality.

9. Understanding Check

10. What's Next

The invariants are now stated, measured and written as properties. A transfer can be stretched to any length, and a correct design carries its identity, its data and its single side effect across the whole of it.

Every bug in this module so far has been introduced deliberately and diagnosed with the answer already known. That is the wrong way round from how they arrive in practice.

A transfer is stuck, or completed with the wrong data, and nobody knows why. Where do you look?

Chapter 9.5 — Common Bugs turns these invariants into a debugging procedure, against traces where the fault is not announced in advance. 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.