diff --git a/README.ZEPHYR.md b/README.ZEPHYR.md new file mode 100644 index 000000000..4fe9b84b7 --- /dev/null +++ b/README.ZEPHYR.md @@ -0,0 +1,35 @@ + + +# AtomVM for Zephyr +This is an experimental port that will hopefully replace the current stm32 `libopencm3` based port, and should support many more boards. +Not all boards supported by Zephyr are capable of running AtomVM. The most likely excluding factor will be lack of flash storage space to accomodate both the VM and user BEAM applications. We will likely need a cusomized device tree to only define the boards that AtomVM can support. Currently a build can be attempeted for any board supported by the Zephyr SDK and the user will need to determing if the final build can fit onto the device and still have space left for a user application partition (on stm32 devices this is typically a 128K block size). + +## Prerequisites + +* `west` Make sure to follow its [installation procedure](https://docs.zephyrproject.org/latest/develop/getting_started/index.html#get-zephyr-and-install-python-dependencies) +* `Zephyr SDK` [installation procedure](https://docs.zephyrproject.org/latest/develop/getting_started/index.html#install-the-zephyr-sdk) +* `cmake` +* `ninja` +* An appropriate flashing tool and software for your device, such as [st-flash](https://github.com/texane/stlink) for flashing STM32 devices with a [st-link v2](https://www.st.com/en/development-tools/st-link-v2.html) or [st-link v3](https://www.st.com/en/development-tools/stlink-v3set.html) device. +* A serial console program, such as `minicom` or `screen`, so that you can view console output from your AtomVM application. + +## Building +Before building for the first time you need to have set up `west` and `Zephyr SDK`, following the [Zephyr Project Getting Started instuctions](https://docs.zephyrproject.org/latest/develop/getting_started/index.html). After setup is complete from inside the AtomVM/src/platforms/zephyr directory use `west` to build for your board with the `-b` switch: + + $ west build -b nucleo_f429zi -p always . + +The `-p always` option instructs `west` to perform a prestine build, which is a recommended practice. + +## Listing `west` target devices +A complete list of boards supported by + + $ west boards + +## Flashing + + $ west flash + diff --git a/src/platforms/zephyr/.gitignore b/src/platforms/zephyr/.gitignore new file mode 100644 index 000000000..0d2502599 --- /dev/null +++ b/src/platforms/zephyr/.gitignore @@ -0,0 +1,5 @@ +# Copyright 2023 Winford (Uncle Grumpy) +# +# SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later + +build/ diff --git a/src/platforms/zephyr/CMakeLists.txt b/src/platforms/zephyr/CMakeLists.txt new file mode 100644 index 000000000..bda49a7e4 --- /dev/null +++ b/src/platforms/zephyr/CMakeLists.txt @@ -0,0 +1,128 @@ +# +# This file is part of AtomVM. +# +# Copyright 2023 Winford (Uncle Grumpy) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later +# + +cmake_minimum_required (VERSION 3.20) +set(CMAKE_C_STANDARD_REQUIRED YES) +set(CMAKE_CXX_STANDARD_REQUIRED YES) + +# Enforce to disable any compiler-specific extensions +set(CMAKE_C_EXTENSIONS NO) +set(CMAKE_CXX_EXTENSIONS NO) +set(C_STANDARD_REQUIRED YES) +set_property(GLOBAL PROPERTY C_STANDARD_REQUIRED TRUE) +set_property(GLOBAL PROPERTY CSTD iso9899:2011) +set_property(GLOBAL PROPERTY C_STANDARD iso9899:2011) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(AtomVM LANGUAGES C) + +if (NOT BOARD) + message(FATAL_ERROR "No BOARD specified for device config generator") +endif () + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../CMakeModules") +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) + +# Options that make sense for this platform +option(AVM_USE_32BIT_FLOAT "Use 32 bit floats." ON) +option(AVM_VERBOSE_ABORT "Print module and line number on VM abort" OFF) +option(AVM_CREATE_STACKTRACES "Create stacktraces" ON) +option(AVM_NEWLIB_NANO "Use 'nano' newlib. Saves 46kB, no `long long` support" OFF) +option(AVM_LOG_DISABLE "Disable log output" OFF) +option(AVM_ENABLE_LOG_COLOR "Use color log output" OFF) +option(AVM_ENABLE_LOG_LINES "Include source and line info for all enbled levels" OFF) +option(AVM_CONFIG_REBOOT_ON_NOT_OK "Reboot when application exits with non 'ok' return" OFF) +option(AVM_DISABLE_GPIO_NIFS "Disable GPIO nifs (input and output)" OFF) +option(AVM_DISABLE_GPIO_PORT_DRIVER "Disable GPIO 'port' driver (input, output, and interrupts)" OFF) + +set(AVM_DISABLE_SMP ON FORCE) +set(CONFIG_ATOMVM_DISABLE_SMP ON FORCE) +zephyr_library_compile_definitions(AVM_NO_SMP) +zephyr_library_compile_definitions(CONFIG_AVM_NO_SMP) +add_compile_definitions(AVM_NO_SMP) + +set(HAVE_CLOCK_SETTIME ON FORCE) + +if (AVM_NEWLIB_NANO) + set(LINKER_FLAGS "${LINKER_FLAGS} -specs=nano.specs") + set(AVM_LOG_DISABLE ON FORCE) +endif() + +if (AVM_CONFIG_REBOOT_ON_NOT_OK) + add_compile_definitions(CONFIG_REBOOT_ON_NOT_OK) +endif() + + # Configure logging +if (AVM_LOG_DISABLE) + add_compile_definitions(AVM_LOG_DISABLE) +elseif (AVM_LOG_LEVEL_MAX) + set(AVM_LOG_LEVEL_MAX ${AVM_LOG_LEVEL_MAX} CACHE STRING "AtomVM max log level") +else() + set(AVM_LOG_LEVEL_MAX LOG_INFO CACHE STRING "AtomVM max log level") +endif() +if (AVM_LOG_LEVEL_MAX) + set_property(CACHE AVM_LOG_LEVEL_MAX PROPERTY STRINGS LOG_NONE LOG_ERROR LOG_WARN LOG_INFO LOG_DEBUG) + add_compile_definitions(AVM_LOG_LEVEL_MAX=${AVM_LOG_LEVEL_MAX}) +endif() +if (AVM_ENABLE_LOG_COLOR) + add_compile_definitions(ENABLE_LOG_COLOR) +endif() +if (AVM_ENABLE_LOG_LINES) + add_compile_definitions(ENABLE_LOG_LINE_INFO) +endif() + +# Configure Drivers +if (AVM_DISABLE_GPIO_NIFS) + add_compile_definitions(AVM_DISABLE_GPIO_NIFS) +endif() +if (AVM_DISABLE_GPIO_PORT_DRIVER) + add_compile_definitions(AVM_DISABLE_GPIO_PORT_DRIVER) +endif() + +## Include additional compilation flags +#include(cmake/compile-flags.cmake) + +set( + PLATFORM_LIB_SUFFIX + ${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR} +) + +# Specify output executable +target_sources(app PRIVATE src/main.c) +target_include_directories(app PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/libAtomVM) + +add_subdirectory(src/lib) + +target_link_libraries(app PRIVATE libAtomVM${PLATFORM_LIB_SUFFIX}) + +set_property(TARGET app PROPERTY C_STANDARD 11) + +if(CMAKE_COMPILER_IS_GNUCC) + target_compile_options(app PUBLIC -Wall -Wextra -ggdb) +endif() + +add_subdirectory(src/libAtomVM libAtomVM) +target_link_libraries(app PUBLIC libAtomVM) + +message("----------------------------------------") +message(STATUS "Board : ${BOARD}") +message("--------Device Configuration Info-------") +message(STATUS "Clock Hz : ${CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC}") +message(STATUS "Flash Size : ${CONFIG_FLASH_SIZE}K") diff --git a/src/platforms/zephyr/VERSION b/src/platforms/zephyr/VERSION new file mode 100644 index 000000000..81ea5000e --- /dev/null +++ b/src/platforms/zephyr/VERSION @@ -0,0 +1,5 @@ +VERSION_MAJOR = 0 +VERSION_MINOR = 6 +PATCHLEVEL = 0 +VERSION_TWEAK = 2 +EXTRAVERSION = alpha diff --git a/src/platforms/zephyr/VERSION.license b/src/platforms/zephyr/VERSION.license new file mode 100644 index 000000000..1ad7d4f25 --- /dev/null +++ b/src/platforms/zephyr/VERSION.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2023 Winford (Uncle Grumpy) + +SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later diff --git a/src/platforms/zephyr/prj.conf b/src/platforms/zephyr/prj.conf new file mode 100644 index 000000000..6a31775dc --- /dev/null +++ b/src/platforms/zephyr/prj.conf @@ -0,0 +1,39 @@ +# +# This file is part of AtomVM. +# +# Copyright 2023 Winford (Uncle Grumpy) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later +# + +CONFIG_KERNEL_BIN_NAME="AtomVM" +CONFIG_STDOUT_CONSOLE=y +CONFIG_CONSOLE_SUBSYS=y +CONFIG_SYS_HEAP_AUTO=y +CONFIG_GPIO=y +CONFIG_PINCTRL_DYNAMIC=y +CONFIG_POSIX_CLOCK=y +CONFIG_REQUIRES_FLOAT_PRINTF=y +CONFIG_FPU=y +CONFIG_RTC=y +CONFIG_RESET=y +CONFIG_REBOOT=y +CONFIG_STACK_USAGE=y +CONFIG_RUNTIME_ERROR_CHECKS=y +CONFIG_OUTPUT_PRINT_MEMORY_USAGE=y +CONFIG_DYNAMIC_INTERRUPTS=y +CONFIG_FORTIFY_SOURCE_RUN_TIME=y + +CONFIG_DEBUG_OPTIMIZATIONS=y diff --git a/src/platforms/zephyr/src/lib/CMakeLists.txt b/src/platforms/zephyr/src/lib/CMakeLists.txt new file mode 100644 index 000000000..b42cb1796 --- /dev/null +++ b/src/platforms/zephyr/src/lib/CMakeLists.txt @@ -0,0 +1,48 @@ +# +# This file is part of AtomVM. +# +# Copyright 2022 Paul Guyot +# Copyright 2023 Winford +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later +# + +cmake_minimum_required (VERSION 3.20.5) + +set( + PLATFORM_LIB_SUFFIX + ${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR} +) +project (libAtomVM${PLATFORM_LIB_SUFFIX}) +zephyr_library_named(libAtomVM${PLATFORM_LIB_SUFFIX}) + +zephyr_include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ../../../../libAtomVM + ${CMAKE_BINARY_DIR}/zephyr/include/generated/ +) + +zephyr_library_sources( + platform_nifs.c + sys.c +) + +if(CMAKE_COMPILER_IS_GNUCC) + target_compile_options(libAtomVM${PLATFORM_LIB_SUFFIX} PUBLIC -Wall -pedantic -Wextra -ggdb) +endif() +set_property(TARGET libAtomVM${PLATFORM_LIB_SUFFIX} PROPERTY C_STANDARD 11) + +target_link_libraries(app PUBLIC libAtomVM${PLATFORM_LIB_SUFFIX}) +target_link_options(libAtomVM${PLATFORM_LIB_SUFFIX} PUBLIC ${LINK_OPTIONS} -Wl,--whole-archive ${CMAKE_CURRENT_BINARY_DIR}/liblibAtomVM${PLATFORM_LIB_SUFFIX}.a -Wl,--no-whole-archive) diff --git a/src/platforms/zephyr/src/lib/avm_devcfg.h b/src/platforms/zephyr/src/lib/avm_devcfg.h new file mode 100644 index 000000000..9e42062db --- /dev/null +++ b/src/platforms/zephyr/src/lib/avm_devcfg.h @@ -0,0 +1,33 @@ +/* This file is part of AtomVM. + * + * Copyright 2023 Winford (Uncle Grumpy) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later + */ + +#ifndef _AVM_DEVCFG_H_ +#define _AVM_DEVCFG_H_ + +#include + +#if (CONFIG_FLASH_SIZE == 512) +#define AVM_APP_ADDRESS ((CONFIG_FLASH_BASE_ADDRESS) + 0x60000U) +#else +#define AVM_APP_ADDRESS ((CONFIG_FLASH_BASE_ADDRESS) + 0x80000U) +#endif + +#define CFG_FLASH_END ((uint32_t) ((CONFIG_FLASH_BASE_ADDRESS) + ((CONFIG_FLASH_SIZE) *1024))) + +#endif /* _AVM_DEVCFG_H_ */ diff --git a/src/platforms/zephyr/src/lib/avm_log.h b/src/platforms/zephyr/src/lib/avm_log.h new file mode 100644 index 000000000..1f75956af --- /dev/null +++ b/src/platforms/zephyr/src/lib/avm_log.h @@ -0,0 +1,112 @@ +/* This file is part of AtomVM. + * + * Copyright 2023 Winford (Uncle Grumpy) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later + */ + +#ifndef _AVM_LOG_H_ +#define _AVM_LOG_H_ + +#include +#include + +#include + +enum AVMLogLevel +{ + LOG_NONE = 0, + LOG_ERROR, + LOG_WARN, + LOG_INFO, + LOG_DEBUG +}; + +#ifndef AVM_LOG_DISABLE + +#ifdef AVM_LOG_LEVEL_MAX +#define AVM_MAX_LOG AVM_LOG_LEVEL_MAX +#else +#define AVM_MAX_LOG LOG_INFO +#endif + +/* clang-format off */ +#ifdef ENABLE_LOG_COLOR +// #define LOG_COLOR_BLACK "\033[0;30m" +// #define LOG_COLOR_GREY "\033[1;30m" +// #define LOG_COLOR_RED "\033[0;31m" +// #define LOG_BOLD_RED "\033[1;31m" +// #define LOG_COLOR_GREEN "\033[0;32m" +// #define LOG_COLOR_ALIEN "\033[1;32m" +// #define LOG_COLOR_ORANGE "\033[0;33m" +// #define LOG_COLOR_YELLOW "\033[1;33m" +// #define LOG_COLOR_BLUE "\033[0;34m" +// #define LOG_COLOR_PURPLE "\033[0;35m" +// #define LOG_COLOR_PINK "\033[1;35m" +// #define LOG_COLOR_CYAN "\033[0;36m" +#define LOG_COLOR_ERROR "\033[1;31m" +#define LOG_COLOR_WARN "\033[0;33m" +#define LOG_COLOR_INFO "\033[0;32m" +#define LOG_COLOR_DEBUG "\033[0;34m" +#define LOG_RESET_COLOR "\033[0m" +#else /* ENABLE_LOG_COLOR OFF */ +#define LOG_COLOR_ERROR +#define LOG_COLOR_WARN +#define LOG_COLOR_INFO +#define LOG_COLOR_DEBUG +#define LOG_RESET_COLOR +#endif /* ENABLE_LOG_COLOR */ + +#ifdef ENABLE_LOG_LINE_INFO +#define LINE_FORMAT " (%s:%i)" +#define LINE_DATA , __FILE__, __LINE__ +#else /* ENABLE_LOG_LINE_INFO OFF*/ +#define LINE_FORMAT +#define LINE_DATA +#endif /* ENABLE_LOG_LINE_INFO */ +/* clang-format on */ + +#define AVM_LOGE(tag, format, ...) \ + do { \ + uint64_t log_timestamp = sys_monotonic_time_u64(); \ + if (AVM_MAX_LOG >= LOG_ERROR) \ + printf(LOG_COLOR_ERROR "ERROR [%llu] %s: " format " (%s:%i)" LOG_RESET_COLOR "\n", log_timestamp, tag, ##__VA_ARGS__, __FILE__, __LINE__); \ + } while (0) +#define AVM_LOGW(tag, format, ...) \ + do { \ + uint64_t log_timestamp = sys_monotonic_time_u64(); \ + if (AVM_MAX_LOG >= LOG_WARN) \ + printf(LOG_COLOR_WARN "WARN [%llu] %s: " format LINE_FORMAT LOG_RESET_COLOR "\n", log_timestamp, tag, ##__VA_ARGS__ LINE_DATA); \ + } while (0) +#define AVM_LOGI(tag, format, ...) \ + do { \ + uint64_t log_timestamp = sys_monotonic_time_u64(); \ + if (AVM_MAX_LOG >= LOG_INFO) \ + printf(LOG_COLOR_INFO "INFO [%llu] %s: " format LINE_FORMAT LOG_RESET_COLOR "\n", log_timestamp, tag, ##__VA_ARGS__ LINE_DATA); \ + } while (0) +#define AVM_LOGD(tag, format, ...) \ + do { \ + uint64_t log_timestamp = sys_monotonic_time_u64(); \ + if (AVM_MAX_LOG >= LOG_DEBUG) \ + printf(LOG_COLOR_DEBUG "DEBUG [%llu] %s: " format LINE_FORMAT LOG_RESET_COLOR "\n", log_timestamp, tag, ##__VA_ARGS__ LINE_DATA); \ + } while (0) +#else +#define AVM_LOGE(tag, format, ...) +#define AVM_LOGW(tag, format, ...) +#define AVM_LOGI(tag, format, ...) +#define AVM_LOGD(tag, format, ...) +#endif /* AVM_LOG_DISABLE */ + +#endif /* _AVM_LOG_H_ */ diff --git a/src/platforms/zephyr/src/lib/platform_nifs.c b/src/platforms/zephyr/src/lib/platform_nifs.c new file mode 100644 index 000000000..4c7ddf060 --- /dev/null +++ b/src/platforms/zephyr/src/lib/platform_nifs.c @@ -0,0 +1,55 @@ +/* + * This file is part of AtomVM. + * + * Copyright 2019 Fred Dushin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later + */ + +#include +#include +#include +#include + +// #define ENABLE_TRACE +#include + +#include "zephyros_sys.h" + +static term nif_atomvm_platform(Context *ctx, int argc, term argv[]) +{ + UNUSED(ctx); + UNUSED(argc); + UNUSED(argv); + return globalcontext_make_atom(ctx->global, ATOM_STR("\x6", "zephyr")); +} + +static const struct Nif atomvm_platform_nif = { + .base.type = NIFFunctionType, + .nif_ptr = nif_atomvm_platform +}; + +const struct Nif *platform_nifs_get_nif(const char *nifname) +{ + if (strcmp("atomvm:platform/0", nifname) == 0) { + TRACE("Resolved platform nif %s ...\n", nifname); + return &atomvm_platform_nif; + } + const struct Nif *nif = nif_collection_resolve_nif(nifname); + if (nif) { + return nif; + } + return NULL; +} diff --git a/src/platforms/zephyr/src/lib/sys.c b/src/platforms/zephyr/src/lib/sys.c new file mode 100644 index 000000000..25bad6b27 --- /dev/null +++ b/src/platforms/zephyr/src/lib/sys.c @@ -0,0 +1,235 @@ +/* + * This file is part of AtomVM. + * + * Copyright 2023 Winford (Uncle Grumpy]) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later + */ + +#include +#include +#include +#include +// #define ENABLE_TRACE +#include + +#include +#include + +#include "avm_log.h" +#include "zephyros_sys.h" + +#define TAG "sys" + +struct PortDriverDefListItem *port_driver_list; +struct NifCollectionDefListItem *nif_collection_list; + +static inline void sys_clock_gettime(struct timespec *t) +{ + uint64_t now = sys_monotonic_time_u64(); + t->tv_sec = (time_t) now / 1000; + t->tv_nsec = ((int32_t) now % 1000) * 1000000; +} + +static int32_t timespec_diff_to_ms(struct timespec *timespec1, struct timespec *timespec2) +{ + return (int32_t) ((timespec1->tv_sec - timespec2->tv_sec) * 1000 + (timespec1->tv_nsec - timespec2->tv_nsec) / 1000000); +} + +/* TODO: Needed because `defaultatoms_init` in libAtomVM/defaultatoms.c calls this function. + * We should be able to remove this after `platform_defaulatoms.{c,h}` are removed on all platforms + * and `defaultatoms_init` is no longer called. + */ +void platform_defaultatoms_init(GlobalContext *glb) +{ + UNUSED(glb); +} + +void sys_init_platform(GlobalContext *glb) +{ + UNUSED(glb); +} + +void sys_free_platform(GlobalContext *glb) +{ + UNUSED(glb); +} + +void sys_poll_events(GlobalContext *glb, int timeout_ms) +{ + UNUSED(glb); + UNUSED(timeout_ms); +} + +void sys_listener_destroy(struct ListHead *item) +{ + UNUSED(item); +} + +void sys_register_select_event(GlobalContext *global, ErlNifEvent event, bool is_write) +{ + UNUSED(global); + UNUSED(event); + UNUSED(is_write); +} + +void sys_unregister_select_event(GlobalContext *global, ErlNifEvent event, bool is_write) +{ + UNUSED(global); + UNUSED(event); + UNUSED(is_write); +} + +void sys_time(struct timespec *t) +{ + sys_clock_gettime(t); +} + +void sys_monotonic_time(struct timespec *t) +{ + sys_clock_gettime(t); +} + +uint64_t sys_monotonic_time_u64() +{ + return k_uptime_get(); +} + +uint64_t sys_monotonic_time_ms_to_u64(uint64_t ms) +{ + return ms; +} + +uint64_t sys_monotonic_time_u64_to_ms(uint64_t t) +{ + return t; +} + +enum OpenAVMResult sys_open_avm_from_file( + GlobalContext *global, const char *path, struct AVMPackData **data) +{ + TRACE("sys_open_avm_from_file: Going to open: %s\n", path); + + // TODO + AVM_LOGW(TAG, "Open from file not supported on this platform."); + return AVM_OPEN_NOT_SUPPORTED; +} + +Module *sys_load_module_from_file(GlobalContext *global, const char *path) +{ + // TODO + return NULL; +} + +Module *sys_load_module(GlobalContext *global, const char *module_name) +{ + const void *beam_module = NULL; + uint32_t beam_module_size = 0; + + struct ListHead *avmpack_data_list = synclist_rdlock(&global->avmpack_data); + struct ListHead *item; + LIST_FOR_EACH (item, avmpack_data_list) { + struct AVMPackData *avmpack_data = GET_LIST_ENTRY(item, struct AVMPackData, avmpack_head); + avmpack_data->in_use = true; + if (avmpack_find_section_by_name(avmpack_data->data, module_name, &beam_module, &beam_module_size)) { + break; + } + } + synclist_unlock(&global->avmpack_data); + + if (IS_NULL_PTR(beam_module)) { + AVM_LOGE(TAG, "Failed to open module: %s.", module_name); + return NULL; + } + + Module *new_module = module_new_from_iff_binary(global, beam_module, beam_module_size); + new_module->module_platform_data = NULL; + + return new_module; +} + +Context *sys_create_port(GlobalContext *glb, const char *driver_name, term opts) +{ + Context *new_ctx = port_driver_create_port(driver_name, glb, opts); + if (IS_NULL_PTR(new_ctx)) { + AVM_LOGE(TAG, "Failed to load port \"%s\". Ensure the port is configured properly in the build.", driver_name); + new_ctx = NULL; + } + return new_ctx; +} + +term sys_get_info(Context *ctx, term key) +{ + return UNDEFINED_ATOM; +} + +void port_driver_init_all(GlobalContext *global) +{ + for (struct PortDriverDefListItem *item = port_driver_list; item != NULL; item = item->next) { + if (item->def->port_driver_init_cb) { + item->def->port_driver_init_cb(global); + } + } +} + +void port_driver_destroy_all(GlobalContext *global) +{ + for (struct PortDriverDefListItem *item = port_driver_list; item != NULL; item = item->next) { + if (item->def->port_driver_destroy_cb) { + item->def->port_driver_destroy_cb(global); + } + } +} + +static Context *port_driver_create_port(const char *port_name, GlobalContext *global, term opts) +{ + for (struct PortDriverDefListItem *item = port_driver_list; item != NULL; item = item->next) { + if (strcmp(port_name, item->def->port_driver_name) == 0) { + return item->def->port_driver_create_port_cb(global, opts); + } + } + + return NULL; +} + +void nif_collection_init_all(GlobalContext *global) +{ + for (struct NifCollectionDefListItem *item = nif_collection_list; item != NULL; item = item->next) { + if (item->def->nif_collection_init_cb) { + item->def->nif_collection_init_cb(global); + } + } +} + +void nif_collection_destroy_all(GlobalContext *global) +{ + for (struct NifCollectionDefListItem *item = nif_collection_list; item != NULL; item = item->next) { + if (item->def->nif_collection_destroy_cb) { + item->def->nif_collection_destroy_cb(global); + } + } +} + +const struct Nif *nif_collection_resolve_nif(const char *name) +{ + for (struct NifCollectionDefListItem *item = nif_collection_list; item != NULL; item = item->next) { + const struct Nif *res = item->def->nif_collection_resolve_nif_cb(name); + if (res) { + return res; + } + } + + return NULL; +} diff --git a/src/platforms/zephyr/src/libAtomVM/CMakeLists.txt b/src/platforms/zephyr/src/libAtomVM/CMakeLists.txt new file mode 100644 index 000000000..8eaa221a0 --- /dev/null +++ b/src/platforms/zephyr/src/libAtomVM/CMakeLists.txt @@ -0,0 +1,174 @@ +# +# This file is part of AtomVM. +# +# Copyright 2020 Davide Bettio +# Copyright 2020 Fred Dushin +# Copyright 2023 Adolfo E. GarcĂ­a +# Copyright 2023 Winford (Uncle Grumpy) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later +# + +cmake_minimum_required(VERSION 3.20) +zephyr_library_named(libAtomVM) +project(libAtomVM) + +function(gperf_generate input output) + add_custom_command( + OUTPUT ${output} + COMMAND gperf -t ${input} > ${output} + DEPENDS ${input} + COMMENT "Hashing ${input}" + ) +endfunction() + +set(ATOMVM_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../../../../) + +gperf_generate(${ATOMVM_DIR}/src/libAtomVM/bifs.gperf bifs_hash.h) +gperf_generate(${ATOMVM_DIR}/src/libAtomVM/nifs.gperf nifs_hash.h) + +add_custom_target(generated DEPENDS bifs_hash.h) +add_custom_target(generated-nifs-hash DEPENDS nifs_hash.h) + +include(${ATOMVM_DIR}/version.cmake) + +if (ATOMVM_DEV) + set(ATOMVM_GIT_REVISION "") + execute_process( + COMMAND git rev-parse --short HEAD + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE ATOMVM_GIT_REVISION + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if (NOT ATOMVM_GIT_REVISION STREQUAL "") + set(ATOMVM_VERSION "${ATOMVM_BASE_VERSION}+git.${ATOMVM_GIT_REVISION}") + else() + set(ATOMVM_VERSION ${ATOMVM_BASE_VERSION}) + endif() +else() + set(ATOMVM_VERSION ${ATOMVM_BASE_VERSION}) +endif() + +configure_file(${ATOMVM_DIR}/src/libAtomVM/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/avm_version.h) +add_custom_target(version DEPENDS avm_version.h) + +target_include_directories(libAtomVM PRIVATE + ${CMAKE_CURRENT_BINARY_DIR} + ${ATOMVM_DIR}/src/libAtomVM/ +) + +set(SOURCE_FILES + ${ATOMVM_DIR}/src/libAtomVM/atom.c + ${ATOMVM_DIR}/src/libAtomVM/atomshashtable.c + ${ATOMVM_DIR}/src/libAtomVM/avmpack.c + ${ATOMVM_DIR}/src/libAtomVM/bif.c + ${ATOMVM_DIR}/src/libAtomVM/bitstring.c + ${ATOMVM_DIR}/src/libAtomVM/context.c + ${ATOMVM_DIR}/src/libAtomVM/debug.c + ${ATOMVM_DIR}/src/libAtomVM/defaultatoms.c + ${ATOMVM_DIR}/src/libAtomVM/dictionary.c + ${ATOMVM_DIR}/src/libAtomVM/externalterm.c + ${ATOMVM_DIR}/src/libAtomVM/globalcontext.c + ${ATOMVM_DIR}/src/libAtomVM/iff.c + ${ATOMVM_DIR}/src/libAtomVM/interop.c + ${ATOMVM_DIR}/src/libAtomVM/mailbox.c + ${ATOMVM_DIR}/src/libAtomVM/memory.c + ${ATOMVM_DIR}/src/libAtomVM/module.c + ${ATOMVM_DIR}/src/libAtomVM/nifs.c + ${ATOMVM_DIR}/src/libAtomVM/port.c + ${ATOMVM_DIR}/src/libAtomVM/posix_nifs.c + ${ATOMVM_DIR}/src/libAtomVM/refc_binary.c + ${ATOMVM_DIR}/src/libAtomVM/resources.c + ${ATOMVM_DIR}/src/libAtomVM/scheduler.c + ${ATOMVM_DIR}/src/libAtomVM/stacktrace.c + ${ATOMVM_DIR}/src/libAtomVM/term.c + ${ATOMVM_DIR}/src/libAtomVM/timer_list.c + ${ATOMVM_DIR}/src/libAtomVM/valueshashtable.c +) + +target_sources(libAtomVM PRIVATE ${SOURCE_FILES}) + +add_dependencies(libAtomVM generated generated-nifs-hash version) + +if (CONFIG_ATOMVM_ADVANCED_TRACING) + zephyr_library_compile_definitions(ENABLE_ADVANCED_TRACE) +endif() + +if (CONFIG_ATOMVM_DISABLE_SMP) + zephyr_library_compile_definitions(AVM_NO_SMP) + add_compile_definitions(AVM_NO_SMP) +else() + include(CheckIncludeFile) + CHECK_INCLUDE_FILE(stdatomic.h STDATOMIC_INCLUDE) + if (HAVE_PLATFORM_SMP_H) + zephyr_library_compile_definitions(HAVE_PLATFORM_SMP_H) + endif() + include(CheckCSourceCompiles) + check_c_source_compiles(" + #include + int main() { + _Static_assert(ATOMIC_POINTER_LOCK_FREE == 2, \"Expected ATOMIC_POINTER_LOCK_FREE to be equal to 2\"); + } + " ATOMIC_POINTER_LOCK_FREE_IS_TWO) + if(NOT ATOMIC_POINTER_LOCK_FREE_IS_TWO AND NOT HAVE_PLATFORM_SMP_H) + if(NOT STDATOMIC_INCLUDE) + message(FATAL_ERROR "stdatomic.h cannot be found, you need to disable SMP on this platform or provide platform_smp.h and define HAVE_PLATFORM_SMP_H") + else() + message(FATAL_ERROR "Platform doesn't support atomic pointers, you need to disable SMP or provide platform_smp.h and define HAVE_PLATFORM_SMP_H") + endif() + endif() +endif() + +if (CONFIG_ATOMVM_USE_32BIT_FLOAT) + zephyr_library_compile_definitions(AVM_USE_SINGLE_PRECISION) +endif() + +if (CONFIG_ATOMVM_VERBOSE_ABORT) + zephyr_library_compile_definitions(AVM_VERBOSE_ABORT) +endif() + +if (CONFIG_ATOMVM_CREATE_STACKTRACES) + zephyr_library_compile_definitions(AVM_CREATE_STACKTRACES) +endif() + +include(DefineIfExists) +# HAVE_OPEN & HAVE_CLOSE are used in globalcontext.h +define_if_function_exists(libAtomVM open "zephyr/posix/fcntl.h" PUBLIC HAVE_OPEN) +define_if_function_exists(libAtomVM close "zephyr/posix/unistd.h" PUBLIC HAVE_CLOSE) +define_if_function_exists(libAtomVM mkfifo "zephyr/posix/sys/stat.h" PRIVATE HAVE_MKFIFO) +define_if_function_exists(libAtomVM unlink "zephyr/posix/unistd.h" PRIVATE HAVE_UNLINK) +define_if_symbol_exists(libAtomVM O_CLOEXEC "zephyr/posix/fcntl.h" PRIVATE HAVE_O_CLOEXEC) +define_if_symbol_exists(libAtomVM O_DIRECTORY "zephyr/posix/fcntl.h" PRIVATE HAVE_O_DIRECTORY) +define_if_symbol_exists(libAtomVM O_DSYNC "zephyr/posix/fcntl.h" PRIVATE HAVE_O_DSYNC) +define_if_symbol_exists(libAtomVM O_EXEC "zephyr/posix/fcntl.h" PRIVATE HAVE_O_EXEC) +define_if_symbol_exists(libAtomVM O_NOFOLLOW "zephyr/posix/fcntl.h" PRIVATE HAVE_O_NOFOLLOW) +define_if_symbol_exists(libAtomVM O_RSYNC "zephyr/posix/fcntl.h" PRIVATE HAVE_O_RSYNC) +define_if_symbol_exists(libAtomVM O_SEARCH "zephyr/posix/fcntl.h" PRIVATE HAVE_O_SEARCH) +define_if_symbol_exists(libAtomVM O_TTY_INIT "zephyr/posix/fcntl.h" PRIVATE HAVE_O_TTY_INIT) +define_if_symbol_exists(libAtomVM clock_settime "zephyr/posix/time.h" PRIVATE HAVE_CLOCK_SETTIME) +define_if_symbol_exists(libAtomVM settimeofday "zephyr/posix/sys/time.h" PRIVATE HAVE_SETTIMEOFDAY) +define_if_symbol_exists(libAtomVM socket "zephyr/posix/sys/socket.h" PUBLIC HAVE_SOCKET) +define_if_symbol_exists(libAtomVM select "zephyr/posix/sys/select.h" PUBLIC HAVE_SELECT) + +if(CMAKE_COMPILER_IS_GNUCC) + target_compile_options(libAtomVM PUBLIC -Wall -pedantic -Wextra -ggdb) +endif() + +set_property(TARGET libAtomVM PROPERTY C_STANDARD 11) + +if (COVERAGE) + include(CodeCoverage) + append_coverage_compiler_flags_to_target(libAtomVM) +endif() diff --git a/src/platforms/zephyr/src/main.c b/src/platforms/zephyr/src/main.c new file mode 100644 index 000000000..4ac04ed7a --- /dev/null +++ b/src/platforms/zephyr/src/main.c @@ -0,0 +1,134 @@ + +/* + * This file is part of AtomVM. + * + * Copyright 2023 Winford (Uncle Grumpy) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later + */ + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "lib/avm_devcfg.h" +#include "lib/avm_log.h" +#include "lib/zephyros_sys.h" + +#define AVM_ADDRESS (AVM_APP_ADDRESS) +#define AVM_FLASH_END (CFG_FLASH_END) + +#define TAG "AtomVM" + +#define ATOMVM_BANNER \ + "\n" \ + " ###########################################################\n" \ + "\n" \ + " ### ######## ####### ## ## ## ## ## ## \n" \ + " ## ## ## ## ## ### ### ## ## ### ### \n" \ + " ## ## ## ## ## #### #### ## ## #### #### \n" \ + " ## ## ## ## ## ## ### ## ## ## ## ### ## \n" \ + " ######### ## ## ## ## ## ## ## ## ## \n" \ + " ## ## ## ## ## ## ## ## ## ## ## \n" \ + " ## ## ## ####### ## ## ### ## ## \n" \ + "\n" \ + " ###########################################################\n" \ + "\n" + +int main() +{ + + GlobalContext *glb = globalcontext_new(); + + printk("%s", ATOMVM_BANNER); + AVM_LOGI(TAG, "Starting AtomVM revision %s", ATOMVM_VERSION); + + const void *flashed_avm = (void *) AVM_ADDRESS; + uint32_t size = (AVM_FLASH_END - AVM_ADDRESS); + + uint32_t startup_beam_size; + const void *startup_beam; + const char *startup_module_name; + + AVM_LOGD(TAG, "Maximum application size: %lu KiB", (size / 1024)); + + port_driver_init_all(glb); + nif_collection_init_all(glb); + + if (!avmpack_is_valid(flashed_avm, size) || !avmpack_find_section_by_flag(flashed_avm, BEAM_START_FLAG, &startup_beam, &startup_beam_size, &startup_module_name)) { + AVM_LOGE(TAG, "Invalid AVM Pack"); + AVM_ABORT(); + } + AVM_LOGD(TAG, "Booting file mapped at: %p, size: %lu", flashed_avm, startup_beam_size); + AVM_LOGI(TAG, "Application size: %lu B, free flash space: %lu KiB", startup_beam_size, ((size - startup_beam_size) / 1024)); + + struct ConstAVMPack *avmpack_data = malloc(sizeof(struct ConstAVMPack)); + if (IS_NULL_PTR(avmpack_data)) { + AVM_LOGE(TAG, "Memory error: Cannot allocate AVMPackData."); + AVM_ABORT(); + } + avmpack_data_init(&avmpack_data->base, &const_avm_pack_info); + avmpack_data->base.data = flashed_avm; + avmpack_data->base.in_use = true; + synclist_append(&glb->avmpack_data, &avmpack_data->base.avmpack_head); + + Module *mod = module_new_from_iff_binary(glb, startup_beam, startup_beam_size); + globalcontext_insert_module(glb, mod); + Context *ctx = context_new(glb); + ctx->leader = 1; + + printk("Starting: %s...\n", startup_module_name); + printk("---\n"); + + context_execute_loop(ctx, mod, "start", 0); + + term ret_value = ctx->x[0]; + char *ret_atom_string = interop_atom_to_string(ctx, ret_value); + if (ret_atom_string != NULL) { + AVM_LOGI(TAG, "Exited with return: %s", ret_atom_string); + } else { + AVM_LOGI(TAG, "Exited with return value: %lx", (long) term_to_int32(ret_value)); + } + free(ret_atom_string); + + bool reboot_on_not_ok = +#if defined(CONFIG_REBOOT_ON_NOT_OK) + CONFIG_REBOOT_ON_NOT_OK ? true : false; +#else + false; +#endif + if (reboot_on_not_ok && ret_value != OK_ATOM) { + AVM_LOGE(TAG, "AtomVM application terminated with non-ok return value. Rebooting ..."); + sys_reboot(SYS_REBOOT_COLD); + } else { + AVM_LOGI(TAG, "AtomVM application terminated. Going to sleep forever ..."); + while (1) { + ; + } + } + return 0; +} diff --git a/src/platforms/zephyr/west.yml b/src/platforms/zephyr/west.yml new file mode 100644 index 000000000..060baf557 --- /dev/null +++ b/src/platforms/zephyr/west.yml @@ -0,0 +1,21 @@ +# Copyright (c) 2021 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +manifest: + remotes: + - name: zephyrproject-rtos + url-base: https://github.com/zephyrproject-rtos + defaults: + remote: zephyrproject-rtos + projects: + - name: zephyr + remote: zephyrproject-rtos + revision: v3.5.0 + import: + # By using name-allowlist we can clone only the modules that are + # strictly needed by the application. + name-allowlist: + - cmsis # required by the ARM port + - stm32 # required by STM32 boards + self: + path: .