tan-cli 0.7.0__py3-none-any.whl

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 (330) hide show
  1. tan/__init__.py +0 -0
  2. tan/__main__.py +120 -0
  3. tan/cli.py +939 -0
  4. tan/command_table.py +265 -0
  5. tan/commands/__init__.py +8 -0
  6. tan/commands/bootstrap_cmd.py +4856 -0
  7. tan/commands/bootstrap_patches.py +164 -0
  8. tan/commands/bootstrap_toolchain_fetch.py +155 -0
  9. tan/commands/bootstrap_toolchain_pin.py +36 -0
  10. tan/commands/build/__init__.py +1 -0
  11. tan/commands/build/configure_inputs.py +277 -0
  12. tan/commands/build/execute.py +1852 -0
  13. tan/commands/build/host_python.py +59 -0
  14. tan/commands/build/link_stale.py +78 -0
  15. tan/commands/build/manifest.py +428 -0
  16. tan/commands/build/materialise.py +81 -0
  17. tan/commands/build/token_substitution.py +381 -0
  18. tan/commands/build/toolchain.py +482 -0
  19. tan/commands/build/user_defines_wipe.py +115 -0
  20. tan/commands/build/workspace_patches.py +65 -0
  21. tan/commands/build_cmd.py +2236 -0
  22. tan/commands/build_output.py +331 -0
  23. tan/commands/clean_cmd.py +1183 -0
  24. tan/commands/completion_cmd.py +786 -0
  25. tan/commands/debug_config_cmd.py +2425 -0
  26. tan/commands/diff_cmd.py +940 -0
  27. tan/commands/doctor_cmd.py +4767 -0
  28. tan/commands/examples_cmd.py +644 -0
  29. tan/commands/explain_cmd.py +1319 -0
  30. tan/commands/faultdecode_cmd.py +931 -0
  31. tan/commands/flash_atoc_guard.py +240 -0
  32. tan/commands/flash_cmd.py +5731 -0
  33. tan/commands/flash_mram_guard.py +150 -0
  34. tan/commands/flash_ram.py +578 -0
  35. tan/commands/flash_raw.py +583 -0
  36. tan/commands/generate_cmd.py +1456 -0
  37. tan/commands/image_cmd.py +753 -0
  38. tan/commands/init_cmd.py +1740 -0
  39. tan/commands/inspect_cmd.py +377 -0
  40. tan/commands/kconfig_cmd.py +734 -0
  41. tan/commands/model_cmd.py +1514 -0
  42. tan/commands/model_device_cmd.py +351 -0
  43. tan/commands/model_device_live.py +234 -0
  44. tan/commands/model_host_cmd.py +430 -0
  45. tan/commands/model_zoo_cmd.py +363 -0
  46. tan/commands/monitor_actions.py +125 -0
  47. tan/commands/monitor_cmd.py +626 -0
  48. tan/commands/monitor_session.py +505 -0
  49. tan/commands/new_som_cmd.py +1522 -0
  50. tan/commands/pinmux_cmd.py +568 -0
  51. tan/commands/presets_cmd.py +1123 -0
  52. tan/commands/probe_cmd.py +421 -0
  53. tan/commands/reset_cmd.py +354 -0
  54. tan/commands/reset_confirm.py +168 -0
  55. tan/commands/run_cmd.py +637 -0
  56. tan/commands/scaffold_cmd.py +490 -0
  57. tan/commands/sdk_cmd.py +1723 -0
  58. tan/commands/size_cmd.py +1020 -0
  59. tan/commands/support_bundle_cmd.py +1073 -0
  60. tan/commands/trace_cmd.py +341 -0
  61. tan/commands/validate_cmd.py +2009 -0
  62. tan/commands/west_forward_cmd.py +736 -0
  63. tan/commands/workspace_patch_check.py +296 -0
  64. tan/core/__init__.py +1 -0
  65. tan/core/alp_conf_pregen.py +100 -0
  66. tan/core/artifact_provenance.py +240 -0
  67. tan/core/atoc_guard.py +276 -0
  68. tan/core/atoc_guard_messages.py +232 -0
  69. tan/core/atoc_replacement.py +118 -0
  70. tan/core/atomic_write.py +178 -0
  71. tan/core/board_context.py +351 -0
  72. tan/core/board_diagnostic.py +111 -0
  73. tan/core/board_files.py +221 -0
  74. tan/core/board_validator.py +542 -0
  75. tan/core/board_validator_compat.py +196 -0
  76. tan/core/board_validator_run.py +129 -0
  77. tan/core/board_validator_schema.py +186 -0
  78. tan/core/board_validator_skew.py +46 -0
  79. tan/core/board_yaml_edit.py +295 -0
  80. tan/core/board_yaml_pos.py +118 -0
  81. tan/core/bootstrap.py +2572 -0
  82. tan/core/build_plan.py +625 -0
  83. tan/core/consent.py +104 -0
  84. tan/core/console_filter.py +245 -0
  85. tan/core/debug_launch.py +1803 -0
  86. tan/core/devicetree_lint.py +168 -0
  87. tan/core/dir_removal.py +229 -0
  88. tan/core/doctor_git.py +172 -0
  89. tan/core/doctor_libraries.py +253 -0
  90. tan/core/doctor_render.py +185 -0
  91. tan/core/doctor_scope.py +66 -0
  92. tan/core/doctor_setools.py +237 -0
  93. tan/core/doctor_stale.py +180 -0
  94. tan/core/document_guards.py +723 -0
  95. tan/core/dp_id.py +97 -0
  96. tan/core/elf_load.py +63 -0
  97. tan/core/error_catalog.py +171 -0
  98. tan/core/example_catalog.py +499 -0
  99. tan/core/example_facets.py +151 -0
  100. tan/core/faultdecode.py +521 -0
  101. tan/core/flash_plan.py +2501 -0
  102. tan/core/flow_d_report.py +381 -0
  103. tan/core/flow_d_uptime.py +164 -0
  104. tan/core/fs_confine.py +130 -0
  105. tan/core/global_flags.py +230 -0
  106. tan/core/host_python.py +210 -0
  107. tan/core/image_bundle.py +247 -0
  108. tan/core/inert.py +147 -0
  109. tan/core/jlink_binary.py +128 -0
  110. tan/core/jlink_probe.py +481 -0
  111. tan/core/jsonc_splice.py +404 -0
  112. tan/core/launch_provenance.py +257 -0
  113. tan/core/link_refusal.py +76 -0
  114. tan/core/metadata_schema.py +376 -0
  115. tan/core/model_check.py +227 -0
  116. tan/core/model_device.py +449 -0
  117. tan/core/model_doctor.py +198 -0
  118. tan/core/model_host.py +48 -0
  119. tan/core/model_list.py +112 -0
  120. tan/core/model_zoo.py +353 -0
  121. tan/core/module_template.py +340 -0
  122. tan/core/mram_link.py +87 -0
  123. tan/core/new_som_ospi.py +94 -0
  124. tan/core/os_class.py +159 -0
  125. tan/core/patch_paths.py +87 -0
  126. tan/core/pending.py +40 -0
  127. tan/core/plain_zephyr_plan.py +138 -0
  128. tan/core/plan_exec.py +373 -0
  129. tan/core/plan_tokens.py +600 -0
  130. tan/core/probe.py +155 -0
  131. tan/core/probe_plan.py +237 -0
  132. tan/core/proxy.py +168 -0
  133. tan/core/publish.py +31 -0
  134. tan/core/python_floor.py +178 -0
  135. tan/core/ram_run.py +694 -0
  136. tan/core/ram_watch.py +231 -0
  137. tan/core/raw_write.py +160 -0
  138. tan/core/reset_plan.py +56 -0
  139. tan/core/run.py +161 -0
  140. tan/core/scaffold.py +2203 -0
  141. tan/core/scaffold_selftest_identity.py +225 -0
  142. tan/core/sdk_default_registry.py +565 -0
  143. tan/core/sdk_discovery.py +996 -0
  144. tan/core/sdk_removal.py +465 -0
  145. tan/core/serial_actions.py +175 -0
  146. tan/core/serial_capture.py +185 -0
  147. tan/core/serial_url.py +45 -0
  148. tan/core/setools.py +608 -0
  149. tan/core/setools_scratch.py +243 -0
  150. tan/core/shapes.py +194 -0
  151. tan/core/size.py +610 -0
  152. tan/core/som_buildability.py +204 -0
  153. tan/core/som_schema_version.py +62 -0
  154. tan/core/subprocess_env.py +201 -0
  155. tan/core/sysbuild_image.py +94 -0
  156. tan/core/system_manifest.py +827 -0
  157. tan/core/text_layout.py +73 -0
  158. tan/core/timestamp.py +182 -0
  159. tan/core/tool_lookup.py +383 -0
  160. tan/core/toolchain_pin.py +117 -0
  161. tan/core/toolchain_provision.py +1224 -0
  162. tan/core/trusted_path.py +134 -0
  163. tan/core/uboot_breakin.py +154 -0
  164. tan/core/uri_reference.py +415 -0
  165. tan/core/user_defines.py +152 -0
  166. tan/core/venv.py +332 -0
  167. tan/core/west_patches.py +143 -0
  168. tan/core/west_workspace_refusal.py +159 -0
  169. tan/core/zephyr_env.py +124 -0
  170. tan/env.py +185 -0
  171. tan/envelope.py +647 -0
  172. tan/exit_codes.py +12 -0
  173. tan/lazy_typer.py +185 -0
  174. tan/model/__init__.py +2 -0
  175. tan/model/_gen_fixture.py +132 -0
  176. tan/model/adapters/__init__.py +116 -0
  177. tan/model/adapters/cpu.py +26 -0
  178. tan/model/adapters/deepx.py +94 -0
  179. tan/model/adapters/drpai.py +242 -0
  180. tan/model/adapters/ethos_u.py +951 -0
  181. tan/model/adapters/executorch.py +55 -0
  182. tan/model/analyze.py +694 -0
  183. tan/model/build.py +304 -0
  184. tan/model/check.py +682 -0
  185. tan/model/manifest.py +625 -0
  186. tan/model/measure.py +216 -0
  187. tan/model/npu_ops_validate.py +130 -0
  188. tan/model/package.py +125 -0
  189. tan/model/perf.py +635 -0
  190. tan/model/perf_apply.py +721 -0
  191. tan/model/prep.py +193 -0
  192. tan/model/sram_fit.py +502 -0
  193. tan/model/targets.py +374 -0
  194. tan/model/tensorio.py +287 -0
  195. tan/net.py +73 -0
  196. tan/output_format.py +213 -0
  197. tan/planner/__init__.py +219 -0
  198. tan/planner/aperture.py +202 -0
  199. tan/planner/buildplan.py +1092 -0
  200. tan/planner/camera_owner.py +192 -0
  201. tan/planner/cameras.py +132 -0
  202. tan/planner/carveout.py +755 -0
  203. tan/planner/cli.py +274 -0
  204. tan/planner/headers.py +315 -0
  205. tan/planner/kconfig.py +2435 -0
  206. tan/planner/kconfig_symbols.py +473 -0
  207. tan/planner/libraries.py +677 -0
  208. tan/planner/link_target.py +237 -0
  209. tan/planner/linux_ownership.py +216 -0
  210. tan/planner/loader.py +1525 -0
  211. tan/planner/manifest.py +116 -0
  212. tan/planner/memory.py +161 -0
  213. tan/planner/memregion.py +56 -0
  214. tan/planner/models.py +533 -0
  215. tan/planner/orchestrator.py +874 -0
  216. tan/planner/ownership.py +203 -0
  217. tan/planner/partition.py +1160 -0
  218. tan/planner/paths.py +32 -0
  219. tan/planner/plain_slice.py +139 -0
  220. tan/planner/project_emit/__init__.py +60 -0
  221. tan/planner/project_emit/bom_netlist.py +375 -0
  222. tan/planner/project_emit/dts.py +580 -0
  223. tan/planner/project_emit/hw_info.py +221 -0
  224. tan/planner/project_emit/native_sim.py +136 -0
  225. tan/planner/project_emit/west_libs.py +213 -0
  226. tan/planner/project_loader.py +264 -0
  227. tan/planner/sdk_capability.py +203 -0
  228. tan/planner/sdk_compat.py +395 -0
  229. tan/planner/secure.py +350 -0
  230. tan/planner/slugs.py +394 -0
  231. tan/planner/som_metadata.py +325 -0
  232. tan/planner/strict_loaders.py +131 -0
  233. tan/planner/template.py +1037 -0
  234. tan/planner/template_pins.py +535 -0
  235. tan/planner/template_rewrite.py +714 -0
  236. tan/planner/topology.py +208 -0
  237. tan/planner/validate.py +365 -0
  238. tan/planner/whole_device_alias.py +67 -0
  239. tan/planner/zephyr_board.py +3510 -0
  240. tan/planner_cli.py +97 -0
  241. tan/planner_emit.py +617 -0
  242. tan/planner_root.py +119 -0
  243. tan/soc_ref.py +58 -0
  244. tan/templates/__init__.py +34 -0
  245. tan/templates/vendored/MANIFEST.md +1862 -0
  246. tan/templates/vendored/diagnostics/E1M-AEN801/CMakeLists.txt +8 -0
  247. tan/templates/vendored/diagnostics/E1M-AEN801/README.md +112 -0
  248. tan/templates/vendored/diagnostics/E1M-AEN801/board.yaml +38 -0
  249. tan/templates/vendored/diagnostics/E1M-AEN801/boards/native_sim_native_64.conf +11 -0
  250. tan/templates/vendored/diagnostics/E1M-AEN801/boards/native_sim_native_64.overlay +29 -0
  251. tan/templates/vendored/diagnostics/E1M-AEN801/prj.conf +5 -0
  252. tan/templates/vendored/diagnostics/E1M-AEN801/src/main.c +266 -0
  253. tan/templates/vendored/diagnostics/E1M-AEN801/testcase.yaml +53 -0
  254. tan/templates/vendored/diagnostics/E1M-V2N101/CMakeLists.txt +8 -0
  255. tan/templates/vendored/diagnostics/E1M-V2N101/README.md +112 -0
  256. tan/templates/vendored/diagnostics/E1M-V2N101/board.yaml +38 -0
  257. tan/templates/vendored/diagnostics/E1M-V2N101/boards/native_sim_native_64.conf +11 -0
  258. tan/templates/vendored/diagnostics/E1M-V2N101/boards/native_sim_native_64.overlay +29 -0
  259. tan/templates/vendored/diagnostics/E1M-V2N101/prj.conf +5 -0
  260. tan/templates/vendored/diagnostics/E1M-V2N101/src/main.c +266 -0
  261. tan/templates/vendored/diagnostics/E1M-V2N101/testcase.yaml +53 -0
  262. tan/templates/vendored/edge-ai/E1M-AEN801/CMakeLists.txt +10 -0
  263. tan/templates/vendored/edge-ai/E1M-AEN801/README.md +75 -0
  264. tan/templates/vendored/edge-ai/E1M-AEN801/board.yaml +38 -0
  265. tan/templates/vendored/edge-ai/E1M-AEN801/prj.conf +8 -0
  266. tan/templates/vendored/edge-ai/E1M-AEN801/src/cold_chain.c +371 -0
  267. tan/templates/vendored/edge-ai/E1M-AEN801/src/cold_chain.h +178 -0
  268. tan/templates/vendored/edge-ai/E1M-AEN801/src/main.c +373 -0
  269. tan/templates/vendored/edge-ai/E1M-AEN801/testcase.yaml +62 -0
  270. tan/templates/vendored/edge-ai/E1M-V2N101/CMakeLists.txt +10 -0
  271. tan/templates/vendored/edge-ai/E1M-V2N101/README.md +75 -0
  272. tan/templates/vendored/edge-ai/E1M-V2N101/board.yaml +37 -0
  273. tan/templates/vendored/edge-ai/E1M-V2N101/prj.conf +8 -0
  274. tan/templates/vendored/edge-ai/E1M-V2N101/src/cold_chain.c +371 -0
  275. tan/templates/vendored/edge-ai/E1M-V2N101/src/cold_chain.h +178 -0
  276. tan/templates/vendored/edge-ai/E1M-V2N101/src/main.c +373 -0
  277. tan/templates/vendored/edge-ai/E1M-V2N101/testcase.yaml +62 -0
  278. tan/templates/vendored/iot/E1M-AEN801/CMakeLists.txt +14 -0
  279. tan/templates/vendored/iot/E1M-AEN801/README.md +103 -0
  280. tan/templates/vendored/iot/E1M-AEN801/board.yaml +69 -0
  281. tan/templates/vendored/iot/E1M-AEN801/prj.conf +15 -0
  282. tan/templates/vendored/iot/E1M-AEN801/src/cc3501e_bridge.c +196 -0
  283. tan/templates/vendored/iot/E1M-AEN801/src/cc3501e_bridge.h +173 -0
  284. tan/templates/vendored/iot/E1M-AEN801/src/main.c +262 -0
  285. tan/templates/vendored/iot/E1M-AEN801/testcase.yaml +68 -0
  286. tan/templates/vendored/minimal/E1M-AEN801/CMakeLists.txt +8 -0
  287. tan/templates/vendored/minimal/E1M-AEN801/README.md +108 -0
  288. tan/templates/vendored/minimal/E1M-AEN801/board.yaml +32 -0
  289. tan/templates/vendored/minimal/E1M-AEN801/prj.conf +8 -0
  290. tan/templates/vendored/minimal/E1M-AEN801/src/main.c +111 -0
  291. tan/templates/vendored/minimal/E1M-AEN801/testcase.yaml +49 -0
  292. tan/templates/vendored/minimal/E1M-V2N101/CMakeLists.txt +8 -0
  293. tan/templates/vendored/minimal/E1M-V2N101/README.md +108 -0
  294. tan/templates/vendored/minimal/E1M-V2N101/board.yaml +32 -0
  295. tan/templates/vendored/minimal/E1M-V2N101/prj.conf +8 -0
  296. tan/templates/vendored/minimal/E1M-V2N101/src/main.c +111 -0
  297. tan/templates/vendored/minimal/E1M-V2N101/testcase.yaml +49 -0
  298. tan/templates/vendored/multicore-mailbox/E1M-AEN801/CMakeLists.txt +8 -0
  299. tan/templates/vendored/multicore-mailbox/E1M-AEN801/README.md +120 -0
  300. tan/templates/vendored/multicore-mailbox/E1M-AEN801/board.yaml +57 -0
  301. tan/templates/vendored/multicore-mailbox/E1M-AEN801/boards/native_sim_native_64.overlay +40 -0
  302. tan/templates/vendored/multicore-mailbox/E1M-AEN801/peer/CMakeLists.txt +13 -0
  303. tan/templates/vendored/multicore-mailbox/E1M-AEN801/peer/main.c +171 -0
  304. tan/templates/vendored/multicore-mailbox/E1M-AEN801/peer/prj.conf +17 -0
  305. tan/templates/vendored/multicore-mailbox/E1M-AEN801/peer/testcase.yaml +23 -0
  306. tan/templates/vendored/multicore-mailbox/E1M-AEN801/prj.conf +22 -0
  307. tan/templates/vendored/multicore-mailbox/E1M-AEN801/src/main.c +185 -0
  308. tan/templates/vendored/multicore-mailbox/E1M-AEN801/testcase.yaml +26 -0
  309. tan/templates/vendored/sensor/E1M-AEN801/CMakeLists.txt +8 -0
  310. tan/templates/vendored/sensor/E1M-AEN801/README.md +112 -0
  311. tan/templates/vendored/sensor/E1M-AEN801/board.yaml +57 -0
  312. tan/templates/vendored/sensor/E1M-AEN801/boards/native_sim_native_64.conf +10 -0
  313. tan/templates/vendored/sensor/E1M-AEN801/boards/native_sim_native_64.overlay +28 -0
  314. tan/templates/vendored/sensor/E1M-AEN801/prj.conf +5 -0
  315. tan/templates/vendored/sensor/E1M-AEN801/src/main.c +186 -0
  316. tan/templates/vendored/sensor/E1M-AEN801/testcase.yaml +52 -0
  317. tan/templates/vendored/sensor/E1M-V2N101/CMakeLists.txt +8 -0
  318. tan/templates/vendored/sensor/E1M-V2N101/README.md +112 -0
  319. tan/templates/vendored/sensor/E1M-V2N101/board.yaml +57 -0
  320. tan/templates/vendored/sensor/E1M-V2N101/boards/native_sim_native_64.conf +10 -0
  321. tan/templates/vendored/sensor/E1M-V2N101/boards/native_sim_native_64.overlay +28 -0
  322. tan/templates/vendored/sensor/E1M-V2N101/prj.conf +5 -0
  323. tan/templates/vendored/sensor/E1M-V2N101/src/main.c +186 -0
  324. tan/templates/vendored/sensor/E1M-V2N101/testcase.yaml +52 -0
  325. tan/version.py +102 -0
  326. tan_cli-0.7.0.dist-info/METADATA +125 -0
  327. tan_cli-0.7.0.dist-info/RECORD +330 -0
  328. tan_cli-0.7.0.dist-info/WHEEL +5 -0
  329. tan_cli-0.7.0.dist-info/entry_points.txt +2 -0
  330. tan_cli-0.7.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,580 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ """Zephyr DTS overlay emission (`--emit dts-overlay`).
3
+
4
+ Board-macro parsing (reads `include/alp/boards/<board>.h`) + the carrier
5
+ peripheral DT-wiring catalog + `_emit_dts_overlay` itself.
6
+
7
+ RELOCATED (was alp-sdk `scripts/alp_project_emit/dts.py`) -- see this package's
8
+ `__init__.py` for the move's contract. Two mechanical changes: `REPO` is the
9
+ BOUND SDK checkout (`paths.REPO`), so the board header still resolves out of
10
+ alp-sdk's `include/alp/boards/` and never out of `tan`; and the one `sys.exit`
11
+ became `raise OrchestratorError`, because in `tan`'s own process a `SystemExit`
12
+ would tear the CLI down past every envelope guarantee instead of arriving as a
13
+ non-zero rc from a subprocess. `check_emit_snapshots.py` in alp-sdk and
14
+ `tests/parity/test_planner_emit_parity.py` here both pin the output byte for
15
+ byte.
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ import re
21
+ from pathlib import Path
22
+ from typing import Any
23
+
24
+ from ..models import OrchestratorError
25
+ from ..paths import REPO
26
+ from ..som_metadata import _sku_family
27
+
28
+ from . import _e1m_gpio_canonical
29
+
30
+
31
+ class DtsOverlayUnavailable(OrchestratorError):
32
+ """The board has no header under `include/alp/boards/` to render from.
33
+
34
+ tan adaptation: upstream `sys.exit`s here and `buildplan._slice_dts_overlay`
35
+ converts that to its own `DtsOverlayUnavailable`; in `tan`'s process a
36
+ `SystemExit` is unacceptable (see the module docstring), so the emitter
37
+ raises the buildplan's type directly (`buildplan` re-exports this class).
38
+ """
39
+
40
+
41
+ # ---------------------------------------------------------------------
42
+ # DTS overlay emission (v0.3: i2c / spi / uart / pwm / gpio aliases)
43
+ # ---------------------------------------------------------------------
44
+ #
45
+ # Per the project memory note "pending exact hardware configurations
46
+ # -- mark unknowns TBD, never invent values", the loader translates
47
+ # the macros in include/alp/boards/<board>.h verbatim; it does not
48
+ # invent gpio bank numbers or per-pad GPIO_ACTIVE_* flags. The
49
+ # emitted .overlay declares the board's bus aliases and a stub
50
+ # alp,pin-array with one entry per EVK_PIN_* macro, each annotated
51
+ # with a comment naming the macro and the ALP_E1M_GPIO_IO<N> it
52
+ # resolves to. Customers fill the gpio bank / index columns with
53
+ # their SoM's actual DT controller phandles by cross-referencing the
54
+ # in-tree board file (zephyr/boards/alp/<board>/*.dts, wired in via
55
+ # zephyr/module.yml's `board_root: zephyr`).
56
+ #
57
+ # Bus phandle naming convention matches the manually-written EVK
58
+ # overlays at tests/zephyr/peripheral/boards/alp_e1m_aen801_m55_he.overlay:
59
+ # &i2c<N>, &spi<N>, &uart<N>, &pwm<N>. Per-SoC vendor DT may use
60
+ # alternate names (e.g. &lpi2c0 on some Alif boards); the customer
61
+ # fixes the phandle if their board file diverges -- the loader's
62
+ # job is to surface every alias the board wants, not to second-
63
+ # guess vendor DT naming.
64
+
65
+ # Match `#define <NAME> ALP_E1M_<CLASS><N>` (with optional trailing
66
+ # token). Class is one of the bus / pwm / gpio / analog-converter
67
+ # names we care about. ADC + DAC join the set so the portable
68
+ # <alp/adc.h> / <alp/dac.h> backends -- which resolve their channels
69
+ # via the `alp-adcN` / `alp-dacN` DT aliases -- get a generated alias
70
+ # scaffold from the board's `e1m_routes.adc` / `.dac` entries.
71
+ _DEFINE_E1M_RE = re.compile(
72
+ r"^\s*#\s*define\s+(\w+)\s+ALP_E1M_(I2C|SPI|UART|PWM|ADC|DAC|GPIO_IO)(\d+)\b",
73
+ re.MULTILINE,
74
+ )
75
+
76
+ # Bus-alias buckets the loader emits. Each entry maps the e1m_pinout
77
+ # class name -> (alias prefix, Zephyr DT phandle prefix).
78
+ #
79
+ # The phandle prefix is the convention-default node-label (&i2c0,
80
+ # &adc0, ...); vendor DT may use a different label (e.g. the Alif
81
+ # Ensemble ADCs are node-labelled `adc12_0` and the EEPROM bus is
82
+ # `i2c2`), in which case the per-app board overlay repoints the alias
83
+ # (`aliases { alp-adc0 = &adc12_0; };`) -- the loader's job is to
84
+ # surface every alias the board wires, not to second-guess vendor DT
85
+ # node-label naming.
86
+ _BUS_BUCKETS: tuple[tuple[str, str, str], ...] = (
87
+ ("I2C", "alp-i2c", "i2c"),
88
+ ("SPI", "alp-spi", "spi"),
89
+ ("UART", "alp-uart", "uart"),
90
+ ("PWM", "alp-pwm", "pwm"),
91
+ ("ADC", "alp-adc", "adc"),
92
+ ("DAC", "alp-dac", "dac"),
93
+ )
94
+
95
+
96
+ def _strip_c_comments(text: str) -> str:
97
+ """Strip /* ... */ and // ... comments from C source text."""
98
+ text = re.sub(r"/\*.*?\*/", "", text, flags=re.DOTALL)
99
+ text = re.sub(r"//[^\n]*", "", text)
100
+ return text
101
+
102
+
103
+ def _collapse_line_continuations(text: str) -> str:
104
+ """Join `\\<newline>` continuations into single logical lines so a
105
+ multi-line `#define NAME \\\n VALUE` shows up as one line."""
106
+ return re.sub(r"\\\s*\n\s*", " ", text)
107
+
108
+
109
+ def _board_header_path(board_name: str, repo_root: Path) -> Path:
110
+ """Resolve include/alp/boards/alp_<board>.h for a board name.
111
+
112
+ Example: 'E1M-EVK' -> include/alp/boards/alp_e1m_evk.h.
113
+ """
114
+ fname = "alp_" + board_name.lower().replace("-", "_") + ".h"
115
+ return repo_root / "include" / "alp" / "boards" / fname
116
+
117
+
118
+ _INCLUDE_LOCAL_RE = re.compile(
119
+ r'^\s*#\s*include\s+"(alp/boards/[^"]+\.h)"', re.MULTILINE,
120
+ )
121
+
122
+
123
+ def _read_board_header_with_includes(header_path: Path) -> str:
124
+ """Read `header_path` and inline any `#include "alp/boards/<file>.h"`
125
+ that exists under include/. Used so the loader picks up the
126
+ generated routes header (alp_e1m_evk_routes.h) which holds the
127
+ EVK_* -> ALP_E1M_* macro bindings since slice 1c.
128
+
129
+ Single-level inlining is sufficient -- the generated routes header
130
+ only `#include`s `alp/e1m_pinout.h`, which carries no EVK_* macros.
131
+ """
132
+ text = header_path.read_text(encoding="utf-8")
133
+ include_root = header_path.parent.parent.parent # .../include/
134
+ pieces: list[str] = [text]
135
+ for m in _INCLUDE_LOCAL_RE.finditer(text):
136
+ inc_rel = m.group(1)
137
+ inc_path = include_root / inc_rel
138
+ if inc_path.is_file() and inc_path.resolve() != header_path.resolve():
139
+ pieces.append(inc_path.read_text(encoding="utf-8"))
140
+ return "\n".join(pieces)
141
+
142
+
143
+ def _parse_board_macros(
144
+ header_path: Path,
145
+ ) -> dict[str, list[tuple[str, int]]]:
146
+ """Return {class_name: [(macro_name, channel_index), ...]} for
147
+ each ALP_E1M_<CLASS><N> reference in the board header."""
148
+ raw = _read_board_header_with_includes(header_path)
149
+ text = _strip_c_comments(_collapse_line_continuations(raw))
150
+ out: dict[str, list[tuple[str, int]]] = {
151
+ "I2C": [], "SPI": [], "UART": [], "PWM": [], "GPIO_IO": [],
152
+ }
153
+ for m in _DEFINE_E1M_RE.finditer(text):
154
+ macro_name = m.group(1)
155
+ cls = m.group(2)
156
+ idx = int(m.group(3))
157
+ out.setdefault(cls, []).append((macro_name, idx))
158
+ return out
159
+
160
+
161
+ def _project_pin_indices(project: dict[str, Any], class_name: str) -> set[int]:
162
+ """Return E1M route indices for one class named in board.yaml `pins:`."""
163
+ pattern = re.compile(rf"^E1M_{re.escape(class_name)}(\d+)$")
164
+ indices: set[int] = set()
165
+ for pin in project.get("pins", []) or []:
166
+ if isinstance(pin, str):
167
+ e1m = pin
168
+ elif isinstance(pin, dict):
169
+ e1m = pin.get("e1m")
170
+ else:
171
+ continue
172
+ if not isinstance(e1m, str):
173
+ continue
174
+ m = pattern.match(e1m)
175
+ if m:
176
+ indices.add(int(m.group(1)))
177
+ return indices
178
+
179
+
180
+ def _route_indices_for_catalog(
181
+ project: dict[str, Any],
182
+ macros: dict[str, list[tuple[str, int]]],
183
+ class_name: str,
184
+ default_indices: set[int],
185
+ ) -> set[int]:
186
+ """Route indices a SoM-family catalog entry should own.
187
+
188
+ Apps that name concrete `pins:` get exactly those aliases. Older or
189
+ chip-bound examples without `pins:` keep the catalog's historical default
190
+ instance, e.g. AEN portable I2C0 -> SoC i2c2.
191
+ """
192
+ requested = _project_pin_indices(project, class_name)
193
+ if requested:
194
+ return requested
195
+ board_indices = {idx for _macro, idx in macros.get(class_name, [])}
196
+ return board_indices & default_indices
197
+
198
+
199
+ def _emit_aen_adc_wiring(indices: set[int]) -> str:
200
+ """Emit AEN ADC consumer nodes for the requested portable ADC ids."""
201
+ ordered = sorted(indices)
202
+ lines: list[str] = ["/ {"]
203
+ for idx in ordered:
204
+ lines.append(f"\talp_adc_in{idx}: alp-adc-in{idx} {{")
205
+ lines.append('\t\tcompatible = "alp,adc-input";')
206
+ lines.append(f"\t\tio-channels = <&adc12_0 {idx}>;")
207
+ lines.append("\t};")
208
+ lines.append("\taliases {")
209
+ for idx in ordered:
210
+ lines.append(f"\t\talp-adc{idx} = &alp_adc_in{idx};")
211
+ lines.append("\t};")
212
+ lines.append("};")
213
+ lines.append("&adc12_0 {")
214
+ lines.append('\tstatus = "okay";')
215
+ for idx in ordered:
216
+ lines.append(f"\tchannel@{idx} {{")
217
+ lines.append(f"\t\treg = <{idx}>;")
218
+ lines.append('\t\tzephyr,gain = "ADC_GAIN_1";')
219
+ lines.append('\t\tzephyr,reference = "ADC_REF_INTERNAL";')
220
+ lines.append("\t\tzephyr,vref-mv = <1800>;")
221
+ lines.append("\t\tzephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;")
222
+ lines.append("\t\tzephyr,resolution = <12>;")
223
+ lines.append("\t};")
224
+ lines.append("};")
225
+ return "\n".join(lines) + "\n"
226
+
227
+
228
+ def _catalog_owned_alias_indices(
229
+ fam: str,
230
+ phandle_prefix: str,
231
+ project: dict[str, Any],
232
+ macros: dict[str, list[tuple[str, int]]],
233
+ ) -> set[int]:
234
+ """Return portable alias indices emitted by SoM-family special wiring."""
235
+ if fam == "aen" and phandle_prefix == "i2c":
236
+ return _route_indices_for_catalog(project, macros, "I2C", {0}) & {0}
237
+ if fam == "aen" and phandle_prefix == "adc":
238
+ return _route_indices_for_catalog(project, macros, "ADC", {0})
239
+ return set()
240
+
241
+
242
+ def _catalog_generic_alias_indices(
243
+ fam: str,
244
+ phandle_prefix: str,
245
+ project: dict[str, Any],
246
+ macros: dict[str, list[tuple[str, int]]],
247
+ class_name: str,
248
+ ) -> set[int]:
249
+ """Return generic alias indices to emit for a catalog-owned class."""
250
+ if fam == "aen" and phandle_prefix == "i2c":
251
+ return _route_indices_for_catalog(project, macros, class_name, {0})
252
+ if fam == "aen" and phandle_prefix == "adc":
253
+ return _route_indices_for_catalog(project, macros, class_name, {0})
254
+ return {idx for _macro, idx in macros.get(class_name, [])}
255
+
256
+
257
+ # ---------------------------------------------------------------------
258
+ # Carrier peripheral DT-wiring catalog (single source of truth)
259
+ # ---------------------------------------------------------------------
260
+ #
261
+ # Declaring a peripheral in board.yaml (`cores.<id>.peripherals`) sets the
262
+ # subsystem CONFIG via the conf emit -- but that alone does NOT bind hardware:
263
+ # the controller node sits `disabled` in the SoC dtsi, so e.g. ADC_ALIF never
264
+ # selects and `alp adc read` returns -ENOENT. Examples used to paper over this
265
+ # with a hand-written boards/*.overlay enabling the node + the alp-<x>N alias /
266
+ # io-channels consumer the portable backends resolve.
267
+ #
268
+ # This catalog moves that wiring into the codegen: `_emit_dts_overlay` renders
269
+ # the fragment for each declared peripheral, so `peripherals: [adc]` ALONE
270
+ # yields a working `alp adc read` with NO per-example overlay. Keyed by SoM
271
+ # family (from `_sku_family`); each entry carries the dt-bindings `#include`s it
272
+ # needs + a self-contained DTS fragment (DT permits repeated `/{}` and
273
+ # `&label{}` sections). To add or fix a peripheral's wiring, edit ONE entry
274
+ # here -- never per example. A per-example boards/*.overlay still layers last
275
+ # and wins (override tier), so this is opt-in-complete, not a straitjacket.
276
+ _PERIPH_DT_WIRING: dict[str, dict[str, dict[str, Any]]] = {
277
+ "aen": {
278
+ "i2c": {
279
+ "include": ["zephyr/dt-bindings/i2c/i2c.h",
280
+ "zephyr/dt-bindings/pinctrl/alif-ensemble-pinctrl.h"],
281
+ "dts": (
282
+ "&pinctrl {\n"
283
+ "\tpinctrl_i2c2: pinctrl_i2c2 {\n"
284
+ "\t\tgroup0 {\n"
285
+ "\t\t\tpinmux = <PIN_P5_6__I2C2_SCL_C>, <PIN_P5_7__I2C2_SDA_C>;\n"
286
+ "\t\t\tinput-enable;\n"
287
+ "\t\t\tbias-pull-down;\n"
288
+ "\t\t};\n"
289
+ "\t};\n"
290
+ "};\n"
291
+ "&i2c2 {\n"
292
+ "\tstatus = \"okay\";\n"
293
+ "\tpinctrl-0 = <&pinctrl_i2c2>;\n"
294
+ "\tpinctrl-names = \"default\";\n"
295
+ "\tclock-frequency = <I2C_BITRATE_STANDARD>;\n"
296
+ "};\n"
297
+ "/ {\n"
298
+ "\taliases {\n"
299
+ "\t\talp-i2c0 = &i2c2;\n"
300
+ "\t};\n"
301
+ "};\n"
302
+ ),
303
+ },
304
+ "gpio": {
305
+ "include": [],
306
+ "dts": "&gpio8 {\n\tstatus = \"okay\";\n};\n",
307
+ },
308
+ "adc": {
309
+ "include": ["zephyr/dt-bindings/adc/adc.h"],
310
+ },
311
+ "i3c": {
312
+ # E1M-AEN801 drives I3C through the LP instance
313
+ # (lpi3c0@0x43006000). The two controllers are INDEPENDENT on the
314
+ # E8 and overlap on ONE pad pair only: P7_6/P7_7, exposed as both
315
+ # PIN_P7_6__I3C_SDA_D/PIN_P7_7__I3C_SCL_D (fn6, main i3c0) and
316
+ # PIN_P7_6__LPI3C_SDA_B/PIN_P7_7__LPI3C_SCL_B (fn3, this one).
317
+ # The E1M-AEN801 routes only that pair, so on THIS SoM exactly one
318
+ # node may be enabled and firmware picks the owner -- a board
319
+ # constraint, not a silicon one. We pick the LP instance
320
+ # because it is the M55-HE local peripheral domain (IRQ 50), NOT
321
+ # because the SoM pinout selects it: from-alif.tsv's
322
+ # `alif_peripheral` column names the MAIN-I3C fn6 functions
323
+ # (I3C_SCL_D/I3C_SDA_D) and has no LPI3C row at all. See
324
+ # zephyr/dts/alif/ensemble_e8_peripherals.dtsi. Emission is gated
325
+ # to the m55_he core below -- an HP slice gets no alias, so
326
+ # alp_i3c_open() surfaces NOT_READY instead of a dead IRQ.
327
+ "include": ["zephyr/dt-bindings/pinctrl/alif-ensemble-pinctrl.h"],
328
+ "dts": (
329
+ "&pinctrl {\n"
330
+ "\tpinctrl_lpi3c0: pinctrl_lpi3c0 {\n"
331
+ "\t\tgroup0 {\n"
332
+ "\t\t\tpinmux = <PIN_P7_6__LPI3C_SDA_B>;\n"
333
+ "\t\t\tinput-enable;\n"
334
+ "\t\t};\n"
335
+ "\t\tgroup1 {\n"
336
+ "\t\t\tpinmux = <PIN_P7_7__LPI3C_SCL_B>;\n"
337
+ "\t\t\tinput-enable;\n"
338
+ "\t\t};\n"
339
+ "\t};\n"
340
+ "};\n"
341
+ "&lpi3c0 {\n"
342
+ "\tstatus = \"okay\";\n"
343
+ "\tpinctrl-0 = <&pinctrl_lpi3c0>;\n"
344
+ "\tpinctrl-names = \"default\";\n"
345
+ "};\n"
346
+ "/ {\n"
347
+ "\taliases {\n"
348
+ "\t\talp-i3c0 = &lpi3c0;\n"
349
+ "\t};\n"
350
+ "};\n"
351
+ ),
352
+ },
353
+ },
354
+ }
355
+
356
+
357
+ def _emit_dts_overlay(
358
+ project: dict[str, Any],
359
+ sku_preset: dict[str, Any],
360
+ board_preset: dict[str, Any] | None,
361
+ *,
362
+ v2_peripherals: list[str] | None = None,
363
+ v2_core_id: str | None = None,
364
+ v2_core_os: str | None = None,
365
+ v2_core_ids: list[str] | None = None,
366
+ ) -> str:
367
+ """Emit a Zephyr DTS overlay describing the board wiring.
368
+
369
+ v1 path (`v2_peripherals is None`): reads project-level
370
+ `peripherals:` implicitly via the board header macros.
371
+
372
+ v2 path: the project's peripherals live under `cores.<id>.peripherals`.
373
+ Callers compute the union across Zephyr/baremetal cores (or pick one
374
+ when `--core <id>` is supplied) and pass it in via `v2_peripherals`.
375
+ The list is currently informational -- the bus aliases + `alp,pin-array`
376
+ binding root node are derived from the board header, which describes
377
+ the SoM mounting, not the project. When `v2_core_os` is set to a
378
+ non-Zephyr runtime (`yocto`, `off`, ...), the emitter returns a stub
379
+ overlay with just the header comment.
380
+ """
381
+ lines: list[str] = []
382
+ lines.append("/*")
383
+ lines.append(" * Auto-generated by scripts/alp_project.py "
384
+ "-- do not edit by hand.")
385
+ lines.append(" * Regenerate after changes to board.yaml or "
386
+ "include/alp/boards/<board>.h.")
387
+ lines.append(" *")
388
+ lines.append(" * Per-pad GPIO bank/index values are TBD; cross-reference the in-tree")
389
+ lines.append(" * board file at zephyr/boards/alp/<board>/*.dts (zephyr/module.yml board_root).")
390
+ lines.append(" * The alp,pin-array below is the full 52-entry positional map in")
391
+ lines.append(" * e1m_pinout.h canonical order; fill the <&gpioX Y FLAGS> columns")
392
+ lines.append(" * in place without renumbering (the positional index is the ABI).")
393
+ lines.append(" */")
394
+ lines.append("")
395
+
396
+ # v2 short-circuit: a non-Zephyr core has no Zephyr overlay to emit.
397
+ # Customer-passed `--core <id>` may target a yocto / off slice -- the
398
+ # emitter returns a stub so the caller's pipeline doesn't fail.
399
+ if v2_core_os is not None and v2_core_os not in ("zephyr", "baremetal"):
400
+ lines.append(f"// --core {v2_core_id} has os: {v2_core_os}; no Zephyr DTS overlay applies.")
401
+ return "\n".join(lines) + "\n"
402
+
403
+ lines.append("#include <zephyr/dt-bindings/gpio/gpio.h>")
404
+ lines.append("")
405
+
406
+ sku = project["som"]["sku"]
407
+ # Carrier peripheral DT-wiring catalog for this SoM family. Computed
408
+ # ONCE here and reused both to skip the generic bus aliases the catalog
409
+ # owns (below) and to emit the catalog fragments at the tail.
410
+ fam = _sku_family(sku)
411
+ wiring = _PERIPH_DT_WIRING.get(fam, {})
412
+ board_name = (board_preset or {}).get("name", "")
413
+ if not board_name:
414
+ lines.append("// No board declared in board.yaml; nothing to emit.")
415
+ return "\n".join(lines) + "\n"
416
+
417
+ header_path = _board_header_path(board_name, REPO)
418
+ if not header_path.is_file():
419
+ raise DtsOverlayUnavailable(
420
+ f"no board header at "
421
+ f"{header_path.relative_to(REPO)} for board '{board_name}' "
422
+ f"-- DTS overlay emission requires one.")
423
+
424
+ macros = _parse_board_macros(header_path)
425
+
426
+ lines.append(f"/ {{")
427
+ lines.append(f" /* Board: {board_name} (SoM SKU {sku}) */")
428
+ lines.append(f" /* Source: include/alp/boards/{header_path.name} */")
429
+ if v2_peripherals is not None:
430
+ # Surface which Zephyr peripherals the v2 union resolved to so
431
+ # consumers can correlate the alias list back to their cores.
432
+ if v2_core_id is not None:
433
+ lines.append(
434
+ f" /* v2 scope: --core {v2_core_id} peripherals: "
435
+ f"{', '.join(v2_peripherals) if v2_peripherals else '<none>'} */"
436
+ )
437
+ else:
438
+ lines.append(
439
+ f" /* v2 scope: union of Zephyr/baremetal cores' peripherals: "
440
+ f"{', '.join(v2_peripherals) if v2_peripherals else '<none>'} */"
441
+ )
442
+ lines.append("")
443
+
444
+ # Bus aliases -- one per unique channel the board wires.
445
+ lines.append(" aliases {")
446
+ for class_name, alp_prefix, phandle_prefix in _BUS_BUCKETS:
447
+ # Catalog-owned classes (AEN i2c/adc) are instance-specific: the
448
+ # catalog owns only the aliases it remaps, while requested sibling
449
+ # instances can still receive the generic scaffold.
450
+ if phandle_prefix in wiring and phandle_prefix not in set(v2_peripherals or []):
451
+ continue
452
+ if phandle_prefix in wiring:
453
+ entries = sorted(
454
+ _catalog_generic_alias_indices(fam, phandle_prefix, project, macros, class_name)
455
+ - _catalog_owned_alias_indices(fam, phandle_prefix, project, macros)
456
+ )
457
+ else:
458
+ entries = sorted(set(idx for _macro, idx in macros.get(class_name, [])))
459
+ if not entries:
460
+ continue
461
+ lines.append(f" /* {class_name} */")
462
+ for idx in entries:
463
+ # Comment lists every board macro that references this channel.
464
+ referencing = [m for (m, i) in macros[class_name] if i == idx]
465
+ comment = ", ".join(referencing)
466
+ lines.append(
467
+ f" {alp_prefix}{idx} = &{phandle_prefix}{idx};"
468
+ f" /* {comment} */"
469
+ )
470
+ lines.append(" };")
471
+ lines.append("")
472
+
473
+ # alp,pin-array -- the 52-entry positional GPIO map. Order is fixed
474
+ # by e1m_pinout.h's "Devicetree / overlay invariant" so the GPIO
475
+ # backend's positional resolve (alp_pins[pin_id]) lands on the right
476
+ # pad, including secondary-function pads opened as GPIO via
477
+ # ALP_E1M_GPIO_<class><N>. Every slot is present even when the board
478
+ # doesn't route it; <&gpioX Y FLAGS> triplets are TBD pending the
479
+ # upstream SoM board file.
480
+ io_by_idx = {idx: m for (m, idx) in macros.get("GPIO_IO", [])}
481
+ pwm_by_idx = {idx: m for (m, idx) in macros.get("PWM", [])}
482
+ canonical = _e1m_gpio_canonical()
483
+ lines.append(" alp_pins: alp-pins {")
484
+ lines.append(' compatible = "alp,pin-array";')
485
+ lines.append(" /* 52 entries in E1M canonical order (e1m_pinout.h). Indices:")
486
+ lines.append(" * 0..25 IO0..IO25 26..33 PWM0..PWM7")
487
+ lines.append(" * 34..41 ENC0_X..ENC3_Y 42..49 ADC0..ADC7 50..51 DAC0..DAC1")
488
+ lines.append(" * Each <&gpioX Y FLAGS> triplet is TBD pending the upstream SoM")
489
+ lines.append(" * board file; unrouted pads keep their slot so indices stay")
490
+ lines.append(" * stable (alp_gpio_open of an unrouted pad returns NULL). */")
491
+ lines.append(" gpios =")
492
+ for i, suffix in enumerate(canonical):
493
+ terminator = ";" if i == len(canonical) - 1 else ","
494
+ # Annotate IO / PWM slots with the board macro routed to that pad
495
+ # (parsed from the board header); other classes carry the bare
496
+ # ALP_E1M_GPIO_<suffix> so the customer knows which pad the slot is.
497
+ routed = ""
498
+ if suffix.startswith("IO"):
499
+ n = int(suffix[2:])
500
+ if n in io_by_idx:
501
+ routed = f" routed: {io_by_idx[n]}"
502
+ elif suffix.startswith("PWM"):
503
+ n = int(suffix[3:])
504
+ if n in pwm_by_idx:
505
+ routed = f" default fn: {pwm_by_idx[n]}"
506
+ lines.append(
507
+ f" <&gpio0 0 GPIO_ACTIVE_HIGH>{terminator}"
508
+ f" /* [{i:2d}] ALP_E1M_GPIO_{suffix}{routed} */"
509
+ )
510
+ lines.append(" };")
511
+ lines.append("")
512
+
513
+ lines.append("};")
514
+
515
+ # ── carrier peripheral wiring (catalog-driven) ──────────────────────
516
+ # For each declared peripheral carrying a _PERIPH_DT_WIRING entry, append
517
+ # its controller node-enable + the alp-<x>N alias / io-channels consumer
518
+ # the portable backends resolve -- so `peripherals: [adc]` ALONE binds the
519
+ # hardware (no hand-written boards/*.overlay). The v1 path (v2_peripherals
520
+ # is None) emits nothing here. A per-example overlay still layers last.
521
+ # `fam`/`wiring` were computed once near the top of the function (and also
522
+ # gate the generic alias skip above), so they are reused here verbatim.
523
+ emitted: list[tuple[str, dict[str, Any], str]] = []
524
+ for p in sorted(set(v2_peripherals or [])):
525
+ if p not in wiring:
526
+ continue
527
+ entry = wiring[p]
528
+ if fam == "aen" and p == "adc":
529
+ indices = _route_indices_for_catalog(project, macros, "ADC", {0})
530
+ if not indices:
531
+ continue
532
+ emitted.append((p, entry, _emit_aen_adc_wiring(indices)))
533
+ continue
534
+ if fam == "aen" and p == "i2c":
535
+ indices = _route_indices_for_catalog(project, macros, "I2C", {0})
536
+ if 0 not in indices:
537
+ continue
538
+ if fam == "aen" and p == "i3c":
539
+ # lpi3c0 is M55-HE local domain (IRQ 50), so only a scope that
540
+ # CONTAINS m55_he gets the wiring -- an HP-only slice would
541
+ # otherwise carry an alias for an IRQ its core cannot service.
542
+ #
543
+ # Testing `v2_core_id != "m55_he"` alone would be wrong for the
544
+ # UNSCOPED emit (`--emit dts-overlay` with no `--core`, where
545
+ # v2_core_id is None): that path emits the cross-core union, so
546
+ # a board.yaml whose Zephyr core IS m55_he would silently get no
547
+ # i3c wiring while the conf emit still set CONFIG_I3C=y from
548
+ # peripheral-kconfig.json -- a green build whose alp_i3c_open()
549
+ # returns NOT_READY with nothing to point at. So for the
550
+ # unscoped case, look at whether the project declares m55_he.
551
+ # `project` here is the v1-SHAPED dict, which carries no `cores`
552
+ # key at all -- so the scope has to be passed in: v2_core_ids is
553
+ # [--core] when scoped, and the Zephyr/baremetal core ids when not.
554
+ if v2_core_id is not None:
555
+ in_scope = v2_core_id == "m55_he"
556
+ else:
557
+ in_scope = "m55_he" in (v2_core_ids or [])
558
+ if not in_scope:
559
+ continue
560
+ emitted.append((p, entry, entry.get("dts", "")))
561
+
562
+ if emitted:
563
+ incs: list[str] = []
564
+ for _p, entry, _dts in emitted:
565
+ for inc in entry.get("include", []):
566
+ if inc not in incs:
567
+ incs.append(inc)
568
+ lines.append("")
569
+ lines.append("/* ---- carrier peripheral wiring "
570
+ "(auto, from board.yaml `peripherals:`) ---- */")
571
+ for inc in incs:
572
+ lines.append(f"#include <{inc}>")
573
+ if incs:
574
+ lines.append("")
575
+ for p, _entry, dts in emitted:
576
+ lines.append(f"/* peripheral: {p} */")
577
+ lines.append(dts.rstrip("\n"))
578
+ lines.append("")
579
+
580
+ return "\n".join(lines) + "\n"