Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions docs/cpuif/wishbone.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
Wishbone Bus
============

Implements the register block using an
`Wishbone B4 <https://en.wikipedia.org/wiki/Wishbone_(computer_bus)>`
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``.
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ Links
cpuif/avalon
cpuif/obi
cpuif/passthrough
cpuif/wishbone
cpuif/internal_protocol
cpuif/customizing

Expand Down
46 changes: 46 additions & 0 deletions hdl-src/wishbone_intf.sv
Original file line number Diff line number Diff line change
@@ -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
4 changes: 3 additions & 1 deletion src/peakrdl_regblock/__peakrdl__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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,
}
Expand Down
35 changes: 35 additions & 0 deletions src/peakrdl_regblock/cpuif/wishbone/__init__.py
Original file line number Diff line number Diff line change
@@ -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
30 changes: 30 additions & 0 deletions src/peakrdl_regblock/cpuif/wishbone/wishbone_tmpl.sv
Original file line number Diff line number Diff line change
@@ -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
3 changes: 3 additions & 0 deletions tests/lib/cpuifs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
Expand All @@ -17,4 +18,6 @@
FlatAvalon(),
OBI(),
FlatOBI(),
Wishbone(),
FlatWishbone()
]
18 changes: 18 additions & 0 deletions tests/lib/cpuifs/wishbone/__init__.py
Original file line number Diff line number Diff line change
@@ -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 = []
36 changes: 36 additions & 0 deletions tests/lib/cpuifs/wishbone/tb_inst.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{% 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_cyc;
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_cyc = wb.cyc;
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;
{% endif %}
138 changes: 138 additions & 0 deletions tests/lib/cpuifs/wishbone/wishbone_intf_driver.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
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_cyc;
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.cyc = wb_cyc;
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_cyc;
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_cyc <= '0;
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
Loading