Skip to content
VLSI Mentor

Wishbone · Module 4

RTY_I

A slave saying "not now" rather than "no". Unlike a wait state it releases the bus, which breaks a whole class of deadlock — and unlike an error it invites another attempt, which is what makes livelock possible.

Chapter 4.11 established that retrying an error is a bug. But some refusals are not permanent, and for those, retrying is the entire point.

How does a slave say "not now" rather than "no" — and why is retry the one termination that can deadlock a system?

1. Why Not Just Insert Wait States?

A slave that is momentarily busy already has a mechanism: withhold the acknowledge, as Chapter 4.10 §3 described. So why does RTY_O exist at all?

Because a wait state holds the bus, and a retry releases it.

Withholding the acknowledge keeps the master's transfer presented. CYC_O stays asserted, the arbiter keeps the grant, and no other master can use the bus until this slave is ready.

Asserting RTY_O terminates the transfer. The master negates CYC_O and STB_O, the bus becomes available, and the master comes back later.

When the slave will become ready on its own — a memory refreshing, a pipeline draining — waiting is fine and simpler. Holding the bus for a few cycles costs a little throughput.

2. What a Retry Obliges the Master to Do

The specification says when and how is supplier-defined, so this is engineering judgement, presented as such.

Release the bus. Genuinely negate CYC_O — not merely drop STB_O and keep the tenure. Holding the cycle across a retry recreates the deadlock the retry was meant to break, while looking like correct retry handling.

Do not treat it as success. Nothing happened. No data was transferred, no state changed.

Do not treat it as failure either. Reporting a retry upward as an error converts a transient condition into a spurious failure.

Bound the attempts. This is where retry differs from every other termination, and it deserves its own section.

3. Livelock, and Why Retry Needs a Bound

Unbounded retry is not a safe default. A master that retries forever will retry forever if the condition never clears, and unlike a hang it does so while generating bus traffic — which makes it worse in two ways.

It consumes bandwidth. A hung master sits still. A retrying master occupies the bus repeatedly, delaying everyone else and possibly preventing the very progress that would clear its condition.

It looks healthy. The bus is active, transfers are terminating, no rule is violated, no signal is stuck. Nothing about the waveform says "this system is not making progress", which is what makes livelock harder to recognise than deadlock.

Two masters can retry each other indefinitely. Each holds a resource the other needs, each releases the bus politely on RTY_I, each retries, and the interleaving repeats. Every individual action is correct. The system does nothing, busily.

What a real master does instead:

Count the attempts and escalate after a bounded number — report a failure upward, where a caller can decide.

Back off between attempts, so a retrying master does not monopolise the bus with the requests that prevent the condition clearing. Even a small fixed delay changes the dynamics materially.

Avoid lock-step, where two masters retry in phase and collide every time. A varying backoff — even a crude one derived from the master's ID — breaks the symmetry. Unlike the other two, this one only matters with multiple masters, and a single-master design that omits it will not reveal the omission until a second master arrives.

Document the policy, per RULE 2.15.

4. RTL — A Retrying Slave and a Backing-Off Master

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────────
// wb_rty_slave — a mailbox FIFO that RETRIES rather than waits when full.
//
// WHY RETRY AND NOT A WAIT STATE (Section 1): this FIFO's readiness depends
// on ANOTHER master draining it. Holding the bus while waiting for space
// would prevent the drain and deadlock the system. Terminating with RTY_O
// releases the bus so the drain can happen.
//
// A single-master version of this same FIFO could safely use wait states.
// The RTL would be correct in one system and deadlock the other, which is
// why the choice belongs in the datasheet (RULE 2.15).
//
// Reset: SYNCHRONOUS, ACTIVE HIGH (RULES 2.30, 3.00).
// ─────────────────────────────────────────────────────────────────────────
module wb_rty_slave #(
  parameter int unsigned OFF_AW = 2,
  parameter int unsigned DW     = 32,
  parameter int unsigned DEPTH  = 4
) (
  input  logic                  clk_i,
  input  logic                  rst_i,
  input  logic                  cyc_i,
  input  logic                  stb_i,
  input  logic                  we_i,
  input  logic [OFF_AW-1:0]     adr_i,
  input  logic [DW-1:0]         dat_i,
  output logic [DW-1:0]         dat_o,
  output logic                  ack_o,
  output logic                  err_o,
  output logic                  rty_o,
  output logic [3:0]            level_o
);
  localparam logic [OFF_AW-1:0] W_DATA  = 'd0;    // push (W) / pop (R)
  localparam logic [OFF_AW-1:0] W_LEVEL = 'd1;    // read-only occupancy

  logic [DW-1:0] mem_q [DEPTH];
  logic [3:0]    lvl_q;
  assign level_o = lvl_q;

  logic xfer;
  assign xfer = cyc_i & stb_i;                    // RULES 3.30 & 3.35

  logic full, empty;
  assign full  = (lvl_q == 4'(DEPTH));
  assign empty = (lvl_q == 4'd0);

  // ── TERMINATION SELECTION ──────────────────────────────────────────────
  // Three conditions, three different answers, and the distinction between
  // the last two is the chapter:
  //
  //   e_perm : this can NEVER work        -> ERR_O  (permanent)
  //   r_now  : this cannot work RIGHT NOW -> RTY_O  (transient)
  //   else                                -> ACK_O
  //
  // A full FIFO is not an error. The same write will succeed once a drain
  // happens, so reporting ERR_O would turn a transient condition into a
  // permanent failure and the master would correctly never try again.
  logic e_perm, r_now;

  always_comb begin
    unique case (adr_i)
      W_DATA:  e_perm = 1'b0;
      W_LEVEL: e_perm = we_i;                     // level is read-only
      default: e_perm = 1'b1;                     // unmapped offset
    endcase
  end

  always_comb begin
    r_now = 1'b0;
    if (!e_perm && (adr_i == W_DATA)) begin
      r_now = we_i ? full : empty;                // full: no space to push
    end                                           // empty: nothing to pop
  end

  // RULE 3.45: mutually exclusive by construction. Exactly one is asserted
  // for any qualified transfer, and none otherwise.
  assign err_o = xfer &  e_perm;
  assign rty_o = xfer & ~e_perm &  r_now;
  assign ack_o = xfer & ~e_perm & ~r_now;

  // ── A RETRIED TRANSFER CHANGES NOTHING ─────────────────────────────────
  // The same obligation an errored transfer carries (Chapter 4.11 Sec. 2),
  // and for a sharper reason: the master WILL come back. A retry that
  // half-completed would apply its effect once per attempt.
  logic push, pop;
  assign push = ack_o &  we_i & (adr_i == W_DATA);
  assign pop  = ack_o & ~we_i & (adr_i == W_DATA);

  always_ff @(posedge clk_i) begin
    if (rst_i) begin
      lvl_q <= 4'd0;
      for (int unsigned i = 0; i < DEPTH; i++) mem_q[i] <= '0;
    end else begin
      if (push) begin
        mem_q[lvl_q[1:0]] <= dat_i;
        lvl_q             <= lvl_q + 4'd1;
      end else if (pop) begin
        for (int unsigned i = 0; i < DEPTH-1; i++) mem_q[i] <= mem_q[i+1];
        lvl_q <= lvl_q - 4'd1;
      end
    end
  end

  always_comb begin
    dat_o = '0;                                   // RULE 3.65
    if (xfer && !we_i && ack_o) begin
      dat_o = (adr_i == W_LEVEL) ? {{(DW-4){1'b0}}, lvl_q} : mem_q[0];
    end
  end
endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────────
// wb_rty_master — bounded retry with backoff.
//
// POLICY (the material RULE 2.15 requires in a datasheet):
//   * RTY_I releases the bus completely — CYC_O is negated, not just STB_O.
//   * The transfer is re-presented after a backoff delay.
//   * Backoff grows with each attempt, and is seeded from MASTER_ID so two
//     masters do not retry in lock-step.
//   * After MAX_TRIES attempts the transfer is abandoned and reported as a
//     failure, so a never-clearing condition cannot livelock the system.
//   * ERR_I is NOT retried (Chapter 4.11).
// ─────────────────────────────────────────────────────────────────────────
module wb_rty_master #(
  parameter int unsigned AW        = 30,
  parameter int unsigned DW        = 32,
  parameter int unsigned MAX_TRIES = 8,
  parameter int unsigned MASTER_ID = 0
) (
  input  logic          clk_i,
  input  logic          rst_i,

  input  logic          go_i,
  input  logic          we_i,
  input  logic [AW-1:0] adr_i,
  input  logic [DW-1:0] wdat_i,
  output logic          done_o,
  output logic          ok_o,
  output logic          gave_up_o,
  output logic [3:0]    tries_o,
  output logic [DW-1:0] rdat_o,

  output logic          cyc_o,
  output logic          stb_o,
  output logic          we_o,
  output logic [AW-1:0] adr_o,
  output logic [DW-1:0] dat_o,
  input  logic [DW-1:0] dat_i,
  input  logic          ack_i,
  input  logic          err_i,
  input  logic          rty_i
);
  typedef enum logic [1:0] { T_IDLE, T_XFER, T_BACKOFF } tstate_e;
  tstate_e state_q;

  logic [AW-1:0] adr_q;
  logic [DW-1:0] wdat_q;
  logic          we_q;
  logic [7:0]    wait_q;

  // ── THE BUS IS GENUINELY RELEASED IN T_BACKOFF ─────────────────────────
  // cyc_o is low there, not merely stb_o. Holding CYC_O across a backoff
  // would keep the arbiter's grant and recreate the deadlock the retry
  // exists to break — while looking like correct retry handling.
  assign cyc_o = (state_q == T_XFER);
  assign stb_o = (state_q == T_XFER);
  assign we_o  = we_q;
  assign adr_o = adr_q;
  assign dat_o = wdat_q;

  // Backoff grows with the attempt count. MASTER_ID offsets it so two
  // masters retrying the same resource do not collide every time — the
  // lock-step hazard of Section 3, which only appears with more than one.
  function automatic logic [7:0] backoff_of(input logic [3:0] n);
    backoff_of = 8'((32'(n) << 2) + 32'(MASTER_ID) + 1);
  endfunction

  always_ff @(posedge clk_i) begin
    if (rst_i) begin
      state_q   <= T_IDLE;                        // RULE 3.20 via cyc/stb
      adr_q     <= '0;
      wdat_q    <= '0;
      we_q      <= 1'b0;
      wait_q    <= '0;
      tries_o   <= '0;
      rdat_o    <= '0;
      done_o    <= 1'b0;
      ok_o      <= 1'b0;
      gave_up_o <= 1'b0;
    end else begin
      done_o <= 1'b0;

      unique case (state_q)
        T_IDLE: begin
          if (go_i) begin
            state_q   <= T_XFER;
            adr_q     <= adr_i;
            wdat_q    <= wdat_i;
            we_q      <= we_i;
            tries_o   <= 4'd1;
            gave_up_o <= 1'b0;
          end
        end

        T_XFER: begin
          if (ack_i) begin
            if (!we_q) rdat_o <= dat_i;           // Chapter 4.5's capture
            state_q <= T_IDLE;
            done_o  <= 1'b1;
            ok_o    <= 1'b1;
          end else if (err_i) begin
            // Permanent. Retrying would loop forever (Chapter 4.11).
            state_q <= T_IDLE;
            done_o  <= 1'b1;
            ok_o    <= 1'b0;
          end else if (rty_i) begin
            if (tries_o >= 4'(MAX_TRIES)) begin
              // ── THE BOUND ────────────────────────────────────────────
              // Escalate rather than retry forever. A condition that has
              // not cleared in MAX_TRIES attempts is reported upward,
              // where a caller can decide. Without this, a never-clearing
              // condition livelocks the bus while looking healthy.
              state_q   <= T_IDLE;
              done_o    <= 1'b1;
              ok_o      <= 1'b0;
              gave_up_o <= 1'b1;
            end else begin
              state_q <= T_BACKOFF;               // release the bus
              wait_q  <= backoff_of(tries_o);
              tries_o <= tries_o + 4'd1;
            end
          end
        end

        T_BACKOFF: begin
          if (wait_q == 8'd0) state_q <= T_XFER;  // re-present unchanged
          else                wait_q  <= wait_q - 8'd1;
        end

        default: state_q <= T_IDLE;
      endcase
    end
  end
endmodule

Reading the pair

Purpose. The slave shows a transient condition distinguished from a permanent one. The master shows the bounded, backed-off retry that keeps a transient condition from becoming a livelock.

Ownership. The slave drives all three terminations; the master consumes them and drives its qualifiers.

Combinational logic. Slave: xfer, the permanent and transient conditions, three exclusive terminations, push/pop enables, a read multiplexer gated on ack_o. Master: qualifiers from state, and backoff_of.

Sequential logic. Slave: the FIFO array and occupancy. Master: state, latched request, backoff counter, attempt count, result flags.

Timing. All three terminations are combinational from the qualified transfer. The master spends one cycle in T_XFER per attempt and backoff_of(n) cycles in T_BACKOFF, during which CYC_O is low and the bus is available.

Qualification. Every termination contains xfer; push and pop contain ack_o, so a retried or errored transfer cannot move the FIFO.

Reset. Synchronous, active high. state_q <= T_IDLE negates both qualifiers per RULE 3.20.

Simplifications. The FIFO pops by shifting, which is clear rather than efficient — a real design uses pointers. MAX_TRIES is bounded by the 4-bit counter. The backoff function is deliberately crude; its purpose is to break symmetry, not to be optimal.

Failure modes. Section 6.

5. Waveform — Retry, Release, Succeed

RTY_I: the bus is released between attempts

9 cycles
Nine clock cycles showing a write to a full FIFO being retried. In cycle one the master presents a write with both qualifiers asserted; the FIFO is full so the slave asserts retry rather than acknowledge, and the acknowledge stays low. In cycles two through four the master has negated both its cycle and strobe signals, genuinely releasing the bus rather than merely dropping the strobe; during this backoff another master drains the FIFO and its level falls from four to three. In cycle five the master re-presents the identical transfer and the slave now acknowledges, with the level rising back to four. The attempt counter shows two attempts. The essential detail is that the cycle signal is low throughout the backoff, which is what allows the drain to happen at all.FIFO full: RTY, not ERRFIFO full: RTY, not ERRCYC low — other master drainsCYC low — other masterdrainsretry succeeds unchangedretry succeeds unchangedCLK_ICYC_OSTB_ORTY_IACK_Ilevel444333444tries012222222t0t1t2t3t4t5t6t7t8
Figure 1 — a write to a full FIFO is retried. The master releases the bus, another master drains, the retry succeeds.

Cycles 2 to 4 are the mechanism. CYC_O is low, not just STB_O. The arbiter is free to grant the bus, and the other master's drain — visible as level falling from 4 to 3 — is only possible because of that.

A master that dropped STB_O but held CYC_O would show an identical strobe waveform and would deadlock: the grant never moves, the drain never happens, and every retry finds the FIFO still full. That is why CYC_O is the signal to check when a retry loop never succeeds.

Cycle 1 shows the choice of termination. A full FIFO is not an error — the same write will succeed after a drain. Reporting ERR_O would be correct signalling of an incorrect claim, and a well-behaved master would never try again.

6. Failure Modes and Discriminating Evidence

Symptom: a master retries forever and the condition never clears.

Candidate causes. The master holds CYC_O across its backoff, so the bus is never released and whatever would clear the condition cannot run.

Discriminating evidence. Watch CYC_O, not STB_O. Asserted continuously across the retries is conclusive — and the strobe waveform looks correct in both the working and broken cases, which is why the wrong signal gets checked first.

Likely RTL location. The cyc_o assignment — asserted for the whole transaction rather than only while a transfer is presented.

Property. P2 in Section 7.

Symptom: a system stops making progress but the bus is busy and no signal is stuck.

Candidate causes. Livelock. Two or more masters retrying resources each other holds, each releasing politely, each retrying in lock-step.

Discriminating evidence. Count completed transfers over a window rather than looking for a stuck signal. Zero acknowledges with continuous bus activity is the signature, and it is invisible to any per-transfer check because every individual transfer is legal.

Likely RTL location. The retry policy — unbounded attempts, no backoff, or identical backoff across masters.

Symptom: a transient condition is reported to software as a permanent failure.

Candidate causes. The slave asserts ERR_O where RTY_O was meant — a full FIFO, a busy port, a refresh in progress.

Discriminating evidence. Retry the same access manually after a delay. Success means the condition was transient and the termination was wrong.

Likely RTL location. The termination selection. The question to ask of each condition is whether the identical transfer could succeed later without anything the master controls changing.

Symptom: a retried write takes effect more than once.

Candidate causes. The slave's state change is gated on the qualified transfer rather than on the acknowledge, so each attempt applies the effect.

Discriminating evidence. Push one value into a FIFO that retries twice and check the occupancy. An increase of three for one logical write is conclusive, and this is strictly worse than the errored-write case because the master is expected to come back.

Property. P3.

Symptom: two masters make progress individually but stall when run together.

Candidate causes. Lock-step retry — identical backoff, so both retry on the same cycle every time and collide identically.

Discriminating evidence. The retries align cycle-for-cycle across masters. Changing one master's MASTER_ID and seeing the stall clear confirms it.

7. Verification

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────────
// wb_rty_checker — retry properties.
//
// P1 is SPECIFICATION (RULES 3.35, 3.45). P2, P3 and P4 are DESIGN
// OBLIGATIONS — the specification says when and how a cycle is retried is
// supplier-defined, so these encode THIS system's documented policy, which
// is the material RULE 2.15 requires in the datasheet.
// ─────────────────────────────────────────────────────────────────────────
module wb_rty_checker #(
  parameter int unsigned MAX_TRIES = 8
) (
  input logic       clk_i,
  input logic       rst_i,
  input logic       cyc_i,
  input logic       stb_i,
  input logic       ack_o,
  input logic       err_o,
  input logic       rty_o,
  input logic [3:0] lvl_q,
  input logic [3:0] tries_q
);
  default disable iff (rst_i);

  // P1 — SPECIFICATION. RULE 3.35: generated from the qualified transfer.
  //      RULE 3.45: exclusive with the other two.
  property p_rty_legal;
    @(posedge clk_i) rty_o |-> ((cyc_i && stb_i) && !ack_o && !err_o);
  endproperty
  a_rty_legal : assert property (p_rty_legal)
    else $error("RTY_O unqualified or asserted with another termination");

  // P2 — DESIGN OBLIGATION, and the one this chapter exists for. A retry
  //      must RELEASE THE BUS: cyc_i is negated the cycle after RTY_O.
  //      A master that keeps the tenure recreates the deadlock the retry
  //      was meant to break, while its strobe waveform looks correct.
  property p_rty_releases_bus;
    @(posedge clk_i) (cyc_i && stb_i && rty_o) |=> !cyc_i;
  endproperty
  a_rty_releases_bus : assert property (p_rty_releases_bus)
    else $error("CYC_I still asserted the cycle after RTY_O");

  // P3 — DESIGN OBLIGATION. A retried transfer changes nothing. Sharper
  //      than the errored case: the master WILL come back, so an effect
  //      applied per attempt is applied repeatedly.
  property p_rty_changes_nothing;
    @(posedge clk_i) $changed(lvl_q) |-> !$past(rty_o);
  endproperty
  a_rty_changes_nothing : assert property (p_rty_changes_nothing)
    else $error("slave state changed on a transfer terminated with RTY_O");

  // P4 — DESIGN OBLIGATION, as LIVENESS. Attempts are BOUNDED. Nothing at
  //      the signal level forbids infinite retry, and livelock produces a
  //      bus that looks entirely healthy — so this is the only place the
  //      failure is visible.
  property p_tries_bounded;
    @(posedge clk_i) tries_q <= 4'(MAX_TRIES);
  endproperty
  a_tries_bounded : assert property (p_tries_bounded)
    else $error("retry attempts exceeded the documented bound");
endmodule

P4 is a liveness property in a module otherwise full of safety properties, and the distinction is worth naming. A safety property says nothing bad happens; a liveness property says something good eventually does. Livelock violates no safety property anywhere — every transfer is legal, every rule is obeyed — and is caught only by asking whether progress occurred.

This is the third time this module has reached that boundary: atomicity in Chapter 4.9, the RULE 3.55 hang in Chapter 4.10, and livelock here. All three are conformant buses attached to systems that do not work, and none is detectable from signal legality alone.

Tooling limitation. Icarus has no SVA support; reviewed by inspection only.

8. Common Mistakes

"Retry and error are both failures, so handle them together."

Wrong mental model: a termination is either success or failure.

Concrete bug: if (err_i || rty_i) fail(); — which converts every transient condition into a permanent failure, or, with the branches reversed, retries a permanent error forever.

Observable evidence: spurious failures under load, or a master looping on an unmapped address.

Correct model: they differ in exactly one respect — whether another attempt could succeed. That single bit is why there are three terminations rather than two, and a master that collapses them has discarded the information the third wire exists to carry.

"Retrying is safe, so retry until it works."

Wrong mental model: the condition will always clear.

Concrete bug: unbounded retry. If the condition never clears, the master retries forever while generating bus traffic.

Observable evidence: a system making no progress with a fully active bus and no stuck signals — much harder to recognise than a hang, because everything looks healthy.

Correct model: bound the attempts and escalate. A condition that has not cleared after a documented number of tries is a failure, and reporting it lets a caller decide.

"I dropped STB_O on the retry, so I released the bus."

Wrong mental model: the strobe is what holds the bus.

Concrete bug: dropping STB_O while holding CYC_O across the backoff. The arbiter keeps the grant, the other master never runs, the condition never clears, and the retry never succeeds.

Observable evidence: a retry loop that never succeeds, with a strobe waveform identical to the working case.

Correct model: CYC_O is the bus request (Chapter 4.9). Releasing the bus means negating it. This is the same signal that provides atomicity when held — and here holding it is exactly the bug.

9. Interview Reasoning

At CYC_O during the backoff — not at STB_O, which is where the eye goes first and which looks correct in both the working and broken cases.

The mechanism. The FIFO is drained by another master. For that master to run, the arbiter must grant it the bus. The arbiter cannot grant while the retrying master's CYC_O is asserted, because an open cycle is a live bus request — which is exactly the behaviour that provides atomicity in Chapter 4.9.

So a master that drops STB_O and holds CYC_O across its backoff has released nothing. It looks like it is waiting politely between attempts. It is holding the bus continuously, the drain never happens, every retry finds the FIFO still full, and the loop is infinite.

The one observation. CYC_O asserted continuously across several retries is conclusive. If it is genuinely negated between attempts and the FIFO still never drains, the problem is elsewhere — the other master is blocked on something else, or the arbiter is not granting it.

Why the bug is easy to write. Every other master in this module drives cyc_o and stb_o from the same signal, so separating them for a backoff requires noticing that they should differ in the opposite direction from the block-transfer case: there, CYC_O outlives STB_O deliberately; here, it must not.

The deeper point I would make. This is the same signal doing two opposite jobs. Held across transfers it provides atomicity; held across a retry it causes deadlock. CYC_O is a claim on the bus, and whether holding it is correct depends entirely on whether anything else needs the bus to make your condition clear. That question — does my unblocking depend on someone else getting this resource — is the one to ask before choosing between a wait state and a retry at all.

10. Understanding Check

11. Module 4 Complete

Every signal in the Wishbone Classic interface now has a chapter:

Master outputMaster input
4.3ADR_O — which word4.5DAT_I — received data
4.4DAT_O — driven data4.10ACK_I — completed
4.6WE_O — direction4.11ERR_I — cannot
4.7SEL_O — which bytes4.12RTY_I — not now (this chapter)
4.8STB_O — this transfer
4.9CYC_O — this tenure

With 4.1 and 4.2 supplying the clock and reset every one of them references.

What this module deliberately did not do is teach the handshake as a sequence. Each chapter covered what a signal means, what qualifies it, and what breaks when that qualification is dropped — and each stopped short of cycle-by-cycle timing.

How do these twelve signals combine, cycle by cycle, into a transfer?

Module 5 — The Wishbone Handshake answers it, taking CYC, STB and ACK as a timing contract rather than as three definitions. Module 6 and Module 7 then walk complete read and write cycles, wait states included; neither has shipped yet, which is why they appear in bold rather than as links.

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.