Skip to content
VLSI Mentor

Wishbone · Module 29

FPGA SoCs

A two-bit address decode, an arbiter whose safety rests on a comment, and one wire called CYC at one end and STB at the other — reconstructed from the servant source at a recorded commit.

Every system in Modules 1–28 was built to teach something. This one was built to run on an FPGA, and that changes what it looks like.

Read the source, reconstruct the machine. Module 28 argued you should be able to derive an answer rather than recall one. This module points that at code you did not write.

1. What We Inspected

itemevidence
systemSERV, and the servant SoC that wraps it
repositorygithub.com/olofk/serv
commitf200eb2ed7b69ac1c6b8eddd47654522aeee5ce8
commit date2026-08-25
inspected2026-09-22
filesservile/servile_mux.v, servile/servile_arbiter.v, servile/servile.v, servant/servant.v, rtl/serv_state.v
licenceISC at the repository root; the servile files carry SPDX-License-Identifier: Apache-2.0
statuscanonical upstream, not a fork, not archived; an open-source FPGA SoC

This is an open-source FPGA SoC. Every claim below is scoped to that commit. It is not a statement about SERV's whole history, about FPGA SoCs in general, or about anything having shipped in silicon.

2. Architecture, Reconstructed From Source

itemevidence
Wishbone profileClassic-style; no STALL, no pipelining, no CTI/BTE
mastersone CPU presenting two request ports, instruction and data
slavesRAM, plus a timer and a GPIO behind an "external" port
address width32
data width32
interconnecttwo combinational modules, servile_mux and servile_arbiter
arbitrationinstruction bus wins; relies on a stated CPU property
decodetwo address bits, ADR[31:30]
terminationACK only
ERR usageNOT PRESENT in the inspected files
RTY usageNOT PRESENT in the inspected files
timeout policyNOT PRESENT in the inspected files
byte selectspresent, 4 bits, carried to the memory port
memory mapbelow 0x40000000 is memory; at or above is "external"
verificationa testbench directory and a RISC-V compliance flow exist; NOT inspected for this chapter
source revisionf200eb2e

Two rows deserve attention before anything else. There is no ERR_O, no RTY_O and no timeout anywhere in the bus path. The entire termination vocabulary of this system is one signal.

3. The Decode Is Two Bits

Here is the whole address decision, from servile_mux.v:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   wire		       ext = (i_wb_cpu_adr[31:30] != 2'b00);

   assign o_wb_cpu_rdt = ext ? i_wb_ext_rdt : i_wb_mem_rdt;
   assign o_wb_cpu_ack = i_wb_ext_ack | i_wb_mem_ack | sim_ack;

ORIGINAL SOURCE EXCERPT — olofk/serv, servile/servile_mux.v, commit f200eb2e.

Module 12 built base-and-mask comparators, overlap detection and a default responder. This design has none of them, and it is not an oversight: with one memory region and one aggregate "everything else" port, two address bits are a complete decode. There is nothing to overlap and nothing to leave unmapped.

The acknowledgement is an OR across the two targets with no provenance latch, which is safe here for the same reason — only one of the two strobes is ever asserted, because they are derived from the same ext term.

SOURCE FACT: the decode uses ADR[31:30]. DERIVED INFERENCE: therefore every address from 0x00000000 to 0x3FFFFFFF reaches the memory port, whatever the memory's actual size — the aliasing is absorbed by the RAM's own narrower index, which servant.v takes as wb_mem_adr[$clog2(memsize)-1:2].

4. The Arbiter, And The Comment That Makes It Correct

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   assign o_wb_cpu_dbus_ack = i_wb_mem_ack & !i_wb_cpu_ibus_stb;
   assign o_wb_cpu_ibus_ack = i_wb_mem_ack &  i_wb_cpu_ibus_stb;

   assign o_wb_mem_adr = i_wb_cpu_ibus_stb ? i_wb_cpu_ibus_adr : i_wb_cpu_dbus_adr;

ORIGINAL SOURCE EXCERPT — olofk/serv, servile/servile_arbiter.v, commit f200eb2e.

Look at what steers the response: i_wb_cpu_ibus_stb, the same live signal that steers the request. If that signal changes while a transfer is in flight, the acknowledgement is delivered to the other requester.

Chapter 27.4 built exactly this structure on purpose and measured 99 misdelivered terminations with zero protocol violations. Chapter 28.4 named it as the finding in a design review.

So why is it right here? Because of the first lines of the file:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
/*
 * servile_arbiter.v : I/D arbiter for the servile convenience wrapper.
 *  Relies on the fact that not ibus and dbus are active at the same time.
 */

ORIGINAL SOURCE EXCERPT — same file.

The assumption is written down next to the code that depends on it. That is the difference between engineering and luck. The selector cannot move under a live transfer because SERV never asserts both request lines, and serv_state.v is where that property is produced.

Lift this arbiter out and put it in front of two independent masters and it becomes the Chapter 27.4 defect on the first clock they overlap.

So our reconstruction does not take the comment on trust. It counts the clocks on which the assumption would have been violated:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  // ── the assumption, made measurable ─────────────────────────────────
  // The inspected file says it relies on ibus and dbus never being active
  // together. This counter is how Lab A checks whether the stimulus has
  // honoured that, instead of assuming it.
  logic [15:0] nboth_q, nmem_q, next_q;

VLSI MENTOR RECONSTRUCTION — wb_sv_soc.sv. Preserves the two-bit split, the I-wins merge and the live-selector response steering. Omits SERV's bit-serial datapath, the simulation hooks in servile_mux, and the peripherals. No byte-equivalence to SERV source is claimed or intended.

The result:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  memory phases completed        3
  external phases completed      1
  clocks with I and D both active 0

5. One Wire, Two Names

This is the finding that repays reading the source rather than the block diagram.

serv_state.v produces the core's request qualifiers:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   assign o_dbus_cyc = !o_cnt_en & init_done & i_dbus_en & !i_mem_misalign;

ORIGINAL SOURCE EXCERPT — olofk/serv, rtl/serv_state.v, commit f200eb2e.

The core drives o_ibus_cyc and o_dbus_cyc. There is no separate strobe. servile.v then connects them to wires named for the other signal:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
      .o_ibus_cyc  (wb_ibus_stb),
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
      .o_dbus_cyc  (wb_dbus_stb),

and servant.v completes the round trip, connecting a port named cyc to a wire named stb:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
      .i_wb_cpu_cyc (wb_ext_stb),

ORIGINAL SOURCE EXCERPTS — servile/servile.v and servant/servant.v, commit f200eb2e.

The same physical wire is CYC at one end and STB at the other, because in this system they are the same signal. B3 permits precisely that, with a condition:

PERMISSION 3.40 — If a MASTER doesn't generate wait states, then STB_O and CYC_O MAY be assigned the same signal.

Chapter 28.1 taught that permission and its condition. Here is a shipped FPGA SoC using it, and the naming inconsistency is the visible trace of the decision. A reader who insists that CYC and STB must be distinct signals will conclude this design is broken. It is not; it has taken an option the specification offers.

DO NOT GENERALIZE: this works because this master never negates its strobe inside a cycle. A master that throttles — one performing block cycles with gaps between transfers — cannot collapse the two, and PERMISSION 3.40 does not permit it to.

6. One Transaction, Traced

The reconstructed servant bus path. The SERV core presents two request ports, an instruction bus and a data bus, each carrying a single request qualifier. The data bus first meets the mux, which splits on address bits thirty-one and thirty: everything below the quarter-way point goes to memory, everything else to an external port serving the timer and the GPIO. The memory-bound data traffic then meets the arbiter, which merges it with the instruction bus onto one memory port, with the instruction bus winning when both are present. The memory's single acknowledgement is steered back to whichever of the two buses was selected, by the same live signal that selected the request.SERV coreone request wire perportinstruction buso_ibus_cycdata buso_dbus_cycservile_muxADR[31:30] != 0 ?servile_arbiterI wins; assumes neverbothRAMACK onlytimer + GPIOACK onlyreturnORed ACK, steered byibus_stb12

Three data accesses through our reconstruction of that path:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== LAB A - SERVANT TRANSACTION PATH ===

  step  address     decode        target    cycle  data returned
     0  0x00000010  adr[31:30]=0  memory       1  0xc0de0004
     1  0x40000000  adr[31:30]=1  ext          5  0x713e7000
     2  0x00000020  adr[31:30]=0  memory       7  0xc0de0008

OUR MEASUREMENT, from a VLSI Mentor reconstruction — not from running SERV.

Step by step, and every decision names the boundary it happens at:

stepboundarywhat decidesresult
1CPU data portthe load's address0x00000010 presented
2servile_muxADR[31:30] != 2'b00false → memory side
3servile_arbiteris the instruction bus asking?no → data address forwarded
4memory portRAM index adr[7:2]word 4 selected
5memoryits own acknowledgementACK
6servile_arbiterack & !ibus_stbsteered to the data bus
7CPUdata captured0xc0de0004

Step 2 is the only address decision in the entire system.

7. Local Policies

Everything in this list is this SoC's choice, not Wishbone's:

  • the split at 0x40000000, and that the upper three-quarters of the space is one aggregate port;
  • that an unmapped address does not exist as a concept — there is no hole to fall into and therefore no default responder;
  • that termination is ACK only, so a peripheral has no way to refuse an access;
  • that the instruction bus outranks the data bus;
  • that the arbiter may steer by a live selector, licensed by a CPU property;
  • that CYC and STB are one wire.

The trade-off, stated

Every omission above buys something and costs something, and the design is coherent because the same answer fits all of them:

decisionwhat it costswhat it buys
ACK only, no ERR/RTYa peripheral cannot refuse or deferno error path in the CPU, no policy to define
two-bit decodeno room to grow the map without changing ita decoder that is one comparison
live-selector response steeringcorrectness depends on a CPU propertyno ownership state anywhere
CYC and STB as one wirethe master can never throttle inside a cycleone fewer wire on every port

The pattern is the same each time: this design consistently declines optional capability in exchange for not having the state that implements it. That is a defensible position for a core whose selling point is its size, and an indefensible one for a system that needs any of the four.

8. What Not To Generalize

Do not conclude that Wishbone systems do not need ERR. This one does not, because nothing in it can fail an access in a way software could act on. A system with an address hole, a read-only region or a peripheral that can be busy needs a vocabulary this one has deliberately not paid for.

Do not conclude that two-bit decodes are sufficient. They are sufficient for two targets.

Do not copy the arbiter. Its correctness is a property of the master in front of it. Copying it without the comment — or worse, with the comment and without checking that the new master obeys it — reproduces a defect Module 27 spent a chapter measuring.

Do not read "small" as "simplistic". Every omission in this design is an omission somebody could name. That is what distinguishes a minimal design from an incomplete one.

9. What To Carry Forward

  • Find the address decision. In this system it is one line, and everything else about routing follows from it.
  • A live selector steering a response is a question, not a verdict. Ask what prevents it from moving; if the answer is written down, that is a good design.
  • Naming inconsistencies in source are evidence. cyc connected to stb is PERMISSION 3.40 leaving a fingerprint.
  • Absent signals are decisions. No ERR, no RTY, no timeout — three deliberate omissions, each with a consequence.
  • One repository proves one repository. Everything here is scoped to commit f200eb2e.

Chapter 29.2 looks at a system nobody wrote by hand.

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.