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,116 @@
1
+ #!/usr/bin/env python3
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ """System-manifest emitter -- assembles system-manifest.yaml from the model.
4
+
5
+ `emit_system_manifest` renders the spec-§5.2 manifest (slices, carve-outs,
6
+ storage, memory regions, helper-MCU block) off the parsed BoardProject + the
7
+ resolved carve-outs / partitions / memory regions; `_helper_mcus` builds the
8
+ manifest's `helper_mcus[]` block (shared with the Orchestrator's materialise
9
+ path, which back-imports it). Extracted as the #285 manifest emit seam.
10
+
11
+ `memory[]` is the region table the other two panes refer INTO by name
12
+ (#1365 item 3) -- `memory.resolve_memory_regions` shapes it, and it is
13
+ OMITTED rather than emitted empty when a SoM resolves no regions.
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ from typing import Any, Optional
19
+
20
+ import yaml
21
+
22
+ from .carveout import resolve_carve_outs
23
+ from .memory import resolve_memory_regions
24
+ from .models import BoardProject, Slice, SystemManifest
25
+ from .partition import resolve_storage_partitions
26
+
27
+
28
+ def emit_system_manifest(
29
+ project: BoardProject,
30
+ *,
31
+ slices: Optional[list[Slice]] = None,
32
+ ) -> str:
33
+ """Generate system-manifest.yaml per spec §5.2.
34
+
35
+ If `slices` is None, projects the BoardProject's `cores` dict
36
+ as-is (typical "describe what will run" call). When the
37
+ orchestrator finishes fan_out it passes its updated Slice list
38
+ so the manifest carries status / log_path / etc.
39
+ """
40
+ carve_outs = resolve_carve_outs(project)
41
+ partitions = resolve_storage_partitions(project)
42
+ memory_regions = resolve_memory_regions(project)
43
+ effective_slices = list(slices) if slices is not None else list(project.cores.values())
44
+
45
+ boot_order = list(project.som_preset.get("boot_order") or [])
46
+
47
+ manifest = SystemManifest(
48
+ project=project,
49
+ slices=effective_slices,
50
+ carve_outs=carve_outs,
51
+ partitions=partitions,
52
+ boot_order=boot_order,
53
+ helper_mcus=_helper_mcus(project),
54
+ memory_regions=memory_regions,
55
+ )
56
+
57
+ out = manifest.to_dict()
58
+ # Comment when boot_order is empty so reviewers see the gap.
59
+ text = yaml.safe_dump(out, sort_keys=False, default_flow_style=False)
60
+ if not boot_order:
61
+ text += ("\n# boot_order is empty -- add a `boot_order:` list to "
62
+ f"metadata/e1m_modules/{project.sku}.yaml when the\n"
63
+ "# bring-up order is finalised.\n")
64
+ return text
65
+
66
+
67
+ def _helper_mcus(project: BoardProject) -> list[dict[str, Any]]:
68
+ """Build the manifest's `helper_mcus[]` block.
69
+
70
+ Source: the SoM preset's `helper_firmware:` list (Phase 3). Each key
71
+ the entry declares is projected INDEPENDENTLY -- how the image is
72
+ written locally (flash_method + flash_args), how it is updated in the
73
+ field (update_channel), and who may invoke the flash method
74
+ (flash_policy) are three separate axes, and a helper may declare any
75
+ combination. The GD32 bridge declares two: an `alp_ota_spi_bridge`
76
+ channel for normal field updates, and (until tan-cli#732 removed its
77
+ local write path, `swd_probe`) a `recovery_only` local flash method for
78
+ a bricked board. Dropping the flash keys because a channel exists would
79
+ DELETE that recovery path from the manifest rather than let `tan flash`
80
+ decline it, so this function must never make one key's presence
81
+ suppress another's (alp-sdk #1357). `firmware_path` is entirely ABSENT
82
+ from the row when
83
+ the preset doesn't declare one (e.g. GD32 bridge SKUs pending a
84
+ released binary, alp-sdk #852/#936) -- it is never emitted as `null`,
85
+ because `system-manifest-v1.schema.json` types it `string` when present
86
+ (the orchestrator does NOT fail the build on a missing helper firmware
87
+ path -- the Renesas + Alif flash flows are independently scriptable).
88
+
89
+ Every in-tree SoM preset carries a `helper_firmware:` list (no more
90
+ Phase-1 stragglers), so there is no back-compat `on_module.{
91
+ supervisor_mcu,wifi_ble}` fallback here -- no-legacy-compat.
92
+ """
93
+ out: list[dict[str, Any]] = []
94
+
95
+ helper_firmware = project.som_preset.get("helper_firmware")
96
+ if isinstance(helper_firmware, list):
97
+ for entry in helper_firmware:
98
+ if not isinstance(entry, dict):
99
+ continue
100
+ row: dict[str, Any] = {
101
+ "name": entry.get("name"),
102
+ "chip": entry.get("chip"),
103
+ }
104
+ # Project every declared key independently. No key's presence
105
+ # suppresses another's: `update_channel` describes field
106
+ # updates, `flash_method`/`flash_args` describe a local write,
107
+ # and `flash_policy` says who may perform that write. A helper
108
+ # declaring both halves keeps both -- `tan flash` decides what
109
+ # to do with them from `flash_policy` (alp-sdk #1357).
110
+ for key in ("firmware_path", "flash_method", "flash_args",
111
+ "flash_policy", "update_channel"):
112
+ value = entry.get(key)
113
+ if value is not None:
114
+ row[key] = value
115
+ out.append(row)
116
+ return out
tan/planner/memory.py ADDED
@@ -0,0 +1,161 @@
1
+ #!/usr/bin/env python3
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ """Resolved memory-region view for the system manifest (alp-sdk#1365 item 3).
4
+
5
+ `resolve_memory_regions()` projects the SoM's effective memory-region table
6
+ into the `memory[]` pane `system-manifest-v1` declares -- the third pane
7
+ `ipc[]` and `storage[]` refer INTO by name, not a third copy of them.
8
+
9
+ Three vocabularies, each taken from what the code already produces rather
10
+ than from #1365's issue body (which predates splits A and B and proposes
11
+ shapes the shipped derivation cannot fill):
12
+
13
+ * `kind` is `aperture.classify_region()`'s own four verdicts --
14
+ `flash` / `ram` / `unclassified` / `unresolved`. Collapsing them onto the
15
+ issue's two (`ram` | `flash`) would have to invent a class for the two
16
+ verdicts that exist precisely to say "not proven": `unclassified` (outside
17
+ the aperture AND preset-authored -- an Ensemble OSPI XIP window is outside
18
+ and still flash) and `unresolved` (no extent, or no aperture declared for
19
+ this SoC at all, which is every non-Alif SoM).
20
+
21
+ * the authority field is `write_authority`, passed through verbatim with
22
+ som-preset-v2's six values. The issue's 3-value `owner` was replaced
23
+ during review because one axis cannot separate flash-time from runtime
24
+ authority: `customer_image` and `customer_runtime` would both collapse to
25
+ `customer`, which is the exact distinction the AEN `storage`-vs-`he_slot0`
26
+ hazard needs. A row that authors no `write_authority` carries no key --
27
+ absent means unresolved, never `customer_runtime` (ADR-0034 clause 4).
28
+
29
+ * `status` is `ok` / `unresolved`, the word this schema's own items
30
+ description already made normative, not `ipc[]`'s `ok` / `blocked`. They
31
+ are not synonyms: `blocked` is a verdict about an allocation request,
32
+ while a region is a fact that either resolved an address or did not.
33
+
34
+ `source` is derived here rather than tagged in `alp_project.resolve_memory_map`
35
+ on purpose. That function's precedence is ALL-OR-NOTHING (an authored
36
+ `memory_map:` wins the whole table, else the SoC's fixed `memory_regions`,
37
+ else the silicon-variant derivation), so provenance is a property of the
38
+ table, not of a row -- `carveout.py` and `partition.py` already read it the
39
+ same way, via `bool(som_preset.get("memory_map"))`. Tagging rows inside the
40
+ loader would also add a key to dicts that flow into `$defs/memory_region`'s
41
+ `additionalProperties: false` validation path.
42
+
43
+ Depends only downward -- `som_metadata`, `aperture` and `memregion`; nothing
44
+ calls back into the package.
45
+ """
46
+
47
+ from __future__ import annotations
48
+
49
+ from typing import Any, Optional
50
+
51
+ from .aperture import classify_region, resolve_aperture
52
+ from .memregion import _region_size_bytes
53
+ from .models import BoardProject
54
+ from .som_metadata import resolve_memory_map
55
+
56
+ # The two provenance values a region row can carry. `board_yaml` (the third
57
+ # value #1365's issue body proposes) has no producer: board.yaml never
58
+ # AUTHORS a region, it only NAMES one via `ipc[].carve_out_region` /
59
+ # `storage[].flash_device`, so a board_yaml-sourced region row cannot occur.
60
+ _SOURCE_PRESET = "som_preset"
61
+ _SOURCE_SOC = "soc_derived"
62
+
63
+
64
+ def resolve_memory_regions(project: BoardProject) -> list[dict[str, Any]]:
65
+ """Project the SoM's memory regions into the manifest's `memory[]` pane.
66
+
67
+ Returns `[]` when the SoM resolves no regions at all (an unresolvable
68
+ `silicon_variant`, e.g. `TBD`). The caller must then OMIT the
69
+ key rather than emit `memory: []` -- the schema draws a load-bearing
70
+ distinction between an absent pane ("this producer does not emit it
71
+ yet") and an empty one, and collapsing the two would tell every
72
+ consumer that a SoM with a pending HW-config writeup has no memory.
73
+ """
74
+ metadata_root = project.effective_metadata_root()
75
+ memory_map = resolve_memory_map(project.som_preset, metadata_root)
76
+ if not memory_map:
77
+ return []
78
+
79
+ # Same two signals `carveout.py` / `partition.py` derive, read the same
80
+ # way: the SoC's declared on-die flash aperture (None on every non-Alif
81
+ # SoM), and whether THIS preset authored the table.
82
+ aperture = resolve_aperture(project.som_preset, metadata_root)
83
+ is_preset_authored = bool(project.som_preset.get("memory_map"))
84
+ source = _SOURCE_PRESET if is_preset_authored else _SOURCE_SOC
85
+
86
+ return [
87
+ _resolved_row(region, aperture, is_preset_authored, source)
88
+ for region in memory_map
89
+ if isinstance(region, dict) and region.get("name")
90
+ ]
91
+
92
+
93
+ def _resolved_row(
94
+ region: dict[str, Any],
95
+ aperture: Optional[tuple[int, int]],
96
+ is_preset_authored: bool,
97
+ source: str,
98
+ ) -> dict[str, Any]:
99
+ """Shape one region into a manifest row.
100
+
101
+ Key order is deliberate and load-bearing for the byte-golden `.snap`
102
+ fixtures: identity first (`name`, `source`), then the two derived
103
+ verdicts (`kind`, `status`), then the resolved numbers, then the
104
+ authored passthroughs, then the explanation. Builds a NEW dict --
105
+ the loader's region dicts are never mutated.
106
+ """
107
+ row: dict[str, Any] = {
108
+ "name": str(region.get("name")),
109
+ "source": source,
110
+ "kind": classify_region(region, aperture, is_preset_authored),
111
+ }
112
+
113
+ # `status` keys off the BASE, not off the extent: a region whose base
114
+ # resolves but whose size does not has still resolved its address, and
115
+ # the schema's rule is written about `base` ("carries no `base` and says
116
+ # why"). `region_extent()` folds both into one None, so it cannot
117
+ # answer this question on its own.
118
+ base = region.get("base")
119
+ base_ok = isinstance(base, int) and not isinstance(base, bool)
120
+ row["status"] = "ok" if base_ok else "unresolved"
121
+ if base_ok:
122
+ row["base"] = base
123
+
124
+ size_bytes = _region_size_bytes(region)
125
+ if size_bytes is not None:
126
+ row["size_bytes"] = size_bytes
127
+
128
+ write_authority = region.get("write_authority")
129
+ if isinstance(write_authority, str) and write_authority:
130
+ row["write_authority"] = write_authority
131
+
132
+ accessible_from = region.get("accessible_from")
133
+ if isinstance(accessible_from, list) and accessible_from:
134
+ row["accessible_from"] = [str(core) for core in accessible_from]
135
+
136
+ if not base_ok:
137
+ row["reason"] = _unresolved_reason(row["name"], base, size_bytes)
138
+
139
+ return row
140
+
141
+
142
+ def _unresolved_reason(name: str, base: Any, size_bytes: Optional[int]) -> str:
143
+ """Say WHY a base did not resolve, in terms the author can act on.
144
+
145
+ Never a guessed address (ADR-0034 clause 4): the reason names the field
146
+ that is missing and where to declare it, which is what makes an
147
+ unresolved row actionable instead of merely absent.
148
+ """
149
+ if base is None:
150
+ cause = ("carries no `base:` -- the silicon default applies and no "
151
+ "address is declared for it")
152
+ elif isinstance(base, str):
153
+ cause = f"declares `base: {base}`, a placeholder, not an address"
154
+ else:
155
+ cause = f"declares a non-integer `base:` ({type(base).__name__})"
156
+ sizing = (" Its size resolves, so only the address is missing."
157
+ if size_bytes is not None else
158
+ " Neither its address nor its size resolves.")
159
+ return (f"Region '{name}' {cause}, so no extent can be resolved and no "
160
+ f"class derived.{sizing} Declare `base:` in this SoM preset's "
161
+ f"`memory_map:` to resolve it.")
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env python3
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ """Memory-region math shared across the resolvers -- a leaf.
4
+
5
+ The page granularity and the memory_map size-field -> bytes conversion, used by
6
+ both the carve-out resolver and the storage-partition resolver. A dependency-
7
+ free leaf (typing only) so each resolver imports it directly instead of one
8
+ reaching into the other (or back through the package __init__). Extracted as a
9
+ #285 leaf seam -- the same move as paths.py -- so the partition resolver lands
10
+ cleanly.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ from typing import Any, Optional
16
+
17
+ # Page granularity for carve-out / partition base + size alignment.
18
+ _PAGE = 4096
19
+
20
+
21
+ def _region_size_bytes(region: dict[str, Any]) -> Optional[int]:
22
+ """Convert a memory_map entry's size_mib / size_kib to bytes.
23
+
24
+ Reads each field by its VALUE, not by key presence: the
25
+ `memory_region` schema lets `size_kib` and `size_mib` each
26
+ independently be an int or the literal `"TBD"`, so a `"TBD"` in one
27
+ field must never mask a usable integer sitting in the other (e.g.
28
+ `{size_mib: "TBD", size_kib: 64}` must resolve via `size_kib`, not
29
+ bail out because `size_mib` is present but unresolved). `size_mib`
30
+ is checked first -- matching this module's real prior behaviour: on
31
+ `origin/dev` before alp-sdk#1365 split B, this function's only two
32
+ callers (`carveout.py` and `partition.py`) both went through a
33
+ key-PRESENCE check that looked at `size_mib` first
34
+ (`if "size_mib" in region: ...`). `check_atoc_reservation.py` was
35
+ NOT a caller of this module then -- it carried its own private,
36
+ unrelated `_region_size_kib` helper (kib-first), which split B
37
+ retired in favour of importing this module instead. That gate
38
+ becoming a caller here is what split B changed; it is not evidence
39
+ of this module's own prior precedence, which was mib-first.
40
+
41
+ The schema permits both fields on one region (neither `som-preset-
42
+ v1.schema.json`'s `memory_region` nor `soc-spec-v1.schema.json`'s
43
+ forbids it), and no region in `metadata/e1m_modules/*.yaml` today
44
+ sets both, so this precedence has no real-preset evidence either
45
+ way -- pinned here by test only, matching the module's actual
46
+ history rather than a caller that did not exist yet.
47
+
48
+ Returns None only when NEITHER field resolves to a usable integer.
49
+ """
50
+ size_mib = region.get("size_mib")
51
+ if isinstance(size_mib, int) and not isinstance(size_mib, bool):
52
+ return size_mib * 1024 * 1024
53
+ size_kib = region.get("size_kib")
54
+ if isinstance(size_kib, int) and not isinstance(size_kib, bool):
55
+ return size_kib * 1024
56
+ return None