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/apb3
cpuif/apb4
cpuif/axi4lite
cpuif/passthrough
cpuif/internal_protocol

View File

@@ -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

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 .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

View File

@@ -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

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 .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(),