From 62011ba425c2c8772c820c968774200d389e8f48 Mon Sep 17 00:00:00 2001 From: Alberto Perro Date: Mon, 16 Mar 2026 13:38:53 +0100 Subject: [PATCH 1/6] add wishbone template --- hdl-src/wishbone_intf.sv | 46 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 hdl-src/wishbone_intf.sv diff --git a/hdl-src/wishbone_intf.sv b/hdl-src/wishbone_intf.sv new file mode 100644 index 00000000..098657f7 --- /dev/null +++ b/hdl-src/wishbone_intf.sv @@ -0,0 +1,46 @@ +interface wishbone_intf #( + parameter DATA_WIDTH = 32, + parameter ADDR_WIDTH = 32 +); + // Command + logic cyc; + logic stb; + logic we; + logic stall; + logic [ADDR_WIDTH-1:0] adr; + logic [DATA_WIDTH-1:0] odat; + logic [DATA_WIDTH/8-1:0] sel; + + // Response + logic ack; + logic err; + logic [DATA_WIDTH-1:0] idat; + + modport host ( + output cyc, + output stb, + output we, + input stall, + output adr, + output odat, + output sel, + + input ack, + input err, + input idat + ); + + modport agent ( + input cyc, + input stb, + input we, + output stall, + input adr, + input odat, + input sel, + + output ack, + output err, + output idat + ); +endinterface From 77229eb5c034fa84abf0e148d420f9d1deb0d809 Mon Sep 17 00:00:00 2001 From: Alberto Perro Date: Mon, 16 Mar 2026 13:39:49 +0100 Subject: [PATCH 2/6] add wishbone and wishbone flat to cpuif --- src/peakrdl_regblock/__peakrdl__.py | 4 ++- .../cpuif/wishbone/__init__.py | 35 +++++++++++++++++++ .../cpuif/wishbone/wishbone_tmpl.sv | 30 ++++++++++++++++ 3 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 src/peakrdl_regblock/cpuif/wishbone/__init__.py create mode 100644 src/peakrdl_regblock/cpuif/wishbone/wishbone_tmpl.sv diff --git a/src/peakrdl_regblock/__peakrdl__.py b/src/peakrdl_regblock/__peakrdl__.py index 6ecef93e..48902f94 100644 --- a/src/peakrdl_regblock/__peakrdl__.py +++ b/src/peakrdl_regblock/__peakrdl__.py @@ -7,7 +7,7 @@ from peakrdl.plugins.entry_points import get_entry_points from .exporter import RegblockExporter -from .cpuif import CpuifBase, apb3, apb4, axi4lite, passthrough, avalon, obi +from .cpuif import CpuifBase, apb3, apb4, axi4lite, passthrough, avalon, obi, wishbone from .udps import ALL_UDPS if TYPE_CHECKING: @@ -40,6 +40,8 @@ def get_cpuifs(self) -> Dict[str, Type[CpuifBase]]: "axi4-lite-flat": axi4lite.AXI4Lite_Cpuif_flattened, "avalon-mm": avalon.Avalon_Cpuif, "avalon-mm-flat": avalon.Avalon_Cpuif_flattened, + "wishbone": wishbone.Wishbone_Cpuif, + "wishbone-flat": wishbone.Wishbone_Cpuif_flattened, "obi": obi.OBI_Cpuif, "obi-flat": obi.OBI_Cpuif_flattened, } diff --git a/src/peakrdl_regblock/cpuif/wishbone/__init__.py b/src/peakrdl_regblock/cpuif/wishbone/__init__.py new file mode 100644 index 00000000..6b0d9390 --- /dev/null +++ b/src/peakrdl_regblock/cpuif/wishbone/__init__.py @@ -0,0 +1,35 @@ +from ..base import CpuifBase +from ...utils import clog2 + +class Wishbone_Cpuif(CpuifBase): + template_path = "wishbone_tmpl.sv" + is_interface = True + + @property + def port_declaration(self) -> str: + return "wishbone_intf.agent wb" + + def signal(self, name:str) -> str: + return "wb." + name + +class Wishbone_Cpuif_flattened(Wishbone_Cpuif): + is_interface = False + + @property + def port_declaration(self) -> str: + lines = [ + "input wire " + self.signal("cyc"), + "input wire " + self.signal("stb"), + "input wire " + self.signal("we"), + "output logic " + self.signal("stall"), + f"input wire [{self.addr_width-1}:0] " + self.signal("adr"), + f"input wire [{self.data_width-1}:0] " + self.signal("odat"), + f"input wire [{self.data_width_bytes-1}:0] " + self.signal("sel"), + "output logic " + self.signal("ack"), + "output logic " + self.signal("err"), + f"output logic [{self.data_width-1}:0] " + self.signal("idat") + ] + return ",\n".join(lines) + + def signal(self, name:str) -> str: + return "wb_" + name diff --git a/src/peakrdl_regblock/cpuif/wishbone/wishbone_tmpl.sv b/src/peakrdl_regblock/cpuif/wishbone/wishbone_tmpl.sv new file mode 100644 index 00000000..571193ed --- /dev/null +++ b/src/peakrdl_regblock/cpuif/wishbone/wishbone_tmpl.sv @@ -0,0 +1,30 @@ +{%- if cpuif.is_interface -%} +`ifndef SYNTHESIS + initial begin + assert_bad_addr_width: assert($bits({{cpuif.signal("adr")}}) >= {{cpuif.addr_width}}) + else $error("Interface address width of %0d is too small. Shall be at least %0d bits", $bits({{cpuif.signal("adr")}}), {{cpuif.addr_width}}); + assert_bad_data_width: assert($bits({{cpuif.signal("odat")}}) == {{ds.package_name}}::{{ds.module_name.upper()}}_DATA_WIDTH) + else $error("Interface data width of %0d is incorrect. Shall be %0d bits", $bits({{cpuif.signal("odat")}}), {{ds.package_name}}::{{ds.module_name.upper()}}_DATA_WIDTH); + end +`endif + +{% endif -%} + +// Request +always_comb begin + cpuif_req = {{cpuif.signal("stb")}}; + cpuif_req_is_wr = {{cpuif.signal("we")}}; + cpuif_addr = {{cpuif.signal("adr")}}; + cpuif_wr_data = {{cpuif.signal("odat")}}; + for(int i=0; i<{{cpuif.data_width_bytes}}; i++) begin + cpuif_wr_biten[i*8 +: 8] = {8{ {{-cpuif.signal("sel")}}[i]}}; + end + {{cpuif.signal("stall")}} = {{cpuif.signal("stb")}} & ((cpuif_req_stall_rd) | (cpuif_req_stall_wr & {{cpuif.signal("we")}})); +end + +// Response +always_comb begin + {{cpuif.signal("ack")}} = cpuif_rd_ack | cpuif_wr_ack; + {{cpuif.signal("idat")}} = cpuif_rd_data; + {{cpuif.signal("err")}} = cpuif_rd_err | cpuif_wr_err; +end From 4e3f688a2474ce8ba3476451721047409e91e73d Mon Sep 17 00:00:00 2001 From: Alberto Perro Date: Mon, 16 Mar 2026 13:41:01 +0100 Subject: [PATCH 3/6] add tests for wishbone cpuif --- tests/lib/cpuifs/wishbone/__init__.py | 18 +++ tests/lib/cpuifs/wishbone/tb_inst.sv | 34 +++++ .../cpuifs/wishbone/wishbone_intf_driver.sv | 134 ++++++++++++++++++ 3 files changed, 186 insertions(+) create mode 100644 tests/lib/cpuifs/wishbone/__init__.py create mode 100644 tests/lib/cpuifs/wishbone/tb_inst.sv create mode 100644 tests/lib/cpuifs/wishbone/wishbone_intf_driver.sv diff --git a/tests/lib/cpuifs/wishbone/__init__.py b/tests/lib/cpuifs/wishbone/__init__.py new file mode 100644 index 00000000..a06fb890 --- /dev/null +++ b/tests/lib/cpuifs/wishbone/__init__.py @@ -0,0 +1,18 @@ +from ..base import CpuifTestMode + +from peakrdl_regblock.cpuif.wishbone import Wishbone_Cpuif, Wishbone_Cpuif_flattened + +class Wishbone(CpuifTestMode): + cpuif_cls = Wishbone_Cpuif + rtl_files = [ + "../../../../hdl-src/wishbone_intf.sv", + ] + tb_files = [ + "../../../../hdl-src/wishbone_intf.sv", + "wishbone_intf_driver.sv", + ] + tb_template = "tb_inst.sv" + +class FlatWishbone(Wishbone): + cpuif_cls = Wishbone_Cpuif_flattened + rtl_files = [] diff --git a/tests/lib/cpuifs/wishbone/tb_inst.sv b/tests/lib/cpuifs/wishbone/tb_inst.sv new file mode 100644 index 00000000..93497de3 --- /dev/null +++ b/tests/lib/cpuifs/wishbone/tb_inst.sv @@ -0,0 +1,34 @@ +{% sv_line_anchor %} +wishbone_intf #( + .DATA_WIDTH({{exporter.cpuif.data_width}}), + .ADDR_WIDTH({{exporter.cpuif.addr_width}}) +) wb(); +wishbone_intf_driver #( + .DATA_WIDTH({{exporter.cpuif.data_width}}), + .ADDR_WIDTH({{exporter.cpuif.addr_width}}) +) cpuif ( + .clk(clk), + .rst(rst), + .wb(wb) +); +{% if type(cpuif).__name__.startswith("Flat") %} +{% sv_line_anchor %} +wire wb_stb; +wire wb_we; +wire wb_stall; +wire [{{exporter.cpuif.addr_width - 1}}:0] wb_adr; +wire [{{exporter.cpuif.data_width - 1}}:0] wb_odat; +wire [{{exporter.cpuif.data_width_bytes - 1}}:0] wb_sel; +wire wb_ack; +wire wb_err; +wire [{{exporter.cpuif.data_width - 1}}:0] wb_idat; +assign wb_stb = wb.stb; +assign wb_we = wb.we; +assign wb.waitrequest = wb_stall; +assign wb_adr = wb.adr; +assign wb_odat = wb.odat; +assign wb_sel = wb.sel; +assign wb.ack = wb_ack; +assign wb.err = wb_err; +assign wb.idat = wb_idat; +{% endif %} diff --git a/tests/lib/cpuifs/wishbone/wishbone_intf_driver.sv b/tests/lib/cpuifs/wishbone/wishbone_intf_driver.sv new file mode 100644 index 00000000..d76b6eca --- /dev/null +++ b/tests/lib/cpuifs/wishbone/wishbone_intf_driver.sv @@ -0,0 +1,134 @@ +interface wishbone_intf_driver #( + parameter DATA_WIDTH = 32, + parameter ADDR_WIDTH = 32 + )( + input wire clk, + input wire rst, + wishbone_intf.host wb + ); + timeunit 1ps; + timeprecision 1ps; + + + logic wb_stb; + logic wb_we; + logic wb_stall; + logic [ADDR_WIDTH-1:0] wb_adr; + logic [DATA_WIDTH-1:0] wb_odat; + logic [DATA_WIDTH/8-1:0] wb_sel; + logic wb_ack; + logic wb_err; + logic [DATA_WIDTH-1:0] wb_idat; + + assign wb.stb = wb_stb; + assign wb.we = wb_we; + assign wb_stall = wb.stall; + assign wb.adr = wb_adr; + assign wb.odat = wb_odat; + assign wb.sel = wb_sel; + assign wb_ack = wb.ack; + assign wb_err = wb.err; + assign wb_idat = wb.idat; + + default clocking cb @(posedge clk); + default input #1step output #1; + output wb_stb; + output wb_we; + input wb_stall; + output wb_adr; + output wb_odat; + output wb_sel; + input wb_ack; + input wb_err; + input wb_idat; + endclocking + + task automatic reset(); + cb.wb_stb <= '0; + cb.wb_we <= '0; + cb.wb_adr <= '0; + cb.wb_odat <= '0; + cb.wb_sel <= '0; + endtask + + semaphore req_mutex = new(1); + semaphore resp_mutex = new(1); + + task automatic write(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] data, logic [DATA_WIDTH/8-1:0] strb = {DATA_WIDTH{1'b1}}, logic expects_err = 1'b0); + fork + begin + req_mutex.get(); + ##0; + // Initiate transfer + cb.wb_stb <= '1; + cb.wb_we <= '1; + cb.wb_adr <= addr; + cb.wb_odat <= data; + cb.wb_sel <= strb; + @(cb); + + // Wait for transfer to be accepted + while(cb.wb_stall == 1'b1) @(cb); + reset(); + req_mutex.put(); + end + + begin + resp_mutex.get(); + @cb; + // Wait for response + while(cb.wb_ack !== 1'b1) @(cb); + assert(!$isunknown(cb.wb_err)) else $error("Write to 0x%0x returned X's on wb_err", addr); + resp_mutex.put(); + end + join + endtask + + task automatic read(logic [ADDR_WIDTH-1:0] addr, output logic [DATA_WIDTH-1:0] data, input logic expects_err = 1'b0); + fork + begin + req_mutex.get(); + ##0; + // Initiate transfer + cb.wb_stb <= '1; + cb.wb_we <= '0; + cb.wb_adr <= addr; + @(cb); + + // Wait for transfer to be accepted + while(cb.wb_stall == 1'b1) @(cb); + reset(); + req_mutex.put(); + end + + begin + resp_mutex.get(); + @cb; + // Wait for response + while(cb.wb_ack !== 1'b1) @(cb); + assert(!$isunknown(cb.wb_err)) else $error("Read to 0x%0x returned X's on wb_err", addr); + data = cb.wb_idat; + resp_mutex.put(); + end + join + endtask + + task automatic assert_read(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] expected_data, logic [DATA_WIDTH-1:0] mask = {DATA_WIDTH{1'b1}}, input logic expects_err = 1'b0); + logic [DATA_WIDTH-1:0] data; + read(addr, data, expects_err); + data &= mask; + assert(data == expected_data) else $error("Read from 0x%x returned 0x%x. Expected 0x%x", addr, data, expected_data); + endtask + + initial begin + reset(); + end + + initial forever begin + @cb; + if(!rst) assert(!$isunknown(cb.wb_stall)) else $error("Saw X on wb_stall!"); + if(!rst) assert(!$isunknown(cb.wb_ack)) else $error("Saw X on wb_ack!"); + if(!rst) assert(!$isunknown(cb.wb_err)) else $error("Saw X on wb_err!"); + end + +endinterface From 2d1b3e488f5fa142e31e50f55af8042244f2dcee Mon Sep 17 00:00:00 2001 From: Alberto Perro Date: Mon, 16 Mar 2026 13:46:49 +0100 Subject: [PATCH 4/6] add docs for wishbone --- docs/cpuif/wishbone.rst | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 docs/cpuif/wishbone.rst diff --git a/docs/cpuif/wishbone.rst b/docs/cpuif/wishbone.rst new file mode 100644 index 00000000..6d8976d2 --- /dev/null +++ b/docs/cpuif/wishbone.rst @@ -0,0 +1,29 @@ +Wishbone Bus +============ + +Implements the register block using an +`Wishbone B4 ` +CPU interface. + +The wishbone interface comes in two i/o port flavors: + +SystemVerilog Interface + * Command line: ``--cpuif wishbone`` + * Interface Definition: :download:`wishbone_intf.sv <../../hdl-src/wishbone_intf.sv>` + * Class: :class:`peakrdl_regblock.cpuif.wishbone.Wishbone_Cpuif` + +Flattened inputs/outputs + Flattens the interface into discrete input and output ports. + + * Command line: ``--cpuif wishbone-flat`` + * Class: :class:`peakrdl_regblock.cpuif.wishbone.Wishbone_Cpuif_flattened` + + +Implementation Details +---------------------- +This implementation of the Wishbone protocol has the following features: +- Classic Wishbone Operaions (SINGLE_READ and SINGLE_WRITE) +- Stall and Error optional output signals + +Note that the ``cyc`` signal is not connected and it is a placeholder, since it is redundant in wishbone classic operations. +Commands are captured based on ``stb``. From 177c8db9ed9b642e53b0680f9517f0c9f9f75a33 Mon Sep 17 00:00:00 2001 From: Alberto Perro Date: Mon, 16 Mar 2026 14:36:05 +0100 Subject: [PATCH 5/6] fix wishbone tbs --- tests/lib/cpuifs/__init__.py | 3 +++ tests/lib/cpuifs/wishbone/tb_inst.sv | 4 +++- tests/lib/cpuifs/wishbone/wishbone_intf_driver.sv | 4 ++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/tests/lib/cpuifs/__init__.py b/tests/lib/cpuifs/__init__.py index 82ca94b2..3940d06b 100644 --- a/tests/lib/cpuifs/__init__.py +++ b/tests/lib/cpuifs/__init__.py @@ -3,6 +3,7 @@ from .apb4 import APB4, FlatAPB4 from .axi4lite import AXI4Lite, FlatAXI4Lite from .avalon import Avalon, FlatAvalon +from .wishbone import Wishbone, FlatWishbone from .obi import OBI, FlatOBI ALL_CPUIF = [ @@ -17,4 +18,6 @@ FlatAvalon(), OBI(), FlatOBI(), + Wishbone(), + FlatWishbone() ] diff --git a/tests/lib/cpuifs/wishbone/tb_inst.sv b/tests/lib/cpuifs/wishbone/tb_inst.sv index 93497de3..f2edf6fe 100644 --- a/tests/lib/cpuifs/wishbone/tb_inst.sv +++ b/tests/lib/cpuifs/wishbone/tb_inst.sv @@ -13,6 +13,7 @@ wishbone_intf_driver #( ); {% if type(cpuif).__name__.startswith("Flat") %} {% sv_line_anchor %} +wire wb_cyc; wire wb_stb; wire wb_we; wire wb_stall; @@ -22,9 +23,10 @@ wire [{{exporter.cpuif.data_width_bytes - 1}}:0] wb_sel; wire wb_ack; wire wb_err; wire [{{exporter.cpuif.data_width - 1}}:0] wb_idat; +assign wb_cyc = wb.cyc; assign wb_stb = wb.stb; assign wb_we = wb.we; -assign wb.waitrequest = wb_stall; +assign wb.stall = wb_stall; assign wb_adr = wb.adr; assign wb_odat = wb.odat; assign wb_sel = wb.sel; diff --git a/tests/lib/cpuifs/wishbone/wishbone_intf_driver.sv b/tests/lib/cpuifs/wishbone/wishbone_intf_driver.sv index d76b6eca..efa580ce 100644 --- a/tests/lib/cpuifs/wishbone/wishbone_intf_driver.sv +++ b/tests/lib/cpuifs/wishbone/wishbone_intf_driver.sv @@ -10,6 +10,7 @@ interface wishbone_intf_driver #( timeprecision 1ps; + logic wb_cyc; logic wb_stb; logic wb_we; logic wb_stall; @@ -20,6 +21,7 @@ interface wishbone_intf_driver #( logic wb_err; logic [DATA_WIDTH-1:0] wb_idat; + assign wb.cyc = wb_cyc; assign wb.stb = wb_stb; assign wb.we = wb_we; assign wb_stall = wb.stall; @@ -32,6 +34,7 @@ interface wishbone_intf_driver #( default clocking cb @(posedge clk); default input #1step output #1; + output wb_cyc; output wb_stb; output wb_we; input wb_stall; @@ -44,6 +47,7 @@ interface wishbone_intf_driver #( endclocking task automatic reset(); + cb.wb_cyc <= '0; cb.wb_stb <= '0; cb.wb_we <= '0; cb.wb_adr <= '0; From c0243b7a1da70a0dfce4ebbd7243a11cee23adfe Mon Sep 17 00:00:00 2001 From: Alberto Perro Date: Tue, 17 Mar 2026 12:16:09 +0100 Subject: [PATCH 6/6] add wishdone to toctree --- docs/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.rst b/docs/index.rst index 2dd445b8..6471502f 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -64,6 +64,7 @@ Links cpuif/avalon cpuif/obi cpuif/passthrough + cpuif/wishbone cpuif/internal_protocol cpuif/customizing