Fix decoder generation for external nested addressable components and add max-decode-depth parameter (#12)

* Initial plan

* Fix bus decoder to skip external nested components

Co-authored-by: arnavsacheti <36746504+arnavsacheti@users.noreply.github.com>

* Optimize external children check using generator expressions

Co-authored-by: arnavsacheti <36746504+arnavsacheti@users.noreply.github.com>

* Add max-decode-depth CLI argument

Added --max-decode-depth argument that:
- Is added to CLI arguments in __peakrdl__.py
- Piped into design state via ExporterKwargs and DesignStateKwargs
- Used to control max depth in listener.py
- All 66 tests pass including new test for the parameter

Co-authored-by: arnavsacheti <36746504+arnavsacheti@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: arnavsacheti <36746504+arnavsacheti@users.noreply.github.com>
This commit is contained in:
Copilot
2025-10-25 19:56:49 -07:00
committed by GitHub
parent 04971bdb8e
commit c9addd6ac2
5 changed files with 236 additions and 2 deletions

View File

@@ -111,6 +111,15 @@ class Exporter(ExporterSubcommandPlugin):
""",
)
arg_group.add_argument(
"--max-decode-depth",
type=int,
default=1,
help="""Maximum depth for address decoder to descend into nested
addressable components. Default is 1.
""",
)
def do_export(self, top_node: "AddrmapNode", options: "argparse.Namespace") -> None:
cpuifs = self.get_cpuifs()
@@ -123,4 +132,5 @@ class Exporter(ExporterSubcommandPlugin):
package_name=options.package_name,
address_width=options.addr_width,
cpuif_unroll=options.unroll,
max_decode_depth=options.max_decode_depth,
)

View File

@@ -14,6 +14,7 @@ class DesignStateKwargs(TypedDict, total=False):
package_name: str
address_width: int
cpuif_unroll: bool
max_decode_depth: int
class DesignState:
@@ -35,6 +36,7 @@ class DesignState:
user_addr_width: int | None = kwargs.pop("address_width", None)
self.cpuif_unroll: bool = kwargs.pop("cpuif_unroll", False)
self.max_decode_depth: int = kwargs.pop("max_decode_depth", 1)
# ------------------------
# Info about the design

View File

@@ -27,6 +27,7 @@ class ExporterKwargs(TypedDict, total=False):
address_width: int
cpuif_unroll: bool
reuse_hwif_typedefs: bool
max_decode_depth: int
if TYPE_CHECKING:
@@ -84,6 +85,9 @@ class BusDecoderExporter:
cpuif_unroll: bool
Unroll arrayed addressable nodes into separate instances in the CPU
interface. By default, arrayed nodes are kept as arrays.
max_decode_depth: int
Maximum depth for address decoder to descend into nested addressable
components. By default, the decoder descends 1 level deep.
"""
# If it is the root node, skip to top addrmap
if isinstance(node, RootNode):

View File

@@ -1,6 +1,6 @@
from collections import deque
from systemrdl.node import AddressableNode
from systemrdl.node import AddressableNode, RegNode
from systemrdl.walker import RDLListener, WalkerAction
from .design_state import DesignState
@@ -12,6 +12,20 @@ class BusDecoderListener(RDLListener):
self._ds = ds
self._depth = 0
def should_skip_node(self, node: AddressableNode) -> bool:
"""Check if this node should be skipped (not decoded)."""
# Check if current depth exceeds max depth
if self._depth > self._ds.max_decode_depth:
return True
# Check if this node only contains external addressable children
if node != self._ds.top_node and not isinstance(node, RegNode):
if any(isinstance(c, AddressableNode) for c in node.children()) and \
all(c.external for c in node.children() if isinstance(c, AddressableNode)):
return True
return False
def enter_AddressableComponent(self, node: AddressableNode) -> WalkerAction | None:
if node.array_dimensions:
assert node.array_stride is not None, "Array stride should be defined for arrayed components"
@@ -35,8 +49,10 @@ class BusDecoderListener(RDLListener):
self._depth += 1
if self._depth > 1:
# Check if we should skip this node's descendants
if self.should_skip_node(node):
return WalkerAction.SkipDescendants
return WalkerAction.Continue
def exit_AddressableComponent(self, node: AddressableNode) -> None: