Skip to content

Commit 30961d6

Browse files
tarun292facebook-github-bot
authored andcommitted
Cross compiling for Xtensa (#579)
Summary: Pull Request resolved: #579 Differential Revision: D49852906 Pulled By: tarun292
1 parent 6871938 commit 30961d6

File tree

9 files changed

+705
-1
lines changed

9 files changed

+705
-1
lines changed

CMakeLists.txt

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,9 @@ option(EXECUTORCH_BUILD_SDK
134134

135135
option(EXECUTORCH_BUILD_EXAMPLES "Build the ExecuTorch examples.")
136136

137+
option(EXECUTORCH_BUILD_XTENSA_EXAMPLE
138+
"Build the example targeted for the Xtensa Hifi4 DSP" OFF)
139+
137140
if(NOT BUCK2)
138141
set(BUCK2 buck2)
139142
endif()
@@ -257,9 +260,16 @@ add_subdirectory(schema)
257260
# full operators. Does not contain any backends.
258261
#
259262

263+
# Check if dl exists for this toolchain and only then link it.
264+
find_library(DL_LIBRARY_EXISTS NAMES dl)
265+
266+
# Check if the library was found
267+
if(DL_LIBRARY_EXISTS)
268+
target_link_libraries(executorch PRIVATE dl) # For dladdr()
269+
endif()
270+
260271
add_library(executorch ${_executorch__srcs})
261272
target_link_libraries(executorch PRIVATE program_schema)
262-
target_link_libraries(executorch PRIVATE dl) # For dladdr()
263273
target_include_directories(executorch PUBLIC ${_common_include_directories})
264274
target_compile_options(executorch PUBLIC ${_common_compile_options})
265275
if(MAX_KERNEL_NUM)
@@ -355,5 +365,9 @@ if(EXECUTORCH_BUILD_EXAMPLES)
355365
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/examples)
356366
endif()
357367

368+
if(EXECUTORCH_BUILD_XTENSA_EXAMPLE)
369+
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/examples/xtensa)
370+
endif()
371+
358372
# Print all summary
359373
executorch_print_configuration_summary()

examples/xtensa/CMakeLists.txt

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# Set the minimum required version of CMake for this project.
2+
cmake_minimum_required(VERSION 3.10)
3+
4+
if(NOT CMAKE_CXX_STANDARD)
5+
set(CMAKE_CXX_STANDARD 17)
6+
endif()
7+
8+
# Set the project name.
9+
project(xtensa_executorch_example)
10+
11+
# Source root directory for executorch.
12+
if(NOT EXECUTORCH_ROOT)
13+
set(EXECUTORCH_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../..)
14+
endif()
15+
16+
add_compile_options(
17+
-DSDK_DEBUGCONSOLE=1
18+
-DSERIAL_PORT_TYPE_UART=1
19+
-DDEBUG_CONSOLE_RX_ENABLE=0
20+
-DDEBUG
21+
-DCPU_MIMXRT685SFVKB_dsp
22+
-DMCUXPRESSO_SDK
23+
-g
24+
-O0
25+
-Wall
26+
-fsigned-char
27+
-Wno-missing-braces
28+
-fmessage-length=0
29+
-DPRINTF_FLOAT_ENABLE=1)
30+
31+
if(NOT DEFINED NXP_SDK_ROOT_DIR)
32+
message(FATAL_ERROR "NXP_SDK_ROOT_DIR is not set")
33+
endif()
34+
35+
# lint_cmake: -linelength
36+
set(SOURCES
37+
${NXP_SDK_ROOT_DIR}/components/lists/fsl_component_generic_list.c
38+
${NXP_SDK_ROOT_DIR}/components/uart/fsl_adapter_usart.c
39+
${NXP_SDK_ROOT_DIR}/devices/MIMXRT685S/drivers/fsl_clock.c
40+
${NXP_SDK_ROOT_DIR}/devices/MIMXRT685S/drivers/fsl_common.c
41+
${NXP_SDK_ROOT_DIR}/devices/MIMXRT685S/drivers/fsl_common_dsp.c
42+
${NXP_SDK_ROOT_DIR}/devices/MIMXRT685S/drivers/fsl_flexcomm.c
43+
${NXP_SDK_ROOT_DIR}/devices/MIMXRT685S/drivers/fsl_gpio.c
44+
${NXP_SDK_ROOT_DIR}/devices/MIMXRT685S/drivers/fsl_mu.c
45+
${NXP_SDK_ROOT_DIR}/devices/MIMXRT685S/drivers/fsl_reset.c
46+
${NXP_SDK_ROOT_DIR}/devices/MIMXRT685S/drivers/fsl_usart.c
47+
${NXP_SDK_ROOT_DIR}/devices/MIMXRT685S/system_MIMXRT685S_dsp.c
48+
${NXP_SDK_ROOT_DIR}/devices/MIMXRT685S/utilities/debug_console_lite/fsl_assert.c
49+
${NXP_SDK_ROOT_DIR}/devices/MIMXRT685S/utilities/debug_console_lite/fsl_debug_console.c
50+
${NXP_SDK_ROOT_DIR}/boards/evkmimxrt685/dsp_examples/mu_polling/dsp/board_hifi4.c
51+
${NXP_SDK_ROOT_DIR}/boards/evkmimxrt685/dsp_examples/mu_polling/dsp/pin_mux.c
52+
${NXP_SDK_ROOT_DIR}/devices/MIMXRT685S/utilities/str/fsl_str.c)
53+
54+
add_library(dsp_mu_polling_libs STATIC ${SOURCES})
55+
56+
target_include_directories(
57+
dsp_mu_polling_libs
58+
PUBLIC ${NXP_SDK_ROOT_DIR}
59+
${NXP_SDK_ROOT_DIR}/components/uart
60+
${NXP_SDK_ROOT_DIR}/devices/MIMXRT685S/drivers
61+
${NXP_SDK_ROOT_DIR}/devices/MIMXRT685S/utilities/debug_console_lite
62+
${NXP_SDK_ROOT_DIR}/components/lists
63+
${NXP_SDK_ROOT_DIR}/devices/MIMXRT685S
64+
${NXP_SDK_ROOT_DIR}/CMSIS/Core/Include
65+
${NXP_SDK_ROOT_DIR}/devices/MIMXRT685S/utilities/str
66+
${NXP_SDK_ROOT_DIR}/boards/evkmimxrt685/dsp_examples/mu_polling/dsp)
67+
68+
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/ops)
69+
70+
# Generate the model header file
71+
add_custom_command(
72+
OUTPUT ${CMAKE_BINARY_DIR}/model_pte.h
73+
COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_LIST_DIR}/utils/gen_header.py
74+
--model_path ${MODEL_PATH} --header_output_path ${CMAKE_BINARY_DIR}
75+
COMMENT "Converting .pte model to header file..."
76+
DEPENDS ${CMAKE_CURRENT_LIST_DIR}/utils/gen_header.py)
77+
78+
add_custom_target(gen_model_header DEPENDS ${CMAKE_BINARY_DIR}/model_pte.h)
79+
80+
add_executable(xtensa_executorch_example executor_runner.cpp)
81+
add_dependencies(xtensa_executorch_example gen_model_header)
82+
83+
target_include_directories(xtensa_executorch_example PUBLIC ${ROOT_DIR}/..
84+
${CMAKE_BINARY_DIR})
85+
86+
target_link_options(xtensa_executorch_example PRIVATE
87+
-mlsp=${NXP_SDK_ROOT_DIR}/devices/MIMXRT685S/xtensa/min-rt)
88+
target_link_libraries(xtensa_executorch_example dsp_mu_polling_libs
89+
xtensa_ops_lib executorch)
90+
91+
add_custom_command(
92+
TARGET xtensa_executorch_example
93+
POST_BUILD
94+
COMMAND
95+
${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_LIST_DIR}/utils/post_compilation.py
96+
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME} ${CMAKE_BINARY_DIR}
97+
COMMENT
98+
"Generating .bin files that can be used to flash the DSP with. Copy over
99+
the dsp_text_release.bin and dsp_data_release.bin that are generated into
100+
your NXP MCUXpresso IDE workspace and flash the DSP with these binaries."
101+
DEPENDS
102+
${CMAKE_CURRENT_LIST_DIR}/utils/post_compilation.py)

examples/xtensa/executor_runner.cpp

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
/**
10+
* @file
11+
*
12+
* This is a simple executor_runner that boots up the DSP, configures the serial
13+
* port, sends a bunch of test messages to the M33 core and then loads the model
14+
* defined in model_pte.h. It runs this model using the ops available in
15+
* xtensa/ops directory.
16+
*/
17+
18+
#include <fsl_debug_console.h>
19+
#include "fsl_device_registers.h"
20+
#include "fsl_mu.h"
21+
22+
#include "board_hifi4.h"
23+
#include "model_pte.h"
24+
#include "pin_mux.h"
25+
26+
#include <memory>
27+
#include <vector>
28+
29+
#include <executorch/extension/data_loader/buffer_data_loader.h>
30+
#include <executorch/runtime/executor/method.h>
31+
#include <executorch/runtime/executor/program.h>
32+
#include <executorch/runtime/platform/log.h>
33+
#include <executorch/runtime/platform/profiler.h>
34+
#include <executorch/runtime/platform/runtime.h>
35+
#include <executorch/util/util.h>
36+
37+
static uint8_t method_allocator_pool[18 * 1024U]; // 4 MB
38+
39+
using namespace torch::executor;
40+
#include <xtensa/config/core.h>
41+
42+
#define APP_MU MUB
43+
/* Flag indicates Core Boot Up*/
44+
#define BOOT_FLAG 0x01U
45+
/* Channel transmit and receive register */
46+
#define CHN_MU_REG_NUM 0U
47+
/* How many message is used to test message sending */
48+
#define MSG_LENGTH 32U
49+
50+
using torch::executor::Error;
51+
using torch::executor::Result;
52+
53+
void LED_INIT();
54+
void LED_TOGGLE();
55+
56+
void LED_INIT() {
57+
CLOCK_EnableClock(kCLOCK_HsGpio0);
58+
RESET_PeripheralReset(kHSGPIO0_RST_SHIFT_RSTn);
59+
gpio_pin_config_t pin_config = {kGPIO_DigitalOutput, LOGIC_LED_OFF};
60+
GPIO_PinInit(
61+
BOARD_LED_RED_GPIO,
62+
BOARD_LED_RED_GPIO_PORT,
63+
BOARD_LED_RED_GPIO_PIN,
64+
&pin_config);
65+
}
66+
67+
void LED_TOGGLE() {
68+
LED_RED_TOGGLE();
69+
}
70+
71+
/*!
72+
* @brief Function to create delay for Led blink.
73+
*/
74+
void delay(void) {
75+
volatile uint32_t i = 0;
76+
for (i = 0; i < 5000000; ++i) {
77+
__NOP();
78+
}
79+
}
80+
81+
void et_pal_emit_log_message(
82+
et_timestamp_t timestamp,
83+
et_pal_log_level_t level,
84+
const char* filename,
85+
__ET_UNUSED const char* function,
86+
size_t line,
87+
const char* message,
88+
__ET_UNUSED size_t length) {
89+
PRINTF("\r%s\n", message);
90+
}
91+
92+
int main(int argc, char** argv) {
93+
/* Init board hardware. */
94+
BOARD_InitBootPins();
95+
96+
/* Initialize LED */
97+
LED_INIT();
98+
99+
/* MUB init */
100+
MU_Init(APP_MU);
101+
102+
/* Send flag to Core 0 to indicate Core 1 has startup */
103+
MU_SetFlags(APP_MU, BOOT_FLAG);
104+
105+
BOARD_InitDebugConsole();
106+
ET_LOG(Info, "Booted up in DSP.");
107+
108+
torch::executor::runtime_init();
109+
110+
auto loader =
111+
torch::executor::util::BufferDataLoader(model_pte, sizeof(model_pte));
112+
113+
Result<torch::executor::Program> program =
114+
torch::executor::Program::load(&loader);
115+
if (!program.ok()) {
116+
ET_LOG(
117+
Error,
118+
"ET: Program loading failed @ 0x%p: 0x%" PRIx32,
119+
model_pte,
120+
program.error());
121+
}
122+
123+
ET_LOG(
124+
Info, "AET: Model buffer loaded, has %u methods", program->num_methods());
125+
126+
const char* method_name = nullptr;
127+
{
128+
const auto method_name_result = program->get_method_name(0);
129+
ET_CHECK_MSG(method_name_result.ok(), "Program has no methods");
130+
method_name = *method_name_result;
131+
}
132+
ET_LOG(Info, "ET: Running method %s", method_name);
133+
134+
Result<torch::executor::MethodMeta> method_meta =
135+
program->method_meta(method_name);
136+
if (!method_meta.ok()) {
137+
ET_LOG(
138+
Error,
139+
"ET: Failed to get method_meta for %s: 0x%x",
140+
method_name,
141+
(unsigned int)method_meta.error());
142+
}
143+
144+
torch::executor::MemoryAllocator method_allocator{
145+
torch::executor::MemoryAllocator(
146+
sizeof(method_allocator_pool), method_allocator_pool)};
147+
148+
std::vector<std::unique_ptr<uint8_t[]>> planned_buffers; // Owns the memory
149+
std::vector<torch::executor::Span<uint8_t>>
150+
planned_spans; // Passed to the allocator
151+
size_t num_memory_planned_buffers = method_meta->num_memory_planned_buffers();
152+
153+
for (size_t id = 0; id < num_memory_planned_buffers; ++id) {
154+
size_t buffer_size =
155+
static_cast<size_t>(method_meta->memory_planned_buffer_size(id).get());
156+
ET_LOG(
157+
Info, "ET: Setting up planned buffer %zu, size %zu.", id, buffer_size);
158+
159+
planned_buffers.push_back(std::make_unique<uint8_t[]>(buffer_size));
160+
planned_spans.push_back({planned_buffers.back().get(), buffer_size});
161+
}
162+
163+
torch::executor::HierarchicalAllocator planned_memory(
164+
{planned_spans.data(), planned_spans.size()});
165+
166+
torch::executor::MemoryManager memory_manager(
167+
&method_allocator, &planned_memory);
168+
169+
Result<torch::executor::Method> method =
170+
program->load_method(method_name, &memory_manager);
171+
if (!method.ok()) {
172+
ET_LOG(
173+
Error,
174+
"Loading of method %s failed with status 0x%" PRIx32,
175+
method_name,
176+
method.error());
177+
}
178+
179+
ET_LOG(Info, "Method loaded.");
180+
torch::executor::util::PrepareInputTensors(*method);
181+
ET_LOG(Info, "Starting the model execution...");
182+
183+
Error status = method->execute();
184+
ET_LOG(Info, "Executed model");
185+
if (status != Error::Ok) {
186+
ET_LOG(
187+
Error,
188+
"Execution of method %s failed with status 0x%" PRIx32,
189+
method_name,
190+
status);
191+
} else {
192+
ET_LOG(Info, "Model executed successfully.");
193+
}
194+
195+
while (1) {
196+
delay();
197+
LED_TOGGLE();
198+
}
199+
200+
return 0;
201+
}

examples/xtensa/ops/CMakeLists.txt

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Copyright (c) Meta Platforms, Inc. and affiliates.
2+
# All rights reserved.
3+
#
4+
# This source code is licensed under the BSD-style license found in the
5+
# LICENSE file in the root directory of this source tree.
6+
7+
cmake_minimum_required(VERSION 3.19)
8+
9+
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
10+
if(NOT CMAKE_CXX_STANDARD)
11+
set(CMAKE_CXX_STANDARD 17)
12+
endif()
13+
14+
if(NOT PYTHON_EXECUTABLE)
15+
set(PYTHON_EXECUTABLE python3)
16+
endif()
17+
18+
# Source root directory for pytorch.
19+
if(NOT TORCH_ROOT)
20+
set(TORCH_ROOT ${EXECUTORCH_ROOT}/third-party/pytorch)
21+
endif()
22+
23+
set(_common_compile_options -Wno-deprecated-declarations)
24+
25+
include(${EXECUTORCH_ROOT}/build/Utils.cmake)
26+
include(${EXECUTORCH_ROOT}/build/Codegen.cmake)
27+
# Portable kernel sources TODO(larryliu0820): use buck2 to gather the sources
28+
set(_xtensa_kernels__srcs
29+
"${CMAKE_CURRENT_SOURCE_DIR}/op_add.cpp"
30+
"${EXECUTORCH_ROOT}/kernels/portable/cpu/util/broadcast_util.cpp"
31+
"${EXECUTORCH_ROOT}/kernels/portable/cpu/util/repeat_util.cpp"
32+
)
33+
34+
# Generate C++ bindings to register kernels into both PyTorch (for AOT) and
35+
# Executorch (for runtime). Here select all ops in functions.yaml
36+
gen_selected_ops("${CMAKE_CURRENT_LIST_DIR}/functions.yaml" "" "")
37+
# Expect gen_selected_ops output file to be selected_operators.yaml
38+
generate_bindings_for_kernels(${CMAKE_CURRENT_SOURCE_DIR}/functions.yaml "")
39+
message("Generated files ${gen_command_sources}")
40+
41+
#
42+
# portable_kernels: Pure-C++ kernel library for ATen ops
43+
#
44+
# Focused on portability and understandability rather than speed.
45+
#
46+
add_library(xtensa_kernels ${_xtensa_kernels__srcs})
47+
target_link_libraries(xtensa_kernels PRIVATE executorch)
48+
target_compile_options(xtensa_kernels PUBLIC ${_common_compile_options})
49+
50+
# Build a library for _portable_kernels__srcs
51+
#
52+
# portable_ops_lib: Register portable_ops_lib ops kernels into Executorch
53+
# runtime
54+
gen_operators_lib("xtensa_ops_lib" xtensa_kernels executorch)

0 commit comments

Comments
 (0)