Skip to content
VLSI Mentor

Wishbone · Module 1

The Open Hardware Movement

Source availability and reusability are different properties. A published core tells you what it does; it does not tell you what it requires, and requirements are what integration runs on. What reusable open IP needs beyond the RTL — licensing, documentation, an interface contract, verification, maintenance — and where open hardware is honestly weaker than its advocates claim.

Chapter 1.3 ended on a conditional. Private interfaces multiply badly, but inside one team the multiplication is survivable — everyone can be told the rules. The argument for a published standard gets its force from a situation the chapter assumed without examining: blocks written by people who have never met.

That situation is not hypothetical. It is the environment Wishbone was created in and the reason it looks the way it does. This chapter is about what that environment actually demands of a hardware block, and it is deliberately not a chapter about ideology.

The question worth holding: a company publishes a UART core on the internet, complete, synthesisable, under a permissive licence. Has it supplied a reusable block?

1. What "Reusable" Actually Requires

Reuse has a specific meaning in hardware, and it is stricter than someone else can read it. A block is reusable when an engineer who did not write it can integrate it into a system its author never saw, and be confident it works, without reading its implementation.

That confidence has five independent sources, and publishing a repository supplies exactly one of them.

Five independent requirements must be met before a hardware block is genuinely reusable, and each is supplied by something different. Source availability is supplied by publishing the repository. A clear licence is supplied by a licence file and by a review of it. Documentation of behaviour is supplied by somebody spending time writing it. An interface contract is supplied by adopting a published interface standard. And evidence that the block actually works is supplied by a verification environment that somebody has to fund. Publishing a repository satisfies only the first of the five.Sourcewhat it doesLicencewhether you mayDocumentationwhat it requiresInterfacecontracthow it connectsEvidencewhether it workspublishing itfree, and the easyparta LICENSE fileplus a real reviewsomeone's weeksrarely fundeda publishedstandardthe reusable answera testbenchrarely funded12
Figure 1 — five things a reusable block needs, and what actually supplies each. Publishing source covers the first.

The asymmetry in the bottom row is the chapter. One column is free and automatic. Two are expensive and chronically unfunded. One — the interface contract — is the only column where a community-level decision can do the work that would otherwise fall on every author individually.

That is the specific engineering reason an open-hardware ecosystem benefits from a common bus more than a closed one does. It is not that open projects are poorer; it is that a standard is the only one of the five that scales by agreement rather than by labour.

2. What Source Does Not Tell You

The claim that source is insufficient deserves a concrete demonstration rather than an assertion. Here is a small block whose port list looks entirely portable.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────────
// A clock-domain-crossing FIFO, as it might appear in a published IP core.
//
// Read the port list and the parameters. Nothing in either tells you the
// constraint this design actually carries — you can only find it by reading
// the body, and only if you already know what to look for.
// ─────────────────────────────────────────────────────────────────────────
module cdc_fifo #(
  parameter int unsigned DW    = 32,
  parameter int unsigned DEPTH = 16          // looks like any depth is fine
) (
  input  logic          wr_clk,
  input  logic          wr_rst_n,
  input  logic          wr_en,
  input  logic [DW-1:0] wr_data,
  output logic          full,

  input  logic          rd_clk,
  input  logic          rd_rst_n,
  input  logic          rd_en,
  output logic [DW-1:0] rd_data,
  output logic          empty
);
  localparam int unsigned AW = $clog2(DEPTH);

  logic [AW:0] wr_bin_q, rd_bin_q;           // one extra bit for full/empty
  logic [AW:0] wr_gray_q, rd_gray_q;
  logic [AW:0] wr_gray_sync_q, wr_gray_sync_qq;
  logic [AW:0] rd_gray_sync_q, rd_gray_sync_qq;

  logic [DW-1:0] mem_q [DEPTH];

  // ── Write domain ──────────────────────────────────────────────────────
  always_ff @(posedge wr_clk or negedge wr_rst_n) begin
    if (!wr_rst_n) begin
      wr_bin_q  <= '0;
      wr_gray_q <= '0;
    end else if (wr_en && !full) begin
      wr_bin_q  <= wr_bin_q + 1'b1;
      // Binary-to-Gray. Exactly ONE bit changes per increment — which is
      // the entire reason this value may be sampled by another clock.
      wr_gray_q <= (wr_bin_q + 1'b1) ^ ((wr_bin_q + 1'b1) >> 1);
    end
  end

  always_ff @(posedge wr_clk) begin
    if (wr_en && !full) mem_q[wr_bin_q[AW-1:0]] <= wr_data;
  end

  // ── Read domain ───────────────────────────────────────────────────────
  always_ff @(posedge rd_clk or negedge rd_rst_n) begin
    if (!rd_rst_n) begin
      rd_bin_q  <= '0;
      rd_gray_q <= '0;
    end else if (rd_en && !empty) begin
      rd_bin_q  <= rd_bin_q + 1'b1;
      rd_gray_q <= (rd_bin_q + 1'b1) ^ ((rd_bin_q + 1'b1) >> 1);
    end
  end

  assign rd_data = mem_q[rd_bin_q[AW-1:0]];

  // ── Two-flop synchronisers, one per direction ─────────────────────────
  always_ff @(posedge rd_clk or negedge rd_rst_n) begin
    if (!rd_rst_n) begin
      wr_gray_sync_q  <= '0;
      wr_gray_sync_qq <= '0;
    end else begin
      wr_gray_sync_q  <= wr_gray_q;
      wr_gray_sync_qq <= wr_gray_sync_q;
    end
  end

  always_ff @(posedge wr_clk or negedge wr_rst_n) begin
    if (!wr_rst_n) begin
      rd_gray_sync_q  <= '0;
      rd_gray_sync_qq <= '0;
    end else begin
      rd_gray_sync_q  <= rd_gray_q;
      rd_gray_sync_qq <= rd_gray_sync_q;
    end
  end

  assign empty = (rd_gray_q == wr_gray_sync_qq);
  assign full  = (wr_gray_q == {~rd_gray_sync_qq[AW:AW-1],
                                 rd_gray_sync_qq[AW-2:0]});
endmodule

The requirement that is not in the interface

DEPTH must be a power of two. Nothing in the port list, the parameter list, or the parameter's default value says so.

The reason is the Gray code. A Gray-coded counter has the property that exactly one bit changes per increment, which is what makes the value safe to sample across a clock boundary: a sampling flop that catches the transition gets either the old value or the new one, never a corrupted mixture. That property holds when the counter wraps at a power of two. Set DEPTH to 12 and the counter still counts to 15 and wraps — so the FIFO silently holds 16 entries, indexes mem_q out of range, and the full comparison is computed against a pointer space that no longer matches the memory.

Now ask what a user of this block would experience. It elaborates. It synthesises. It passes a directed test that writes eight words and reads them back. It fails under sustained traffic, on hardware, intermittently, in a way that looks like a clock-domain problem — which it is, but not for the reason anyone will guess.

Three separate lessons sit in this one example.

Source availability did not help. Every line was visible the whole time. Finding the constraint requires knowing that Gray codes and non-power-of-two wrap interact, which is exactly the knowledge someone reaching for a pre-built CDC FIFO does not have.

The constraint is unverifiable as published. There is no assertion, no $fatal, no elaboration-time check. A single line would have converted a silent hazard into a compile error:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  // What the block SHOULD have carried, placed at module scope inside
  // cdc_fifo. Turns an inference into a contract the tool enforces.
  initial begin
    if (DEPTH != (1 << $clog2(DEPTH)))
      $fatal(1, "cdc_fifo: DEPTH must be a power of two (got %0d)", DEPTH);
  end

And the same applies to every other assumption in the file — that both resets are asserted long enough to be seen in both domains, that rd_data is combinational from the memory and therefore has no output register, that the block tolerates the two clocks being unrelated. All true, none stated.

3. What Inspectability Is Genuinely Worth

Having argued that source is insufficient, the honest counterweight: source is worth a great deal, for reasons that are specific and practical rather than philosophical.

You can find out what a block actually assumes. When documentation is thin — and it usually is — the RTL is the only remaining authority. The power-of-two constraint above is undocumented and discoverable. With an encrypted netlist it would be neither.

You can debug across the boundary. An integration failure in a closed block ends at a support ticket. With source you can put the internal state on a waveform, add an assertion inside the block, and determine in an afternoon whether the fault is yours or theirs. On a bring-up schedule that difference is often the whole schedule.

You can adapt. Change the reset polarity, widen a counter, remove a feature that costs area you do not have, add the byte enables the original author skipped. None of this is possible when the block is a binary, and all of it is routine when it is not.

You can verify the claims that matter to you. A vendor's verification report covers what the vendor cared about. With source you can write assertions for your own corner cases and bind them into the block.

You can keep it alive. IP is abandoned constantly — companies fold, maintainers move on, projects go quiet. An abandoned closed core is a dead end at the first tool upgrade. An abandoned open core is a maintenance burden, which is much worse than a supported one and much better than nothing.

The honest summary: inspectability does not make a block reusable, but it makes an unreusable block recoverable. Those are different goods and both are real.

4. Where Open Hardware Is Actually Weak

A chapter that only listed advantages would be advertising. These limitations are not incidental; they follow from how open IP gets made, and an engineer choosing a core needs to price them.

Quality varies enormously, and the distribution is not visible. A repository of open cores contains production-hardened designs that have been through silicon next to a student project uploaded once and never revisited. Both have a README. Neither is labelled.

Verification maturity varies more than design quality. Writing an RTL block that works is an evening's satisfying work; verifying it properly is weeks of unglamorous labour. So the thing most often missing is exactly the thing you most need in order to trust the block — and its absence is silent, because nobody publishes a "we did not verify this" notice.

Documentation is the second thing to go. Register maps drift from the RTL. Timing diagrams describe an earlier revision. The interface requirements from Section 2 are absent almost universally.

Maintenance stops without an announcement. There is no end-of-life notice and no deprecation window. A core simply stops receiving commits, and you discover this when a tool upgrade breaks it.

Licensing still matters, and hardware licensing is genuinely harder than software licensing. "Open" is not one thing: permissive, reciprocal, and hardware-specific licences differ in what they require of a fabricated device rather than of distributed source, and the question of what constitutes a derivative work is less settled for a netlist than for a program. This is a real review, not a formality — and it is a review a lawyer has to do, not an engineer.

Synthesising is not evidence. A design that synthesises, meets timing and passes a smoke test has demonstrated almost nothing about clock-domain safety, reset behaviour, parameter edge cases, error paths, or behaviour under sustained load. The CDC FIFO in Section 2 passes all of those gates.

5. Why an Ecosystem Needs the Interface Column Most

Return to Figure 1 with the limitations in hand, and the argument sharpens into something specific.

Of the five columns, four scale with labour. Documentation is written per block. Verification is built per block. Licences are reviewed per block, per user. Source is published per block. If a community wants a thousand reusable cores, it needs a thousand blocks' worth of each — and Section 4 is the evidence that this does not happen.

The interface column is different. It scales by agreement. One published interface standard, adopted once by each author, makes every adopting block connectable to every adopting system. The work per author is small and the benefit is combinatorial — it is the m × n to m + n collapse from Chapter 1.3, applied not to one project but to a whole community.

And the benefit compounds in a way that is easy to miss: the shared interface makes the other four columns cheaper too.

  • Documentation shrinks, because the interface section — usually the largest and most error-prone part of a core's datasheet — collapses to a citation and a list of which optional features are implemented.
  • Verification becomes partly shareable. A protocol monitor, a checker and an assertion set written once can be bound to every conforming block in the community, which is the only mechanism by which unfunded verification ever gets done.
  • Evaluation gets cheaper, per the order above: a conforming block can be exercised with stimulus you already have.
  • Debugging gets cheaper, because one waveform-reading skill applies to every block in the system.

That is the engineering case, stated without enthusiasm: in an ecosystem of independently authored blocks, a common interface is the single highest-leverage thing the community can standardise, because it is the only requirement that one decision can satisfy for everybody.

It is not a claim that a standard makes open IP good. Section 4 stands. It is a claim about where a fixed amount of community effort buys the most.

6. Common Misconceptions

"Open source means someone has reviewed it."

The wrong model: many eyes, therefore shallow bugs.

What it costs: a core adopted on the strength of its visibility, carrying a defect nobody has hit because nobody has used it the way you are about to. The visibility argument requires users, and most published cores have very few.

The corrected model: review is evidence you have to look for, not a property conferred by publication. The commit history and the testbench are where that evidence lives.

"If I have the source, I do not need documentation."

The wrong model: source is a superset of documentation.

What it costs: every integrator independently re-derives the same requirements, each at a different level of rigour, and the ones who are less expert or more rushed miss the one that matters. Section 2's FIFO is the shape of it.

The corrected model: source describes behaviour; documentation states requirements. They carry different information, and the second is what integration consumes.

"Publishing our core makes it reusable."

The wrong model: reusability is a licensing decision.

What it costs: a repository with users who all fail in the same way and file the same questions, because the block's requirements were never written down.

The corrected model: reusability is four more pieces of work after the licence — documented requirements, a standard interface, evidence it works, and someone to answer questions. Publishing is the start of that, not a substitute for it.

"An interface standard is a governance question, not an engineering one."

The wrong model: which bus to adopt is politics; the engineering is inside the blocks.

What it costs: the decision gets made by default rather than deliberately, usually by whichever core was integrated first, and the community ends up with several partial standards — which is the worst outcome, because it has the coordination cost of a standard and none of the reuse.

The corrected model: the interface choice determines whether independently authored blocks compose at all. It is the most consequential engineering decision an ecosystem makes, and Section 5 is why.

7. Interview Reasoning

Publication answered one question — may I use it — and left the ones integration runs on.

What a strong answer asks for, roughly in cost order:

  • The licence, properly reviewed. Hardware licensing is not software licensing; what a reciprocal licence requires of a fabricated device is a different question from what it requires of distributed source, and it needs a lawyer rather than an engineer.
  • The testbench, before the RTL. Its size relative to the design, and whether it has assertions and coverage, is the strongest available signal about whether anyone has stressed the block.
  • The requirements, not the behaviour. Parameter legal ranges, reset duration and polarity, clock relationships, what happens if a request is withdrawn. These are the things absent from almost every published core.
  • The commit history, read for bug fixes with tests attached — evidence somebody used it in anger.
  • The interface, because a standard one can be exercised with a monitor and stimulus you already own, and a private one cannot.

The framing that shows judgement: synthesising, meeting timing and passing a smoke test is compatible with a broken clock-domain crossing, an unhandled parameter value and no error path at all. Those gates are necessary and prove almost nothing.

8. Understanding Check

9. What's Next

This chapter separated two properties that are routinely conflated: source availability, which gives you a block's behaviour, and reusability, which requires its requirements to be stated in a form something can check. It argued that of the five things a reusable block needs, the interface contract is the one a community can supply by agreement rather than by labour — and was explicit about the four it cannot.

That argument has been made at the level of an ecosystem. It has not yet been made at the level of a board on an engineer's desk.

Take a single FPGA, a soft CPU and half a dozen peripherals — a system one person can build in a week. Where, concretely, does integrating independently written blocks actually become hard, and what does the difficulty look like in decode logic, in timing closure and in the debug session at the end?

Chapter 1.5 — FPGA Design Challenges makes it concrete. It builds the fabric, shows the decode and the read multiplexer that every additional target makes worse, asserts the property that keeps two targets from answering one access, and follows the consequences into resource usage, timing closure and debug visibility. The full path is on the Wishbone curriculum index.

Continue learning

Related tutorials

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.