typecheck + fanout/fanin

This commit is contained in:
Arnav Sacheti
2025-10-19 22:26:13 -07:00
parent eb5e64b151
commit e210167f2e
7 changed files with 106 additions and 49 deletions

View File

@@ -1,5 +1,5 @@
import functools import functools
from typing import TYPE_CHECKING from typing import TYPE_CHECKING, Any
from peakrdl.config import schema from peakrdl.config import schema
from peakrdl.plugins.entry_points import get_entry_points from peakrdl.plugins.entry_points import get_entry_points
@@ -15,55 +15,61 @@ if TYPE_CHECKING:
from systemrdl.node import AddrmapNode from systemrdl.node import AddrmapNode
@functools.lru_cache
def get_cpuifs(
config: dict[str, Any],
) -> dict[str, type[BaseCpuif]]:
# All built-in CPUIFs
cpuifs: dict[str, type[BaseCpuif]] = {
# "passthrough": passthrough.PassthroughCpuif,
"apb3": apb3.APB3Cpuif,
"apb3-flat": apb3.APB3CpuifFlat,
"apb4": apb4.APB4Cpuif,
"apb4-flat": apb4.APB4CpuifFlat,
# "axi4-lite": axi4lite.AXI4Lite_Cpuif,
# "axi4-lite-flat": axi4lite.AXI4Lite_Cpuif_flattened,
}
# Load any cpuifs specified via entry points
for ep, _ in get_entry_points("peakrdl_busdecoder.cpuif"):
name = ep.name
cpuif = ep.load()
if name in cpuifs:
raise RuntimeError(
f"A plugin for 'peakrdl-busdecoder' tried to load cpuif '{name}' but it already exists"
)
if not issubclass(cpuif, BaseCpuif):
raise RuntimeError(
f"A plugin for 'peakrdl-busdecoder' tried to load cpuif '{name}' but it not a BaseCpuif class"
)
cpuifs[name] = cpuif
# Load any CPUIFs via config import
for name, cpuif in config.items():
if name in cpuifs:
raise RuntimeError(
f"A plugin for 'peakrdl-busdecoder' tried to load cpuif '{name}' but it already exists"
)
if not issubclass(cpuif, BaseCpuif):
raise RuntimeError(
f"A plugin for 'peakrdl-busdecoder' tried to load cpuif '{name}' but it not a BaseCpuif class"
)
cpuifs[name] = cpuif
return cpuifs
class Exporter(ExporterSubcommandPlugin): class Exporter(ExporterSubcommandPlugin):
short_desc = "Generate a SystemVerilog control/status register (CSR) block" short_desc = "Generate a SystemVerilog control/status register (CSR) block"
udp_definitions = ALL_UDPS udp_definitions = ALL_UDPS
cfg_schema = { cfg_schema = { # noqa: RUF012
"cpuifs": {"*": schema.PythonObjectImport()}, "cpuifs": {"*": schema.PythonObjectImport()},
} }
@functools.lru_cache
def get_cpuifs(self) -> dict[str, type[BaseCpuif]]: def get_cpuifs(self) -> dict[str, type[BaseCpuif]]:
# All built-in CPUIFs return get_cpuifs(self.cfg["cpuifs"])
cpuifs: dict[str, type[BaseCpuif]] = {
# "passthrough": passthrough.PassthroughCpuif,
"apb3": apb3.APB3Cpuif,
"apb3-flat": apb3.APB3CpuifFlat,
"apb4": apb4.APB4Cpuif,
"apb4-flat": apb4.APB4CpuifFlat,
# "axi4-lite": axi4lite.AXI4Lite_Cpuif,
# "axi4-lite-flat": axi4lite.AXI4Lite_Cpuif_flattened,
}
# Load any cpuifs specified via entry points
for ep, _ in get_entry_points("peakrdl_busdecoder.cpuif"):
name = ep.name
cpuif = ep.load()
if name in cpuifs:
raise RuntimeError(
f"A plugin for 'peakrdl-busdecoder' tried to load cpuif '{name}' but it already exists"
)
if not issubclass(cpuif, BaseCpuif):
raise RuntimeError(
f"A plugin for 'peakrdl-busdecoder' tried to load cpuif '{name}' but it not a BaseCpuif 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-busdecoder' tried to load cpuif '{name}' but it already exists"
)
if not issubclass(cpuif, BaseCpuif):
raise RuntimeError(
f"A plugin for 'peakrdl-busdecoder' tried to load cpuif '{name}' but it not a BaseCpuif class"
)
cpuifs[name] = cpuif
return cpuifs
def add_exporter_arguments(self, arg_group: "argparse.ArgumentParser") -> None: # type: ignore def add_exporter_arguments(self, arg_group: "argparse.ArgumentParser") -> None: # type: ignore
cpuifs = self.get_cpuifs() cpuifs = self.get_cpuifs()

View File

@@ -1,11 +1,15 @@
import inspect import inspect
import os import os
from typing import TYPE_CHECKING from typing import TYPE_CHECKING, Any
import jinja2 as jj import jinja2 as jj
from systemrdl.node import AddressableNode from systemrdl.node import AddressableNode
from systemrdl.walker import RDLSteerableWalker
from ..listener import BusDecoderListener
from ..utils import clog2, get_indexed_path, is_pow2, roundup_pow2 from ..utils import clog2, get_indexed_path, is_pow2, roundup_pow2
from .fanin_gen import FaninGenerator
from .fanout_gen import FanoutGenerator
if TYPE_CHECKING: if TYPE_CHECKING:
from ..exporter import BusDecoderExporter from ..exporter import BusDecoderExporter
@@ -84,10 +88,13 @@ class BaseCpuif:
jj_env.filters["roundup_pow2"] = roundup_pow2 # type: ignore jj_env.filters["roundup_pow2"] = roundup_pow2 # type: ignore
jj_env.filters["address_slice"] = self.get_address_slice # type: ignore jj_env.filters["address_slice"] = self.get_address_slice # type: ignore
jj_env.filters["get_path"] = lambda x: get_indexed_path(self.exp.ds.top_node, x, "i") # type: ignore jj_env.filters["get_path"] = lambda x: get_indexed_path(self.exp.ds.top_node, x, "i") # type: ignore
jj_env.filters["walk"] = self.walk # type: ignore
context = { context = { # type: ignore
"cpuif": self, "cpuif": self,
"ds": self.exp.ds, "ds": self.exp.ds,
"fanout": FanoutGenerator,
"fanin": FaninGenerator,
} }
template = jj_env.get_template(self.template_path) template = jj_env.get_template(self.template_path)
@@ -98,3 +105,9 @@ class BaseCpuif:
size = node.size size = node.size
return f"({cpuif_addr} - 'h{addr:x})[{clog2(size) - 1}:0]" return f"({cpuif_addr} - 'h{addr:x})[{clog2(size) - 1}:0]"
def walk(self, listener_cls: type[BusDecoderListener], **kwargs: Any) -> str:
walker = RDLSteerableWalker()
listener = listener_cls(self.exp.ds, **kwargs)
walker.walk(self.exp.ds.top_node, listener, skip_top=True)
return str(listener)

View File

@@ -0,0 +1,19 @@
from systemrdl.node import AddressableNode
from systemrdl.walker import WalkerAction
from ..design_state import DesignState
from ..listener import BusDecoderListener
from .base_cpuif import BaseCpuif
class FaninGenerator(BusDecoderListener):
def __init__(self, ds: DesignState, cpuif: BaseCpuif) -> None:
super().__init__(ds)
self._cpuif = cpuif
def enter_AddressableComponent(self, node: AddressableNode) -> WalkerAction | None:
action = super().enter_AddressableComponent(node)
return action
def exit_AddressableComponent(self, node: AddressableNode) -> None:
super().exit_AddressableComponent(node)

View File

@@ -0,0 +1,19 @@
from systemrdl.node import AddressableNode
from systemrdl.walker import WalkerAction
from ..design_state import DesignState
from ..listener import BusDecoderListener
from .base_cpuif import BaseCpuif
class FanoutGenerator(BusDecoderListener):
def __init__(self, ds: DesignState, cpuif: BaseCpuif) -> None:
super().__init__(ds)
self._cpuif = cpuif
def enter_AddressableComponent(self, node: AddressableNode) -> WalkerAction | None:
action = super().enter_AddressableComponent(node)
return action
def exit_AddressableComponent(self, node: AddressableNode) -> None:
super().exit_AddressableComponent(node)

View File

@@ -11,11 +11,11 @@ from typing_extensions import Unpack
from .cpuif import BaseCpuif from .cpuif import BaseCpuif
from .cpuif.apb4 import APB4Cpuif from .cpuif.apb4 import APB4Cpuif
from .decoder import DecodeLogicFlavor, DecodeLogicGenerator from .decode_logic_gen import DecodeLogicFlavor, DecodeLogicGenerator
from .design_state import DesignState from .design_state import DesignState
from .identifier_filter import kw_filter as kwf from .identifier_filter import kw_filter as kwf
from .listener import BusDecoderListener from .listener import BusDecoderListener
from .struct_generator import StructGenerator from .struct_gen import StructGenerator
from .sv_int import SVInt from .sv_int import SVInt
from .validate_design import DesignValidator from .validate_design import DesignValidator
@@ -57,8 +57,8 @@ class BusDecoderExporter:
loader=c_loader, loader=c_loader,
undefined=jj.StrictUndefined, undefined=jj.StrictUndefined,
) )
self.jj_env.filters["kwf"] = kwf self.jj_env.filters["kwf"] = kwf # type: ignore
self.jj_env.filters["walk"] = self.walk self.jj_env.filters["walk"] = self.walk # type: ignore
def export(self, node: RootNode | AddrmapNode, output_dir: str, **kwargs: Unpack[ExporterKwargs]) -> None: def export(self, node: RootNode | AddrmapNode, output_dir: str, **kwargs: Unpack[ExporterKwargs]) -> None:
""" """
@@ -106,7 +106,7 @@ class BusDecoderExporter:
DesignValidator(self).do_validate() DesignValidator(self).do_validate()
# Build Jinja template context # Build Jinja template context
context = { context = { # type: ignore
"current_date": datetime.now().strftime("%Y-%m-%d"), "current_date": datetime.now().strftime("%Y-%m-%d"),
"version": version("peakrdl-busdecoder"), "version": version("peakrdl-busdecoder"),
"cpuif": self.cpuif, "cpuif": self.cpuif,
@@ -129,7 +129,7 @@ class BusDecoderExporter:
stream = template.stream(context) stream = template.stream(context)
stream.dump(module_file_path) stream.dump(module_file_path)
def walk(self, listener_cls: type[BusDecoderListener], **kwargs: Any) -> BusDecoderListener: def walk(self, listener_cls: type[BusDecoderListener], **kwargs: Any) -> str:
walker = RDLSteerableWalker() walker = RDLSteerableWalker()
listener = listener_cls(self.ds, **kwargs) listener = listener_cls(self.ds, **kwargs)
walker.walk(self.ds.top_node, listener, skip_top=True) walker.walk(self.ds.top_node, listener, skip_top=True)