Skip to content

Commit a55de72

Browse files
committed
Refactor ocre binary generation with objcopy
Currently, the ocre containers are embedded into the final binary that's going to be flashed by converting the content of the `*.wasm` file into a C header file with an array, dumping all the bytes in hex format (using `xxd` tool). This PRs replaces that mechanism by using some strategies (mainly intended/tested for Zephyr targets): - Instead of using RAM memory to store the array, put it in the flash. For that, it needs a partition for now called `wasm_binary_partition` - Instead of using a C header with an array inside, make use of `objcopy` to convert the `.wasm` file into something that can be flashed
1 parent f9e4f4b commit a55de72

File tree

6 files changed

+152
-348
lines changed

6 files changed

+152
-348
lines changed

CMakeLists.txt

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ endif()
88
if(TARGET_PLATFORM_NAME STREQUAL "Linux")
99
project(ocre LANGUAGES C ASM)
1010
elseif(TARGET_PLATFORM_NAME STREQUAL "Zephyr")
11-
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
11+
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
1212
project(ocre VERSION ${APP_VERSION_MAJOR}.${APP_VERSION_MINOR}.${APP_PATCHLEVEL}.${APP_VERSION_TWEAK} LANGUAGES C ASM)
1313
else()
1414
message(FATAL_ERROR "Unsupported TARGET_PLATFORM_NAME: ${TARGET_PLATFORM_NAME}")
@@ -55,18 +55,18 @@ add_custom_command(
5555
)
5656
add_custom_target(generate_messages DEPENDS ${MSG_GENERATED_FILE})
5757

58+
# Include WASM embedding module
59+
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
60+
include(embed_wasm_binary)
61+
62+
# Embed WASM binary if provided
5863
if(NOT "${OCRE_INPUT_FILE}" STREQUAL "")
59-
message("Using input file: ${OCRE_INPUT_FILE}")
60-
add_custom_command(
61-
OUTPUT ${CMAKE_CURRENT_LIST_DIR}/src/ocre/ocre_input_file.g
62-
COMMAND xxd -n wasm_binary -i ${OCRE_INPUT_FILE} > ${CMAKE_CURRENT_LIST_DIR}/src/ocre/ocre_input_file.g
63-
DEPENDS ${OCRE_INPUT_FILE}
64-
COMMENT "Generating C header from ${OCRE_INPUT_FILE}"
64+
embed_wasm_binary(
65+
INPUT_FILE ${OCRE_INPUT_FILE}
66+
OUTPUT_NAME app.wasm
67+
TARGET_NAME generate_ocre_file
6568
)
66-
6769
add_definitions(-DHAS_GENERATED_INPUT)
68-
69-
add_custom_target(generate_ocre_file DEPENDS ${CMAKE_CURRENT_LIST_DIR}/src/ocre/ocre_input_file.g)
7070
endif()
7171

7272
if(TARGET_PLATFORM_NAME STREQUAL "Linux")

boards/native_sim.overlay

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,5 +35,10 @@
3535
label = "storage";
3636
reg = <0x100000 0x00004000>;
3737
};
38+
39+
wasm_binary_partition: partition@f0000 {
40+
label = "wasm-binary";
41+
reg = <0x000f0000 DT_SIZE_K(64)>;
42+
};
3843
};
3944
};

cmake/embed_wasm_binary.cmake

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
# embed_wasm_binary.cmake
2+
# Embeds a WASM binary file into the application as a linkable object
3+
4+
# Helper function to detect the correct objcopy format based on architecture
5+
function(detect_objcopy_format OUT_FORMAT OUT_ARCH_NAME)
6+
set(FORMAT "")
7+
set(ARCH "unknown")
8+
9+
if(CONFIG_ARM64 OR CONFIG_AARCH64)
10+
set(FORMAT "elf64-littleaarch64")
11+
set(ARCH "ARM64")
12+
elseif(CONFIG_ARM)
13+
set(FORMAT "elf32-littlearm")
14+
set(ARCH "ARM")
15+
elseif(CONFIG_RISCV)
16+
if(CONFIG_64BIT)
17+
set(FORMAT "elf64-littleriscv")
18+
set(ARCH "RISC-V 64")
19+
else()
20+
set(FORMAT "elf32-littleriscv")
21+
set(ARCH "RISC-V 32")
22+
endif()
23+
elseif(CONFIG_X86_64)
24+
set(FORMAT "elf64-x86-64")
25+
set(ARCH "x86-64")
26+
elseif(CONFIG_X86 OR CONFIG_ARCH_POSIX)
27+
# POSIX/native boards are typically x86 32-bit
28+
# Check if 64-bit is explicitly set
29+
if(CONFIG_64BIT OR CMAKE_SIZEOF_VOID_P EQUAL 8)
30+
set(FORMAT "elf64-x86-64")
31+
set(ARCH "x86-64")
32+
else()
33+
set(FORMAT "elf32-i386")
34+
set(ARCH "x86")
35+
endif()
36+
elseif(CONFIG_XTENSA)
37+
set(FORMAT "elf32-xtensa-le")
38+
set(ARCH "Xtensa")
39+
elseif(CONFIG_ARC)
40+
set(FORMAT "elf32-littlearc")
41+
set(ARCH "ARC")
42+
elseif(CONFIG_NIOS2)
43+
set(FORMAT "elf32-littlenios2")
44+
set(ARCH "NIOS2")
45+
else()
46+
# Fallback: try to use default
47+
set(FORMAT "default")
48+
set(ARCH "unknown")
49+
message(WARNING "Could not detect architecture for objcopy, using 'default' format")
50+
endif()
51+
52+
# Return values to caller
53+
set(${OUT_FORMAT} ${FORMAT} PARENT_SCOPE)
54+
set(${OUT_ARCH_NAME} ${ARCH} PARENT_SCOPE)
55+
endfunction()
56+
57+
# Main function to embed WASM binary
58+
function(embed_wasm_binary)
59+
set(options "")
60+
set(oneValueArgs INPUT_FILE OUTPUT_NAME TARGET_NAME)
61+
set(multiValueArgs "")
62+
cmake_parse_arguments(WASM "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
63+
64+
if(NOT WASM_INPUT_FILE)
65+
message(FATAL_ERROR "embed_wasm_binary: INPUT_FILE is required")
66+
endif()
67+
68+
if(NOT WASM_OUTPUT_NAME)
69+
set(WASM_OUTPUT_NAME "app.wasm")
70+
endif()
71+
72+
if(NOT WASM_TARGET_NAME)
73+
set(WASM_TARGET_NAME "generate_wasm_binary")
74+
endif()
75+
76+
message(STATUS "Embedding WASM binary: ${WASM_INPUT_FILE}")
77+
78+
# Stage the WASM file with a fixed name for consistent symbol names
79+
set(WASM_STAGED_FILE ${CMAKE_CURRENT_BINARY_DIR}/${WASM_OUTPUT_NAME})
80+
81+
add_custom_command(
82+
OUTPUT ${WASM_STAGED_FILE}
83+
COMMAND ${CMAKE_COMMAND} -E copy ${WASM_INPUT_FILE} ${WASM_STAGED_FILE}
84+
DEPENDS ${WASM_INPUT_FILE}
85+
COMMENT "Staging WASM file: ${WASM_INPUT_FILE}"
86+
)
87+
88+
# Detect the correct objcopy format for the target architecture
89+
detect_objcopy_format(OBJCOPY_FORMAT ARCH_NAME)
90+
91+
message(STATUS "Detected architecture: ${ARCH_NAME}, using objcopy format: ${OBJCOPY_FORMAT}")
92+
93+
# Convert WASM to object file
94+
set(WASM_OBJECT_FILE ${CMAKE_CURRENT_BINARY_DIR}/wasm_binary.o)
95+
96+
add_custom_command(
97+
OUTPUT ${WASM_OBJECT_FILE}
98+
COMMAND objcopy
99+
-I binary
100+
-O ${OBJCOPY_FORMAT}
101+
--rename-section .data=.rodata.wasm,alloc,load,readonly,data,contents
102+
${WASM_STAGED_FILE}
103+
${WASM_OBJECT_FILE}
104+
DEPENDS ${WASM_STAGED_FILE}
105+
COMMENT "Converting to object file (${OBJCOPY_FORMAT} for ${ARCH_NAME})"
106+
)
107+
108+
# Create a custom target
109+
add_custom_target(${WASM_TARGET_NAME} DEPENDS ${WASM_OBJECT_FILE})
110+
111+
# Export the object file path to parent scope
112+
set(WASM_BINARY_OBJECT ${WASM_OBJECT_FILE} PARENT_SCOPE)
113+
114+
message(STATUS "WASM binary will be embedded as: ${WASM_OBJECT_FILE}")
115+
endfunction()

src/ocre/ocre_gpio/ocre_gpio.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,10 @@ int ocre_gpio_init(void) {
104104
#elif defined(CONFIG_BOARD_W5500_EVB_PICO2)
105105
INIT_GPIO_PORT_NAMED(0, DT_NODELABEL(gpio0), "GPIO0");
106106

107+
#elif defined(CONFIG_BOARD_NRF5340DK_NRF5340_CPUAPP)
108+
INIT_GPIO_PORT_NAMED(0, DT_NODELABEL(gpio0), "GPIO0");
109+
INIT_GPIO_PORT_NAMED(1, DT_NODELABEL(gpio1), "GPIO1");
110+
107111
#else
108112
// Generic fallback
109113
#if DT_NODE_EXISTS(DT_NODELABEL(gpio0))
@@ -384,7 +388,7 @@ static void gpio_callback_handler(const struct device *port, struct gpio_callbac
384388
}
385389

386390
//========================================================================================================================================================================================================================================================================================================
387-
// By Name
391+
// By Name
388392
//========================================================================================================================================================================================================================================================================================================
389393
static int find_port_index(const struct device *port) {
390394
if (!port) {
@@ -662,12 +666,12 @@ int ocre_gpio_wasm_register_callback_by_name(wasm_exec_env_t exec_env, const cha
662666

663667
int global_pin = port_idx * CONFIG_OCRE_GPIO_PINS_PER_PORT + pin;
664668
LOG_INF("Registering callback by name: %s, global_pin=%d", name, global_pin);
665-
669+
666670
if (global_pin >= CONFIG_OCRE_GPIO_MAX_PINS) {
667671
LOG_ERR("Global pin %d exceeds max %d", global_pin, CONFIG_OCRE_GPIO_MAX_PINS);
668672
return -EINVAL;
669673
}
670-
674+
671675
return ocre_gpio_register_callback(global_pin);
672676
}
673677

@@ -686,6 +690,6 @@ int ocre_gpio_wasm_unregister_callback_by_name(wasm_exec_env_t exec_env, const c
686690

687691
int global_pin = port_idx * CONFIG_OCRE_GPIO_PINS_PER_PORT + pin;
688692
LOG_INF("Unregistering callback by name: %s, global_pin=%d", name, global_pin);
689-
693+
690694
return ocre_gpio_unregister_callback(global_pin);
691695
}

0 commit comments

Comments
 (0)