* 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>
226 lines
8.4 KiB
Python
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,
|
|
)
|