Exporter Module#

The Pybind11Exporter turns a SystemRDL compilation into a buildable PyBind11 module. It can be driven directly from Python or via the peakrdl pybind11 subcommand registered by the peakrdl.exporters entry point.

Programmatic API#

The exporter is a plain class. Compile your RDL with the upstream systemrdl-compiler, then hand the root node to Pybind11Exporter.export():

from systemrdl import RDLCompiler
from peakrdl_pybind11 import Pybind11Exporter

rdlc = RDLCompiler()
# Optional: pre-register the exporter's UDPs so users do not have to
# declare ``property is_flag {...}`` etc. in their RDL.
Pybind11Exporter.register_udps(rdlc)

rdlc.compile_file("mychip.rdl")
root = rdlc.elaborate()

Pybind11Exporter().export(
    root,
    output_dir="build/mychip",
    soc_name="mychip",
    split_by_hierarchy=True,
)

CLI options reference#

The exporter is exposed through peakrdl pybind11. peakrdl-cli itself contributes the input file argument and --top; the flags below are the ones added (or aspired to) by this exporter.

--soc-name NAME

Name of the generated SoC module. Defaults to the top-level addrmap’s instance name. The chosen name shapes the import path your test code uses (import NAME -> NAME.create()).

--top NODE

(provided by peakrdl-cli.) Pick a non-root addrmap to export. Useful when one .rdl defines several SoCs and you want bindings for just one.

--gen-pyi / --no-gen-pyi

Emit .pyi stub files alongside the compiled extension so editors and type checkers see the full hierarchy. Default: enabled.

--split-bindings COUNT

Split the generated PyBind11 sources across multiple translation units when the register count exceeds COUNT. Speeds up compilation for large designs by enabling parallel make/ninja jobs. Set to 0 to force a single TU. Default: 100. Ignored when --split-by-hierarchy is used.

--split-by-hierarchy

Split bindings by addrmap/regfile boundary instead of by register count. Keeps related registers in the same translation unit, which is friendlier to incremental rebuilds and matches the way most large SoCs are organized.

--explore

Spawn an IPython REPL with soc already created and ready to use. Inside the REPL, ?soc.uart.control shows full metadata and ??soc.uart.control shows the originating RDL source (sketch §21).

--diff snapA snapB

Render a text or HTML diff of two saved snapshots. Pairs with soc.snapshot() and soc.save()/soc.load() for golden-state regression workflows (sketch §21).

--replay session.json

Replay a recorded master session (the trace Master.record() produces) against a target. Useful for reproducing a bug captured on silicon against a mock or a cosim model (sketch §21).

--watch input.rdl

Re-build and re-load the bound module whenever the input .rdl changes. Backed by watchdog; emits a warning on every reload so you cannot miss it (sketch §21). See Hot reload semantics below.

--strict-fields=false

Build-time opt-out from the strict-fields default. Restores attribute-assign-as-read-modify-write so teams porting C drivers can keep their existing call sites (sketch §22.8).

Warning

--strict-fields=false is intentionally noisy. It emits a DeprecationWarning once at module import and once per loose attribute assignment. Silent RMW is the leading source of “I thought that wrote” bugs, and the warning stream is the price of the escape hatch. The strict default is preferred — use Register.write_fields() or RegisterValue.replace() for multi-field updates.

Hot reload semantics#

--watch (and its in-process twin Soc.reload()) is opt-in. On reload, the runtime:

  • emits a warning so the event is never silent,

  • invalidates outstanding RegisterValue and Snapshot handles — they raise on next access rather than returning stale data,

  • refuses to swap if a context manager (transaction, write-only block, etc.) is currently active, and

  • re-attaches the existing master to the freshly built tree.

Hardware bus state is not affected — only the host-side bindings are replaced. Users who would rather crash than warn can set peakrdl.reload.policy = "fail" and the runtime aborts the reload instead of continuing with new bindings.

API reference#

Main exporter implementation for PeakRDL-pybind11

class peakrdl_pybind11.exporter.Nodes[source]#

Bases: TypedDict

addrmaps: list[AddrmapNode]#
regfiles: list[RegfileNode]#
regs: list[RegNode]#
fields: list[FieldNode]#
mems: list[MemNode]#
flag_regs: list[RegNode]#
enum_regs: list[RegNode]#
signals: list[SignalNode]#
register_members: dict[int, list[tuple[str, int]]]#
field_encodes: dict[str, list[tuple[str, int]]]#
class peakrdl_pybind11.exporter.Pybind11Exporter[source]#

Bases: object

Export SystemRDL register descriptions to PyBind11 C++ modules

__init__()[source]#
Return type:

None

soc_name: str | None#
soc_version: str#
top_node: AddrmapNode | None#
output_dir: Path | None#
export(top_node, output_dir, soc_name=None, soc_version='0.1.0', gen_pyi=True, split_bindings=100, split_by_hierarchy=False, interrupt_pattern=None)[source]#

Export SystemRDL to PyBind11 modules

Parameters:
  • top_node (RootNode | AddrmapNode) – Root node of the SystemRDL compilation

  • output_dir (str) – Directory to write output files

  • soc_name (str | None) – Name of the SoC module (default: derived from top node)

  • soc_version (str) – Version string for the SoC module (default: “0.1.0”)

  • gen_pyi (bool) – Generate .pyi stub files for type hints

  • split_bindings (int) – Split bindings into multiple files when register count exceeds this threshold. Set to 0 to disable splitting. Default: 100 Ignored when split_by_hierarchy is True.

  • split_by_hierarchy (bool) – When True, split bindings by addrmap/regfile hierarchy instead of by register count. This keeps related registers together and provides more logical grouping. Default: False

  • interrupt_pattern (object | None) – Optional override for the interrupt-state-register matcher used by the feature_detection exporter plugin. Accepts a regex string, compiled re.Pattern, or a callable (name: str) -> bool.

Return type:

None

classmethod register_udps(rdl_compiler)[source]#

Register every UDP this exporter recognizes with the given compiler.

For programmatic use:

from systemrdl import RDLCompiler from peakrdl_pybind11 import Pybind11Exporter

rdl = RDLCompiler() Pybind11Exporter.register_udps(rdl) rdl.compile_file(…)

CLI users can equivalently declare the UDPs in their RDL.

Return type:

None

Parameters:

rdl_compiler (object)