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.
- tan/__init__.py +0 -0
- tan/__main__.py +120 -0
- tan/cli.py +939 -0
- tan/command_table.py +265 -0
- tan/commands/__init__.py +8 -0
- tan/commands/bootstrap_cmd.py +4856 -0
- tan/commands/bootstrap_patches.py +164 -0
- tan/commands/bootstrap_toolchain_fetch.py +155 -0
- tan/commands/bootstrap_toolchain_pin.py +36 -0
- tan/commands/build/__init__.py +1 -0
- tan/commands/build/configure_inputs.py +277 -0
- tan/commands/build/execute.py +1852 -0
- tan/commands/build/host_python.py +59 -0
- tan/commands/build/link_stale.py +78 -0
- tan/commands/build/manifest.py +428 -0
- tan/commands/build/materialise.py +81 -0
- tan/commands/build/token_substitution.py +381 -0
- tan/commands/build/toolchain.py +482 -0
- tan/commands/build/user_defines_wipe.py +115 -0
- tan/commands/build/workspace_patches.py +65 -0
- tan/commands/build_cmd.py +2236 -0
- tan/commands/build_output.py +331 -0
- tan/commands/clean_cmd.py +1183 -0
- tan/commands/completion_cmd.py +786 -0
- tan/commands/debug_config_cmd.py +2425 -0
- tan/commands/diff_cmd.py +940 -0
- tan/commands/doctor_cmd.py +4767 -0
- tan/commands/examples_cmd.py +644 -0
- tan/commands/explain_cmd.py +1319 -0
- tan/commands/faultdecode_cmd.py +931 -0
- tan/commands/flash_atoc_guard.py +240 -0
- tan/commands/flash_cmd.py +5731 -0
- tan/commands/flash_mram_guard.py +150 -0
- tan/commands/flash_ram.py +578 -0
- tan/commands/flash_raw.py +583 -0
- tan/commands/generate_cmd.py +1456 -0
- tan/commands/image_cmd.py +753 -0
- tan/commands/init_cmd.py +1740 -0
- tan/commands/inspect_cmd.py +377 -0
- tan/commands/kconfig_cmd.py +734 -0
- tan/commands/model_cmd.py +1514 -0
- tan/commands/model_device_cmd.py +351 -0
- tan/commands/model_device_live.py +234 -0
- tan/commands/model_host_cmd.py +430 -0
- tan/commands/model_zoo_cmd.py +363 -0
- tan/commands/monitor_actions.py +125 -0
- tan/commands/monitor_cmd.py +626 -0
- tan/commands/monitor_session.py +505 -0
- tan/commands/new_som_cmd.py +1522 -0
- tan/commands/pinmux_cmd.py +568 -0
- tan/commands/presets_cmd.py +1123 -0
- tan/commands/probe_cmd.py +421 -0
- tan/commands/reset_cmd.py +354 -0
- tan/commands/reset_confirm.py +168 -0
- tan/commands/run_cmd.py +637 -0
- tan/commands/scaffold_cmd.py +490 -0
- tan/commands/sdk_cmd.py +1723 -0
- tan/commands/size_cmd.py +1020 -0
- tan/commands/support_bundle_cmd.py +1073 -0
- tan/commands/trace_cmd.py +341 -0
- tan/commands/validate_cmd.py +2009 -0
- tan/commands/west_forward_cmd.py +736 -0
- tan/commands/workspace_patch_check.py +296 -0
- tan/core/__init__.py +1 -0
- tan/core/alp_conf_pregen.py +100 -0
- tan/core/artifact_provenance.py +240 -0
- tan/core/atoc_guard.py +276 -0
- tan/core/atoc_guard_messages.py +232 -0
- tan/core/atoc_replacement.py +118 -0
- tan/core/atomic_write.py +178 -0
- tan/core/board_context.py +351 -0
- tan/core/board_diagnostic.py +111 -0
- tan/core/board_files.py +221 -0
- tan/core/board_validator.py +542 -0
- tan/core/board_validator_compat.py +196 -0
- tan/core/board_validator_run.py +129 -0
- tan/core/board_validator_schema.py +186 -0
- tan/core/board_validator_skew.py +46 -0
- tan/core/board_yaml_edit.py +295 -0
- tan/core/board_yaml_pos.py +118 -0
- tan/core/bootstrap.py +2572 -0
- tan/core/build_plan.py +625 -0
- tan/core/consent.py +104 -0
- tan/core/console_filter.py +245 -0
- tan/core/debug_launch.py +1803 -0
- tan/core/devicetree_lint.py +168 -0
- tan/core/dir_removal.py +229 -0
- tan/core/doctor_git.py +172 -0
- tan/core/doctor_libraries.py +253 -0
- tan/core/doctor_render.py +185 -0
- tan/core/doctor_scope.py +66 -0
- tan/core/doctor_setools.py +237 -0
- tan/core/doctor_stale.py +180 -0
- tan/core/document_guards.py +723 -0
- tan/core/dp_id.py +97 -0
- tan/core/elf_load.py +63 -0
- tan/core/error_catalog.py +171 -0
- tan/core/example_catalog.py +499 -0
- tan/core/example_facets.py +151 -0
- tan/core/faultdecode.py +521 -0
- tan/core/flash_plan.py +2501 -0
- tan/core/flow_d_report.py +381 -0
- tan/core/flow_d_uptime.py +164 -0
- tan/core/fs_confine.py +130 -0
- tan/core/global_flags.py +230 -0
- tan/core/host_python.py +210 -0
- tan/core/image_bundle.py +247 -0
- tan/core/inert.py +147 -0
- tan/core/jlink_binary.py +128 -0
- tan/core/jlink_probe.py +481 -0
- tan/core/jsonc_splice.py +404 -0
- tan/core/launch_provenance.py +257 -0
- tan/core/link_refusal.py +76 -0
- tan/core/metadata_schema.py +376 -0
- tan/core/model_check.py +227 -0
- tan/core/model_device.py +449 -0
- tan/core/model_doctor.py +198 -0
- tan/core/model_host.py +48 -0
- tan/core/model_list.py +112 -0
- tan/core/model_zoo.py +353 -0
- tan/core/module_template.py +340 -0
- tan/core/mram_link.py +87 -0
- tan/core/new_som_ospi.py +94 -0
- tan/core/os_class.py +159 -0
- tan/core/patch_paths.py +87 -0
- tan/core/pending.py +40 -0
- tan/core/plain_zephyr_plan.py +138 -0
- tan/core/plan_exec.py +373 -0
- tan/core/plan_tokens.py +600 -0
- tan/core/probe.py +155 -0
- tan/core/probe_plan.py +237 -0
- tan/core/proxy.py +168 -0
- tan/core/publish.py +31 -0
- tan/core/python_floor.py +178 -0
- tan/core/ram_run.py +694 -0
- tan/core/ram_watch.py +231 -0
- tan/core/raw_write.py +160 -0
- tan/core/reset_plan.py +56 -0
- tan/core/run.py +161 -0
- tan/core/scaffold.py +2203 -0
- tan/core/scaffold_selftest_identity.py +225 -0
- tan/core/sdk_default_registry.py +565 -0
- tan/core/sdk_discovery.py +996 -0
- tan/core/sdk_removal.py +465 -0
- tan/core/serial_actions.py +175 -0
- tan/core/serial_capture.py +185 -0
- tan/core/serial_url.py +45 -0
- tan/core/setools.py +608 -0
- tan/core/setools_scratch.py +243 -0
- tan/core/shapes.py +194 -0
- tan/core/size.py +610 -0
- tan/core/som_buildability.py +204 -0
- tan/core/som_schema_version.py +62 -0
- tan/core/subprocess_env.py +201 -0
- tan/core/sysbuild_image.py +94 -0
- tan/core/system_manifest.py +827 -0
- tan/core/text_layout.py +73 -0
- tan/core/timestamp.py +182 -0
- tan/core/tool_lookup.py +383 -0
- tan/core/toolchain_pin.py +117 -0
- tan/core/toolchain_provision.py +1224 -0
- tan/core/trusted_path.py +134 -0
- tan/core/uboot_breakin.py +154 -0
- tan/core/uri_reference.py +415 -0
- tan/core/user_defines.py +152 -0
- tan/core/venv.py +332 -0
- tan/core/west_patches.py +143 -0
- tan/core/west_workspace_refusal.py +159 -0
- tan/core/zephyr_env.py +124 -0
- tan/env.py +185 -0
- tan/envelope.py +647 -0
- tan/exit_codes.py +12 -0
- tan/lazy_typer.py +185 -0
- tan/model/__init__.py +2 -0
- tan/model/_gen_fixture.py +132 -0
- tan/model/adapters/__init__.py +116 -0
- tan/model/adapters/cpu.py +26 -0
- tan/model/adapters/deepx.py +94 -0
- tan/model/adapters/drpai.py +242 -0
- tan/model/adapters/ethos_u.py +951 -0
- tan/model/adapters/executorch.py +55 -0
- tan/model/analyze.py +694 -0
- tan/model/build.py +304 -0
- tan/model/check.py +682 -0
- tan/model/manifest.py +625 -0
- tan/model/measure.py +216 -0
- tan/model/npu_ops_validate.py +130 -0
- tan/model/package.py +125 -0
- tan/model/perf.py +635 -0
- tan/model/perf_apply.py +721 -0
- tan/model/prep.py +193 -0
- tan/model/sram_fit.py +502 -0
- tan/model/targets.py +374 -0
- tan/model/tensorio.py +287 -0
- tan/net.py +73 -0
- tan/output_format.py +213 -0
- tan/planner/__init__.py +219 -0
- tan/planner/aperture.py +202 -0
- tan/planner/buildplan.py +1092 -0
- tan/planner/camera_owner.py +192 -0
- tan/planner/cameras.py +132 -0
- tan/planner/carveout.py +755 -0
- tan/planner/cli.py +274 -0
- tan/planner/headers.py +315 -0
- tan/planner/kconfig.py +2435 -0
- tan/planner/kconfig_symbols.py +473 -0
- tan/planner/libraries.py +677 -0
- tan/planner/link_target.py +237 -0
- tan/planner/linux_ownership.py +216 -0
- tan/planner/loader.py +1525 -0
- tan/planner/manifest.py +116 -0
- tan/planner/memory.py +161 -0
- tan/planner/memregion.py +56 -0
- tan/planner/models.py +533 -0
- tan/planner/orchestrator.py +874 -0
- tan/planner/ownership.py +203 -0
- tan/planner/partition.py +1160 -0
- tan/planner/paths.py +32 -0
- tan/planner/plain_slice.py +139 -0
- tan/planner/project_emit/__init__.py +60 -0
- tan/planner/project_emit/bom_netlist.py +375 -0
- tan/planner/project_emit/dts.py +580 -0
- tan/planner/project_emit/hw_info.py +221 -0
- tan/planner/project_emit/native_sim.py +136 -0
- tan/planner/project_emit/west_libs.py +213 -0
- tan/planner/project_loader.py +264 -0
- tan/planner/sdk_capability.py +203 -0
- tan/planner/sdk_compat.py +395 -0
- tan/planner/secure.py +350 -0
- tan/planner/slugs.py +394 -0
- tan/planner/som_metadata.py +325 -0
- tan/planner/strict_loaders.py +131 -0
- tan/planner/template.py +1037 -0
- tan/planner/template_pins.py +535 -0
- tan/planner/template_rewrite.py +714 -0
- tan/planner/topology.py +208 -0
- tan/planner/validate.py +365 -0
- tan/planner/whole_device_alias.py +67 -0
- tan/planner/zephyr_board.py +3510 -0
- tan/planner_cli.py +97 -0
- tan/planner_emit.py +617 -0
- tan/planner_root.py +119 -0
- tan/soc_ref.py +58 -0
- tan/templates/__init__.py +34 -0
- tan/templates/vendored/MANIFEST.md +1862 -0
- tan/templates/vendored/diagnostics/E1M-AEN801/CMakeLists.txt +8 -0
- tan/templates/vendored/diagnostics/E1M-AEN801/README.md +112 -0
- tan/templates/vendored/diagnostics/E1M-AEN801/board.yaml +38 -0
- tan/templates/vendored/diagnostics/E1M-AEN801/boards/native_sim_native_64.conf +11 -0
- tan/templates/vendored/diagnostics/E1M-AEN801/boards/native_sim_native_64.overlay +29 -0
- tan/templates/vendored/diagnostics/E1M-AEN801/prj.conf +5 -0
- tan/templates/vendored/diagnostics/E1M-AEN801/src/main.c +266 -0
- tan/templates/vendored/diagnostics/E1M-AEN801/testcase.yaml +53 -0
- tan/templates/vendored/diagnostics/E1M-V2N101/CMakeLists.txt +8 -0
- tan/templates/vendored/diagnostics/E1M-V2N101/README.md +112 -0
- tan/templates/vendored/diagnostics/E1M-V2N101/board.yaml +38 -0
- tan/templates/vendored/diagnostics/E1M-V2N101/boards/native_sim_native_64.conf +11 -0
- tan/templates/vendored/diagnostics/E1M-V2N101/boards/native_sim_native_64.overlay +29 -0
- tan/templates/vendored/diagnostics/E1M-V2N101/prj.conf +5 -0
- tan/templates/vendored/diagnostics/E1M-V2N101/src/main.c +266 -0
- tan/templates/vendored/diagnostics/E1M-V2N101/testcase.yaml +53 -0
- tan/templates/vendored/edge-ai/E1M-AEN801/CMakeLists.txt +10 -0
- tan/templates/vendored/edge-ai/E1M-AEN801/README.md +75 -0
- tan/templates/vendored/edge-ai/E1M-AEN801/board.yaml +38 -0
- tan/templates/vendored/edge-ai/E1M-AEN801/prj.conf +8 -0
- tan/templates/vendored/edge-ai/E1M-AEN801/src/cold_chain.c +371 -0
- tan/templates/vendored/edge-ai/E1M-AEN801/src/cold_chain.h +178 -0
- tan/templates/vendored/edge-ai/E1M-AEN801/src/main.c +373 -0
- tan/templates/vendored/edge-ai/E1M-AEN801/testcase.yaml +62 -0
- tan/templates/vendored/edge-ai/E1M-V2N101/CMakeLists.txt +10 -0
- tan/templates/vendored/edge-ai/E1M-V2N101/README.md +75 -0
- tan/templates/vendored/edge-ai/E1M-V2N101/board.yaml +37 -0
- tan/templates/vendored/edge-ai/E1M-V2N101/prj.conf +8 -0
- tan/templates/vendored/edge-ai/E1M-V2N101/src/cold_chain.c +371 -0
- tan/templates/vendored/edge-ai/E1M-V2N101/src/cold_chain.h +178 -0
- tan/templates/vendored/edge-ai/E1M-V2N101/src/main.c +373 -0
- tan/templates/vendored/edge-ai/E1M-V2N101/testcase.yaml +62 -0
- tan/templates/vendored/iot/E1M-AEN801/CMakeLists.txt +14 -0
- tan/templates/vendored/iot/E1M-AEN801/README.md +103 -0
- tan/templates/vendored/iot/E1M-AEN801/board.yaml +69 -0
- tan/templates/vendored/iot/E1M-AEN801/prj.conf +15 -0
- tan/templates/vendored/iot/E1M-AEN801/src/cc3501e_bridge.c +196 -0
- tan/templates/vendored/iot/E1M-AEN801/src/cc3501e_bridge.h +173 -0
- tan/templates/vendored/iot/E1M-AEN801/src/main.c +262 -0
- tan/templates/vendored/iot/E1M-AEN801/testcase.yaml +68 -0
- tan/templates/vendored/minimal/E1M-AEN801/CMakeLists.txt +8 -0
- tan/templates/vendored/minimal/E1M-AEN801/README.md +108 -0
- tan/templates/vendored/minimal/E1M-AEN801/board.yaml +32 -0
- tan/templates/vendored/minimal/E1M-AEN801/prj.conf +8 -0
- tan/templates/vendored/minimal/E1M-AEN801/src/main.c +111 -0
- tan/templates/vendored/minimal/E1M-AEN801/testcase.yaml +49 -0
- tan/templates/vendored/minimal/E1M-V2N101/CMakeLists.txt +8 -0
- tan/templates/vendored/minimal/E1M-V2N101/README.md +108 -0
- tan/templates/vendored/minimal/E1M-V2N101/board.yaml +32 -0
- tan/templates/vendored/minimal/E1M-V2N101/prj.conf +8 -0
- tan/templates/vendored/minimal/E1M-V2N101/src/main.c +111 -0
- tan/templates/vendored/minimal/E1M-V2N101/testcase.yaml +49 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/CMakeLists.txt +8 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/README.md +120 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/board.yaml +57 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/boards/native_sim_native_64.overlay +40 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/peer/CMakeLists.txt +13 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/peer/main.c +171 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/peer/prj.conf +17 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/peer/testcase.yaml +23 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/prj.conf +22 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/src/main.c +185 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/testcase.yaml +26 -0
- tan/templates/vendored/sensor/E1M-AEN801/CMakeLists.txt +8 -0
- tan/templates/vendored/sensor/E1M-AEN801/README.md +112 -0
- tan/templates/vendored/sensor/E1M-AEN801/board.yaml +57 -0
- tan/templates/vendored/sensor/E1M-AEN801/boards/native_sim_native_64.conf +10 -0
- tan/templates/vendored/sensor/E1M-AEN801/boards/native_sim_native_64.overlay +28 -0
- tan/templates/vendored/sensor/E1M-AEN801/prj.conf +5 -0
- tan/templates/vendored/sensor/E1M-AEN801/src/main.c +186 -0
- tan/templates/vendored/sensor/E1M-AEN801/testcase.yaml +52 -0
- tan/templates/vendored/sensor/E1M-V2N101/CMakeLists.txt +8 -0
- tan/templates/vendored/sensor/E1M-V2N101/README.md +112 -0
- tan/templates/vendored/sensor/E1M-V2N101/board.yaml +57 -0
- tan/templates/vendored/sensor/E1M-V2N101/boards/native_sim_native_64.conf +10 -0
- tan/templates/vendored/sensor/E1M-V2N101/boards/native_sim_native_64.overlay +28 -0
- tan/templates/vendored/sensor/E1M-V2N101/prj.conf +5 -0
- tan/templates/vendored/sensor/E1M-V2N101/src/main.c +186 -0
- tan/templates/vendored/sensor/E1M-V2N101/testcase.yaml +52 -0
- tan/version.py +102 -0
- tan_cli-0.7.0.dist-info/METADATA +125 -0
- tan_cli-0.7.0.dist-info/RECORD +330 -0
- tan_cli-0.7.0.dist-info/WHEEL +5 -0
- tan_cli-0.7.0.dist-info/entry_points.txt +2 -0
- tan_cli-0.7.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,535 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""SoM/board-metadata reads for `template.py`'s `--emit scaffold` sku swap.
|
|
3
|
+
|
|
4
|
+
SPLIT out of `tan/planner/template.py` (tan-cli#1142; that module was 2206
|
|
5
|
+
lines against `MODULE_CAP = 800`). This is NOT a second hand-port: both this
|
|
6
|
+
module and `template_rewrite.py` are the SAME `HAND_PORT_SOURCES` entry
|
|
7
|
+
(`scripts/alp_template.py`) as `template.py` itself -- see
|
|
8
|
+
`test_planner_relocation_freshness.py::HAND_PORT_SOURCES` and
|
|
9
|
+
`test_hand_port_tan_side.py::HAND_PORT_TAN_SIDE`, both of which map all three
|
|
10
|
+
tan files to the one upstream source, the same many-to-one shape
|
|
11
|
+
`alp_project_loader.py` already has for `project_loader.py` + `som_metadata.py`.
|
|
12
|
+
|
|
13
|
+
`_module_size_budget_core.MIRRORED_PREFIX` ("upstream's to split, not this
|
|
14
|
+
repo's") does NOT bar this split: that comment describes `PINNED_HASHES`
|
|
15
|
+
modules, which are 3-way MERGED against a moving upstream file and would
|
|
16
|
+
conflict on every future upstream hunk if they diverged in shape here.
|
|
17
|
+
`template.py` is `HAND_PORT_SOURCES`-tracked instead -- FLAGGED, never merged
|
|
18
|
+
(`test_planner_relocation_freshness.py`'s own module docstring) -- so there is
|
|
19
|
+
no base/theirs/ours triple for a shape change to break. See
|
|
20
|
+
`_module_size_budget_core.py`'s corrected `MIRRORED_PREFIX` comment and
|
|
21
|
+
`test_module_size_budget.py::test_the_mirrored_planner_is_named_as_out_of_scope`,
|
|
22
|
+
which now checks only the `PINNED_HASHES` subset.
|
|
23
|
+
|
|
24
|
+
WHAT'S HERE: every read of `metadata/e1m_modules/<sku>.yaml` /
|
|
25
|
+
`metadata/boards/<board>.yaml` and the sku/core/pin RE-DERIVATION this module's
|
|
26
|
+
functions compute from them -- `_load_som_doc` through `_derive_pin_doc_renames`,
|
|
27
|
+
plus `_core_board` (moved here despite sitting elsewhere in the original file,
|
|
28
|
+
alongside the other `_topology_for_sku` reader rather than beside the
|
|
29
|
+
`board.yaml` TEXT rewriters in `template_rewrite.py`, which is where it lived
|
|
30
|
+
physically before the split). Every function here reads metadata and returns a
|
|
31
|
+
plain value or a rename map; none of them touch `board.yaml`/CMakeLists.txt/
|
|
32
|
+
README.md TEXT -- that half is `template_rewrite.py`.
|
|
33
|
+
|
|
34
|
+
Every docstring below is UNCHANGED from `template.py` -- issue numbers, tan-cli
|
|
35
|
+
numbers and worked examples all still refer to that module's own history,
|
|
36
|
+
because this split moved code, not its provenance.
|
|
37
|
+
|
|
38
|
+
Imports `TemplateError` / `_require_field` / `_read_yaml_mapping` back FROM
|
|
39
|
+
`.template` rather than the reverse: `template.py` still owns the
|
|
40
|
+
`DocumentGuards(TemplateError)` binding (`_GUARDS` and friends), since
|
|
41
|
+
`_rendered_bytes`/`render_to_envelope` stayed there and need
|
|
42
|
+
it too, and `cli.py`'s `from .template import (TemplateError, emit_scaffold,
|
|
43
|
+
find_template_by_cores, load_catalog)` must keep resolving unchanged. That
|
|
44
|
+
makes the import direction CIRCULAR at the module-object level (`template.py`
|
|
45
|
+
imports names from this module for its own re-exports, and this module imports
|
|
46
|
+
`TemplateError`/`_require_field`/`_read_yaml_mapping` back from `template.py`)
|
|
47
|
+
-- which works only because `template.py` binds all three before its own
|
|
48
|
+
`from .template_pins import ...` line runs; see that file's own comment at the
|
|
49
|
+
same spot. Do not move this module's import of `.template` above where
|
|
50
|
+
`template.py` defines those three names, and do not move `template.py`'s
|
|
51
|
+
import of this module above its own `_GUARDS` block.
|
|
52
|
+
"""
|
|
53
|
+
from __future__ import annotations
|
|
54
|
+
|
|
55
|
+
from pathlib import Path
|
|
56
|
+
from typing import Any
|
|
57
|
+
|
|
58
|
+
from .template import TemplateError, _read_yaml_mapping, _require_field
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _load_som_doc(sku: str, metadata_root: Path) -> dict[str, Any]:
|
|
62
|
+
"""Parse metadata/e1m_modules/<sku>.yaml -- shared by
|
|
63
|
+
`_default_preset_for_sku` (the `default_board:` field),
|
|
64
|
+
`_derive_core_renames` (the `topology:` block), and `_core_board`
|
|
65
|
+
(also `topology:`), so all three read the exact same doc for the
|
|
66
|
+
same `(sku, metadata_root)`."""
|
|
67
|
+
som_path = metadata_root / "e1m_modules" / f"{sku}.yaml"
|
|
68
|
+
# Mirrors `resolve_targets`'s `preset` guard (tan-cli#1010,
|
|
69
|
+
# `tan/model/targets.py:312-323`): a SoM YAML that parses but is not
|
|
70
|
+
# a mapping (e.g. a bare list or a bare scalar) must not reach a
|
|
71
|
+
# caller's bare `.get(...)` -- every caller of this function does
|
|
72
|
+
# exactly that -- which raises a raw AttributeError instead of a
|
|
73
|
+
# curated error a caller can distinguish from any other such error
|
|
74
|
+
# in the call stack (tan-cli#1025). Shared with the module's two
|
|
75
|
+
# other outer-document reads since tan-cli#1052; the message is
|
|
76
|
+
# unchanged.
|
|
77
|
+
#
|
|
78
|
+
# tan-cli#1133: the READ and the PARSE are guarded too, and the
|
|
79
|
+
# `is_file()` pre-flight this used to open with is gone -- see
|
|
80
|
+
# `_read_yaml_mapping`. The missing-file message is byte-identical to
|
|
81
|
+
# the one that pre-flight raised (pinned by `tests/planner/
|
|
82
|
+
# test_render_to_envelope_malformed_example_board.py`).
|
|
83
|
+
return _read_yaml_mapping(
|
|
84
|
+
som_path, what="SoM preset",
|
|
85
|
+
absent=f"no metadata/e1m_modules/{sku}.yaml for sku {sku!r}")
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _default_preset_for_sku(sku: str, metadata_root: Path) -> str:
|
|
89
|
+
"""The board preset a fresh project targeting `sku` ships with by
|
|
90
|
+
default -- metadata/e1m_modules/<sku>.yaml's `default_board:`,
|
|
91
|
+
lower-cased to match the `preset:` value every example board.yaml
|
|
92
|
+
already uses (e.g. `E1M-EVK` -> `e1m-evk`, `E1M-X-EVK` ->
|
|
93
|
+
`e1m-x-evk`). This is the SAME field board.yaml's own comments point
|
|
94
|
+
customers at by hand ("copy this directory, change som.sku ...,
|
|
95
|
+
edit the preset:") -- render_to_envelope just does that edit for
|
|
96
|
+
them."""
|
|
97
|
+
board = _load_som_doc(sku, metadata_root).get("default_board")
|
|
98
|
+
if not board:
|
|
99
|
+
raise TemplateError(
|
|
100
|
+
f"metadata/e1m_modules/{sku}.yaml has no default_board")
|
|
101
|
+
# Same document-class guard as `_load_som_doc` itself (tan-cli#1025
|
|
102
|
+
# sweep): a `default_board:` that parses but isn't a scalar string
|
|
103
|
+
# (e.g. a YAML list) must not reach `.lower()` bare -- that raises
|
|
104
|
+
# `AttributeError: 'list' object has no attribute 'lower'` instead
|
|
105
|
+
# of a curated error naming the file.
|
|
106
|
+
_require_field(board, str, doc=f"metadata/e1m_modules/{sku}.yaml",
|
|
107
|
+
field="default_board")
|
|
108
|
+
return board.lower()
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _topology_for_sku(sku: str, metadata_root: Path) -> dict[str, Any]:
|
|
112
|
+
"""`metadata/e1m_modules/<sku>.yaml` `topology:` -- shared by
|
|
113
|
+
`_derive_core_renames` and `_core_board`, both of which key into the
|
|
114
|
+
result by core id (`.items()`/`.get(core_id)`) and then read a
|
|
115
|
+
`board:`/`app:` field off each core's own entry (`spec.get(...)`).
|
|
116
|
+
|
|
117
|
+
Same document-class guard as `_load_som_doc` itself (tan-cli#1025
|
|
118
|
+
sweep, round 2 -- the first round guarded the outer document but
|
|
119
|
+
left every nested read this function serves unguarded): a
|
|
120
|
+
`topology:` block that parses but is not itself a mapping (e.g. a
|
|
121
|
+
bare list) must not reach either caller's bare `.items()`/`.get()`
|
|
122
|
+
-- that raises a raw AttributeError instead of a curated error
|
|
123
|
+
naming the file and the actual type. Guarding the OUTER shape here,
|
|
124
|
+
once, also closes a quieter failure mode a plain per-caller
|
|
125
|
+
AttributeError guard would not: `_derive_core_renames`'s own `cid
|
|
126
|
+
not in topology` membership test does not raise against a list
|
|
127
|
+
`topology` at all (a list supports `in`), so an unguarded list would
|
|
128
|
+
silently resolve `stale == []` and return `None` -- a byte-
|
|
129
|
+
identical-passthrough verdict that is not true, instead of
|
|
130
|
+
surfacing the malformed document.
|
|
131
|
+
|
|
132
|
+
Each core's own entry is validated too, for the same reason one
|
|
133
|
+
level down: `_derive_core_renames`'s `spec.get("board")` and
|
|
134
|
+
`_core_board`'s `topology.get(core_id).get("board")` both assume
|
|
135
|
+
the per-core value is itself a mapping. A `topology:` that IS a
|
|
136
|
+
mapping but whose value for some core id is a bare list/scalar
|
|
137
|
+
(legal YAML, illegal `som-preset` schema) would otherwise reach
|
|
138
|
+
those bare `.get()` calls unguarded -- validating every entry here,
|
|
139
|
+
once, closes that for both callers the same way the outer check
|
|
140
|
+
does."""
|
|
141
|
+
som_yaml = f"metadata/e1m_modules/{sku}.yaml"
|
|
142
|
+
topology = _require_field(
|
|
143
|
+
_load_som_doc(sku, metadata_root).get("topology") or {}, dict,
|
|
144
|
+
doc=som_yaml, field="topology")
|
|
145
|
+
for core_id, spec in topology.items():
|
|
146
|
+
_require_field(spec, dict, doc=som_yaml, field=f"topology.{core_id}")
|
|
147
|
+
return topology
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _derive_core_renames(
|
|
151
|
+
original_core_ids: list[str], sku: str, metadata_root: Path,
|
|
152
|
+
) -> dict[str, str] | None:
|
|
153
|
+
"""Re-derive every STALE core id a catalog template's `cores:`
|
|
154
|
+
block declares, for `sku`'s OWN SoM topology (issue #864 follow-up:
|
|
155
|
+
the shallow "byte-copy the example + swap som.sku" `render_to_
|
|
156
|
+
envelope()` #864 shipped hard-coded the CANONICAL example's own
|
|
157
|
+
core id -- e.g. `m55_hp`, an Alif-only Zephyr cluster -- into every
|
|
158
|
+
substituted board.yaml, emitting a non-buildable
|
|
159
|
+
scaffold for any cross-SoM-family sku: `alp_project.py --emit
|
|
160
|
+
zephyr-conf --core m55_hp` against an E1M-V2N101 board.yaml fails
|
|
161
|
+
with rc=1, "unknown core id ... did you mean ['a55_cluster',
|
|
162
|
+
'm33_sm']").
|
|
163
|
+
|
|
164
|
+
EVERY key `cores:` declares must exist in the target sku's own
|
|
165
|
+
topology -- `alp_orchestrate.loader._validate_topology_cores` hard-
|
|
166
|
+
errors on an unmatched key unconditionally, whether or not that
|
|
167
|
+
core is `os: off` -- so a template that also declares the OTHER
|
|
168
|
+
cluster explicitly disabled (edge-ai's `cores.a32_cluster: {os:
|
|
169
|
+
off}`, alongside the active `m55_hp`) needs THAT id renamed too,
|
|
170
|
+
not just the one core the app actually runs on.
|
|
171
|
+
|
|
172
|
+
Returns `None` when every declared id already exists in `sku`'s
|
|
173
|
+
`metadata/e1m_modules/<sku>.yaml` `topology:` -- the canonical
|
|
174
|
+
example's own SoM, or a same-family sibling that shares its core
|
|
175
|
+
ids -- a byte-identical passthrough, nothing to rewrite. Otherwise
|
|
176
|
+
returns `{old_core_id: new_core_id, ...}` for every stale id: each
|
|
177
|
+
replacement is `sku`'s own topology core sharing the same leading
|
|
178
|
+
core-class letter (`m`/`a`), additionally requiring a Zephyr
|
|
179
|
+
`board:` target for an `m`-class replacement (only that core is
|
|
180
|
+
ever `--core`-buildable); an `a`-class utility core carries
|
|
181
|
+
no such requirement (it's only ever `os: off` in every template
|
|
182
|
+
that declares one today).
|
|
183
|
+
|
|
184
|
+
Candidates are picked in `topology:`'s OWN declaration order, NOT
|
|
185
|
+
alphabetically (issue #864 Fable-review MAJOR D): a multi-m-core
|
|
186
|
+
SoM's PRIMARY app core is whichever one the SoM preset author
|
|
187
|
+
listed first, not whichever sorts first -- E1M-AEN801's `topology:`
|
|
188
|
+
declares `m55_hp` (the real app core) before `m55_he` (a stock-shim
|
|
189
|
+
peer core), but `m55_he` sorts first alphabetically.
|
|
190
|
+
`alp_project_emit.hw_info._pick_primary_core_os` picks its "primary
|
|
191
|
+
core" alphabetically -- that convention is NOT reused here for
|
|
192
|
+
exactly this reason; it would silently rename onto the wrong core
|
|
193
|
+
the day any multi-m-core SKU joins a template's supported set
|
|
194
|
+
(verified: `_derive_core_renames(["m33_sm"], "E1M-AEN801", ...)`
|
|
195
|
+
resolved `m55_he`, not the real app core `m55_hp`, before this
|
|
196
|
+
fix -- unreachable today, since no template's `supported.som_skus`
|
|
197
|
+
combo exercises it, but latently wrong).
|
|
198
|
+
"""
|
|
199
|
+
topology = _topology_for_sku(sku, metadata_root)
|
|
200
|
+
stale = [cid for cid in original_core_ids if cid not in topology]
|
|
201
|
+
if not stale:
|
|
202
|
+
return None
|
|
203
|
+
claimed = set(original_core_ids) & set(topology)
|
|
204
|
+
renames: dict[str, str] = {}
|
|
205
|
+
for old in stale:
|
|
206
|
+
prefix = old[0]
|
|
207
|
+
require_board = prefix == "m"
|
|
208
|
+
candidates = [
|
|
209
|
+
cid for cid, spec in topology.items()
|
|
210
|
+
if cid.startswith(prefix) and cid not in claimed
|
|
211
|
+
and cid not in renames.values()
|
|
212
|
+
and (spec.get("board") if require_board else True)
|
|
213
|
+
]
|
|
214
|
+
if not candidates:
|
|
215
|
+
raise TemplateError(
|
|
216
|
+
f"metadata/e1m_modules/{sku}.yaml topology has no "
|
|
217
|
+
f"{prefix!r}-class core"
|
|
218
|
+
+ (" with a Zephyr `board:` target" if require_board else "")
|
|
219
|
+
+ f" to replace {old!r}")
|
|
220
|
+
renames[old] = candidates[0]
|
|
221
|
+
return renames
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
_ROUTE_SECTIONS = ("gpio", "buses", "pwm", "adc", "dac", "i2s", "can", "qenc")
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def _board_route_entries(board_name: str, metadata_root: Path) -> list[dict[str, Any]]:
|
|
228
|
+
"""Every metadata/boards/<board_name>.yaml `e1m_routes:` entry,
|
|
229
|
+
flattened across sections -- mirrors
|
|
230
|
+
scripts/gen_portability_matrix.py's `_route_entries` (same section
|
|
231
|
+
list, same join; mirrored here rather than imported across module
|
|
232
|
+
boundaries for a handful of lines).
|
|
233
|
+
|
|
234
|
+
Same document-class guard as `_load_som_doc`/`_topology_for_sku`
|
|
235
|
+
(tan-cli#1037, its own round-two amendment, plus a same-shape
|
|
236
|
+
third level a PR #1048 review found still bare): a `metadata/
|
|
237
|
+
boards/<board_name>.yaml` that parses but is not itself a mapping
|
|
238
|
+
(e.g. a bare list) must not reach the outer `.get("e1m_routes")`
|
|
239
|
+
bare; an `e1m_routes:` that parses but is not itself a mapping
|
|
240
|
+
must not reach the per-section `.get(section)` one line down; and
|
|
241
|
+
a per-section value (`e1m_routes.<section>`) that parses but is
|
|
242
|
+
not itself a list must not reach the `for entry in ...` below --
|
|
243
|
+
all three raised a raw `AttributeError`/`TypeError` instead of a
|
|
244
|
+
curated error naming the file, the section, and the actual type
|
|
245
|
+
(tan-cli#1037's Measured, PR #1034's round-two review, and PR
|
|
246
|
+
#1048's review, respectively). The third level mirrors
|
|
247
|
+
`_topology_for_sku`'s own outer-plus-per-entry shape: guarding
|
|
248
|
+
every entry, not just the container, closes the same class one
|
|
249
|
+
level down that guarding only `e1m_routes:` itself would leave
|
|
250
|
+
open."""
|
|
251
|
+
board_path = metadata_root / "boards" / f"{board_name}.yaml"
|
|
252
|
+
# tan-cli#1133, same change as `_load_som_doc`'s: no `is_file()`
|
|
253
|
+
# pre-flight, and the read and the parse are curated rather than bare.
|
|
254
|
+
doc = _read_yaml_mapping(
|
|
255
|
+
board_path, what="board metadata",
|
|
256
|
+
absent=f"no metadata/boards/{board_name}.yaml for board {board_name!r}")
|
|
257
|
+
routes = _require_field(doc.get("e1m_routes") or {}, dict,
|
|
258
|
+
doc=board_path, field="e1m_routes")
|
|
259
|
+
for section in _ROUTE_SECTIONS:
|
|
260
|
+
_require_field(routes.get(section) or [], list,
|
|
261
|
+
doc=board_path, field=f"e1m_routes.{section}")
|
|
262
|
+
return [
|
|
263
|
+
entry for section in _ROUTE_SECTIONS
|
|
264
|
+
for entry in (routes.get(section) or [])
|
|
265
|
+
if isinstance(entry, dict)
|
|
266
|
+
]
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def _board_alias_to_entry(board_name: str, metadata_root: Path) -> dict[str, dict[str, Any]]:
|
|
270
|
+
"""{board_alias: route_entry} -- mirrors
|
|
271
|
+
scripts/gen_portability_matrix.py's `_route_by_alias`."""
|
|
272
|
+
out: dict[str, dict[str, Any]] = {}
|
|
273
|
+
for entry in _board_route_entries(board_name, metadata_root):
|
|
274
|
+
alias = entry.get("board_alias")
|
|
275
|
+
if isinstance(alias, str):
|
|
276
|
+
out[alias] = entry
|
|
277
|
+
return out
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def _pin_pad_and_macro(item: Any) -> tuple[str | None, str | None]:
|
|
281
|
+
"""A `pins:` list item is either a bare E1M pad string, or a
|
|
282
|
+
`{e1m, macro?, doc?}` mapping -- normalise both shapes to
|
|
283
|
+
`(pad, macro)`, `None` for whichever half a bare string doesn't
|
|
284
|
+
carry."""
|
|
285
|
+
if isinstance(item, str):
|
|
286
|
+
return item, None
|
|
287
|
+
if isinstance(item, dict):
|
|
288
|
+
pad = item.get("e1m")
|
|
289
|
+
macro = item.get("macro")
|
|
290
|
+
return (
|
|
291
|
+
pad if isinstance(pad, str) else None,
|
|
292
|
+
macro if isinstance(macro, str) else None,
|
|
293
|
+
)
|
|
294
|
+
return None, None
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
def _alias_for_pin(entries: list[dict[str, Any]], pad: str, macro: str | None) -> str | None:
|
|
298
|
+
"""Resolve a `pins:` entry's `board_alias` on its OWN board --
|
|
299
|
+
MACRO-FIRST (issue #876 review MAJOR 1). A pad can carry more
|
|
300
|
+
than one `board_alias:` (e.g. e1m-evk's `E1M_PWM1` is BOTH
|
|
301
|
+
`BOARD_PWM_LED_BLUE` and `BOARD_PWM_ARD1`, at different `macro:`
|
|
302
|
+
entries), and macro names are unique per board -- every one is
|
|
303
|
+
compiled into a single C header (scripts/gen_board_header.py), so
|
|
304
|
+
two entries sharing a macro would be a duplicate-symbol build
|
|
305
|
+
error -- so matching by `macro:` first is unambiguous.
|
|
306
|
+
|
|
307
|
+
Only when the pin carries no `macro:` at all (a bare pad-string
|
|
308
|
+
entry) does this fall back to matching by `e1m:` alone, and only
|
|
309
|
+
when exactly ONE route entry claims that pad: a bare-string entry
|
|
310
|
+
naming a multi-alias pad has nothing to disambiguate with, so
|
|
311
|
+
`None` is returned (the caller hard-errors) rather than silently
|
|
312
|
+
picking whichever entry happens to come first in the board's
|
|
313
|
+
`e1m_routes:` -- the same class of silent-wrong-pin bug a naive
|
|
314
|
+
`{alias: pad}` dict inversion (this function's predecessor) had
|
|
315
|
+
for the macro-bearing case."""
|
|
316
|
+
if macro is not None:
|
|
317
|
+
for entry in entries:
|
|
318
|
+
if entry.get("macro") == macro:
|
|
319
|
+
alias = entry.get("board_alias")
|
|
320
|
+
return alias if isinstance(alias, str) else None
|
|
321
|
+
return None
|
|
322
|
+
matches = [entry for entry in entries if entry.get("e1m") == pad]
|
|
323
|
+
if len(matches) != 1:
|
|
324
|
+
return None
|
|
325
|
+
alias = matches[0].get("board_alias")
|
|
326
|
+
return alias if isinstance(alias, str) else None
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
def _resolve_pin_target(
|
|
330
|
+
item: Any, sku: str, source_preset: str, metadata_root: Path,
|
|
331
|
+
) -> dict[str, Any] | None:
|
|
332
|
+
"""Resolve ONE `pins:` entry to its target-board route entry
|
|
333
|
+
(issue #876, hardened by the review's MAJOR 1): looks up the
|
|
334
|
+
entry's `board_alias` on `source_preset` via `_alias_for_pin`
|
|
335
|
+
(macro-first, so a multi-alias pad resolves to the RIGHT alias
|
|
336
|
+
instead of whichever wins a lossy pad->alias dict inversion), then
|
|
337
|
+
looks that alias up on `sku`'s own target board preset.
|
|
338
|
+
|
|
339
|
+
Returns `None` when `sku`'s own default board preset IS
|
|
340
|
+
`source_preset` -- the canonical example's own sku, or a same-
|
|
341
|
+
family sibling that shares its board preset -- a byte-identical
|
|
342
|
+
passthrough, nothing to resolve.
|
|
343
|
+
|
|
344
|
+
DESIGN DECISION (maintainer-approved default): a pad with no
|
|
345
|
+
unambiguous `board_alias:` on `source_preset` -- no cross-EVK
|
|
346
|
+
correspondence declared for that role at all, or a multi-alias
|
|
347
|
+
pad with no `macro:` to disambiguate (issue #876 review MINOR 3)
|
|
348
|
+
-- is a hard error, same philosophy as `_derive_core_renames`'s
|
|
349
|
+
missing-core-class error: a genuinely unsupportable combo must
|
|
350
|
+
fail loudly here, never emit a `pins:` entry that's silently
|
|
351
|
+
stale (or a scaffold `--emit zephyr-conf` then rejects)."""
|
|
352
|
+
target_preset = _default_preset_for_sku(sku, metadata_root)
|
|
353
|
+
if target_preset == source_preset:
|
|
354
|
+
return None
|
|
355
|
+
pad, macro = _pin_pad_and_macro(item)
|
|
356
|
+
if pad is None:
|
|
357
|
+
return None
|
|
358
|
+
alias = _alias_for_pin(_board_route_entries(source_preset, metadata_root), pad, macro)
|
|
359
|
+
if alias is None:
|
|
360
|
+
raise TemplateError(
|
|
361
|
+
f"metadata/boards/{source_preset}.yaml `e1m_routes:` has no "
|
|
362
|
+
f"unambiguous `board_alias:` for pins: entry {item!r} -- can't "
|
|
363
|
+
f"re-derive it for sku {sku!r} (board {target_preset!r})")
|
|
364
|
+
target_entry = _board_alias_to_entry(target_preset, metadata_root).get(alias)
|
|
365
|
+
if target_entry is None:
|
|
366
|
+
raise TemplateError(
|
|
367
|
+
f"metadata/boards/{target_preset}.yaml `e1m_routes:` has no "
|
|
368
|
+
f"route for board_alias {alias!r} (needed to re-derive pins: "
|
|
369
|
+
f"entry {item!r} for sku {sku!r})")
|
|
370
|
+
return target_entry
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
def _derive_pin_renames(
|
|
374
|
+
original_pins: list[Any], sku: str, source_preset: str, metadata_root: Path,
|
|
375
|
+
) -> dict[str, str]:
|
|
376
|
+
"""Re-derive every catalog template's `pins:` entries -- each an
|
|
377
|
+
E1M pad name (`E1M_GPIO_IO4`) taken from the CANONICAL example's
|
|
378
|
+
own board preset (`source_preset`, e.g. `e1m-evk`) -- for `sku`'s
|
|
379
|
+
OWN default board preset (issue #876: the #864/#877 stopgap that
|
|
380
|
+
dropped E1M-V2N101 from `peripheral`/`sensor`/`edge-ai`'s
|
|
381
|
+
`supported.som_skus` rather than shipping a scaffold whose `pins:`
|
|
382
|
+
block named an E1M-EVK-only pad that an E1M-X-EVK-resolved
|
|
383
|
+
board.yaml's `e1m_routes:` doesn't have).
|
|
384
|
+
|
|
385
|
+
Each item is resolved via `_resolve_pin_target` (macro-first
|
|
386
|
+
`board_alias` match -- see its docstring for the multi-alias
|
|
387
|
+
fix). Returns `{}` when `sku`'s own default board preset IS
|
|
388
|
+
`source_preset` -- a byte-identical passthrough, nothing to
|
|
389
|
+
rewrite. Raises if two DIFFERENT `pins:` entries name the SAME
|
|
390
|
+
source pad but resolve to two different target pads (only
|
|
391
|
+
possible if a template ever lists one pad twice under two
|
|
392
|
+
different `board_alias` roles) -- ambiguous for the flat
|
|
393
|
+
`{old_pad: new_pad}` map `_substitute_board_yaml_pins` applies
|
|
394
|
+
across the whole file, so this fails loudly rather than silently
|
|
395
|
+
keeping whichever resolution happened to run last."""
|
|
396
|
+
renames: dict[str, str] = {}
|
|
397
|
+
for item in original_pins:
|
|
398
|
+
target = _resolve_pin_target(item, sku, source_preset, metadata_root)
|
|
399
|
+
if target is None:
|
|
400
|
+
continue
|
|
401
|
+
pad, _ = _pin_pad_and_macro(item)
|
|
402
|
+
new_pad = target.get("e1m")
|
|
403
|
+
if not isinstance(new_pad, str):
|
|
404
|
+
raise TemplateError(
|
|
405
|
+
f"metadata/boards/{_default_preset_for_sku(sku, metadata_root)}"
|
|
406
|
+
f".yaml route for pins: entry {item!r} has no `e1m:` pad")
|
|
407
|
+
if new_pad == pad:
|
|
408
|
+
continue
|
|
409
|
+
if pad in renames and renames[pad] != new_pad:
|
|
410
|
+
raise TemplateError(
|
|
411
|
+
f"pad {pad!r} re-derives to two different targets "
|
|
412
|
+
f"({renames[pad]!r} and {new_pad!r}) across `pins:` "
|
|
413
|
+
f"entries for sku {sku!r} -- ambiguous")
|
|
414
|
+
renames[pad] = new_pad
|
|
415
|
+
return renames
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
def _derive_pin_macro_renames(
|
|
419
|
+
original_pins: list[Any], sku: str, source_preset: str, metadata_root: Path,
|
|
420
|
+
) -> dict[str, str]:
|
|
421
|
+
"""Companion to `_derive_pin_renames`: re-derives a `pins:` entry's
|
|
422
|
+
`macro:` field (`EVK_PIN_ENCODER_SW` -> `XEVK_PIN_ENCODER_SW`)
|
|
423
|
+
alongside whichever pad it renames. Needed because
|
|
424
|
+
`alp_orchestrate.loader._validate_topology_cores`'s `pins:` cross-
|
|
425
|
+
check hard-errors when a declared `macro:` doesn't match the
|
|
426
|
+
resolved board's OWN macro for the (possibly re-derived) pad, not
|
|
427
|
+
only on an unrecognised `e1m:` pad (verified: re-deriving `e1m:`
|
|
428
|
+
alone against `peripheral`/E1M-V2N101 still failed `--emit
|
|
429
|
+
zephyr-conf` with `pins[0].macro: EVK_PIN_ENCODER_SW does not
|
|
430
|
+
match the resolved board 'E1M-X-EVK's macros for pad
|
|
431
|
+
E1M_X_GPIO_IO28: ['XEVK_PIN_ENCODER_SW']`). A bare-string `pins:`
|
|
432
|
+
entry (no `macro:` at all) contributes nothing here -- there's no
|
|
433
|
+
macro to keep in sync. Same passthrough/ambiguity-collision
|
|
434
|
+
philosophy as `_derive_pin_renames` (whose pad-rename result this
|
|
435
|
+
always agrees with -- both resolve the SAME target entry via
|
|
436
|
+
`_resolve_pin_target`, just read a different column off it)."""
|
|
437
|
+
renames: dict[str, str] = {}
|
|
438
|
+
for item in original_pins:
|
|
439
|
+
if not isinstance(item, dict):
|
|
440
|
+
continue
|
|
441
|
+
old_macro = item.get("macro")
|
|
442
|
+
if not isinstance(old_macro, str):
|
|
443
|
+
continue
|
|
444
|
+
target = _resolve_pin_target(item, sku, source_preset, metadata_root)
|
|
445
|
+
if target is None:
|
|
446
|
+
continue
|
|
447
|
+
new_macro = target.get("macro")
|
|
448
|
+
if not isinstance(new_macro, str):
|
|
449
|
+
raise TemplateError(
|
|
450
|
+
f"metadata/boards/{_default_preset_for_sku(sku, metadata_root)}"
|
|
451
|
+
f".yaml route for pins: entry {item!r} has no `macro:`")
|
|
452
|
+
if new_macro == old_macro:
|
|
453
|
+
continue
|
|
454
|
+
if old_macro in renames and renames[old_macro] != new_macro:
|
|
455
|
+
raise TemplateError(
|
|
456
|
+
f"macro {old_macro!r} re-derives to two different targets "
|
|
457
|
+
f"({renames[old_macro]!r} and {new_macro!r}) across "
|
|
458
|
+
f"`pins:` entries for sku {sku!r} -- ambiguous")
|
|
459
|
+
renames[old_macro] = new_macro
|
|
460
|
+
return renames
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
def _derive_pin_doc_renames(
|
|
464
|
+
original_pins: list[Any], sku: str, source_preset: str, metadata_root: Path,
|
|
465
|
+
) -> dict[str, str | None]:
|
|
466
|
+
"""Companion to `_derive_pin_renames`: re-derives a `pins:`
|
|
467
|
+
entry's `doc:` field to the TARGET route's own `doc:` (issue #876
|
|
468
|
+
review MAJOR 2) -- a renamed pin's `doc:` otherwise keeps
|
|
469
|
+
describing the SOURCE board's physical pad/electricals (e.g.
|
|
470
|
+
e1m-evk's encoder-switch doc names a PEC12R-4222F-S0024 debounce
|
|
471
|
+
network; e1m-x-evk's own doc for the same role describes a
|
|
472
|
+
different part with RC debounce), which is actively wrong prose
|
|
473
|
+
once the pad itself has changed -- the same "copy the target's
|
|
474
|
+
own doc" behaviour scripts/gen_portability_matrix.py's
|
|
475
|
+
`_remap_pins` already applies for its own (unrelated) board-preset
|
|
476
|
+
swap path.
|
|
477
|
+
|
|
478
|
+
A value of `None` in the returned map means DROP the `doc:` field
|
|
479
|
+
entirely -- the target route has no `doc:` of its own, and the
|
|
480
|
+
loader already falls back to the resolved board's own `doc:` in
|
|
481
|
+
that case (metadata/schemas/board.schema.json), so dropping it is
|
|
482
|
+
safe, not a silent content gap. An entry without its own `doc:`
|
|
483
|
+
at all contributes nothing (nothing to re-derive).
|
|
484
|
+
|
|
485
|
+
Same ambiguity-collision philosophy as `_derive_pin_renames` and
|
|
486
|
+
`_derive_pin_macro_renames` (issue #1394): a `doc:` string two
|
|
487
|
+
`pins:` entries legitimately SHARE -- one sentence describing a
|
|
488
|
+
debounce network, a bus, or a connector common to both pads --
|
|
489
|
+
keys ONE entry in the flat map `_substitute_board_yaml_pin_docs`
|
|
490
|
+
applies across the whole file, so two entries re-deriving it to
|
|
491
|
+
two different targets must fail loudly instead of silently
|
|
492
|
+
keeping whichever resolution ran last (i.e. whichever `pins:`
|
|
493
|
+
ordering the source file happened to use). `None` participates in
|
|
494
|
+
that check on both sides: "rename it" and "drop it" are
|
|
495
|
+
contradictory instructions for one key, and so are "keep it" (a
|
|
496
|
+
target `doc:` byte-identical to `old_doc`, which contributes no
|
|
497
|
+
map entry) and "drop it" -- the latter pair being the one that
|
|
498
|
+
loses documentation from a pin whose own re-derived `doc:` was
|
|
499
|
+
perfectly good. Hence the separate `resolved` map: it records
|
|
500
|
+
EVERY entry's resolution, including the keep-it ones `renames`
|
|
501
|
+
deliberately omits."""
|
|
502
|
+
renames: dict[str, str | None] = {}
|
|
503
|
+
resolved: dict[str, str | None] = {}
|
|
504
|
+
for item in original_pins:
|
|
505
|
+
if not isinstance(item, dict):
|
|
506
|
+
continue
|
|
507
|
+
old_doc = item.get("doc")
|
|
508
|
+
if not isinstance(old_doc, str):
|
|
509
|
+
continue
|
|
510
|
+
target = _resolve_pin_target(item, sku, source_preset, metadata_root)
|
|
511
|
+
if target is None:
|
|
512
|
+
continue
|
|
513
|
+
new_doc = target.get("doc")
|
|
514
|
+
new = new_doc if isinstance(new_doc, str) else None
|
|
515
|
+
if old_doc in resolved and resolved[old_doc] != new:
|
|
516
|
+
raise TemplateError(
|
|
517
|
+
f"doc {old_doc!r} re-derives to two different targets "
|
|
518
|
+
f"({resolved[old_doc]!r} and {new!r}) across `pins:` "
|
|
519
|
+
f"entries for sku {sku!r} -- ambiguous")
|
|
520
|
+
resolved[old_doc] = new
|
|
521
|
+
if new != old_doc:
|
|
522
|
+
renames[old_doc] = new
|
|
523
|
+
return renames
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
def _core_board(sku: str, core_id: str | None, metadata_root: Path) -> str | None:
|
|
527
|
+
"""`metadata/e1m_modules/<sku>.yaml` `topology.<core_id>.board` --
|
|
528
|
+
the qualified Zephyr board id (`<board>/<soc>/<cpucluster>`) `west
|
|
529
|
+
build -b` needs. `None` for a missing/off/a-class core (no Zephyr
|
|
530
|
+
target) so callers can skip the README board-target rewrite
|
|
531
|
+
cleanly instead of guessing."""
|
|
532
|
+
if not core_id:
|
|
533
|
+
return None
|
|
534
|
+
topology = _topology_for_sku(sku, metadata_root)
|
|
535
|
+
return (topology.get(core_id) or {}).get("board")
|