Skip to content
VLSI Mentor

Wishbone · Module 19

Open Hardware Systems

picorv32, SERV and NEORV32 each attach to Wishbone differently and each documented its own side because nothing else does — plus a withdrawn request, and two ways of not making progress that look identical from the core.

Chapter 19.2 looked at a generator that has already decided everything. This chapter looks at the cores it would be deciding about, and finds that no two of them present the same interface.

If three open RISC-V cores each attach to Wishbone differently, what exactly is the thing they are each doing?

1. Three Cores, Read Side by Side

corewhat its own README says about its bus
picorv32"The native memory interface of PicoRV32 is a simple valid-ready interface that can run one memory transfer at a time." A separate module, picorv32_wb, is "The version of the CPU with Wishbone Master interface".
SERV"The bus interface is kind of Wishbone, but with most signals removed. There's an important difference though. Don't send acks on the instruction or data buses unless serv explicitly asks for something by raising its cyc signal."
NEORV32"32-bit external bus interface - Wishbone-compatible (XBUS)", with a separate "bridge for AXI4-compatible interfaces" in rtl/system_integration.
Three attachment strategies, drawn side by side. In the top row picorv32 drives a native valid-ready memory interface into a separate optional wrapper module named picorv32_wb, which produces the Wishbone master port. In the middle row SERV drives a reduced Wishbone port directly, carrying a documented extra constraint that a slave must not acknowledge unless SERV has raised its cyc signal. In the bottom row NEORV32 drives a Wishbone-compatible XBUS port directly, and ships a separate AXI4 bridge alongside it for a different ecosystem. All three end at the same Wishbone system on the right.picorv32native valid/readypicorv32_wbseparate wrapperSERVreduced WishboneNEORV32XBUS portAXI4 bridgea differentecosystemWishbonesystemthe same busmem_validno ack unless cyc12

Three cores, three shapes, one bus. The differences are not disagreements about Wishbone — every one of those right-hand edges is a conformant master port. The differences are about what sits to the left of it, which is the part no specification reaches.

Read those three rows as one finding. One core has a native interface and a separate Wishbone wrapper. One has a reduced Wishbone with an explicitly documented extra constraint. One has a Wishbone-compatible port and ships a bridge to a different protocol entirely.

None of them is wrong, and none of them is what the other two did. That is what "unspecified" looks like in practice.

2. SERV's Sentence Is the Whole Module in One Line

"Don't send acks on the instruction or data buses unless serv explicitly asks for something by raising its cyc signal."

That is a requirement on the slave side that appears in no specification. B3's RULE 3.30 says a slave may not respond when CYC_I is negated — so SERV's warning is, in one reading, just B3 being restated. But SERV's authors put it in their README in bold-adjacent terms, which tells you they had been bitten.

It also tells you something sharper. A core author does not write that sentence unless the integrator's mistake is plausible. The failure being warned about is not a protocol violation by the core; it is an integration habit — a testbench or a bridge that acknowledges eagerly.

3. What a "Native Interface" Actually Is

picorv32's README gives the cleanest statement of the contract on the core's side:

"The core initiates a memory transfer by asserting mem_valid. The valid signal stays high until the peer asserts mem_ready. All core outputs are stable over the mem_valid period."

That last sentence is SEAM 1's first obligation, and it is the one an adapter is most likely to break. The traffic generator built for this module enforces exactly it:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────────
// wb_core_model — a deterministic two-port traffic generator.
//
// THIS IS NOT A RISC-V CORE. It executes no instructions, has no register
// file, no decoder and no pipeline. It exists to produce two realistic
// master traffic streams and to consume what comes back. Nothing about
// timing, IPC or frequency is claimed for it, and its native interface
// does not match any real core's.
//
// What it DOES model is the part Module 19 is about: the shape of the
// contract a core presents to a bus adapter.
//
// ── THE NATIVE CONTRACT ─────────────────────────────────────────────────
// Two independent valid/ready request ports, one for instruction fetch and
// one for load/store. The shape is taken from a real core's documentation -
// picorv32's README states its native interface is "a simple valid-ready
// interface that can run one memory transfer at a time", and that "the
// valid signal stays high until the peer asserts mem_ready. All core
// outputs are stable over the mem_valid period."
//
// That second sentence is the whole of SEAM 1's first obligation, and this
// model enforces it: once a request is asserted it does not change and does
// not go away until it is accepted.
//

4. The Failure That Breaks It

A request that has been accepted at the seam must not be withdrawn because the core stopped being ready. Readiness is about the answer, not about the request.

REQ_DROP is that mistake, made in one line of the adapter:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  logic drop_now;
  assign drop_now = REQ_DROP && busy_q && core_stall_i && !term;

Read it as an argument somebody might make. "The core has stalled; it isn't going to take the answer; there's no point holding the bus." It is a plausible sentence and it is wrong, because the slave is already mid-transfer and the cycle is not the core's to withdraw.

SIM C puts the core into a stall while a three-wait-state RAM is still answering:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  === SIM C - the request belongs to the bus ===
    the RAM inserts three wait states. The core is stalled by
    something that is not the bus while the transfer is still
    outstanding. picorv32's README states the contract this
    tests: "All core outputs are stable over the mem_valid
    period."

    measure                        correct   REQ_DROP
      core requests accepted           3          3
      bus transfers terminated         3          3
      answers returned to the core     3          3
      cycles that vanished unanswered  0          3

    last value the core received   correct 0xaaaa0002
                                   broken  0xaaaa0002
    -> the broken adapter withdrew a cycle the slave was
       still answering. Nothing on the bus was violated;
       a master simply stopped being there.

Both systems delivered the same value to the core, and both counted three requests, three transfers and three answers. The only column that separates them is cycles that vanished unanswered: zero against three.

5. Two Ways of Not Making Progress

Here is the measurement that most directly repays reading a core's own documentation.

From inside the core, these two situations are indistinguishable: the pipeline is held by something that is not the bus, and a slave is withholding ACK. Nothing is progressing either way. From the seam, they are different counters, and conflating them produces performance analysis that blames the wrong subsystem.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  === SIM D - two ways of not making progress ===
    first a load with no stall at all, then eight clocks of
    stall with nothing outstanding.

    after one load into a three-wait-state RAM
      clocks the core was stalled by its pipeline   0
      clocks a phase was presented and unanswered   4
    after eight further clocks of stall, bus idle
      clocks the core was stalled by its pipeline   8
      clocks a phase was presented and unanswered   4

    -> the second number did not move. A slave withholding
       ACK and a pipeline holding itself are different
       counters, and from the core they look identical:
       nothing is progressing either way.

The second number did not move. Eight clocks of pipeline stall with the bus idle added zero wait clocks, because there was no phase presented to wait on.

who is refusingwhat fixes it
stallthe corea faster core, a deeper pipeline, a different instruction mix
waitthe slavea faster slave, a different address map, a cache

These two counters are never added together anywhere in this module. Summing them produces a number that describes nothing — and the integration audit in Chapter 19.5 §8 prints them on separate lines with a note saying why.

6. Why Every Core Does This Differently

Because the choice is genuinely free, and the constraints that decide it are local.

a core withtends towardbecause
a tiny area budget (SERV is bit-serial)a reduced bus, extra documented constraintsevery removed signal is real area
a native pipeline shape (picorv32)a native interface plus a separate wrapperthe wrapper can be omitted when unused
multiple target ecosystems (NEORV32)a compatible port plus bridgesthe bridge is where the ecosystem-specific cost lives

All three strategies are responses to the same gap. Wishbone does not describe a core; RISC-V does not describe a bus; so each core author writes down their own side and ships it.

That is why the READMEs are load-bearing documents — and why RULE 2.00 ("Each WISHBONE compatible IP core MUST include a WISHBONE DATASHEET as part of the IP core documentation") is not bureaucratic filler. It is B3 acknowledging that the specification cannot close the gap and requiring you to close it in writing.

7. What Carries Forward

observationwhere it is measured
a request, once accepted, belongs to the bus§4 above, and 19.4 §3
stall and wait are different counters§5 above, and 19.5 §8
a core is more than one master19.1 §5
what a core does with ERR_I is a documentation obligation19.4 §6

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.