Add APB4 cpuif
This commit is contained in:
31
docs/cpuif/apb4.rst
Normal file
31
docs/cpuif/apb4.rst
Normal file
@@ -0,0 +1,31 @@
|
||||
AMBA 4 APB
|
||||
==========
|
||||
|
||||
Implements the register block using an
|
||||
`AMBA 4 APB <https://developer.arm.com/documentation/ihi0024/d/?lang=en>`_
|
||||
CPU interface.
|
||||
|
||||
The APB4 CPU interface comes in two i/o port flavors:
|
||||
|
||||
SystemVerilog Interface
|
||||
Class: :class:`peakrdl_regblock.cpuif.apb4.APB4_Cpuif`
|
||||
|
||||
Interface Definition: :download:`apb4_intf.sv <../../tests/lib/cpuifs/apb4/apb4_intf.sv>`
|
||||
|
||||
Flattened inputs/outputs
|
||||
Flattens the interface into discrete input and output ports.
|
||||
|
||||
Class: :class:`peakrdl_regblock.cpuif.apb4.APB4_Cpuif_flattened`
|
||||
|
||||
|
||||
.. warning::
|
||||
Some IP vendors will incorrectly implement the address signalling
|
||||
assuming word-addresses. (that each increment of ``PADDR`` is the next word)
|
||||
|
||||
For this exporter, values on the interface's ``PADDR`` input are interpreted
|
||||
as byte-addresses. (a 32-bit APB bus increments ``PADDR`` in steps of 4)
|
||||
Although APB protocol does not allow for unaligned transfers, this is in
|
||||
accordance to the official AMBA bus specification.
|
||||
|
||||
Be sure to double-check the interpretation of your interconnect IP. A simple
|
||||
bit-shift operation can be used to correct this if necessary.
|
||||
@@ -95,6 +95,7 @@ Links
|
||||
|
||||
cpuif/introduction
|
||||
cpuif/apb3
|
||||
cpuif/apb4
|
||||
cpuif/axi4lite
|
||||
cpuif/passthrough
|
||||
cpuif/internal_protocol
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from .exporter import RegblockExporter
|
||||
from .cpuif import apb3, axi4lite, passthrough
|
||||
from .cpuif import apb3, apb4, axi4lite, passthrough
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import argparse
|
||||
@@ -12,6 +12,8 @@ if TYPE_CHECKING:
|
||||
CPUIF_DICT = {
|
||||
"apb3": apb3.APB3_Cpuif,
|
||||
"apb3-flat": apb3.APB3_Cpuif_flattened,
|
||||
"apb4": apb4.APB4_Cpuif,
|
||||
"apb4-flat": apb4.APB4_Cpuif_flattened,
|
||||
"axi4-lite": axi4lite.AXI4Lite_Cpuif,
|
||||
"axi4-lite-flat": axi4lite.AXI4Lite_Cpuif_flattened,
|
||||
"passthrough": passthrough.PassthroughCpuif
|
||||
|
||||
36
src/peakrdl_regblock/cpuif/apb4/__init__.py
Normal file
36
src/peakrdl_regblock/cpuif/apb4/__init__.py
Normal file
@@ -0,0 +1,36 @@
|
||||
from ..base import CpuifBase
|
||||
|
||||
class APB4_Cpuif(CpuifBase):
|
||||
template_path = "apb4_tmpl.sv"
|
||||
|
||||
@property
|
||||
def port_declaration(self) -> str:
|
||||
return "apb4_intf.slave s_apb"
|
||||
|
||||
def signal(self, name:str) -> str:
|
||||
return "s_apb." + name.upper()
|
||||
|
||||
@property
|
||||
def data_width_bytes(self) -> int:
|
||||
return self.data_width // 8
|
||||
|
||||
|
||||
class APB4_Cpuif_flattened(APB4_Cpuif):
|
||||
@property
|
||||
def port_declaration(self) -> str:
|
||||
lines = [
|
||||
"input wire " + self.signal("psel"),
|
||||
"input wire " + self.signal("penable"),
|
||||
"input wire " + self.signal("pwrite"),
|
||||
"input wire [2:0] " + self.signal("pprot"),
|
||||
f"input wire [{self.addr_width-1}:0] " + self.signal("paddr"),
|
||||
f"input wire [{self.data_width-1}:0] " + self.signal("pwdata"),
|
||||
f"input wire [{self.data_width_bytes-1}:0] " + self.signal("pstrb"),
|
||||
"output logic " + self.signal("pready"),
|
||||
f"output logic [{self.data_width-1}:0] " + self.signal("prdata"),
|
||||
"output logic " + self.signal("pslverr"),
|
||||
]
|
||||
return ",\n".join(lines)
|
||||
|
||||
def signal(self, name:str) -> str:
|
||||
return "s_apb_" + name
|
||||
39
src/peakrdl_regblock/cpuif/apb4/apb4_tmpl.sv
Normal file
39
src/peakrdl_regblock/cpuif/apb4/apb4_tmpl.sv
Normal file
@@ -0,0 +1,39 @@
|
||||
// Request
|
||||
logic is_active;
|
||||
always_ff {{get_always_ff_event(cpuif.reset)}} begin
|
||||
if({{get_resetsignal(cpuif.reset)}}) begin
|
||||
is_active <= '0;
|
||||
cpuif_req <= '0;
|
||||
cpuif_req_is_wr <= '0;
|
||||
cpuif_addr <= '0;
|
||||
cpuif_wr_data <= '0;
|
||||
cpuif_wr_biten <= '0;
|
||||
end else begin
|
||||
if(~is_active) begin
|
||||
if({{cpuif.signal("psel")}}) begin
|
||||
is_active <= '1;
|
||||
cpuif_req <= '1;
|
||||
cpuif_req_is_wr <= {{cpuif.signal("pwrite")}};
|
||||
{%- if cpuif.data_width == 8 %}
|
||||
cpuif_addr <= {{cpuif.signal("paddr")}}[{{cpuif.addr_width-1}}:0];
|
||||
{%- else %}
|
||||
cpuif_addr <= { {{-cpuif.signal("paddr")}}[{{cpuif.addr_width-1}}:{{clog2(cpuif.data_width//8)}}], {{clog2(cpuif.data_width//8)}}'b0};
|
||||
{%- endif %}
|
||||
cpuif_wr_data <= {{cpuif.signal("pwdata")}};
|
||||
for(int i=0; i<{{cpuif.data_width_bytes}}; i++) begin
|
||||
cpuif_wr_biten[i*8 +: 8] <= {8{ {{-cpuif.signal("pstrb")}}[i]}};
|
||||
end
|
||||
end
|
||||
end else begin
|
||||
cpuif_req <= '0;
|
||||
if(cpuif_rd_ack || cpuif_wr_ack) begin
|
||||
is_active <= '0;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// Response
|
||||
assign {{cpuif.signal("pready")}} = cpuif_rd_ack | cpuif_wr_ack;
|
||||
assign {{cpuif.signal("prdata")}} = cpuif_rd_data;
|
||||
assign {{cpuif.signal("pslverr")}} = cpuif_rd_err | cpuif_wr_err;
|
||||
@@ -11,7 +11,7 @@ from .readback import Readback
|
||||
from .identifier_filter import kw_filter as kwf
|
||||
|
||||
from .cpuif import CpuifBase
|
||||
from .cpuif.apb3 import APB3_Cpuif
|
||||
from .cpuif.apb4 import APB4_Cpuif
|
||||
from .hwif import Hwif
|
||||
from .utils import get_always_ff_event
|
||||
from .scan_design import DesignScanner
|
||||
@@ -57,7 +57,7 @@ class RegblockExporter:
|
||||
Output includes two files: a module definition and package definition.
|
||||
cpuif_cls: :class:`peakrdl_regblock.cpuif.CpuifBase`
|
||||
Specify the class type that implements the CPU interface of your choice.
|
||||
Defaults to AMBA APB3.
|
||||
Defaults to AMBA APB4.
|
||||
module_name: str
|
||||
Override the SystemVerilog module name. By default, the module name
|
||||
is the top-level node's name.
|
||||
@@ -100,7 +100,7 @@ class RegblockExporter:
|
||||
self.top_node = node
|
||||
|
||||
|
||||
cpuif_cls = kwargs.pop("cpuif_cls", None) or APB3_Cpuif # type: Type[CpuifBase]
|
||||
cpuif_cls = kwargs.pop("cpuif_cls", None) or APB4_Cpuif # type: Type[CpuifBase]
|
||||
module_name = kwargs.pop("module_name", None) or kwf(self.top_node.inst_name) # type: str
|
||||
package_name = kwargs.pop("package_name", None) or (module_name + "_pkg") # type: str
|
||||
reuse_hwif_typedefs = kwargs.pop("reuse_hwif_typedefs", True) # type: bool
|
||||
|
||||
@@ -11,7 +11,7 @@ from systemrdl import RDLCompiler
|
||||
|
||||
from peakrdl_regblock import RegblockExporter
|
||||
from .cpuifs.base import CpuifTestMode
|
||||
from .cpuifs.apb3 import APB3
|
||||
from .cpuifs.apb4 import APB4
|
||||
|
||||
|
||||
class BaseTestCase(unittest.TestCase):
|
||||
@@ -28,7 +28,7 @@ class BaseTestCase(unittest.TestCase):
|
||||
rdl_elab_params = {}
|
||||
|
||||
#: Define what CPUIF to use for this testcase
|
||||
cpuif = APB3() # type: CpuifTestMode
|
||||
cpuif = APB4() # type: CpuifTestMode
|
||||
|
||||
# Other exporter args:
|
||||
retime_read_fanin = False
|
||||
|
||||
18
tests/lib/cpuifs/apb4/__init__.py
Normal file
18
tests/lib/cpuifs/apb4/__init__.py
Normal file
@@ -0,0 +1,18 @@
|
||||
from ..base import CpuifTestMode
|
||||
|
||||
from peakrdl_regblock.cpuif.apb4 import APB4_Cpuif, APB4_Cpuif_flattened
|
||||
|
||||
class APB4(CpuifTestMode):
|
||||
cpuif_cls = APB4_Cpuif
|
||||
rtl_files = [
|
||||
"apb4_intf.sv",
|
||||
]
|
||||
tb_files = [
|
||||
"apb4_intf.sv",
|
||||
"apb4_intf_driver.sv",
|
||||
]
|
||||
tb_template = "tb_inst.sv"
|
||||
|
||||
class FlatAPB4(APB4):
|
||||
cpuif_cls = APB4_Cpuif_flattened
|
||||
rtl_files = []
|
||||
46
tests/lib/cpuifs/apb4/apb4_intf.sv
Normal file
46
tests/lib/cpuifs/apb4/apb4_intf.sv
Normal file
@@ -0,0 +1,46 @@
|
||||
interface apb4_intf #(
|
||||
parameter DATA_WIDTH = 32,
|
||||
parameter ADDR_WIDTH = 32
|
||||
);
|
||||
// Command
|
||||
logic PSEL;
|
||||
logic PENABLE;
|
||||
logic PWRITE;
|
||||
logic [2:0] PPROT;
|
||||
logic [ADDR_WIDTH-1:0] PADDR;
|
||||
logic [DATA_WIDTH-1:0] PWDATA;
|
||||
logic [DATA_WIDTH/8-1:0] PSTRB;
|
||||
|
||||
// Response
|
||||
logic [DATA_WIDTH-1:0] PRDATA;
|
||||
logic PREADY;
|
||||
logic PSLVERR;
|
||||
|
||||
modport master (
|
||||
output PSEL,
|
||||
output PENABLE,
|
||||
output PWRITE,
|
||||
output PPROT,
|
||||
output PADDR,
|
||||
output PWDATA,
|
||||
output PSTRB,
|
||||
|
||||
input PRDATA,
|
||||
input PREADY,
|
||||
input PSLVERR
|
||||
);
|
||||
|
||||
modport slave (
|
||||
input PSEL,
|
||||
input PENABLE,
|
||||
input PWRITE,
|
||||
input PPROT,
|
||||
input PADDR,
|
||||
input PWDATA,
|
||||
input PSTRB,
|
||||
|
||||
output PRDATA,
|
||||
output PREADY,
|
||||
output PSLVERR
|
||||
);
|
||||
endinterface
|
||||
128
tests/lib/cpuifs/apb4/apb4_intf_driver.sv
Normal file
128
tests/lib/cpuifs/apb4/apb4_intf_driver.sv
Normal file
@@ -0,0 +1,128 @@
|
||||
interface apb4_intf_driver #(
|
||||
parameter DATA_WIDTH = 32,
|
||||
parameter ADDR_WIDTH = 32
|
||||
)(
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
apb4_intf.master m_apb
|
||||
);
|
||||
|
||||
timeunit 1ps;
|
||||
timeprecision 1ps;
|
||||
|
||||
logic PSEL;
|
||||
logic PENABLE;
|
||||
logic PWRITE;
|
||||
logic [2:0] PPROT;
|
||||
logic [ADDR_WIDTH-1:0] PADDR;
|
||||
logic [DATA_WIDTH-1:0] PWDATA;
|
||||
logic [DATA_WIDTH/8-1:0] PSTRB;
|
||||
logic [DATA_WIDTH-1:0] PRDATA;
|
||||
logic PREADY;
|
||||
logic PSLVERR;
|
||||
|
||||
assign m_apb.PSEL = PSEL;
|
||||
assign m_apb.PENABLE = PENABLE;
|
||||
assign m_apb.PWRITE = PWRITE;
|
||||
assign m_apb.PPROT = PPROT;
|
||||
assign m_apb.PADDR = PADDR;
|
||||
assign m_apb.PWDATA = PWDATA;
|
||||
assign m_apb.PSTRB = PSTRB;
|
||||
assign PRDATA = m_apb.PRDATA;
|
||||
assign PREADY = m_apb.PREADY;
|
||||
assign PSLVERR = m_apb.PSLVERR;
|
||||
|
||||
default clocking cb @(posedge clk);
|
||||
default input #1step output #1;
|
||||
output PSEL;
|
||||
output PENABLE;
|
||||
output PWRITE;
|
||||
output PPROT;
|
||||
output PADDR;
|
||||
output PWDATA;
|
||||
output PSTRB;
|
||||
input PRDATA;
|
||||
input PREADY;
|
||||
input PSLVERR;
|
||||
endclocking
|
||||
|
||||
task automatic reset();
|
||||
cb.PSEL <= '0;
|
||||
cb.PENABLE <= '0;
|
||||
cb.PWRITE <= '0;
|
||||
cb.PPROT <= '0;
|
||||
cb.PADDR <= '0;
|
||||
cb.PWDATA <= '0;
|
||||
cb.PSTRB <= '0;
|
||||
endtask
|
||||
|
||||
semaphore txn_mutex = new(1);
|
||||
|
||||
task automatic write(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] data);
|
||||
txn_mutex.get();
|
||||
##0;
|
||||
|
||||
// Initiate transfer
|
||||
cb.PSEL <= '1;
|
||||
cb.PENABLE <= '0;
|
||||
cb.PWRITE <= '1;
|
||||
cb.PPROT <= '0;
|
||||
cb.PADDR <= addr;
|
||||
cb.PWDATA <= data;
|
||||
cb.PSTRB <= '1;
|
||||
@(cb);
|
||||
|
||||
// active phase
|
||||
cb.PENABLE <= '1;
|
||||
@(cb);
|
||||
|
||||
// Wait for response
|
||||
while(cb.PREADY !== 1'b1) @(cb);
|
||||
reset();
|
||||
txn_mutex.put();
|
||||
endtask
|
||||
|
||||
task automatic read(logic [ADDR_WIDTH-1:0] addr, output logic [DATA_WIDTH-1:0] data);
|
||||
txn_mutex.get();
|
||||
##0;
|
||||
|
||||
// Initiate transfer
|
||||
cb.PSEL <= '1;
|
||||
cb.PENABLE <= '0;
|
||||
cb.PWRITE <= '0;
|
||||
cb.PPROT <= '0;
|
||||
cb.PADDR <= addr;
|
||||
cb.PWDATA <= '0;
|
||||
cb.PSTRB <= '0;
|
||||
@(cb);
|
||||
|
||||
// active phase
|
||||
cb.PENABLE <= '1;
|
||||
@(cb);
|
||||
|
||||
// Wait for response
|
||||
while(cb.PREADY !== 1'b1) @(cb);
|
||||
assert(!$isunknown(cb.PRDATA)) else $error("Read from 0x%0x returned X's on PRDATA", addr);
|
||||
assert(!$isunknown(cb.PSLVERR)) else $error("Read from 0x%0x returned X's on PSLVERR", addr);
|
||||
data = cb.PRDATA;
|
||||
reset();
|
||||
txn_mutex.put();
|
||||
endtask
|
||||
|
||||
task automatic assert_read(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] expected_data, logic [DATA_WIDTH-1:0] mask = '1);
|
||||
logic [DATA_WIDTH-1:0] data;
|
||||
read(addr, data);
|
||||
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.PREADY)) else $error("Saw X on PREADY!");
|
||||
end
|
||||
|
||||
endinterface
|
||||
36
tests/lib/cpuifs/apb4/tb_inst.sv
Normal file
36
tests/lib/cpuifs/apb4/tb_inst.sv
Normal file
@@ -0,0 +1,36 @@
|
||||
{% sv_line_anchor %}
|
||||
apb4_intf #(
|
||||
.DATA_WIDTH({{exporter.cpuif.data_width}}),
|
||||
.ADDR_WIDTH({{exporter.cpuif.addr_width}})
|
||||
) s_apb();
|
||||
apb4_intf_driver #(
|
||||
.DATA_WIDTH({{exporter.cpuif.data_width}}),
|
||||
.ADDR_WIDTH({{exporter.cpuif.addr_width}})
|
||||
) cpuif (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
.m_apb(s_apb)
|
||||
);
|
||||
{% if type(cpuif).__name__.startswith("Flat") %}
|
||||
{% sv_line_anchor %}
|
||||
wire s_apb_psel;
|
||||
wire s_apb_penable;
|
||||
wire s_apb_pwrite;
|
||||
wire [2:0] s_apb_pprot;
|
||||
wire [{{exporter.cpuif.addr_width - 1}}:0] s_apb_paddr;
|
||||
wire [{{exporter.cpuif.data_width - 1}}:0] s_apb_pwdata;
|
||||
wire [{{exporter.cpuif.data_width_bytes - 1}}:0] s_apb_pstrb;
|
||||
wire s_apb_pready;
|
||||
wire [{{exporter.cpuif.data_width - 1}}:0] s_apb_prdata;
|
||||
wire s_apb_pslverr;
|
||||
assign s_apb_psel = s_apb.PSEL;
|
||||
assign s_apb_penable = s_apb.PENABLE;
|
||||
assign s_apb_pwrite = s_apb.PWRITE;
|
||||
assign s_apb_pprot = s_apb.PPROT;
|
||||
assign s_apb_paddr = s_apb.PADDR;
|
||||
assign s_apb_pwdata = s_apb.PWDATA;
|
||||
assign s_apb_pstrb = s_apb.PSTRB;
|
||||
assign s_apb.PREADY = s_apb_pready;
|
||||
assign s_apb.PRDATA = s_apb_prdata;
|
||||
assign s_apb.PSLVERR = s_apb_pslverr;
|
||||
{% endif %}
|
||||
@@ -1,6 +1,7 @@
|
||||
from itertools import product
|
||||
|
||||
from .cpuifs.apb3 import APB3, FlatAPB3
|
||||
from .cpuifs.apb4 import APB4, FlatAPB4
|
||||
from .cpuifs.axi4lite import AXI4Lite, FlatAXI4Lite
|
||||
from .cpuifs.passthrough import Passthrough
|
||||
|
||||
@@ -8,6 +9,8 @@ from .cpuifs.passthrough import Passthrough
|
||||
all_cpuif = [
|
||||
APB3(),
|
||||
FlatAPB3(),
|
||||
APB4(),
|
||||
FlatAPB4(),
|
||||
AXI4Lite(),
|
||||
FlatAXI4Lite(),
|
||||
Passthrough(),
|
||||
|
||||
Reference in New Issue
Block a user