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
tan/planner/models.py
ADDED
|
@@ -0,0 +1,533 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
#
|
|
3
|
+
# Orchestrator data model — the dataclasses the fan-out resolves into and the
|
|
4
|
+
# emitters serialise from. Extracted verbatim from alp_orchestrate.py as the
|
|
5
|
+
# first step of the #285 modularization (one seam, no behaviour change): these
|
|
6
|
+
# are pure data + their own serialisation, with no resolver/emitter logic, so
|
|
7
|
+
# they make a clean leaf module that alp_orchestrate.py (and, later,
|
|
8
|
+
# alp_project.py) re-exports. Public names are unchanged — `from alp_orchestrate
|
|
9
|
+
# import Slice` still works because alp_orchestrate re-exports from here.
|
|
10
|
+
#
|
|
11
|
+
# Relocated into tan-cli (tan/planner/) from alp-sdk's scripts/alp_orchestrate/
|
|
12
|
+
# models.py -- NOT byte-identical to the upstream source; diverges in several
|
|
13
|
+
# places, INCLUDING (not limited to): the import at the bottom of
|
|
14
|
+
# `Slice.to_manifest_entry` is relative (`from .orchestrator import ...`, so
|
|
15
|
+
# the package resolves as `tan.planner`) rather than absolute, and a few
|
|
16
|
+
# exception docstrings (`SdkRevisionUnsupported` and its two siblings) were
|
|
17
|
+
# reworded because the alp-sdk caller they originally named
|
|
18
|
+
# (`scripts/validate_board_yaml.py`) has no tan-cli counterpart. What IS
|
|
19
|
+
# guaranteed byte-identical is the `Slice.to_manifest_entry()` OUTPUT for a
|
|
20
|
+
# given input -- `tests/parity/test_planner_emit_parity.py` diffs
|
|
21
|
+
# `system-manifest` emits against alp-sdk's own front door byte-for-byte; a
|
|
22
|
+
# module-source diff against upstream is not that guarantee and should not be
|
|
23
|
+
# read as one.
|
|
24
|
+
"""Dataclasses for the board.yaml orchestrator."""
|
|
25
|
+
|
|
26
|
+
from __future__ import annotations
|
|
27
|
+
|
|
28
|
+
import re
|
|
29
|
+
from dataclasses import dataclass, field
|
|
30
|
+
from pathlib import Path
|
|
31
|
+
from typing import Any, Optional
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class OrchestratorError(RuntimeError):
|
|
35
|
+
"""Raised when the orchestrator can't resolve / build a project.
|
|
36
|
+
|
|
37
|
+
Carries a human-readable message; the caller (west wrapper / CI)
|
|
38
|
+
prints it and exits non-zero.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class SdkRevisionUnsupported(OrchestratorError):
|
|
43
|
+
"""The running SDK is outside a requested hw_rev's declared range.
|
|
44
|
+
|
|
45
|
+
A subclass rather than a flag so a caller COULD map exactly this
|
|
46
|
+
failure to a distinct exit code without string-matching a message. In
|
|
47
|
+
alp-sdk that caller is `scripts/validate_board_yaml.py`, which maps it
|
|
48
|
+
to exit 3 -- but that script does not exist in tan-cli, and no port of
|
|
49
|
+
it is planned; `tan validate`'s own full-validator (spawn) path is
|
|
50
|
+
unported (`tan/commands/validate_cmd.py`). Every command that reaches
|
|
51
|
+
`load_board_yaml` in-process (`build_cmd.py`'s `_emit_plan`,
|
|
52
|
+
`kconfig_cmd.py`'s planner-emit backstop, `generate_cmd.py`'s
|
|
53
|
+
`_emit_one_in_process`) currently catches it only via a broad
|
|
54
|
+
`except Exception`, folding it into the same generic runtime-failure
|
|
55
|
+
issue every other planner error gets -- distinguishable in the message
|
|
56
|
+
text (the exception's class name is included) but not by exit code or
|
|
57
|
+
issue code. So today this is purely a subclass for `except
|
|
58
|
+
OrchestratorError` compatibility (#1019); no tan surface maps it to a
|
|
59
|
+
distinct verdict.
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class SdkRevisionUnknown(OrchestratorError):
|
|
64
|
+
"""The requested hw_rev is not a key in its resolved `hw_revisions:` table.
|
|
65
|
+
|
|
66
|
+
Distinct from `SdkRevisionUnsupported`: that one names a revision that
|
|
67
|
+
exists but whose declared SDK range excludes the running SDK. This one
|
|
68
|
+
names a revision that doesn't exist at all -- there is no range to
|
|
69
|
+
compare against, so reporting it as out-of-range would name the wrong
|
|
70
|
+
cause. A subclass (not a flag) for the same reason as
|
|
71
|
+
`SdkRevisionUnsupported`, and with the same tan-side caveat: alp-sdk's
|
|
72
|
+
`scripts/validate_board_yaml.py` maps exactly this failure to its own
|
|
73
|
+
exit code, but that script has no tan counterpart, so here the subclass
|
|
74
|
+
buys `except OrchestratorError` compatibility only (alp-sdk #1025, the
|
|
75
|
+
existence-only half).
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class SdkRevisionNotBuildable(OrchestratorError):
|
|
80
|
+
"""The requested hw_rev exists but its declared `status:` refuses a build.
|
|
81
|
+
|
|
82
|
+
Distinct from `SdkRevisionUnknown`: that one names a revision that
|
|
83
|
+
isn't a key in its table at all. This one names a revision that IS a
|
|
84
|
+
key -- it exists -- but is `status: reserved`, `status: tbd`, or
|
|
85
|
+
carries no `status` key at all (the maintainer's broad-reading decision
|
|
86
|
+
on alp-sdk #1025's status half). In alp-sdk this gets its own exit code
|
|
87
|
+
in `scripts/validate_board_yaml.py`; that script has no tan counterpart
|
|
88
|
+
(see `SdkRevisionUnsupported`'s docstring), so here the subclass buys
|
|
89
|
+
`except OrchestratorError` compatibility only.
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
_E1M_I2C_BUS_RE = re.compile(r"^e1m(?:_x)?_i2c([0-9]+)$")
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _feature_i2c_bus_id(bus: Any) -> int:
|
|
97
|
+
if not isinstance(bus, str):
|
|
98
|
+
raise OrchestratorError(
|
|
99
|
+
"features.hw_info.eeprom.bus must be an E1M I2C bus slug "
|
|
100
|
+
"such as `e1m_i2c0`.")
|
|
101
|
+
match = _E1M_I2C_BUS_RE.fullmatch(bus)
|
|
102
|
+
if match is None:
|
|
103
|
+
raise OrchestratorError(
|
|
104
|
+
f"features.hw_info.eeprom.bus: {bus!r} is not supported; "
|
|
105
|
+
"use an E1M I2C bus slug such as `e1m_i2c0`.")
|
|
106
|
+
return int(match.group(1), 10)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _feature_non_negative_int(raw: dict[str, Any], key: str) -> int:
|
|
110
|
+
value = raw.get(key)
|
|
111
|
+
if not isinstance(value, int) or value < 0:
|
|
112
|
+
raise OrchestratorError(
|
|
113
|
+
f"features.hw_info.eeprom.{key} must be a non-negative integer.")
|
|
114
|
+
return value
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _normalise_hw_info_eeprom(raw: dict[str, Any], source: str) -> dict[str, Any]:
|
|
118
|
+
return {
|
|
119
|
+
"source": source,
|
|
120
|
+
"bus": raw.get("bus"),
|
|
121
|
+
"bus_id": _feature_i2c_bus_id(raw.get("bus")),
|
|
122
|
+
"addr_7bit": _feature_non_negative_int(raw, "addr_7bit"),
|
|
123
|
+
"offset": _feature_non_negative_int(raw, "offset"),
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
# ---------------------------------------------------------------------
|
|
128
|
+
# Dataclasses
|
|
129
|
+
# ---------------------------------------------------------------------
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
@dataclass
|
|
133
|
+
class Slice:
|
|
134
|
+
"""One per-core build slice."""
|
|
135
|
+
|
|
136
|
+
core_id: str
|
|
137
|
+
os: str # zephyr | yocto | baremetal | off
|
|
138
|
+
app: Optional[str] = None
|
|
139
|
+
image: Optional[str] = None # Yocto image recipe name
|
|
140
|
+
# Yocto bitbake recipe that packages this slice's `app:` source dir.
|
|
141
|
+
# Required for an app-only (`app:` set, no `image:`) Yocto slice --
|
|
142
|
+
# `app:` is a filesystem path, never a valid bitbake target itself
|
|
143
|
+
# (issue #597).
|
|
144
|
+
recipe: Optional[str] = None
|
|
145
|
+
machine: Optional[str] = None # Yocto MACHINE
|
|
146
|
+
board: Optional[str] = None # Zephyr board target
|
|
147
|
+
toolchain: Optional[str] = None
|
|
148
|
+
peripherals: list[str] = field(default_factory=list)
|
|
149
|
+
libraries: list[str] = field(default_factory=list)
|
|
150
|
+
# Open-set escape hatch for libraries the SDK doesn't curate. Each
|
|
151
|
+
# entry is a dict with `name:` + (exclusively) `kconfig:` OR
|
|
152
|
+
# `profile:`; loader's _validate_consistency() enforces the
|
|
153
|
+
# exactly-one and uniqueness rules. See docs/board-config-schema.md
|
|
154
|
+
# `extra_libraries:`.
|
|
155
|
+
extra_libraries: list[dict[str, Any]] = field(default_factory=list)
|
|
156
|
+
inference: dict[str, Any] = field(default_factory=dict)
|
|
157
|
+
iot: dict[str, Any] = field(default_factory=dict)
|
|
158
|
+
memory: dict[str, Any] = field(default_factory=dict) # stack_kib, heap_kib, isr_stack_kib
|
|
159
|
+
power: dict[str, Any] = field(default_factory=dict) # sleep_mode, wakeup_sources
|
|
160
|
+
# False when this core has no hardware UART console (headless -- e.g.
|
|
161
|
+
# the RZ/V2N M33 system-manager, whose debug UART the A55 owns). A
|
|
162
|
+
# SoM-topology fact (`topology.<id>.hw_console:` in the SoM preset),
|
|
163
|
+
# NOT customer-overridable; defaults True so every other core keeps
|
|
164
|
+
# its console. Consumed by the console emitter for `diagnostics.
|
|
165
|
+
# sim_console:` (issue #686).
|
|
166
|
+
hw_console: bool = True
|
|
167
|
+
# `diagnostics.link: itcm` (tan-cli#1350): set by the loader on the one
|
|
168
|
+
# slice the knob retargets (the Zephyr M55-HE). Such a slice links at 0x0
|
|
169
|
+
# and must never be flashed to MRAM -- `_slice_flash_recipe` keys off it.
|
|
170
|
+
link_target: Optional[str] = None
|
|
171
|
+
# SEGGER J-Link **part-number flash-device** profile for this slice's
|
|
172
|
+
# SoC variant (soc-spec-v1 `variants[].debug.jlink_flash_device`) --
|
|
173
|
+
# unlocks the built-in Alif MRAM loader (Flow D), distinct from the
|
|
174
|
+
# generic attach/debug `jlink_device` profile. A resolved SoC-variant
|
|
175
|
+
# fact, like `hw_console` above, NOT customer-overridable; None when
|
|
176
|
+
# the variant publishes no such profile -- a published "unknown", not
|
|
177
|
+
# a gap to guess at (see soc-spec-v1.schema.json's `jlink_flash_device`
|
|
178
|
+
# description). Consumed by `_slice_flash_recipe`'s `zephyr` branch to
|
|
179
|
+
# arm the direct-flash path in `flash_args`.
|
|
180
|
+
jlink_flash_device: Optional[str] = None
|
|
181
|
+
# Whether the resolved variant's `debug:` block carried the
|
|
182
|
+
# `jlink_flash_device` KEY AT ALL, independent of what it resolved to
|
|
183
|
+
# (tan-cli#734). `debug.get(...)` collapses a schema-declared
|
|
184
|
+
# `jlink_flash_device: null` -- a deliberate "this variant has no known
|
|
185
|
+
# J-Link flash profile" -- into the same `None` as a genuinely absent
|
|
186
|
+
# key, and the emitter's truthiness test then dropped both. Downstream,
|
|
187
|
+
# `flash_plan.flow_d_available` decides on KEY PRESENCE precisely so a
|
|
188
|
+
# declared-null reaches `plan_alif_mram_jlink`'s loud refusal instead of
|
|
189
|
+
# silently downgrading to Flow A over the SE-UART; that distinction has
|
|
190
|
+
# to survive the loader for it to mean anything.
|
|
191
|
+
jlink_flash_device_declared: bool = False
|
|
192
|
+
# The read-only SW-DP IDR (DPIDR) wrong-board preflight PAIR for this
|
|
193
|
+
# core (soc-spec-v1 `variants[].debug.expect_dpidr` +
|
|
194
|
+
# `variants[].debug.jlink_device[<core_id>]`), resolved together by
|
|
195
|
+
# `loader._resolve_flow_d_preflight` -- both None (preflight not armed,
|
|
196
|
+
# every variant that has not been measured) or both set; never one of
|
|
197
|
+
# the two. `expect_dpidr` is the ID the board's debug port must answer
|
|
198
|
+
# BEFORE any write; `jlink_device` is the LIVE-CORE attach profile that
|
|
199
|
+
# read is performed with -- distinct from `jlink_flash_device` above,
|
|
200
|
+
# which is the part-number flash-algorithm profile. Resolved SoC-variant
|
|
201
|
+
# facts like `jlink_flash_device`, NOT customer-overridable. Consumed by
|
|
202
|
+
# `_slice_flash_recipe`'s `zephyr` branch, which emits them into
|
|
203
|
+
# `flash_args` as an inseparable pair (alp-sdk #1355).
|
|
204
|
+
expect_dpidr: Optional[str] = None
|
|
205
|
+
jlink_device: Optional[str] = None
|
|
206
|
+
# This core's AEN MRAM slot0-XIP load address, `0x`-prefixed hex string
|
|
207
|
+
# (tan-cli#353) -- where Flow D's built-in Alif MRAM loader must write
|
|
208
|
+
# the slot0-linked application blob itself, distinct from
|
|
209
|
+
# `jlink_flash_device` above (which only selects the loader's device
|
|
210
|
+
# PROFILE, not an address). Resolved by
|
|
211
|
+
# `loader._resolve_slot0_load_address` from the SoM preset's
|
|
212
|
+
# `memory_map:` (NOT the SoC JSON -- this is SDK/module build policy,
|
|
213
|
+
# not a silicon fact), so like `jlink_flash_device` it is NOT
|
|
214
|
+
# customer-overridable. None when this core has no AEN slot0-XIP window
|
|
215
|
+
# (every non-AEN slice, and any AEN core whose SoC variant publishes no
|
|
216
|
+
# `jlink_flash_device`) -- a published "unknown", never a value to
|
|
217
|
+
# invent. Consumed by `_slice_flash_recipe`'s `zephyr` branch.
|
|
218
|
+
slot0_load_address: Optional[str] = None
|
|
219
|
+
|
|
220
|
+
# Populated by Orchestrator.fan_out:
|
|
221
|
+
build_dir: Optional[Path] = None
|
|
222
|
+
output_artefact: Optional[str] = None
|
|
223
|
+
status: str = "pending" # pending | ok | failed | skipped
|
|
224
|
+
reason: Optional[str] = None # populated for skipped / failed
|
|
225
|
+
log_path: Optional[Path] = None
|
|
226
|
+
duration_s: float = 0.0
|
|
227
|
+
|
|
228
|
+
def to_manifest_entry(self) -> dict[str, Any]:
|
|
229
|
+
"""Project this slice as a dict for system-manifest.yaml.
|
|
230
|
+
|
|
231
|
+
Includes the per-os `flash_method:` + `flash_args:` so
|
|
232
|
+
`west alp-flash` can dispatch each slice without re-deriving
|
|
233
|
+
the backend. The actual backend implementations (driver
|
|
234
|
+
invocations) are the subject of Phase 5 follow-ups; this
|
|
235
|
+
Phase 3 wiring is just the data plumbing.
|
|
236
|
+
|
|
237
|
+
Spec §6.1 byte-stability: the manifest MUST be deterministic
|
|
238
|
+
across rebuilds. `duration_s` is a wall-clock runtime metric
|
|
239
|
+
that varies run-to-run, so it stays on the Slice dataclass
|
|
240
|
+
but never lands in the manifest. Same goes for anything else
|
|
241
|
+
timer / PID-style — keep the manifest content-addressable.
|
|
242
|
+
"""
|
|
243
|
+
# Local import: the flash-recipe deriver lives in orchestrator.py; a
|
|
244
|
+
# module-level import here would create a models<->orchestrator cycle.
|
|
245
|
+
# By call time both modules are loaded, so this resolves cleanly.
|
|
246
|
+
# RELOCATED: relative, so the package can be reached as `tan.planner`.
|
|
247
|
+
from .orchestrator import _slice_flash_recipe
|
|
248
|
+
|
|
249
|
+
flash_method, flash_args = _slice_flash_recipe(self)
|
|
250
|
+
entry: dict[str, Any] = {
|
|
251
|
+
"core_id": self.core_id,
|
|
252
|
+
"os": self.os,
|
|
253
|
+
"app": self.app,
|
|
254
|
+
"image": self.image,
|
|
255
|
+
"recipe": self.recipe,
|
|
256
|
+
"machine": self.machine,
|
|
257
|
+
"board": self.board,
|
|
258
|
+
"toolchain": self.toolchain,
|
|
259
|
+
"build_dir": str(self.build_dir) if self.build_dir else None,
|
|
260
|
+
"output_artefact": self.output_artefact,
|
|
261
|
+
"status": self.status,
|
|
262
|
+
"log_path": str(self.log_path) if self.log_path else None,
|
|
263
|
+
"flash_method": flash_method,
|
|
264
|
+
"flash_args": flash_args,
|
|
265
|
+
}
|
|
266
|
+
if self.reason:
|
|
267
|
+
entry["reason"] = self.reason
|
|
268
|
+
# Drop keys with None values to keep the manifest tidy.
|
|
269
|
+
return {k: v for k, v in entry.items() if v is not None}
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
@dataclass
|
|
273
|
+
class IpcEntry:
|
|
274
|
+
"""Raw IPC declaration straight from board.yaml."""
|
|
275
|
+
|
|
276
|
+
name: str
|
|
277
|
+
kind: str
|
|
278
|
+
endpoints: list[str]
|
|
279
|
+
carve_out_kb: int
|
|
280
|
+
cacheable: Optional[bool] = None
|
|
281
|
+
address: Optional[int] = None # explicit base-address override
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
@dataclass
|
|
285
|
+
class ResolvedCarveOut:
|
|
286
|
+
"""An IpcEntry after allocation from the SoM memory_map.
|
|
287
|
+
|
|
288
|
+
`status` is "ok" for a fully resolved carve-out; "blocked" when
|
|
289
|
+
the SoM metadata has TBDs (mailbox controller, memory_map base /
|
|
290
|
+
size, etc.) or the board.yaml entry can't be satisfied. Blocked
|
|
291
|
+
entries land in `system-manifest.yaml` with `status: blocked` +
|
|
292
|
+
`reason: ...` so reviewers see the gap; the actual slice-build
|
|
293
|
+
step (which CI doesn't run) is what fails on a blocked carve-out.
|
|
294
|
+
"""
|
|
295
|
+
|
|
296
|
+
name: str
|
|
297
|
+
kind: str
|
|
298
|
+
endpoints: list[str]
|
|
299
|
+
base: int # 0 when blocked
|
|
300
|
+
size: int # in bytes; 0 when blocked
|
|
301
|
+
region: str # source memory-region name; "" when blocked
|
|
302
|
+
cacheable: bool
|
|
303
|
+
src_ept: int # 0 when blocked
|
|
304
|
+
dst_ept: int # 0 when blocked
|
|
305
|
+
mailbox_channel: int # 0 when blocked
|
|
306
|
+
status: str = "ok" # "ok" | "blocked"
|
|
307
|
+
reason: Optional[str] = None # populated when blocked
|
|
308
|
+
|
|
309
|
+
def to_manifest_entry(self) -> dict[str, Any]:
|
|
310
|
+
if self.status == "blocked":
|
|
311
|
+
return {
|
|
312
|
+
"name": self.name,
|
|
313
|
+
"kind": self.kind,
|
|
314
|
+
"endpoints": list(self.endpoints),
|
|
315
|
+
"status": "blocked",
|
|
316
|
+
"reason": self.reason or "",
|
|
317
|
+
}
|
|
318
|
+
return {
|
|
319
|
+
"name": self.name,
|
|
320
|
+
"kind": self.kind,
|
|
321
|
+
"endpoints": list(self.endpoints),
|
|
322
|
+
"carve_out_base": f"0x{self.base:08x}",
|
|
323
|
+
"carve_out_size": f"0x{self.size:08x}",
|
|
324
|
+
"carve_out_region": self.region,
|
|
325
|
+
"cacheable": self.cacheable,
|
|
326
|
+
"rpmsg_endpoint_ids": {
|
|
327
|
+
"src": f"0x{self.src_ept:08x}",
|
|
328
|
+
"dst": f"0x{self.dst_ept:08x}",
|
|
329
|
+
},
|
|
330
|
+
"mailbox_channel": self.mailbox_channel,
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
@dataclass
|
|
335
|
+
class StorageEntry:
|
|
336
|
+
"""Raw storage-partition declaration straight from board.yaml.
|
|
337
|
+
|
|
338
|
+
Mirrors the shape under `storage:` in board.schema.json; the
|
|
339
|
+
orchestrator turns these into ResolvedPartitions in
|
|
340
|
+
`resolve_storage_partitions()`.
|
|
341
|
+
"""
|
|
342
|
+
|
|
343
|
+
name: str
|
|
344
|
+
size_kib: int
|
|
345
|
+
fs: str # littlefs | fat | ext4 | raw
|
|
346
|
+
mount: Optional[str] = None
|
|
347
|
+
flash_device: Optional[str] = None
|
|
348
|
+
offset_kib: Optional[int] = None # explicit offset override
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
@dataclass
|
|
352
|
+
class ResolvedPartition:
|
|
353
|
+
"""A StorageEntry after allocation against the SoM flash devices.
|
|
354
|
+
|
|
355
|
+
`status` follows the IPC carve-out convention: "ok" for a fully
|
|
356
|
+
resolved partition; "blocked" when the SoM metadata has TBDs
|
|
357
|
+
(flash device base/size unset) or the entry can't be satisfied
|
|
358
|
+
(unknown flash_device, page-misaligned offset, overlap with a
|
|
359
|
+
sibling partition). Blocked entries land in `system-manifest.yaml`
|
|
360
|
+
with `reason: ...` so reviewers see the gap.
|
|
361
|
+
"""
|
|
362
|
+
|
|
363
|
+
name: str
|
|
364
|
+
fs: str
|
|
365
|
+
flash_device: str # original SDK name from board.yaml
|
|
366
|
+
dt_label: str # Zephyr DT label resolved by the loader
|
|
367
|
+
base_kib: int # offset within the flash device, in KiB; 0 when blocked
|
|
368
|
+
size_kib: int
|
|
369
|
+
mount: Optional[str] = None
|
|
370
|
+
status: str = "ok" # "ok" | "blocked"
|
|
371
|
+
reason: Optional[str] = None
|
|
372
|
+
|
|
373
|
+
def to_manifest_entry(self) -> dict[str, Any]:
|
|
374
|
+
if self.status == "blocked":
|
|
375
|
+
return {
|
|
376
|
+
"name": self.name,
|
|
377
|
+
"fs": self.fs,
|
|
378
|
+
"flash_device": self.flash_device,
|
|
379
|
+
"status": "blocked",
|
|
380
|
+
"reason": self.reason or "",
|
|
381
|
+
}
|
|
382
|
+
entry: dict[str, Any] = {
|
|
383
|
+
"name": self.name,
|
|
384
|
+
"fs": self.fs,
|
|
385
|
+
"flash_device": self.flash_device,
|
|
386
|
+
"dt_label": self.dt_label,
|
|
387
|
+
"offset_kib": self.base_kib,
|
|
388
|
+
"size_kib": self.size_kib,
|
|
389
|
+
}
|
|
390
|
+
if self.mount:
|
|
391
|
+
entry["mount"] = self.mount
|
|
392
|
+
return entry
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
@dataclass
|
|
396
|
+
class BoardProject:
|
|
397
|
+
"""Resolved board.yaml project ready for fan-out."""
|
|
398
|
+
|
|
399
|
+
sku: str
|
|
400
|
+
hw_rev: Optional[str]
|
|
401
|
+
board_name: Optional[str]
|
|
402
|
+
board_hw_rev: Optional[str]
|
|
403
|
+
cores: dict[str, Slice] # effective per-core slices
|
|
404
|
+
ipc: list[IpcEntry]
|
|
405
|
+
soc_spec: dict[str, Any]
|
|
406
|
+
som_preset: dict[str, Any]
|
|
407
|
+
board_preset: Optional[dict[str, Any]]
|
|
408
|
+
diagnostics: dict[str, Any] = field(default_factory=dict)
|
|
409
|
+
chips: list[str] = field(default_factory=list)
|
|
410
|
+
# Project-wide curated third-party libraries (ADR 0018). Each name
|
|
411
|
+
# resolves to metadata/libraries/<name>.yaml; the emitters wire its
|
|
412
|
+
# per-OS integration into every slice whose OS the manifest supports.
|
|
413
|
+
libraries: list[str] = field(default_factory=list)
|
|
414
|
+
features: dict[str, Any] = field(default_factory=dict)
|
|
415
|
+
boot: dict[str, Any] = field(default_factory=dict)
|
|
416
|
+
ota: dict[str, Any] = field(default_factory=dict)
|
|
417
|
+
storage: list[StorageEntry] = field(default_factory=list)
|
|
418
|
+
security: dict[str, Any] = field(default_factory=dict)
|
|
419
|
+
# Resolved per-product core ownership ({E1M instance: core}); empty when
|
|
420
|
+
# the SoM family declares no `assignable:` block (ownership.resolve_ownership).
|
|
421
|
+
ownership: dict[str, str] = field(default_factory=dict)
|
|
422
|
+
raw: dict[str, Any] = field(default_factory=dict)
|
|
423
|
+
# The metadata tree this project was RESOLVED against (tan-cli#573).
|
|
424
|
+
# `load_board_yaml(..., metadata_root=...)` records its caller's root
|
|
425
|
+
# here so the downstream resolvers -- `resolve_storage_partitions`,
|
|
426
|
+
# `resolve_carve_outs`, the Kconfig emitters -- read the same tree the
|
|
427
|
+
# loader validated against instead of the module-level bound
|
|
428
|
+
# `paths.METADATA_ROOT`. Without it the loader and the resolvers
|
|
429
|
+
# disagree on an alternate tree: the loader accepts a
|
|
430
|
+
# `storage[].flash_device` the resolver then blocks.
|
|
431
|
+
# `None` means "the bound root" -- see `effective_metadata_root()`.
|
|
432
|
+
metadata_root: Optional[Path] = None
|
|
433
|
+
# Directory of the board.yaml this project was loaded from; anchors a
|
|
434
|
+
# slice's relative `app:` so the emitters can read the app's own
|
|
435
|
+
# `prj.conf` (e.g. to avoid shrinking an app-set RAM console size,
|
|
436
|
+
# tan-cli#1401). `None` for a project built without a file.
|
|
437
|
+
source_dir: Optional[Path] = None
|
|
438
|
+
|
|
439
|
+
def effective_metadata_root(self) -> Path:
|
|
440
|
+
"""The metadata tree to read SoM/SoC facts from for THIS project.
|
|
441
|
+
|
|
442
|
+
`paths.METADATA_ROOT` is imported lazily, not at module scope:
|
|
443
|
+
`paths` evaluates `REPO = sdk_root()` on import and raises when no
|
|
444
|
+
SDK root is bound, and `models` is deliberately a bound-root-free
|
|
445
|
+
leaf (`tests/core/test_sdk_revision_gate.py` loads `sdk_compat`
|
|
446
|
+
-- and through it `models` -- with no SDK at all).
|
|
447
|
+
"""
|
|
448
|
+
if self.metadata_root is not None:
|
|
449
|
+
return self.metadata_root
|
|
450
|
+
from .paths import METADATA_ROOT
|
|
451
|
+
return METADATA_ROOT
|
|
452
|
+
|
|
453
|
+
def hw_info_eeprom_feature(self) -> Optional[dict[str, Any]]:
|
|
454
|
+
"""Return the explicit ``features.hw_info.eeprom`` projection."""
|
|
455
|
+
hw_info = self.features.get("hw_info")
|
|
456
|
+
if not isinstance(hw_info, dict):
|
|
457
|
+
return None
|
|
458
|
+
eeprom = hw_info.get("eeprom")
|
|
459
|
+
if eeprom is None:
|
|
460
|
+
return None
|
|
461
|
+
if not isinstance(eeprom, dict):
|
|
462
|
+
raise OrchestratorError(
|
|
463
|
+
"features.hw_info.eeprom must be a mapping.")
|
|
464
|
+
return _normalise_hw_info_eeprom(eeprom, "features.hw_info.eeprom")
|
|
465
|
+
|
|
466
|
+
def hw_info_eeprom_config(self) -> Optional[dict[str, Any]]:
|
|
467
|
+
"""Return the effective EEPROM reader config for Kconfig emit."""
|
|
468
|
+
explicit = self.hw_info_eeprom_feature()
|
|
469
|
+
if explicit is not None:
|
|
470
|
+
return explicit
|
|
471
|
+
if (self.som_preset.get("on_module") or {}).get("eeprom"):
|
|
472
|
+
return {
|
|
473
|
+
"source": "som.on_module.eeprom",
|
|
474
|
+
"bus": "e1m_i2c0",
|
|
475
|
+
"bus_id": 0,
|
|
476
|
+
"addr_7bit": 0x50,
|
|
477
|
+
"offset": 0,
|
|
478
|
+
}
|
|
479
|
+
return None
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
@dataclass
|
|
483
|
+
class SystemManifest:
|
|
484
|
+
"""The artefact written to build/system-manifest.yaml."""
|
|
485
|
+
|
|
486
|
+
project: BoardProject
|
|
487
|
+
slices: list[Slice]
|
|
488
|
+
carve_outs: list[ResolvedCarveOut]
|
|
489
|
+
partitions: list[ResolvedPartition] = field(default_factory=list)
|
|
490
|
+
boot_order: list[dict[str, Any]] = field(default_factory=list)
|
|
491
|
+
helper_mcus: list[dict[str, Any]] = field(default_factory=list)
|
|
492
|
+
memory_regions: list[dict[str, Any]] = field(default_factory=list)
|
|
493
|
+
|
|
494
|
+
def to_dict(self) -> dict[str, Any]:
|
|
495
|
+
hw_info: dict[str, Any] = {
|
|
496
|
+
"sku": self.project.sku,
|
|
497
|
+
"som_hw_rev": self.project.hw_rev,
|
|
498
|
+
"board_name": self.project.board_name,
|
|
499
|
+
"board_hw_rev": self.project.board_hw_rev,
|
|
500
|
+
"silicon": self.project.som_preset.get("silicon"),
|
|
501
|
+
}
|
|
502
|
+
eeprom = self.project.hw_info_eeprom_feature()
|
|
503
|
+
if eeprom is not None:
|
|
504
|
+
hw_info["eeprom"] = {
|
|
505
|
+
"bus": eeprom["bus"],
|
|
506
|
+
"bus_id": eeprom["bus_id"],
|
|
507
|
+
"addr_7bit": eeprom["addr_7bit"],
|
|
508
|
+
"offset": eeprom["offset"],
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
out: dict[str, Any] = {
|
|
512
|
+
"schema_version": 1,
|
|
513
|
+
"generated_by": "scripts/alp_orchestrate.py",
|
|
514
|
+
"hw_info": hw_info,
|
|
515
|
+
"slices": [s.to_manifest_entry() for s in self.slices],
|
|
516
|
+
"ipc": [c.to_manifest_entry() for c in self.carve_outs],
|
|
517
|
+
"helper_mcus": list(self.helper_mcus),
|
|
518
|
+
"boot_order": list(self.boot_order),
|
|
519
|
+
}
|
|
520
|
+
if self.partitions:
|
|
521
|
+
out["storage"] = [p.to_manifest_entry() for p in self.partitions]
|
|
522
|
+
# `memory` is OMITTED, never emitted empty (#1365 item 3): the schema
|
|
523
|
+
# makes an absent pane mean "this producer does not emit it yet",
|
|
524
|
+
# so `memory: []` would tell a consumer a SoM whose silicon_variant
|
|
525
|
+
# is still `TBD` has no memory regions. Rows arrive already shaped
|
|
526
|
+
# by memory.resolve_memory_regions() -- unlike ipc/storage there is
|
|
527
|
+
# no per-row dataclass, matching the helper_mcus precedent.
|
|
528
|
+
if self.memory_regions:
|
|
529
|
+
out["memory"] = list(self.memory_regions)
|
|
530
|
+
# Additive, omitted when the SoM declares no assignable resources.
|
|
531
|
+
if self.project.ownership:
|
|
532
|
+
out["ownership"] = dict(self.project.ownership)
|
|
533
|
+
return out
|