Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 98 additions & 0 deletions examples/rpi/build_firmware_pico.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
#!/bin/bash
# Copyright (c) Meta Platforms, Inc. and affiliates.
# All rights reserved.
#
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree.

#!/bin/bash
# build_firmware_pico.sh
# Simple script to cross-compile ExecuTorch and build Pico2 firmware with optional model input

set -e
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
set -e
set -eu


# Paths
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)" # examples/rpi/ -> root dir
PICO2_DIR="${ROOT_DIR}/examples/rpi/pico2"
BUILD_DIR="${PICO2_DIR}/build"
EXECUTORCH_BUILD_DIR="${ROOT_DIR}/cmake-out"

# Default model
DEFAULT_MODEL="default_model.pte"

usage() {
echo "Usage: $0 [--clean] [--model=path/to/model.pte]"
echo " --clean Clean build directories"
echo " --model=FILE Specify model file to embed (relative to pico2/)"
exit 1
}

# Parse args
MODEL_INPUT=""
CLEAN_BUILD=0

for arg in "$@"; do
case $arg in
--clean)
CLEAN_BUILD=1
shift
;;
--model=*)
MODEL_INPUT="${arg#*=}"
shift
;;
*)
usage
;;
esac
done

# Clean if requested
if [ $CLEAN_BUILD -eq 1 ]; then
echo "Cleaning build directories..."
rm -rf "${EXECUTORCH_BUILD_DIR}" "${BUILD_DIR}"
echo "Clean complete."
exit 0
fi

# Step 1: Cross compile ExecuTorch from root dir
echo "Cross compiling ExecuTorch baremetal ARM..."

cmake -B "${EXECUTORCH_BUILD_DIR}" \
-DCMAKE_TOOLCHAIN_FILE="${ROOT_DIR}/examples/arm/ethos-u-setup/arm-none-eabi-gcc.cmake" \
-DTARGET_CPU=cortex-m0plus \
-DEXECUTORCH_BUILD_ARM_BAREMETAL=ON \
-DEXECUTORCH_PAL_DEFAULT=minimal \
-DEXECUTORCH_DTYPE_SELECTIVE_BUILD=ON \
-DCMAKE_BUILD_TYPE=MinSizeRel \
-DEXECUTORCH_ENABLE_LOGGING=OFF \
-DEXECUTORCH_SELECT_ALL_OPS=OFF \
-DEXECUTORCH_BUILD_EXECUTOR_RUNNER=OFF \
-DCMAKE_INSTALL_PREFIX="${EXECUTORCH_BUILD_DIR}" \
"${ROOT_DIR}"

cmake --build "${EXECUTORCH_BUILD_DIR}" --target install -j$(nproc)

echo "ExecuTorch cross compile complete."

# Step 2: Build firmware for Pico2 with model input

cd "${PICO2_DIR}"

if [ -n "$MODEL_INPUT" ]; then
# Use specified model
if [ ! -f "${MODEL_INPUT}" ]; then
echo "Error: Specified model file '${MODEL_INPUT}' not found in pico2 directory."
exit 1
fi
echo "Building firmware with model: ${MODEL_INPUT}"
cmake -B "${BUILD_DIR}" -DPICO_BOARD=pico2 -DINPUT_MODEL="./${MODEL_INPUT}" -DCMAKE_BUILD_TYPE=Release
else
# Use default model
echo "Building firmware with default model: ${DEFAULT_MODEL}"
cmake -B "${BUILD_DIR}" -DPICO_BOARD=pico2 -DINPUT_MODEL="./${DEFAULT_MODEL}" -DCMAKE_BUILD_TYPE=Release
fi

cmake --build "${BUILD_DIR}" -j$(nproc)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW nproc won't work on a macos


echo "Firmware build complete. Output in ${BUILD_DIR}, Binary: executorch_pico.uf2"
143 changes: 143 additions & 0 deletions examples/rpi/pico2/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit s/rpi/raspberrypi

# All rights reserved.
#
# This source code is licensed under the BSD-style license found in the LICENSE
# file in the root directory of this source tree.

cmake_minimum_required(VERSION 3.13)

# Include FetchContent for Pico SDK
include(FetchContent)

FetchContent_Declare(
pico_sdk
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk.git
GIT_TAG 2.0.0
)

FetchContent_MakeAvailable(pico_sdk)
set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})
include(${PICO_SDK_PATH}/pico_sdk_init.cmake)

project(executorch_pico C CXX ASM)
pico_sdk_init()

set(EXECUTORCH_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../../../..)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

# Configure input model file
set(INPUT_MODEL
"default_model.pte"
CACHE STRING "Input PTE model file to embed"
)

# Convert relative paths to absolute
if(NOT IS_ABSOLUTE "${INPUT_MODEL}")
set(INPUT_MODEL "${CMAKE_CURRENT_SOURCE_DIR}/${INPUT_MODEL}")
endif()

message(STATUS "Using model file: ${INPUT_MODEL}")

# Validate model file exists
if(NOT EXISTS "${INPUT_MODEL}")
message(FATAL_ERROR "Model file '${INPUT_MODEL}' does not exist.")
endif()

# Create model_pte.c if it doesn't exist
set(MODEL_PTE_C "${CMAKE_CURRENT_SOURCE_DIR}/model_pte.c")
if(NOT EXISTS "${MODEL_PTE_C}")
file(
WRITE "${MODEL_PTE_C}"
"#include \"model_pte.h\"\n"
"\n"
"const uint8_t model_pte[] __attribute__((aligned(8))) = {\n"
" // Model data will be injected here\n"
"};\n"
"\n"
"const unsigned int model_pte_len = sizeof(model_pte);\n"
)
message(STATUS "Created initial model_pte.c")
endif()

# Use a stamp file instead of declaring model_pte.c as OUTPUT
set(MODEL_STAMP "${CMAKE_CURRENT_BINARY_DIR}/model_pte.stamp")

add_custom_command(
OUTPUT ${MODEL_STAMP}
COMMAND python3 ${EXECUTORCH_ROOT}/executorch/examples/rpi/pte_to_array.py
--model ${INPUT_MODEL} --file ${MODEL_PTE_C}
COMMAND ${CMAKE_COMMAND} -E touch ${MODEL_STAMP}
DEPENDS ${INPUT_MODEL}
${EXECUTORCH_ROOT}/executorch/examples/rpi/pte_to_array.py
COMMENT "Injecting PTE data from '${INPUT_MODEL}' into model_pte.c"
VERBATIM
)

# Target to ensure model injection happens
add_custom_target(inject_model_data ALL DEPENDS ${MODEL_STAMP})

# Check if source files exist
if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/main.cpp")
message(FATAL_ERROR "main.cpp not found in ${CMAKE_CURRENT_SOURCE_DIR}")
endif()

# Create the executable
add_executable(
executorch_pico main.cpp ${MODEL_PTE_C} # Use the full path to be explicit
)

# Ensure model data is injected before building
add_dependencies(executorch_pico inject_model_data)

# Set correct target properties for Pico2/RP2350
set_target_properties(
executorch_pico PROPERTIES SUFFIX ".elf" OUTPUT_NAME "executorch_pico"
)

# Configure Pico-specific settings
pico_enable_stdio_usb(executorch_pico 1)
pico_enable_stdio_uart(executorch_pico 0)

# Set correct flags for Pico2 (RP2350)
target_compile_options(
executorch_pico PRIVATE -mcpu=cortex-m33 -mfloat-abi=soft -mthumb
)

target_include_directories(
executorch_pico
PRIVATE ${EXECUTORCH_ROOT} ${EXECUTORCH_ROOT}/executorch/third-party
${EXECUTORCH_ROOT}/executorch/runtime/core/portable_type/c10
)

target_compile_definitions(
executorch_pico
PRIVATE C10_USING_CUSTOM_GENERATED_MACROS EXECUTORCH_ENABLE_LOGGING=OFF
EXECUTORCH_PAL_DEFAULT=minimal
)

# Optimization flags
target_compile_options(
executorch_pico PRIVATE -Os -ffunction-sections -fdata-sections
)

set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections")

set(BAREMETAL_BUILD_DIR ${EXECUTORCH_ROOT}/executorch/cmake-out/)

# Link ExecuTorch and Pico libraries
target_link_libraries(
executorch_pico
PRIVATE ${BAREMETAL_BUILD_DIR}/lib/libexecutorch.a
${BAREMETAL_BUILD_DIR}/lib/libexecutorch_core.a
-Wl,--whole-archive
${BAREMETAL_BUILD_DIR}/lib/libportable_ops_lib.a
-Wl,--no-whole-archive
${BAREMETAL_BUILD_DIR}/lib/libportable_kernels.a
pico_stdlib
pico_stdio_usb
)

# Only add extra outputs if the target builds successfully
if(TARGET executorch_pico)
pico_add_extra_outputs(executorch_pico)
endif()
45 changes: 45 additions & 0 deletions examples/rpi/pico2/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Overview
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's make a web doc as well? it can be same content. Add a picture or gif too :)

This document outlines the steps required to run a simple Add Module on the Pico2 microcontroller using executorch.

## (Pre-requisistes) Prepare the Environment for Arm
1. See <a href="https://docs.pytorch.org/executorch/main/tutorial-arm.html#set-up-the-developer-environment"/> for instructions on setting up the environment for Arm.
2. Make sure you have the toolchain configured correctly.
```bash
which arm-none-eabi-gcc
--> return something like executorch/examples/arm/ethos-u-scratch/arm-gnu-toolchain-13.3.rel1-x86_64-arm-none-eabi/bin/arm-none-eabi-gcc
```

## Build Pico2 Firmware with Executorch

This step involves two sub steps

1. Cross Compile Executorch for Arm Cortex M, Pico2 target
2. Build the firmware with the input model provided (If not provided, it will use the default_model.pte)

Use the following command to build the firmware:
``` bash
executorch/examples/rpi/build_firmware_pico.sh --model=<path_to_model.pte>
```

### Flash Firmware
Hold the BOOTSEL button on the Pico2 and connect it to your computer; it will mount as RPI-RP2. Copy the executorch_pico.uf2 file to this drive.

### Verify Execution
Check that the Pico2's LED blinks 10 times at 500 ms interval to confirm successful firmware execution.
The Pico2's LED should blink 10 times at 500 ms intervals, indicating successful firmware execution. If connected via serial, you should see:

```bash
Method loaded [forward]
Output: 13.000000, 136.000000, 24.000000, 131.000000
```

### Debugging via Serial Terminal
On macOS or Linux, open a serial terminal with:
```bash
screen /dev/tty.usbmodem1101 115200
```

Replace /dev/tty.usbmodem1101 with your device path. This terminal shows program logs and errors. If
the LED blinks 10 times at 100 ms intervals, your program hit an error state—check the logs here.

These steps complete running the simple model on Pico2 using ExecuTorch.
Loading
Loading