Skip to content
Closed
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
20 changes: 6 additions & 14 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ endif()
if(TARGET_PLATFORM_NAME STREQUAL "Linux")
project(ocre LANGUAGES C ASM)
elseif(TARGET_PLATFORM_NAME STREQUAL "Zephyr")
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(ocre VERSION ${APP_VERSION_MAJOR}.${APP_VERSION_MINOR}.${APP_PATCHLEVEL}.${APP_VERSION_TWEAK} LANGUAGES C ASM)
else()
message(FATAL_ERROR "Unsupported TARGET_PLATFORM_NAME: ${TARGET_PLATFORM_NAME}")
Expand Down Expand Up @@ -55,22 +55,14 @@ add_custom_command(
)
add_custom_target(generate_messages DEPENDS ${MSG_GENERATED_FILE})

if(NOT "${OCRE_INPUT_FILE}" STREQUAL "")
message("Using input file: ${OCRE_INPUT_FILE}")
add_custom_command(
OUTPUT ${CMAKE_CURRENT_LIST_DIR}/src/ocre/ocre_input_file.g
COMMAND xxd -n wasm_binary -i ${OCRE_INPUT_FILE} > ${CMAKE_CURRENT_LIST_DIR}/src/ocre/ocre_input_file.g
DEPENDS ${OCRE_INPUT_FILE}
COMMENT "Generating C header from ${OCRE_INPUT_FILE}"
)

add_definitions(-DHAS_GENERATED_INPUT)

add_custom_target(generate_ocre_file DEPENDS ${CMAKE_CURRENT_LIST_DIR}/src/ocre/ocre_input_file.g)
endif()
# Include and process WASM binaries
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
include(embed_wasm_binary)
process_wasm_binaries()

if(TARGET_PLATFORM_NAME STREQUAL "Linux")
include(${CMAKE_CURRENT_LIST_DIR}/src/shared/platform/posix/ocre_internal.cmake)
elseif(TARGET_PLATFORM_NAME STREQUAL "Zephyr")
include_directories(${CMAKE_CURRENT_BINARY_DIR})
include(${CMAKE_CURRENT_LIST_DIR}/src/shared/platform/zephyr/ocre_internal.cmake)
endif()
5 changes: 5 additions & 0 deletions boards/native_sim.overlay
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,10 @@
label = "storage";
reg = <0x100000 0x00004000>;
};

wasm_binary_partition: partition@f0000 {
label = "wasm-binary";
reg = <0x000f0000 DT_SIZE_K(64)>;
};
};
};
31 changes: 23 additions & 8 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@
show_help() {
echo "Usage: $0 -t <target> [-r] [-f <file1> [file2 ...]]"
echo " -t <target> (Required) Specify the target. z for Zephyr and l for Linux"
echo " -r (Optional) Specify whether run after the build"
echo " -f <file(s)> (Optional) Specify one or more input files"
echo " -r (Optional) Run after build"
echo " -f <file(s)> (Optional) Specify WASM file(s) to embed"
echo " Example: -f app.wasm"
echo " Example: -f blinky.wasm sensor.wasm control.wasm"
echo " -b <board> (Optional, Zephyr only) Select board:"
echo " uw -> b_u585i_iot02a + W5500"
echo " ue -> b_u585i_iot02a + ENC28J60"
echo " note: when no board is selected, native_sim is the default target for Zephyr"
echo " (default: native_sim)"
echo " -h Display help"
exit 0
}
Expand Down Expand Up @@ -99,14 +101,27 @@ if [[ "$TARGET" == "z" ]]; then
esac

if [[ ${#INPUT_FILES[@]} -gt 0 ]]; then
echo "Input files provided: ${INPUT_FILES[*]}"
rm flash.bin
echo "Input WASM files provided: ${INPUT_FILES[*]}"

WASM_LIST=$(IFS=';'; echo "${INPUT_FILES[*]}")

for WASM_FILE in "${INPUT_FILES[@]}"; do
WASM_NAME=$(basename "$WASM_FILE" .wasm)
echo " → Will embed: ${WASM_NAME} <- ${WASM_FILE}"
done

rm -f flash.bin
west build -p -b $ZEPHYR_BOARD ./application -d build -- \
-DMODULE_EXT_ROOT=`pwd`/application -DOCRE_INPUT_FILE="${INPUT_FILES[0]}" -DTARGET_PLATFORM_NAME=Zephyr $CONF_EXTRA || exit 1
-DMODULE_EXT_ROOT=`pwd`/application \
-DOCRE_WASM_FILES="$WASM_LIST" \
-DTARGET_PLATFORM_NAME=Zephyr \
$CONF_EXTRA || exit 1
else
rm flash.bin
rm -f flash.bin
west build -p -b $ZEPHYR_BOARD ./application -d build -- \
-DMODULE_EXT_ROOT=`pwd`/application -DTARGET_PLATFORM_NAME=Zephyr $CONF_EXTRA || exit 1
-DMODULE_EXT_ROOT=`pwd`/application \
-DTARGET_PLATFORM_NAME=Zephyr \
$CONF_EXTRA || exit 1
fi
elif [[ "$TARGET" == "l" ]]; then
echo "Target is: Linux"
Expand Down
198 changes: 198 additions & 0 deletions cmake/embed_wasm_binary.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
# embed_wasm_binary.cmake
# Embeds a WASM binary file into the application as a linkable object

# Helper function to detect the correct objcopy format based on architecture
function(detect_objcopy_format OUT_FORMAT OUT_ARCH_NAME)
set(FORMAT "")
set(ARCH "unknown")

if(CONFIG_ARM64 OR CONFIG_AARCH64)
set(FORMAT "elf64-littleaarch64")
set(ARCH "ARM64")
elseif(CONFIG_ARM)
set(FORMAT "elf32-littlearm")
set(ARCH "ARM")
elseif(CONFIG_RISCV)
if(CONFIG_64BIT)
set(FORMAT "elf64-littleriscv")
set(ARCH "RISC-V 64")
else()
set(FORMAT "elf32-littleriscv")
set(ARCH "RISC-V 32")
endif()
elseif(CONFIG_X86_64)
set(FORMAT "elf64-x86-64")
set(ARCH "x86-64")
elseif(CONFIG_X86 OR CONFIG_ARCH_POSIX)
# POSIX/native boards are typically x86 32-bit
# Check if 64-bit is explicitly set
if(CONFIG_64BIT OR CMAKE_SIZEOF_VOID_P EQUAL 8)
set(FORMAT "elf64-x86-64")
set(ARCH "x86-64")
else()
set(FORMAT "elf32-i386")
set(ARCH "x86")
endif()
elseif(CONFIG_XTENSA)
set(FORMAT "elf32-xtensa-le")
set(ARCH "Xtensa")
elseif(CONFIG_ARC)
set(FORMAT "elf32-littlearc")
set(ARCH "ARC")
elseif(CONFIG_NIOS2)
set(FORMAT "elf32-littlenios2")
set(ARCH "NIOS2")
else()
# Fallback: try to use default
set(FORMAT "default")
set(ARCH "unknown")
message(WARNING "Could not detect architecture for objcopy, using 'default' format")
endif()

# Return values to caller
set(${OUT_FORMAT} ${FORMAT} PARENT_SCOPE)
set(${OUT_ARCH_NAME} ${ARCH} PARENT_SCOPE)
endfunction()

# Main function to embed WASM binary
function(embed_wasm_binary)
set(options "")
set(oneValueArgs INPUT_FILE OUTPUT_NAME TARGET_NAME SYMBOL_NAME)
set(multiValueArgs "")
cmake_parse_arguments(WASM "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})

if(NOT WASM_INPUT_FILE)
message(FATAL_ERROR "embed_wasm_binary: INPUT_FILE is required")
endif()

if(NOT WASM_OUTPUT_NAME)
set(WASM_OUTPUT_NAME "app.wasm")
endif()

if(NOT WASM_SYMBOL_NAME)
get_filename_component(WASM_SYMBOL_NAME "${WASM_OUTPUT_NAME}" NAME_WE)
endif()

message(STATUS "Embedding WASM binary: ${WASM_INPUT_FILE}")

# Stage the WASM file with a fixed name for consistent symbol names
set(WASM_STAGED_FILE ${CMAKE_CURRENT_BINARY_DIR}/${WASM_OUTPUT_NAME})

add_custom_command(
OUTPUT ${WASM_STAGED_FILE}
COMMAND ${CMAKE_COMMAND} -E copy ${WASM_INPUT_FILE} ${WASM_STAGED_FILE}
DEPENDS ${WASM_INPUT_FILE}
COMMENT "Staging WASM file: ${WASM_INPUT_FILE}"
)

# Detect the correct objcopy format for the target architecture
detect_objcopy_format(OBJCOPY_FORMAT ARCH_NAME)

message(STATUS "Detected architecture: ${ARCH_NAME}, using objcopy format: ${OBJCOPY_FORMAT}")

# Convert WASM to object file (use symbol name for unique files)
set(WASM_OBJECT_FILE ${CMAKE_CURRENT_BINARY_DIR}/wasm_${WASM_SYMBOL_NAME}.o)

add_custom_command(
OUTPUT ${WASM_OBJECT_FILE}
COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_CURRENT_BINARY_DIR}
objcopy
-I binary
-O ${OBJCOPY_FORMAT}
--rename-section .data=.rodata.wasm.${WASM_SYMBOL_NAME},alloc,load,readonly,data,contents
${WASM_OUTPUT_NAME}
wasm_${WASM_SYMBOL_NAME}.o
DEPENDS ${WASM_STAGED_FILE}
COMMENT "Converting to object file (${OBJCOPY_FORMAT} for ${ARCH_NAME})"
)

# Create a custom target
add_custom_target(${WASM_TARGET_NAME} DEPENDS ${WASM_OBJECT_FILE})

# Export the object file path to parent scope with symbol name
set(WASM_BINARY_OBJECT_${WASM_SYMBOL_NAME} ${WASM_OBJECT_FILE} PARENT_SCOPE)

message(STATUS "WASM binary will be embedded as: ${WASM_OBJECT_FILE}")
endfunction()

# Generate a manifest header file using template and Python script
function(generate_wasm_manifest WASM_NAMES_LIST)
if(NOT WASM_NAMES_LIST)
message(WARNING "No WASM names provided for manifest generation")
return()
endif()

set(TEMPLATE_FILE ${CMAKE_CURRENT_SOURCE_DIR}/src/ocre/wasm_manifest.h.in)
set(MANIFEST_FILE ${CMAKE_CURRENT_BINARY_DIR}/wasm_manifest.h)

# Find Python executable (reuse from earlier in CMakeLists)
if(NOT DEFINED PYTHON_EXECUTABLE)
find_package(Python3 QUIET REQUIRED Interpreter)
set(PYTHON_EXECUTABLE ${Python3_EXECUTABLE})
endif()

# Generate manifest using Python script with template
execute_process(
COMMAND ${PYTHON_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/generate_wasm_manifest.py
${WASM_NAMES_LIST}
-t ${TEMPLATE_FILE}
-o ${MANIFEST_FILE}
RESULT_VARIABLE RESULT
OUTPUT_VARIABLE OUTPUT
ERROR_VARIABLE ERROR
)

if(NOT RESULT EQUAL 0)
message(FATAL_ERROR "Failed to generate WASM manifest: ${ERROR}")
endif()

message(STATUS "${OUTPUT}")
endfunction()

# High-level function to process WASM files from build script
function(process_wasm_binaries)
if(NOT DEFINED OCRE_WASM_FILES OR "${OCRE_WASM_FILES}" STREQUAL "")
message(STATUS "No WASM files specified")
return()
endif()

message(STATUS "Processing WASM files: ${OCRE_WASM_FILES}")

set(WASM_NAMES "")

foreach(WASM_PATH ${OCRE_WASM_FILES})
if(EXISTS "${WASM_PATH}")
get_filename_component(WASM_NAME "${WASM_PATH}" NAME_WE)
message(STATUS " → Embedding: ${WASM_NAME} from ${WASM_PATH}")

embed_wasm_binary(
INPUT_FILE "${WASM_PATH}"
OUTPUT_NAME "${WASM_NAME}.wasm"
TARGET_NAME "generate_${WASM_NAME}_wasm"
SYMBOL_NAME "${WASM_NAME}"
)

list(APPEND WASM_NAMES ${WASM_NAME})
else()
message(WARNING "WASM file not found: ${WASM_PATH}")
endif()
endforeach()

if(WASM_NAMES)
# Export to parent scope and cache for use in other cmake files
set(WASM_MANIFEST_ENTRIES ${WASM_NAMES} PARENT_SCOPE)
set(WASM_MANIFEST_ENTRIES ${WASM_NAMES} CACHE INTERNAL "List of embedded WASM names")

list(LENGTH WASM_NAMES WASM_COUNT)
message(STATUS "Successfully configured ${WASM_COUNT} WASM file(s): ${WASM_NAMES}")

# Generate the manifest header
generate_wasm_manifest("${WASM_NAMES}")

add_definitions(-DHAS_GENERATED_INPUT)
add_definitions(-DHAS_WASM_MANIFEST)
else()
message(WARNING "No valid WASM files were processed")
endif()
endfunction()
12 changes: 8 additions & 4 deletions src/ocre/ocre_gpio/ocre_gpio.c
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ int ocre_gpio_init(void) {
#elif defined(CONFIG_BOARD_W5500_EVB_PICO2)
INIT_GPIO_PORT_NAMED(0, DT_NODELABEL(gpio0), "GPIO0");

#elif defined(CONFIG_BOARD_NRF5340DK_NRF5340_CPUAPP)
INIT_GPIO_PORT_NAMED(0, DT_NODELABEL(gpio0), "GPIO0");
INIT_GPIO_PORT_NAMED(1, DT_NODELABEL(gpio1), "GPIO1");

#else
// Generic fallback
#if DT_NODE_EXISTS(DT_NODELABEL(gpio0))
Expand Down Expand Up @@ -384,7 +388,7 @@ static void gpio_callback_handler(const struct device *port, struct gpio_callbac
}

//========================================================================================================================================================================================================================================================================================================
// By Name
// By Name
//========================================================================================================================================================================================================================================================================================================
static int find_port_index(const struct device *port) {
if (!port) {
Expand Down Expand Up @@ -662,12 +666,12 @@ int ocre_gpio_wasm_register_callback_by_name(wasm_exec_env_t exec_env, const cha

int global_pin = port_idx * CONFIG_OCRE_GPIO_PINS_PER_PORT + pin;
LOG_INF("Registering callback by name: %s, global_pin=%d", name, global_pin);

if (global_pin >= CONFIG_OCRE_GPIO_MAX_PINS) {
LOG_ERR("Global pin %d exceeds max %d", global_pin, CONFIG_OCRE_GPIO_MAX_PINS);
return -EINVAL;
}

return ocre_gpio_register_callback(global_pin);
}

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

int global_pin = port_idx * CONFIG_OCRE_GPIO_PINS_PER_PORT + pin;
LOG_INF("Unregistering callback by name: %s, global_pin=%d", name, global_pin);

return ocre_gpio_unregister_callback(global_pin);
}
Loading
Loading