From 786633f6513cebf7b5e6eeaba7d6c7fedfd7d7a4 Mon Sep 17 00:00:00 2001 From: Usamah Zaheer Date: Fri, 6 Nov 2026 15:49:18 +0000 Subject: [PATCH] Add draft Silero VAD learning path for Alif E8 Authored with assistance from OpenAI Codex. --- .../deploy-silero-vad-on-alif-e8/_index.md | 64 +++++++++ .../_next-steps.md | 8 ++ .../build-the-firmware.md | 85 ++++++++++++ .../export-the-streaming-model.md | 99 ++++++++++++++ .../prepare-the-environment.md | 123 ++++++++++++++++++ .../program-the-board.md | 115 ++++++++++++++++ .../silero-alif-e8.zip | Bin 0 -> 14285 bytes .../troubleshoot.md | 61 +++++++++ .../understand-the-system.md | 56 ++++++++ .../validate-speech-probabilities.md | 92 +++++++++++++ 10 files changed, 703 insertions(+) create mode 100644 content/learning-paths/embedded-and-microcontrollers/deploy-silero-vad-on-alif-e8/_index.md create mode 100644 content/learning-paths/embedded-and-microcontrollers/deploy-silero-vad-on-alif-e8/_next-steps.md create mode 100644 content/learning-paths/embedded-and-microcontrollers/deploy-silero-vad-on-alif-e8/build-the-firmware.md create mode 100644 content/learning-paths/embedded-and-microcontrollers/deploy-silero-vad-on-alif-e8/export-the-streaming-model.md create mode 100644 content/learning-paths/embedded-and-microcontrollers/deploy-silero-vad-on-alif-e8/prepare-the-environment.md create mode 100644 content/learning-paths/embedded-and-microcontrollers/deploy-silero-vad-on-alif-e8/program-the-board.md create mode 100644 content/learning-paths/embedded-and-microcontrollers/deploy-silero-vad-on-alif-e8/silero-alif-e8.zip create mode 100644 content/learning-paths/embedded-and-microcontrollers/deploy-silero-vad-on-alif-e8/troubleshoot.md create mode 100644 content/learning-paths/embedded-and-microcontrollers/deploy-silero-vad-on-alif-e8/understand-the-system.md create mode 100644 content/learning-paths/embedded-and-microcontrollers/deploy-silero-vad-on-alif-e8/validate-speech-probabilities.md diff --git a/content/learning-paths/embedded-and-microcontrollers/deploy-silero-vad-on-alif-e8/_index.md b/content/learning-paths/embedded-and-microcontrollers/deploy-silero-vad-on-alif-e8/_index.md new file mode 100644 index 0000000000..49b1635d5e --- /dev/null +++ b/content/learning-paths/embedded-and-microcontrollers/deploy-silero-vad-on-alif-e8/_index.md @@ -0,0 +1,64 @@ +--- +title: Deploy Silero VAD on Alif Ensemble E8 with ExecuTorch +description: Export a stateful Silero voice activity detector, build its Zephyr firmware for the Alif E8, and compare recorded-audio probabilities with a host reference. +minutes_to_complete: 120 +draft: true +cascade: + draft: true +who_is_this_for: This Learning Path is for embedded ML developers porting streaming audio inference to the Cortex-M55 HP core and Ethos-U85-256 on an Alif Ensemble E8. +learning_objectives: + - Explain the waveform context and recurrent state retained between Silero VAD invocations + - Export a quantized Ethos-U85 program and matching speech-probability reference using released Python packages + - Build E8 Zephyr firmware with the model, recorded audio, and required CPU operators + - Program the E8 and compare all 79 frame probabilities with the host reference +prerequisites: + - An [Alif Ensemble E8 Development Kit](https://alifsemi.com/support/kits/ensemble-e8devkit/) and a data-capable USB cable + - An Apple silicon Mac with Python 3.12, Git, CMake, Ninja, ripgrep, and a C++ build toolchain + - Access to the E8 application UART4 console through PRG USB and the board’s SW4 selector; no separate USB-to-UART adapter is required + - Zephyr SDK 0.17.0, Alif Security Toolkit for your silicon revision, and SEGGER J-Link + - Experience with PyTorch model export, C++, and Zephyr builds +author: Usamah Zaheer +generate_summary_faq: false +rerun_summary: false +rerun_faqs: false +skilllevels: Advanced +subjects: ML +armips: + - Ethos-U +tools_software_languages: + - ExecuTorch + - PyTorch + - Zephyr + - Python + - CMake +operatingsystems: + - macOS + - RTOS +further_reading: + - resource: + title: Deploy Silero VAD on Arm Ethos-U with ExecuTorch + link: /learning-paths/embedded-and-microcontrollers/silero-vad-ethos-u/ + type: documentation + - resource: + title: Set up the Alif Ensemble E8 board + link: /learning-paths/embedded-and-microcontrollers/alif-image-classification/board-setup/ + type: documentation + - resource: + title: Alif Ensemble E8 DevKit support + link: https://alifsemi.com/support/kits/ensemble-e8devkit/ + type: website + - resource: + title: ExecuTorch Silero VAD export and runtime source + link: https://github.com/pytorch/executorch/tree/12dac4aa042b66a7229e8d7d3af2bdde8c9f262f/examples/arm/silero_vad_example_ethos_u + type: repository + - resource: + title: Silero VAD project + link: https://github.com/snakers4/silero-vad + type: repository + +### FIXED, DO NOT MODIFY +# ================================================================================ +weight: 1 +layout: learningpathall +learning_path_main_page: "yes" +--- diff --git a/content/learning-paths/embedded-and-microcontrollers/deploy-silero-vad-on-alif-e8/_next-steps.md b/content/learning-paths/embedded-and-microcontrollers/deploy-silero-vad-on-alif-e8/_next-steps.md new file mode 100644 index 0000000000..727b395ddd --- /dev/null +++ b/content/learning-paths/embedded-and-microcontrollers/deploy-silero-vad-on-alif-e8/_next-steps.md @@ -0,0 +1,8 @@ +--- +# ================================================================================ +# FIXED, DO NOT MODIFY THIS FILE +# ================================================================================ +weight: 21 # The weight controls the order of the pages. _index.md always has weight 1. +title: "Next Steps" # Always the same, html page title. +layout: "learningpathall" # All files under learning paths have this same wrapper for Hugo processing. +--- diff --git a/content/learning-paths/embedded-and-microcontrollers/deploy-silero-vad-on-alif-e8/build-the-firmware.md b/content/learning-paths/embedded-and-microcontrollers/deploy-silero-vad-on-alif-e8/build-the-firmware.md new file mode 100644 index 0000000000..2172099ee8 --- /dev/null +++ b/content/learning-paths/embedded-and-microcontrollers/deploy-silero-vad-on-alif-e8/build-the-firmware.md @@ -0,0 +1,85 @@ +--- +title: Build the E8 Zephyr firmware +description: Embed the exported model and recording, link the CPU operators and Ethos-U delegate, and inspect the HP firmware memory use. +weight: 5 +layout: learningpathall +--- + +## Inspect the application configuration + +The application in `zephyr/samples/silero-vad-ethosu` runs once over the embedded recording. It loads `forward` once and preserves that method's recurrent state for every window. Resetting the board starts an independent stream. + +The board-specific files select the HP Ethos-U85-256, enable its clock, and assign application SRAM beginning at `0x020C0000` with size `0x740000` bytes. This is a 7.25 MiB application region for the pinned E8 memory map. It is not portable to another board or a concurrently running application that owns the same SRAM. + +The starting allocation capacities are: + +| Allocation | Capacity or source | +| --- | --- | +| Method and planned buffers | 2 MiB static allocator pool | +| Temporary delegate allocations | 1 MiB static allocator pool | +| PTE copy | Exported PTE size, aligned to 16 bytes in SRAM | +| C/C++ heap | 64 KiB | +| Main stack | 16 KiB | +| Audio | Up to 40,000 normalized float32 samples generated from the PCM16 WAV | + +These are configured capacities, not measurements of model demand. The generated audio array occupies 160,000 bytes for this clip. The PTE and audio begin in the firmware image; startup copies the PTE once to SRAM for NPU access. The linker report checks the static allocation footprint. A completed board run is still needed to verify allocator demand and NPU access. + +## Build for the HP core + +Keep the released Python environment active. Build with absolute paths to the exact PTE and WAV from the previous lesson: + +```bash +cd "$EXECUTORCH_ROOT" +west build \ + -b alif_e8_dk/ae822fa0e5597xx0/rtss_hp \ + zephyr/samples/silero-vad-ethosu \ + -d "$SILERO_BUILD" -- \ + -DZEPHYR_EXTRA_MODULES="$EXECUTORCH_ROOT;$SDK_ALIF" \ + -DPython3_EXECUTABLE="$(command -v python)" \ + -DET_PTE_FILE_PATH="$SILERO_OUTPUT/silero_vad_ethos_u.pte" \ + -DAUDIO_PATH="$SILERO_OUTPUT/validation.wav" +``` + +The CMake application generates the model/audio headers and selects portable CPU operators from the PTE. It links those registrations, the quantized kernels, and the Ethos-U backend into the firmware. The app also rejects inputs that do not match the single float32 `[1,576]` audio tensor and single float32 probability output. + +If you change the board, PTE, WAV, or allocation configuration, rebuild in a new directory or use `west build -p always` with the complete command. This avoids keeping headers or operator selections from an earlier model. + +## Check the firmware and memory placement + +Require the ELF and binary, then inspect the generated target and allocator configuration: + +```bash +test -s "$SILERO_BUILD/zephyr/zephyr.elf" +test -s "$SILERO_BUILD/zephyr/zephyr.bin" +rg 'CONFIG_(RTSS_HP|ARM_ETHOS_U85_256|EXECUTORCH_.*ALLOCATOR_POOL_SIZE)=' \ + "$SILERO_BUILD/zephyr/.config" +rg -n 'app_sram|020c0000|20c0000|ethosu' \ + "$SILERO_BUILD/zephyr/zephyr.dts" +"$ZEPHYR_SDK_INSTALL_DIR/arm-zephyr-eabi/bin/arm-zephyr-eabi-size" \ + "$SILERO_BUILD/zephyr/zephyr.elf" +``` + +Require the HP target and `CONFIG_ARM_ETHOS_U85_256=y`. Keep the memory-usage report printed by the linker and inspect `zephyr.map` if a region is close to its limit. The `.bss` includes allocator capacities and the PTE copy; unused space inside a pool is still reserved in the linked firmware. + +Successful linking proves that the firmware fits the configured static regions. It does not prove that `load_method()` or the largest delegate allocation fits at runtime. The application prints its planned buffer sizes during startup and reports allocation or execution failures; retain those messages during validation. + +The local cross-build with the pinned source, vendor modules, Zephyr SDK 0.17.0, and released Python environment passed. Its linker report is: + +| Region | Used | Available | Utilization | +| --- | --- | --- | --- | +| Flash application partition | 830,084 bytes | 3,584 KiB | 22.62% | +| Application RAM | 3,597,440 bytes | 7,424 KiB | 47.32% | + +The binary is linked at `0x80200000`, including the vendor flash load offset. These are cross-build results, not measurements from a running E8. Physical execution and probability agreement remain to be checked. + +Record the firmware identity alongside the model inputs: + +```bash +shasum -a 256 "$SILERO_BUILD/zephyr/zephyr.elf" \ + "$SILERO_BUILD/zephyr/zephyr.bin" \ + > "$SILERO_OUTPUT/firmware.sha256" +``` + +## What you've accomplished + +You have built one firmware image containing the recorded clip, exported program, CPU kernels, and U85 delegate, and checked its configured memory placement. Next, program this matching image onto the E8 HP core. diff --git a/content/learning-paths/embedded-and-microcontrollers/deploy-silero-vad-on-alif-e8/export-the-streaming-model.md b/content/learning-paths/embedded-and-microcontrollers/deploy-silero-vad-on-alif-e8/export-the-streaming-model.md new file mode 100644 index 0000000000..84f430eccf --- /dev/null +++ b/content/learning-paths/embedded-and-microcontrollers/deploy-silero-vad-on-alif-e8/export-the-streaming-model.md @@ -0,0 +1,99 @@ +--- +title: Export the streaming VAD model +description: Quantize Silero VAD for Ethos-U85 and generate the recorded-audio reference used to check the E8 output. +weight: 4 +layout: learningpathall +--- + +## Download the model and calibration audio + +Use the model and recording from one pinned Silero VAD revision. Keep the upstream project's license with any redistributed model or audio. The application archive contains neither file. + +Download both inputs into your output directory: + +```bash +cd "$EXECUTORCH_ROOT" +curl --fail --location \ + --output "$SILERO_OUTPUT/silero_vad.jit" \ + https://raw.githubusercontent.com/snakers4/silero-vad/dbacf536adadf42210f37ae50fbaf75f6235b3cf/src/silero_vad/data/silero_vad.jit +curl --fail --location \ + --output "$SILERO_OUTPUT/test.wav" \ + https://raw.githubusercontent.com/snakers4/silero-vad/dbacf536adadf42210f37ae50fbaf75f6235b3cf/tests/data/test.wav +shasum -a 256 "$SILERO_OUTPUT/silero_vad.jit" "$SILERO_OUTPUT/test.wav" +``` + +The expected hashes are: + +| File | SHA256 | +| --- | --- | +| `silero_vad.jit` | `e1122837f4154c511485fe0b9c64455f7b929c96fbb8d79fbdb336383ebd3720` | +| `test.wav` | `89f17d9c94c4b31eb320f424628bcbc920abaddbee6e2760fd868bfb1d9a2e47` | + +Trim the first 2.5 seconds for a short firmware validation workload: + +```bash +python examples/arm/silero_vad_example_ethos_u/model_export/generate_smoke_audio.py \ + --source-wav "$SILERO_OUTPUT/test.wav" \ + --output "$SILERO_OUTPUT/validation.wav" +``` + +This produces 40,000 samples. The file remains mono 16-bit PCM at 16 kHz. Using a real speech recording exercises speech and silence probabilities; the helper's optional synthetic tone is a different workload. + +## Export the quantized program and reference + +The exporter calibrates int8 activations and weights, estimates the hidden/cell state quantization parameters, and serializes initialized recurrent state. It lowers supported computation to Ethos-U85-256 while retaining CPU operators needed for state updates and tensor conversion. + +Use `test.wav` as the calibration source and the trimmed clip for validation. The exporter caps calibration at 256 windows by default and processes all 79 validation windows. Enable pipeline failure propagation so an export error remains visible even when saving the log: + +```bash +set -o pipefail +python examples/arm/silero_vad_example_ethos_u/model_export/export_silero_vad_ethos_u.py \ + --jit-model "$SILERO_OUTPUT/silero_vad.jit" \ + --calibration-audio "$SILERO_OUTPUT/test.wav" \ + --validation-audio "$SILERO_OUTPUT/validation.wav" \ + --output-path "$SILERO_OUTPUT/silero_vad_ethos_u.pte" \ + --expected-output-path "$SILERO_OUTPUT/expected_probs.bin" \ + 2>&1 | tee "$SILERO_OUTPUT/export.log" +``` + +The compile specification in this pinned example selects `ethos-u85-256`, `Shared_Sram`, and the default `Ethos_U85_SYS_DRAM_Mid` system configuration. The latter is a compiler performance model, not a claim that the E8 has the FVP memory system. The application copies the PTE into aligned SRAM and supplies SRAM for delegated scratch. Keep the exported memory mode and firmware placement together when changing either. + +The export generates two artifacts that must stay paired: + +| Artifact | Purpose | +| --- | --- | +| `silero_vad_ethos_u.pte` | Quantized program, delegated command streams, weights, and initial recurrent state | +| `expected_probs.bin` | Host quantized-graph probabilities for the 79 windows in `validation.wav` | + +The host reference checks agreement with the quantized model. It is not the floating-point model's accuracy against labeled speech. Calibration and validation overlap in this example; use separate labeled recordings for an accuracy study. + +The pinned source and released environment were checked together: export produced a 363,392-byte PTE and 316-byte reference. Both matched the retained FVP-tested release artifacts byte for byte. This checks host export reproducibility; it does not establish physical E8 execution. + +## Inspect the exported operators + +Require both nonempty artifacts, then generate the CPU operator selection that the firmware build will use: + +```bash +test -s "$SILERO_OUTPUT/silero_vad_ethos_u.pte" +test -s "$SILERO_OUTPUT/expected_probs.bin" +python codegen/tools/gen_oplist.py \ + --model_file_path "$SILERO_OUTPUT/silero_vad_ethos_u.pte" \ + --output_path "$SILERO_OUTPUT/selected_operators.yaml" +cat "$SILERO_OUTPUT/selected_operators.yaml" +``` + +CPU operators are expected. A Vela subgraph's zero CPU-operator count describes that compiled subgraph, not the entire ExecuTorch program. Do not apply a fully delegated MobileSAM check to this stateful model. + +Record hashes so you can identify the exact firmware inputs later: + +```bash +shasum -a 256 \ + "$SILERO_OUTPUT/silero_vad_ethos_u.pte" \ + "$SILERO_OUTPUT/expected_probs.bin" \ + "$SILERO_OUTPUT/validation.wav" \ + > "$SILERO_OUTPUT/model-inputs.sha256" +``` + +## What you've accomplished + +You have exported the U85 program, preserved its initialized recurrent state, and generated a 79-window host reference from the same audio. Next, embed the program and recording in E8 firmware with the required CPU and NPU components. diff --git a/content/learning-paths/embedded-and-microcontrollers/deploy-silero-vad-on-alif-e8/prepare-the-environment.md b/content/learning-paths/embedded-and-microcontrollers/deploy-silero-vad-on-alif-e8/prepare-the-environment.md new file mode 100644 index 0000000000..6c5c71b9e1 --- /dev/null +++ b/content/learning-paths/embedded-and-microcontrollers/deploy-silero-vad-on-alif-e8/prepare-the-environment.md @@ -0,0 +1,123 @@ +--- +title: Prepare ExecuTorch and the E8 tools +description: Pin the source and vendor SDKs, install released export dependencies, and prepare the board connection. +weight: 3 +layout: learningpathall +--- + +## Select the host and workspace + +Use an Apple silicon Mac with Python 3.12, CMake, Ninja, Git, and ripgrep installed. Install [Zephyr SDK 0.17.0](https://github.com/zephyrproject-rtos/sdk-ng/releases/tag/v0.17.0) for macOS on Arm, including the Arm toolchain. Set its location below. + +The upstream Silero example supplies the exporter and audio helpers. The downloadable application adds the E8 Zephyr target. It is a local port, so use the exact source and vendor revisions in this lesson when reproducing it. + +Choose a new workspace and define the paths used throughout this Learning Path: + +```bash +export ALIF_WORK="$HOME/silero-alif-e8" +export EXECUTORCH_ROOT="$ALIF_WORK/executorch" +export ZEPHYR_BASE="$ALIF_WORK/zephyr_alif" +export SDK_ALIF="$ALIF_WORK/sdk-alif" +export SILERO_OUTPUT="$ALIF_WORK/model-and-builds" +export SILERO_BUILD="$SILERO_OUTPUT/build-alif" +export ZEPHYR_SDK_INSTALL_DIR=/absolute/path/to/zephyr-sdk-0.17.0 +export ZEPHYR_TOOLCHAIN_VARIANT=zephyr +mkdir -p "$ALIF_WORK" "$SILERO_OUTPUT" +``` + +## Get the model source and E8 application + +Clone the pinned ExecuTorch source, including its build dependencies: + +```bash +git clone https://github.com/pytorch/executorch.git "$EXECUTORCH_ROOT" +git -C "$EXECUTORCH_ROOT" checkout --detach \ + 12dac4aa042b66a7229e8d7d3af2bdde8c9f262f +git -C "$EXECUTORCH_ROOT" submodule update --init --recursive +``` + +Download the [E8 Silero application](../silero-alif-e8.zip) and save it as `$ALIF_WORK/silero-alif-e8.zip`. The archive contains source code for `zephyr/samples/silero-vad-ethosu`, its license notices, and a README. It contains no model weights or audio. + +Check the archive hash, then extract it into the new checkout: + +```bash +shasum -a 256 "$ALIF_WORK/silero-alif-e8.zip" +unzip "$ALIF_WORK/silero-alif-e8.zip" -d "$EXECUTORCH_ROOT" +test -f "$EXECUTORCH_ROOT/zephyr/samples/silero-vad-ethosu/src/main.cpp" +``` + +The expected SHA256 is `7c671b6665bdf11d087cc4574fd8def27a75b2c5f8ec3b3db0a7ef83eac32f78`. + +## Pin the vendor board support + +This application uses Alif's Zephyr fork and SDK. The board name and NPU configuration below belong to these revisions: + +```bash +git clone https://github.com/alifsemi/zephyr_alif.git "$ZEPHYR_BASE" +git -C "$ZEPHYR_BASE" checkout --detach \ + f002a4d8499c35fc70ec4d5324faa7b01c152c72 +git clone https://github.com/alifsemi/sdk-alif.git "$SDK_ALIF" +git -C "$SDK_ALIF" checkout --detach \ + 1db82c9d0cc594e2b548a13699e8a4157e6dcd4c +``` + +The selected target is `alif_e8_dk/ae822fa0e5597xx0/rtss_hp`: the Cortex-M55 HP core with an Ethos-U85-256. Do not substitute the HE target or an older sample's U55 configuration. + +## Install released Python dependencies + +Create an isolated environment for export and firmware code generation: + +```bash +python3.12 -m venv "$ALIF_WORK/.venv" +source "$ALIF_WORK/.venv/bin/activate" +python -m pip install "torch==2.14.0" \ + --index-url https://download.pytorch.org/whl/cpu +python -m pip install "executorch[ethos_u]==1.5.1" \ + "torchao==0.18.0" "cmake<4" "west==1.5.0" fdt pytest +python -m pip install -r "$ZEPHYR_BASE/scripts/requirements.txt" +``` + +These are released packages. The wheel supplies host export and code-generation components; the pinned source supplies the example scripts and the C++ runtime built for Zephyr. Keep this environment active for both operations. The source revision and wheel version are separate pins, so retain the model/firmware validation results for this combination. + +The `ethos_u` extra installs Vela and TOSA tooling. This board workflow uses the Zephyr SDK compiler and does not require an FVP installation. Do not run the source tree's general nightly dependency installer over this environment. + +## Initialize the vendor west workspace + +Initialize west once in the new workspace, resolve the vendor modules, and save the resolved versions: + +```bash +cd "$ALIF_WORK" +west init -l "$ZEPHYR_BASE" +west update +west zephyr-export +west manifest --freeze > "$SILERO_OUTPUT/west-manifest.yml" +python -m pip freeze > "$SILERO_OUTPUT/python-requirements.txt" +cd "$EXECUTORCH_ROOT" +``` + +If resuming an existing workspace, verify its manifest instead of initializing it again. The pinned vendor manifest includes moving module references. Save the frozen manifest above with each build; the Zephyr and SDK commit pins alone do not lock every west project. + +## Prepare the board tools + +Install Alif Security Toolkit for your E8 silicon revision and SEGGER J-Link. Follow the Security Toolkit and debug-probe portions of the [E8 board setup guide](https://learn.arm.com/learning-paths/embedded-and-microcontrollers/alif-image-classification/board-setup/). Its CMSIS application build is a separate example. + +Connect the board's **PRG USB** port with a data-capable cable, retain the vendor jumper settings, and leave **SW4 on SEUART** for programming. Confirm that the host discovers the serial port and J-Link probe. Close applications holding the programming serial port. + +The E8 DevKit has an onboard USB-to-UART bridge. SW4 routes it to SEUART for programming or UART4 for the HP application console at 115200 baud, 8N1. No separate adapter is required. The programming lesson shows the single switch change to UART4 after flashing and the return to SEUART before any later programming. + +Set the installed tool locations and HP device: + +```bash +export ALIF_SE_TOOLS_DIR=/absolute/path/to/alif-security-toolkit +export JLINK_COMMANDER=/absolute/path/to/JLinkExe +export ALIF_DEVICE=AE822FA0E5597LS0_M55_HP +test -d "$ALIF_SE_TOOLS_DIR/build/config" +test -x "$JLINK_COMMANDER" +command -v west +``` + +Match the toolkit's A0/A1 configuration to your actual board. The device above selects the HP core; it does not detect the silicon revision for you. + +## What you've accomplished + +You have selected the source, E8 vendor revisions, released Python packages, and board tools. Next, generate the quantized streaming model and its matching probability reference. diff --git a/content/learning-paths/embedded-and-microcontrollers/deploy-silero-vad-on-alif-e8/program-the-board.md b/content/learning-paths/embedded-and-microcontrollers/deploy-silero-vad-on-alif-e8/program-the-board.md new file mode 100644 index 0000000000..1ab9342226 --- /dev/null +++ b/content/learning-paths/embedded-and-microcontrollers/deploy-silero-vad-on-alif-e8/program-the-board.md @@ -0,0 +1,115 @@ +--- +title: Program the Silero firmware on the E8 HP core +description: Generate an application image configuration and program the firmware, embedded model, and audio into E8 MRAM. +weight: 6 +layout: learningpathall +--- + +## Check the firmware and programming connection + +The firmware contains both the PTE and validation audio. Program the single `zephyr.bin` produced by the previous lesson into MRAM. Rebuild and reprogram that firmware whenever you change the model or embedded recording. + +Connect the board's **PRG USB** port with a data-capable cable. For programming, leave **SW4 on SEUART** and close any terminal using the Security Toolkit's serial port. Use the Security Toolkit configured for your E8 silicon revision, with the device configuration and boot images already established in the setup lesson. + +Check the paths from your active shell: + +```bash +test -s "$SILERO_BUILD/zephyr/zephyr.bin" +test -s "$SILERO_BUILD/zephyr/zephyr.elf" +test -x "$ALIF_SE_TOOLS_DIR/app-gen-toc" +test -x "$ALIF_SE_TOOLS_DIR/app-write-mram" +``` + +The pinned HP board configuration places the Zephyr application at **`0x80200000`**: MRAM base `0x80000000` plus flash load offset `0x200000`. This is the Zephyr HP partition used here. The address must agree with your generated build configuration and linker layout. + +## Preserve the existing boot configuration + +The Security Toolkit generates an application table of contents (AToC) from a JSON configuration. Start with the configuration already used by your working E8 boot setup. Keep its device configuration, AP images, HE images, signing options, and clock settings. This lesson changes only the HP application entry. + +The standard Zephyr runner uses `build/config/app-cpu-stubs.json` as its template. Use that file only after confirming that its device configuration and companion images match your E8. If your working setup uses a different application configuration, set `ALIF_APP_CONFIG` to that file instead. + +Save a copy of the toolkit configuration and stage the new binary under a distinct name: + +```bash +export ALIF_APP_CONFIG="$ALIF_SE_TOOLS_DIR/build/config/app-cpu-stubs.json" + +test -s "$ALIF_APP_CONFIG" +tar -czf "$SILERO_OUTPUT/setools-config-before-silero.tar.gz" \ + -C "$ALIF_SE_TOOLS_DIR" build/config +cp "$SILERO_BUILD/zephyr/zephyr.bin" \ + "$ALIF_SE_TOOLS_DIR/build/images/silero-vad-hp.bin" +``` + +The next command reads that template, checks the build's HP flash address, and writes a separate application configuration. It leaves the original template unchanged: + +```bash +python - "$SILERO_BUILD/zephyr/.config" "$ALIF_APP_CONFIG" \ + "$SILERO_OUTPUT/silero-vad-hp.json" <<'PY' +from pathlib import Path +import json +import sys + +build_config, template, destination = map(Path, sys.argv[1:]) +values = dict( + line.split("=", 1) + for line in build_config.read_text().splitlines() + if line.startswith("CONFIG_") and "=" in line +) +assert values.get("CONFIG_RTSS_HP") == "y", "Build must target the HP core" +address = int(values["CONFIG_FLASH_BASE_ADDRESS"], 0) + int( + values["CONFIG_FLASH_LOAD_OFFSET"], 0 +) +assert address == 0x80200000, f"Unexpected HP flash address: {address:#x}" + +configuration = json.loads(template.read_text()) +hp = configuration["HP_APP"] +assert hp["cpu_id"] == "M55_HP", "Template must contain the HP application" +hp["disabled"] = False +hp["binary"] = "silero-vad-hp.bin" +hp["mramAddress"] = hex(address) +hp["flags"] = ["boot"] +hp.pop("loadAddress", None) +destination.write_text(json.dumps(configuration, indent=4) + "\n") +print(json.dumps(hp, indent=2)) +PY +``` + +Review the printed HP entry. It must select `M55_HP`, name `silero-vad-hp.bin`, use `mramAddress` of `0x80200000`, and contain the `boot` flag. MRAM execution uses `mramAddress`; the template's SRAM `loadAddress` and `load` flag must be absent from this entry. + +Review the complete new JSON before generating images. Its other entries should match the known working template, and every referenced binary must remain available in the toolkit's `build/images` directory. Keep the existing Security Enclave table of contents (SETOC) and device clock configuration; this application update does not require replacing them. + +## Generate and program the application image + +Run the toolkit from its installation directory so it finds its configuration and image files: + +```bash +cd "$ALIF_SE_TOOLS_DIR" +./app-gen-toc -f "$SILERO_OUTPUT/silero-vad-hp.json" +./app-write-mram -p +cd "$EXECUTORCH_ROOT" +``` + +Select the E8's SEUART programming port when prompted. Require successful image generation and MRAM programming with no device-revision, image, authentication, or serial-port errors. Programming success establishes that the image was transferred; the next lesson checks model execution and output agreement. + +## Capture the application console + +The pinned E8 HP device tree selects **UART4 at 115200 baud, 8N1** for the Zephyr console. The E8 DevKit routes its onboard USB-to-UART bridge between SEUART, UART2, and UART4 using SW4. It uses the same PRG USB connection for programming and application logging; no separate serial adapter is needed. + +After programming completes, disconnect board power, move **SW4 from SEUART to UART4**, and reconnect PRG USB. Rediscover the host serial device, then set `ALIF_CONSOLE` to that device. PySerial is installed by the Zephyr requirements; use its terminal to capture one run: + +```bash +python -m serial.tools.list_ports +export ALIF_CONSOLE=/dev/cu.replace-with-your-application-uart +python -u -m serial.tools.miniterm "$ALIF_CONSOLE" 115200 --raw \ + 2>&1 | tee "$SILERO_OUTPUT/alif-silero.log" +``` + +Start capture first, then press the board’s **RESET** button to begin a fresh run. Wait for all probability lines and `Model executed successfully.` before exiting the terminal with **Ctrl+]**. Keep one run per log file. If the application reports an error or the console remains silent, preserve that log and diagnose the failed stage before retrying the probability comparison. + +Before any later programming operation, close the terminal, disconnect board power, return **SW4 to SEUART**, and reconnect PRG USB. SETOOLS cannot communicate with the Secure Enclave while the bridge is routed to UART4. + +## What you've accomplished + +After completing these steps, you have programmed the HP firmware with its embedded model and audio while retaining the E8's other boot-image and device settings. You have also captured a complete application run for validation. + +Next, compare its 79 speech probabilities with the matching quantized host reference. diff --git a/content/learning-paths/embedded-and-microcontrollers/deploy-silero-vad-on-alif-e8/silero-alif-e8.zip b/content/learning-paths/embedded-and-microcontrollers/deploy-silero-vad-on-alif-e8/silero-alif-e8.zip new file mode 100644 index 0000000000000000000000000000000000000000..3b89bf75af6f8cb4ba17759f1f054c0eea71dab4 GIT binary patch literal 14285 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