diff --git a/.github/workflows/build-rti.yml b/.github/workflows/build-rti.yml index bcf714ea7..2561b7da0 100644 --- a/.github/workflows/build-rti.yml +++ b/.github/workflows/build-rti.yml @@ -4,7 +4,7 @@ on: workflow_call: jobs: - run: + native-build: strategy: matrix: platform: [ubuntu-latest, macos-latest, windows-latest] @@ -12,8 +12,24 @@ jobs: steps: - name: Check out reactor-c repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Build the RTI with AUTH=OFF run: .github/scripts/build-rti.sh -DAUTH=OFF - name: Build the RTI with AUTH=ON run: .github/scripts/build-rti.sh -DAUTH=ON + + docker-build: + runs-on: ubuntu-latest + steps: + - name: Check out reactor-c repository + uses: actions/checkout@v4 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Build Docker image + uses: docker/build-push-action@v6 + with: + file: ./core/federated/RTI/rti.Dockerfile + context: . + platforms: linux/amd64, linux/arm64, linux/arm/v7, linux/riscv64 + push: false + tags: lflang/rti:latest diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4ba3c90c3..dd12b4eff 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ on: concurrency: group: ci-${{ github.ref }}-${{ github.event_path }} - cancel-in-progress: ${{ github.ref != 'refs/heads/master' }} + cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} jobs: check-labels: @@ -97,4 +97,4 @@ jobs: compiler-ref: ${{ needs.fetch-lf.outputs.ref }} scheduler: ADAPTIVE all-platforms: ${{ !github.event.pull_request.draft || contains( github.event.pull_request.labels.*.name, 'mac') || contains( github.event.pull_request.labels.*.name, 'windows') }} - if: ${{ !github.event.pull_request.draft || contains( github.event.pull_request.labels.*.name, 'schedulers') }} \ No newline at end of file + if: ${{ !github.event.pull_request.draft || contains( github.event.pull_request.labels.*.name, 'schedulers') }} diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 3b1a6d0a0..04443d5ab 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -7,7 +7,7 @@ include(${LF_ROOT}/core/lf_utils.cmake) list(APPEND GENERAL_SOURCES tag.c clock.c port.c mixed_radix.c reactor_common.c lf_token.c environment.c) # Add tracing support if requested -if (DEFINED LF_TRACE) +if(DEFINED LF_TRACE) message(STATUS "Including sources specific to tracing.") list(APPEND GENERAL_SOURCES tracepoint.c) endif() @@ -16,7 +16,7 @@ endif() list(APPEND REACTORC_SOURCES ${GENERAL_SOURCES}) # Add sources for either threaded or single-threaded runtime -if (DEFINED FEDERATED) +if(DEFINED FEDERATED) include(federated/CMakeLists.txt) include(federated/network/CMakeLists.txt) endif() @@ -35,14 +35,14 @@ endif() # Add sources for the local RTI if we are using scheduling enclaves if(DEFINED LF_ENCLAVES) -include(federated/RTI/local_rti.cmake) + include(federated/RTI/local_rti.cmake) endif() # Include sources from subdirectories include(utils/CMakeLists.txt) -if (DEFINED MODAL_REACTORS) -include(modal_models/CMakeLists.txt) +if(DEFINED MODAL_REACTORS) + include(modal_models/CMakeLists.txt) endif() # Print sources used for compilation @@ -53,7 +53,7 @@ add_library(reactor-c) target_sources(reactor-c PRIVATE ${REACTORC_SOURCES}) lf_enable_compiler_warnings(reactor-c) -if (DEFINED LF_TRACE) +if(DEFINED LF_TRACE) include(${LF_ROOT}/trace/api/CMakeLists.txt) target_link_libraries(reactor-c PUBLIC lf::trace-api) # If the user specified an external trace plugin. Find it and link with it @@ -106,18 +106,19 @@ target_include_directories(reactor-c PUBLIC ../include/core/threaded) target_include_directories(reactor-c PUBLIC ../include/core/utils) target_include_directories(reactor-c PUBLIC federated/RTI/) -if (APPLE) - SET(CMAKE_C_ARCHIVE_CREATE " Scr ") +if(APPLE) + SET(CMAKE_C_ARCHIVE_CREATE " Scr ") SET(CMAKE_CXX_ARCHIVE_CREATE " Scr ") - SET(CMAKE_C_ARCHIVE_FINISH " -no_warning_for_no_symbols -c ") + SET(CMAKE_C_ARCHIVE_FINISH " -no_warning_for_no_symbols -c ") SET(CMAKE_CXX_ARCHIVE_FINISH " -no_warning_for_no_symbols -c ") endif() # Link with OpenSSL library if(DEFINED FEDERATED_AUTHENTICATED) - if (APPLE) + if(APPLE) set(OPENSSL_ROOT_DIR /usr/local/opt/openssl) endif() + find_package(OpenSSL REQUIRED) target_link_libraries(reactor-c PUBLIC OpenSSL::SSL) endif() @@ -130,10 +131,11 @@ if(DEFINED FEDERATED) endif() # Unless specified otherwise initial event queue and reaction queue to size 10 -if (NOT DEFINED INITIAL_EVENT_QUEUE_SIZE) +if(NOT DEFINED INITIAL_EVENT_QUEUE_SIZE) set(INITIAL_EVENT_QUEUE_SIZE 10) endif() -if (NOT DEFINED INITIAL_REACT_QUEUE_SIZE) + +if(NOT DEFINED INITIAL_REACT_QUEUE_SIZE) set(INITIAL_REACT_QUEUE_SIZE 10) endif() diff --git a/core/environment.c b/core/environment.c index d2d56a593..af192c09b 100644 --- a/core/environment.c +++ b/core/environment.c @@ -117,7 +117,10 @@ static void environment_init_modes(environment_t* env, int num_modes, int num_st * @brief Initialize the federation-specific parts of the environment struct. */ static void environment_init_federated(environment_t* env, int num_is_present_fields) { -#ifdef FEDERATED_DECENTRALIZED +#if defined(FEDERATED_CENTRALIZED) + env->need_to_send_LTC = false; + (void)num_is_present_fields; +#elif defined(FEDERATED_DECENTRALIZED) if (num_is_present_fields > 0) { env->_lf_intended_tag_fields = (tag_t**)calloc(num_is_present_fields, sizeof(tag_t*)); LF_ASSERT_NON_NULL(env->_lf_intended_tag_fields); diff --git a/core/federated/RTI/rti.Dockerfile b/core/federated/RTI/rti.Dockerfile index e70e34584..0103abde8 100644 --- a/core/federated/RTI/rti.Dockerfile +++ b/core/federated/RTI/rti.Dockerfile @@ -1,5 +1,5 @@ -# Docker file for building the image of the rti -FROM alpine:latest +ARG BASEIMAGE=alpine:latest +FROM ${BASEIMAGE} as builder COPY . /lingua-franca WORKDIR /lingua-franca/core/federated/RTI RUN set -ex && apk add --no-cache gcc musl-dev cmake make && \ @@ -9,5 +9,14 @@ RUN set -ex && apk add --no-cache gcc musl-dev cmake make && \ make && \ make install -# Use ENTRYPOINT not CMD so that command-line arguments go through -ENTRYPOINT ["RTI"] +WORKDIR /lingua-franca + +# application stage +FROM ${BASEIMAGE} as app +LABEL maintainer="lf-lang" +LABEL source="https://github.com/lf-lang/reactor-c/tree/main/core/federated/RTI" +COPY --from=builder /usr/local/bin/RTI /usr/local/bin/RTI + +WORKDIR /lingua-franca + +ENTRYPOINT ["/usr/local/bin/RTI"] diff --git a/core/federated/RTI/rti_common.c b/core/federated/RTI/rti_common.c index 3335812c4..f1229493f 100644 --- a/core/federated/RTI/rti_common.c +++ b/core/federated/RTI/rti_common.c @@ -64,7 +64,7 @@ void _logical_tag_complete(scheduling_node_t* enclave, tag_t completed) { enclave->completed = completed; - LF_PRINT_LOG("RTI received from federate/enclave %d the latest tag complete (LTC) " PRINTF_TAG ".", enclave->id, + LF_PRINT_LOG("RTI received from federate/enclave %d the latest tag confirmed (LTC) " PRINTF_TAG ".", enclave->id, enclave->completed.time - start_time, enclave->completed.microstep); // Check downstream scheduling_nodes to see whether they should now be granted a TAG. diff --git a/core/federated/RTI/rti_common.h b/core/federated/RTI/rti_common.h index d4e0c1236..615ca8a8a 100644 --- a/core/federated/RTI/rti_common.h +++ b/core/federated/RTI/rti_common.h @@ -199,7 +199,7 @@ void notify_provisional_tag_advance_grant(scheduling_node_t* e, tag_t tag); * If M is equal to the NET of the federate, then return PTAG(M). * * This should be called whenever an immediately upstream federate sends to - * the RTI an LTC (latest tag complete), or when a transitive upstream + * the RTI an LTC (latest tag confirmed), or when a transitive upstream * federate sends a NET (Next Event Tag) message. * It is also called when an upstream federate resigns from the federation. * diff --git a/core/federated/RTI/rti_remote.c b/core/federated/RTI/rti_remote.c index 11b7d2f03..507480b24 100644 --- a/core/federated/RTI/rti_remote.c +++ b/core/federated/RTI/rti_remote.c @@ -721,7 +721,7 @@ void handle_timed_message(federate_info_t* sending_federate, unsigned char* buff LF_MUTEX_UNLOCK(&rti_mutex); } -void handle_latest_tag_complete(federate_info_t* fed) { +void handle_latest_tag_confirmed(federate_info_t* fed) { unsigned char buffer[sizeof(int64_t) + sizeof(uint32_t)]; read_from_socket_fail_on_error(&fed->socket, sizeof(int64_t) + sizeof(uint32_t), buffer, NULL, "RTI failed to read the content of the logical tag complete from federate %d.", @@ -1485,8 +1485,8 @@ void* federate_info_thread_TCP(void* fed) { case MSG_TYPE_NEXT_EVENT_TAG: handle_next_event_tag(my_fed); break; - case MSG_TYPE_LATEST_TAG_COMPLETE: - handle_latest_tag_complete(my_fed); + case MSG_TYPE_LATEST_TAG_CONFIRMED: + handle_latest_tag_confirmed(my_fed); break; case MSG_TYPE_STOP_REQUEST: handle_stop_request_message(my_fed); // FIXME: Reviewed until here. diff --git a/core/federated/RTI/rti_remote.h b/core/federated/RTI/rti_remote.h index df86803de..6aa014559 100644 --- a/core/federated/RTI/rti_remote.h +++ b/core/federated/RTI/rti_remote.h @@ -263,14 +263,14 @@ void handle_port_absent_message(federate_info_t* sending_federate, unsigned char void handle_timed_message(federate_info_t* sending_federate, unsigned char* buffer); /** - * Handle a latest tag complete (LTC) message. @see - * MSG_TYPE_LATEST_TAG_COMPLETE in rti.h. + * Handle a latest tag confirmed (LTC) message. @see + * MSG_TYPE_LATEST_TAG_CONFIRMED in rti.h. * * This function assumes the caller does not hold the mutex. * * @param fed The federate that has completed a logical tag. */ -void handle_latest_tag_complete(federate_info_t* fed); +void handle_latest_tag_confirmed(federate_info_t* fed); /** * Handle a next event tag (NET) message. @see MSG_TYPE_NEXT_EVENT_TAG in rti.h. diff --git a/core/federated/federate.c b/core/federated/federate.c index 37ac7a4c1..10d94faf0 100644 --- a/core/federated/federate.c +++ b/core/federated/federate.c @@ -126,7 +126,7 @@ static void send_time(unsigned char type, instant_t time) { /** * Send a tag to the RTI. * This function acquires the lf_outbound_socket_mutex. - * @param type The message type (MSG_TYPE_NEXT_EVENT_TAG or MSG_TYPE_LATEST_TAG_COMPLETE). + * @param type The message type (MSG_TYPE_NEXT_EVENT_TAG or MSG_TYPE_LATEST_TAG_CONFIRMED). * @param tag The tag. */ static void send_tag(unsigned char type, tag_t tag) { @@ -1280,7 +1280,7 @@ static void handle_provisional_tag_advance_grant() { // TAG. In either case, we know that at the PTAG tag, all outputs // have either been sent or are absent, so we can send an LTC. // Send an LTC to indicate absent outputs. - lf_latest_tag_complete(PTAG); + lf_latest_tag_confirmed(PTAG); // Nothing more to do. LF_MUTEX_UNLOCK(&env->mutex); return; @@ -2226,14 +2226,21 @@ void* lf_handle_p2p_connections_from_federates(void* env_arg) { return NULL; } -void lf_latest_tag_complete(tag_t tag_to_send) { - int compare_with_last_tag = lf_tag_compare(_fed.last_sent_LTC, tag_to_send); - if (compare_with_last_tag >= 0) { +void lf_latest_tag_confirmed(tag_t tag_to_send) { + environment_t* env; + if (lf_tag_compare(_fed.last_sent_LTC, tag_to_send) >= 0) { + return; // Already sent this or later tag. + } + _lf_get_environments(&env); + if (!env->need_to_send_LTC) { + LF_PRINT_LOG("Skip sending Latest Tag Confirmed (LTC) to the RTI because there was no tagged message with the " + "tag " PRINTF_TAG " that this federate has received.", + tag_to_send.time - start_time, tag_to_send.microstep); return; } - LF_PRINT_LOG("Sending Latest Tag Complete (LTC) " PRINTF_TAG " to the RTI.", tag_to_send.time - start_time, + LF_PRINT_LOG("Sending Latest Tag Confirmed (LTC) " PRINTF_TAG " to the RTI.", tag_to_send.time - start_time, tag_to_send.microstep); - send_tag(MSG_TYPE_LATEST_TAG_COMPLETE, tag_to_send); + send_tag(MSG_TYPE_LATEST_TAG_CONFIRMED, tag_to_send); _fed.last_sent_LTC = tag_to_send; } diff --git a/core/reactor_common.c b/core/reactor_common.c index 55d3342ca..34f9d68eb 100644 --- a/core/reactor_common.c +++ b/core/reactor_common.c @@ -210,6 +210,10 @@ void _lf_start_time_step(environment_t* env) { } #endif // FEDERATED_DECENTRALIZED +#ifdef FEDERATED_CENTRALIZED + env->need_to_send_LTC = false; +#endif // FEDERATED_CENTRALIZED + // Reset absent fields on network ports because // their status is unknown lf_reset_status_fields_on_input_port_triggers(); diff --git a/core/threaded/reactor_threaded.c b/core/threaded/reactor_threaded.c index 188efea18..e6ac72226 100644 --- a/core/threaded/reactor_threaded.c +++ b/core/threaded/reactor_threaded.c @@ -883,6 +883,15 @@ void _lf_worker_do_work(environment_t* env, int worker_number) { worker_number, current_reaction_to_execute->name, LF_LEVEL(current_reaction_to_execute->index), current_reaction_to_execute->is_an_input_reaction, current_reaction_to_execute->deadline); +#ifdef FEDERATED_CENTRALIZED + if (current_reaction_to_execute->is_an_input_reaction) { + // This federate has received a tagged message with the current tag and + // must send LTC at the current tag to confirm that the federate has successfully + // received and processed tagged messages with the current tag. + env->need_to_send_LTC = true; + } +#endif // FEDERATED_CENTRALIZED + bool violation = _lf_worker_handle_violations(env, worker_number, current_reaction_to_execute); if (!violation) { diff --git a/core/threaded/watchdog.c b/core/threaded/watchdog.c index 7cb319533..3240fc381 100644 --- a/core/threaded/watchdog.c +++ b/core/threaded/watchdog.c @@ -58,9 +58,14 @@ void watchdog_wait(watchdog_t* watchdog) { instant_t physical_time = lf_time_physical(); while (watchdog->expiration != NEVER && physical_time < watchdog->expiration && !watchdog->terminate) { // Wait for expiration, or a signal to stop or terminate. + LF_PRINT_DEBUG("Watchdog %p sleeps until " PRINTF_TIME, (void*)watchdog, watchdog->expiration); lf_clock_cond_timedwait(&watchdog->cond, watchdog->expiration); physical_time = lf_time_physical(); + LF_PRINT_DEBUG("Watchdog %p woke up at " PRINTF_TIME " expires at " PRINTF_TIME, (void*)watchdog, physical_time, + watchdog->expiration); } + LF_PRINT_DEBUG("Watchdog %p returns with expired=%d terminated=%d", (void*)watchdog, + physical_time >= watchdog->expiration, watchdog->terminate); } /** @@ -94,24 +99,31 @@ static void* watchdog_thread_main(void* arg) { // Step 1: Wait for a timeout to start watching for. if (watchdog->expiration == NEVER) { + LF_PRINT_DEBUG("Watchdog %p waiting on cond var to be started", (void*)watchdog); // Watchdog has been stopped. // Let the runtime know that we are in an inactive/stopped state. watchdog->active = false; // Wait here until the watchdog is started and we can enter the active state. LF_COND_WAIT(&watchdog->cond); + LF_PRINT_DEBUG("Watchdog %p woke up from cond var", (void*)watchdog); continue; } else { // Watchdog has been started. + LF_PRINT_DEBUG("Watchdog %p started", (void*)watchdog); watchdog_wait(watchdog); // At this point we have returned from the watchdog wait. But it could // be that it was to terminate the watchdog. - if (watchdog->terminate) + if (watchdog->terminate) { + LF_PRINT_DEBUG("Watchdog %p was terminated", (void*)watchdog); break; + } // It could also be that the watchdog was stopped - if (watchdog->expiration == NEVER) + if (watchdog->expiration == NEVER) { + LF_PRINT_DEBUG("Watchdog %p was stopped", (void*)watchdog); continue; + } // If we reach here, the watchdog actually timed out. Handle it. LF_PRINT_DEBUG("Watchdog %p timed out", (void*)watchdog); diff --git a/include/core/environment.h b/include/core/environment.h index 038f97e4e..8099eed26 100644 --- a/include/core/environment.h +++ b/include/core/environment.h @@ -87,6 +87,7 @@ typedef struct environment_t { #if defined(FEDERATED) tag_t** _lf_intended_tag_fields; int _lf_intended_tag_fields_size; + bool need_to_send_LTC; #endif // FEDERATED #ifdef LF_ENCLAVES // TODO: Consider dropping #ifdef enclave_info_t* enclave_info; diff --git a/include/core/federated/federate.h b/include/core/federated/federate.h index 64f20752f..409bdfa7e 100644 --- a/include/core/federated/federate.h +++ b/include/core/federated/federate.h @@ -150,12 +150,12 @@ typedef struct federate_instance_t { bool received_stop_request_from_rti; /** - * A record of the most recently sent LTC (latest tag complete) message. + * A record of the most recently sent LTC (latest tag confirmed) message. * In some situations, federates can send logical_tag_complete for * the same tag twice or more in-a-row to the RTI. For example, when * _lf_next() returns without advancing tag. To prevent overwhelming * the RTI with extra messages, record the last sent logical tag - * complete message and check against it in lf_latest_tag_complete(). + * complete message and check against it in lf_latest_tag_confirmed(). * * @note Here, the underlying assumption is that the TCP stack will * deliver the Logical TAG Complete message to the RTI eventually @@ -297,7 +297,7 @@ void lf_enqueue_port_absent_reactions(environment_t* env); void* lf_handle_p2p_connections_from_federates(void*); /** - * @brief Send a latest tag complete (LTC) signal to the RTI. + * @brief Send a latest tag confirmed (LTC) signal to the RTI. * * This avoids the send if an equal or later LTC has previously been sent. * @@ -306,7 +306,7 @@ void* lf_handle_p2p_connections_from_federates(void*); * * @param tag_to_send The tag to send. */ -void lf_latest_tag_complete(tag_t); +void lf_latest_tag_confirmed(tag_t); /** * @brief Parse the address of the RTI and store them into the global federation_metadata struct. diff --git a/include/core/federated/network/net_common.h b/include/core/federated/network/net_common.h index 7af7d939b..76949c680 100644 --- a/include/core/federated/network/net_common.h +++ b/include/core/federated/network/net_common.h @@ -163,7 +163,7 @@ THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * each federate has a valid event at the start tag (start time, 0) and it will * inform the RTI of this event. * Subsequently, at the conclusion of each tag, each federate will send a - * `MSG_TYPE_LATEST_TAG_COMPLETE` followed by a `MSG_TYPE_NEXT_EVENT_TAG` (see + * `MSG_TYPE_LATEST_TAG_CONFIRMED` followed by a `MSG_TYPE_NEXT_EVENT_TAG` (see * the comment for each message for further explanation). Each federate would * have to wait for a `MSG_TYPE_TAG_ADVANCE_GRANT` or a * `MSG_TYPE_PROVISIONAL_TAG_ADVANCE_GRANT` before it can advance to a @@ -435,12 +435,12 @@ THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define MSG_TYPE_PROVISIONAL_TAG_ADVANCE_GRANT 8 /** - * Byte identifying a latest tag complete (LTC) message sent by a federate + * Byte identifying a latest tag confirmed (LTC) message sent by a federate * to the RTI. * The next eight bytes will be the timestep of the completed tag. * The next four bytes will be the microsteps of the completed tag. */ -#define MSG_TYPE_LATEST_TAG_COMPLETE 9 +#define MSG_TYPE_LATEST_TAG_CONFIRMED 9 /////////// Messages used in lf_request_stop() /////////////// //// Overview of the algorithm: diff --git a/low_level_platform/api/CMakeLists.txt b/low_level_platform/api/CMakeLists.txt index 9f2172bce..599f87e59 100644 --- a/low_level_platform/api/CMakeLists.txt +++ b/low_level_platform/api/CMakeLists.txt @@ -7,6 +7,8 @@ if(${CMAKE_SYSTEM_NAME} STREQUAL "nRF52") target_compile_definitions(lf-low-level-platform-api INTERFACE PLATFORM_NRF52) elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Zephyr") target_compile_definitions(lf-low-level-platform-api INTERFACE PLATFORM_ZEPHYR) +elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Patmos") + target_compile_definitions(lf-low-level-platform-api INTERFACE PLATFORM_PATMOS) elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Rp2040") target_compile_definitions(lf-low-level-platform-api INTERFACE PLATFORM_RP2040) target_link_libraries(lf-low-level-platform-api INTERFACE pico_stdlib) diff --git a/low_level_platform/api/low_level_platform.h b/low_level_platform/api/low_level_platform.h index 9611870cc..afffd2a9e 100644 --- a/low_level_platform/api/low_level_platform.h +++ b/low_level_platform/api/low_level_platform.h @@ -48,6 +48,8 @@ int lf_critical_section_exit(environment_t* env); #include "platform/lf_zephyr_support.h" #elif defined(PLATFORM_NRF52) #include "platform/lf_nrf52_support.h" +#elif defined(PLATFORM_PATMOS) +#include "platform/lf_patmos_support.h" #elif defined(PLATFORM_RP2040) #include "platform/lf_rp2040_support.h" #elif defined(PLATFORM_FLEXPRET) diff --git a/low_level_platform/api/platform/lf_patmos_support.h b/low_level_platform/api/platform/lf_patmos_support.h new file mode 100644 index 000000000..afd7e1ce0 --- /dev/null +++ b/low_level_platform/api/platform/lf_patmos_support.h @@ -0,0 +1,52 @@ + +/* Patmos API support for the C target of Lingua Franca. */ + +/************* +Copyright (c) 2024, The University of California at Berkeley. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************/ + +/** + * Patmos API support for the C target of Lingua Franca. + * + * This is based on lf_nrf_support.h in icyphy/lf-buckler. + * + * @author{Ehsan Khodadad } + * @author{Luca Pezzarossa } + * @author{Martin Schoeberl } + */ + +#ifndef LF_PATMOS_SUPPORT_H +#define LF_PATMOS_SUPPORT_H + +// This embedded platform has no TTY suport +#define NO_TTY + +#include // For fixed-width integral types +#include + +#include // Needed to define PRId64 and PRIu32 +#define PRINTF_TIME "%" PRId64 +#define PRINTF_MICROSTEP "%" PRIu32 +#define PRINTF_TAG "(%" PRId64 ", %" PRIu32 ")" + +#endif // LF_PATMOS_SUPPORT_H diff --git a/low_level_platform/impl/CMakeLists.txt b/low_level_platform/impl/CMakeLists.txt index c0f2d8bb5..ac035dcf4 100644 --- a/low_level_platform/impl/CMakeLists.txt +++ b/low_level_platform/impl/CMakeLists.txt @@ -44,8 +44,13 @@ elseif(${CMAKE_SYSTEM_NAME} STREQUAL "FlexPRET") ${CMAKE_CURRENT_LIST_DIR}/src/lf_flexpret_support.c ${CMAKE_CURRENT_LIST_DIR}/src/lf_atomic_irq.c ) +elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Patmos") + set(LF_LOW_LEVEL_PLATFORM_FILES + ${CMAKE_CURRENT_LIST_DIR}/src/lf_patmos_support.c + ${CMAKE_CURRENT_LIST_DIR}/src/lf_atomic_irq.c + ) else() - message(FATAL_ERROR "Your platform is not supported! The C target supports FlexPRET, Linux, MacOS, nRF52, RP2040, Windows, and Zephyr.") + message(FATAL_ERROR "Your platform is not supported! The C target supports FlexPRET, Patmos, Linux, MacOS, nRF52, RP2040, Windows, and Zephyr.") endif() list(APPEND LF_LOW_LEVEL_PLATFORM_FILES ${CMAKE_CURRENT_LIST_DIR}/src/lf_platform_util.c) @@ -56,6 +61,7 @@ if(${CMAKE_SYSTEM_NAME} STREQUAL "Zephyr") else() message(STATUS "Building Zephyr library with Kernel clock ") endif() + zephyr_library_named(lf-low-level-platform-impl) zephyr_library_sources(${LF_LOW_LEVEL_PLATFORM_FILES}) zephyr_library_link_libraries(kernel) @@ -93,8 +99,11 @@ elseif(${CMAKE_SYSTEM_NAME} STREQUAL "FlexPRET") ) endif() endif() +elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Patmos") + add_library(lf-low-level-platform-impl STATIC ${LF_LOW_LEVEL_PLATFORM_FILES}) else() add_library(lf-low-level-platform-impl STATIC ${LF_LOW_LEVEL_PLATFORM_FILES}) + # Link the platform to a threading library if(NOT DEFINED LF_SINGLE_THREADED OR DEFINED LF_TRACE) find_package(Threads REQUIRED) @@ -116,6 +125,7 @@ macro(low_level_platform_define X) target_compile_definitions(lf-low-level-platform-impl PUBLIC ${X}=${${X}}) endif(DEFINED ${X}) endmacro() + low_level_platform_define(LF_SINGLE_THREADED) low_level_platform_define(LOG_LEVEL) low_level_platform_define(MODAL_REACTORS) diff --git a/low_level_platform/impl/src/lf_atomic_irq.c b/low_level_platform/impl/src/lf_atomic_irq.c index 2854a6f11..3a9d72086 100644 --- a/low_level_platform/impl/src/lf_atomic_irq.c +++ b/low_level_platform/impl/src/lf_atomic_irq.c @@ -1,5 +1,5 @@ #if defined(PLATFORM_ARDUINO) || defined(PLATFORM_NRF52) || defined(PLATFORM_ZEPHYR) || defined(PLATFORM_RP2040) || \ - defined(PLATFORM_FLEXPRET) + defined(PLATFORM_FLEXPRET) || defined(PLATFORM_PATMOS) /** * @author Erling Rennemo Jellum * @copyright (c) 2023 diff --git a/low_level_platform/impl/src/lf_patmos_support.c b/low_level_platform/impl/src/lf_patmos_support.c new file mode 100644 index 000000000..43f20710c --- /dev/null +++ b/low_level_platform/impl/src/lf_patmos_support.c @@ -0,0 +1,118 @@ +#if defined(PLATFORM_PATMOS) +/************* +Copyright (c) 2024, The University of California at Berkeley. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +***************/ + +/** + * @author{Ehsan Khodadad } + * @author{Luca Pezzarossa } + * @author{Martin Schoeberl } + */ +#include +#include +#include +#include "platform/lf_patmos_support.h" +#include "low_level_platform.h" +#include +#include +#include + +// Keep track of physical actions being entered into the system +static volatile bool _lf_async_event = false; +// Keep track of whether we are in a critical section or not +static volatile int _lf_num_nested_critical_sections = 0; +/** + * @brief Sleep until an absolute time. + * Since there is no sleep mode in Patmos, and energy saving is not important for real-time systems, + * we just used a busy sleep. + * + * @param wakeup int64_t time of wakeup + * @return int 0 if successful sleep, -1 if awoken by async event + */ + +int _lf_interruptable_sleep_until_locked(environment_t* env, instant_t wakeup) { + instant_t now; + _lf_async_event = false; + lf_enable_interrupts_nested(); + + // Do busy sleep + do { + _lf_clock_gettime(&now); + } while ((now < wakeup) && !_lf_async_event); + + lf_disable_interrupts_nested(); + + if (_lf_async_event) { + _lf_async_event = false; + return -1; + } else { + return 0; + } +} + +/** + * Patmos clock does not need initialization. + */ +void _lf_initialize_clock() {} + +/** + * Write the current time in nanoseconds into the location given by the argument. + * This returns 0 (it never fails, assuming the argument gives a valid memory location). + */ + +int _lf_clock_gettime(instant_t* t) { + + assert(t != NULL); + + *t = get_cpu_usecs() * 1000; + + return 0; +} + +#if defined(LF_SINGLE_THREADED) + +int lf_disable_interrupts_nested() { + if (_lf_num_nested_critical_sections++ == 0) { + intr_disable(); + } + return 0; +} + +int lf_enable_interrupts_nested() { + if (_lf_num_nested_critical_sections <= 0) { + return 1; + } + + if (--_lf_num_nested_critical_sections == 0) { + intr_enable(); + } + return 0; +} + +int _lf_single_threaded_notify_of_event() { + _lf_async_event = true; + return 0; +} +#endif // LF_SINGLE_THREADED + +#endif // PLATFORM_PATMOS diff --git a/platform/impl/CMakeLists.txt b/platform/impl/CMakeLists.txt index 32753e7eb..3c86b3817 100644 --- a/platform/impl/CMakeLists.txt +++ b/platform/impl/CMakeLists.txt @@ -9,6 +9,9 @@ elseif(${CMAKE_SYSTEM_NAME} STREQUAL "FlexPRET") add_library(lf-platform-impl STATIC) target_sources(lf-platform-impl PUBLIC ${LF_PLATFORM_FILES}) target_link_libraries(lf-platform-impl PRIVATE fp-sdk) +elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Patmos") + add_library(lf-platform-impl STATIC) + target_sources(lf-platform-impl PUBLIC ${LF_PLATFORM_FILES}) else() add_library(lf-platform-impl STATIC) target_sources(lf-platform-impl PUBLIC ${LF_PLATFORM_FILES}) diff --git a/python/include/pythontarget.h b/python/include/pythontarget.h index a828e0689..b9bcefd15 100644 --- a/python/include/pythontarget.h +++ b/python/include/pythontarget.h @@ -185,6 +185,29 @@ PyObject* convert_C_action_to_py(void* action); */ PyObject* get_python_function(string module, string class, int instance_id, string func); +/** + * Load the Serializer class from package name + * @param package_name Name of the python package to load + * @return Initialized Serializer class + */ +PyObject* load_serializer(string package_name); + +/** + * Serialize Python object to a bytes object using external serializer + * @param obj The Python object to serialize + * @param custom_serializer The custom Serializer class + * @return Serialized Python bytes object + */ +PyObject* custom_serialize(PyObject* obj, PyObject* custom_serializer); + +/** + * Deserialize Python object from a bytes object using external serializer + * @param serialized_pyobject The serialized bytes Python object + * @param custom_serializer The custom Serializer class + * @return Deserialized Python object + */ +PyObject* custom_deserialize(PyObject* serialized_pyobject, PyObject* custom_serializer); + /* * The Python runtime will call this function to initialize the module. * The name of this function is dynamically generated to follow diff --git a/python/lib/pythontarget.c b/python/lib/pythontarget.c index 3a3e7b2b4..ae43959d3 100644 --- a/python/lib/pythontarget.c +++ b/python/lib/pythontarget.c @@ -695,3 +695,58 @@ PyObject* get_python_function(string module, string class, int instance_id, stri PyGILState_Release(gstate); return Py_None; } + +PyObject* load_serializer(string package_name) { + // import package_name + PyObject* pName = PyUnicode_DecodeFSDefault(package_name); + PyObject* pModule = PyImport_Import(pName); + Py_DECREF(pName); + if (PyErr_Occurred()) + PyErr_Print(); + if (pModule == NULL) + lf_print_error_and_exit("Could not load the custom serializer package '%s'.", package_name); + // Get the Serializer class + PyObject* SerializerClass = PyObject_GetAttrString(pModule, "Serializer"); + if (PyErr_Occurred()) + PyErr_Print(); + if (SerializerClass == NULL) + lf_print_error_and_exit("Could not find class 'Serializer' in module '%s'.", package_name); + // Instanciate and initialize Serializer class + PyObject* custom_serializer = PyObject_CallObject(SerializerClass, NULL); + if (PyErr_Occurred()) + PyErr_Print(); + if (custom_serializer == NULL) + lf_print_error_and_exit("Could not instantiate class 'Serializer' in module '%s'.", package_name); + lf_print_log("Successfully loaded custom serializer package '%s'.\n", package_name); + return custom_serializer; +} + +PyObject* custom_serialize(PyObject* obj, PyObject* custom_serializer) { + if (custom_serializer == NULL) + lf_print_error_and_exit("Serializer is null."); + PyObject* serializer_serialize = PyObject_GetAttrString(custom_serializer, "serialize"); + PyObject* args = PyTuple_Pack(1, obj); + PyObject* serialized_pyobject = PyObject_CallObject(serializer_serialize, args); + Py_XDECREF(serializer_serialize); + Py_XDECREF(args); + if (PyErr_Occurred()) + PyErr_Print(); + if (serialized_pyobject == NULL) + lf_print_error_and_exit("Could not serialize object."); + return serialized_pyobject; +} + +PyObject* custom_deserialize(PyObject* serialized_pyobject, PyObject* custom_serializer) { + if (custom_serializer == NULL) + lf_print_error_and_exit("Serializer is null."); + PyObject* serializer_deserialize = PyObject_GetAttrString(custom_serializer, "deserialize"); + PyObject* args = PyTuple_Pack(1, serialized_pyobject); + PyObject* deserialized_obj = PyObject_CallObject(serializer_deserialize, args); + Py_XDECREF(serializer_deserialize); + Py_XDECREF(args); + if (PyErr_Occurred()) + PyErr_Print(); + if (deserialized_obj == NULL) + lf_print_error_and_exit("Could not deserialize deserialized_obj."); + return deserialized_obj; +} \ No newline at end of file