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/lazy_typer.py
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""A Typer app / Click group that builds each subcommand on first use.
|
|
3
|
+
|
|
4
|
+
``tan.cli`` used to import all ~31 command modules at module scope, so every
|
|
5
|
+
invocation -- ``tan --version`` included -- paid for the heaviest of them
|
|
6
|
+
(tan-cli#1505). Here the Click tree is assembled from ``COMMAND_TABLE`` and a
|
|
7
|
+
command's module is imported only when Click asks for that command by name
|
|
8
|
+
(dispatch, ``tan <cmd> --help``) or when something walks the whole tree
|
|
9
|
+
(``tan --help``, a ``--format`` domain check): ``LazyCommands`` is a mapping, so
|
|
10
|
+
every ``group.commands[...]`` / ``sorted(group.commands)`` / ``.items()`` caller
|
|
11
|
+
keeps working unchanged; only ``.items()``/``.values()`` pay for everything.
|
|
12
|
+
|
|
13
|
+
The loaders in ``tan.command_table`` stay plain static ``from ... import``
|
|
14
|
+
statements, which is what keeps PyInstaller able to see every command module.
|
|
15
|
+
"""
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from collections.abc import Callable, Iterator, MutableMapping
|
|
19
|
+
from typing import TYPE_CHECKING, Any
|
|
20
|
+
|
|
21
|
+
import typer
|
|
22
|
+
from typer.core import TyperGroup
|
|
23
|
+
from typer.main import get_command_from_info
|
|
24
|
+
from typer.models import CommandInfo
|
|
25
|
+
|
|
26
|
+
from tan.command_table import COMMAND_TABLE, CommandSpec
|
|
27
|
+
|
|
28
|
+
if TYPE_CHECKING:
|
|
29
|
+
import click
|
|
30
|
+
|
|
31
|
+
#: Captured at import so a swap of `typer.main.get_command` (see
|
|
32
|
+
#: `LazyTyper.__call__`) can never make this module call itself.
|
|
33
|
+
_REAL_GET_COMMAND = typer.main.get_command
|
|
34
|
+
|
|
35
|
+
COMMAND_NAMES: tuple[str, ...] = tuple(spec.name for spec in COMMAND_TABLE)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
_REGISTER_MSG = (
|
|
39
|
+
"tan commands are not registered with app.command(); add a loader and a "
|
|
40
|
+
"CommandSpec row to tan.command_table.COMMAND_TABLE"
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class LazyCommands(MutableMapping): # type: ignore[type-arg]
|
|
45
|
+
"""name -> Click command, built on first lookup, in ``COMMAND_TABLE`` order."""
|
|
46
|
+
|
|
47
|
+
def __init__(self, build: Callable[[CommandSpec], 'click.Command']) -> None:
|
|
48
|
+
self._build = build
|
|
49
|
+
self._specs = {spec.name: spec for spec in COMMAND_TABLE}
|
|
50
|
+
self._built: dict[str, 'click.Command'] = {}
|
|
51
|
+
self._removed: set[str] = set()
|
|
52
|
+
self._extra: dict[str, 'click.Command'] = {}
|
|
53
|
+
|
|
54
|
+
def __getitem__(self, name: str) -> 'click.Command':
|
|
55
|
+
if name in self._extra:
|
|
56
|
+
return self._extra[name]
|
|
57
|
+
if name in self._built:
|
|
58
|
+
return self._built[name]
|
|
59
|
+
if name in self._removed or name not in self._specs:
|
|
60
|
+
raise KeyError(name)
|
|
61
|
+
if name not in self._built:
|
|
62
|
+
self._built[name] = self._build(self._specs[name])
|
|
63
|
+
return self._built[name]
|
|
64
|
+
|
|
65
|
+
def __setitem__(self, name: str, command: 'click.Command') -> None:
|
|
66
|
+
if name in self._specs:
|
|
67
|
+
self._removed.discard(name)
|
|
68
|
+
self._built[name] = command
|
|
69
|
+
return
|
|
70
|
+
self._extra[name] = command
|
|
71
|
+
|
|
72
|
+
def __delitem__(self, name: str) -> None:
|
|
73
|
+
if name not in self:
|
|
74
|
+
raise KeyError(name)
|
|
75
|
+
self._extra.pop(name, None)
|
|
76
|
+
self._built.pop(name, None)
|
|
77
|
+
if name in self._specs:
|
|
78
|
+
self._removed.add(name)
|
|
79
|
+
|
|
80
|
+
def __iter__(self) -> Iterator[str]:
|
|
81
|
+
for name in self._specs:
|
|
82
|
+
if name not in self._removed:
|
|
83
|
+
yield name
|
|
84
|
+
yield from self._extra
|
|
85
|
+
|
|
86
|
+
def __len__(self) -> int:
|
|
87
|
+
return sum(1 for _ in self)
|
|
88
|
+
|
|
89
|
+
def __contains__(self, name: object) -> bool:
|
|
90
|
+
return name in self._extra or (name in self._specs and name not in self._removed)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class LazyGroup(TyperGroup):
|
|
94
|
+
"""A ``TyperGroup`` whose ``commands`` is a ``LazyCommands`` when built lazily."""
|
|
95
|
+
|
|
96
|
+
def install_lazy_commands(self, build: Callable[[CommandSpec], 'click.Command']) -> None:
|
|
97
|
+
self.commands = LazyCommands(build)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class LazyTyper(typer.Typer):
|
|
101
|
+
"""``typer.Typer`` whose subcommands come from ``COMMAND_TABLE``.
|
|
102
|
+
|
|
103
|
+
``registered_commands`` still answers with the full, ordered list of
|
|
104
|
+
``CommandInfo`` (built on first read) so table-derived tests and tools keep
|
|
105
|
+
their view, and ``typer.main.get_command(app)`` still yields the complete
|
|
106
|
+
eager tree. Only ``__call__`` -- the real ``tan`` entry -- and
|
|
107
|
+
``lazy_group()`` take the lazy path.
|
|
108
|
+
"""
|
|
109
|
+
|
|
110
|
+
def __init__(self, *args: Any, command_cls: type | None = None, **kwargs: Any) -> None:
|
|
111
|
+
self._command_cls = command_cls
|
|
112
|
+
self._lazy_build = False
|
|
113
|
+
self._lazy_group: LazyGroup | None = None
|
|
114
|
+
self._infos: dict[str, CommandInfo] = {}
|
|
115
|
+
super().__init__(*args, cls=LazyGroup, **kwargs)
|
|
116
|
+
|
|
117
|
+
# `Typer.__init__` assigns `self.registered_commands = []`; absorb it.
|
|
118
|
+
@property
|
|
119
|
+
def registered_commands(self) -> list[CommandInfo]: # type: ignore[override]
|
|
120
|
+
if self._lazy_build:
|
|
121
|
+
return []
|
|
122
|
+
return [self.command_info(spec) for spec in COMMAND_TABLE]
|
|
123
|
+
|
|
124
|
+
@registered_commands.setter
|
|
125
|
+
def registered_commands(self, value: list[CommandInfo]) -> None:
|
|
126
|
+
if value:
|
|
127
|
+
raise TypeError(_REGISTER_MSG)
|
|
128
|
+
|
|
129
|
+
def command(self, *args: Any, **kwargs: Any) -> Any: # type: ignore[override]
|
|
130
|
+
raise TypeError(_REGISTER_MSG)
|
|
131
|
+
|
|
132
|
+
def command_info(self, spec: CommandSpec) -> CommandInfo:
|
|
133
|
+
info = self._infos.get(spec.name)
|
|
134
|
+
if info is None:
|
|
135
|
+
callback, context_settings = spec.load()
|
|
136
|
+
info = CommandInfo(
|
|
137
|
+
name=spec.name,
|
|
138
|
+
cls=self._command_cls,
|
|
139
|
+
context_settings=context_settings,
|
|
140
|
+
callback=callback,
|
|
141
|
+
rich_help_panel=spec.rich_help_panel,
|
|
142
|
+
)
|
|
143
|
+
self._infos[spec.name] = info
|
|
144
|
+
return info
|
|
145
|
+
|
|
146
|
+
def _build_command(self, spec: CommandSpec) -> 'click.Command':
|
|
147
|
+
return get_command_from_info(
|
|
148
|
+
self.command_info(spec),
|
|
149
|
+
pretty_exceptions_short=self.pretty_exceptions_short,
|
|
150
|
+
rich_markup_mode=self.rich_markup_mode,
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
def lazy_group(self) -> 'click.Command':
|
|
154
|
+
"""The Click tree with subcommand modules deferred until looked up.
|
|
155
|
+
|
|
156
|
+
Built once per instance; every caller (dispatch, help envelope, the
|
|
157
|
+
`--format` domain walk) shares it.
|
|
158
|
+
"""
|
|
159
|
+
if self._lazy_group is None:
|
|
160
|
+
self._lazy_build = True
|
|
161
|
+
try:
|
|
162
|
+
group = _REAL_GET_COMMAND(self)
|
|
163
|
+
finally:
|
|
164
|
+
self._lazy_build = False
|
|
165
|
+
assert isinstance(group, LazyGroup)
|
|
166
|
+
group.install_lazy_commands(self._build_command)
|
|
167
|
+
self._lazy_group = group
|
|
168
|
+
return self._lazy_group
|
|
169
|
+
|
|
170
|
+
def __call__(self, *args: Any, **kwargs: Any) -> Any:
|
|
171
|
+
group = self.lazy_group()
|
|
172
|
+
|
|
173
|
+
def _get_command(typer_instance: typer.Typer) -> 'click.Command':
|
|
174
|
+
# `Typer.__call__` rebuilds the tree via `typer.main.get_command(self)`;
|
|
175
|
+
# hand it the lazy group for THIS app and delegate for anything else.
|
|
176
|
+
if typer_instance is self:
|
|
177
|
+
return group
|
|
178
|
+
return _REAL_GET_COMMAND(typer_instance)
|
|
179
|
+
|
|
180
|
+
original = typer.main.get_command
|
|
181
|
+
typer.main.get_command = _get_command # type: ignore[assignment]
|
|
182
|
+
try:
|
|
183
|
+
return super().__call__(*args, **kwargs)
|
|
184
|
+
finally:
|
|
185
|
+
typer.main.get_command = original
|
tan/model/__init__.py
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""Generate the canonical test fixtures: a minimal .alpmodel + its C byte
|
|
3
|
+
array, and the issue #1254 onnx_cpu regression fixture (tests/yocto/).
|
|
4
|
+
|
|
5
|
+
These three committed files (`tests/fixtures/alpmodel/minimal.alpmodel`,
|
|
6
|
+
`tests/unit/alpmodel_reader/src/fixture.h`, `tests/yocto/onnx_cpu_fixture.h`)
|
|
7
|
+
live in alp-sdk, not tan-cli -- `tests/unit/` and `tests/yocto/` are
|
|
8
|
+
Zephyr/Yocto C-test trees with no tan-cli equivalent (ADR-0028 Decision-3:
|
|
9
|
+
the on-device `.alpmodel` C reader, and the tests that exercise it, stay in
|
|
10
|
+
alp-sdk). `python/tests/model/test_package.py` compares the (now relocated)
|
|
11
|
+
Python encoder's output against these files read out of a bound alp-sdk
|
|
12
|
+
checkout, the same way `tests.conftest.sdk_root()` binds every other
|
|
13
|
+
cross-repo test in this suite.
|
|
14
|
+
|
|
15
|
+
Run as: python -m tan.model._gen_fixture --root <path-to-alp-sdk-checkout>
|
|
16
|
+
(or set ALP_SDK_ROOT; --root wins when both are given)"""
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
import argparse
|
|
19
|
+
import os
|
|
20
|
+
import sys
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
from ..core.shapes import SDK_MARKER as _SDK_MARKER
|
|
23
|
+
from .manifest import Manifest, Tensor, Target, Coverage
|
|
24
|
+
from .package import write_package
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _manifest() -> Manifest:
|
|
28
|
+
return Manifest(
|
|
29
|
+
name="minimal",
|
|
30
|
+
src_sha=bytes(range(32)),
|
|
31
|
+
inputs=[Tensor("int8", 4, [1, 96, 96, 1], 0.0078, -1)],
|
|
32
|
+
outputs=[Tensor("int8", 2, [1, 2], 0.004, 0)],
|
|
33
|
+
targets=[
|
|
34
|
+
Target("ethos_u", "alif:ensemble:e8", "vela_tflite", "ethos-u85-256",
|
|
35
|
+
65536, {"sram_kib": 256, "op_features": []}, 0, compiler_version="vela 4.1.0"),
|
|
36
|
+
Target("cpu", "*", "tflite", "", 131072,
|
|
37
|
+
{"sram_kib": 0, "op_features": []}, 1, compiler_version="passthrough"),
|
|
38
|
+
],
|
|
39
|
+
coverage=[Coverage("deepx_dxm1", "", "skipped", "dx-compiler not found")],
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def build_fixture_bytes() -> bytes:
|
|
44
|
+
return write_package(_manifest(), [b"VELA\x00\x01\x02\x03", b"TFLITE\x00"])
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _onnx_cpu_manifest() -> Manifest:
|
|
48
|
+
return Manifest(
|
|
49
|
+
name="onnx_cpu_smoke",
|
|
50
|
+
src_sha=bytes(range(32)),
|
|
51
|
+
targets=[
|
|
52
|
+
Target("cpu", "*", "onnx", "", 65536, {"sram_kib": 0, "op_features": []}, 0,
|
|
53
|
+
compiler_version="passthrough"),
|
|
54
|
+
],
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def build_onnx_cpu_fixture_bytes() -> bytes:
|
|
59
|
+
"""Issue #1254 end-to-end regression fixture (tests/yocto/alpmodel_onnx_cpu.c):
|
|
60
|
+
an "onnx" blob_format .alpmodel container, decoded by the real
|
|
61
|
+
zcbor-backed alp_model_parse()."""
|
|
62
|
+
return write_package(_onnx_cpu_manifest(), [b"\x08\x01\x12\x04test-onnx-blob-placeholder"])
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def to_c_header(raw: bytes, *, array_name: str = "alp_model_fixture",
|
|
66
|
+
guard: str = "ALP_MODEL_FIXTURE_H") -> str:
|
|
67
|
+
# clang-format guards: the array wrap width is the generator's,
|
|
68
|
+
# not the formatter's -- without them a repo-wide clang-format
|
|
69
|
+
# sweep rewraps the rows and the committed-matches-generator
|
|
70
|
+
# pytest fails.
|
|
71
|
+
lines = ["/* GENERATED by `python -m tan.model._gen_fixture` -- do not edit. */",
|
|
72
|
+
"/* clang-format off */",
|
|
73
|
+
f"#ifndef {guard}", f"#define {guard}",
|
|
74
|
+
"#include <stddef.h>", "#include <stdint.h>", "",
|
|
75
|
+
f"static const uint8_t {array_name}[] = {{"]
|
|
76
|
+
for i in range(0, len(raw), 12):
|
|
77
|
+
chunk = ", ".join(f"0x{b:02x}" for b in raw[i:i + 12])
|
|
78
|
+
lines.append(f" {chunk},")
|
|
79
|
+
lines += ["};",
|
|
80
|
+
f"static const size_t {array_name}_len = {len(raw)};",
|
|
81
|
+
"", f"#endif /* {guard} */",
|
|
82
|
+
"/* clang-format on */", ""]
|
|
83
|
+
return "\n".join(lines)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _resolve_sdk_root(explicit: str | None) -> Path:
|
|
87
|
+
"""`--root` wins over `ALP_SDK_ROOT`; either must name a real alp-sdk
|
|
88
|
+
checkout (the same `scripts/alp_project.py` marker `tan`'s own SDK
|
|
89
|
+
discovery uses), not just an existing directory -- a typo'd path should
|
|
90
|
+
fail loudly here, not by writing into the wrong tree."""
|
|
91
|
+
root = explicit or os.environ.get("ALP_SDK_ROOT")
|
|
92
|
+
if not root:
|
|
93
|
+
print("tan.model._gen_fixture: no alp-sdk checkout given -- pass "
|
|
94
|
+
"--root <path> or set ALP_SDK_ROOT.", file=sys.stderr)
|
|
95
|
+
raise SystemExit(1)
|
|
96
|
+
resolved = Path(root).resolve()
|
|
97
|
+
if not resolved.joinpath(*_SDK_MARKER).is_file():
|
|
98
|
+
print(f"tan.model._gen_fixture: {resolved} does not look like an "
|
|
99
|
+
f"alp-sdk checkout (missing {'/'.join(_SDK_MARKER)}).",
|
|
100
|
+
file=sys.stderr)
|
|
101
|
+
raise SystemExit(1)
|
|
102
|
+
return resolved
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def main(argv: list[str] | None = None) -> None:
|
|
106
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
107
|
+
parser.add_argument("--root", help="alp-sdk checkout to write the fixtures into "
|
|
108
|
+
"(defaults to $ALP_SDK_ROOT)")
|
|
109
|
+
args = parser.parse_args(argv)
|
|
110
|
+
sdk_root = _resolve_sdk_root(args.root)
|
|
111
|
+
|
|
112
|
+
alpmodel = sdk_root / "tests/fixtures/alpmodel/minimal.alpmodel"
|
|
113
|
+
header = sdk_root / "tests/unit/alpmodel_reader/src/fixture.h"
|
|
114
|
+
onnx_cpu_header_path = sdk_root / "tests/yocto/onnx_cpu_fixture.h"
|
|
115
|
+
|
|
116
|
+
raw = build_fixture_bytes()
|
|
117
|
+
alpmodel.parent.mkdir(parents=True, exist_ok=True)
|
|
118
|
+
header.parent.mkdir(parents=True, exist_ok=True)
|
|
119
|
+
alpmodel.write_bytes(raw)
|
|
120
|
+
header.write_text(to_c_header(raw), encoding="utf-8", newline="")
|
|
121
|
+
|
|
122
|
+
onnx_cpu_raw = build_onnx_cpu_fixture_bytes()
|
|
123
|
+
onnx_cpu_header = to_c_header(onnx_cpu_raw, array_name="k_onnx_cpu_alpmodel",
|
|
124
|
+
guard="ALP_MODEL_ONNX_CPU_FIXTURE_H")
|
|
125
|
+
onnx_cpu_header_path.parent.mkdir(parents=True, exist_ok=True)
|
|
126
|
+
onnx_cpu_header_path.write_text(onnx_cpu_header, encoding="utf-8", newline="")
|
|
127
|
+
|
|
128
|
+
print(f"wrote {alpmodel} ({len(raw)} bytes), {header} and {onnx_cpu_header_path}")
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
if __name__ == "__main__":
|
|
132
|
+
main()
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""Compiler-adapter interface: one adapter per backend toolchain."""
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
from abc import ABC, abstractmethod
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class Blob:
|
|
11
|
+
"""One compiled artifact + the manifest metadata the writer needs."""
|
|
12
|
+
format: str # conventionally one of manifest.VALID_BLOB_FORMATS
|
|
13
|
+
# (vela_tflite | tflite | drpai_dir | dxnn |
|
|
14
|
+
# onnx | executorch); this is a WRITE-side
|
|
15
|
+
# field and is never itself validated -- an
|
|
16
|
+
# unlisted format here is rejected only when
|
|
17
|
+
# the package is later DECODED
|
|
18
|
+
# (Manifest.from_dict), and
|
|
19
|
+
# tests/gates/test_blob_format_producers_stay_in_valid_blob_formats.py
|
|
20
|
+
# is what actually keeps a producer's own
|
|
21
|
+
# literal in sync with the set.
|
|
22
|
+
payload: bytes
|
|
23
|
+
arena_bytes: int = 0
|
|
24
|
+
compiler_version: str = ""
|
|
25
|
+
req_sram_kib: int = 0
|
|
26
|
+
# Real per-operator NPU-vs-CPU placement, as the compiler itself reports
|
|
27
|
+
# it -- today only `VelaAdapter` populates these (parsed from vela's own
|
|
28
|
+
# "CPU/NPU operators = N (P%)" summary line, `ethos_u._parse_vela_
|
|
29
|
+
# placement`). None for every other adapter, and None when the compiler
|
|
30
|
+
# ran but didn't print a placement summary this could parse: a compiler
|
|
31
|
+
# exiting 0 is NOT proof every op landed on the NPU (vela exits 0 on a
|
|
32
|
+
# full CPU fallback by design), so a caller that wants "did this compile
|
|
33
|
+
# actually place anything on the accelerator" must read these, never
|
|
34
|
+
# infer it from the mere absence of an exception.
|
|
35
|
+
npu_op_count: int | None = None
|
|
36
|
+
cpu_op_count: int | None = None
|
|
37
|
+
# Adapter-authored caveats about THIS compile that a report must not drop
|
|
38
|
+
# -- each already a complete, customer-readable sentence. Today only
|
|
39
|
+
# `VelaAdapter` populates them, for the profile flags vela had to default:
|
|
40
|
+
# `--memory-mode` now comes from the SoC spec (alp-sdk #1470), but
|
|
41
|
+
# `--system-config` still does not, because the SoM-authoritative names
|
|
42
|
+
# live only in a proprietary .ini alp-sdk does not redistribute. Surfaced
|
|
43
|
+
# rather than swallowed so a compile tuned to vela's own bandwidth model
|
|
44
|
+
# -- or, under a memory mode whose const area sits on the arena's OTHER
|
|
45
|
+
# AXI port (`Shared_Sram`), one whose WEIGHTS vela's default system config
|
|
46
|
+
# placed as well, or, when no memory mode was resolvable at all, one sent
|
|
47
|
+
# to vela's DRAM-backed default on a module that has no DRAM -- cannot be
|
|
48
|
+
# mistaken for a module-authoritative one; `tan.model.check`'s
|
|
49
|
+
# `_report_from_vela_compile` carries them into `BackendReport.notes` and
|
|
50
|
+
# so into the JSON envelope. Never a substitute for failing: a figure that
|
|
51
|
+
# cannot be derived is refused (`ethos_u._refuse_zero_sram_footprint`),
|
|
52
|
+
# not caveated.
|
|
53
|
+
caveats: tuple[str, ...] = ()
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class CompilerAdapter(ABC):
|
|
57
|
+
backend: str # cpu | ethos_u | drpai | deepx_dxm1
|
|
58
|
+
# True for backends that need a per-model compile config the SDK can't
|
|
59
|
+
# derive (DEEPX JSON+calibration, DRP-AI spec). build_model records a
|
|
60
|
+
# "no compile config" coverage skip for these when no opts block is given.
|
|
61
|
+
requires_compile_opts: bool = False
|
|
62
|
+
|
|
63
|
+
@abstractmethod
|
|
64
|
+
def is_available(self) -> bool:
|
|
65
|
+
"""True if this backend's compiler is installed/usable on this host."""
|
|
66
|
+
|
|
67
|
+
@abstractmethod
|
|
68
|
+
def accepts(self, src_format: str) -> bool:
|
|
69
|
+
"""True if this adapter can consume the given source format (onnx|tflite)."""
|
|
70
|
+
|
|
71
|
+
@abstractmethod
|
|
72
|
+
def compile(self, source: Path, *, accel_config: str, out_dir: Path,
|
|
73
|
+
opts: dict | None = None,
|
|
74
|
+
vela_memory_mode: str | None = None,
|
|
75
|
+
vela_system_config: str | None = None,
|
|
76
|
+
vela_vendor_system_config: str | None = None,
|
|
77
|
+
vela_vendor_config_filename: str | None = None,
|
|
78
|
+
soc_declares_dram: bool | None = None) -> Blob:
|
|
79
|
+
"""Compile @source for @accel_config; return the Blob.
|
|
80
|
+
|
|
81
|
+
@opts is the per-model compile config for this backend
|
|
82
|
+
(board.yaml `models[].compile.<backend>`), with any path values already
|
|
83
|
+
resolved to absolute paths by the caller; None when the backend needs
|
|
84
|
+
no per-model config (cpu, ethos_u).
|
|
85
|
+
|
|
86
|
+
@vela_memory_mode / @vela_system_config / @vela_vendor_system_config
|
|
87
|
+
ARE compile inputs, and the only backend-specific ones on this
|
|
88
|
+
interface: `TargetSpec.vela_*`, the silicon's own vela invocation
|
|
89
|
+
profile out of the SoC spec's `npu_toolchain.vela` (alp-sdk #1470).
|
|
90
|
+
Only `VelaAdapter` acts on them -- every other adapter accepts and
|
|
91
|
+
ignores them, exactly as `cpu` and `ethos_u` accept and ignore `opts`
|
|
92
|
+
-- and they are named for vela on purpose, so a future DRP-AI or DEEPX
|
|
93
|
+
profile cannot be mistaken for being carried by them. `None` means the
|
|
94
|
+
caller resolved no profile, and an adapter must then invoke its
|
|
95
|
+
compiler exactly as it did before the profile existed rather than
|
|
96
|
+
substituting a default of its own: a guessed memory model compiles a
|
|
97
|
+
command stream for hardware the module does not have. The vendor
|
|
98
|
+
System_Config is a SEPARATE field from the built-in one because it is
|
|
99
|
+
legal only alongside a `--config` the customer supplies through
|
|
100
|
+
`ALP_VELA_CONFIG`; an adapter handed both in one field could put a
|
|
101
|
+
section name on a command line vela cannot resolve, which is rc=1.
|
|
102
|
+
|
|
103
|
+
@vela_vendor_config_filename / @soc_declares_dram are the opposite:
|
|
104
|
+
DIAGNOSTIC facts about this part, out of the same SoC spec
|
|
105
|
+
(`npu_toolchain.vela.vendor_config_filename` and
|
|
106
|
+
`external_memory_interfaces[]`), for an adapter whose ERROR TEXT is
|
|
107
|
+
part-specific even though its compile is not. Today only `VelaAdapter`
|
|
108
|
+
reads them -- to name the vendor `.ini` a part actually declares, and
|
|
109
|
+
to say that a DRAM placement describes memory this part has no
|
|
110
|
+
interface to. They replaced a `silicon_ref` vendor-prefix match that
|
|
111
|
+
stood in for both questions (tan-cli#789 review (g)); metadata answers
|
|
112
|
+
them per part, so no adapter has to know who builds what. `None` means
|
|
113
|
+
the caller resolved no such fact, and an adapter must then say nothing
|
|
114
|
+
about it rather than infer one from an accelerator config or a
|
|
115
|
+
compiler-reported profile name. Neither is a compile input: no adapter
|
|
116
|
+
may change the artifact it emits based on these."""
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""CPU/TFLM passthrough adapter: the model runs as-is on reference kernels."""
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from . import CompilerAdapter, Blob
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class CpuAdapter(CompilerAdapter):
|
|
9
|
+
backend = "cpu"
|
|
10
|
+
|
|
11
|
+
def is_available(self) -> bool:
|
|
12
|
+
return True # always available; no external tool
|
|
13
|
+
|
|
14
|
+
def accepts(self, src_format: str) -> bool:
|
|
15
|
+
return src_format == "tflite"
|
|
16
|
+
|
|
17
|
+
def compile(self, source: Path, *, accel_config: str, out_dir: Path,
|
|
18
|
+
opts: dict | None = None,
|
|
19
|
+
vela_memory_mode: str | None = None,
|
|
20
|
+
vela_system_config: str | None = None,
|
|
21
|
+
vela_vendor_system_config: str | None = None,
|
|
22
|
+
vela_vendor_config_filename: str | None = None,
|
|
23
|
+
soc_declares_dram: bool | None = None) -> Blob:
|
|
24
|
+
payload = source.read_bytes()
|
|
25
|
+
return Blob(format="tflite", payload=payload, arena_bytes=0,
|
|
26
|
+
compiler_version="passthrough")
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""DEEPX DX-M1 compiler adapter (host model compiler).
|
|
3
|
+
|
|
4
|
+
DEEPX's compiler is the proprietary, license-gated `dx-com` Python wheel
|
|
5
|
+
(console script `dxcom`; `dx-com` 2.3.0 verified -- Linux x86_64, CPython 3.12,
|
|
6
|
+
ONNX frontend, Target Hardware: M1). It compiles an ONNX model into a DEEPX NPU
|
|
7
|
+
model binary:
|
|
8
|
+
dxcom -m <model.onnx> -c <config.json> -o <output_dir>
|
|
9
|
+
with a per-model JSON config (the calibration dataset is referenced from inside
|
|
10
|
+
that config -- `dxcom` has no separate calibration CLI flag). `-o` is a
|
|
11
|
+
*directory*, but a successful compile writes exactly one canonical artifact into
|
|
12
|
+
it: ``<model_stem>.dxnn`` -- a self-describing flatbuffer (magic ``DXNN`` + a
|
|
13
|
+
JSON header). That single ``.dxnn`` is what the on-device `dx_rt` runtime loads,
|
|
14
|
+
so the adapter returns its raw bytes with `blob_format` ``dxnn`` (matching the
|
|
15
|
+
device-side ALP_INFERENCE_MODEL_DXNN that `alp_model_select` decodes), NOT a tar
|
|
16
|
+
of the directory. (Confirmed against a real `dxcom` 2.3.0 run, 2026-05-27: a
|
|
17
|
+
tiny CNN and a real yolo11n both emit a single ``<stem>.dxnn``; `compiler.log`
|
|
18
|
+
only appears with `--gen_log`. dxcom also requires >15 GB host RAM.)
|
|
19
|
+
|
|
20
|
+
The wheel is not redistributable, so it is NOT bundled; is_available() is True
|
|
21
|
+
only when `dxcom` is on PATH or ALP_DEEPX_SDK_HOME points at an install. The
|
|
22
|
+
per-model config + calibration come from board.yaml `models[].compile.deepx_dxm1`
|
|
23
|
+
(threaded in as `opts`). The dx_rt A55/PCIe *runtime* backend
|
|
24
|
+
(`src/yocto/inference_deepx.cpp`) is Stage 2 step 4 (bench-gated)."""
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
import os
|
|
27
|
+
import re
|
|
28
|
+
import shutil
|
|
29
|
+
import subprocess
|
|
30
|
+
from pathlib import Path
|
|
31
|
+
from tan.core.subprocess_env import spawn_env
|
|
32
|
+
from . import CompilerAdapter, Blob
|
|
33
|
+
|
|
34
|
+
# dxcom does post-training quantization + compilation (torch/onnx under the
|
|
35
|
+
# hood); minutes for a real model, but never unbounded in CI.
|
|
36
|
+
_DXCOM_TIMEOUT_S = 1800
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _dxcom_version() -> str:
|
|
40
|
+
"""Best-effort compiler version, e.g. 'DX-COM 2.3.0'; 'dxcom' on failure."""
|
|
41
|
+
try:
|
|
42
|
+
proc = subprocess.run(
|
|
43
|
+
["dxcom", "-v"], capture_output=True, text=True, timeout=60, env=spawn_env()
|
|
44
|
+
)
|
|
45
|
+
except (OSError, subprocess.SubprocessError):
|
|
46
|
+
return "dxcom"
|
|
47
|
+
m = re.search(r"DX-COM[^\d]*(\d+\.\d+\.\d+)", proc.stdout + proc.stderr)
|
|
48
|
+
return f"DX-COM {m.group(1)}" if m else "dxcom"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class DeepxAdapter(CompilerAdapter):
|
|
52
|
+
backend = "deepx_dxm1"
|
|
53
|
+
requires_compile_opts = True # needs a per-model dxcom JSON config
|
|
54
|
+
|
|
55
|
+
def is_available(self) -> bool:
|
|
56
|
+
if shutil.which("dxcom"): # the dx-com wheel's console script
|
|
57
|
+
return True
|
|
58
|
+
root = os.environ.get("ALP_DEEPX_SDK_HOME")
|
|
59
|
+
return bool(root) and Path(root).is_dir()
|
|
60
|
+
|
|
61
|
+
def accepts(self, src_format: str) -> bool:
|
|
62
|
+
return src_format == "onnx" # dxcom is an ONNX frontend
|
|
63
|
+
|
|
64
|
+
def compile(self, source: Path, *, accel_config: str, out_dir: Path,
|
|
65
|
+
opts: dict | None = None,
|
|
66
|
+
vela_memory_mode: str | None = None,
|
|
67
|
+
vela_system_config: str | None = None,
|
|
68
|
+
vela_vendor_system_config: str | None = None,
|
|
69
|
+
vela_vendor_config_filename: str | None = None,
|
|
70
|
+
soc_declares_dram: bool | None = None) -> Blob:
|
|
71
|
+
config = (opts or {}).get("config")
|
|
72
|
+
if not config:
|
|
73
|
+
raise RuntimeError(
|
|
74
|
+
"DEEPX compile needs models[].compile.deepx_dxm1.config "
|
|
75
|
+
"(a dxcom JSON config; the calibration set is referenced from it)")
|
|
76
|
+
dst = out_dir / f"{source.stem}_dxnn"
|
|
77
|
+
dst.mkdir(parents=True, exist_ok=True)
|
|
78
|
+
cmd = ["dxcom", "-m", str(source), "-c", str(config), "-o", str(dst)]
|
|
79
|
+
try:
|
|
80
|
+
proc = subprocess.run(
|
|
81
|
+
cmd, capture_output=True, text=True, timeout=_DXCOM_TIMEOUT_S, env=spawn_env()
|
|
82
|
+
)
|
|
83
|
+
except subprocess.TimeoutExpired as exc:
|
|
84
|
+
raise RuntimeError(f"dxcom timed out after {exc.timeout}s") from exc
|
|
85
|
+
if proc.returncode != 0:
|
|
86
|
+
raise RuntimeError(f"dxcom failed: {proc.stderr.strip() or proc.stdout.strip()}")
|
|
87
|
+
artifacts = sorted(dst.glob("*.dxnn"))
|
|
88
|
+
if not artifacts:
|
|
89
|
+
raise RuntimeError(f"dxcom produced no .dxnn in {dst}")
|
|
90
|
+
# A single-input compile emits exactly one <stem>.dxnn; prefer the
|
|
91
|
+
# model-stem-named artifact if dxcom ever emits more than one.
|
|
92
|
+
canonical = next((p for p in artifacts if p.stem == source.stem), artifacts[0])
|
|
93
|
+
return Blob(format="dxnn", payload=canonical.read_bytes(),
|
|
94
|
+
compiler_version=_dxcom_version())
|