Skip to content

Commit a09ffed

Browse files
committed
Explain ExecuTorch in the ESP-IDF component's README
The component's README is its page on the ESP Component Registry, and it opened with how the tree is generated and versioned, which tells a newcomer nothing about what ExecuTorch is or where to start. It now introduces ExecuTorch and how a model goes from torch.export to a .pte that the runtime runs from flash, lists what the component carries, and gives the command that creates the hello_executorch project from the registry. The usage notes become steps in the order a project takes them, with an export example for the ESP-NN delegate, run against the current package, and the et_model example from its header. How the tree is generated and versioned moves to the end. Authored with Claude Code.
1 parent b580243 commit a09ffed

1 file changed

Lines changed: 170 additions & 48 deletions

File tree

  • examples/espressif/idf_component_template

‎examples/espressif/idf_component_template/README.md‎

Lines changed: 170 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,72 @@ This source code is licensed under the BSD-style license found in the
66
LICENSE file in the root directory of this source tree.
77
-->
88

9-
# ExecuTorch for ESP-IDF (`executorch` component)
9+
# ExecuTorch for ESP-IDF
1010

11-
**This tree is generated. Do not edit it by hand.** It is produced by
12-
`examples/espressif/build_idf_component.sh` in the
13-
[ExecuTorch](https://github.com/pytorch/executorch) repository.
11+
[ExecuTorch](https://github.com/pytorch/executorch) is PyTorch's open
12+
source stack for running AI locally on phones, wearables, laptops,
13+
browsers, embedded systems and microcontrollers. This component brings
14+
its runtime to ESP-IDF 6.0 and later, for any ESP target, as source that
15+
builds with your project.
16+
17+
## How ExecuTorch works
18+
19+
A model is prepared ahead of time, on your computer, with the
20+
`executorch` Python package. You export it with `torch.export`,
21+
optionally quantize it, and lower it to a `.pte` program. Lowering can
22+
hand the parts of the model that a backend supports to that backend's
23+
delegate, and it plans where the model's intermediate tensors live in
24+
memory, so the device keeps them in an arena whose size is known before
25+
it starts.
26+
27+
On the device, the runtime reads the `.pte` in place from flash and runs
28+
it: each operator with a kernel from a kernel library, and each
29+
delegated part with its backend.
30+
31+
[How ExecuTorch works](https://docs.pytorch.org/executorch/stable/intro-how-it-works.html)
32+
and [Model export and lowering](https://docs.pytorch.org/executorch/stable/using-executorch-export.html)
33+
cover this in more depth.
1434

15-
`VERSION` in this directory records the ExecuTorch commit, the generator's
16-
options, the kernel libraries the component ships and the torch to
17-
install with executorch. `examples/hello_executorch` is a complete
18-
project that runs a small model with its kernel bundle; its README says
19-
how they were made.
35+
## What the component provides
2036

21-
## Using it
37+
- The ExecuTorch runtime, and `et_model.h`, a small C API that runs a
38+
`.pte` from flash.
39+
- Kernel libraries: portable (reference kernels for the core ATen
40+
operators), quantized, and the Cadence backend's generic kernels.
41+
- The ESP-NN delegate, `EspNNBackend`, which runs the int8 parts of a
42+
model lowered for it with Espressif's
43+
[ESP-NN](https://github.com/espressif/esp-nn) kernels. ESP-NN has SIMD
44+
kernels for the ESP32-S3 and portable C kernels for the other chips.
45+
The delegate is a prototype under active development.
46+
- `tools/gen_kernels.py`, which makes the kernel bundle that registers
47+
the kernels your models use.
48+
- `examples/hello_executorch`, a complete project, in every published
49+
version.
50+
51+
## Try it
52+
53+
`hello_executorch` runs a small model and checks its outputs. Its `.pte`
54+
and kernel bundle are already made, so it needs only ESP-IDF:
55+
56+
```sh
57+
idf.py create-project-from-example "pytorch/executorch==@ET_COMPONENT_VERSION@:hello_executorch"
58+
cd hello_executorch
59+
idf.py set-target esp32s3
60+
idf.py build flash monitor
61+
```
62+
63+
It prints `ET_COMPONENT_SMOKE: PASS` when every output matches. A
64+
release candidate is on the staging registry only, so for one, first
65+
`export IDF_COMPONENT_REGISTRY_URL=https://components-staging.espressif.com`
66+
in the shell, since the build resolves the version again. In a copy of
67+
this component you generated yourself, build `examples/hello_executorch`
68+
in place instead. The generator leaves it out only of an
69+
`--allow-dev-stamp` copy made without the executorch package built from
70+
that commit.
71+
72+
## Using it in your project
73+
74+
### Add the dependency
2275

2376
Depend on one exact version and commit `dependencies.lock`:
2477

@@ -27,47 +80,75 @@ dependencies:
2780
pytorch/executorch: "==@ET_COMPONENT_VERSION@"
2881
```
2982
30-
The registry's dev versions, `X.Y.Z-dev.YYYYMMDD`, come from the
31-
`nightly` branch. A copy you generate yourself is not published; use it
32-
from your project's `components/` or through `override_path`.
83+
A release candidate's dependency also needs
84+
`registry_url: https://components-staging.espressif.com`.
3385

3486
A kernel bundle is made for one component version, a `~N` respin
3587
included, and the build refuses a bundle made for another version, or
3688
for the same version from another commit. With a range, the build
3789
breaks once the lock is re-solved to a newer version (for example by
3890
`idf.py update-dependencies`), until the bundle is regenerated.
3991

40-
The component carries the runtime, every kernel of its kernel
41-
libraries (portable, quantized, the Cadence backend's generic kernels
42-
and the `esp_nn::` op library) and the ESP-NN delegate
43-
(`EspNNBackend`), but registers only the kernels your models use and
44-
links the delegate only when one of them uses it. It depends on
45-
`espressif/esp-nn`, which the component manager fetches from the
46-
production registry, components.espressif.com, even for a project that
47-
names another registry; a project that builds without the component
48-
manager provides it as `components/esp-nn`. The kernels come
49-
from a kernel bundle that `tools/gen_kernels.py` makes from your
50-
`.pte` files. Run it with the Python environment you exported the
51-
models with, after every export, ExecuTorch upgrade or update of this
52-
component, and keep the bundle in your project:
92+
The component depends on `espressif/esp-nn`, which the component manager
93+
fetches from the production registry, components.espressif.com, even
94+
for a project that names another registry. A project that builds
95+
without the component manager provides it as `components/esp-nn`.
96+
97+
### Set up Python
98+
99+
Exporting models and making kernel bundles need the executorch this
100+
component was built from, and the torch that `VERSION` names as
101+
`torch_requirement`, from its `torch_index`, since ExecuTorch's wheels
102+
declare no torch:
53103

54104
```sh
55-
python <this directory>/tools/gen_kernels.py --pte main/model.pte --out main/et_kernels
105+
@ET_INSTALL@
56106
```
57107

58-
The Python environment you export models and run `gen_kernels.py` with
59-
needs the executorch this component was built from, and the torch that
60-
`VERSION` names as `torch_requirement`, from its `torch_index`, since
61-
ExecuTorch's wheels declare no torch:
108+
### Export a model
109+
110+
To run a model with ESP-NN, quantize it with `EspNNQuantizer` and lower
111+
it with `to_espnn_edge`, which delegates the int8 regions ESP-NN
112+
supports and leaves the rest to the CPU kernels:
113+
114+
```python
115+
import torch
116+
from executorch.backends.espressif import EspNNQuantizer, to_espnn_edge
117+
from torchao.quantization.pt2e.quantize_pt2e import convert_pt2e, prepare_pt2e
118+
119+
model = MyModel().eval() # your torch.nn.Module
120+
example_inputs = (torch.randn(1, 3, 96, 96),)
121+
122+
prepared = prepare_pt2e(
123+
torch.export.export(model, example_inputs).module(), EspNNQuantizer()
124+
)
125+
# Tuples of representative inputs, from which the int8 scales are set.
126+
for inputs in calibration_inputs:
127+
prepared(*inputs)
128+
quantized = torch.export.export(convert_pt2e(prepared), example_inputs)
129+
edge, report = to_espnn_edge(quantized)
130+
print(report["cpu_ops"]) # the operators left to the CPU kernels
131+
with open("main/model.pte", "wb") as f:
132+
f.write(edge.to_executorch().buffer)
133+
```
134+
135+
### Make the kernel bundle
136+
137+
`tools/gen_kernels.py` makes the kernel bundle from your `.pte` files.
138+
Run it with the Python environment you exported the models with, after
139+
every export, ExecuTorch upgrade or update of this component, and keep
140+
the bundle in your project:
62141

63142
```sh
64-
@ET_INSTALL@
143+
python <this directory>/tools/gen_kernels.py --pte main/model.pte --out main/et_kernels
65144
```
66145

67-
Then declare the models and the bundle, from a component's
68-
`CMakeLists.txt` after its `idf_component_register()` or from the
69-
project's after `project()`, and add `executorch` to the requirements
70-
of the components that use it:
146+
### Declare the models and the bundle
147+
148+
From a component's `CMakeLists.txt` after its `idf_component_register()`,
149+
or from the project's after `project()`, declare the models and the
150+
bundle, and add `executorch` to the requirements of the components that
151+
use it:
71152

72153
```cmake
73154
idf_component_register(SRCS "main.c" PRIV_REQUIRES executorch)
@@ -102,18 +183,43 @@ With a bundle that registers kernels, the build compiles every vendored
102183
kernel source, and the link keeps only the ones the bundle registers.
103184
The extra kernels cost clean-build time, not flash.
104185

105-
- `#include <et_model.h>` for the small C inference API (flash XIP
106-
model bytes; method, planned and optional scratch arenas either
107-
passed in or allocated with heap_caps internal/PSRAM placement). This
108-
is what most applications should use. The `.pte` bytes must be
109-
16-byte aligned; `et_model_create` rejects misaligned data. Application pipelines built on it (audio
110-
front ends, detectors, wake word logic) live with the application,
111-
not in this runtime component.
112-
- `#include <executorch/...>` for the core C++ runtime
113-
(`Program`/`Method`) and `BufferDataLoader`. `extension::Module`, the
114-
file and mmap data loaders, and external `.ptd` data maps are not
115-
included. Runtime logging goes through ESP_LOG under the
116-
`executorch` tag.
186+
### Run the model
187+
188+
`et_model.h` loads a `.pte` and runs it:
189+
190+
```c
191+
#include <esp_log.h>
192+
#include <et_model.h>
193+
194+
et_model_config_t cfg = ET_MODEL_CONFIG_DEFAULT(model_pte, model_pte_size);
195+
cfg.planned_location = ET_MEM_INTERNAL; // planned tensors in internal SRAM
196+
et_model_t *m = et_model_create(&cfg);
197+
if (!m) {
198+
return; // et_model_create logs why
199+
}
200+
size_t n;
201+
const void *out = NULL;
202+
if (!et_model_load_method(m, NULL) || !et_model_set_input(m, 0, frame, sizeof(frame)) ||
203+
!et_model_execute(m) || !(out = et_model_output_data(m, 0, &n))) {
204+
ESP_LOGE("app", "%s", et_model_last_error(m));
205+
et_model_destroy(m);
206+
return;
207+
}
208+
```
209+
210+
`model_pte` and `model_pte_size` are the `.pte` bytes, read in place,
211+
typically from flash. They must be 16-byte aligned, and
212+
`et_model_create` rejects misaligned data. ESP-IDF's `EMBED_FILES` does
213+
not align what it embeds, so `examples/hello_executorch/main/CMakeLists.txt`
214+
embeds its `.pte` with an assembler `.balign 16` and `.incbin` instead.
215+
The method, planned and optional scratch arenas are either passed in or
216+
allocated with heap_caps in internal RAM or PSRAM. Application pipelines
217+
built on it (audio front ends, detectors, wake word logic) live with the
218+
application, not in this component.
219+
220+
For the core C++ runtime (`Program`/`Method`) and `BufferDataLoader`,
221+
`#include <executorch/...>`. `extension::Module`, the file and mmap data
222+
loaders, and external `.ptd` data maps are not included.
117223

118224
## Size and debug options
119225

@@ -148,3 +254,19 @@ Projects moving from the op profiles of earlier versions remove
148254
`CONFIG_ET_ENABLE` and `CONFIG_ET_OP_PROFILE_*` from their sdkconfig,
149255
which no longer do anything, and declare their models and a kernel
150256
bundle as above.
257+
258+
## About this component
259+
260+
**This tree is generated. Do not edit it by hand.** It is produced by
261+
`examples/espressif/build_idf_component.sh` in the ExecuTorch
262+
repository. Releases are versioned as ExecuTorch's, `X.Y.Z`, with
263+
`X.Y.Z-rc.N` for release candidates and `X.Y.Z~N` for respins of this
264+
component. The registry's dev versions, `X.Y.Z-dev.YYYYMMDD`, come from
265+
the `nightly` branch. A copy you generate yourself is not published; use
266+
it from your project's `components/` or through `override_path`.
267+
268+
`VERSION` records the ExecuTorch commit, the generator's options, the
269+
kernel libraries the component ships and the torch to install with
270+
executorch. The README of `examples/hello_executorch` says how its model
271+
and kernel bundle were made. Vendored third-party code is listed in
272+
`THIRD_PARTY_LICENSES` and the `sbom*.yml` files.

0 commit comments

Comments
 (0)