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,192 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""Which core owns a board.yaml `cameras:` entry -- the ONE ownership rule.
|
|
3
|
+
|
|
4
|
+
A dependency-free leaf (like `slugs.py`): plain dicts in, plain data out, so
|
|
5
|
+
the CLI validator (`alp_cli/validator.py`, lazy import) and the build planner
|
|
6
|
+
(`cameras.py`) cannot disagree.
|
|
7
|
+
|
|
8
|
+
A camera is owned by exactly one core, and ONLY that core's build gets the
|
|
9
|
+
camera (`-DSHIELD` for Zephyr, `ALP_CAMERA_CAM<n>` for Yocto).
|
|
10
|
+
|
|
11
|
+
* `cameras[].core` names the owner explicitly; it must be a candidate.
|
|
12
|
+
* Otherwise the candidates are the cores whose OS the camera's CONNECTOR
|
|
13
|
+
supports: a Zephyr core running a customer app (not `alp-stock-shim`)
|
|
14
|
+
needs the connector's `zephyr_shields` and the module's `zephyr_shield`; a
|
|
15
|
+
Yocto core needs the connector's `linux: true`. Exactly one -> it owns
|
|
16
|
+
the camera; none or several is an error (several: set `cameras[].core`).
|
|
17
|
+
|
|
18
|
+
`cores` maps core id -> {"os": ..., "app": ..., "board": ...}, already merged
|
|
19
|
+
from the board.yaml `cores:` over the SoM preset's `topology:`.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
from dataclasses import dataclass, field
|
|
25
|
+
from pathlib import Path
|
|
26
|
+
from typing import Any, Optional
|
|
27
|
+
|
|
28
|
+
#: The SDK's own M-core shim (firmware/alp-stock-shim): never a camera owner.
|
|
29
|
+
STOCK_SHIM_APP = "alp-stock-shim"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass
|
|
33
|
+
class CameraPlan:
|
|
34
|
+
connector: str
|
|
35
|
+
module: str
|
|
36
|
+
owner: Optional[str]
|
|
37
|
+
errors: list[str] = field(default_factory=list)
|
|
38
|
+
#: With no owner: the cores whose build this camera's errors block -- the
|
|
39
|
+
#: ones that could own it (usable, or of an OS the connector supports),
|
|
40
|
+
#: never an unrelated core (e.g. the AEN A32 for a Zephyr-only connector).
|
|
41
|
+
blocks: list[str] = field(default_factory=list)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def resolve_cores(project_cores: Any, topology: Any) -> dict[str, dict[str, Any]]:
|
|
45
|
+
"""Merge board.yaml `cores:` over the SoM `topology:` into
|
|
46
|
+
{id: {os, app, board}}. `os` falls back the way the loader's class rule
|
|
47
|
+
does for the shipped SoMs: a Zephyr `board:` -> zephyr, a Yocto
|
|
48
|
+
`machine:` -> yocto, else off."""
|
|
49
|
+
topo = topology if isinstance(topology, dict) else {}
|
|
50
|
+
proj = project_cores if isinstance(project_cores, dict) else {}
|
|
51
|
+
out: dict[str, dict[str, Any]] = {}
|
|
52
|
+
for cid, base in topo.items():
|
|
53
|
+
entry = dict(base) if isinstance(base, dict) else {}
|
|
54
|
+
if isinstance(proj.get(cid), dict):
|
|
55
|
+
entry.update(proj[cid])
|
|
56
|
+
os_ = entry.get("os") or (
|
|
57
|
+
"zephyr" if entry.get("board")
|
|
58
|
+
else "yocto" if entry.get("machine") else "off")
|
|
59
|
+
out[cid] = {"os": os_, "app": entry.get("app"), "board": entry.get("board")}
|
|
60
|
+
return out
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def candidates(cores: dict[str, dict[str, Any]]) -> list[str]:
|
|
64
|
+
return [cid for cid, c in cores.items()
|
|
65
|
+
if c["os"] == "yocto"
|
|
66
|
+
or (c["os"] == "zephyr" and c.get("app")
|
|
67
|
+
and c["app"] != STOCK_SHIM_APP)]
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _usable(core: dict[str, Any], connector: dict[str, Any],
|
|
71
|
+
module_doc: Optional[dict[str, Any]]) -> bool:
|
|
72
|
+
"""Can a core of this OS drive a camera of this module on this connector?"""
|
|
73
|
+
if core["os"] == "yocto":
|
|
74
|
+
return bool(connector.get("linux"))
|
|
75
|
+
return bool(connector.get("zephyr_shields")) and (
|
|
76
|
+
module_doc is None or bool(module_doc.get("zephyr_shield")))
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def camera_owner(cores: dict[str, dict[str, Any]], explicit: Optional[str],
|
|
80
|
+
conn: str, connector: dict[str, Any], module: str,
|
|
81
|
+
module_doc: Optional[dict[str, Any]]
|
|
82
|
+
) -> tuple[Optional[str], Optional[str]]:
|
|
83
|
+
"""(owner core id, None) or (None, error message).
|
|
84
|
+
|
|
85
|
+
An explicit `core:` must be a structural candidate (the OS-specific
|
|
86
|
+
problems are reported by `plan_cameras`). Otherwise the candidates are
|
|
87
|
+
the structural ones whose OS the connector (and, for Zephyr, the module)
|
|
88
|
+
supports: one -> owner; none or several -> error."""
|
|
89
|
+
cands = candidates(cores)
|
|
90
|
+
if explicit is not None:
|
|
91
|
+
if explicit in cands:
|
|
92
|
+
return explicit, None
|
|
93
|
+
return None, (f"cameras: core '{explicit}' is not a Zephyr core running a "
|
|
94
|
+
f"customer app or a Yocto core of this project "
|
|
95
|
+
f"(candidates: {', '.join(cands) or 'none'})")
|
|
96
|
+
usable = [c for c in cands if _usable(cores[c], connector, module_doc)]
|
|
97
|
+
if len(usable) == 1:
|
|
98
|
+
return usable[0], None
|
|
99
|
+
if usable:
|
|
100
|
+
return None, (f"cameras: ambiguous camera owner for {conn} "
|
|
101
|
+
f"({', '.join(usable)}): set `cameras[].core`")
|
|
102
|
+
supports = [n for n, ok in (("Zephyr", connector.get("zephyr_shields")),
|
|
103
|
+
("Linux (Yocto)", connector.get("linux"))) if ok]
|
|
104
|
+
if not supports:
|
|
105
|
+
return None, (f"cameras: connector {conn} declares neither "
|
|
106
|
+
f"`zephyr_shields:` nor `linux: true`, so no core can own "
|
|
107
|
+
f"its camera")
|
|
108
|
+
why = ""
|
|
109
|
+
if module_doc is not None and not module_doc.get("zephyr_shield"):
|
|
110
|
+
why = (f"; module '{module}' has no `zephyr_shield:` in "
|
|
111
|
+
f"metadata/camera_modules/{module}.yaml, so Zephyr cannot use it")
|
|
112
|
+
return None, (f"cameras: no core can own the camera on {conn}: the connector "
|
|
113
|
+
f"supports {' and '.join(supports)} only, and the project has "
|
|
114
|
+
f"no matching core (Zephyr customer app / Yocto){why}")
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def has_overlay(shield: str, board: str, repo: Path) -> bool:
|
|
118
|
+
"""Zephyr applies `boards/<board>_<qualifiers>.overlay`, else `<board>.overlay`."""
|
|
119
|
+
name, *quals = board.split()[0].split("/")
|
|
120
|
+
d = repo / "zephyr" / "boards" / "shields" / shield / "boards"
|
|
121
|
+
return any((d / f"{n}.overlay").is_file()
|
|
122
|
+
for n in ("_".join([name, *quals]), name))
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def plan_cameras(cameras: Any, connectors: Any, cores: dict[str, dict[str, Any]],
|
|
126
|
+
modules: dict[str, Any], repo: Path) -> list[CameraPlan]:
|
|
127
|
+
"""One CameraPlan per `cameras:` entry: its owner and every static
|
|
128
|
+
problem (empty `errors` = buildable). `modules` maps module id -> its
|
|
129
|
+
camera-module YAML (absent id: reported elsewhere, not here)."""
|
|
130
|
+
connectors = connectors if isinstance(connectors, dict) else {}
|
|
131
|
+
plans: list[CameraPlan] = []
|
|
132
|
+
seen: dict[tuple[str, str], str] = {} # (owner, module shield) -> connector
|
|
133
|
+
for entry in cameras if isinstance(cameras, list) else []:
|
|
134
|
+
if not isinstance(entry, dict):
|
|
135
|
+
continue
|
|
136
|
+
conn, mod = entry.get("connector"), entry.get("module")
|
|
137
|
+
if not isinstance(conn, str) or not isinstance(mod, str):
|
|
138
|
+
continue
|
|
139
|
+
if conn not in connectors: # reported by the validator / planner
|
|
140
|
+
plans.append(CameraPlan(conn, mod, None))
|
|
141
|
+
continue
|
|
142
|
+
connector = connectors[conn] if isinstance(connectors[conn], dict) else {}
|
|
143
|
+
owner, err = camera_owner(cores, entry.get("core"), conn, connector,
|
|
144
|
+
mod, modules.get(mod))
|
|
145
|
+
plan = CameraPlan(conn, mod, owner, [err] if err else [])
|
|
146
|
+
plans.append(plan)
|
|
147
|
+
if owner is None:
|
|
148
|
+
cands = candidates(cores)
|
|
149
|
+
usable = [c for c in cands
|
|
150
|
+
if _usable(cores[c], connector, modules.get(mod))]
|
|
151
|
+
by_os = [c for c in cands
|
|
152
|
+
if connector.get("linux" if cores[c]["os"] == "yocto"
|
|
153
|
+
else "zephyr_shields")]
|
|
154
|
+
plan.blocks = usable if len(usable) > 1 else (by_os or cands)
|
|
155
|
+
if owner is not None and cores[owner]["os"] == "yocto" and not connector.get("linux"):
|
|
156
|
+
plan.errors.append(
|
|
157
|
+
f"cameras: connector {conn} does not declare Linux support "
|
|
158
|
+
f"(`linux: true`), so Yocto core '{owner}' cannot own its camera")
|
|
159
|
+
if owner is None or cores[owner]["os"] != "zephyr":
|
|
160
|
+
continue
|
|
161
|
+
shield = (modules.get(mod) or {}).get("zephyr_shield")
|
|
162
|
+
carrier = connector.get("zephyr_shields") or []
|
|
163
|
+
if mod in modules and not shield:
|
|
164
|
+
plan.errors.append(
|
|
165
|
+
f"cameras: module '{mod}' on {conn} has no `zephyr_shield:` in "
|
|
166
|
+
f"metadata/camera_modules/{mod}.yaml -- it cannot be selected on "
|
|
167
|
+
f"Zephyr core '{owner}'; use a module with one, or give the "
|
|
168
|
+
f"camera to a Yocto core with `cameras[].core`")
|
|
169
|
+
if not carrier:
|
|
170
|
+
plan.errors.append(
|
|
171
|
+
f"cameras: connector {conn} declares no `zephyr_shields:` -- no "
|
|
172
|
+
f"Zephyr carrier shield exists for it, so Zephyr core '{owner}' "
|
|
173
|
+
f"cannot own its camera")
|
|
174
|
+
board = cores[owner].get("board")
|
|
175
|
+
if board:
|
|
176
|
+
for sh in carrier:
|
|
177
|
+
if not has_overlay(sh, board, repo):
|
|
178
|
+
plan.errors.append(
|
|
179
|
+
f"cameras: no Zephyr shield overlay for board target "
|
|
180
|
+
f"'{board}' (core '{owner}'): shield '{sh}' has no "
|
|
181
|
+
f"zephyr/boards/shields/{sh}/boards/<board>.overlay for "
|
|
182
|
+
f"it -- add one for that SoM target")
|
|
183
|
+
if shield:
|
|
184
|
+
# A module shield instantiates ONE camera node; two connectors
|
|
185
|
+
# naming the same module can't both be expressed in one -DSHIELD.
|
|
186
|
+
if (owner, shield) in seen:
|
|
187
|
+
plan.errors.append(
|
|
188
|
+
f"cameras: module shield '{shield}' is used by both "
|
|
189
|
+
f"{seen[(owner, shield)]} and {conn} on core '{owner}'; a "
|
|
190
|
+
f"Zephyr shield is a single instance")
|
|
191
|
+
seen.setdefault((owner, shield), conn)
|
|
192
|
+
return plans
|
tan/planner/cameras.py
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""Resolve a project's `cameras: [{connector, module, core?}]` into build inputs.
|
|
3
|
+
|
|
4
|
+
One resolver for both OS lanes, so the same board.yaml line picks the camera
|
|
5
|
+
on every SoM. Ownership (which core gets the camera) is `camera_owner.py`,
|
|
6
|
+
shared with the CLI validator; ONLY the owner core's build is touched (the owner is the core whose OS the
|
|
7
|
+
connector supports, see there):
|
|
8
|
+
|
|
9
|
+
* Zephyr owner -> ONE `-DSHIELD=<carrier shields> <module shield>` define
|
|
10
|
+
(carrier half from the board's `camera_connectors.<CAMn>.zephyr_shields`,
|
|
11
|
+
camera half from `metadata/camera_modules/<module>.yaml: zephyr_shield`).
|
|
12
|
+
On a sysbuild it is `-D<image>_SHIELD=...` so MCUboot does not get it.
|
|
13
|
+
The space-joined value is ONE argv element (a shell would need it quoted:
|
|
14
|
+
`-DSHIELD="a b"`).
|
|
15
|
+
* Yocto owner -> `ALP_CAMERA_CAM<n> = "<module_id>"` in local.conf. The
|
|
16
|
+
kernel bbappend keys the sensor DT include generated by
|
|
17
|
+
scripts/gen_camera_dt.py (#2736) on that variable; it reads CAM0 only.
|
|
18
|
+
|
|
19
|
+
Connector `CAMn` is camera index `n` (`alp-camera<n>` in Zephyr DT, the `n`
|
|
20
|
+
in `ALP_CAMERA_CAM<n>`).
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
from pathlib import Path
|
|
26
|
+
from typing import Optional
|
|
27
|
+
|
|
28
|
+
import yaml
|
|
29
|
+
|
|
30
|
+
from .camera_owner import CameraPlan, plan_cameras
|
|
31
|
+
from .models import BoardProject, OrchestratorError, Slice
|
|
32
|
+
from .paths import REPO
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class CameraSelectError(OrchestratorError):
|
|
36
|
+
"""A `cameras:` entry cannot be turned into a build input for a slice."""
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _connectors(project: BoardProject) -> dict:
|
|
40
|
+
# A `preset:` board carries them; an inline board's live in board.yaml.
|
|
41
|
+
return ((project.board_preset or {}).get("camera_connectors")
|
|
42
|
+
or project.raw.get("camera_connectors") or {})
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _module(project: BoardProject, module_id: str) -> Optional[dict]:
|
|
46
|
+
p = project.effective_metadata_root() / "camera_modules" / f"{module_id}.yaml"
|
|
47
|
+
try:
|
|
48
|
+
return yaml.safe_load(p.read_text(encoding="utf-8")) or {}
|
|
49
|
+
except OSError:
|
|
50
|
+
return None
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _cores(project: BoardProject) -> dict:
|
|
54
|
+
return {cid: {"os": s.os, "app": s.app, "board": s.board}
|
|
55
|
+
for cid, s in project.cores.items()}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _plans(project: BoardProject, repo: Path = REPO) -> list[CameraPlan]:
|
|
59
|
+
cams = sorted(project.raw.get("cameras") or [],
|
|
60
|
+
key=lambda c: int(c["connector"][3:]))
|
|
61
|
+
if not cams:
|
|
62
|
+
return []
|
|
63
|
+
modules = {c["module"]: _module(project, c["module"]) for c in cams}
|
|
64
|
+
plans = plan_cameras(cams, _connectors(project), _cores(project),
|
|
65
|
+
{m: d for m, d in modules.items() if d is not None}, repo)
|
|
66
|
+
connectors = _connectors(project)
|
|
67
|
+
for p in plans:
|
|
68
|
+
if modules.get(p.module) is None:
|
|
69
|
+
p.errors.append(f"cameras: unknown module '{p.module}' "
|
|
70
|
+
f"(no metadata/camera_modules/{p.module}.yaml)")
|
|
71
|
+
if p.connector not in connectors:
|
|
72
|
+
p.errors.append(f"cameras: connector '{p.connector}' is not a camera "
|
|
73
|
+
f"connector of this board")
|
|
74
|
+
return plans
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def check(project: BoardProject, slice_: Slice,
|
|
78
|
+
repo: Path = REPO) -> list[CameraPlan]:
|
|
79
|
+
"""The cameras this slice owns. Raises CameraSelectError when they are
|
|
80
|
+
unbuildable for it (the backstop for what `tan validate` reports as
|
|
81
|
+
ALP-B003). An ownerless error (none / ambiguous) blocks only the cores
|
|
82
|
+
that could have owned the camera (`CameraPlan.blocks`)."""
|
|
83
|
+
plans = _plans(project, repo)
|
|
84
|
+
mine = [p for p in plans if p.owner == slice_.core_id]
|
|
85
|
+
errs = [e for p in plans
|
|
86
|
+
if p.owner == slice_.core_id
|
|
87
|
+
or (p.owner is None and slice_.core_id in p.blocks)
|
|
88
|
+
for e in p.errors]
|
|
89
|
+
if errs:
|
|
90
|
+
raise CameraSelectError("; ".join(dict.fromkeys(errs)))
|
|
91
|
+
return mine
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def zephyr_shield_define(project: BoardProject, slice_: Slice,
|
|
95
|
+
image: Optional[str] = None,
|
|
96
|
+
repo: Path = REPO) -> Optional[str]:
|
|
97
|
+
"""`SHIELD=<...>` (`<image>_SHIELD=<...>` on a sysbuild) for the Zephyr
|
|
98
|
+
slice that owns cameras, else None."""
|
|
99
|
+
mine = check(project, slice_, repo)
|
|
100
|
+
if not mine or slice_.os != "zephyr":
|
|
101
|
+
return None
|
|
102
|
+
connectors = _connectors(project)
|
|
103
|
+
shields: list[str] = []
|
|
104
|
+
for p in mine:
|
|
105
|
+
shields += connectors[p.connector]["zephyr_shields"]
|
|
106
|
+
shields.append(_module(project, p.module)["zephyr_shield"])
|
|
107
|
+
shields = list(dict.fromkeys(shields)) # a carrier shield is shared
|
|
108
|
+
return f"{image + '_' if image else ''}SHIELD=" + " ".join(shields)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def shield_define_for_build(project: BoardProject, slice_: Slice,
|
|
112
|
+
base_dir: Optional[Path] = None) -> Optional[str]:
|
|
113
|
+
"""`zephyr_shield_define` with the sysbuild image prefix resolved the way
|
|
114
|
+
`_slice_command` names `<image>_EXTRA_CONF_FILE` (#866)."""
|
|
115
|
+
if not check(project, slice_) or slice_.os != "zephyr":
|
|
116
|
+
return None
|
|
117
|
+
from .orchestrator import _zephyr_app_dir
|
|
118
|
+
from .secure import emit_sysbuild_conf, emit_tfm_sysbuild_conf
|
|
119
|
+
image = None
|
|
120
|
+
if emit_sysbuild_conf(project) or emit_tfm_sysbuild_conf(project):
|
|
121
|
+
image = _zephyr_app_dir(
|
|
122
|
+
slice_.app, base_dir or project.source_dir or Path.cwd()).name
|
|
123
|
+
return zephyr_shield_define(project, slice_, image)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def yocto_camera_lines(project: BoardProject, slice_: Slice) -> list[str]:
|
|
127
|
+
"""local.conf lines for the cameras this Yocto slice owns. Never raises:
|
|
128
|
+
`tan validate` and the command path report the problems."""
|
|
129
|
+
if slice_.os != "yocto":
|
|
130
|
+
return []
|
|
131
|
+
return [f'ALP_CAMERA_CAM{p.connector[3:]} = "{p.module}"'
|
|
132
|
+
for p in _plans(project) if p.owner == slice_.core_id]
|