Add APB4 cpuif

This commit is contained in:
Alex Mykyta
2022-09-13 22:39:36 -07:00
parent 6e4246a2cc
commit 3b4289f2c6
12 changed files with 346 additions and 6 deletions

31
docs/cpuif/apb4.rst Normal file
View 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.

View File

@@ -95,6 +95,7 @@ Links
cpuif/introduction cpuif/introduction
cpuif/apb3 cpuif/apb3
cpuif/apb4
cpuif/axi4lite cpuif/axi4lite
cpuif/passthrough cpuif/passthrough
cpuif/internal_protocol cpuif/internal_protocol

View File

@@ -1,7 +1,7 @@
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from .exporter import RegblockExporter from .exporter import RegblockExporter
from .cpuif import apb3, axi4lite, passthrough from .cpuif import apb3, apb4, axi4lite, passthrough
if TYPE_CHECKING: if TYPE_CHECKING:
import argparse import argparse
@@ -12,6 +12,8 @@ if TYPE_CHECKING:
CPUIF_DICT = { CPUIF_DICT = {
"apb3": apb3.APB3_Cpuif, "apb3": apb3.APB3_Cpuif,
"apb3-flat": apb3.APB3_Cpuif_flattened, "apb3-flat": apb3.APB3_Cpuif_flattened,
"apb4": apb4.APB4_Cpuif,
"apb4-flat": apb4.APB4_Cpuif_flattened,
"axi4-lite": axi4lite.AXI4Lite_Cpuif, "axi4-lite": axi4lite.AXI4Lite_Cpuif,
"axi4-lite-flat": axi4lite.AXI4Lite_Cpuif_flattened, "axi4-lite-flat": axi4lite.AXI4Lite_Cpuif_flattened,
"passthrough": passthrough.PassthroughCpuif "passthrough": passthrough.PassthroughCpuif

View 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

View 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;

View File

@@ -11,7 +11,7 @@ from .readback import Readback
from .identifier_filter import kw_filter as kwf from .identifier_filter import kw_filter as kwf
from .cpuif import CpuifBase from .cpuif import CpuifBase
from .cpuif.apb3 import APB3_Cpuif from .cpuif.apb4 import APB4_Cpuif
from .hwif import Hwif from .hwif import Hwif
from .utils import get_always_ff_event from .utils import get_always_ff_event
from .scan_design import DesignScanner from .scan_design import DesignScanner
@@ -57,7 +57,7 @@ class RegblockExporter:
Output includes two files: a module definition and package definition. Output includes two files: a module definition and package definition.
cpuif_cls: :class:`peakrdl_regblock.cpuif.CpuifBase` cpuif_cls: :class:`peakrdl_regblock.cpuif.CpuifBase`
Specify the class type that implements the CPU interface of your choice. Specify the class type that implements the CPU interface of your choice.
Defaults to AMBA APB3. Defaults to AMBA APB4.
module_name: str module_name: str
Override the SystemVerilog module name. By default, the module name Override the SystemVerilog module name. By default, the module name
is the top-level node's name. is the top-level node's name.
@@ -100,7 +100,7 @@ class RegblockExporter:
self.top_node = node 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 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 package_name = kwargs.pop("package_name", None) or (module_name + "_pkg") # type: str
reuse_hwif_typedefs = kwargs.pop("reuse_hwif_typedefs", True) # type: bool reuse_hwif_typedefs = kwargs.pop("reuse_hwif_typedefs", True) # type: bool

View File

@@ -11,7 +11,7 @@ from systemrdl import RDLCompiler
from peakrdl_regblock import RegblockExporter from peakrdl_regblock import RegblockExporter
from .cpuifs.base import CpuifTestMode from .cpuifs.base import CpuifTestMode
from .cpuifs.apb3 import APB3 from .cpuifs.apb4 import APB4
class BaseTestCase(unittest.TestCase): class BaseTestCase(unittest.TestCase):
@@ -28,7 +28,7 @@ class BaseTestCase(unittest.TestCase):
rdl_elab_params = {} rdl_elab_params = {}
#: Define what CPUIF to use for this testcase #: Define what CPUIF to use for this testcase
cpuif = APB3() # type: CpuifTestMode cpuif = APB4() # type: CpuifTestMode
# Other exporter args: # Other exporter args:
retime_read_fanin = False retime_read_fanin = False

View 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 = []

View 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

View 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

View 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 %}

View File

@@ -1,6 +1,7 @@
from itertools import product from itertools import product
from .cpuifs.apb3 import APB3, FlatAPB3 from .cpuifs.apb3 import APB3, FlatAPB3
from .cpuifs.apb4 import APB4, FlatAPB4
from .cpuifs.axi4lite import AXI4Lite, FlatAXI4Lite from .cpuifs.axi4lite import AXI4Lite, FlatAXI4Lite
from .cpuifs.passthrough import Passthrough from .cpuifs.passthrough import Passthrough
@@ -8,6 +9,8 @@ from .cpuifs.passthrough import Passthrough
all_cpuif = [ all_cpuif = [
APB3(), APB3(),
FlatAPB3(), FlatAPB3(),
APB4(),
FlatAPB4(),
AXI4Lite(), AXI4Lite(),
FlatAXI4Lite(), FlatAXI4Lite(),
Passthrough(), Passthrough(),