peakrdl-busdecoder 0.2.0__tar.gz → 0.5.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. peakrdl_busdecoder-0.5.0/.devcontainer/Dockerfile +22 -0
  2. peakrdl_busdecoder-0.5.0/.devcontainer/devcontainer.json +33 -0
  3. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/.github/workflows/release.yml +3 -4
  4. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/.github/workflows/test.yml +9 -5
  5. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/PKG-INFO +2 -2
  6. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/pyproject.toml +3 -2
  7. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/src/peakrdl_busdecoder/__peakrdl__.py +3 -1
  8. peakrdl_busdecoder-0.5.0/src/peakrdl_busdecoder/cpuif/apb3/apb3_cpuif.py +92 -0
  9. peakrdl_busdecoder-0.2.0/src/peakrdl_busdecoder/cpuif/apb3/apb3_cpuif.py → peakrdl_busdecoder-0.5.0/src/peakrdl_busdecoder/cpuif/apb3/apb3_cpuif_flat.py +12 -11
  10. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/src/peakrdl_busdecoder/cpuif/apb3/apb3_tmpl.sv +7 -0
  11. peakrdl_busdecoder-0.5.0/src/peakrdl_busdecoder/cpuif/apb4/apb4_cpuif.py +95 -0
  12. peakrdl_busdecoder-0.2.0/src/peakrdl_busdecoder/cpuif/apb4/apb4_cpuif.py → peakrdl_busdecoder-0.5.0/src/peakrdl_busdecoder/cpuif/apb4/apb4_cpuif_flat.py +12 -12
  13. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/src/peakrdl_busdecoder/cpuif/apb4/apb4_tmpl.sv +7 -0
  14. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/src/peakrdl_busdecoder/cpuif/axi4lite/axi4_lite_cpuif.py +31 -6
  15. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/src/peakrdl_busdecoder/cpuif/axi4lite/axi4_lite_cpuif_flat.py +2 -2
  16. peakrdl_busdecoder-0.2.0/src/peakrdl_busdecoder/cpuif/axi4lite/axi4lite_tmpl.sv → peakrdl_busdecoder-0.5.0/src/peakrdl_busdecoder/cpuif/axi4lite/axi4_lite_tmpl.sv +7 -0
  17. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/src/peakrdl_busdecoder/cpuif/base_cpuif.py +24 -5
  18. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/src/peakrdl_busdecoder/cpuif/fanin_gen.py +11 -0
  19. peakrdl_busdecoder-0.5.0/src/peakrdl_busdecoder/cpuif/fanin_intermediate_gen.py +142 -0
  20. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/src/peakrdl_busdecoder/cpuif/fanout_gen.py +11 -0
  21. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/src/peakrdl_busdecoder/cpuif/interface.py +10 -1
  22. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/src/peakrdl_busdecoder/decode_logic_gen.py +19 -1
  23. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/src/peakrdl_busdecoder/design_state.py +54 -1
  24. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/src/peakrdl_busdecoder/exporter.py +3 -1
  25. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/src/peakrdl_busdecoder/listener.py +6 -1
  26. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/src/peakrdl_busdecoder/module_tmpl.sv +2 -2
  27. peakrdl_busdecoder-0.5.0/src/peakrdl_busdecoder/struct_gen.py +68 -0
  28. peakrdl_busdecoder-0.5.0/src/peakrdl_busdecoder/sv_int.py +48 -0
  29. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/src/peakrdl_busdecoder.egg-info/PKG-INFO +2 -2
  30. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/src/peakrdl_busdecoder.egg-info/SOURCES.txt +5 -2
  31. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/tools/shims/xargs +0 -1
  32. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/uv.lock +1 -1
  33. peakrdl_busdecoder-0.2.0/src/peakrdl_busdecoder/cpuif/apb3/apb3_cpuif_flat.py +0 -68
  34. peakrdl_busdecoder-0.2.0/src/peakrdl_busdecoder/cpuif/apb4/apb4_cpuif_flat.py +0 -70
  35. peakrdl_busdecoder-0.2.0/src/peakrdl_busdecoder/struct_gen.py +0 -57
  36. peakrdl_busdecoder-0.2.0/src/peakrdl_busdecoder/sv_int.py +0 -21
  37. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  38. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  39. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/.github/ISSUE_TEMPLATE/question.md +0 -0
  40. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/.github/pull_request_template.md +0 -0
  41. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/.github/workflows/build.yml +0 -0
  42. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/.github/workflows/docs.yml +0 -0
  43. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/.github/workflows/format.yml +0 -0
  44. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/.github/workflows/lint.yml +0 -0
  45. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/.github/workflows/typecheck.yml +0 -0
  46. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/.gitignore +0 -0
  47. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/.readthedocs.yaml +0 -0
  48. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/CONTRIBUTING.md +0 -0
  49. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/LICENSE +0 -0
  50. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/MANIFEST.in +0 -0
  51. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/README.md +0 -0
  52. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/docs/Makefile +0 -0
  53. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/docs/api.rst +0 -0
  54. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/docs/architecture.rst +0 -0
  55. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/docs/conf.py +0 -0
  56. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/docs/configuring.rst +0 -0
  57. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/docs/cpuif/apb.rst +0 -0
  58. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/docs/cpuif/avalon.rst +0 -0
  59. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/docs/cpuif/axi4lite.rst +0 -0
  60. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/docs/cpuif/customizing.rst +0 -0
  61. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/docs/cpuif/internal_protocol.rst +0 -0
  62. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/docs/cpuif/introduction.rst +0 -0
  63. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/docs/cpuif/passthrough.rst +0 -0
  64. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/docs/dev_notes/Alpha-Beta Versioning +0 -0
  65. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/docs/dev_notes/Hierarchy-and-Indexing +0 -0
  66. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/docs/dev_notes/Program Flow +0 -0
  67. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/docs/dev_notes/Resets +0 -0
  68. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/docs/dev_notes/Signal Dereferencer +0 -0
  69. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/docs/dev_notes/Validation Needed +0 -0
  70. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/docs/dev_notes/template-layers/1-port-declaration +0 -0
  71. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/docs/dev_notes/template-layers/1.1.hardware-interface +0 -0
  72. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/docs/dev_notes/template-layers/2-CPUIF +0 -0
  73. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/docs/dev_notes/template-layers/3-address-decode +0 -0
  74. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/docs/dev_notes/template-layers/4-fields +0 -0
  75. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/docs/dev_notes/template-layers/5-readback-mux +0 -0
  76. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/docs/dev_notes/template-layers/6-output-port-mapping +0 -0
  77. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/docs/diagrams/arch.png +0 -0
  78. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/docs/diagrams/diagrams.odg +0 -0
  79. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/docs/diagrams/rbuf.png +0 -0
  80. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/docs/diagrams/readback.png +0 -0
  81. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/docs/diagrams/wbuf.png +0 -0
  82. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/docs/faq.rst +0 -0
  83. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/docs/hwif.rst +0 -0
  84. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/docs/img/err.svg +0 -0
  85. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/docs/img/ok.svg +0 -0
  86. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/docs/img/warn.svg +0 -0
  87. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/docs/index.rst +0 -0
  88. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/docs/licensing.rst +0 -0
  89. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/docs/limitations.rst +0 -0
  90. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/docs/props/addrmap.rst +0 -0
  91. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/docs/props/field.rst +0 -0
  92. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/docs/props/reg.rst +0 -0
  93. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/docs/props/rhs_props.rst +0 -0
  94. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/docs/props/signal.rst +0 -0
  95. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/docs/rdl_features/external.rst +0 -0
  96. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/docs/requirements.txt +0 -0
  97. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/docs/udps/intro.rst +0 -0
  98. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/hdl-src/README.md +0 -0
  99. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/hdl-src/apb3_intf.sv +0 -0
  100. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/hdl-src/apb4_intf.sv +0 -0
  101. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/hdl-src/avalon_mm_intf.sv +0 -0
  102. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/hdl-src/axi4lite_intf.sv +0 -0
  103. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/setup.cfg +0 -0
  104. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/src/peakrdl_busdecoder/__init__.py +0 -0
  105. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/src/peakrdl_busdecoder/body/__init__.py +0 -0
  106. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/src/peakrdl_busdecoder/body/body.py +0 -0
  107. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/src/peakrdl_busdecoder/body/combinational_body.py +0 -0
  108. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/src/peakrdl_busdecoder/body/for_loop_body.py +0 -0
  109. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/src/peakrdl_busdecoder/body/if_body.py +0 -0
  110. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/src/peakrdl_busdecoder/body/struct_body.py +0 -0
  111. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/src/peakrdl_busdecoder/cpuif/__init__.py +0 -0
  112. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/src/peakrdl_busdecoder/cpuif/apb3/__init__.py +0 -0
  113. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/src/peakrdl_busdecoder/cpuif/apb3/apb3_interface.py +0 -0
  114. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/src/peakrdl_busdecoder/cpuif/apb4/__init__.py +0 -0
  115. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/src/peakrdl_busdecoder/cpuif/apb4/apb4_interface.py +0 -0
  116. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/src/peakrdl_busdecoder/cpuif/axi4lite/__init__.py +0 -0
  117. /peakrdl_busdecoder-0.2.0/src/peakrdl_busdecoder/cpuif/axi4lite/axi4lite_interface.py → /peakrdl_busdecoder-0.5.0/src/peakrdl_busdecoder/cpuif/axi4lite/axi4_lite_interface.py +0 -0
  118. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/src/peakrdl_busdecoder/design_scanner.py +0 -0
  119. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/src/peakrdl_busdecoder/identifier_filter.py +0 -0
  120. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/src/peakrdl_busdecoder/package_tmpl.sv +0 -0
  121. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/src/peakrdl_busdecoder/py.typed +0 -0
  122. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/src/peakrdl_busdecoder/udps/__init__.py +0 -0
  123. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/src/peakrdl_busdecoder/utils.py +0 -0
  124. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/src/peakrdl_busdecoder/validate_design.py +0 -0
  125. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/src/peakrdl_busdecoder.egg-info/dependency_links.txt +0 -0
  126. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/src/peakrdl_busdecoder.egg-info/entry_points.txt +0 -0
  127. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/src/peakrdl_busdecoder.egg-info/requires.txt +0 -0
  128. {peakrdl_busdecoder-0.2.0 → peakrdl_busdecoder-0.5.0}/src/peakrdl_busdecoder.egg-info/top_level.txt +0 -0
@@ -0,0 +1,22 @@
1
+ FROM verilator/verilator:latest
2
+
3
+ ENV DEBIAN_FRONTEND=noninteractive
4
+
5
+ RUN apt-get update && \
6
+ apt-get install -y --no-install-recommends \
7
+ python3 \
8
+ python3-venv \
9
+ python3-pip \
10
+ python3-dev \
11
+ build-essential \
12
+ pkg-config \
13
+ git \
14
+ curl \
15
+ ca-certificates && \
16
+ rm -rf /var/lib/apt/lists/*
17
+
18
+ ENV UV_INSTALL_DIR=/usr/local/bin
19
+ ENV UV_LINK_MODE=copy
20
+ # Install uv globally so both VS Code and terminals can use it
21
+ RUN curl -LsSf https://astral.sh/uv/install.sh | sh
22
+ RUN uv --version
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "PeakRDL BusDecoder",
3
+ "build": {
4
+ "dockerfile": "Dockerfile"
5
+ },
6
+ "runArgs": [
7
+ "--init"
8
+ ],
9
+ "features": {
10
+ "ghcr.io/devcontainers/features/common-utils:2": {
11
+ "username": "vscode",
12
+ "uid": "1000",
13
+ "gid": "1000",
14
+ "installZsh": "false",
15
+ "installOhMyZsh": "false"
16
+ }
17
+ },
18
+ "remoteUser": "vscode",
19
+ "postCreateCommand": "uv sync --frozen --all-extras --group tools --group test",
20
+ "customizations": {
21
+ "vscode": {
22
+ "settings": {
23
+ "python.defaultInterpreterPath": ".venv/bin/python",
24
+ "terminal.integrated.shell.linux": "/bin/bash"
25
+ },
26
+ "extensions": [
27
+ "ms-python.python",
28
+ "ms-python.vscode-pylance",
29
+ "ms-vscode.cpptools"
30
+ ]
31
+ }
32
+ }
33
+ }
@@ -40,7 +40,6 @@ jobs:
40
40
 
41
41
  - name: Publish to PyPI
42
42
  if: startsWith(github.ref, 'refs/tags/')
43
- env:
44
- TWINE_USERNAME: __token__
45
- TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
46
- run: uvx twine upload dist/*
43
+ uses: pypa/gh-action-pypi-publish@release/v1
44
+ with:
45
+ skip-existing: true
@@ -14,6 +14,8 @@ on:
14
14
  jobs:
15
15
  test:
16
16
  runs-on: ubuntu-latest
17
+ container:
18
+ image: verilator/verilator:latest
17
19
  permissions:
18
20
  contents: read
19
21
  strategy:
@@ -27,19 +29,21 @@ jobs:
27
29
  uses: astral-sh/setup-uv@v3
28
30
  with:
29
31
  python-version: ${{ matrix.python-version }}
32
+ enable-cache: true
30
33
 
31
- - name: Install Verilator
34
+ - name: Check Verilator version
35
+ run: verilator --version
36
+
37
+ - name: Install Python development packages
32
38
  run: |
33
- sudo apt-get update
34
- sudo apt-get install -y verilator
35
- verilator --version
39
+ apt-get update && apt-get install -y python3-dev libpython3-dev
36
40
 
37
41
  - name: Install dependencies
38
42
  run: |
39
43
  uv sync --all-extras --group test
40
44
 
41
45
  - name: Run tests
42
- run: uv run pytest tests/ -v --cov=peakrdl_busdecoder --cov-report=xml --cov-report=term
46
+ run: uv run pytest tests/ --cov=peakrdl_busdecoder --cov-report=xml --cov-report=term
43
47
 
44
48
  - name: Upload coverage to Codecov
45
49
  uses: codecov/codecov-action@v4
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: peakrdl-busdecoder
3
- Version: 0.2.0
3
+ Version: 0.5.0
4
4
  Summary: Generate a SystemVerilog bus decoder from SystemRDL for splitting CPU interfaces to multiple sub-address spaces
5
- Author: Alex Mykyta
5
+ Author: Arnav Sacheti
6
6
  License: LGPLv3
7
7
  Project-URL: Source, https://github.com/arnavsacheti/PeakRDL-BusDecoder
8
8
  Project-URL: Tracker, https://github.com/arnavsacheti/PeakRDL-BusDecoder/issues
@@ -4,11 +4,11 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "peakrdl-busdecoder"
7
- version = "0.2.0"
7
+ version = "0.5.0"
8
8
  requires-python = ">=3.10"
9
9
  dependencies = ["jinja2>=3.1.6", "systemrdl-compiler~=1.30.1"]
10
10
 
11
- authors = [{ name = "Alex Mykyta" }]
11
+ authors = [{ name = "Arnav Sacheti" }]
12
12
  description = "Generate a SystemVerilog bus decoder from SystemRDL for splitting CPU interfaces to multiple sub-address spaces"
13
13
  readme = "README.md"
14
14
  license = { text = "LGPLv3" }
@@ -114,3 +114,4 @@ markers = [
114
114
  "simulation: marks tests as requiring cocotb simulation (deselect with '-m \"not simulation\"')",
115
115
  "verilator: marks tests as requiring verilator simulator (deselect with '-m \"not verilator\"')",
116
116
  ]
117
+ filterwarnings = ["error", "ignore::UserWarning"]
@@ -116,7 +116,9 @@ class Exporter(ExporterSubcommandPlugin):
116
116
  type=int,
117
117
  default=1,
118
118
  help="""Maximum depth for address decoder to descend into nested
119
- addressable components. Default is 1.
119
+ addressable components. Value of 0 decodes all levels (infinite depth).
120
+ Value of 1 decodes only top-level children. Value of 2 decodes top-level
121
+ and one level deeper, etc. Default is 1.
120
122
  """,
121
123
  )
122
124
 
@@ -0,0 +1,92 @@
1
+ from typing import TYPE_CHECKING, overload
2
+
3
+ from systemrdl.node import AddressableNode
4
+
5
+ from ...utils import get_indexed_path
6
+ from ..base_cpuif import BaseCpuif
7
+ from .apb3_interface import APB3SVInterface
8
+
9
+ if TYPE_CHECKING:
10
+ from ...exporter import BusDecoderExporter
11
+
12
+
13
+ class APB3Cpuif(BaseCpuif):
14
+ template_path = "apb3_tmpl.sv"
15
+
16
+ def __init__(self, exp: "BusDecoderExporter") -> None:
17
+ super().__init__(exp)
18
+ self._interface = APB3SVInterface(self)
19
+
20
+ @property
21
+ def is_interface(self) -> bool:
22
+ return self._interface.is_interface
23
+
24
+ @property
25
+ def port_declaration(self) -> str:
26
+ return self._interface.get_port_declaration("s_apb", "m_apb_")
27
+
28
+ @overload
29
+ def signal(self, signal: str, node: None = None, indexer: None = None) -> str: ...
30
+ @overload
31
+ def signal(self, signal: str, node: AddressableNode, indexer: str) -> str: ...
32
+ def signal(self, signal: str, node: AddressableNode | None = None, indexer: str | None = None) -> str:
33
+ return self._interface.signal(signal, node, indexer)
34
+
35
+ def fanout(self, node: AddressableNode) -> str:
36
+ fanout: dict[str, str] = {}
37
+ fanout[self.signal("PSEL", node, "gi")] = (
38
+ f"cpuif_wr_sel.{get_indexed_path(self.exp.ds.top_node, node, 'gi')}|cpuif_rd_sel.{get_indexed_path(self.exp.ds.top_node, node, 'gi')}"
39
+ )
40
+ fanout[self.signal("PENABLE", node, "gi")] = self.signal("PENABLE")
41
+ fanout[self.signal("PWRITE", node, "gi")] = (
42
+ f"cpuif_wr_sel.{get_indexed_path(self.exp.ds.top_node, node, 'gi')}"
43
+ )
44
+ fanout[self.signal("PADDR", node, "gi")] = self.signal("PADDR")
45
+ fanout[self.signal("PWDATA", node, "gi")] = "cpuif_wr_data"
46
+
47
+ return "\n".join(map(lambda kv: f"assign {kv[0]} = {kv[1]};", fanout.items()))
48
+
49
+ def fanin(self, node: AddressableNode | None = None) -> str:
50
+ fanin: dict[str, str] = {}
51
+ if node is None:
52
+ fanin["cpuif_rd_ack"] = "'0"
53
+ fanin["cpuif_rd_err"] = "'0"
54
+ else:
55
+ # Use intermediate signals for interface arrays to avoid
56
+ # non-constant indexing of interface arrays in procedural blocks
57
+ if self.is_interface and node.is_array and node.array_dimensions:
58
+ # Generate array index string [i0][i1]... for the intermediate signal
59
+ array_idx = "".join(f"[i{i}]" for i in range(len(node.array_dimensions)))
60
+ fanin["cpuif_rd_ack"] = f"{node.inst_name}_fanin_ready{array_idx}"
61
+ fanin["cpuif_rd_err"] = f"{node.inst_name}_fanin_err{array_idx}"
62
+ else:
63
+ fanin["cpuif_rd_ack"] = self.signal("PREADY", node, "i")
64
+ fanin["cpuif_rd_err"] = self.signal("PSLVERR", node, "i")
65
+
66
+ return "\n".join(map(lambda kv: f"{kv[0]} = {kv[1]};", fanin.items()))
67
+
68
+ def readback(self, node: AddressableNode | None = None) -> str:
69
+ fanin: dict[str, str] = {}
70
+ if node is None:
71
+ fanin["cpuif_rd_data"] = "'0"
72
+ else:
73
+ # Use intermediate signals for interface arrays to avoid
74
+ # non-constant indexing of interface arrays in procedural blocks
75
+ if self.is_interface and node.is_array and node.array_dimensions:
76
+ # Generate array index string [i0][i1]... for the intermediate signal
77
+ array_idx = "".join(f"[i{i}]" for i in range(len(node.array_dimensions)))
78
+ fanin["cpuif_rd_data"] = f"{node.inst_name}_fanin_data{array_idx}"
79
+ else:
80
+ fanin["cpuif_rd_data"] = self.signal("PRDATA", node, "i")
81
+
82
+ return "\n".join(map(lambda kv: f"{kv[0]} = {kv[1]};", fanin.items()))
83
+
84
+ def fanin_intermediate_assignments(
85
+ self, node: AddressableNode, inst_name: str, array_idx: str, master_prefix: str, indexed_path: str
86
+ ) -> list[str]:
87
+ """Generate intermediate signal assignments for APB3 interface arrays."""
88
+ return [
89
+ f"assign {inst_name}_fanin_ready{array_idx} = {master_prefix}{indexed_path}.PREADY;",
90
+ f"assign {inst_name}_fanin_err{array_idx} = {master_prefix}{indexed_path}.PSLVERR;",
91
+ f"assign {inst_name}_fanin_data{array_idx} = {master_prefix}{indexed_path}.PRDATA;",
92
+ ]
@@ -1,21 +1,21 @@
1
- from typing import TYPE_CHECKING, overload
1
+ from typing import TYPE_CHECKING
2
2
 
3
3
  from systemrdl.node import AddressableNode
4
4
 
5
5
  from ...utils import get_indexed_path
6
6
  from ..base_cpuif import BaseCpuif
7
- from .apb3_interface import APB3SVInterface
7
+ from .apb3_interface import APB3FlatInterface
8
8
 
9
9
  if TYPE_CHECKING:
10
10
  from ...exporter import BusDecoderExporter
11
11
 
12
12
 
13
- class APB3Cpuif(BaseCpuif):
13
+ class APB3CpuifFlat(BaseCpuif):
14
14
  template_path = "apb3_tmpl.sv"
15
15
 
16
16
  def __init__(self, exp: "BusDecoderExporter") -> None:
17
17
  super().__init__(exp)
18
- self._interface = APB3SVInterface(self)
18
+ self._interface = APB3FlatInterface(self)
19
19
 
20
20
  @property
21
21
  def is_interface(self) -> bool:
@@ -23,14 +23,15 @@ class APB3Cpuif(BaseCpuif):
23
23
 
24
24
  @property
25
25
  def port_declaration(self) -> str:
26
- return self._interface.get_port_declaration("s_apb", "m_apb_")
26
+ return self._interface.get_port_declaration("s_apb_", "m_apb_")
27
27
 
28
- @overload
29
- def signal(self, signal: str, node: None = None, indexer: None = None) -> str: ...
30
- @overload
31
- def signal(self, signal: str, node: AddressableNode, indexer: str) -> str: ...
32
- def signal(self, signal: str, node: AddressableNode | None = None, indexer: str | None = None) -> str:
33
- return self._interface.signal(signal, node, indexer)
28
+ def signal(
29
+ self,
30
+ signal: str,
31
+ node: AddressableNode | None = None,
32
+ idx: str | int | None = None,
33
+ ) -> str:
34
+ return self._interface.signal(signal, node, idx)
34
35
 
35
36
  def fanout(self, node: AddressableNode) -> str:
36
37
  fanout: dict[str, str] = {}
@@ -26,6 +26,13 @@ assign {{cpuif.signal("PSLVERR")}} = cpuif_rd_err | cpuif_rd_sel.cpuif_err | cpu
26
26
  // Fanout CPU Bus interface signals
27
27
  //--------------------------------------------------------------------------
28
28
  {{fanout|walk(cpuif=cpuif)}}
29
+ {%- if cpuif.is_interface %}
30
+
31
+ //--------------------------------------------------------------------------
32
+ // Intermediate signals for interface array fanin
33
+ //--------------------------------------------------------------------------
34
+ {{fanin_intermediate|walk(cpuif=cpuif)}}
35
+ {%- endif %}
29
36
 
30
37
  //--------------------------------------------------------------------------
31
38
  // Fanin CPU Bus interface signals
@@ -0,0 +1,95 @@
1
+ from typing import TYPE_CHECKING, overload
2
+
3
+ from systemrdl.node import AddressableNode
4
+
5
+ from ...utils import get_indexed_path
6
+ from ..base_cpuif import BaseCpuif
7
+ from .apb4_interface import APB4SVInterface
8
+
9
+ if TYPE_CHECKING:
10
+ from ...exporter import BusDecoderExporter
11
+
12
+
13
+ class APB4Cpuif(BaseCpuif):
14
+ template_path = "apb4_tmpl.sv"
15
+
16
+ def __init__(self, exp: "BusDecoderExporter") -> None:
17
+ super().__init__(exp)
18
+ self._interface = APB4SVInterface(self)
19
+
20
+ @property
21
+ def is_interface(self) -> bool:
22
+ return self._interface.is_interface
23
+
24
+ @property
25
+ def port_declaration(self) -> str:
26
+ """Returns the port declaration for the APB4 interface."""
27
+ return self._interface.get_port_declaration("s_apb", "m_apb_")
28
+
29
+ @overload
30
+ def signal(self, signal: str, node: None = None, indexer: None = None) -> str: ...
31
+ @overload
32
+ def signal(self, signal: str, node: AddressableNode, indexer: str) -> str: ...
33
+ def signal(self, signal: str, node: AddressableNode | None = None, indexer: str | None = None) -> str:
34
+ return self._interface.signal(signal, node, indexer)
35
+
36
+ def fanout(self, node: AddressableNode) -> str:
37
+ fanout: dict[str, str] = {}
38
+ fanout[self.signal("PSEL", node, "gi")] = (
39
+ f"cpuif_wr_sel.{get_indexed_path(self.exp.ds.top_node, node, 'gi')}|cpuif_rd_sel.{get_indexed_path(self.exp.ds.top_node, node, 'gi')}"
40
+ )
41
+ fanout[self.signal("PENABLE", node, "gi")] = self.signal("PENABLE")
42
+ fanout[self.signal("PWRITE", node, "gi")] = (
43
+ f"cpuif_wr_sel.{get_indexed_path(self.exp.ds.top_node, node, 'gi')}"
44
+ )
45
+ fanout[self.signal("PADDR", node, "gi")] = self.signal("PADDR")
46
+ fanout[self.signal("PPROT", node, "gi")] = self.signal("PPROT")
47
+ fanout[self.signal("PWDATA", node, "gi")] = "cpuif_wr_data"
48
+ fanout[self.signal("PSTRB", node, "gi")] = "cpuif_wr_byte_en"
49
+
50
+ return "\n".join(map(lambda kv: f"assign {kv[0]} = {kv[1]};", fanout.items()))
51
+
52
+ def fanin(self, node: AddressableNode | None = None) -> str:
53
+ fanin: dict[str, str] = {}
54
+ if node is None:
55
+ fanin["cpuif_rd_ack"] = "'0"
56
+ fanin["cpuif_rd_err"] = "'0"
57
+ else:
58
+ # Use intermediate signals for interface arrays to avoid
59
+ # non-constant indexing of interface arrays in procedural blocks
60
+ if self.is_interface and node.is_array and node.array_dimensions:
61
+ # Generate array index string [i0][i1]... for the intermediate signal
62
+ array_idx = "".join(f"[i{i}]" for i in range(len(node.array_dimensions)))
63
+ fanin["cpuif_rd_ack"] = f"{node.inst_name}_fanin_ready{array_idx}"
64
+ fanin["cpuif_rd_err"] = f"{node.inst_name}_fanin_err{array_idx}"
65
+ else:
66
+ fanin["cpuif_rd_ack"] = self.signal("PREADY", node, "i")
67
+ fanin["cpuif_rd_err"] = self.signal("PSLVERR", node, "i")
68
+
69
+ return "\n".join(map(lambda kv: f"{kv[0]} = {kv[1]};", fanin.items()))
70
+
71
+ def readback(self, node: AddressableNode | None = None) -> str:
72
+ fanin: dict[str, str] = {}
73
+ if node is None:
74
+ fanin["cpuif_rd_data"] = "'0"
75
+ else:
76
+ # Use intermediate signals for interface arrays to avoid
77
+ # non-constant indexing of interface arrays in procedural blocks
78
+ if self.is_interface and node.is_array and node.array_dimensions:
79
+ # Generate array index string [i0][i1]... for the intermediate signal
80
+ array_idx = "".join(f"[i{i}]" for i in range(len(node.array_dimensions)))
81
+ fanin["cpuif_rd_data"] = f"{node.inst_name}_fanin_data{array_idx}"
82
+ else:
83
+ fanin["cpuif_rd_data"] = self.signal("PRDATA", node, "i")
84
+
85
+ return "\n".join(map(lambda kv: f"{kv[0]} = {kv[1]};", fanin.items()))
86
+
87
+ def fanin_intermediate_assignments(
88
+ self, node: AddressableNode, inst_name: str, array_idx: str, master_prefix: str, indexed_path: str
89
+ ) -> list[str]:
90
+ """Generate intermediate signal assignments for APB4 interface arrays."""
91
+ return [
92
+ f"assign {inst_name}_fanin_ready{array_idx} = {master_prefix}{indexed_path}.PREADY;",
93
+ f"assign {inst_name}_fanin_err{array_idx} = {master_prefix}{indexed_path}.PSLVERR;",
94
+ f"assign {inst_name}_fanin_data{array_idx} = {master_prefix}{indexed_path}.PRDATA;",
95
+ ]
@@ -1,21 +1,21 @@
1
- from typing import TYPE_CHECKING, overload
1
+ from typing import TYPE_CHECKING
2
2
 
3
3
  from systemrdl.node import AddressableNode
4
4
 
5
5
  from ...utils import get_indexed_path
6
6
  from ..base_cpuif import BaseCpuif
7
- from .apb4_interface import APB4SVInterface
7
+ from .apb4_interface import APB4FlatInterface
8
8
 
9
9
  if TYPE_CHECKING:
10
10
  from ...exporter import BusDecoderExporter
11
11
 
12
12
 
13
- class APB4Cpuif(BaseCpuif):
13
+ class APB4CpuifFlat(BaseCpuif):
14
14
  template_path = "apb4_tmpl.sv"
15
15
 
16
16
  def __init__(self, exp: "BusDecoderExporter") -> None:
17
17
  super().__init__(exp)
18
- self._interface = APB4SVInterface(self)
18
+ self._interface = APB4FlatInterface(self)
19
19
 
20
20
  @property
21
21
  def is_interface(self) -> bool:
@@ -23,15 +23,15 @@ class APB4Cpuif(BaseCpuif):
23
23
 
24
24
  @property
25
25
  def port_declaration(self) -> str:
26
- """Returns the port declaration for the APB4 interface."""
27
- return self._interface.get_port_declaration("s_apb", "m_apb_")
26
+ return self._interface.get_port_declaration("s_apb_", "m_apb_")
28
27
 
29
- @overload
30
- def signal(self, signal: str, node: None = None, indexer: None = None) -> str: ...
31
- @overload
32
- def signal(self, signal: str, node: AddressableNode, indexer: str) -> str: ...
33
- def signal(self, signal: str, node: AddressableNode | None = None, indexer: str | None = None) -> str:
34
- return self._interface.signal(signal, node, indexer)
28
+ def signal(
29
+ self,
30
+ signal: str,
31
+ node: AddressableNode | None = None,
32
+ idx: str | int | None = None,
33
+ ) -> str:
34
+ return self._interface.signal(signal, node, idx)
35
35
 
36
36
  def fanout(self, node: AddressableNode) -> str:
37
37
  fanout: dict[str, str] = {}
@@ -29,6 +29,13 @@ assign {{cpuif.signal("PSLVERR")}} = cpuif_rd_err | cpuif_rd_sel.cpuif_err | cpu
29
29
  // Fanout CPU Bus interface signals
30
30
  //--------------------------------------------------------------------------
31
31
  {{fanout|walk(cpuif=cpuif)}}
32
+ {%- if cpuif.is_interface %}
33
+
34
+ //--------------------------------------------------------------------------
35
+ // Intermediate signals for interface array fanin
36
+ //--------------------------------------------------------------------------
37
+ {{fanin_intermediate|walk(cpuif=cpuif)}}
38
+ {%- endif %}
32
39
 
33
40
  //--------------------------------------------------------------------------
34
41
  // Fanin CPU Bus interface signals
@@ -4,14 +4,14 @@ from systemrdl.node import AddressableNode
4
4
 
5
5
  from ...utils import get_indexed_path
6
6
  from ..base_cpuif import BaseCpuif
7
- from .axi4lite_interface import AXI4LiteSVInterface
7
+ from .axi4_lite_interface import AXI4LiteSVInterface
8
8
 
9
9
  if TYPE_CHECKING:
10
10
  from ...exporter import BusDecoderExporter
11
11
 
12
12
 
13
13
  class AXI4LiteCpuif(BaseCpuif):
14
- template_path = "axi4lite_tmpl.sv"
14
+ template_path = "axi4_lite_tmpl.sv"
15
15
 
16
16
  def __init__(self, exp: "BusDecoderExporter") -> None:
17
17
  super().__init__(exp)
@@ -68,9 +68,17 @@ class AXI4LiteCpuif(BaseCpuif):
68
68
  fanin["cpuif_rd_ack"] = "'0"
69
69
  fanin["cpuif_rd_err"] = "'0"
70
70
  else:
71
- # Read side: ack comes from RVALID; err if RRESP[1] is set (SLVERR/DECERR)
72
- fanin["cpuif_rd_ack"] = self.signal("RVALID", node, "i")
73
- fanin["cpuif_rd_err"] = f"{self.signal('RRESP', node, 'i')}[1]"
71
+ # Use intermediate signals for interface arrays to avoid
72
+ # non-constant indexing of interface arrays in procedural blocks
73
+ if self.is_interface and node.is_array and node.array_dimensions:
74
+ # Generate array index string [i0][i1]... for the intermediate signal
75
+ array_idx = "".join(f"[i{i}]" for i in range(len(node.array_dimensions)))
76
+ fanin["cpuif_rd_ack"] = f"{node.inst_name}_fanin_ready{array_idx}"
77
+ fanin["cpuif_rd_err"] = f"{node.inst_name}_fanin_err{array_idx}"
78
+ else:
79
+ # Read side: ack comes from RVALID; err if RRESP[1] is set (SLVERR/DECERR)
80
+ fanin["cpuif_rd_ack"] = self.signal("RVALID", node, "i")
81
+ fanin["cpuif_rd_err"] = f"{self.signal('RRESP', node, 'i')}[1]"
74
82
 
75
83
  return "\n".join(f"{lhs} = {rhs};" for lhs, rhs in fanin.items())
76
84
 
@@ -79,6 +87,23 @@ class AXI4LiteCpuif(BaseCpuif):
79
87
  if node is None:
80
88
  fanin["cpuif_rd_data"] = "'0"
81
89
  else:
82
- fanin["cpuif_rd_data"] = self.signal("RDATA", node, "i")
90
+ # Use intermediate signals for interface arrays to avoid
91
+ # non-constant indexing of interface arrays in procedural blocks
92
+ if self.is_interface and node.is_array and node.array_dimensions:
93
+ # Generate array index string [i0][i1]... for the intermediate signal
94
+ array_idx = "".join(f"[i{i}]" for i in range(len(node.array_dimensions)))
95
+ fanin["cpuif_rd_data"] = f"{node.inst_name}_fanin_data{array_idx}"
96
+ else:
97
+ fanin["cpuif_rd_data"] = self.signal("RDATA", node, "i")
83
98
 
84
99
  return "\n".join(f"{lhs} = {rhs};" for lhs, rhs in fanin.items())
100
+
101
+ def fanin_intermediate_assignments(
102
+ self, node: AddressableNode, inst_name: str, array_idx: str, master_prefix: str, indexed_path: str
103
+ ) -> list[str]:
104
+ """Generate intermediate signal assignments for AXI4-Lite interface arrays."""
105
+ return [
106
+ f"assign {inst_name}_fanin_ready{array_idx} = {master_prefix}{indexed_path}.RVALID;",
107
+ f"assign {inst_name}_fanin_err{array_idx} = {master_prefix}{indexed_path}.RRESP[1];",
108
+ f"assign {inst_name}_fanin_data{array_idx} = {master_prefix}{indexed_path}.RDATA;",
109
+ ]
@@ -4,7 +4,7 @@ from systemrdl.node import AddressableNode
4
4
 
5
5
  from ...utils import get_indexed_path
6
6
  from ..base_cpuif import BaseCpuif
7
- from .axi4lite_interface import AXI4LiteFlatInterface
7
+ from .axi4_lite_interface import AXI4LiteFlatInterface
8
8
 
9
9
  if TYPE_CHECKING:
10
10
  from ...exporter import BusDecoderExporter
@@ -13,7 +13,7 @@ if TYPE_CHECKING:
13
13
  class AXI4LiteCpuifFlat(BaseCpuif):
14
14
  """Verilator-friendly variant that flattens the AXI4-Lite interface ports."""
15
15
 
16
- template_path = "axi4lite_tmpl.sv"
16
+ template_path = "axi4_lite_tmpl.sv"
17
17
 
18
18
  def __init__(self, exp: "BusDecoderExporter") -> None:
19
19
  super().__init__(exp)
@@ -53,6 +53,13 @@ assign {{cpuif.signal("BRESP")}} = (cpuif_wr_err | cpuif_wr_sel.cpuif_err | cpu
53
53
  // Fanout CPU Bus interface signals
54
54
  //--------------------------------------------------------------------------
55
55
  {{fanout|walk(cpuif=cpuif)}}
56
+ {%- if cpuif.is_interface %}
57
+
58
+ //--------------------------------------------------------------------------
59
+ // Intermediate signals for interface array fanin
60
+ //--------------------------------------------------------------------------
61
+ {{fanin_intermediate|walk(cpuif=cpuif)}}
62
+ {%- endif %}
56
63
 
57
64
  //--------------------------------------------------------------------------
58
65
  // Fanin CPU Bus interface signals
@@ -7,6 +7,7 @@ from systemrdl.node import AddressableNode
7
7
 
8
8
  from ..utils import clog2, get_indexed_path, is_pow2, roundup_pow2
9
9
  from .fanin_gen import FaninGenerator
10
+ from .fanin_intermediate_gen import FaninIntermediateGenerator
10
11
  from .fanout_gen import FanoutGenerator
11
12
 
12
13
  if TYPE_CHECKING:
@@ -24,11 +25,7 @@ class BaseCpuif:
24
25
 
25
26
  @property
26
27
  def addressable_children(self) -> list[AddressableNode]:
27
- return [
28
- child
29
- for child in self.exp.ds.top_node.children(unroll=self.unroll)
30
- if isinstance(child, AddressableNode)
31
- ]
28
+ return self.exp.ds.get_addressable_children_at_depth(unroll=self.unroll)
32
29
 
33
30
  @property
34
31
  def addr_width(self) -> int:
@@ -97,6 +94,7 @@ class BaseCpuif:
97
94
  "ds": self.exp.ds,
98
95
  "fanout": FanoutGenerator,
99
96
  "fanin": FaninGenerator,
97
+ "fanin_intermediate": FaninIntermediateGenerator,
100
98
  }
101
99
 
102
100
  template = jj_env.get_template(self.template_path)
@@ -116,3 +114,24 @@ class BaseCpuif:
116
114
 
117
115
  def readback(self, node: AddressableNode | None = None) -> str:
118
116
  raise NotImplementedError
117
+
118
+ def fanin_intermediate_assignments(
119
+ self, node: AddressableNode, inst_name: str, array_idx: str, master_prefix: str, indexed_path: str
120
+ ) -> list[str]:
121
+ """Generate intermediate signal assignments for interface array fanin.
122
+
123
+ This method should be implemented by cpuif classes that use interfaces.
124
+ It returns a list of assignment strings that copy signals from interface
125
+ arrays to intermediate unpacked arrays using constant (genvar) indexing.
126
+
127
+ Args:
128
+ node: The addressable node
129
+ inst_name: Instance name for the intermediate signals
130
+ array_idx: Array index string (e.g., "[gi0][gi1]")
131
+ master_prefix: Master interface prefix
132
+ indexed_path: Indexed path to the interface element
133
+
134
+ Returns:
135
+ List of assignment strings
136
+ """
137
+ return [] # Default: no intermediate assignments needed
@@ -27,6 +27,17 @@ class FaninGenerator(BusDecoderListener):
27
27
  def enter_AddressableComponent(self, node: AddressableNode) -> WalkerAction | None:
28
28
  action = super().enter_AddressableComponent(node)
29
29
 
30
+ should_generate = action == WalkerAction.SkipDescendants
31
+ if not should_generate and self._ds.max_decode_depth == 0:
32
+ for child in node.children():
33
+ if isinstance(child, AddressableNode):
34
+ break
35
+ else:
36
+ should_generate = True
37
+
38
+ if not should_generate:
39
+ return action
40
+
30
41
  if node.array_dimensions:
31
42
  for i, dim in enumerate(node.array_dimensions):
32
43
  fb = ForLoopBody(