peakrdl-busdecoder 0.7.2__tar.gz → 0.7.3__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 (118) hide show
  1. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/PKG-INFO +1 -1
  2. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/docs/api.rst +1 -1
  3. peakrdl_busdecoder-0.7.3/docs/architecture.rst +90 -0
  4. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/docs/limitations.rst +3 -9
  5. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/pyproject.toml +1 -1
  6. peakrdl_busdecoder-0.7.3/spec/audit-docs.md +60 -0
  7. peakrdl_busdecoder-0.7.3/spec/audit-impl.md +128 -0
  8. peakrdl_busdecoder-0.7.3/spec/busdecoder.md +218 -0
  9. peakrdl_busdecoder-0.7.3/spec/peakrdl.md +106 -0
  10. peakrdl_busdecoder-0.7.3/spec/systemrdl.md +215 -0
  11. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/src/peakrdl_busdecoder/cpuif/apb3/apb3_cpuif.py +12 -7
  12. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/src/peakrdl_busdecoder/cpuif/apb4/apb4_cpuif.py +12 -7
  13. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/src/peakrdl_busdecoder/cpuif/axi4lite/axi4_lite_cpuif.py +16 -10
  14. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/src/peakrdl_busdecoder/cpuif/base_cpuif.py +4 -3
  15. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/src/peakrdl_busdecoder/cpuif/fanin_gen.py +0 -7
  16. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/src/peakrdl_busdecoder/cpuif/fanin_intermediate_gen.py +6 -5
  17. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/src/peakrdl_busdecoder/cpuif/fanout_gen.py +0 -6
  18. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/src/peakrdl_busdecoder/cpuif/interface.py +12 -4
  19. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/src/peakrdl_busdecoder/decode_logic_gen.py +14 -13
  20. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/src/peakrdl_busdecoder/design_state.py +56 -26
  21. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/src/peakrdl_busdecoder/listener.py +3 -19
  22. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/src/peakrdl_busdecoder/module_tmpl.sv +4 -2
  23. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/src/peakrdl_busdecoder/package_tmpl.sv +2 -2
  24. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/src/peakrdl_busdecoder/validate_design.py +1 -32
  25. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/src/peakrdl_busdecoder.egg-info/PKG-INFO +1 -1
  26. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/src/peakrdl_busdecoder.egg-info/SOURCES.txt +5 -0
  27. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/uv.lock +29 -27
  28. peakrdl_busdecoder-0.7.2/docs/architecture.rst +0 -54
  29. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/.devcontainer/Dockerfile +0 -0
  30. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/.devcontainer/devcontainer.json +0 -0
  31. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  32. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  33. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/.github/ISSUE_TEMPLATE/question.md +0 -0
  34. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/.github/pull_request_template.md +0 -0
  35. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/.github/workflows/build.yml +0 -0
  36. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/.github/workflows/docs.yml +0 -0
  37. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/.github/workflows/format.yml +0 -0
  38. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/.github/workflows/lint.yml +0 -0
  39. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/.github/workflows/release.yml +0 -0
  40. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/.github/workflows/test.yml +0 -0
  41. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/.github/workflows/typecheck.yml +0 -0
  42. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/.gitignore +0 -0
  43. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/.readthedocs.yaml +0 -0
  44. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/CONTRIBUTING.md +0 -0
  45. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/LICENSE +0 -0
  46. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/MANIFEST.in +0 -0
  47. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/README.md +0 -0
  48. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/docs/Makefile +0 -0
  49. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/docs/conf.py +0 -0
  50. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/docs/configuring.rst +0 -0
  51. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/docs/cpuif/apb.rst +0 -0
  52. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/docs/cpuif/axi4lite.rst +0 -0
  53. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/docs/cpuif/customizing.rst +0 -0
  54. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/docs/cpuif/internal_protocol.rst +0 -0
  55. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/docs/cpuif/introduction.rst +0 -0
  56. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/docs/dev_notes/Alpha-Beta Versioning +0 -0
  57. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/docs/dev_notes/Hierarchy-and-Indexing +0 -0
  58. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/docs/dev_notes/Program Flow +0 -0
  59. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/docs/dev_notes/Resets +0 -0
  60. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/docs/dev_notes/Signal Dereferencer +0 -0
  61. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/docs/dev_notes/Validation Needed +0 -0
  62. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/docs/dev_notes/template-layers/1-port-declaration +0 -0
  63. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/docs/dev_notes/template-layers/1.1.hardware-interface +0 -0
  64. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/docs/dev_notes/template-layers/2-CPUIF +0 -0
  65. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/docs/dev_notes/template-layers/3-address-decode +0 -0
  66. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/docs/dev_notes/template-layers/4-fields +0 -0
  67. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/docs/dev_notes/template-layers/5-readback-mux +0 -0
  68. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/docs/dev_notes/template-layers/6-output-port-mapping +0 -0
  69. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/docs/diagrams/arch.png +0 -0
  70. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/docs/diagrams/diagrams.odg +0 -0
  71. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/docs/diagrams/rbuf.png +0 -0
  72. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/docs/diagrams/readback.png +0 -0
  73. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/docs/diagrams/wbuf.png +0 -0
  74. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/docs/img/err.svg +0 -0
  75. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/docs/img/ok.svg +0 -0
  76. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/docs/img/warn.svg +0 -0
  77. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/docs/index.rst +0 -0
  78. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/docs/licensing.rst +0 -0
  79. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/docs/parameters.rst +0 -0
  80. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/docs/requirements.txt +0 -0
  81. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/hdl-src/README.md +0 -0
  82. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/hdl-src/apb3_intf.sv +0 -0
  83. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/hdl-src/apb4_intf.sv +0 -0
  84. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/hdl-src/avalon_mm_intf.sv +0 -0
  85. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/hdl-src/axi4lite_intf.sv +0 -0
  86. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/setup.cfg +0 -0
  87. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/src/peakrdl_busdecoder/__init__.py +0 -0
  88. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/src/peakrdl_busdecoder/__peakrdl__.py +0 -0
  89. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/src/peakrdl_busdecoder/body/__init__.py +0 -0
  90. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/src/peakrdl_busdecoder/body/body.py +0 -0
  91. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/src/peakrdl_busdecoder/body/combinational_body.py +0 -0
  92. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/src/peakrdl_busdecoder/body/for_loop_body.py +0 -0
  93. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/src/peakrdl_busdecoder/body/if_body.py +0 -0
  94. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/src/peakrdl_busdecoder/body/struct_body.py +0 -0
  95. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/src/peakrdl_busdecoder/cpuif/__init__.py +0 -0
  96. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/src/peakrdl_busdecoder/cpuif/apb3/__init__.py +0 -0
  97. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/src/peakrdl_busdecoder/cpuif/apb3/apb3_interface.py +0 -0
  98. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/src/peakrdl_busdecoder/cpuif/apb3/apb3_tmpl.sv +0 -0
  99. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/src/peakrdl_busdecoder/cpuif/apb4/__init__.py +0 -0
  100. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/src/peakrdl_busdecoder/cpuif/apb4/apb4_interface.py +0 -0
  101. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/src/peakrdl_busdecoder/cpuif/apb4/apb4_tmpl.sv +0 -0
  102. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/src/peakrdl_busdecoder/cpuif/axi4lite/__init__.py +0 -0
  103. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/src/peakrdl_busdecoder/cpuif/axi4lite/axi4_lite_interface.py +0 -0
  104. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/src/peakrdl_busdecoder/cpuif/axi4lite/axi4_lite_tmpl.sv +0 -0
  105. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/src/peakrdl_busdecoder/design_scanner.py +0 -0
  106. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/src/peakrdl_busdecoder/exporter.py +0 -0
  107. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/src/peakrdl_busdecoder/identifier_filter.py +0 -0
  108. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/src/peakrdl_busdecoder/py.typed +0 -0
  109. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/src/peakrdl_busdecoder/rdl_params.py +0 -0
  110. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/src/peakrdl_busdecoder/struct_gen.py +0 -0
  111. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/src/peakrdl_busdecoder/sv_int.py +0 -0
  112. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/src/peakrdl_busdecoder/udps/__init__.py +0 -0
  113. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/src/peakrdl_busdecoder/utils.py +0 -0
  114. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/src/peakrdl_busdecoder.egg-info/dependency_links.txt +0 -0
  115. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/src/peakrdl_busdecoder.egg-info/entry_points.txt +0 -0
  116. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/src/peakrdl_busdecoder.egg-info/requires.txt +0 -0
  117. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/src/peakrdl_busdecoder.egg-info/top_level.txt +0 -0
  118. {peakrdl_busdecoder-0.7.2 → peakrdl_busdecoder-0.7.3}/tools/shims/xargs +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: peakrdl-busdecoder
3
- Version: 0.7.2
3
+ Version: 0.7.3
4
4
  Summary: Generate a SystemVerilog bus decoder from SystemRDL for splitting CPU interfaces to multiple sub-address spaces
5
5
  Author: Arnav Sacheti
6
6
  License: LGPLv3
@@ -13,7 +13,7 @@ Below is a simple example that demonstrates how to generate a SystemVerilog
13
13
  implementation from SystemRDL source.
14
14
 
15
15
  .. code-block:: python
16
- :emphasize-lines: 2-4, 29-33
16
+ :emphasize-lines: 3-5, 25-30
17
17
 
18
18
  import sys
19
19
 
@@ -0,0 +1,90 @@
1
+ Bus Decoder Architecture
2
+ ========================
3
+
4
+ The generated RTL is a pure bus-routing layer. It accepts a single CPU interface
5
+ on the slave side and fans transactions out to a set of child interfaces on the
6
+ master side. No register storage or field logic is generated.
7
+
8
+ Although you do not need to know the inner workings to use the exporter, the
9
+ sections below explain the structure of the generated module and how it maps to
10
+ SystemRDL hierarchy.
11
+
12
+
13
+ CPU Interface Adapter
14
+ ---------------------
15
+ Each supported CPU interface protocol (APB3, APB4, AXI4-Lite) provides a small
16
+ adapter that translates the external bus protocol into internal request/response
17
+ signals. These internal signals are then used by the address decoder and fanout
18
+ logic.
19
+
20
+ If you write a custom CPU interface, it must implement the internal signals
21
+ described in :ref:`cpuif_protocol`.
22
+
23
+
24
+ Address Decode
25
+ --------------
26
+ The address decoder computes per-child select signals based on address ranges.
27
+ The decode boundary is controlled by ``max_decode_depth``:
28
+
29
+ * ``0``: Decode down to addressable leaves, including registers and external memories
30
+ * ``1`` (default): Decode only top-level children
31
+ * ``N``: Decode down to depth ``N`` from the top-level
32
+
33
+ This allows you to choose whether the bus decoder routes to large blocks (e.g.,
34
+ child addrmaps) or to smaller sub-blocks.
35
+
36
+ Port collection, target naming, select generation, and fanout/fanin use the same
37
+ decode-boundary decision. A container whose addressable children are all external
38
+ remains an early boundary, preserving the existing treatment of opaque blocks.
39
+
40
+ When the decode boundary is a register, read requests select it only if a
41
+ present field is software-readable; write requests select it only if a present
42
+ field is software-writable. This separates legal read-only/write-only register
43
+ overlaps at one address. A disallowed direction returns a local unmapped error.
44
+ Block targets remain opaque address regions and forward both directions.
45
+
46
+ Decode bounds and downstream addresses are relative to the exported addrmap,
47
+ including when the Python API exports an inner addrmap with a nonzero system
48
+ base address. Array stride gaps select no child and return a local error rather
49
+ than waiting indefinitely for a downstream response.
50
+
51
+
52
+ Fanout to Child Interfaces
53
+ --------------------------
54
+ For each decoded child, the bus decoder drives a master-side CPU interface.
55
+ All address, data, and control signals are forwarded to the selected child.
56
+
57
+ Arrayed children can be kept as arrays or unrolled into discrete interfaces using
58
+ ``--unroll``. This only affects port structure and naming; decode semantics are
59
+ unchanged.
60
+
61
+ Target naming
62
+ ~~~~~~~~~~~~~
63
+ A target keeps its local SystemRDL instance name when that name is unique among
64
+ the decoded targets. Uniqueness is checked after converting to uppercase, since
65
+ package constants and array-count localparams use uppercase identifiers.
66
+
67
+ When local names collide, the exporter uses the target's complete path relative
68
+ to the exported top-level addrmap, replacing hierarchy separators with
69
+ underscores. For example, ``blk_a.ctrl`` and ``blk_b.ctrl`` become
70
+ ``blk_a_ctrl`` and ``blk_b_ctrl``. Existing unique local names are reserved first.
71
+ If flattened paths still collide with each other or with a reserved name,
72
+ ``__2``, ``__3``, and so on are appended until the name is unique. These names
73
+ are allocated in sorted relative-path order, independently of traversal order.
74
+ Array indices are not part of this base identifier.
75
+
76
+ The same resolved identifier is used for APB3, APB4, and AXI4-Lite interface and
77
+ flat master ports, their fanout and fanin references, interface-array
78
+ intermediate signals, ``<MODULE>_<TARGET>_ADDR_WIDTH`` package constants, and
79
+ ``N_<TARGET>S`` array-count localparams. The internal select structures retain
80
+ their hierarchical member paths; they are not flattened to match the ports.
81
+
82
+
83
+ Fanin and Error Handling
84
+ ------------------------
85
+ Read and write responses are muxed back from the selected child to the slave
86
+ interface. If no child is selected for a transaction, the decoder generates an
87
+ error response on the slave interface.
88
+
89
+ The exact error signaling depends on the chosen CPU interface protocol (e.g.,
90
+ ``PSLVERR`` for APB, ``RRESP/BRESP`` for AXI4-Lite).
@@ -17,15 +17,9 @@ If a register is wider than its ``accesswidth`` (a multi-word register), its
17
17
  ``accesswidth`` must match the CPU interface data width. Multi-word registers
18
18
  with a smaller accesswidth are not supported.
19
19
 
20
-
21
- Fields Spanning Sub-Words
22
- -------------------------
23
- If a field spans multiple sub-words of a wide register:
24
-
25
- * Software-writable fields must have write buffering enabled
26
- * Fields with ``onread`` side-effects must have read buffering enabled
27
-
28
- These rules are enforced to avoid ambiguous multi-word access behavior.
20
+ Field storage, read side-effects and multi-word access atomicity are owned by
21
+ the downstream register block. The decoder does not require field read/write
22
+ buffering properties; it forwards each bus transfer independently.
29
23
 
30
24
 
31
25
  External Boundary References
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "peakrdl-busdecoder"
7
- version = "0.7.2"
7
+ version = "0.7.3"
8
8
  requires-python = ">=3.10"
9
9
  dependencies = [
10
10
  "jinja2~=3.1",
@@ -0,0 +1,60 @@
1
+ # Documentation vs. Formal Specification Audit
2
+
3
+ This audit evaluates the consistency and completeness of the documentation (`docs/**/*.rst`, `README.md`) against the formal specification (`spec/busdecoder.md`, `spec/systemrdl.md`, `spec/peakrdl.md`).
4
+
5
+ Every finding is assigned an ID (`DOC-FINDING-*`), categorized as **Compliant**, **Divergent** (doc contradicts formal spec), **Incomplete** (doc omits critical formal rule), or **Stale** (doc contains obsolete copy-paste).
6
+
7
+ ## Follow-up status
8
+
9
+ The findings below retain the original audit evidence, not current source line numbers. The field-buffering restriction has been removed from `docs/limitations.rst` and the exporter; downstream blocks own field atomicity. The decoder's default-miss implementation now covers array stride gaps, preserving the documented unmapped-error contract rather than documenting the deadlock as supported behavior. Local submap addressing and direction-specific register routing are covered by the follow-up implementation and RTL conformance sweeps. Depth-policy and Avalon support observations remain separate, unresolved items; this patch does not add Avalon support.
10
+
11
+ ---
12
+
13
+ ## 1. Compliance Matrix
14
+
15
+ | Requirement | Doc Location | Documented Claim | Formal Spec | Status | Action Needed |
16
+ | :--- | :--- | :--- | :--- | :--- | :--- |
17
+ | **BD-ARCH-PURE-ROUTER** | `docs/index.rst:7-9` | *"strictly a bus-routing layer that decodes addresses and forwards requests to child blocks"* | BD-ARCH-PURE-ROUTER | **Compliant** | None. |
18
+ | **BD-ARCH-LOCAL-ADDR** | `docs/cpuif/introduction.rst:19-22` | *"The busdecoder exporter will always generate its address decoding logic using local address offsets."* | BD-ARCH-LOCAL-ADDR | **Compliant** | None. |
19
+ | **BD-CFG-CPUIF** | `docs/index.rst:21` | Lists `apb3`, `apb4`, `axi4-lite` + flat variants | BD-CFG-CPUIF | **Compliant** | None. |
20
+ | **BD-CFG-DATA-WIDTH** | `docs/cpuif/introduction.rst:10-13` | *"equal to the widest accesswidth encountered in the design. If the exported addrmap contains only external components... default to 32 bits"* | BD-CFG-DATA-WIDTH | **Incomplete** | Doc fails to note that only *internal* registers are scanned; child addrmaps (which are implicitly external) are ignored even if their inner registers have wider accesswidth. |
21
+ | **BD-CFG-MAX-DEPTH** | `docs/architecture.rst:19-24` | `0`: all levels; `1`: top-level children; `N`: depth $N$ | BD-CFG-MAX-DEPTH | **Incomplete** | Doc does not define whether leaves at depth $< N$ are preserved or dropped when $N > 1$. |
22
+ | **BD-DEC-UNMAPPED-ERROR** | `docs/architecture.rst:39-44`, `docs/cpuif/apb.rst:12-15` | Unmapped addresses complete with error response (PSLVERR / RRESP) | BD-DEC-UNMAPPED-ERROR | **Compliant** | None. |
23
+ | **BD-DEC-GAP-ERROR** | Not documented | — | BD-DEC-GAP-ERROR | **Divergent** | Docs imply all unmapped addresses error; neither docs nor RTL handle stride gaps in arrays. |
24
+ | **BD-AXIL-COUPLED-WRITE** | `docs/cpuif/axi4lite.rst:29-31` | *"AW and W channels must be asserted together for writes"* | BD-AXIL-COUPLED-WRITE | **Compliant** | None. |
25
+ | **BD-AXIL-NO-DECERR** | `docs/architecture.rst:44` | Mentions `RRESP/BRESP` | BD-AXIL-NO-DECERR | **Incomplete** | Doc omits that only `SLVERR` (`2'b10`) is returned, never `DECERR` (`2'b11`). |
26
+ | **Field Buffering** | `docs/limitations.rst:24-29` | Multi-word registers with fields spanning subwords must enable write/read buffering UDPs | N/A (regblock copy-paste) | **Stale / Erroneous** | BusDecoder does not synthesize fields or storage; read/write buffering UDPs are meaningless for a pure decoder. |
27
+ | **Avalon-MM** | `hdl-src/avalon_mm_intf.sv` | Interface file exists in repo | BD-CFG-CPUIF | **Omission** | Avalon-MM interface definition exists in source tree but is completely absent from docs and unreferenced by the exporter. |
28
+
29
+ ---
30
+
31
+ ## 2. Detailed Findings
32
+
33
+ ### DOC-FINDING-01: Stale Limitations Carried Over from PeakRDL-regblock
34
+ - **Location:** `docs/limitations.rst:24-29`
35
+ - **Text:**
36
+ > Software-writable fields that span across multiple software-accessible sub-words must have write double-buffering enabled.
37
+ > Fields that have an onread side-effect and span across multiple software-accessible sub-words must have read double-buffering enabled.
38
+ - **Problem:** PeakRDL-BusDecoder is a bus routing layer (`BD-ARCH-PURE-ROUTER`). It does not generate registers, flip-flops, or field logic. Demanding write/read double-buffering UDPs on a bus decoder is a verbatim copy-paste error from `PeakRDL-regblock`.
39
+ - **Required Change:** Delete lines 24–29 of `docs/limitations.rst` and the corresponding check in `src/peakrdl_busdecoder/validate_design.py:126-143`.
40
+
41
+ ### DOC-FINDING-02: Silent Failure Mode in Array Stride Gaps
42
+ - **Location:** `docs/cpuif/apb.rst:12-15`, `docs/architecture.rst:39-44`
43
+ - **Problem:** The documentation states:
44
+ > An unmapped address completes with an error in the first access cycle. The decoder asserts PREADY and PSLVERR together only when PSEL and PENABLE are high...
45
+ However, for an array with `stride > size`, an address falling in the gap matches the outer array bound in the generated `if` statement, but matches no loop body `if` branch. The decoder defaults all selects to 0 and asserts `cpuif_err = 0`. On APB, this leaves `PREADY = 0` forever, **hanging the bus** rather than returning `PSLVERR`.
46
+ - **Required Change:** Document the stride gap behavior and add a fallback error assignment in `decode_logic_gen.py`.
47
+
48
+ ### DOC-FINDING-03: Ambiguous Depth Semantics for Mixed Hierarchies
49
+ - **Location:** `docs/architecture.rst:19-24`
50
+ - **Problem:** The documentation specifies:
51
+ > - 0: Decode all the way down to leaf registers
52
+ > - 1: Decode only top-level children
53
+ > - N: Decode down to depth N from the top-level
54
+ In a design where one branch terminates in a register at depth 1 while another branch contains sub-addrmaps down to depth 3, the behavior of `--max-decode-depth 2` on the shallow branch is unspecified in the documentation.
55
+ - **Required Change:** Clarify that decode targets are collected at depth $N$, or at the first leaf encountered along a branch if depth $< N$.
56
+
57
+ ### DOC-FINDING-04: Undocumented Omission of Avalon-MM CPUIF
58
+ - **Location:** `docs/index.rst:21`, `hdl-src/avalon_mm_intf.sv`
59
+ - **Problem:** The repository ships an `avalon_mm_intf.sv` SystemVerilog interface file in `hdl-src/`, but neither `docs/index.rst` nor `src/peakrdl_busdecoder/cpuif/` implement an Avalon-MM adapter class. Users reading `hdl-src/` reasonably expect Avalon-MM support.
60
+ - **Required Change:** Either document Avalon-MM as a roadmap item / unimplemented interface template, or remove `avalon_mm_intf.sv` from the active source tree.
@@ -0,0 +1,128 @@
1
+ # Implementation vs. Specification Audit
2
+
3
+ This audit evaluates the actual Python codebase (`src/peakrdl_busdecoder/**`) and generated SystemVerilog templates against the formal specification (`spec/busdecoder.md`, `spec/systemrdl.md`, `spec/peakrdl.md`) and the project documentation.
4
+
5
+ Every finding is assigned an ID (`IMPL-FINDING-*`), categorized by severity (**Critical** = functional bug / bus deadlock, **Major** = spec violation, **Minor** = non-fatal inconsistency, **Deadweight** = dead/obsolete code).
6
+
7
+ The detailed findings preserve pre-fix evidence and historical line numbers. The follow-up patch implements findings 01–05; the table tracks current status. This is finite-design RTL conformance testing, not an unbounded proof of every SystemRDL design or AXI temporal behavior.
8
+
9
+ Verification: `PYTHONPATH=src uv run pytest tests -q` completed with **306 passed, 1 skipped**, including cocotb and standalone Verilator regressions. `uv run ruff check src/peakrdl_busdecoder tests/formal` passed. RTL sweeps cover sparse arrays, both directions, idle/setup/access phases, child stalls/errors, readback and local offsets, exported submaps, wide fields, and both RO/WO declaration orders. Naming/depth runtime tests cover APB3/APB4/AXI4-Lite, flat/interface ports, collisions and depth-zero external memories.
10
+
11
+ ---
12
+
13
+ ## 1. Compliance Summary Table
14
+
15
+ | Spec ID | Impl Location | Status | Summary of Divergence |
16
+ | :--- | :--- | :--- | :--- |
17
+ | **BD-DEC-GAP-ERROR** | `module_tmpl.sv`, `decode_logic_gen.py` | **IMPLEMENTED** | Active requests default to error; both scalar and array-element hits clear error. Idle stays zero. Oracle sweeps cover every address in the sparse-array fixture. |
18
+ | **BD-CFG-MAX-DEPTH** | `design_state.py:is_decode_boundary`, `listener.py:should_skip_node` | **BOUNDARY CONSISTENCY VERIFIED** | Collector and RTL generators share one boundary decision, including external memory leaves at depth 0. Existing early boundaries for containers with all-external children and positive-depth shallow-leaf semantics are unchanged; the proposed shallow-leaf-preservation requirement remains outside these fixes. |
19
+ | **BD-NAME-PORTS** | `design_state.py:get_target_name`, `cpuif/interface.py`, `package_tmpl.sv` | **VERIFIED FOR REGRESSION CASES** | Shared target identifiers preserve unique local names and disambiguate collisions using relative paths plus deterministic suffixes, including uppercase and flattened-path collisions. Behavioral compile/runtime regressions passed across all six built-in protocol/style combinations. |
20
+ | **BD-ARCH-LOCAL-ADDR** | `decode_logic_gen.py`, `cpuif/base_cpuif.py`, protocol fanout generators | **IMPLEMENTED** | Bounds, subtraction and truncation decisions use offsets relative to the exported top; a nonzero-base exported submap exercises non-power-of-two targets. |
21
+ | **BD-DEC-EXCLUSIVITY** | `decode_logic_gen.py:_access_predicate` | **IMPLEMENTED** | Register selects are direction-filtered for scalar and array targets. Both declaration orders of overlapping RO/WO registers are exercised; denied directions return a local error. |
22
+ | **BD-CFG-DATA-WIDTH** | `design_scanner.py` | **SCOPED CONTRACT** | Inference intentionally stops at external boundaries, matching BD-CFG-DATA-WIDTH's internal-scope rule. A deeper opaque block's width is not inferred; this is not a general width-conversion implementation. |
23
+ | **BD-AXIL-NO-DECERR** | `axi4_lite_tmpl.sv:51, 56` | **COMPLIANT** | Correctly maps all error paths to `SLVERR` (`2'b10`). |
24
+ | **BD-APB-ENABLE-GATE** | `apb3_cpuif.py:53`, `apb4_cpuif.py:53` | **COMPLIANT** | Master PENABLE is strictly ANDed with target select signal. |
25
+ | **Field buffering checks** | `validate_design.py`, `docs/limitations.rst` | **REMOVED** | Field atomicity belongs to the downstream block. The RTL sweep exports a writable/onread 64-bit field with 32-bit accesswidth without buffering UDPs. |
26
+
27
+ ---
28
+
29
+ ## 2. Detailed Findings and Recommended Changes
30
+
31
+ ### IMPL-FINDING-01: Array Stride Gap Causes Bus Deadlock (CRITICAL)
32
+ - **Files:** `src/peakrdl_busdecoder/decode_logic_gen.py:112-159`, `src/peakrdl_busdecoder/module_tmpl.sv:65-90`
33
+ - **Specification:** `BD-DEC-GAP-ERROR`
34
+ - **Evidence:**
35
+ In `tests/cocotb_lib/rdl/array_only.rdl`:
36
+ ```systemrdl
37
+ dummy_regs dummy_regs[4] @0x00 += 0x80; // size = 4, stride = 0x80
38
+ ```
39
+ The generated SystemVerilog decode logic in `module_tmpl.sv` is:
40
+ ```systemverilog
41
+ always_comb begin
42
+ cpuif_wr_sel = '{default: '0};
43
+ if (cpuif_req && cpuif_wr_en) begin
44
+ for (int i0 = 0; i0 < 4; i0++) begin
45
+ if ((cpuif_wr_addr >= 9'(9'h0+(i0*9'h80))) && (cpuif_wr_addr < 9'(9'h4+(i0*9'h80)))) begin
46
+ cpuif_wr_sel.dummy_regs[i0] = 1'b1;
47
+ end
48
+ end
49
+ end
50
+ end
51
+ ```
52
+ - **Consequence:**
53
+ If an access is made to address `0x10` (inside the stride gap $[0x04, 0x80)$):
54
+ 1. The `if (cpuif_wr_addr >= ...)` condition inside the loop evaluates to `0` for all $i_0 \in [0, 3]$.
55
+ 2. All select signals remain `0`.
56
+ 3. `cpuif_wr_sel.cpuif_err` remains `0` (the unmapped fallback `else` branch is outside the loop or omitted).
57
+ 4. On APB, `PREADY` is generated as `cpuif_wr_ack | cpuif_rd_ack`. Because neither a child nor the unmapped error handler is selected, `PREADY` stays `0` indefinitely.
58
+ 5. **The bus hangs permanently.**
59
+ - **Required Fix:**
60
+ Add a reduction check across array element hits: if the address falls within the overall array bounds $[0, 4 \times 0x80)$ but matches no element, explicitly assert `cpuif_<flavor>_sel.cpuif_err = 1'b1;`.
61
+
62
+ ---
63
+
64
+ ### IMPL-FINDING-02: Non-Relative Address Decode on Sub-Addrmap Exports (MAJOR)
65
+ - **Files:** `src/peakrdl_busdecoder/decode_logic_gen.py:45-48`
66
+ - **Specification:** `BD-ARCH-LOCAL-ADDR`
67
+ - **Evidence:**
68
+ ```python
69
+ l_bound = SVInt(
70
+ node.raw_absolute_address,
71
+ addr_width,
72
+ )
73
+ ```
74
+ `node.raw_absolute_address` returns the byte offset from the *root of the compilation unit*, not the root of the exported addrmap.
75
+ - **Consequence:**
76
+ If a user passes an inner sub-addrmap to `BusDecoderExporter.export(sub_node, ...)` via the Python API, where `sub_node` is at offset `0x1000` in the parent chip map:
77
+ - The generated decoder will expect incoming addresses to start at `0x1000`.
78
+ - But `BD-ARCH-LOCAL-ADDR` and `docs/cpuif/introduction.rst:19-22` state:
79
+ > *"The busdecoder exporter will always generate its address decoding logic using local address offsets."*
80
+ - The generated decoder will reject all valid local accesses in $[0, \text{size}(\text{sub\_node}))$.
81
+ - **Required Fix:**
82
+ Replace `node.raw_absolute_address` with the relative offset from `self._ds.top_node`:
83
+ ```python
84
+ rel_addr = node.raw_absolute_address - self._ds.top_node.raw_absolute_address
85
+ l_bound = SVInt(rel_addr, addr_width)
86
+ ```
87
+
88
+ ---
89
+
90
+ ### IMPL-FINDING-03: Port Name Collisions at Deep Hierarchies (IMPLEMENTED; VERIFICATION PENDING)
91
+ - **Files:** `src/peakrdl_busdecoder/design_state.py`, `src/peakrdl_busdecoder/cpuif/interface.py`, `src/peakrdl_busdecoder/cpuif/base_cpuif.py`, `src/peakrdl_busdecoder/cpuif/fanin_intermediate_gen.py`, the APB3/APB4/AXI4-Lite CPU adapters, and `src/peakrdl_busdecoder/package_tmpl.sv`
92
+ - **Specification:** `BD-NAME-PORTS`, `BD-NAME-PACKAGE`
93
+ - **Original defect:** At decode depth 2, `blk_a.ctrl` and `blk_b.ctrl` both emitted `m_apb_ctrl`. The same local-name-only construction also collided in flat ports, package address-width constants, array-count localparams, and interface-array response intermediates.
94
+ - **Implemented resolution:**
95
+ - `DesignState` allocates a shared target identifier before rendering either package or module. Identity is the path relative to the exported top, independent of array indices and Python node wrappers.
96
+ - Unique local names are preserved. Uniqueness is checked after uppercase conversion so package constants and array-count localparams remain distinct as well.
97
+ - Conflicting local names use the flattened full relative path: `blk_a_ctrl` and `blk_b_ctrl`. Unique legacy names are reserved before allocating qualified names.
98
+ - Flattened-path or uppercase collisions receive deterministic `__2`, `__3`, ... suffixes in sorted relative-path order.
99
+ - All APB3/APB4/AXI4-Lite flat/interface declarations and references, fanout width references, fanin intermediates, package constants, and array-count localparams use that identifier. Package constants are emitted once per rolled target identity.
100
+ - Hierarchical select-structure member paths remain unchanged.
101
+ - Port collection and RTL listeners share `DesignState.is_decode_boundary`; depth-zero external memory leaves therefore receive both declarations and resolved names rather than a dictionary fallback.
102
+ - **Regression coverage:** `tests/formal/test_target_naming.py` compiles and runs generated RTL for duplicate `ctrl` targets under two blocks at depth 2, scalar and rolled-array targets, all three protocols, and both flat and interface ports. It also exercises uppercase and flattened-path collisions, preservation of an existing unique name, and depth-zero external memory alongside internal register leaves. The tests use independently driven target responses and hardcoded public port/constant names, not generated-source string assertions.
103
+ - **Verification status:** These regressions were added but not executed during fix 4. Runtime/compile compliance remains pending the integration verification run.
104
+
105
+ ---
106
+
107
+ ### IMPL-FINDING-04: Sub-Word Buffering Validations are Deadweight (DEADWEIGHT)
108
+ - **Files:** `src/peakrdl_busdecoder/validate_design.py:126-143`
109
+ - **Specification:** `BD-ARCH-PURE-ROUTER`
110
+ - **Evidence:**
111
+ `DesignValidator` checks whether software-writable fields spanning multiple subwords have `rdl_write_buffering` UDPs, and whether on-read fields have `rdl_read_buffering`.
112
+ - **Consequence:**
113
+ This check blocks valid SystemRDL designs from generating a bus decoder simply because they contain wide fields, even though a bus decoder never handles subword slicing (it merely forwards transfers to the downstream register block that owns the fields).
114
+ - **Required Fix:**
115
+ Delete lines 126–143 of `validate_design.py`.
116
+
117
+ ---
118
+
119
+ ### IMPL-FINDING-05: Missing Independent Read/Write Decode for Overlapping Registers (MINOR)
120
+ - **Files:** `src/peakrdl_busdecoder/decode_logic_gen.py:94-131`
121
+ - **Specification:** `BD-DEC-EXCLUSIVITY`, SystemRDL Clause 10.1-h
122
+ - **Evidence:**
123
+ SystemRDL 2.0 explicitly permits two registers to occupy the same address offset if one is read-only and the other is write-only.
124
+ However, `DecodeLogicGenerator` treats read and write decoding identically (`enter_AddressableComponent` does not check `is_sw_readable` or `is_sw_writable`). It emits an `if ... else if ...` ladder based on tree order alone.
125
+ - **Consequence:**
126
+ In a design with legal RO/WO overlapping registers, a read request will hit whichever register is visited first, even if that register is write-only!
127
+ - **Required Fix:**
128
+ In `DecodeLogicGenerator.cpuif_addr_predicate()`, gate read decoding with `node.has_sw_readable` and write decoding with `node.has_sw_writable`.
@@ -0,0 +1,218 @@
1
+ # PeakRDL-BusDecoder — Formal Specification
2
+
3
+ This specification defines the behavior, interface contracts, protocol invariants, and address-decoding mathematics of the **PeakRDL-BusDecoder** generator.
4
+
5
+ Every normative statement is assigned a unique identifier `BD-*`. Downstream audits and verification harnesses reference these identifiers directly.
6
+
7
+ ---
8
+
9
+ ## 1. Top-Level Architectural Contract
10
+
11
+ ```
12
+ +---------------------------------------+
13
+ | PeakRDL-BusDecoder |
14
+ | |
15
+ Slave CPUIF | +-------------+ +---------------+ | Master CPUIF 0
16
+ (APB3/4, AXI4-L) ===> | Slave CPUIF |-->| Write Decoder |--===> (Child 0)
17
+ | | Adapter | +---------------+ |
18
+ | | | | Master CPUIF 1
19
+ | | | +---------------+ |===> (Child 1)
20
+ | | |-->| Read Decoder |--+
21
+ | | | +---------------+ | ...
22
+ | | |<--| Readback Mux |--+ Master CPUIF K-1
23
+ | +-------------+ +---------------+ |===> (Child K-1)
24
+ +---------------------------------------+
25
+ ```
26
+
27
+ - **BD-ARCH-PURE-ROUTER**: The generated RTL shall be a pure routing and bus-translation layer containing no software register storage, no field-level logic, and no flip-flops unless required by protocol pipelining.
28
+ - **BD-ARCH-LOCAL-ADDR**: The decoder shall operate strictly on addresses relative to the root of the exported address map. The base address of the exported root addrmap within the system interconnect is defined to be 0.
29
+ - **BD-ARCH-SV-STD**: Generated RTL shall conform to IEEE 1800-2012 SystemVerilog and synthesize without errors on major EDA toolchains.
30
+
31
+ ---
32
+
33
+ ## 2. Configuration and Exporter Parameters
34
+
35
+ - **BD-CFG-CPUIF**: The exporter shall support the following standard CPU interface targets:
36
+ - `apb3`: AMBA APB v3 using SystemVerilog interface (`apb3_intf.sv`).
37
+ - `apb3-flat`: AMBA APB v3 flattened into discrete scalar/vector ports.
38
+ - `apb4`: AMBA APB v4 using SystemVerilog interface (`apb4_intf.sv`).
39
+ - `apb4-flat`: AMBA APB v4 flattened into discrete scalar/vector ports.
40
+ - `axi4-lite`: AMBA AXI4-Lite using SystemVerilog interface (`axi4lite_intf.sv`).
41
+ - `axi4-lite-flat`: AMBA AXI4-Lite flattened into discrete scalar/vector ports.
42
+ - Plugin interfaces registered via entry points under `peakrdl_busdecoder.cpuif`.
43
+ - **BD-CFG-DEFAULT-CPUIF**: If unspecified, the CPU interface shall default to `apb4`.
44
+ - **BD-CFG-DATA-WIDTH**:
45
+ - The CPU interface data bus width $W_{\text{data}}$ shall be inferred as the maximum `accesswidth` of any register reachable within the internal scope of the exported addrmap.
46
+ - If no internal register exists (e.g. only external blocks), $W_{\text{data}}$ shall default to 32 bits and emit a warning.
47
+ - All master interfaces shall have data width equal to $W_{\text{data}}$.
48
+ - **BD-CFG-ADDR-WIDTH**:
49
+ - The minimum address width $W_{\text{addr,min}}$ shall satisfy:
50
+ $$W_{\text{addr,min}} = \max\left( \lceil\log_2(\text{size}(c_{\text{top}}))\rceil,\; \lceil\log_2(W_{\text{data}} / 8)\rceil + 1 \right)$$
51
+ - The user may override $W_{\text{addr}} \ge W_{\text{addr,min}}$. If the user specifies $W_{\text{addr}} < W_{\text{addr,min}}$, export shall abort with a fatal error.
52
+ - **BD-CFG-MAX-DEPTH**:
53
+ - `max_decode_depth = 0`: The decoder shall descend to leaf registers ($\text{RegNode}$) across all nested blocks.
54
+ - `max_decode_depth = 1` (default): The decoder shall decode only immediate children of the top-level addrmap.
55
+ - `max_decode_depth = N` ($N > 1$): The decoder shall decode children positioned at depth $N$ in the instance tree.
56
+ - Leaves occurring at depth $< N$ shall also be preserved as decode targets. *(Formal requirement; see Audit for current divergence).*
57
+ - **BD-CFG-UNROLL**:
58
+ - When `cpuif_unroll = false` (default), arrayed children shall be exported as SystemVerilog interface arrays (`[dim0][dim1]...` or `[N]`) and iterated procedurally with `for` loops.
59
+ - When `cpuif_unroll = true`, arrayed children shall be expanded into distinct scalar master interfaces with index suffixes `_<idx0>_<idx1>`.
60
+ - **BD-CFG-PARAMETRIZE**:
61
+ - When `--parametrize` is asserted, root-level RDL parameters that control array dimensions shall be extracted and emitted as SystemVerilog module parameters.
62
+ - The module parameter default value shall be the elaborated maximum.
63
+ - Package constant `<MODULE>_MAX_<PARAM>` shall be defined.
64
+ - Runtime range assertion `assert (PARAM >= 0 && PARAM <= MAX)` shall be emitted.
65
+
66
+ ---
67
+
68
+ ## 3. Child Interface Target Set ($\mathcal{T}$)
69
+
70
+ Let $c_{\text{top}}$ be the exported addrmap. A component instance $t$ belongs to the decode target set $\mathcal{T}$ if and only if:
71
+
72
+ 1. $t \in \mathcal{A} \land t \ne c_{\text{top}}$.
73
+ 2. $t$ is an ancestor of no other target $t' \in \mathcal{T}$ (targets are mutually disjoint subtrees).
74
+ 3. Selection rule:
75
+ - If $\text{max\_decode\_depth} = 0$: $t \in \text{RegNode} \lor (t \in \text{MemNode} \land \text{virtual\_regs}(t) = \emptyset) \lor (t.\text{external} \land \text{is\_leaf}(t))$.
76
+ - If $\text{max\_decode\_depth} = N \ge 1$: $\text{depth}(t) = N \lor (\text{depth}(t) < N \land t \in \text{RegNode})$.
77
+
78
+ Each target $t \in \mathcal{T}$ defines:
79
+ - A local address span within $c_{\text{top}}$:
80
+ - If scalar: $S(t) = [\text{rel\_offset}(t),\; \text{rel\_offset}(t) + \text{size}(t))$.
81
+ - If arrayed: for element $\vec{j}$, $S(t, \vec{j}) = [\text{elem\_rel\_offset}(t, \vec{j}),\; \text{elem\_rel\_offset}(t, \vec{j}) + \text{size}(t))$.
82
+ - A master port on the generated decoder.
83
+ - Master address width $W_{\text{addr}}(t) = \max(\lceil\log_2(\text{size}(t))\rceil, 1)$.
84
+
85
+ ---
86
+
87
+ ## 4. Address Decoding Formal Model
88
+
89
+ Let $A \in [0, 2^{W_{\text{addr}}} - 1]$ be the incoming transaction byte address, and $D \in \{\text{READ}, \text{WRITE}\}$ the transaction flavor.
90
+
91
+ ### 4.1 Target Match Predicate
92
+ For target $t \in \mathcal{T}$ and element index tuple $\vec{j}$:
93
+
94
+ $$\text{match}(t, \vec{j}, A) \iff A \ge \text{elem\_rel\_offset}(t, \vec{j}) \;\land\; A < \text{elem\_rel\_offset}(t, \vec{j}) + \text{size}(t)$$
95
+
96
+ $$\text{match}(t, A) \iff \exists \vec{j} \in \prod_{m=0}^{k-1} [0, d_m-1] : \text{match}(t, \vec{j}, A)$$
97
+
98
+ ### 4.2 Select Signal Requirements
99
+ Let $\text{sel}_{D}(t, \vec{j}) \in \{0, 1\}$ denote the select signal asserted by the decoder for flavor $D$, and $\text{err}_{D} \in \{0, 1\}$ the unmapped error signal.
100
+
101
+ - **BD-DEC-SOUNDNESS (No Spurious Select):**
102
+ $$\text{sel}_{D}(t, \vec{j}) = 1 \implies \text{match}(t, \vec{j}, A)$$
103
+ - **BD-DEC-EXCLUSIVITY (At Most One Select):**
104
+ $$\sum_{t \in \mathcal{T}} \sum_{\vec{j}} \text{sel}_{D}(t, \vec{j}) \le 1$$
105
+ *(If legal RO/WO overlap exists at address $A$, the read decoder selects the readable register and the write decoder selects the writable register independently).*
106
+ - **BD-DEC-COMPLETENESS (Mapped Hit):**
107
+ For an active request of direction $D$, each select is asserted exactly when its element matches and permits that direction:
108
+ $$\text{sel}_{D}(t,\vec{j}) = \text{match}(t,\vec{j},A) \land \text{permits}(t,D).$$
109
+ For register targets, `permits(READ)` is `has_sw_readable` and `permits(WRITE)` is `has_sw_writable`, considering present fields. Blocks are routed as opaque regions in both directions; the downstream block enforces its own permissions.
110
+ - **BD-DEC-UNMAPPED-ERROR (Error on Miss):**
111
+ For an active request, including an access with no direction-permitted register:
112
+ $$\text{err}_{D} = \neg\bigvee_{t,\vec{j}}\text{sel}_{D}(t,\vec{j}).$$
113
+ This default-miss invariant also applies inside nested arrays and to parameter-disabled elements. When idle, both selects and error remain zero.
114
+ - **BD-DEC-IDLE:**
115
+ When no transaction of flavor $D$ is requested, all select and error signals for flavor $D$ shall be 0:
116
+ $$\neg \text{req}_D \implies \forall t, \vec{j}: \text{sel}_{D}(t, \vec{j}) = 0 \;\land\; \text{err}_{D} = 0$$
117
+ - **BD-DEC-GAP-ERROR:**
118
+ For any address $A$ that falls within an array's total span $[\text{raw\_offset}(t), \text{raw\_offset}(t) + \text{total\_size}(t))$ but does not satisfy $\text{match}(t, \vec{j}, A)$ for any element $\vec{j}$ (an array stride gap), the decoder shall assert $\text{err}_D = 1$ and all selects 0.
119
+
120
+ ### 4.3 Master Address Forwarding
121
+ For selected target $t$ and element index $\vec{j}$:
122
+ - **BD-FWD-ADDR-LOCAL**: The master address driven to child $t$ shall be the byte offset relative to element $(t, \vec{j})$:
123
+ $$A_{\text{master}}(t) = A - \text{elem\_rel\_offset}(t, \vec{j})$$
124
+ - **BD-FWD-ADDR-TRUNC**: If $\text{size}(t) = 2^p$ and $\text{elem\_rel\_offset}(t, \vec{j}) \equiv 0 \pmod{2^p}$ for all $\vec{j}$, the decoder may implement $A_{\text{master}}(t)$ as bit-truncation:
125
+ $$A_{\text{master}}(t) = A[p-1:0]$$
126
+ Otherwise, subtractive arithmetic must be emitted.
127
+
128
+ ---
129
+
130
+ ## 5. Protocol Handshake and Invariant Specifications
131
+
132
+ ### 5.1 Internal CPUIF Interface Protocol
133
+ All protocol adapters convert slave transactions into the internal CPUIF signals:
134
+ - Request: `cpuif_req`, `cpuif_wr_en`, `cpuif_rd_en`, `cpuif_wr_addr`, `cpuif_rd_addr`, `cpuif_wr_data`, `cpuif_wr_byte_en`.
135
+ - Response: `cpuif_wr_ack`, `cpuif_wr_err`, `cpuif_rd_ack`, `cpuif_rd_err`, `cpuif_rd_data`.
136
+
137
+ **Invariants:**
138
+ - **BD-PROTO-SINGLE-FLIGHT**: At most one transaction (read or write) shall be in flight through the internal CPUIF at any clock cycle:
139
+ $$\text{cpuif\_req} \implies (\text{cpuif\_wr\_en} \oplus \text{cpuif\_rd\_en})$$
140
+ - **BD-PROTO-REQ-STABLE**: Once asserted, `cpuif_req`, `cpuif_wr_en`, `cpuif_rd_en`, addresses, and write data shall remain stable until the qualifying acknowledgment (`cpuif_wr_ack` or `cpuif_rd_ack`) is asserted.
141
+ - **BD-PROTO-ACK-STROBE**: Acknowledgments `cpuif_wr_ack` and `cpuif_rd_ack` shall be single-cycle pulses.
142
+ - **BD-PROTO-ACK-DATA**: Read data `cpuif_rd_data` and error flag `cpuif_rd_err` are valid and sampled on the cycle of `cpuif_rd_ack`.
143
+
144
+ ---
145
+
146
+ ### 5.2 APB3 and APB4 Adapter Semantics
147
+
148
+ Let $P$ be the slave APB interface ($s\_apb$) and $M_t$ the master APB interface for target $t$.
149
+
150
+ #### Request Phase Mapping
151
+ - `cpuif_req = s_apb.PSEL`
152
+ - `cpuif_wr_en = s_apb.PSEL & s_apb.PWRITE`
153
+ - `cpuif_rd_en = s_apb.PSEL & ~s_apb.PWRITE`
154
+ - `cpuif_wr_addr = s_apb.PADDR`
155
+ - `cpuif_rd_addr = s_apb.PADDR`
156
+ - `cpuif_wr_data = s_apb.PWDATA`
157
+ - APB4: `cpuif_wr_byte_en = s_apb.PSTRB`
158
+
159
+ #### Master Forwarding Rules
160
+ - **BD-APB-SEL-GATE**: For each target $t$:
161
+ $$M_t.\text{PSEL} = \text{sel}_{\text{wr}}(t) \lor \text{sel}_{\text{rd}}(t)$$
162
+ - **BD-APB-ENABLE-GATE**:
163
+ $$M_t.\text{PENABLE} = (\text{sel}_{\text{wr}}(t) \lor \text{sel}_{\text{rd}}(t)) \land s\_apb.\text{PENABLE}$$
164
+ *(Crucial bugfix from v0.7.0b3/v0.7.1: unselected master PENABLE must be forced low to prevent spurious downstream state transitions).*
165
+ - **BD-APB-BROADCAST**:
166
+ $M_t.\text{PWRITE} = s\_apb.\text{PWRITE}$, $M_t.\text{PWDATA} = s\_apb.\text{PWDATA}$, $M_t.\text{PPROT} = s\_apb.\text{PPROT}$, $M_t.\text{PSTRB} = s\_apb.\text{PSTRB}$.
167
+
168
+ #### Response and Error Timing
169
+ - **BD-APB-MAPPED-RESP**: When $\text{sel}(t) = 1$:
170
+ $$s\_apb.\text{PREADY} = M_t.\text{PREADY}$$
171
+ $$s\_apb.\text{PSLVERR} = M_t.\text{PSLVERR}$$
172
+ $$s\_apb.\text{PRDATA} = M_t.\text{PRDATA} \quad (\text{if read})$$
173
+ - **BD-APB-UNMAPPED-RESP**: When $\text{err}_{\text{wr}} = 1 \lor \text{err}_{\text{rd}} = 1$:
174
+ - Setup cycle ($s\_apb.\text{PSEL} = 1 \land s\_apb.\text{PENABLE} = 0$):
175
+ $$s\_apb.\text{PREADY} = 0, \quad s\_apb.\text{PSLVERR} = 0$$
176
+ - Access cycle ($s\_apb.\text{PSEL} = 1 \land s\_apb.\text{PENABLE} = 1$):
177
+ $$s\_apb.\text{PREADY} = 1, \quad s\_apb.\text{PSLVERR} = 1$$
178
+ - This guarantees a standard 2-cycle unmapped APB error termination without hanging the bus.
179
+
180
+ ---
181
+
182
+ ### 5.3 AXI4-Lite Adapter Semantics
183
+
184
+ Let $AXI$ be the slave AXI4-Lite interface.
185
+
186
+ - **BD-AXIL-COUPLED-WRITE**: The write channel adapter shall require `AWVALID` and `WVALID` to be asserted together:
187
+ $$\text{axi\_wr\_valid} = \text{AWVALID} \land \text{WVALID}$$
188
+ $$\text{AWREADY} = \text{axi\_wr\_valid}, \quad \text{WREADY} = \text{axi\_wr\_valid}$$
189
+ - **BD-AXIL-DECOUPLED-ERR**: If `AWVALID ^ WVALID` occurs, the adapter shall immediately terminate the invalid request with `BVALID = 1` and `BRESP = 2'b10` (`SLVERR`).
190
+ - **BD-AXIL-READ-CHANNEL**:
191
+ $$\text{ARREADY} = \text{ARVALID}$$
192
+ $$\text{RVALID} = \text{cpuif\_rd\_ack} \lor \text{cpuif\_rd\_sel.cpuif\_err}$$
193
+ $$\text{RRESP} = (\text{cpuif\_rd\_err} \lor \text{cpuif\_rd\_sel.cpuif\_err}) \;?\; 2'b10 : 2'b00$$
194
+ - **BD-AXIL-WRITE-CHANNEL**:
195
+ $$\text{BVALID} = \text{cpuif\_wr\_ack} \lor \text{cpuif\_wr\_sel.cpuif\_err} \lor \text{axi\_wr\_invalid}$$
196
+ $$\text{BRESP} = (\text{cpuif\_wr\_err} \lor \text{cpuif\_wr\_sel.cpuif\_err} \lor \text{axi\_wr\_invalid}) \;?\; 2'b10 : 2'b00$$
197
+ - **BD-AXIL-NO-DECERR**: All error responses on AXI4-Lite shall be signaled as `SLVERR` (`2'b10`). `DECERR` (`2'b11`) is never emitted.
198
+
199
+ ---
200
+
201
+ ## 6. Naming and Structural Contracts
202
+
203
+ - **BD-NAME-KW-FILTER**: Any identifier matching a SystemVerilog keyword shall be appended with an underscore `_`.
204
+ - **BD-NAME-PACKAGE**:
205
+ - The generated package shall be named `<package_name>.sv`.
206
+ - Constants emitted:
207
+ - `<MODULE>_DATA_WIDTH`
208
+ - `<MODULE>_MIN_ADDR_WIDTH`
209
+ - `<MODULE>_SIZE`
210
+ - `<MODULE>_<TARGET>_ADDR_WIDTH` for every target.
211
+ - `<MODULE>_MAX_<PARAM>` for extracted RDL parameters.
212
+ - **BD-NAME-PORTS**:
213
+ - Slave ports: prefixed with `s_apb_` or `s_axil_`.
214
+ - Master ports: prefixed with `m_apb_<target>_` or `m_axil_<target>_`.
215
+ - Port disambiguation: when targets share names across hierarchies or nested arrays, port names must uniquely identify the target. *(Formal requirement; see Audit for collision risks).*
216
+ - **BD-STRUCT-SEL**:
217
+ - The select typedef `cpuif_sel_t` shall reflect the hierarchy down to each target.
218
+ - An explicit `logic cpuif_err;` field shall be included at the root level of `cpuif_sel_t`.