Files
PeakRDL-regblock/src/peakrdl_regblock/__peakrdl__.py
sbaillou d69af23be5 Error response for unmapped address or forbidden read/write access (#168)
* feat: add ability to enable error output on the cpuif, when decoding errors occur (generate_cpuif_err in API).

* fix: move signal to new place (after automatic vers)

* feat: add info about new api (generate_cpuif_err)

* fix: repair readback with latency

* Adding generate_cpuif_err argument to peakrdl-regblock to generate cpuif error response, when the address is decoded incorrectly

* add sw rd or/and wr attribure error response related and add error respone for external mem

* add sw rd or/and wr error response test

* add sw rd or/and wr error response for external register test and fix generation of rtl logic for external register

* add sw rd or/and wr error response for external mem test

* add sw rd or/and wr error response for apb3 imterfaces driver

* add error response test for APB4, AXI4Lite and Avalon interfaces

* rename --generate_cpuif_err to --generate-cpuif-err

* style: minor typo fixes and test clean-up

* refactor: move expected error check inside write/read functions

* feat: add error response check to OBI testbench interface

* feat: split generate-cpuif-err option into err-if-bad-addr and err-if-bad-rw options

* feat: add err_if_bad_addr/rw to cfg_schema

* feat: extend cpuif_err_rsp test to cover all combinations of bad_addr/bad_rw

* style: lint fixes

* fix: removed redundant if node.external condition to help coverage

* Fix dangling hwif_in signals in testcase

---------

Co-authored-by: Denis Trifonov <d.trifonov@yadro.com>
Co-authored-by: Dominik Tanous <tanous@kandou.com>
Co-authored-by: Sebastien Baillou <baillou@kandou.com>
Co-authored-by: Alex Mykyta <amykyta3@users.noreply.github.com>
2025-10-25 18:22:15 -07:00

226 lines
8.4 KiB
Python

from typing import TYPE_CHECKING, Dict, Type
import functools
import sys
from peakrdl.plugins.exporter import ExporterSubcommandPlugin
from peakrdl.config import schema
from peakrdl.plugins.entry_points import get_entry_points
from .exporter import RegblockExporter
from .cpuif import CpuifBase, apb3, apb4, axi4lite, passthrough, avalon, obi
from .udps import ALL_UDPS
if TYPE_CHECKING:
import argparse
from systemrdl.node import AddrmapNode
class Exporter(ExporterSubcommandPlugin):
short_desc = "Generate a SystemVerilog control/status register (CSR) block"
udp_definitions = ALL_UDPS
cfg_schema = {
"cpuifs": {"*": schema.PythonObjectImport()},
"default_reset": schema.Choice(["rst", "rst_n", "arst", "arst_n"]),
"err_if_bad_addr": schema.Boolean(),
"err_if_bad_rw": schema.Boolean(),
}
@functools.lru_cache()
def get_cpuifs(self) -> Dict[str, Type[CpuifBase]]:
# All built-in CPUIFs
cpuifs = {
"passthrough": passthrough.PassthroughCpuif,
"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,
"avalon-mm": avalon.Avalon_Cpuif,
"avalon-mm-flat": avalon.Avalon_Cpuif_flattened,
"obi": obi.OBI_Cpuif,
"obi-flat": obi.OBI_Cpuif_flattened,
}
# Load any cpuifs specified via entry points
for ep, dist in get_entry_points("peakrdl_regblock.cpuif"):
name = ep.name
cpuif = ep.load()
if name in cpuifs:
raise RuntimeError(f"A plugin for 'peakrdl-regblock' tried to load cpuif '{name}' but it already exists")
if not issubclass(cpuif, CpuifBase):
raise RuntimeError(f"A plugin for 'peakrdl-regblock' tried to load cpuif '{name}' but it not a CpuifBase class")
cpuifs[name] = cpuif
# Load any CPUIFs via config import
for name, cpuif in self.cfg['cpuifs'].items():
if name in cpuifs:
raise RuntimeError(f"A plugin for 'peakrdl-regblock' tried to load cpuif '{name}' but it already exists")
if not issubclass(cpuif, CpuifBase):
raise RuntimeError(f"A plugin for 'peakrdl-regblock' tried to load cpuif '{name}' but it not a CpuifBase class")
cpuifs[name] = cpuif
return cpuifs
def add_exporter_arguments(self, arg_group: 'argparse._ActionsContainer') -> None:
cpuifs = self.get_cpuifs()
arg_group.add_argument(
"--cpuif",
choices=cpuifs.keys(),
default="apb3",
help="Select the CPU interface protocol to use [apb3]"
)
arg_group.add_argument(
"--module-name",
metavar="NAME",
default=None,
help="Override the SystemVerilog module name"
)
arg_group.add_argument(
"--package-name",
metavar="NAME",
default=None,
help="Override the SystemVerilog package name"
)
arg_group.add_argument(
"--type-style",
dest="type_style",
choices=['lexical', 'hier'],
default="lexical",
help="""Choose how HWIF struct type names are generated.
The 'lexical' style will use RDL lexical scope & type names where
possible and attempt to re-use equivalent type definitions.
The 'hier' style uses component's hierarchy as the struct type name. [lexical]
"""
)
arg_group.add_argument(
"--hwif-report",
action="store_true",
default=False,
help="Generate a HWIF report file"
)
arg_group.add_argument(
"--addr-width",
type=int,
default=None,
help="""Override the CPU interface's address width. By default,
address width is sized to the contents of the regblock.
"""
)
arg_group.add_argument(
"--rt-read-fanin",
action="store_true",
default=False,
help="Enable additional read path retiming. Good for register blocks with large readback fan-in"
)
arg_group.add_argument(
"--rt-read-response",
action="store_true",
default=False,
help="Enable additional retiming stage between readback fan-in and cpu interface"
)
arg_group.add_argument(
"--rt-external",
help="Retime outputs to external components. Specify a comma-separated list of: reg,regfile,mem,addrmap,all"
)
arg_group.add_argument(
"--default-reset",
choices=["rst", "rst_n", "arst", "arst_n"],
default=None,
help="""Choose the default style of reset signal if not explicitly
specified by the SystemRDL design. If unspecified, the default reset
is active-high and synchronous [rst]"""
)
arg_group.add_argument(
"--err-if-bad-addr",
action="store_true",
default=False,
help="Generate CPUIF error response, when the address is decoded incorrectly"
)
arg_group.add_argument(
"--err-if-bad-rw",
action="store_true",
default=False,
help="""Generate CPUIF error response, when an illegal access is
performed to a read-only or write-only register"""
)
def do_export(self, top_node: 'AddrmapNode', options: 'argparse.Namespace') -> None:
cpuifs = self.get_cpuifs()
retime_external_reg = False
retime_external_regfile = False
retime_external_mem = False
retime_external_addrmap = False
if options.rt_external:
for key in options.rt_external.split(","):
key = key.strip().lower()
if key == "reg":
retime_external_reg = True
elif key == "regfile":
retime_external_regfile = True
elif key == "mem":
retime_external_mem = True
elif key == "addrmap":
retime_external_addrmap = True
elif key == "all":
retime_external_reg = True
retime_external_regfile = True
retime_external_mem = True
retime_external_addrmap = True
else:
print("error: invalid option for --rt-external: '%s'" % key, file=sys.stderr)
# Get default reset. Favor command-line over cfg. Fall back to 'rst'
default_rst = options.default_reset or self.cfg['default_reset'] or "rst"
if default_rst == "rst":
default_reset_activelow = False
default_reset_async = False
elif default_rst == "rst_n":
default_reset_activelow = True
default_reset_async = False
elif default_rst == "arst":
default_reset_activelow = False
default_reset_async = True
elif default_rst == "arst_n":
default_reset_activelow = True
default_reset_async = True
else:
raise RuntimeError
x = RegblockExporter()
x.export(
top_node,
options.output,
cpuif_cls=cpuifs[options.cpuif],
module_name=options.module_name,
package_name=options.package_name,
reuse_hwif_typedefs=(options.type_style == "lexical"),
retime_read_fanin=options.rt_read_fanin,
retime_read_response=options.rt_read_response,
retime_external_reg=retime_external_reg,
retime_external_regfile=retime_external_regfile,
retime_external_mem=retime_external_mem,
retime_external_addrmap=retime_external_addrmap,
generate_hwif_report=options.hwif_report,
address_width=options.addr_width,
default_reset_activelow=default_reset_activelow,
err_if_bad_addr=options.err_if_bad_addr or self.cfg['err_if_bad_addr'],
err_if_bad_rw=options.err_if_bad_rw or self.cfg['err_if_bad_rw'],
default_reset_async=default_reset_async,
)