Skip to content
VLSI Mentor

Wishbone · Module 15

Multi-Master Systems

Byte-identical masters against two interconnects. The ownership timeline names clock 19, and three checkers are validated against the defects they claim to catch.

Chapter 15.3 bounded what a bus lock can reach. Two chapters have used a two-master system as laboratory equipment without looking at it closely.

What does the interconnect have to do, and what happens when it does not?

1. RTL — The Smallest Thing That Can Give Two Masters One Slave

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────────
// wb_owner2 — the smallest thing that can give two masters one slave.
//
// THIS IS LABORATORY EQUIPMENT, NOT AN ARBITER DESIGN. It exists so that
// atomicity can be measured against competition, because atomicity cannot
// be demonstrated honestly with one master. Fairness, starvation, priority
// schemes, round-robin and scalable fabrics are Module 17's subject and
// none of them appear here: this selector is fixed-priority, two-port, and
// deliberately uninteresting.
//
// The specification is explicit that this is the integrator's territory:
// "Arbitration methodology is defined by the end user (priority arbiter,
// round-robin arbiter, etc.)." So every behaviour below is LOCAL POLICY.
//
// THE ONE PARAMETER THAT MATTERS: HONOUR_LOCK.
//
//   HONOUR_LOCK = 1  once a master owns the bus with LOCK_O asserted, the
//                    owner is not changed until that master negates LOCK_O
//                    or CYC_O. This is the behaviour LOCK_O's description
//                    states: "Once the transfer has started, the INTERCON
//                    does not grant the bus to any other MASTER, until the
//                    current MASTER negates [LOCK_O] or [CYC_O]."
//
//   HONOUR_LOCK = 0  ownership is re-evaluated whenever the current owner
//                    is not presenting a transfer, LOCK_O or not.
//
// HONOUR_LOCK = 0 IS NOT A PROTOCOL VIOLATION IN ANY RULE I CAN CITE. No
// numbered rule in the Classic chapter constrains an arbiter. What it
// violates is LOCK_O's stated meaning, and Chapter 15.4 measures what a
// system loses when a signal's meaning is not implemented.
//
// RECOMMENDATION 3.05 is the closest the specification comes to describing
// arbiter behaviour: "Arbitration logic often uses [CYC_I] to select
// between MASTER interfaces." Note "often" - it is advisory, and it
// describes selection, not retention.
// ─────────────────────────────────────────────────────────────────────────
module wb_owner2 #(
  parameter int unsigned AW          = 30,
  parameter int unsigned DW          = 32,
  parameter bit          HONOUR_LOCK = 1'b1
) (
  input  logic            clk_i,
  input  logic            rst_i,
  // master A
  input  logic            a_cyc_i,
  input  logic            a_stb_i,
  input  logic            a_lock_i,
  input  logic            a_we_i,
  input  logic [AW-1:0]   a_adr_i,
  input  logic [DW-1:0]   a_dat_i,
  input  logic [DW/8-1:0] a_sel_i,
  output logic [DW-1:0]   a_dat_o,
  output logic            a_ack_o,
  output logic            a_err_o,
  // master B
  input  logic            b_cyc_i,
  input  logic            b_stb_i,
  input  logic            b_lock_i,
  input  logic            b_we_i,
  input  logic [AW-1:0]   b_adr_i,
  input  logic [DW-1:0]   b_dat_i,
  input  logic [DW/8-1:0] b_sel_i,
  output logic [DW-1:0]   b_dat_o,
  output logic            b_ack_o,
  output logic            b_err_o,
  // slave side
  output logic            s_cyc_o,
  output logic            s_stb_o,
  output logic            s_we_o,
  output logic [AW-1:0]   s_adr_o,
  output logic [DW-1:0]   s_dat_o,
  output logic [DW/8-1:0] s_sel_o,
  input  logic [DW-1:0]   s_dat_i,
  input  logic            s_ack_i,
  input  logic            s_err_i,
  // observation only
  output logic            owner_o,        // 0 = A, 1 = B
  output logic            owned_o         // somebody holds it
);
  logic owner_q, owned_q;

  // The current owner's own signals, whoever that is.
  logic cur_cyc, cur_stb, cur_lock;
  assign cur_cyc  = owner_q ? b_cyc_i  : a_cyc_i;
  assign cur_stb  = owner_q ? b_stb_i  : a_stb_i;
  assign cur_lock = owner_q ? b_lock_i : a_lock_i;

  // Who else wants it.
  logic other_cyc;
  assign other_cyc = owner_q ? a_cyc_i : b_cyc_i;

  // OWNERSHIP, in one always_comb so the precedence is readable.
  //
  // This selector implements LOCK_O's stated meaning and nothing more:
  // "Once the transfer has started, the INTERCON does not grant the bus to
  // any other MASTER, until the current MASTER negates [LOCK_O] or
  // [CYC_O]." A LOCKED owner is therefore untouchable until it drops one.
  //
  // An UNLOCKED owner is a case the specification does not legislate.
  // RECOMMENDATION 3.05 observes only that arbiters "often use [CYC_I] to
  // select between MASTER interfaces", and warns that "keeping [CYC_O]
  // asserted may lead to arbitration problems". This selector's LOCAL
  // POLICY is the ordinary one that warning describes: an unlocked owner
  // that is not presenting a transfer yields to a master that is waiting.
  //
  // That single rule is what makes retaining CYC_O and asserting LOCK_O
  // measurably different. Without it, an owner would keep the bus through
  // its own idle clocks and every policy would look identical.
  //
  // With HONOUR_LOCK cleared, the locked branch is removed and a locked
  // owner is treated exactly like an unlocked one - Chapter 15.4's broken
  // interconnect.
  logic next_owner, next_owned;
  always_comb begin
    next_owner = owner_q;
    next_owned = owned_q;
    if (!owned_q) begin
      if      (a_cyc_i) begin next_owner = 1'b0; next_owned = 1'b1; end
      else if (b_cyc_i) begin next_owner = 1'b1; next_owned = 1'b1; end
    end else if (!cur_cyc) begin
      // the owner has finished its cycle
      if      (other_cyc) begin next_owner = ~owner_q; next_owned = 1'b1; end
      else                       next_owned = 1'b0;
    end else if (HONOUR_LOCK && cur_lock) begin
      next_owner = owner_q;              // locked: nothing moves
    end else if (!cur_stb && other_cyc) begin
      next_owner = ~owner_q;             // unlocked and idle: yield
    end
  end

  always_ff @(posedge clk_i) begin
    if (rst_i) begin
      owner_q <= 1'b0; owned_q <= 1'b0;
    end else begin
      owner_q <= next_owner;
      owned_q <= next_owned;
    end
  end

  // Route the owner's request down, and the slave's answer back to the
  // owner only. A non-owner sees no termination and no data, so it simply
  // waits - which is what makes it a competitor rather than a corrupter.
  assign s_cyc_o = owned_q && (owner_q ? b_cyc_i  : a_cyc_i);
  assign s_stb_o = owned_q && (owner_q ? b_stb_i  : a_stb_i);
  assign s_we_o  =            owner_q ? b_we_i   : a_we_i;
  assign s_adr_o =            owner_q ? b_adr_i  : a_adr_i;
  assign s_dat_o =            owner_q ? b_dat_i  : a_dat_i;
  assign s_sel_o =            owner_q ? b_sel_i  : a_sel_i;

  assign a_ack_o = owned_q && !owner_q && s_ack_i;
  assign a_err_o = owned_q && !owner_q && s_err_i;
  assign a_dat_o = s_dat_i;
  assign b_ack_o = owned_q &&  owner_q && s_ack_i;
  assign b_err_o = owned_q &&  owner_q && s_err_i;
  assign b_dat_o = s_dat_i;

  assign owner_o = owner_q;
  assign owned_o = owned_q;
endmodule

Reading it

HONOUR_LOCK is the only functional difference between SIM G's system and SIM H's. One branch of one always_comb.

The locked branch is LOCK_O's description turned into logic. "Once the transfer has started, the INTERCON does not grant the bus to any other MASTER, until the current MASTER negates LOCK_O or CYC_O." — so while cur_cyc && cur_lock, ownership does not move.

The unlocked branch is the case the specification does not legislate, and the policy chosen here is the ordinary one: an unlocked owner that is not presenting a transfer yields to a waiting master. RECOMMENDATION 3.05 warns in exactly this direction"Keeping CYC_O asserted may lead to arbitration problems."

That single rule is what makes CYC_O retention and LOCK_O measurably different. Without it, an owner would keep the bus through its own idle clocks and every policy in Chapter 15.2's table would look identical.

Response routing is the other half of correctness and it is three lines. A non-owner sees no termination and no data, so it waits — which is what makes it a competitor rather than a corrupter. A selector that let a non-owner see the slave's acknowledgement would produce two masters believing one answer.

2. RTL — The Instrument

Every claim in this chapter is a claim about ownership over an interval, so the interval has to be defined in one place.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────────
// wb_rmw_probe — the ownership timeline, and three checkers built on it.
//
// The three checkers are the ones Chapter 15.4 validates in BOTH
// directions: each must pass a correct system AND fail a targeted broken
// one, for the reason it claims to check. A checker that has only ever
// passed is an untested checker.
//
//   OWNER STABILITY    ownership does not change while a locked owner is
//                      mid-operation. Targeted defect: an owner selector
//                      that ignores LOCK_O.
//   COMMIT COUNT       one successful increment produces exactly one write
//                      commit. Targeted defect: a slave that re-executes a
//                      write while its termination is held.
//   ATOMIC RESULT      N successful increments move the counter by exactly
//                      N. Targeted defect: any lost update.
//
// The protected interval is defined here once, because every ownership
// claim in the module depends on which clocks are inside it:
//
//   from the clock a master's CYC_O is first asserted for the operation
//   through the clock its write phase is answered
//
// That is the interval an RMW must protect. An owner change inside it is
// the defect; an owner change outside it is ordinary arbitration.
//
// Everything here is simulation instrumentation. `int unsigned` counters
// are not synthesizable and none of this is a Wishbone feature.
// ─────────────────────────────────────────────────────────────────────────
module wb_rmw_probe (
  input  logic        clk_i,
  input  logic        rst_i,
  input  logic        owner_i,        // 0 = A, 1 = B
  input  logic        owned_i,
  // the two masters' own signals
  input  logic        a_cyc_i,
  input  logic        a_lock_i,
  input  logic        b_cyc_i,
  input  logic        b_lock_i,
  // the slave's commit evidence
  input  logic        wr_commit_i,    // one clock per committed write
  output int unsigned owner_changes_o,
  output int unsigned unsafe_changes_o,   // changes inside a protected interval
  output int unsigned commits_o,
  output int unsigned a_cycles_o,
  output int unsigned b_cycles_o
);
  logic owner_q, owned_q, a_cyc_q, b_cyc_q;

  // A master is mid-operation while its own CYC_O is asserted. Protection
  // is claimed only while it also asserts LOCK_O - that is the difference
  // between requesting the resource and holding it.
  logic a_protected, b_protected, cur_protected;
  assign a_protected = a_cyc_i && a_lock_i;
  assign b_protected = b_cyc_i && b_lock_i;
  assign cur_protected = owner_i ? b_protected : a_protected;

  always_ff @(posedge clk_i) begin
    if (rst_i) begin
      owner_q <= 1'b0; owned_q <= 1'b0; a_cyc_q <= 1'b0; b_cyc_q <= 1'b0;
      owner_changes_o <= 0; unsafe_changes_o <= 0; commits_o <= 0;
      a_cycles_o <= 0; b_cycles_o <= 0;
    end else begin
      owner_q <= owner_i; owned_q <= owned_i;
      a_cyc_q <= a_cyc_i; b_cyc_q <= b_cyc_i;

      if (owned_q && owned_i && (owner_i != owner_q)) begin
        owner_changes_o <= owner_changes_o + 1;
        // Was the master that just lost the bus still holding it under
        // lock? If so the selector took the bus from a protected owner.
        if (owner_q ? b_protected : a_protected)
          unsafe_changes_o <= unsafe_changes_o + 1;
      end

      if (wr_commit_i) commits_o <= commits_o + 1;
      if (a_cyc_i && !a_cyc_q) a_cycles_o <= a_cycles_o + 1;
      if (b_cyc_i && !b_cyc_q) b_cycles_o <= b_cycles_o + 1;
    end
  end
endmodule

Reading it

unsafe_changes_o is the module's central measurement and its definition is narrow on purpose. It counts an ownership change only when the master losing the bus was asserting both CYC_O and LOCK_O — that is, only when it had actually claimed protection.

That narrowness is why Chapter 15.1's CYC-only row reports zero unsafe changes while losing an update. That master never asserted LOCK_O. It was not robbed; it never asked. A counter that flagged every ownership change would have reported a violation there and been wrong about what happened.

The commit input comes from the slave's own register update, not from acknowledgements. Section 6 shows why: a slave that executes a write several times per phase still produces one acknowledgement, so a bus-side counter cannot see that defect at all.

3. Simulation — SIM G: An Interconnect That Honours LOCK_O

Both masters released on the same clock, both asserting LOCK_O across their read and write halves. The timeline records every clock on which the slave port is active.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  === SIM G - two masters, the selector honours LOCK_O ===
    Both masters are released on the same clock. Both assert
    LOCK_O across their read and write halves.

    clk  owner  CYC  LOCK  STB  WE  ACK  phase
      4    A     1    1     1    0   1   read answered
      5    A     1    1     0    0   0   -
      6    A     1    1     1    1   1   write commits
      8    B     1    1     1    0   1   read answered
      9    B     1    1     0    0   0   -
     10    B     1    1     1    1   1   write commits

    A read 10, wrote 11    B read 11, wrote 12
    owner changes                      1
    owner changes inside a protected interval  0
    write commits                      2
    counter  10 -> 12   expected 12

    Ownership changed once, and it changed between the two
    operations rather than inside either of them.

Reading it

Read the owner column down the timeline: A, A, A, then B, B, B. One change, and it falls between the two operations rather than inside either.

Clock 5 is the interesting one. The owner is A, CYC is high, LOCK is high, and STB is low — A is presenting nothing. That is exactly the clock an unlocked owner would have yielded on, and Chapter 15.1's CYC-only row did. Here the lock holds it.

A read 10 and wrote 11. B read 11 and wrote 12. B's read was not served until A's write had committed, so it saw the updated value and derived from it. Counter 10 → 12.

Owner changes inside a protected interval: 0. Two write commits for two increments.

4. Simulation — SIM H: An Interconnect That Does Not

Identical masters, identical slave, identical stimulus. HONOUR_LOCK is cleared and nothing else changes.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  === SIM H - the same masters, the selector ignores LOCK_O ===
    Identical masters, identical slave, identical stimulus.
    HONOUR_LOCK is cleared, so a locked owner is treated
    exactly like an unlocked one.

    clk  owner  CYC  LOCK  STB  WE  ACK  phase
     17    A     1    1     1    0   1   read answered
     18    A     1    1     0    0   0   -
     19    B     1    1     1    0   1   read answered
     20    B     1    1     0    0   0   -
     21    A     1    1     1    1   1   write commits
     23    B     1    1     1    1   1   write commits

    A read 10, wrote 11    B read 10, wrote 11
    owner changes                      3
    owner changes inside a protected interval  2
    write commits                      2
    counter  10 -> 11   expected 12

    Both masters are byte-identical to SIM G's. Both asserted
    LOCK_O for the whole interval. The signal was correct and
    the interconnect did not implement its meaning.

Reading it — the timeline names the clock

Clock 18: owner A, LOCK high, STB low. A has read 10 and is between its halves.

Clock 19: owner B. The selector granted the bus to B while A was holding a lock. B reads 10 — the value A has already captured and is about to supersede.

Clock 21: owner A again, write commits 11. Clock 23: B commits 11. Counter 10 → 11.

Owner changes: 3. Inside a protected interval: 2.

And notice what the ownership timeline gives that the final value does not. "Counter is 11, expected 12" says something is wrong. "Owner changed at clock 19 while A held a lock" says what, where and whose.

When the other master was able to enter

10 cycles
Ten clock cycles comparing the two interconnects on the same stimulus. Master A asserts cycle and lock throughout its operation in both. In the honouring interconnect the owner row reads A across A's whole operation and changes to B only after A's write commits. In the ignoring interconnect the owner row switches to B on the clock where A is between its halves and presenting nothing, and B's read returns the stale value ten.A has read 10; write still pendingA has read 10; write stillpendingA presents nothing: the only way inA presents nothing: theonly way inhonouring: B enters only nowhonouring: B enters onlynowCLK_IA: CYC_OA: LOCK_OA: STB_OB: CYC_Ohonours: owner0AAAAABBBBignores: owner0AABAABBBBignores: B read00010101010101010t0t1t2t3t4t5t6t7t8t9

The two owner rows are identical except at cycle 3, and cycle 3 is the clock on which A: STB_O is low while A: CYC_O and A: LOCK_O are both high.

That is the whole difference. A is between its halves, presenting nothing, and holding a lock. One interconnect treats the lock as binding and one does not.

The bottom row is the consequence. B reads 10 — a value A captured two clocks earlier and is about to supersede — and it will write 11 over A's 11.

5. Simulation — SIM I: Wait States Inside the Protected Interval

The slave withholds its termination for two clocks on every phase, so each half of each operation waits. The interval an interloper would need to enter is three times longer.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  === SIM I - wait states inside the protected interval ===
    The slave withholds its termination for two clocks on
    every phase, so each half of each RMW waits.

    clk  owner  CYC  LOCK  STB  WE  ACK  phase
     30    A     1    1     1    0   0   read
     31    A     1    1     1    0   0   read
     32    A     1    1     1    0   1   read answered
     33    A     1    1     0    0   0   -
     34    A     1    1     1    1   0   write
     35    A     1    1     1    1   0   write
     36    A     1    1     1    1   1   write commits
     38    B     1    1     1    0   0   read
     39    B     1    1     1    0   0   read
     40    B     1    1     1    0   1   read answered
     41    B     1    1     0    0   0   -
     42    B     1    1     1    1   0   write
     43    B     1    1     1    1   0   write
     44    B     1    1     1    1   1   write commits

    owner changes inside a protected interval  0
    write commits                      2
    counter  10 -> 12   expected 12

    Longer interval, same guarantee. The wait states stretch
    the window an interloper would need, and ownership did
    not move for any of those clocks. Note also that a held
    phase produced ONE commit, not one per clock.

Reading it

Fourteen active clocks instead of six, and the owner column still reads A, A, A, A, A, A, A, then B seven times.

Zero owner changes inside a protected interval. The wait states stretched the window and the lock covered all of it — because the master's LOCK_O is derived from its state machine, which leaves the read, gap and write states only on an answered phase. The release cannot precede the commit by construction rather than by timing.

And the commit count is the second finding in this run. Each write phase is presented for three clocks and commits once. wr_fire is true only on the answered edge, so a held phase produces one register update. Chapter 15.1 called this load-bearing; this is where it bears the load — a slave committing per presented clock would have written six times here.

This also reconnects Module 9 directly — the transaction extension Chapter 9.4 measured. A wait state is the slave withholding its termination, and it has no effect on ownership whatsoever — the lock is a master-to-interconnect statement and the slave is not party to it.

6. The Negative-Control Gate

A checker that has only ever passed has not been shown to check anything. Each of the three below is run twice: once against a correct system, once against a system containing the specific defect it claims to detect.

No checker was weakened to make a broken case fail. Where a checker initially passed both, the experiment was wrong and was corrected — Section 7 says which.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  === NEGATIVE-CONTROL GATE ===
    Each checker run against a correct system and against the
    one defect it claims to detect. A checker that cannot
    fail has not been shown to check anything.

    checker          invariant                        correct  broken
    OWNER STABILITY  no owner change inside a          PASS     FAIL
                     protected interval
    COMMIT COUNT     two increments, two write         PASS     FAIL
                     commits
    ATOMIC RESULT    counter moves by exactly two      PASS     FAIL

    targeted defects:
      OWNER STABILITY  wb_owner2 with HONOUR_LOCK = 0
      COMMIT COUNT     slave committing on every presented clock
      ATOMIC RESULT    the same lock-ignoring selector

    'commits' below is the slave's own register-update count,
    not a count of acknowledgements. The held-phase rig
    produces one ACK per phase either way.

    evidence behind each verdict:
      correct rig    unsafe owner changes 0   commits 2   counter 12
      lock-ignoring  unsafe owner changes 2   commits 2   counter 11
      held-phase     unsafe owner changes 0   commits 6   counter 12

    CHECKERS REQUIRED                >= 3
    CHECKERS PASSING CORRECT DUT     3 / 3
    CHECKERS FAILING TARGET DEFECT   3 / 3

Reading it

Three checkers, three correct-system passes, three targeted failures. Each fails for the reason it claims.

The third row of the evidence block is the one worth studying. The held-phase rig shows 6 commits and a counter of 12 — the final value is correct.

So ATOMIC RESULT passes that rig. The counter moved by exactly two, because writing 11 three times leaves 11. Only COMMIT COUNT sees the defect, and it sees it because it reads the slave's own register-update count rather than counting acknowledgements on the bus.

That is the case for having three checkers rather than one. Each catches something the others cannot:

defectOWNER STABILITYCOMMIT COUNTATOMIC RESULT
interconnect ignores LOCK_Ocatchesmissescatches
slave commits per presented clockmissescatchesmisses
a stale read from any causemissesmissescatches

A single end-to-end check would have passed the middle row, and on a FIFO port or a command register that defect is destructive rather than harmless.

7. What This Gate Cost, Honestly

Two of the three checkers worked first time. The third did not, and the reason is worth publishing.

COMMIT COUNT initially passed both the correct system and the broken one. The "broken" rig was a slave with wait states, which is not broken at all — a held phase still commits once. The experiment was testing nothing.

The fix was to build the real defect, not to loosen the checker: a slave parameter that gates the commit on the transfer being presented rather than answered, which is the repeated-side-effect failure Modules 5, 7, 9 and 11 each measured in their own way.

It still passed both. The second problem was the instrument: the commit counter was reading ACK on the bus, and a slave that commits six times still acknowledges once per phase. Pointing the counter at the slave's own register update made the defect visible — 6 against 2.

Both corrections were to the experiment. Neither weakened an invariant, and the gate's value is precisely that it forced them: before this, the commit checker had never failed and there was no evidence it could.

8. Simulation — SIM J: Errors Inside a Read-Modify-Write

The slave implements one offset and answers ERR for any other, which is Chapter 12.3's ordinary unmapped behaviour. It also supports write-protection. Both give a genuine ERR without a specially faulty slave.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  === SIM J - errors inside a read-modify-write ===
    The slave implements offset 0 and answers ERR for any
    other offset, which is Module 12's ordinary unmapped
    behaviour. Aiming a phase at offset 1 injects a real ERR.

    --- ERR on the read half ---
    client result            failure
    read phases served       0
    write commits            0
    counter  10 -> 10

    LOCAL MASTER POLICY: an error on the read abandons the
    operation. No write phase was presented, so nothing was
    committed and there is nothing to undo. The client is
    told the operation failed.

    --- ERR on the write half ---
    value read               10
    value the master derived 11
    client result            failure
    read phases served       1
    write commits            0
    counter  10 -> 10

    The read succeeded and the write did not. The client is
    told failure, and the counter is unchanged.

    THE COUNTER IS UNCHANGED BECAUSE OF THIS SLAVE, NOT
    BECAUSE OF WISHBONE. wb_counter_slave gates its commit on
    an acknowledged write, so a phase it answers with ERR
    commits nothing - LOCAL SLAVE POLICY, and verifiable in
    its source. A slave that latched first and errored after
    would leave the value changed and report failure, and
    nothing in the specification forbids that.

    NOTHING HERE IS A ROLLBACK. Wishbone defines error
    termination; it defines no mechanism for undoing a
    committed write, and no master can restore a value it
    never knew had changed.

    Across both cases: one client request, one client
    completion, and no partial success reported as success.

Reading it

The read-half error is the simple case. Nothing was committed because no write was ever presented, and there is nothing to undo. One client request, one client completion, classified as failure.

The write-half error is the one that needs care. The read succeeded, the master derived 11, the write was refused, and the counter is unchanged.

And the counter is unchanged because of this slave, not because of Wishbone. wb_counter_slave computes its acknowledgement and its commit from the same refused term, so a phase it answers with ERR commits nothing. That is LOCAL SLAVE POLICY and it is verifiable in the sourceP12 in Chapter 15.3 states it.

A slave that latched first and errored afterwards would leave the value changed and report failure, and nothing in the specification forbids that. The master would have no way to know which kind it was talking to.

9. Failure Modes and Discriminating Evidence

Symptom: correct masters, correct slave, lost updates.

Candidate causes. An interconnect that does not honour LOCK_I.

Discriminating evidence. An ownership timeline across the interval. An owner change while the losing master asserted CYC_O and LOCK_O is conclusive and names the clock. Inspecting the master will find nothing — measured here with byte-identical masters on both sides.

Likely location: the interconnect, not either master.

Symptom: the final value is correct and something is still wrong.

Candidate causes. Repeated commits, invisible for an idempotent write.

Discriminating evidence. The slave's register-update count against the number of successful operations. Six against two. Acknowledgement counts will not show it, and for a FIFO or command port the same defect is destructive rather than harmless.

Symptom: an operation fails only when the slave is slow.

Candidate causes. A lock released on write presentation rather than write completion.

Discriminating evidence. The last clock on which LOCK_O is asserted, against the clock the write commits. With a zero-wait slave these coincide and the defect is invisible; with wait states they separate. SIM I is the condition that exposes it.

Symptom: an RMW reports success after an ERR.

Candidate causes. The client completion folds all terminations into one path.

Discriminating evidence. The termination class on each phase against the reported client result. P7 requires exactly one of success or failure per completion, and P8 requires an error not to be reported as success.

Symptom: a system works on the bench and fails in the product.

Candidate causes. The bench had one master.

Discriminating evidence. Whether a competitor exists at all. A point-to-point system has no arbiter and no interloper, so every protection scheme in Chapter 15.2's table passes. The defect appears when a second master is added, which is an integration event rather than a code change.

10. Verification

The interconnect's own two properties, and both are local — the specification places no numbered requirement on an arbiter.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────────
// wb_owner_props — the interconnect's half of atomicity. All LOCAL: the
// specification places no numbered requirement on an arbiter, and states
// that "Arbitration methodology is defined by the end user".
// ─────────────────────────────────────────────────────────────────────────
module wb_owner_props (
  input logic clk_i,
  input logic rst_i,
  input logic owner_i,
  input logic owned_i,
  input logic a_cyc_i,
  input logic a_lock_i,
  input logic b_cyc_i,
  input logic b_lock_i
);
  default disable iff (rst_i);

  logic cur_locked;
  assign cur_locked = owner_i ? (b_cyc_i && b_lock_i) : (a_cyc_i && a_lock_i);

  // P9 — LOCAL INTERCONNECT POLICY, implementing LOCK_O's stated meaning:
  // "Once the transfer has started, the INTERCON does not grant the bus to
  // any other MASTER, until the current MASTER negates [LOCK_O] or
  // [CYC_O]." This is the property wb_owner2 with HONOUR_LOCK = 0
  // violates, and it is validated in both directions in Chapter 15.4.
  property p_owner_stable_while_locked;
    @(posedge clk_i) (owned_i && cur_locked) |=> $stable(owner_i);
  endproperty
  a_owner_stable_while_locked: assert property (p_owner_stable_while_locked);

  // P10 — LOCAL INTERCONNECT POLICY. A master that holds nothing is not
  // the owner. Ownership implies somebody is asserting CYC_O.
  property p_owned_implies_requesting;
    @(posedge clk_i) owned_i |-> (a_cyc_i || b_cyc_i);
  endproperty
  a_owned_implies_requesting: assert property (p_owned_implies_requesting);
endmodule

P9 is LOCK_O's description written as a property, and it is the one validated in both directions in Section 6 — it passes the honouring selector and fails the one that ignores locks.

P10 is the sanity property that prevents the selector claiming an owner nobody asked to be.

Neither is specification-derived, and the distinction is the module's whole point. A design can satisfy every numbered rule in the Classic chapter and still provide no atomicity, because no numbered rule mentions an arbiter at all.

11. Common Mistakes

"The masters are conformant, so the operation is atomic."

Wrong mental model: conformance composes upward.

What is true: measured false with byte-identical masters. Correct master RTL against an interconnect that ignores LOCK_I loses the update, and every transfer on the bus is conformant throughout.

"The arbiter must hold ownership while LOCK_O is asserted."

Wrong mental model: a rule requires it.

What is true: LOCK_O's description says what an INTERCON does; no numbered rule compels one. Arbitration methodology is "defined by the end user", so an integration has to establish it rather than assume it.

"An error rolls back the operation."

Wrong mental model: the bus has transaction semantics.

What is true: nothing undoes a committed write. This slave commits nothing on an error, which is verifiable in its source and is its policy — a different slave could latch first and error afterwards.

"A held ACK means the write happened more than once."

Wrong mental model: acknowledgement equals commit.

What is true: they are the same only in a well-built slave. SIM I holds a phase for three clocks and commits once. The broken variant commits three times and still acknowledges once — which is why a commit count has to be taken at the resource.

"Checking the final value is enough."

Wrong mental model: one end-to-end check covers everything.

What is true: six commits with a correct final value. For an increment that defect is invisible in the state; on a FIFO port it is data loss.

"Two masters is Module 16's subject, so this is out of scope."

Wrong mental model: any second master means multi-master architecture.

What is true: atomicity cannot be measured without a competitor, and the selector here is laboratory equipment. Why multiple masters exist, how they are arbitrated and how fabrics scale are all Module 16 and Module 17 — none of it is taught here.

12. Interview Reasoning

Atomicity is lost and every master in the system is still conformant.

Measured directly. Two masters asserting LOCK_O across their read and write halves, an interconnect with that branch removed, and the ownership timeline shows the second master granted the bus at clock 19 — while the first was holding a lock, between its read and its write. It reads the stale value and both write 11.

Nothing on the bus is malformed. Four transfers, four terminations, correct addresses, correct data.

And no numbered rule was broken. The Classic chapter places no requirement on an arbiter, and the introduction states that arbitration methodology is "defined by the end user". What was violated is LOCK_O's stated meaning, which is a different thing from a rule.

The practical consequence is the one to lead with in a review: a master cannot verify atomicity by inspecting itself. The guarantee is the interconnect's, and whether a given one honours LOCK_I is an integration fact that RULE 2.15's datasheet should record.

13. Understanding Check

Clock 19.

At clock 18 the owner is A, CYC and LOCK are both high, and STB is low — A has read 10 and is between its halves.

At clock 19 the owner is B. The selector granted the bus to a different master while A was holding a lock.

Everything after that follows correctly from a wrong decision. B reads 10, which is still the true value. Both masters derive 11. Both write it. The counter ends at 11.

The probe reports owner changes inside a protected interval: 2, and the timeline says which clocks they were.

Compare SIM G's timeline at the same point: clock 5, owner A, STB low, and the owner column does not change. Same master, same slave, same stimulus — one branch of one always_comb apart.

14. What Module 15 Established

An operation is atomic only over an interval, only against a named set of competitors, and only if something enforces it.

15.1 — the failure, and the cycle. Two conformant masters, four conformant transfers, and a counter that moves by one. The RMW cycle is defined by §3.4 as read half, write half, CYC_O across both — and measured, that structure alone loses the update.

15.2 — the interval. From before the read is observable to the write's commit, continuously. Two locks that are real, correctly asserted, and useless: CYC_O requests, LOCK_O holds, which the specification says in one sentence that is not in the RMW section.

15.3 — the scope. A perfect lock, an honouring arbiter, no second master, one update lost. LOCK_I promises a slave is "accessed by a single MASTER only"and everything that is not a master is outside that sentence.

15.4 — the enforcer. Byte-identical masters against two interconnects; the timeline names clock 19. And three checkers validated against the defects they claim to catch, one of which needed two corrections to the experiment before it could fail at all.

The thread is one question asked four ways: what, exactly, is excluded — and by whom? Every defect in this module is an answer assumed rather than established.

15. What's Next

Read-modify-write is complete. The interval, the competitors, the enforcer, and the measured consequences of getting each one wrong.

Two masters have been laboratory equipment for three chapters. Nothing has explained why a system has them, or how an interconnect decides between them when neither holds a lock.

Why do real systems have more than one master, and what does sharing a bus actually require?

Module 16 — Multi-Master Systems takes up the architecture this module borrowed: why a CPU and a DMA engine share an interconnect, how ownership is granted, and what resource sharing costs. 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.