AtomVM icon indicating copy to clipboard operation
AtomVM copied to clipboard

Port to Zephyr OS

Open UncleGrumpy opened this issue 2 years ago • 8 comments

Setup a freestanding west application project for Zephyr SDK.

These changes are made under both the "Apache 2.0" and the "GNU Lesser General Public License 2.1 or later" license terms (dual license).

SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later

UncleGrumpy avatar Nov 22 '23 01:11 UncleGrumpy

avm_version.h PR (#959) has been merged, let's rebase this PR.

bettio avatar Nov 25 '23 12:11 bettio

I'm adding a first list of goals, in order to keep this PR as focused as possible.

Zephyr port must not implement all the features and peripherals, but it should rather focus on general scaffolding and the basics.

  • [ ] Starting from first PR CI should be running also for Zephyr
  • [ ] native_sim must be supported (and it should be likely used on CI)
  • [ ] qemu x86 should be supported in order to avoid leaking ARM specific details into our Zephyr port

bettio avatar Nov 25 '23 13:11 bettio

These sound like a reasonable starting point for this port. I was definitely not planning on taking out of "draft" until the there is a CI workflow to test the port. I believe native_sim is already working, but I have not attempted a qemu build yet.

UncleGrumpy avatar Nov 25 '23 16:11 UncleGrumpy

After investigating a bit further native_sim will require a separate port from one intended to run on microcontrollers, the reason being that MCU must be able to load the application from a flash address. But creating a west project build structure for our current generic_unix port should be trivial, after we solve the standard c11 issue.

UncleGrumpy avatar Nov 25 '23 22:11 UncleGrumpy

@bettio @UncleGrumpy would modifying src/libAtomVM/CMakeLists.txt like below be a good alternative to avoid code duplication?

cmake_minimum_required (VERSION 3.13)

project(libAtomVM C)

set(HEADER_FILES
    atom.h
    atomshashtable.h
    avmpack.h
    bif.h
    bitstring.h
    context.h
    debug.h
    defaultatoms.h
    dictionary.h
    erl_nif.h
    erl_nif_priv.h
    exportedfunction.h
    externalterm.h
    globalcontext.h
    iff.h
    interop.h
    list.h
    listeners.h
    mailbox.h
    memory.h
    module.h
    opcodes.h
    opcodesswitch.h
    overflow_helpers.h
    nifs.h
    platform_nifs.h
    port.h
    posix_nifs.h
    refc_binary.h
    resources.h
    scheduler.h
    smp.h
    synclist.h
    stacktrace.h
    sys.h
    term_typedef.h
    term.h
    timer_list.h
    trace.h
    utils.h
    valueshashtable.h
    ${CMAKE_CURRENT_BINARY_DIR}/avm_version.h
)

set(SOURCE_FILES
    atom.c
    atomshashtable.c
    avmpack.c
    bif.c
    bitstring.c
    context.c
    debug.c
    defaultatoms.c
    dictionary.c
    externalterm.c
    globalcontext.c
    iff.c
    interop.c
    mailbox.c
    memory.c
    module.c
    nifs.c
    port.c
    posix_nifs.c
    refc_binary.c
    resources.c
    scheduler.c
    stacktrace.c
    term.c
    timer_list.c
    valueshashtable.c
)

set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -Werror=unknown-pragmas")

include(CheckCSourceCompiles)

check_c_source_compiles("
     #include <fenv.h>
     int main() {
         #pragma STDC FENV_ACCESS ON
        return 0;
     }
 " HAVE_PRAGMA_STDC_FENV_ACCESS FAIL_REGEX FENV_ACCESS)

 check_c_source_compiles("
 #include <stdatomic.h>
 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 AVM_PORT_ZEPHYR)
    add_library(libAtomVM ${SOURCE_FILES} ${HEADER_FILES})
    target_include_directories(libAtomVM PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
    target_link_libraries(libAtomVM PUBLIC m)
    target_compile_features(libAtomVM PUBLIC c_std_11)
    if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
        target_compile_options(libAtomVM PUBLIC -Wall -pedantic -Wextra -ggdb -Werror=incompatible-pointer-types)
    elseif (CMAKE_C_COMPILER_ID MATCHES "Clang")
        target_compile_options(libAtomVM PUBLIC -Wall --extra-warnings -Werror=incompatible-pointer-types)
    endif()
else()
    zephyr_library_named(libAtomVM)
    zephyr_library_sources(${SOURCE_FILES} ${HEADER_FILES})
    zephyr_include_directories(${CMAKE_CURRENT_SOURCE_DIR})
    zephyr_library_compile_features(c_std_11)
endif()

# AtomVM options used in libAtomVM
if (ADVANCED_TRACING)
    if(NOT AVM_PORT_ZEPHYR)
        target_compile_definitions(libAtomVM PUBLIC ENABLE_ADVANCED_TRACE)
    else()
        zephyr_compile_definitions(ENABLE_ADVANCED_TRACE)
    endif()
endif()

if (HAVE_PRAGMA_STDC_FENV_ACCESS)
    if(NOT AVM_PORT_ZEPHYR)
        target_compile_definitions(libAtomVM PUBLIC HAVE_PRAGMA_STDC_FENV_ACCESS)
    else()
        zephyr_compile_definitions(HAVE_PRAGMA_STDC_FENV_ACCESS)
    endif()
endif()

if(${CMAKE_C_FLAGS} MATCHES -DAVM_NO_SMP)
    message("SMP is disabled by CFLAGS environment")
    set(AVM_DISABLE_SMP ON)
endif()

if(NOT AVM_PORT_ZEPHYR)
    if (AVM_DISABLE_SMP)
        target_compile_definitions(libAtomVM PUBLIC AVM_NO_SMP)
    else()
        include(CheckIncludeFile)
        CHECK_INCLUDE_FILE(stdatomic.h STDATOMIC_INCLUDE)
        if(HAVE_PLATFORM_SMP_H)
            target_compile_definitions(libAtomVM PUBLIC HAVE_PLATFORM_SMP_H)
        endif()
        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()
else()
    if(AVM_DISABLE_SMP)
        zephyr_compile_definitions(AVM_NO_SMP)
    else()
        if(CONFIG_SMP)
            zephyr_compile_definitions(HAVE_PLATFORM_SMP_H)
        endif()
    endif()
endif()

if (AVM_USE_32BIT_FLOAT)
    if(NOT AVM_PORT_ZEPHYR)
        target_compile_definitions(libAtomVM PUBLIC AVM_USE_SINGLE_PRECISION)
    else()
        zephyr_compile_definitions(AVM_USE_SINGLE_PRECISION)
    endif()
endif()

if (AVM_VERBOSE_ABORT)
    if(NOT AVM_PORT_ZEPHYR)
        target_compile_definitions(libAtomVM PUBLIC AVM_VERBOSE_ABORT)
    else()
        zephyr_compile_definitions(AVM_VERBOSE_ABORT)
    endif()
endif()

if(AVM_CREATE_STACKTRACES)
    if(NOT AVM_PORT_ZEPHYR)
        target_compile_definitions(libAtomVM PUBLIC AVM_CREATE_STACKTRACES)
    else()
        zephyr_compile_definitions(AVM_CREATE_STACKTRACES)
    endif()
endif()

include(DefineIfExists)
# HAVE_OPEN & HAVE_CLOSE are used in globalcontext.h
if(NOT AVM_PORT_ZEPHYR)
    define_if_function_exists(libAtomVM open "fcntl.h" PUBLIC HAVE_OPEN)
    define_if_function_exists(libAtomVM close "unistd.h" PUBLIC HAVE_CLOSE)
    define_if_function_exists(libAtomVM mkfifo "sys/stat.h" PRIVATE HAVE_MKFIFO)
    define_if_function_exists(libAtomVM unlink "unistd.h" PRIVATE HAVE_UNLINK)
    define_if_symbol_exists(libAtomVM O_CLOEXEC "fcntl.h" PRIVATE HAVE_O_CLOEXEC)
    define_if_symbol_exists(libAtomVM O_DIRECTORY "fcntl.h" PRIVATE HAVE_O_DIRECTORY)
    define_if_symbol_exists(libAtomVM O_DSYNC "fcntl.h" PRIVATE HAVE_O_DSYNC)
    define_if_symbol_exists(libAtomVM O_EXEC "fcntl.h" PRIVATE HAVE_O_EXEC)
    define_if_symbol_exists(libAtomVM O_NOFOLLOW "fcntl.h" PRIVATE HAVE_O_NOFOLLOW)
    define_if_symbol_exists(libAtomVM O_RSYNC "fcntl.h" PRIVATE HAVE_O_RSYNC)
    define_if_symbol_exists(libAtomVM O_SEARCH "fcntl.h" PRIVATE HAVE_O_SEARCH)
    define_if_symbol_exists(libAtomVM O_TTY_INIT "fcntl.h" PRIVATE HAVE_O_TTY_INIT)
    define_if_symbol_exists(libAtomVM clock_settime "time.h" PRIVATE HAVE_CLOCK_SETTIME)
    define_if_symbol_exists(libAtomVM settimeofday "sys/time.h" PRIVATE HAVE_SETTIMEOFDAY)
    define_if_symbol_exists(libAtomVM socket "sys/socket.h" PUBLIC HAVE_SOCKET)
    define_if_symbol_exists(libAtomVM select "sys/select.h" PUBLIC HAVE_SELECT)
else()
    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)
endif()

# Automatically use zlib if present to load .beam files
if(NOT AVM_PORT_ZEPHYR)
    if (${CMAKE_SYSTEM_NAME} STREQUAL "Darwin" OR ${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
        find_package(ZLIB)
        if (ZLIB_FOUND)
            target_compile_definitions(libAtomVM PRIVATE WITH_ZLIB)
            target_link_libraries(libAtomVM PUBLIC ${ZLIB_LIBRARIES})
        endif(ZLIB_FOUND)
    endif()
endif()

function(gperf_generate input output)
    add_custom_command(
        OUTPUT ${output}
        COMMAND gperf -t ${input} > ${output}
        DEPENDS ${input}
        COMMENT "Hashing ${input}"
    )
endfunction()

gperf_generate(${CMAKE_CURRENT_SOURCE_DIR}/bifs.gperf bifs_hash.h)
gperf_generate(${CMAKE_CURRENT_SOURCE_DIR}/nifs.gperf nifs_hash.h)

add_custom_target(generated DEPENDS bifs_hash.h)
add_custom_target(generated-nifs-hash DEPENDS nifs_hash.h)

if(NOT AVM_PORT_ZEPHYR)
    add_dependencies(libAtomVM generated generated-nifs-hash)
else()
    zephyr_library_add_dependencies(generated generated-nifs-hash)
endif()

include(../../version.cmake)

if (ATOMVM_DEV)
    set(ATOMVM_GIT_REVISION "<unknown>")
    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()

# Add include to directory where avm_version.h is generated so targets linking
# libAtomVM can access it
if(NOT AVM_PORT_ZEPHYR)
    target_include_directories(libAtomVM PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
else()
    zephyr_include_directories(${CMAKE_CURRENT_BINARY_DIR})
endif()
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/avm_version.h)

if(NOT AVM_PORT_ZEPHYR)
    if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Generic")
        target_link_libraries(libAtomVM PUBLIC libAtomVM${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR})
    endif()
endif()

if (COVERAGE)
    include(CodeCoverage)
    append_coverage_compiler_flags_to_target(libAtomVM)
endif()

adolfogc avatar Nov 26 '23 03:11 adolfogc

That looks like it might be a better solution, except that using zephyr_library_compile_features to set C11 standard results in an error from, cmake:

Unknown CMake command "zephyr_library_compile_features"

I still have not found any way set the standard, including the recommended solution in their documentation setting: set_property(GLOBAL PROPERTY CSTD c11) or set_property(GLOBAL PROPERTY CSTD iso9899:2011) before find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) in CMakeLists.txt. Using either of these still results in warnings about features introduced in standard C and C99.

UncleGrumpy avatar Nov 26 '23 16:11 UncleGrumpy

@UncleGrumpy The target name remains the same, can you try this instead:

target_compile_features(libAtomVM PUBLIC c_std_11)

adolfogc avatar Nov 26 '23 17:11 adolfogc

Then we are back to the original problem encountered when including libAtomVM that @bettio opened a issue on here: https://github.com/zephyrproject-rtos/zephyr/discussions/65704

The error is:

CMake Error in /home/winford/zephyrproject/zephyr/CMakeLists.txt:
  No known features for C compiler

  "GNU"

  version 12.2.0.

UncleGrumpy avatar Nov 26 '23 17:11 UncleGrumpy