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/model/build.py
ADDED
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""Build driver: SKU + source model -> .alpmodel package (compile-what's-available).
|
|
3
|
+
|
|
4
|
+
Resolves the SoM's targets, runs each *available* compiler adapter, and assembles
|
|
5
|
+
the package. A backend whose adapter is missing, or whose tool is not installed,
|
|
6
|
+
is recorded as a `coverage` skip; a source format no adapter accepts is
|
|
7
|
+
`incompatible`. If *no* blob is produced the build fails loudly -- an .alpmodel
|
|
8
|
+
with zero runnable blobs is broken.
|
|
9
|
+
|
|
10
|
+
PER-TARGET, NOT PER-PACKAGE (tan-cli#789 review BLOCKER 1). A SKU resolves to
|
|
11
|
+
several targets and they succeed or fail independently, so two of them are
|
|
12
|
+
scoped to ONE target's coverage entry instead of killing the package:
|
|
13
|
+
|
|
14
|
+
* `VelaFootprintRefused` -- vela compiled cleanly and tan refused the
|
|
15
|
+
footprint it reported (see `tan.model.adapters.ethos_u`). Measured, real
|
|
16
|
+
`ethos-u-vela` 5.1.0 over `tests/fixtures/models/tiny_int8.tflite`: with no
|
|
17
|
+
guard here, `E1M-AEN801`'s `ethos-u85-256` refusal aborted the whole build
|
|
18
|
+
-- `ethos-u55-256`, `ethos-u55-128` and `cpu` all compiled fine and NONE of
|
|
19
|
+
them shipped, and the same held for `E1M-AEN401` and `E1M-AEN601`.
|
|
20
|
+
* A ZERO-placement accelerator compile -- see `_placed_nothing_on_accelerator`.
|
|
21
|
+
|
|
22
|
+
Every OTHER `adapter.compile()` exception still fails the whole build loudly.
|
|
23
|
+
That is deliberate: a toolchain that crashed, timed out or produced no artifact
|
|
24
|
+
is a broken build, not a coverage line (`tests/model/test_build.py` states the
|
|
25
|
+
same contract from the other side).
|
|
26
|
+
|
|
27
|
+
If EVERY target ends up skipped there is no blob, and the existing zero-blob
|
|
28
|
+
guard at the bottom raises with the full coverage detail -- each refusal named.
|
|
29
|
+
A package with no runnable blob is never written: that is worse than an error,
|
|
30
|
+
because the customer only finds out on the device."""
|
|
31
|
+
from __future__ import annotations
|
|
32
|
+
import hashlib
|
|
33
|
+
import re
|
|
34
|
+
from pathlib import Path
|
|
35
|
+
|
|
36
|
+
from ..core.atomic_write import atomic_write_bytes
|
|
37
|
+
from .adapters import Blob, CompilerAdapter
|
|
38
|
+
from .adapters.cpu import CpuAdapter
|
|
39
|
+
from .adapters.ethos_u import VelaAdapter, VelaFootprintRefused
|
|
40
|
+
from .adapters.drpai import DrpaiAdapter
|
|
41
|
+
from .adapters.deepx import DeepxAdapter
|
|
42
|
+
from .adapters.executorch import ExecutorchAdapter
|
|
43
|
+
from .manifest import Manifest, Target, Coverage
|
|
44
|
+
from .package import write_package
|
|
45
|
+
from .sram_fit import SramFit, evaluate_sram_fit
|
|
46
|
+
from .targets import TargetSpec, resolve_targets
|
|
47
|
+
from .tensorio import extract_io
|
|
48
|
+
|
|
49
|
+
# Default adapter registry. Each is detect-and-skip (is_available() False when
|
|
50
|
+
# its tool is absent); vela (ethos_u) skips on hosts without the ethos-u-vela package.
|
|
51
|
+
# A backend may carry more than one adapter (cpu: CpuAdapter for .tflite,
|
|
52
|
+
# ExecutorchAdapter for .pte) -- see the by_backend grouping below, which
|
|
53
|
+
# selects among a backend's adapters by accepts(src_fmt), not by last-one-wins.
|
|
54
|
+
_ADAPTERS: list[CompilerAdapter] = [
|
|
55
|
+
CpuAdapter(), VelaAdapter(), DrpaiAdapter(), DeepxAdapter(), ExecutorchAdapter(),
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
# #1125: mirrors metadata/schemas/board.schema.json's `models[].name` pattern.
|
|
59
|
+
# build_model() is called directly by non-CLI callers (tests, future tooling),
|
|
60
|
+
# not just alp_cli.model's schema-validated path -- an allowlist here is the
|
|
61
|
+
# root-cause guard, independent of whether the caller validated board.yaml.
|
|
62
|
+
_NAME_RE = re.compile(r"^[A-Za-z][A-Za-z0-9_-]*$")
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _src_format(source: Path) -> str:
|
|
66
|
+
return source.suffix.lstrip(".").lower() # "tflite" | "onnx"
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _placed_nothing_on_accelerator(backend: str, blob: Blob) -> bool:
|
|
70
|
+
"""True for an accelerator compile whose own compiler reports it placed
|
|
71
|
+
ZERO operators on the accelerator.
|
|
72
|
+
|
|
73
|
+
Such a target is not an accelerator target. It carries `arena=0` and
|
|
74
|
+
`requires={'sram_kib': 0}` -- the exact "fits any envelope" shape
|
|
75
|
+
alp-sdk's on-device selector reads unconditionally
|
|
76
|
+
(`return e->arena_sram_kib == 0u || t->req_sram_kib <= e->arena_sram_kib;`,
|
|
77
|
+
src/backends/inference/alp_model_select.c) -- so the board would happily
|
|
78
|
+
select an `ethos_u` blob that runs every operator on the CPU anyway, while
|
|
79
|
+
the honest `cpu` target sits beside it in the same package. Measured with
|
|
80
|
+
real `ethos-u-vela` 5.1.0 on `tests/fixtures/models/float32_fc.tflite`:
|
|
81
|
+
`E1M-AEN801` shipped THREE such `ethos_u` targets (`ethos-u85-256`,
|
|
82
|
+
`ethos-u55-256`, `ethos-u55-128`). Dropped to a coverage skip instead -- legibly absent,
|
|
83
|
+
with the reason, rather than silently present as a zero (tan-cli#789
|
|
84
|
+
review MINOR 7).
|
|
85
|
+
|
|
86
|
+
`cpu` is exempt: a CPU target legitimately places nothing on an
|
|
87
|
+
accelerator, and its 0 arena is a real figure, not a missing one.
|
|
88
|
+
`npu_op_count is None` (every adapter but vela today, and vela itself when
|
|
89
|
+
its placement summary could not be parsed) is exempt too -- unknown is not
|
|
90
|
+
zero, and inventing a verdict from an unreadable summary is exactly what
|
|
91
|
+
`_parse_vela_placement` returning None exists to prevent."""
|
|
92
|
+
return backend != "cpu" and blob.npu_op_count == 0
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class SramNoFitRefused(Exception):
|
|
96
|
+
"""A certain arena or SRAM0-residency NO-FIT under `Sram_Only`
|
|
97
|
+
(tan-cli#1288, `tan.model.sram_fit`) on EVERY ethos_u target the build
|
|
98
|
+
reached -- refuses the WHOLE per-model build (a single target's no-fit
|
|
99
|
+
is a coverage skip, like `VelaFootprintRefused`; tan-cli#1486): every
|
|
100
|
+
ethos_u variant this SKU would ship describes a placement that cannot
|
|
101
|
+
execute on it (`src/backends/inference/ethos_u_aen.cpp` pins every NPU
|
|
102
|
+
access to the SRAM AXI port under `Sram_Only`), so no partial package is
|
|
103
|
+
written -- `_run_build` (`tan.commands.model_cmd`) catches this ahead of
|
|
104
|
+
the generic per-model except-clause and reports `model.sram-no-fit` at
|
|
105
|
+
`ExitCode.VALIDATION_FAILURE`, not the generic `model.build-failed`."""
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _sram_no_fit_message(spec: TargetSpec, fit: SramFit) -> str:
|
|
109
|
+
parts = []
|
|
110
|
+
if fit.arena.verdict == "no-fit":
|
|
111
|
+
parts.append(f"arena needs {fit.arena.needed_kib} KiB, only "
|
|
112
|
+
f"{fit.arena.limit_kib} KiB available "
|
|
113
|
+
f"(cores.<id>.inference.default_arena_kib)")
|
|
114
|
+
if fit.sram0.verdict == "no-fit":
|
|
115
|
+
# NOT `fit.arena.limit_kib` (tan-cli#1288 review round 2, finding 4):
|
|
116
|
+
# the arena figure `sram0` actually summed against is whichever
|
|
117
|
+
# `resolve_arena_budget` shape decided it -- the SAME core's budget
|
|
118
|
+
# only when `arena.kind == "single"`; `sram0` uses the SMALLEST of a
|
|
119
|
+
# `"range"`'s several candidates (`arena.limit_kib` there is the
|
|
120
|
+
# LARGEST, a different number), and a `MIN_ARENA_KIB` lower bound
|
|
121
|
+
# when arena itself is unresolved (`arena.limit_kib` is `None`
|
|
122
|
+
# there -- this would have literally printed "arena None KiB").
|
|
123
|
+
# `sram0.needed_kib` is ALWAYS `blob_kib + <that figure>` by
|
|
124
|
+
# construction (`tan.model.sram_fit._evaluate_sram0_*`), so deriving
|
|
125
|
+
# it back out this way is correct regardless of which shape produced
|
|
126
|
+
# the verdict -- reading `fit.arena.limit_kib` instead is not.
|
|
127
|
+
arena_used = fit.sram0.needed_kib - fit.blob_kib
|
|
128
|
+
parts.append(f"SRAM0 needs {fit.sram0.needed_kib} KiB (blob "
|
|
129
|
+
f"{fit.blob_kib} KiB + arena {arena_used} KiB), "
|
|
130
|
+
f"only {fit.sram0.limit_kib} KiB available")
|
|
131
|
+
detail = "; ".join(parts)
|
|
132
|
+
return (f"{spec.accel_config or spec.backend}: {detail} -- refusing to ship a "
|
|
133
|
+
f"Sram_Only target whose weights/arena cannot be SRAM0-resident "
|
|
134
|
+
f"(src/backends/inference/ethos_u_aen.cpp pins every NPU access "
|
|
135
|
+
f"to the SRAM AXI port). No package written for this model.")
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _no_placement_reason(spec: TargetSpec, blob: Blob) -> str:
|
|
139
|
+
"""The coverage reason for a dropped zero-placement accelerator target --
|
|
140
|
+
the compiler's own verdict, the target it applies to, and what the package
|
|
141
|
+
still offers instead. One line, same as every other coverage reason."""
|
|
142
|
+
total = (blob.npu_op_count or 0) + (blob.cpu_op_count or 0)
|
|
143
|
+
tool = blob.compiler_version or spec.backend
|
|
144
|
+
# "accepts ... against ANY arena size", not the retired "fits" vocabulary --
|
|
145
|
+
# same reason `ethos_u._refuse_zero_sram_footprint` words it that way.
|
|
146
|
+
return (f"{tool} placed 0 of {total} operators on {spec.accel_config or spec.backend}; "
|
|
147
|
+
f"an accelerator target with no accelerator placement would ship arena 0 / "
|
|
148
|
+
f"sram_kib 0, which alp-sdk's on-device selector accepts against ANY arena "
|
|
149
|
+
f"size (src/backends/inference/alp_model_select.c). The cpu target runs this "
|
|
150
|
+
f"model.")
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def build_model(*, sku: str, name: str, source: Path, out_dir: Path,
|
|
154
|
+
metadata_root: Path,
|
|
155
|
+
adapters: list[CompilerAdapter] | None = None,
|
|
156
|
+
compile_opts: dict[str, dict] | None = None,
|
|
157
|
+
board_doc: dict | None = None) -> Path:
|
|
158
|
+
if not _NAME_RE.fullmatch(name):
|
|
159
|
+
raise ValueError(f"invalid model name {name!r}: must match {_NAME_RE.pattern!r}")
|
|
160
|
+
registry = list(_ADAPTERS if adapters is None else adapters)
|
|
161
|
+
by_backend: dict[str, list[CompilerAdapter]] = {}
|
|
162
|
+
for a in registry:
|
|
163
|
+
by_backend.setdefault(a.backend, []).append(a)
|
|
164
|
+
specs = resolve_targets(sku, metadata_root=metadata_root)
|
|
165
|
+
src_fmt = _src_format(source)
|
|
166
|
+
opts_by_backend = compile_opts or {}
|
|
167
|
+
|
|
168
|
+
out_dir.mkdir(parents=True, exist_ok=True)
|
|
169
|
+
targets: list[Target] = []
|
|
170
|
+
coverage: list[Coverage] = []
|
|
171
|
+
blobs: list[bytes] = []
|
|
172
|
+
no_fit_msgs: list[str] = []
|
|
173
|
+
ethos_u_fit = 0
|
|
174
|
+
for spec in specs:
|
|
175
|
+
candidates = by_backend.get(spec.backend, [])
|
|
176
|
+
if not candidates:
|
|
177
|
+
coverage.append(Coverage(spec.backend, spec.accel_config, "skipped",
|
|
178
|
+
f"no compiler adapter for {spec.backend}"))
|
|
179
|
+
continue
|
|
180
|
+
if len(candidates) > 1:
|
|
181
|
+
# A backend with more than one adapter (cpu: CpuAdapter + ExecutorchAdapter)
|
|
182
|
+
# is disambiguated by source format up front -- accepts() decides identity,
|
|
183
|
+
# not registration order. A single-adapter backend keeps the original order
|
|
184
|
+
# below (requires_compile_opts / is_available reported before "incompatible"),
|
|
185
|
+
# so an unrelated format mismatch doesn't mask a "no compile config" skip.
|
|
186
|
+
adapter = next((a for a in candidates if a.accepts(src_fmt)), None)
|
|
187
|
+
if adapter is None:
|
|
188
|
+
coverage.append(Coverage(spec.backend, spec.accel_config, "incompatible",
|
|
189
|
+
f"{spec.backend} does not accept .{src_fmt}"))
|
|
190
|
+
continue
|
|
191
|
+
else:
|
|
192
|
+
adapter = candidates[0]
|
|
193
|
+
backend_opts = opts_by_backend.get(spec.backend)
|
|
194
|
+
if adapter.requires_compile_opts and not backend_opts:
|
|
195
|
+
coverage.append(Coverage(spec.backend, spec.accel_config, "skipped",
|
|
196
|
+
f"no compile config for {spec.backend} "
|
|
197
|
+
f"(add models[].compile.{spec.backend} to board.yaml)"))
|
|
198
|
+
continue
|
|
199
|
+
if not adapter.is_available():
|
|
200
|
+
coverage.append(Coverage(spec.backend, spec.accel_config, "skipped",
|
|
201
|
+
f"{spec.backend} compiler not installed"))
|
|
202
|
+
continue
|
|
203
|
+
if not adapter.accepts(src_fmt):
|
|
204
|
+
coverage.append(Coverage(spec.backend, spec.accel_config, "incompatible",
|
|
205
|
+
f"{spec.backend} does not accept .{src_fmt}"))
|
|
206
|
+
continue
|
|
207
|
+
try:
|
|
208
|
+
# `spec.vela_vendor_config_filename` / `spec.soc_declares_dram` are
|
|
209
|
+
# passed for the adapter's DIAGNOSTICS, not its output -- see
|
|
210
|
+
# `CompilerAdapter.compile`. They are what let a vela footprint
|
|
211
|
+
# refusal name the proprietary profile file a part's own SoC spec
|
|
212
|
+
# declares -- and stay silent for one that declares none
|
|
213
|
+
# (tan-cli#789 review (g)) -- and say that a DRAM
|
|
214
|
+
# placement went to memory this part declares no interface to.
|
|
215
|
+
#
|
|
216
|
+
# `spec.vela_memory_mode` / `.vela_system_config` /
|
|
217
|
+
# `.vela_vendor_system_config` are the opposite: the silicon's own
|
|
218
|
+
# vela memory profile (SoC spec `npu_toolchain.vela`, alp-sdk
|
|
219
|
+
# #1470), and it DOES change the artifact -- that is the point. It
|
|
220
|
+
# is what makes an `ethos-u85-256` target ship at all: measured,
|
|
221
|
+
# real `ethos-u-vela` 5.1.0 over `tests/fixtures/models/
|
|
222
|
+
# tiny_int8.tflite` reported `sram_memory_used = 0.0` for
|
|
223
|
+
# E1M-AEN801 without it (refused, a coverage row) and 0.03125 KiB
|
|
224
|
+
# with `--memory-mode Sram_Only` (a real target). Passed on the
|
|
225
|
+
# same call as everything else so a target can never be compiled
|
|
226
|
+
# with one target's accel-config and another's memory model; every
|
|
227
|
+
# vela field is None for a non-ethos_u backend by construction
|
|
228
|
+
# (`targets._soc_targets`).
|
|
229
|
+
blob = adapter.compile(source, accel_config=spec.accel_config,
|
|
230
|
+
out_dir=out_dir, opts=backend_opts,
|
|
231
|
+
vela_memory_mode=spec.vela_memory_mode,
|
|
232
|
+
vela_system_config=spec.vela_system_config,
|
|
233
|
+
vela_vendor_system_config=spec.vela_vendor_system_config,
|
|
234
|
+
vela_vendor_config_filename=spec.vela_vendor_config_filename,
|
|
235
|
+
soc_declares_dram=spec.soc_declares_dram)
|
|
236
|
+
except VelaFootprintRefused as err:
|
|
237
|
+
# ONE target's refusal, not the package's. See the module docstring.
|
|
238
|
+
coverage.append(Coverage(spec.backend, spec.accel_config, "skipped", str(err)))
|
|
239
|
+
continue
|
|
240
|
+
if spec.backend == "ethos_u":
|
|
241
|
+
# tan-cli#1288: a certain arena/SRAM0 no-fit under `Sram_Only`
|
|
242
|
+
# skips THIS target as a coverage row; the model is refused
|
|
243
|
+
# (see `SramNoFitRefused`) only when no ethos_u target ships
|
|
244
|
+
# (tan-cli#1486) -- a no-op for every other memory mode
|
|
245
|
+
# (`evaluate_sram_fit` itself gates on it).
|
|
246
|
+
fit = evaluate_sram_fit(
|
|
247
|
+
memory_mode=spec.vela_memory_mode, req_sram_kib=blob.req_sram_kib,
|
|
248
|
+
blob_len_bytes=len(blob.payload), board_doc=board_doc,
|
|
249
|
+
paired_core=spec.paired_core, sku=sku, metadata_root=metadata_root,
|
|
250
|
+
)
|
|
251
|
+
if fit.no_fit:
|
|
252
|
+
# ONE target's no-fit skips that target only; the model is
|
|
253
|
+
# refused as a whole (below) only when NO ethos_u target
|
|
254
|
+
# survives (tan-cli#1486).
|
|
255
|
+
msg = _sram_no_fit_message(spec, fit)
|
|
256
|
+
no_fit_msgs.append(msg)
|
|
257
|
+
coverage.append(Coverage(spec.backend, spec.accel_config, "skipped", msg))
|
|
258
|
+
continue
|
|
259
|
+
if _placed_nothing_on_accelerator(spec.backend, blob):
|
|
260
|
+
coverage.append(Coverage(spec.backend, spec.accel_config, "skipped",
|
|
261
|
+
_no_placement_reason(spec, blob)))
|
|
262
|
+
continue
|
|
263
|
+
if spec.backend == "ethos_u":
|
|
264
|
+
ethos_u_fit += 1 # fits AND ships; a fit that places nothing does not count
|
|
265
|
+
targets.append(Target(
|
|
266
|
+
backend=spec.backend, silicon_ref=spec.silicon_ref,
|
|
267
|
+
blob_format=blob.format, accel_config=spec.accel_config,
|
|
268
|
+
arena=blob.arena_bytes,
|
|
269
|
+
requires={"sram_kib": blob.req_sram_kib, "op_features": []},
|
|
270
|
+
blob=len(blobs), compiler_version=blob.compiler_version,
|
|
271
|
+
# The compiler's unresolved caveats travel WITH the blob into the
|
|
272
|
+
# package. Dropping them here was the gap: `tan model check
|
|
273
|
+
# --exact` reported them and shipped nothing, `tan model build`
|
|
274
|
+
# shipped bytes and reported nothing, so a blob compiled against
|
|
275
|
+
# vela's BUILT-IN default memory model could reach a board with
|
|
276
|
+
# the package silent about it -- while `arena`/`sram_kib` right
|
|
277
|
+
# beside it, figures that describe THAT default memory model, are
|
|
278
|
+
# what alp-sdk's on-device selector consumes
|
|
279
|
+
# (src/backends/inference/alp_model_select.c).
|
|
280
|
+
caveats=list(blob.caveats)))
|
|
281
|
+
blobs.append(blob.payload)
|
|
282
|
+
|
|
283
|
+
if no_fit_msgs and not ethos_u_fit:
|
|
284
|
+
raise SramNoFitRefused("; ".join(no_fit_msgs))
|
|
285
|
+
|
|
286
|
+
if not blobs:
|
|
287
|
+
detail = "; ".join(f"{c.backend}:{c.status} ({c.reason})" for c in coverage)
|
|
288
|
+
raise ValueError(f"no blob compiled for model '{name}' (.{src_fmt}); coverage: {detail}")
|
|
289
|
+
|
|
290
|
+
src_bytes = source.read_bytes() # read once: shared by the sha + tensor-I/O
|
|
291
|
+
inputs, outputs = extract_io(source, raw=src_bytes)
|
|
292
|
+
mft = Manifest(name=name, src_sha=hashlib.sha256(src_bytes).digest(),
|
|
293
|
+
inputs=inputs, outputs=outputs,
|
|
294
|
+
targets=targets, coverage=coverage)
|
|
295
|
+
out_path = out_dir / f"{name}.alpmodel"
|
|
296
|
+
# Belt-and-suspenders: the name allowlist above already makes escape
|
|
297
|
+
# impossible for a bare filename, but fail closed on containment too --
|
|
298
|
+
# a resolved-path check catches this even if the join expression above
|
|
299
|
+
# ever grows a second path segment.
|
|
300
|
+
resolved_out_dir = out_dir.resolve()
|
|
301
|
+
if not out_path.resolve().is_relative_to(resolved_out_dir):
|
|
302
|
+
raise ValueError(f"refusing to write outside out_dir: {out_path}")
|
|
303
|
+
atomic_write_bytes(str(out_path), write_package(mft, blobs))
|
|
304
|
+
return out_path
|