From 43e5c3098da602b24e636acd732a072bdadb61de Mon Sep 17 00:00:00 2001 From: Abhi Gundrala Date: Wed, 7 Jun 2023 19:28:33 -0700 Subject: [PATCH 01/22] pico-support initial draft unthreaded --- core/platform/CMakeLists.txt | 5 + core/platform/Platform.cmake | 2 + core/platform/lf_pico_support.c | 329 ++++++++++++++++++++++++ include/core/platform.h | 2 + include/core/platform/lf_pico_support.h | 73 ++++++ 5 files changed, 411 insertions(+) create mode 100644 core/platform/lf_pico_support.c create mode 100644 include/core/platform/lf_pico_support.h diff --git a/core/platform/CMakeLists.txt b/core/platform/CMakeLists.txt index 326bebfa9..7f71629b3 100644 --- a/core/platform/CMakeLists.txt +++ b/core/platform/CMakeLists.txt @@ -9,6 +9,7 @@ lf_macos_support.c lf_windows_support.c lf_nrf52_support.c lf_zephyr_support.c +lf_pico_support.c ) if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows") @@ -18,6 +19,10 @@ elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Nrf52") target_compile_definitions(core PUBLIC PLATFORM_NRF52) elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Zephyr") target_compile_definitions(core PUBLIC PLATFORM_ZEPHYR) +elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Pico") + # platform pico_sdk dependent libraries + target_link_libraries(core pico_stdlib pico_multicore pico_sync) + target_compile_definitions(core PUBLIC PLATFORM_PICO) endif() # Add sources to the list for debug info diff --git a/core/platform/Platform.cmake b/core/platform/Platform.cmake index df1d6c6b0..f5a12183e 100644 --- a/core/platform/Platform.cmake +++ b/core/platform/Platform.cmake @@ -10,6 +10,8 @@ elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Windows") message("Using Windows SDK version ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}") elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Zephyr") set(LF_PLATFORM_FILE lf_zephyr_support.c) +elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Pico") + set(LF_PLATFORM_FILE lf_pico_support.c) else() message(FATAL_ERROR "Your platform is not supported! The C target supports Linux, MacOS and Windows.") endif() diff --git a/core/platform/lf_pico_support.c b/core/platform/lf_pico_support.c new file mode 100644 index 000000000..c8e327395 --- /dev/null +++ b/core/platform/lf_pico_support.c @@ -0,0 +1,329 @@ +#ifdef PLATFORM_PICO +/************* +Copyright (c) 2022, 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. +***************/ + +/** + * @brief Raspberry Pi Pico support for the C target of Lingua Franca + * Uses the baremetal pico-sdk + * + * @author{Abhi Gundrala } + */ + +#include "lf_pico_support.h" +#include "platform.h" +#include "utils/util.h" +#include "tag.h" + +#include +#include +#include + +#ifdef LF_UNTHREADED +// critical section struct binding +static critical_section_t _lf_crit_sec; +// semaphore used to notify if sleep was interupted by irq +static semaphore_t _lf_sem_irq_event; +static uint32_t _lf_num_nested_critical_sections = 0; +#endif + +/** + * Initialize the LF clock. Must be called before using other clock-related APIs. + */ +void lf_initialize_clock(void) { + // init stdlib peripherals + stdio_init_all(); + // init sync structs + critical_section_init(&_lf_crit_sec); + sem_init(&_lf_sem_irq_event, 0, 1); + // TODO: use core1 + multicore_reset_core1(); +} + +/** + * Fetch the value of an internal (and platform-specific) physical clock and + * store it in `t`. in nanoseconds + * + * Ideally, the underlying platform clock should be monotonic. However, the + * core lib tries to enforce monotonicity at higher level APIs (see tag.h). + * TODO: might want to use the RTC + * @return 0 for success, or -1 for failure + */ +int lf_clock_gettime(instant_t* t) { + absolute_time_t now; + uint64_t ns_from_boot; + now = get_absolute_time(); + ns_from_boot = to_us_since_boot(now) * 1000; + *t = (instant_t) ns_from_boot; + return 0; +} + +/** + * Pause execution for a given duration. + * + * @return 0 for success, or -1 for failure. + */ +int lf_sleep(interval_t sleep_duration) { + if (sleep_duration < 0) { + return -1; + } + sleep_us((uint64_t) (sleep_duration / 1000)); + return 0; +} + +/** + * @brief Sleep until the given wakeup time. + * + * @param wakeup_time The time instant at which to wake up. + * @return int 0 if sleep completed, or -1 if it was interrupted. + */ +int lf_sleep_until_locked(instant_t wakeup_time) { + int ret_code = 0; + if (wakeup_time < 0) { + ret_code = -1; + return ret_code; + } + absolute_time_t target; + // reset semaphore to 0 + // TODO: leverage the semaphore permit number + sem_reset(&_lf_sem_irq_event, 0); + target = from_us_since_boot((uint64_t) (wakeup_time / 1000)); + lf_critical_section_exit(&_lf_crit_sec); + // sleep till target or return on processor event + if(sem_acquire_block_until(&_lf_sem_irq_event, target)) { + ret_code = -1; + } + lf_critical_section_enter(&_lf_crit_sec); + return ret_code; +} +/* +* Critical sections are only provided for an unthreaded, single core +* runtime. In the unthreaded runtime, all interactions with core1 are through +* physical actions and interupts outside of the runtime. +*/ +#ifdef LF_UNTHREADED +/** + * Enter a critical section where logical time and the event queue are guaranteed + * to not change unless they are changed within the critical section. + * this can be implemented by disabling interrupts. + * Users of this function must ensure that lf_init_critical_sections() is + * called first and that lf_critical_section_exit() is called later. + * @return 0 on success, platform-specific error number otherwise. + * TODO: needs to be used sparingly + */ +int lf_critical_section_enter() { + if (!critical_section_is_initialized(&_lf_crit_sec)) { + return 1; + } + // disables irq and spin-locks core + if (_lf_num_nested_critical_sections++ == 0) { + critical_section_enter_blocking(&_lf_crit_sec); + } + return 0; +} + +/** + * Exit the critical section entered with lf_lock_time(). + * @return 0 on success, platform-specific error number otherwise. + * TODO: needs to be used sparingly, find a better way for event queue + * mutual exclusion for embedded platforms. better leverage the nvic + */ +int lf_critical_section_exit() { + if (!critical_section_is_initialized(&_lf_crit_sec) || + _lf_num_nested_critical_sections <= 0) { + return 1; + } + // restores system execution state + if (--_lf_num_nested_critical_sections == 0) { + critical_section_exit(&_lf_crit_sec); + } + return 0; +} + +/** + * Notify any listeners that an event has been created. + * The caller should call lf_critical_section_enter() before calling this function. + * @return 0 on success, platform-specific error number otherwise. + */ +int lf_notify_of_event() { + // un-block threads that acquired this binary semaphore + sem_release(&_lf_sem_irq_event); + return 0; +} +#endif + +// For platforms with threading support, the following functions +// abstract the API so that the LF runtime remains portable. + +#ifdef LF_THREADED +/** + * @brief Get the number of cores on the host machine. + * pico has two physical cores and runs only two worker threads + */ +int lf_available_cores() { + return 2; +} + +void _pico_core_loader() { + // create method that executes provided + // functions with specific through a comunicating api call + // the api call will be used by thread create and join + // alternatively use free-rtos an launch tasks + /// TODO: create a dispatcher program that runs on the second core similar to rtic + /// TODO: maybe assigning an enclave to core1 is the best path forward to avoid + // reimplementing a threading library +} + +/** + * Create a new thread, starting with execution of lf_thread + * getting passed arguments. The new handle is stored in thread_id. + * + * @return 0 on success, platform-specific error number otherwise. + * TODO: learn more about function pointers and resolving this interface + */ +int lf_thread_create(lf_thread_t* thread, void *(*lf_thread) (void *), void* arguments) { + /// TODO: wrap in secondary function that takes these arguments + /// run that function on core1 with provided args + // multicore_launch_core1(lf_thread); + // fill thread instance +} + +/** + * Make calling thread wait for termination of the thread. The + * exit status of the thread is stored in thread_return if thread_return + * is not NULL. + * @param thread The thread. + * @param thread_return A pointer to where to store the exit status of the thread. + * + * @return 0 on success, platform-specific error number otherwise. + */ +int lf_thread_join(lf_thread_t thread, void** thread_return) { + /// TODO: implement +} + +/** + * Initialize a mutex. + * + * @return 0 on success, platform-specific error number otherwise. + */ +int lf_mutex_init(lf_mutex_t* mutex) { + recursive_mutex_init(mutex); + return 0; +} + +/** + * Lock a mutex. + * + * @return 0 on success, platform-specific error number otherwise. + * TODO: should this block? + */ +int lf_mutex_lock(lf_mutex_t* mutex) { + if (!recursive_mutex_is_initialized(mutex)) { + return -1; + } + recursive_mutex_enter_blocking(mutex); + return 0; +} + +/** + * Unlock a mutex. + * + * @return 0 on success, platform-specific error number otherwise. + */ +int lf_mutex_unlock(lf_mutex_t* mutex) { + if (!recursive_mutex_is_initialized(mutex)) { + return -1; + } + recursive_mutex_exit(mutex); + return 0; +} + +/** + * Initialize a conditional variable. + * @return 0 on success, platform-specific error number otherwise. + * /// TODO: mutex here not used + */ +int lf_cond_init(lf_cond_t* cond, lf_mutex_t* mutex) { + // set max permits to number of threads + sem_init(cond, 0, NUMBER_OF_WORKERS); + return 0; +} + +/** + * Wake up all threads waiting for condition variable cond. + * + * @return 0 on success, platform-specific error number otherwise. + */ +int lf_cond_broadcast(lf_cond_t* cond) { + // release all permits + while(sem_release(cond)); + // check all released + if (sem_available(cond) != NUMBER_OF_WORKERS) { + return -1; + } + return 0; +} + +/** + * Wake up one thread waiting for condition variable cond. + * + * @return 0 on success, platform-specific error number otherwise. + */ +int lf_cond_signal(lf_cond_t* cond) { + if(!sem_release(cond)) { + return -1; + } + return 0; +} + +/** + * Wait for condition variable "cond" to be signaled or broadcast. + * "mutex" is assumed to be locked before. + * + * @return 0 on success, platform-specific error number otherwise. + */ +int lf_cond_wait(lf_cond_t* cond) { + sem_acquire_blocking(cond); + return 0; +} + +/** + * Block current thread on the condition variable until condition variable + * pointed by "cond" is signaled or time pointed by "absolute_time_ns" in + * nanoseconds is reached. + * + * @return 0 on success, LF_TIMEOUT on timeout, and platform-specific error + * number otherwise. + */ +int lf_cond_timedwait(lf_cond_t* cond, instant_t absolute_time_ns) { + absolute_time_t target; + target = from_us_since_boot((uint64_t) (absolute_time_ns / 1000)); + if (!sem_acquire_block_until(cond, target)) { + return LF_TIMEOUT; + } + return 0; +} + +#endif // LF_THREADED +#endif // PLATFORM_PICO + diff --git a/include/core/platform.h b/include/core/platform.h index 2a632d5fc..c6c22e845 100644 --- a/include/core/platform.h +++ b/include/core/platform.h @@ -57,6 +57,8 @@ extern "C" { #include "platform/lf_zephyr_support.h" #elif defined(PLATFORM_NRF52) #include "platform/lf_nrf52_support.h" +#elif defined(PLATFORM_PICO) + #include "platform/lf_pico_support.h" #elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) // Windows platforms #include "lf_windows_support.h" diff --git a/include/core/platform/lf_pico_support.h b/include/core/platform/lf_pico_support.h new file mode 100644 index 000000000..f08d18688 --- /dev/null +++ b/include/core/platform/lf_pico_support.h @@ -0,0 +1,73 @@ +/** +Pico Support +**/ + +/** + * @brief pico support for reactor-c + */ + +#ifndef LF_PICO_SUPPORT_H +#define LF_PICO_SUPPORT_H + +#include +#include +#include + +// Define PRINTF_TIME and PRINTF_MICROSTEP, which are the printf +// codes (like the d in %d to print an int) for time and microsteps. +// To use these, specify the printf as follows: +// printf("Time: " PRINTF_TIME "\n", time_value); +// On most platforms, time is an signed 64-bit number (int64_t) and +// the microstep is an unsigned 32-bit number (uint32_t). +// Sadly, in C, there is no portable to print such numbers using +// printf without getting a warning on some platforms. +// On each platform, the code for printf if given by the macros +// PRId64 and PRIu32 defined in inttypes.h. Hence, here, we import +// inttypes.h, then define PRINTF_TIME and PRINTF_MICROSTEP. +// If you are targeting a platform that uses some other type +// for time and microsteps, you can simply define +// PRINTF_TIME and PRINTF_MICROSTEP directly in the same file that +// defines the types instant_t, interval_t, and microstep_t. +// TODO: int 64 print format for pico +#define PRINTF_TIME "%" PRId32 +#define PRINTF_MICROSTEP "%" PRIu32 + +//#define instant_t int64_t +//#define interval_t int64_t +//#define microstep_t uint32_t + +// For convenience, the following string can be inserted in a printf +// format for printing both time and microstep as follows: +// printf("Tag is " PRINTF_TAG "\n", time_value, microstep); +#define PRINTF_TAG "(" PRINTF_TIME ", " PRINTF_MICROSTEP ")" + +/** + * For user-friendly reporting of time values, the buffer length required. + * This is calculated as follows, based on 64-bit time in nanoseconds: + * Maximum number of weeks is 15,250 + * Maximum number of days is 6 + * Maximum number of hours is 23 + * Maximum number of minutes is 59 + * Maximum number of seconds is 59 + * Maximum number of nanoseconds is 999,999,999 + * Maximum number of microsteps is 4,294,967,295 + * Total number of characters for the above is 24. + * Text descriptions and spaces add an additional 55, + * for a total of 79. One more allows for a null terminator. + */ +#define LF_TIME_BUFFER_LENGTH 80 +// TODO: define interval_t and instant_t +// interval as u64_t +// instant -> rpi pico docs on time module +#define _LF_TIMEOUT 1 + +#ifdef LF_THREADED +typedef recursive_mutex_t lf_mutex_t; +typedef semaphore_t lf_cond_t; +/// TODO: figure out thread representation +typedef uint32_t lf_thread_t; + +/// TODO: atomic definitions + +#endif // LF_THREADED +#endif // LF_PICO_SUPPORT_H \ No newline at end of file From fa78ba216c375681baa73137782eed47ed3a9577 Mon Sep 17 00:00:00 2001 From: Abhi Gundrala Date: Thu, 8 Jun 2023 14:53:37 -0700 Subject: [PATCH 02/22] move reactor-c linking to make cmake in generator --- core/platform/CMakeLists.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/platform/CMakeLists.txt b/core/platform/CMakeLists.txt index 7f71629b3..1dd0b3de4 100644 --- a/core/platform/CMakeLists.txt +++ b/core/platform/CMakeLists.txt @@ -20,8 +20,6 @@ elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Nrf52") elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Zephyr") target_compile_definitions(core PUBLIC PLATFORM_ZEPHYR) elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Pico") - # platform pico_sdk dependent libraries - target_link_libraries(core pico_stdlib pico_multicore pico_sync) target_compile_definitions(core PUBLIC PLATFORM_PICO) endif() From 2973e98f897efd774e42a3970f6d5423a868ba3f Mon Sep 17 00:00:00 2001 From: Abhi Gundrala Date: Mon, 26 Jun 2023 12:22:12 -0700 Subject: [PATCH 03/22] env changes --- core/platform/lf_pico_support.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/core/platform/lf_pico_support.c b/core/platform/lf_pico_support.c index c8e327395..b9d3b4983 100644 --- a/core/platform/lf_pico_support.c +++ b/core/platform/lf_pico_support.c @@ -41,6 +41,7 @@ THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifdef LF_UNTHREADED // critical section struct binding +// TODO: maybe be more precise and use nvic interrupt mask static critical_section_t _lf_crit_sec; // semaphore used to notify if sleep was interupted by irq static semaphore_t _lf_sem_irq_event; @@ -50,7 +51,7 @@ static uint32_t _lf_num_nested_critical_sections = 0; /** * Initialize the LF clock. Must be called before using other clock-related APIs. */ -void lf_initialize_clock(void) { +void _lf_initialize_clock(void) { // init stdlib peripherals stdio_init_all(); // init sync structs @@ -69,7 +70,7 @@ void lf_initialize_clock(void) { * TODO: might want to use the RTC * @return 0 for success, or -1 for failure */ -int lf_clock_gettime(instant_t* t) { +int _lf_clock_now(instant_t* t) { absolute_time_t now; uint64_t ns_from_boot; now = get_absolute_time(); @@ -97,7 +98,7 @@ int lf_sleep(interval_t sleep_duration) { * @param wakeup_time The time instant at which to wake up. * @return int 0 if sleep completed, or -1 if it was interrupted. */ -int lf_sleep_until_locked(instant_t wakeup_time) { +int _lf_interruptable_sleep_until_locked(environment_t* env, instant_t wakeup_time) { int ret_code = 0; if (wakeup_time < 0) { ret_code = -1; @@ -108,12 +109,12 @@ int lf_sleep_until_locked(instant_t wakeup_time) { // TODO: leverage the semaphore permit number sem_reset(&_lf_sem_irq_event, 0); target = from_us_since_boot((uint64_t) (wakeup_time / 1000)); - lf_critical_section_exit(&_lf_crit_sec); + lf_critical_section_exit(env); // sleep till target or return on processor event if(sem_acquire_block_until(&_lf_sem_irq_event, target)) { ret_code = -1; } - lf_critical_section_enter(&_lf_crit_sec); + lf_critical_section_enter(env); return ret_code; } /* @@ -131,7 +132,7 @@ int lf_sleep_until_locked(instant_t wakeup_time) { * @return 0 on success, platform-specific error number otherwise. * TODO: needs to be used sparingly */ -int lf_critical_section_enter() { +int lf_disable_interrupts_nested() { if (!critical_section_is_initialized(&_lf_crit_sec)) { return 1; } @@ -148,7 +149,7 @@ int lf_critical_section_enter() { * TODO: needs to be used sparingly, find a better way for event queue * mutual exclusion for embedded platforms. better leverage the nvic */ -int lf_critical_section_exit() { +int lf_enable_interrupts_nested() { if (!critical_section_is_initialized(&_lf_crit_sec) || _lf_num_nested_critical_sections <= 0) { return 1; @@ -165,7 +166,7 @@ int lf_critical_section_exit() { * The caller should call lf_critical_section_enter() before calling this function. * @return 0 on success, platform-specific error number otherwise. */ -int lf_notify_of_event() { +int _lf_unthreaded_notify_of_event() { // un-block threads that acquired this binary semaphore sem_release(&_lf_sem_irq_event); return 0; From d43d241808ffd54364280acded943b74393b9843 Mon Sep 17 00:00:00 2001 From: Abhinav Gundrala Date: Thu, 6 Jul 2023 19:41:20 -0700 Subject: [PATCH 04/22] free rtos stub --- core/platform/lf_freertos_threads_support.c | 87 +++++++++++++++++++ core/platform/lf_pico_support.c | 2 +- .../platform/lf_freertos_threads_support.h | 47 ++++++++++ 3 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 core/platform/lf_freertos_threads_support.c create mode 100644 include/core/platform/lf_freertos_threads_support.h diff --git a/core/platform/lf_freertos_threads_support.c b/core/platform/lf_freertos_threads_support.c new file mode 100644 index 000000000..2149caaf3 --- /dev/null +++ b/core/platform/lf_freertos_threads_support.c @@ -0,0 +1,87 @@ +#if defined(LF_THREADED) +// FIXME: build system with pico-support +#include "platform.h" +#include "lf_freertos_threads_support.h" + +#include +#include +#include // For fixed-width integral types + +int lf_thread_create(lf_thread_t* thread, void *(*lf_thread) (void *), void* arguments) { + return pthread_create((pthread_t*)thread, NULL, lf_thread, arguments); +} + +int lf_thread_join(lf_thread_t thread, void** thread_return) { + return pthread_join((pthread_t)thread, thread_return); +} + +int lf_mutex_init(lf_mutex_t* mutex) { + // Set up a recursive mutex + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + // Initialize the mutex to be recursive, meaning that it is OK + // for the same thread to lock and unlock the mutex even if it already holds + // the lock. + // FIXME: This is dangerous. The docs say this: "It is advised that an + // application should not use a PTHREAD_MUTEX_RECURSIVE mutex with + // condition variables because the implicit unlock performed for a + // pthread_cond_wait() or pthread_cond_timedwait() may not actually + // release the mutex (if it had been locked multiple times). + // If this happens, no other thread can satisfy the condition + // of the predicate.” This seems like a bug in the implementation of + // pthreads. Maybe it has been fixed? + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + return pthread_mutex_init((pthread_mutex_t*)mutex, &attr); +} + +int lf_mutex_lock(lf_mutex_t* mutex) { + return pthread_mutex_lock((pthread_mutex_t*)mutex); +} + +int lf_mutex_unlock(lf_mutex_t* mutex) { + return pthread_mutex_unlock((pthread_mutex_t*)mutex); +} + +int lf_cond_init(lf_cond_t* cond, lf_mutex_t* mutex) { + cond->mutex = mutex; + pthread_condattr_t cond_attr; + pthread_condattr_init(&cond_attr); + // Limit the scope of the condition variable to this process (default) + pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_PRIVATE); + return pthread_cond_init(&cond->condition, &cond_attr); +} + +int lf_cond_broadcast(lf_cond_t* cond) { + return pthread_cond_broadcast((pthread_cond_t*)&cond->condition); +} + +int lf_cond_signal(lf_cond_t* cond) { + return pthread_cond_signal((pthread_cond_t*)&cond->condition); +} + +int lf_cond_wait(lf_cond_t* cond) { + return pthread_cond_wait((pthread_cond_t*)&cond->condition, (pthread_mutex_t*)cond->mutex); +} + +int lf_cond_timedwait(lf_cond_t* cond, int64_t absolute_time_ns) { + // Convert the absolute time to a timespec. + // timespec is seconds and nanoseconds. + struct timespec timespec_absolute_time + = {(time_t)absolute_time_ns / 1000000000LL, (long)absolute_time_ns % 1000000000LL}; + int return_value = 0; + return_value = pthread_cond_timedwait( + (pthread_cond_t*)&cond->condition, + (pthread_mutex_t*)cond->mutex, + ×pec_absolute_time + ); + switch (return_value) { + case ETIMEDOUT: + return_value = LF_TIMEOUT; + break; + + default: + break; + } + return return_value; +} +#endif diff --git a/core/platform/lf_pico_support.c b/core/platform/lf_pico_support.c index b9d3b4983..6c538d1ca 100644 --- a/core/platform/lf_pico_support.c +++ b/core/platform/lf_pico_support.c @@ -192,7 +192,7 @@ void _pico_core_loader() { // alternatively use free-rtos an launch tasks /// TODO: create a dispatcher program that runs on the second core similar to rtic /// TODO: maybe assigning an enclave to core1 is the best path forward to avoid - // reimplementing a threading library + // reimplementing a threading library } /** diff --git a/include/core/platform/lf_freertos_threads_support.h b/include/core/platform/lf_freertos_threads_support.h new file mode 100644 index 000000000..6eecde5d5 --- /dev/null +++ b/include/core/platform/lf_freertos_threads_support.h @@ -0,0 +1,47 @@ +/* FreeRTOS threading support for the C target of Lingua Franca. */ + +/************* +Copyright (c) 2021, 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. +***************/ + +/** + * POSIX API support for the C target of Lingua Franca. + * + * @author{Soroush Bateni } + * + * All functions return 0 on success. + */ + +#ifndef LF_POSIX_THREADS_SUPPORT_H +#define LF_POSIX_THREADS_SUPPORT_H + +#include + +typedef pthread_mutex_t lf_mutex_t; +typedef struct { + lf_mutex_t* mutex; + pthread_cond_t condition; +} lf_cond_t; +typedef pthread_t lf_thread_t; + +#endif From 198b54f436941af291f2cbe57eda9e08041e1b1c Mon Sep 17 00:00:00 2001 From: Abhinav Gundrala Date: Tue, 11 Jul 2023 13:49:20 -0700 Subject: [PATCH 05/22] refactor headers for rp2040 --- include/core/platform.h | 4 +- .../platform/lf_freertos_threads_support.h | 7 +- include/core/platform/lf_pico_support.h | 73 ------------------- include/core/platform/lf_rp2040_support.h | 30 ++++++++ 4 files changed, 36 insertions(+), 78 deletions(-) delete mode 100644 include/core/platform/lf_pico_support.h create mode 100644 include/core/platform/lf_rp2040_support.h diff --git a/include/core/platform.h b/include/core/platform.h index d35e1b720..3cd43043d 100644 --- a/include/core/platform.h +++ b/include/core/platform.h @@ -56,8 +56,8 @@ typedef struct environment_t environment_t; #include "platform/lf_zephyr_support.h" #elif defined(PLATFORM_NRF52) #include "platform/lf_nrf52_support.h" -#elif defined(PLATFORM_PICO) - #include "platform/lf_pico_support.h" +#elif defined(PLATFORM_RP2040) + #include "platform/lf_rp2040_support.h" #elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) // Windows platforms #include "lf_windows_support.h" diff --git a/include/core/platform/lf_freertos_threads_support.h b/include/core/platform/lf_freertos_threads_support.h index 6eecde5d5..128873be2 100644 --- a/include/core/platform/lf_freertos_threads_support.h +++ b/include/core/platform/lf_freertos_threads_support.h @@ -25,15 +25,16 @@ THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************/ /** - * POSIX API support for the C target of Lingua Franca. + * FreeRTOS thread support. + * For multicore devices, the SMP version of freertos is used. * * @author{Soroush Bateni } * * All functions return 0 on success. */ -#ifndef LF_POSIX_THREADS_SUPPORT_H -#define LF_POSIX_THREADS_SUPPORT_H +#ifndef LF_FREERTOS_THREADS_SUPPORT_H +#define LF_FREERTOS_THREADS_SUPPORT_H #include diff --git a/include/core/platform/lf_pico_support.h b/include/core/platform/lf_pico_support.h deleted file mode 100644 index f08d18688..000000000 --- a/include/core/platform/lf_pico_support.h +++ /dev/null @@ -1,73 +0,0 @@ -/** -Pico Support -**/ - -/** - * @brief pico support for reactor-c - */ - -#ifndef LF_PICO_SUPPORT_H -#define LF_PICO_SUPPORT_H - -#include -#include -#include - -// Define PRINTF_TIME and PRINTF_MICROSTEP, which are the printf -// codes (like the d in %d to print an int) for time and microsteps. -// To use these, specify the printf as follows: -// printf("Time: " PRINTF_TIME "\n", time_value); -// On most platforms, time is an signed 64-bit number (int64_t) and -// the microstep is an unsigned 32-bit number (uint32_t). -// Sadly, in C, there is no portable to print such numbers using -// printf without getting a warning on some platforms. -// On each platform, the code for printf if given by the macros -// PRId64 and PRIu32 defined in inttypes.h. Hence, here, we import -// inttypes.h, then define PRINTF_TIME and PRINTF_MICROSTEP. -// If you are targeting a platform that uses some other type -// for time and microsteps, you can simply define -// PRINTF_TIME and PRINTF_MICROSTEP directly in the same file that -// defines the types instant_t, interval_t, and microstep_t. -// TODO: int 64 print format for pico -#define PRINTF_TIME "%" PRId32 -#define PRINTF_MICROSTEP "%" PRIu32 - -//#define instant_t int64_t -//#define interval_t int64_t -//#define microstep_t uint32_t - -// For convenience, the following string can be inserted in a printf -// format for printing both time and microstep as follows: -// printf("Tag is " PRINTF_TAG "\n", time_value, microstep); -#define PRINTF_TAG "(" PRINTF_TIME ", " PRINTF_MICROSTEP ")" - -/** - * For user-friendly reporting of time values, the buffer length required. - * This is calculated as follows, based on 64-bit time in nanoseconds: - * Maximum number of weeks is 15,250 - * Maximum number of days is 6 - * Maximum number of hours is 23 - * Maximum number of minutes is 59 - * Maximum number of seconds is 59 - * Maximum number of nanoseconds is 999,999,999 - * Maximum number of microsteps is 4,294,967,295 - * Total number of characters for the above is 24. - * Text descriptions and spaces add an additional 55, - * for a total of 79. One more allows for a null terminator. - */ -#define LF_TIME_BUFFER_LENGTH 80 -// TODO: define interval_t and instant_t -// interval as u64_t -// instant -> rpi pico docs on time module -#define _LF_TIMEOUT 1 - -#ifdef LF_THREADED -typedef recursive_mutex_t lf_mutex_t; -typedef semaphore_t lf_cond_t; -/// TODO: figure out thread representation -typedef uint32_t lf_thread_t; - -/// TODO: atomic definitions - -#endif // LF_THREADED -#endif // LF_PICO_SUPPORT_H \ No newline at end of file diff --git a/include/core/platform/lf_rp2040_support.h b/include/core/platform/lf_rp2040_support.h new file mode 100644 index 000000000..7dfb27aa5 --- /dev/null +++ b/include/core/platform/lf_rp2040_support.h @@ -0,0 +1,30 @@ +/* RP2040 API support for the C target of Lingua Franca */ + +/** + * @brief pico support for reactor-c + */ + +#ifndef LF_PICO_SUPPORT_H +#define LF_PICO_SUPPORT_H + +#include +#include + +// Defines for time and microstep data types +#define instant_t int64_t +#define interval_t int64_t +#define microstep_t uint32_t + +// Defines for formatting time in printf for pico +#define PRINTF_TAG "(" PRINTF_TIME ", " PRINTF_MICROSTEP ")" +#define PRINTF_TIME "%lld" +#define PRINTF_MICROSTEP "%d" + +#define LF_TIME_BUFFER_LENGTH 80 +#define _LF_TIMEOUT 1 + +#ifdef LF_THREADED +// TODO: if threaded, use the free-rtos defines +#endif // LF_THREADED + +#endif // LF_PICO_SUPPORT_H From a5dadf537595effac2d3cd0187e0daa0fcb8495b Mon Sep 17 00:00:00 2001 From: Abhinav Gundrala Date: Tue, 11 Jul 2023 13:49:56 -0700 Subject: [PATCH 06/22] refactor sources and cmake form rp2040 --- core/platform/CMakeLists.txt | 6 +++--- core/platform/Platform.cmake | 4 ++-- .../{lf_pico_support.c => lf_rp2040_support.c} | 15 ++++++++++++++- 3 files changed, 19 insertions(+), 6 deletions(-) rename core/platform/{lf_pico_support.c => lf_rp2040_support.c} (96%) diff --git a/core/platform/CMakeLists.txt b/core/platform/CMakeLists.txt index 1dd0b3de4..7a8c591a6 100644 --- a/core/platform/CMakeLists.txt +++ b/core/platform/CMakeLists.txt @@ -9,7 +9,7 @@ lf_macos_support.c lf_windows_support.c lf_nrf52_support.c lf_zephyr_support.c -lf_pico_support.c +lf_rp2040_support.c ) if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows") @@ -19,8 +19,8 @@ elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Nrf52") target_compile_definitions(core PUBLIC PLATFORM_NRF52) elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Zephyr") target_compile_definitions(core PUBLIC PLATFORM_ZEPHYR) -elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Pico") - target_compile_definitions(core PUBLIC PLATFORM_PICO) +elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Rp2040") + target_compile_definitions(core PUBLIC PLATFORM_RP2040) endif() # Add sources to the list for debug info diff --git a/core/platform/Platform.cmake b/core/platform/Platform.cmake index f5a12183e..c3107d023 100644 --- a/core/platform/Platform.cmake +++ b/core/platform/Platform.cmake @@ -10,8 +10,8 @@ elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Windows") message("Using Windows SDK version ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}") elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Zephyr") set(LF_PLATFORM_FILE lf_zephyr_support.c) -elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Pico") - set(LF_PLATFORM_FILE lf_pico_support.c) +elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Rp2040") + set(LF_PLATFORM_FILE lf_rp2040_support.c) else() message(FATAL_ERROR "Your platform is not supported! The C target supports Linux, MacOS and Windows.") endif() diff --git a/core/platform/lf_pico_support.c b/core/platform/lf_rp2040_support.c similarity index 96% rename from core/platform/lf_pico_support.c rename to core/platform/lf_rp2040_support.c index 6c538d1ca..d1e66717f 100644 --- a/core/platform/lf_pico_support.c +++ b/core/platform/lf_rp2040_support.c @@ -177,6 +177,15 @@ int _lf_unthreaded_notify_of_event() { // abstract the API so that the LF runtime remains portable. #ifdef LF_THREADED +#warning "Baremetal threaded support only allows two threads of execution" + +// If threaded is enabled set number of workers +// Compiler warning when NUMBER_OF_WORKERS > 2 +#if !defined(NUMBER_OF_WORKERS) || NUMBER_OF_WORKERS==0 +#undef NUMBER_OF_WORKERS +#define NUMBER_OF_WORKERS 2 +#endif + /** * @brief Get the number of cores on the host machine. * pico has two physical cores and runs only two worker threads @@ -192,7 +201,8 @@ void _pico_core_loader() { // alternatively use free-rtos an launch tasks /// TODO: create a dispatcher program that runs on the second core similar to rtic /// TODO: maybe assigning an enclave to core1 is the best path forward to avoid - // reimplementing a threading library + // reimplementing a threading library + } /** @@ -207,6 +217,9 @@ int lf_thread_create(lf_thread_t* thread, void *(*lf_thread) (void *), void* arg /// run that function on core1 with provided args // multicore_launch_core1(lf_thread); // fill thread instance + *thread = + lf_thread(args) + } /** From 2207668f6a703b9ea22e3c81668579b36cdd18db Mon Sep 17 00:00:00 2001 From: Abhinav Gundrala Date: Tue, 11 Jul 2023 19:35:37 -0700 Subject: [PATCH 07/22] bare multicore thread runner --- core/platform/lf_rp2040_support.c | 75 +++++++++++++++++------ include/core/platform/lf_rp2040_support.h | 50 ++++++++++++--- 2 files changed, 97 insertions(+), 28 deletions(-) diff --git a/core/platform/lf_rp2040_support.c b/core/platform/lf_rp2040_support.c index d1e66717f..602eb5787 100644 --- a/core/platform/lf_rp2040_support.c +++ b/core/platform/lf_rp2040_support.c @@ -25,7 +25,7 @@ THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /** * @brief Raspberry Pi Pico support for the C target of Lingua Franca - * Uses the baremetal pico-sdk + * Uses the pico-sdk which targets the lower level peripheral layer. * * @author{Abhi Gundrala } */ @@ -186,6 +186,21 @@ int _lf_unthreaded_notify_of_event() { #define NUMBER_OF_WORKERS 2 #endif +static lf_function_t _lf_core0_worker; +static void *_lf_core0_args; + +static lf_function_t _lf_core1_worker; +static void *_lf_core1_args; + + +void _rp2040_core1_entry() { + void *res = _lf_core1_worker(_lf_core1_args); + // use fifo and send result + // after worker exit fill fifo with result and block + while(multicore_fifo_wready()) + multicore_fifo_push_blocking((uint32_t) res); +} + /** * @brief Get the number of cores on the host machine. * pico has two physical cores and runs only two worker threads @@ -194,17 +209,6 @@ int lf_available_cores() { return 2; } -void _pico_core_loader() { - // create method that executes provided - // functions with specific through a comunicating api call - // the api call will be used by thread create and join - // alternatively use free-rtos an launch tasks - /// TODO: create a dispatcher program that runs on the second core similar to rtic - /// TODO: maybe assigning an enclave to core1 is the best path forward to avoid - // reimplementing a threading library - -} - /** * Create a new thread, starting with execution of lf_thread * getting passed arguments. The new handle is stored in thread_id. @@ -213,13 +217,29 @@ void _pico_core_loader() { * TODO: learn more about function pointers and resolving this interface */ int lf_thread_create(lf_thread_t* thread, void *(*lf_thread) (void *), void* arguments) { - /// TODO: wrap in secondary function that takes these arguments - /// run that function on core1 with provided args - // multicore_launch_core1(lf_thread); - // fill thread instance - *thread = - lf_thread(args) - + // this method can only be invoked twice. + // each case for each core is specially handled + static uint32_t call_cnt = 0; + if (call_cnt >= 2) { return -1 }; + switch (call_cnt) { + case 0: + _lf_core0_worker = (lf_function_t) lf_thread; + _lf_core0_args = arguments; + *thread = CORE_0; + // cant launch first core worker + break; + case 1: + _lf_core1_worker = (lf_function_t) lf_thread; + _lf_core1_args = arguments; + *thread = CORE_1; + // launch second core worker + multicore_launch_core1(&_rp2040_core1_entry); + break; + default: + return -1; + } + call_cnt++; + return 0; } /** @@ -233,6 +253,23 @@ int lf_thread_create(lf_thread_t* thread, void *(*lf_thread) (void *), void* arg */ int lf_thread_join(lf_thread_t thread, void** thread_return) { /// TODO: implement + /// run the core0 worker method here till completion and fill thread return + switch(thread) { + case CORE_0: + // start core0 worker, block until completion + *thread_return = _lf_core0_worker(args); + break; + case CORE_1: + // use multicore fifo + // remove any extraneous messages from fifo + multicore_fifo_drain(); + // block until thread return value received from fifo + *thread_return = (void *) multicore_fifo_pop_blocking(); + break; + default: + return -1; + } + return 0; } /** diff --git a/include/core/platform/lf_rp2040_support.h b/include/core/platform/lf_rp2040_support.h index 7dfb27aa5..b18bccc3a 100644 --- a/include/core/platform/lf_rp2040_support.h +++ b/include/core/platform/lf_rp2040_support.h @@ -4,17 +4,12 @@ * @brief pico support for reactor-c */ -#ifndef LF_PICO_SUPPORT_H -#define LF_PICO_SUPPORT_H +#ifndef LF_RP2040_SUPPORT_H +#define LF_RP2040_SUPPORT_H #include #include -// Defines for time and microstep data types -#define instant_t int64_t -#define interval_t int64_t -#define microstep_t uint32_t - // Defines for formatting time in printf for pico #define PRINTF_TAG "(" PRINTF_TIME ", " PRINTF_MICROSTEP ")" #define PRINTF_TIME "%lld" @@ -24,7 +19,44 @@ #define _LF_TIMEOUT 1 #ifdef LF_THREADED -// TODO: if threaded, use the free-rtos defines -#endif // LF_THREADED +typedef void *(*lf_function_t) (void *); +typedef semaphore_t lf_cond_t; +typedef recursive_mutex_t lf_mutex_t; +typedef enum { + CORE_0, + CORE_1, +} lf_thread_t; + +/** + * @brief Method to run on core1 of pico to load + * second worker thread. + */ +void _rp2040_core1_entry(); + +/** + * @brief Add `value` to `*ptr` and return original value of `*ptr` + */ +int _rp2040_atomic_fetch_add(int *ptr, int value); + +/** + * @brief Add `value` to `*ptr` and return new updated value of `*ptr` + */ +int _rp2040_atomic_add_fetch(int *ptr, int value); + +/** + * @brief Compare and swap for boolean value. + * If `*ptr` is equal to `value` then overwrite it with `newval` + * If not do nothing. Returns true on overwrite. + */ +bool _rp2040_bool_compare_and_swap(bool *ptr, bool value, bool newval); + +/** + * @brief Compare and swap for integers. + * If `*ptr` is equal to `value`, it is updated to `newval`. + * If not do nothing. The function returns the original value of `*ptr`. + */ +int _rp2040_val_compare_and_swap(int *ptr, int value, int newval); + +#endif // LF_THREADED #endif // LF_PICO_SUPPORT_H From 1dabfa9348a55be3938faa33081716fb1760f89a Mon Sep 17 00:00:00 2001 From: Abhinav Gundrala Date: Tue, 11 Jul 2023 21:15:31 -0700 Subject: [PATCH 08/22] [refactor] comments --- core/platform/Platform.cmake | 1 + core/platform/lf_rp2040_support.c | 5 ++--- include/core/platform/lf_rp2040_support.h | 1 - 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/core/platform/Platform.cmake b/core/platform/Platform.cmake index c3107d023..549d98cb5 100644 --- a/core/platform/Platform.cmake +++ b/core/platform/Platform.cmake @@ -11,6 +11,7 @@ elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Windows") elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Zephyr") set(LF_PLATFORM_FILE lf_zephyr_support.c) elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Rp2040") + message("Using pico-sdk for RP2040 target"); set(LF_PLATFORM_FILE lf_rp2040_support.c) else() message(FATAL_ERROR "Your platform is not supported! The C target supports Linux, MacOS and Windows.") diff --git a/core/platform/lf_rp2040_support.c b/core/platform/lf_rp2040_support.c index 602eb5787..8484d1ede 100644 --- a/core/platform/lf_rp2040_support.c +++ b/core/platform/lf_rp2040_support.c @@ -46,7 +46,7 @@ static critical_section_t _lf_crit_sec; // semaphore used to notify if sleep was interupted by irq static semaphore_t _lf_sem_irq_event; static uint32_t _lf_num_nested_critical_sections = 0; -#endif +#endif //LF_UNTHREADED /** * Initialize the LF clock. Must be called before using other clock-related APIs. @@ -57,7 +57,6 @@ void _lf_initialize_clock(void) { // init sync structs critical_section_init(&_lf_crit_sec); sem_init(&_lf_sem_irq_event, 0, 1); - // TODO: use core1 multicore_reset_core1(); } @@ -171,7 +170,7 @@ int _lf_unthreaded_notify_of_event() { sem_release(&_lf_sem_irq_event); return 0; } -#endif +#endif //LF_UNTHREADED // For platforms with threading support, the following functions // abstract the API so that the LF runtime remains portable. diff --git a/include/core/platform/lf_rp2040_support.h b/include/core/platform/lf_rp2040_support.h index b18bccc3a..3f42ddffe 100644 --- a/include/core/platform/lf_rp2040_support.h +++ b/include/core/platform/lf_rp2040_support.h @@ -58,5 +58,4 @@ bool _rp2040_bool_compare_and_swap(bool *ptr, bool value, bool newval); int _rp2040_val_compare_and_swap(int *ptr, int value, int newval); #endif // LF_THREADED - #endif // LF_PICO_SUPPORT_H From 3daba0d3c697e75f811485786f82562416764889 Mon Sep 17 00:00:00 2001 From: Abhinav Gundrala Date: Tue, 11 Jul 2023 21:50:45 -0700 Subject: [PATCH 09/22] [refactor] change source define for support --- core/platform/Platform.cmake | 2 +- core/platform/lf_rp2040_support.c | 4 ++-- include/core/platform/lf_rp2040_support.h | 2 ++ 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/core/platform/Platform.cmake b/core/platform/Platform.cmake index 549d98cb5..cc6042c7c 100644 --- a/core/platform/Platform.cmake +++ b/core/platform/Platform.cmake @@ -11,7 +11,7 @@ elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Windows") elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Zephyr") set(LF_PLATFORM_FILE lf_zephyr_support.c) elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Rp2040") - message("Using pico-sdk for RP2040 target"); + message("Using pico-sdk for RP2040 target") set(LF_PLATFORM_FILE lf_rp2040_support.c) else() message(FATAL_ERROR "Your platform is not supported! The C target supports Linux, MacOS and Windows.") diff --git a/core/platform/lf_rp2040_support.c b/core/platform/lf_rp2040_support.c index 8484d1ede..d3805423b 100644 --- a/core/platform/lf_rp2040_support.c +++ b/core/platform/lf_rp2040_support.c @@ -1,4 +1,4 @@ -#ifdef PLATFORM_PICO +#if defined(PLATFORM_RP2040) /************* Copyright (c) 2022, The University of California at Berkeley. @@ -375,5 +375,5 @@ int lf_cond_timedwait(lf_cond_t* cond, instant_t absolute_time_ns) { } #endif // LF_THREADED -#endif // PLATFORM_PICO +#endif // PLATFORM_RP2040 diff --git a/include/core/platform/lf_rp2040_support.h b/include/core/platform/lf_rp2040_support.h index 3f42ddffe..ea97f8070 100644 --- a/include/core/platform/lf_rp2040_support.h +++ b/include/core/platform/lf_rp2040_support.h @@ -10,6 +10,8 @@ #include #include +#define NO_TTY + // Defines for formatting time in printf for pico #define PRINTF_TAG "(" PRINTF_TIME ", " PRINTF_MICROSTEP ")" #define PRINTF_TIME "%lld" From 7433c9414d4c72a6ee2c8cf3942cd2d4c58c0295 Mon Sep 17 00:00:00 2001 From: Abhinav Gundrala Date: Wed, 12 Jul 2023 10:53:18 -0700 Subject: [PATCH 10/22] [refactor] change header --- core/platform/lf_rp2040_support.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/platform/lf_rp2040_support.c b/core/platform/lf_rp2040_support.c index d3805423b..4b3699fd7 100644 --- a/core/platform/lf_rp2040_support.c +++ b/core/platform/lf_rp2040_support.c @@ -30,7 +30,7 @@ THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * @author{Abhi Gundrala } */ -#include "lf_pico_support.h" +#include "lf_rp2040_support.h" #include "platform.h" #include "utils/util.h" #include "tag.h" From bc458330d5c6e9821569e2812095160bf159da74 Mon Sep 17 00:00:00 2001 From: Abhinav Gundrala Date: Sun, 16 Jul 2023 23:28:25 -0500 Subject: [PATCH 11/22] [condvar] stub custom cond var --- core/CMakeLists.txt | 7 +++++-- core/platform/lf_rp2040_support.c | 23 ++++++++++++----------- include/core/platform/lf_rp2040_support.h | 6 +++++- 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 8c6aa919c..2f649ec72 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -69,8 +69,11 @@ endif() # Link with thread library if(DEFINED LF_THREADED OR DEFINED LF_TRACE) - find_package(Threads REQUIRED) - target_link_libraries(core PUBLIC Threads::Threads) + # FIXME make this more general for other platforms + if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Rp2040") + find_package(Threads REQUIRED) + target_link_libraries(core PUBLIC Threads::Threads) + endif() endif() # Macro for translating a command-line argument into compile definition for diff --git a/core/platform/lf_rp2040_support.c b/core/platform/lf_rp2040_support.c index 4b3699fd7..09255f21e 100644 --- a/core/platform/lf_rp2040_support.c +++ b/core/platform/lf_rp2040_support.c @@ -314,8 +314,12 @@ int lf_mutex_unlock(lf_mutex_t* mutex) { * /// TODO: mutex here not used */ int lf_cond_init(lf_cond_t* cond, lf_mutex_t* mutex) { - // set max permits to number of threads - sem_init(cond, 0, NUMBER_OF_WORKERS); + lf_mutex_lock(mutex); + // init cond var + cond->flag = false; + cond->num_waiting = 0; + cond->mut = mutex; + lf_mutex_unlock(mutex); return 0; } @@ -325,12 +329,10 @@ int lf_cond_init(lf_cond_t* cond, lf_mutex_t* mutex) { * @return 0 on success, platform-specific error number otherwise. */ int lf_cond_broadcast(lf_cond_t* cond) { - // release all permits - while(sem_release(cond)); - // check all released - if (sem_available(cond) != NUMBER_OF_WORKERS) { - return -1; - } + lf_mutex_lock(cond->mut); + cond->flag = true; + cond->num_waiting = 0; + lf_mutex_unlock(cond->mut); return 0; } @@ -340,9 +342,8 @@ int lf_cond_broadcast(lf_cond_t* cond) { * @return 0 on success, platform-specific error number otherwise. */ int lf_cond_signal(lf_cond_t* cond) { - if(!sem_release(cond)) { - return -1; - } + cond->flag = true; + cond->num_waiting--; return 0; } diff --git a/include/core/platform/lf_rp2040_support.h b/include/core/platform/lf_rp2040_support.h index ea97f8070..d6fdf691b 100644 --- a/include/core/platform/lf_rp2040_support.h +++ b/include/core/platform/lf_rp2040_support.h @@ -22,8 +22,12 @@ #ifdef LF_THREADED typedef void *(*lf_function_t) (void *); -typedef semaphore_t lf_cond_t; typedef recursive_mutex_t lf_mutex_t; +typedef struct { + bool flag; + uint32_t num_wait; + lf_mutex_t *mut; +} lf_cond_t; typedef enum { CORE_0, CORE_1, From 61156d08b17984f108a6e254118b396096aafb68 Mon Sep 17 00:00:00 2001 From: Abhinav Gundrala Date: Mon, 17 Jul 2023 01:19:01 -0500 Subject: [PATCH 12/22] [condvar] implement and integrate --- core/platform/lf_rp2040_support.c | 213 +++++++++++++++++++--- include/core/platform/lf_rp2040_support.h | 107 ++++++++--- 2 files changed, 275 insertions(+), 45 deletions(-) diff --git a/core/platform/lf_rp2040_support.c b/core/platform/lf_rp2040_support.c index 09255f21e..510109907 100644 --- a/core/platform/lf_rp2040_support.c +++ b/core/platform/lf_rp2040_support.c @@ -39,14 +39,12 @@ THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -#ifdef LF_UNTHREADED // critical section struct binding // TODO: maybe be more precise and use nvic interrupt mask static critical_section_t _lf_crit_sec; // semaphore used to notify if sleep was interupted by irq static semaphore_t _lf_sem_irq_event; static uint32_t _lf_num_nested_critical_sections = 0; -#endif //LF_UNTHREADED /** * Initialize the LF clock. Must be called before using other clock-related APIs. @@ -122,6 +120,7 @@ int _lf_interruptable_sleep_until_locked(environment_t* env, instant_t wakeup_ti * physical actions and interupts outside of the runtime. */ #ifdef LF_UNTHREADED + /** * Enter a critical section where logical time and the event queue are guaranteed * to not change unless they are changed within the critical section. @@ -196,8 +195,9 @@ void _rp2040_core1_entry() { void *res = _lf_core1_worker(_lf_core1_args); // use fifo and send result // after worker exit fill fifo with result and block - while(multicore_fifo_wready()) + while(multicore_fifo_wready()) { multicore_fifo_push_blocking((uint32_t) res); + } } /** @@ -277,7 +277,7 @@ int lf_thread_join(lf_thread_t thread, void** thread_return) { * @return 0 on success, platform-specific error number otherwise. */ int lf_mutex_init(lf_mutex_t* mutex) { - recursive_mutex_init(mutex); + mutex_init(mutex); return 0; } @@ -288,10 +288,10 @@ int lf_mutex_init(lf_mutex_t* mutex) { * TODO: should this block? */ int lf_mutex_lock(lf_mutex_t* mutex) { - if (!recursive_mutex_is_initialized(mutex)) { + if (!mutex_is_initialized(mutex)) { return -1; } - recursive_mutex_enter_blocking(mutex); + mutex_enter_blocking(mutex); return 0; } @@ -301,25 +301,23 @@ int lf_mutex_lock(lf_mutex_t* mutex) { * @return 0 on success, platform-specific error number otherwise. */ int lf_mutex_unlock(lf_mutex_t* mutex) { - if (!recursive_mutex_is_initialized(mutex)) { + if (!mutex_is_initialized(mutex)) { return -1; } - recursive_mutex_exit(mutex); + mutex_exit(mutex); return 0; } /** * Initialize a conditional variable. * @return 0 on success, platform-specific error number otherwise. - * /// TODO: mutex here not used */ int lf_cond_init(lf_cond_t* cond, lf_mutex_t* mutex) { - lf_mutex_lock(mutex); - // init cond var - cond->flag = false; - cond->num_waiting = 0; + if (!mutex_is_initialized(mutex)) { + return -1; + } cond->mut = mutex; - lf_mutex_unlock(mutex); + cond_init(&(cond->cv)); return 0; } @@ -329,10 +327,7 @@ int lf_cond_init(lf_cond_t* cond, lf_mutex_t* mutex) { * @return 0 on success, platform-specific error number otherwise. */ int lf_cond_broadcast(lf_cond_t* cond) { - lf_mutex_lock(cond->mut); - cond->flag = true; - cond->num_waiting = 0; - lf_mutex_unlock(cond->mut); + cond_broadcast(&(cond->cv)); return 0; } @@ -342,8 +337,7 @@ int lf_cond_broadcast(lf_cond_t* cond) { * @return 0 on success, platform-specific error number otherwise. */ int lf_cond_signal(lf_cond_t* cond) { - cond->flag = true; - cond->num_waiting--; + cond_signal(&(cond->cv)); return 0; } @@ -354,7 +348,9 @@ int lf_cond_signal(lf_cond_t* cond) { * @return 0 on success, platform-specific error number otherwise. */ int lf_cond_wait(lf_cond_t* cond) { - sem_acquire_blocking(cond); + mutex_enter_blocking(cond->mut); + cond_wait(&(cond->cv), cond->mut); + mutex_exit(cond->mut); return 0; } @@ -369,12 +365,185 @@ int lf_cond_wait(lf_cond_t* cond) { int lf_cond_timedwait(lf_cond_t* cond, instant_t absolute_time_ns) { absolute_time_t target; target = from_us_since_boot((uint64_t) (absolute_time_ns / 1000)); - if (!sem_acquire_block_until(cond, target)) { + mutex_enter_blocking(cond->mut); + if (!cond_wait_until(&(cond->cv), cond->mut, target)) { + mutex_exit(cond->mut); return LF_TIMEOUT; } + mutex_exit(cond->mut); return 0; } +void cond_init(cond_t *cond) { + lock_init(&cond->core, next_striped_spin_lock_num()); + cond->waiter = LOCK_INVALID_OWNER_ID; + cond->broadcast_count = 0; + cond->signaled = false; + __mem_fence_release(); +} + +bool __time_critical_func(cond_wait_until)(cond_t *cond, mutex_t *mtx, absolute_time_t until) { + bool success = true; + lock_owner_id_t caller = lock_get_caller_owner_id(); + uint32_t save = save_and_disable_interrupts(); + // Acquire the mutex spin lock + spin_lock_unsafe_blocking(mtx->core.spin_lock); + assert(lock_is_owner_id_valid(mtx->owner)); + assert(caller == mtx->owner); + + // Mutex and cond spin locks can be the same as spin locks are attributed + // using `next_striped_spin_lock_num()`. To avoid any deadlock, we only + // acquire the condition variable spin lock if it is different from the + // mutex spin lock + bool same_spinlock = mtx->core.spin_lock == cond->core.spin_lock; + + // Acquire the condition variable spin_lock + if (!same_spinlock) { + spin_lock_unsafe_blocking(cond->core.spin_lock); + } + + mtx->owner = LOCK_INVALID_OWNER_ID; + + uint64_t current_broadcast = cond->broadcast_count; + + if (lock_is_owner_id_valid(cond->waiter)) { + // Release the mutex but without restoring interrupts and notify. + if (!same_spinlock) { + spin_unlock_unsafe(mtx->core.spin_lock); + } + + // There is a valid owner of the condition variable: we are not the + // first waiter. + // First iteration: notify + lock_internal_spin_unlock_with_notify(&cond->core, save); + save = spin_lock_blocking(cond->core.spin_lock); + // Further iterations: wait + do { + if (!lock_is_owner_id_valid(cond->waiter)) { + break; + } + if (cond->broadcast_count != current_broadcast) { + break; + } + if (is_at_the_end_of_time(until)) { + lock_internal_spin_unlock_with_wait(&cond->core, save); + } else { + if (lock_internal_spin_unlock_with_best_effort_wait_or_timeout(&cond->core, save, until)) { + // timed out + success = false; + break; + } + } + save = spin_lock_blocking(cond->core.spin_lock); + } while (true); + } else { + // Release the mutex but without restoring interrupts + if (!same_spinlock) { + uint32_t disabled_ints = save_and_disable_interrupts(); + lock_internal_spin_unlock_with_notify(&mtx->core, disabled_ints); + } + } + + if (success && cond->broadcast_count == current_broadcast) { + cond->waiter = caller; + + // Wait for the signal + do { + if (cond->signaled) { + cond->waiter = LOCK_INVALID_OWNER_ID; + cond->signaled = false; + break; + } + if (is_at_the_end_of_time(until)) { + lock_internal_spin_unlock_with_wait(&cond->core, save); + } else { + if (lock_internal_spin_unlock_with_best_effort_wait_or_timeout(&cond->core, save, until)) { + // timed out + cond->waiter = LOCK_INVALID_OWNER_ID; + success = false; + break; + } + } + save = spin_lock_blocking(cond->core.spin_lock); + } while (true); + } + + // We got the signal (or timed out) + + if (lock_is_owner_id_valid(mtx->owner)) { + // Acquire the mutex spin lock and release the core spin lock. + if (!same_spinlock) { + spin_lock_unsafe_blocking(mtx->core.spin_lock); + spin_unlock_unsafe(cond->core.spin_lock); + } + + // Another core holds the mutex. + // First iteration: notify + lock_internal_spin_unlock_with_notify(&mtx->core, save); + save = spin_lock_blocking(mtx->core.spin_lock); + // Further iterations: wait + do { + if (!lock_is_owner_id_valid(mtx->owner)) { + break; + } + // We always wait for the mutex. + lock_internal_spin_unlock_with_wait(&mtx->core, save); + save = spin_lock_blocking(mtx->core.spin_lock); + } while (true); + } else { + // Acquire the mutex spin lock and release the core spin lock + // with notify but without restoring interrupts + if (!same_spinlock) { + spin_lock_unsafe_blocking(mtx->core.spin_lock); + uint32_t disabled_ints = save_and_disable_interrupts(); + lock_internal_spin_unlock_with_notify(&cond->core, disabled_ints); + } + } + + // Eventually hold the mutex. + mtx->owner = caller; + + // Restore the interrupts now + spin_unlock(mtx->core.spin_lock, save); + + return success; +} + +bool __time_critical_func(cond_wait_timeout_ms)(cond_t *cond, mutex_t *mtx, uint32_t timeout_ms) { + return cond_wait_until(cond, mtx, make_timeout_time_ms(timeout_ms)); +} + +bool __time_critical_func(cond_wait_timeout_us)(cond_t *cond, mutex_t *mtx, uint32_t timeout_us) { + return cond_wait_until(cond, mtx, make_timeout_time_us(timeout_us)); +} + +void __time_critical_func(cond_wait)(cond_t *cond, mutex_t *mtx) { + cond_wait_until(cond, mtx, at_the_end_of_time); +} + +void __time_critical_func(cond_signal)(cond_t *cond) { + uint32_t save = spin_lock_blocking(cond->core.spin_lock); + if (lock_is_owner_id_valid(cond->waiter)) { + // We have a waiter, we can signal. + cond->signaled = true; + lock_internal_spin_unlock_with_notify(&cond->core, save); + } else { + spin_unlock(cond->core.spin_lock, save); + } +} + +void __time_critical_func(cond_broadcast)(cond_t *cond) { + uint32_t save = spin_lock_blocking(cond->core.spin_lock); + if (lock_is_owner_id_valid(cond->waiter)) { + // We have a waiter, we can broadcast. + cond->signaled = true; + cond->broadcast_count++; + lock_internal_spin_unlock_with_notify(&cond->core, save); + } else { + spin_unlock(cond->core.spin_lock, save); + } +} + #endif // LF_THREADED #endif // PLATFORM_RP2040 diff --git a/include/core/platform/lf_rp2040_support.h b/include/core/platform/lf_rp2040_support.h index d6fdf691b..ee12c28ae 100644 --- a/include/core/platform/lf_rp2040_support.h +++ b/include/core/platform/lf_rp2040_support.h @@ -21,12 +21,22 @@ #define _LF_TIMEOUT 1 #ifdef LF_THREADED + +// pico-sdk condition variable +typedef struct __packed_aligned +{ + lock_core_t core; + lock_owner_id_t waiter; + uint32_t broadcast_count; // Overflow is unlikely + bool signaled; +} cond_t; + +// lf threading primitives typedef void *(*lf_function_t) (void *); -typedef recursive_mutex_t lf_mutex_t; -typedef struct { - bool flag; - uint32_t num_wait; - lf_mutex_t *mut; +typedef mutex_t lf_mutex_t; +typedef struct _lf_cond_t { + cond_t cv; + mutex_t *mut; } lf_cond_t; typedef enum { CORE_0, @@ -35,33 +45,84 @@ typedef enum { /** * @brief Method to run on core1 of pico to load - * second worker thread. + cond_t; + +/* \brief Initialize a condition variable structure + * \ingroup cond + * + * \param cv Pointer to condition variable structure */ -void _rp2040_core1_entry(); +void cond_init(cond_t *cv); -/** - * @brief Add `value` to `*ptr` and return original value of `*ptr` +/* \brief Wait on a condition variable + * \ingroup cond + * + * Wait until a condition variable is signaled or broadcast. The mutex should + * be owned and is released atomically. It is reacquired when this function + * returns. + * + * \param cv Condition variable to wait on + * \param mtx Currently held mutex */ -int _rp2040_atomic_fetch_add(int *ptr, int value); +void cond_wait(cond_t *cv, mutex_t *mtx); -/** - * @brief Add `value` to `*ptr` and return new updated value of `*ptr` +/* \brief Wait on a condition variable with a timeout. + * \ingroup cond + * + * Wait until a condition variable is signaled or broadcast until a given + * time. The mutex is released atomically and reacquired even if the wait + * timed out. + * + * \param cv Condition variable to wait on + * \param mtx Currently held mutex + * \param until The time after which to return if the condition variable was + * not signaled. + * \return true if the condition variable was signaled, false otherwise */ -int _rp2040_atomic_add_fetch(int *ptr, int value); +bool cond_wait_until(cond_t *cv, mutex_t *mtx, absolute_time_t until); -/** - * @brief Compare and swap for boolean value. - * If `*ptr` is equal to `value` then overwrite it with `newval` - * If not do nothing. Returns true on overwrite. +/* \brief Wait on a condition variable with a timeout. + * \ingroup cond + * + * Wait until a condition variable is signaled or broadcast until a given + * time. The mutex is released atomically and reacquired even if the wait + * timed out. + * + * \param cv Condition variable to wait on + * \param mtx Currently held mutex + * \param timeout_ms The timeout in milliseconds. + * \return true if the condition variable was signaled, false otherwise */ -bool _rp2040_bool_compare_and_swap(bool *ptr, bool value, bool newval); +bool cond_wait_timeout_ms(cond_t *cv, mutex_t *mtx, uint32_t timeout_ms); -/** - * @brief Compare and swap for integers. - * If `*ptr` is equal to `value`, it is updated to `newval`. - * If not do nothing. The function returns the original value of `*ptr`. +/* \brief Wait on a condition variable with a timeout. + * \ingroup cond + * + * Wait until a condition variable is signaled or broadcast until a given + * time. The mutex is released atomically and reacquired even if the wait + * timed out. + * + * \param cv Condition variable to wait on + * \param mtx Currently held mutex + * \param timeout_ms The timeout in microseconds. + * \return true if the condition variable was signaled, false otherwise + */ +bool cond_wait_timeout_us(cond_t *cv, mutex_t *mtx, uint32_t timeout_us); + +/* \brief Signal on a condition variable and wake the waiter + * \ingroup cond + * + * \param cv Condition variable to signal */ -int _rp2040_val_compare_and_swap(int *ptr, int value, int newval); +void cond_signal(cond_t *cv); + +/* \brief Broadcast a condition variable and wake every waiters + * \ingroup cond + * + * \param cv Condition variable to signal + */ +void cond_broadcast(cond_t *cv); + #endif // LF_THREADED #endif // LF_PICO_SUPPORT_H From 5f338b722671f0637c308cee52e2b4fba7d4b792 Mon Sep 17 00:00:00 2001 From: Abhinav Gundrala Date: Mon, 17 Jul 2023 01:37:35 -0500 Subject: [PATCH 13/22] [naming] change semaphore_t due to name collision --- core/utils/semaphore.c | 14 +++++++------- include/core/platform/lf_rp2040_support.h | 4 ++++ include/core/threaded/scheduler_instance.h | 2 +- include/core/utils/semaphore.h | 14 +++++++------- 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/core/utils/semaphore.c b/core/utils/semaphore.c index 7cf0322d5..87ce94443 100644 --- a/core/utils/semaphore.c +++ b/core/utils/semaphore.c @@ -38,10 +38,10 @@ THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * @brief Create a new semaphore. * * @param count The count to start with. - * @return semaphore_t* Can be NULL on error. + * @return lf_semaphore_t* Can be NULL on error. */ -semaphore_t* lf_semaphore_new(int count) { - semaphore_t* semaphore = (semaphore_t*)malloc(sizeof(semaphore_t)); +lf_semaphore_t* lf_semaphore_new(int count) { + lf_semaphore_t* semaphore = (lf_semaphore_t*)malloc(sizeof(lf_semaphore_t)); lf_mutex_init(&semaphore->mutex); lf_cond_init(&semaphore->cond, &semaphore->mutex); semaphore->count = count; @@ -54,7 +54,7 @@ semaphore_t* lf_semaphore_new(int count) { * @param semaphore Instance of a semaphore * @param i The count to add. */ -void lf_semaphore_release(semaphore_t* semaphore, int i) { +void lf_semaphore_release(lf_semaphore_t* semaphore, int i) { assert(semaphore != NULL); lf_mutex_lock(&semaphore->mutex); semaphore->count += i; @@ -67,7 +67,7 @@ void lf_semaphore_release(semaphore_t* semaphore, int i) { * * @param semaphore Instance of a semaphore. */ -void lf_semaphore_acquire(semaphore_t* semaphore) { +void lf_semaphore_acquire(lf_semaphore_t* semaphore) { assert(semaphore != NULL); lf_mutex_lock(&semaphore->mutex); while (semaphore->count == 0) { @@ -82,7 +82,7 @@ void lf_semaphore_acquire(semaphore_t* semaphore) { * * @param semaphore Instance of a semaphore. */ -void lf_semaphore_wait(semaphore_t* semaphore) { +void lf_semaphore_wait(lf_semaphore_t* semaphore) { assert(semaphore != NULL); lf_mutex_lock(&semaphore->mutex); while (semaphore->count == 0) { @@ -96,7 +96,7 @@ void lf_semaphore_wait(semaphore_t* semaphore) { * * @param semaphore Instance of a semaphore. */ -void lf_semaphore_destroy(semaphore_t* semaphore) { +void lf_semaphore_destroy(lf_semaphore_t* semaphore) { assert(semaphore != NULL); free(semaphore); } diff --git a/include/core/platform/lf_rp2040_support.h b/include/core/platform/lf_rp2040_support.h index ee12c28ae..599236534 100644 --- a/include/core/platform/lf_rp2040_support.h +++ b/include/core/platform/lf_rp2040_support.h @@ -43,6 +43,10 @@ typedef enum { CORE_1, } lf_thread_t; +void _rp2040_core1_entry(); +// TODO: add atomics + + /** * @brief Method to run on core1 of pico to load cond_t; diff --git a/include/core/threaded/scheduler_instance.h b/include/core/threaded/scheduler_instance.h index 5ecb356b1..976178509 100644 --- a/include/core/threaded/scheduler_instance.h +++ b/include/core/threaded/scheduler_instance.h @@ -77,7 +77,7 @@ typedef struct lf_scheduler_t { * * FIXME: specific comment */ - semaphore_t* semaphore; + lf_semaphore_t* semaphore; /** * @brief Indicate whether the program should stop diff --git a/include/core/utils/semaphore.h b/include/core/utils/semaphore.h index 7d49f756b..625dda84f 100644 --- a/include/core/utils/semaphore.h +++ b/include/core/utils/semaphore.h @@ -44,15 +44,15 @@ typedef struct { int count; lf_mutex_t mutex; lf_cond_t cond; -} semaphore_t; +} lf_semaphore_t; /** * @brief Create a new semaphore. * * @param count The count to start with. - * @return semaphore_t* Can be NULL on error. + * @return lf_semaphore_t* Can be NULL on error. */ -semaphore_t* lf_semaphore_new(int count); +lf_semaphore_t* lf_semaphore_new(int count); /** * @brief Release the 'semaphore' and add 'i' to its count. @@ -60,27 +60,27 @@ semaphore_t* lf_semaphore_new(int count); * @param semaphore Instance of a semaphore * @param i The count to add. */ -void lf_semaphore_release(semaphore_t* semaphore, int i); +void lf_semaphore_release(lf_semaphore_t* semaphore, int i); /** * @brief Acquire the 'semaphore'. Will block if count is 0. * * @param semaphore Instance of a semaphore. */ -void lf_semaphore_acquire(semaphore_t* semaphore); +void lf_semaphore_acquire(lf_semaphore_t* semaphore); /** * @brief Wait on the 'semaphore' if count is 0. * * @param semaphore Instance of a semaphore. */ -void lf_semaphore_wait(semaphore_t* semaphore); +void lf_semaphore_wait(lf_semaphore_t* semaphore); /** * @brief Destroy the 'semaphore'. * * @param semaphore Instance of a semaphore. */ -void lf_semaphore_destroy(semaphore_t* semaphore); +void lf_semaphore_destroy(lf_semaphore_t* semaphore); #endif // LF_SEMAPHORE_H From f9201af952fa86bde619ae96c84193942cc498fd Mon Sep 17 00:00:00 2001 From: Abhinav Gundrala Date: Mon, 17 Jul 2023 01:38:55 -0500 Subject: [PATCH 14/22] [minor] src fixes --- core/platform/lf_rp2040_support.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/platform/lf_rp2040_support.c b/core/platform/lf_rp2040_support.c index 510109907..f0de969b4 100644 --- a/core/platform/lf_rp2040_support.c +++ b/core/platform/lf_rp2040_support.c @@ -219,7 +219,9 @@ int lf_thread_create(lf_thread_t* thread, void *(*lf_thread) (void *), void* arg // this method can only be invoked twice. // each case for each core is specially handled static uint32_t call_cnt = 0; - if (call_cnt >= 2) { return -1 }; + if (call_cnt >= 2) { + return -1; + } switch (call_cnt) { case 0: _lf_core0_worker = (lf_function_t) lf_thread; @@ -256,7 +258,7 @@ int lf_thread_join(lf_thread_t thread, void** thread_return) { switch(thread) { case CORE_0: // start core0 worker, block until completion - *thread_return = _lf_core0_worker(args); + *thread_return = _lf_core0_worker(_lf_core0_args); break; case CORE_1: // use multicore fifo From bd704bece43c10c1a17d985250968b04d7feed7e Mon Sep 17 00:00:00 2001 From: Abhinav Gundrala Date: Mon, 17 Jul 2023 02:12:53 -0500 Subject: [PATCH 15/22] [atomics] add and iimplement --- core/platform/lf_rp2040_support.c | 66 +++++++++++++++++++++++ include/core/platform.h | 8 +++ include/core/platform/lf_rp2040_support.h | 7 ++- 3 files changed, 80 insertions(+), 1 deletion(-) diff --git a/core/platform/lf_rp2040_support.c b/core/platform/lf_rp2040_support.c index f0de969b4..0daed1b62 100644 --- a/core/platform/lf_rp2040_support.c +++ b/core/platform/lf_rp2040_support.c @@ -42,6 +42,7 @@ THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // critical section struct binding // TODO: maybe be more precise and use nvic interrupt mask static critical_section_t _lf_crit_sec; +static critical_section_t _lf_crit_sec_atomic; // semaphore used to notify if sleep was interupted by irq static semaphore_t _lf_sem_irq_event; static uint32_t _lf_num_nested_critical_sections = 0; @@ -54,6 +55,7 @@ void _lf_initialize_clock(void) { stdio_init_all(); // init sync structs critical_section_init(&_lf_crit_sec); + critical_section_init(&_lf_crit_sec_atomic); sem_init(&_lf_sem_irq_event, 0, 1); multicore_reset_core1(); } @@ -376,6 +378,70 @@ int lf_cond_timedwait(lf_cond_t* cond, instant_t absolute_time_ns) { return 0; } + +// Atomics + + +/** + * @brief Add `value` to `*ptr` and return original value of `*ptr` + * + */ +int _rp2040_atomic_fetch_add(int *ptr, int value) { + critical_section_enter_blocking(&_lf_crit_sec_atomic); + int res = *ptr; + *ptr += value; + critical_section_exit(&_lf_crit_sec_atomic); + return res; +} +/** + * @brief Add `value` to `*ptr` and return new updated value of `*ptr` + */ +int _rp2040_atomic_add_fetch(int *ptr, int value) { + //lf_disable_interrupts_nested(); + critical_section_enter_blocking(&_lf_crit_sec_atomic); + int res = *ptr + value; + *ptr = res; + //lf_enable_interrupts_nested(); + critical_section_exit(&_lf_crit_sec_atomic); + return res; +} + +/** + * @brief Compare and swap for boolaen value. + * If `*ptr` is equal to `value` then overwrite it + * with `newval`. If not do nothing. Retruns true on overwrite. + */ +bool _rp2040_bool_compare_and_swap(bool *ptr, bool value, bool newval) { + //lf_disable_interrupts_nested(); + critical_section_enter_blocking(&_lf_crit_sec_atomic); + bool res = false; + if (*ptr == value) { + *ptr = newval; + res = true; + } + //lf_enable_interrupts_nested(); + critical_section_exit(&_lf_crit_sec_atomic); + return res; +} + +/** + * @brief Compare and swap for integers. If `*ptr` is equal + * to `value`, it is updated to `newval`. The function returns + * the original value of `*ptr`. + */ +int _rp2040_val_compare_and_swap(int *ptr, int value, int newval) { + //lf_disable_interrupts_nested(); + critical_section_enter_blocking(&_lf_crit_sec_atomic); + int res = *ptr; + if (*ptr == value) { + *ptr = newval; + } + //lf_enable_interrupts_nested(); + critical_section_exit(&_lf_crit_sec_atomic); + return res; +} + + void cond_init(cond_t *cond) { lock_init(&cond->core, next_striped_spin_lock_num()); cond->waiter = LOCK_INVALID_OWNER_ID; diff --git a/include/core/platform.h b/include/core/platform.h index 3cd43043d..5aa20682b 100644 --- a/include/core/platform.h +++ b/include/core/platform.h @@ -210,6 +210,8 @@ int lf_cond_timedwait(lf_cond_t* cond, instant_t absolute_time_ns); */ #if defined(PLATFORM_ZEPHYR) #define lf_atomic_fetch_add(ptr, value) _zephyr_atomic_fetch_add((int*) ptr, value) +#elif defined(PLATFORM_RP2040) +#define lf_atomic_fetch_add(ptr, value) _rp2040_atomic_fetch_add((int*) ptr, value) #elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) // Assume that an integer is 32 bits. #define lf_atomic_fetch_add(ptr, value) InterlockedExchangeAdd(ptr, value) @@ -227,6 +229,8 @@ int lf_cond_timedwait(lf_cond_t* cond, instant_t absolute_time_ns); */ #if defined(PLATFORM_ZEPHYR) #define lf_atomic_add_fetch(ptr, value) _zephyr_atomic_add_fetch((int*) ptr, value) +#elif defined(PLATFORM_RP2040) +#define lf_atomic_add_fetch(ptr, value) _rp2040_atomic_add_fetch((int*) ptr, value) #elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) // Assume that an integer is 32 bits. #define lf_atomic_add_fetch(ptr, value) InterlockedAdd(ptr, value) @@ -246,6 +250,8 @@ int lf_cond_timedwait(lf_cond_t* cond, instant_t absolute_time_ns); */ #if defined(PLATFORM_ZEPHYR) #define lf_bool_compare_and_swap(ptr, value, newval) _zephyr_bool_compare_and_swap((bool*) ptr, value, newval) +#elif defined(PLATFORM_RP2040) +#define lf_bool_compare_and_swap(ptr, value, newval) _rp2040_bool_compare_and_swap((bool*) ptr, value, newval) #elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) // Assume that a boolean is represented with a 32-bit integer. #define lf_bool_compare_and_swap(ptr, oldval, newval) (InterlockedCompareExchange(ptr, newval, oldval) == oldval) @@ -265,6 +271,8 @@ int lf_cond_timedwait(lf_cond_t* cond, instant_t absolute_time_ns); */ #if defined(PLATFORM_ZEPHYR) #define lf_val_compare_and_swap(ptr, value, newval) _zephyr_val_compare_and_swap((int*) ptr, value, newval) +#elif defined(PLATFORM_RP2040) +#define lf_val_compare_and_swap(ptr, value, newval) _rp2040_val_compare_and_swap((int*) ptr, value, newval) #elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) #define lf_val_compare_and_swap(ptr, oldval, newval) InterlockedCompareExchange(ptr, newval, oldval) #elif defined(__GNUC__) || defined(__clang__) diff --git a/include/core/platform/lf_rp2040_support.h b/include/core/platform/lf_rp2040_support.h index 599236534..53a908a62 100644 --- a/include/core/platform/lf_rp2040_support.h +++ b/include/core/platform/lf_rp2040_support.h @@ -44,7 +44,12 @@ typedef enum { } lf_thread_t; void _rp2040_core1_entry(); -// TODO: add atomics + +// TODO: implement optimized versions +int _rp2040_atomic_fetch_add(int *ptr, int value); +int _rp2040_atomic_add_fetch(int *ptr, int value); +bool _rp2040_bool_compare_and_swap(bool *ptr, bool value, bool newval); +int _rp2040_val_compare_and_swap(int *ptr, int value, int newval); /** From 46d455d44dc087ebcb96806bd070ab36afcc4f47 Mon Sep 17 00:00:00 2001 From: Abhinav Gundrala Date: Fri, 21 Jul 2023 13:21:18 -0500 Subject: [PATCH 16/22] [refactor] remove untested thread code --- core/platform/lf_freertos_threads_support.c | 87 ---- core/platform/lf_rp2040_support.c | 440 +------------------- 2 files changed, 2 insertions(+), 525 deletions(-) delete mode 100644 core/platform/lf_freertos_threads_support.c diff --git a/core/platform/lf_freertos_threads_support.c b/core/platform/lf_freertos_threads_support.c deleted file mode 100644 index 2149caaf3..000000000 --- a/core/platform/lf_freertos_threads_support.c +++ /dev/null @@ -1,87 +0,0 @@ -#if defined(LF_THREADED) -// FIXME: build system with pico-support -#include "platform.h" -#include "lf_freertos_threads_support.h" - -#include -#include -#include // For fixed-width integral types - -int lf_thread_create(lf_thread_t* thread, void *(*lf_thread) (void *), void* arguments) { - return pthread_create((pthread_t*)thread, NULL, lf_thread, arguments); -} - -int lf_thread_join(lf_thread_t thread, void** thread_return) { - return pthread_join((pthread_t)thread, thread_return); -} - -int lf_mutex_init(lf_mutex_t* mutex) { - // Set up a recursive mutex - pthread_mutexattr_t attr; - pthread_mutexattr_init(&attr); - // Initialize the mutex to be recursive, meaning that it is OK - // for the same thread to lock and unlock the mutex even if it already holds - // the lock. - // FIXME: This is dangerous. The docs say this: "It is advised that an - // application should not use a PTHREAD_MUTEX_RECURSIVE mutex with - // condition variables because the implicit unlock performed for a - // pthread_cond_wait() or pthread_cond_timedwait() may not actually - // release the mutex (if it had been locked multiple times). - // If this happens, no other thread can satisfy the condition - // of the predicate.” This seems like a bug in the implementation of - // pthreads. Maybe it has been fixed? - pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); - return pthread_mutex_init((pthread_mutex_t*)mutex, &attr); -} - -int lf_mutex_lock(lf_mutex_t* mutex) { - return pthread_mutex_lock((pthread_mutex_t*)mutex); -} - -int lf_mutex_unlock(lf_mutex_t* mutex) { - return pthread_mutex_unlock((pthread_mutex_t*)mutex); -} - -int lf_cond_init(lf_cond_t* cond, lf_mutex_t* mutex) { - cond->mutex = mutex; - pthread_condattr_t cond_attr; - pthread_condattr_init(&cond_attr); - // Limit the scope of the condition variable to this process (default) - pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_PRIVATE); - return pthread_cond_init(&cond->condition, &cond_attr); -} - -int lf_cond_broadcast(lf_cond_t* cond) { - return pthread_cond_broadcast((pthread_cond_t*)&cond->condition); -} - -int lf_cond_signal(lf_cond_t* cond) { - return pthread_cond_signal((pthread_cond_t*)&cond->condition); -} - -int lf_cond_wait(lf_cond_t* cond) { - return pthread_cond_wait((pthread_cond_t*)&cond->condition, (pthread_mutex_t*)cond->mutex); -} - -int lf_cond_timedwait(lf_cond_t* cond, int64_t absolute_time_ns) { - // Convert the absolute time to a timespec. - // timespec is seconds and nanoseconds. - struct timespec timespec_absolute_time - = {(time_t)absolute_time_ns / 1000000000LL, (long)absolute_time_ns % 1000000000LL}; - int return_value = 0; - return_value = pthread_cond_timedwait( - (pthread_cond_t*)&cond->condition, - (pthread_mutex_t*)cond->mutex, - ×pec_absolute_time - ); - switch (return_value) { - case ETIMEDOUT: - return_value = LF_TIMEOUT; - break; - - default: - break; - } - return return_value; -} -#endif diff --git a/core/platform/lf_rp2040_support.c b/core/platform/lf_rp2040_support.c index 0daed1b62..00eeacab0 100644 --- a/core/platform/lf_rp2040_support.c +++ b/core/platform/lf_rp2040_support.c @@ -42,7 +42,6 @@ THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // critical section struct binding // TODO: maybe be more precise and use nvic interrupt mask static critical_section_t _lf_crit_sec; -static critical_section_t _lf_crit_sec_atomic; // semaphore used to notify if sleep was interupted by irq static semaphore_t _lf_sem_irq_event; static uint32_t _lf_num_nested_critical_sections = 0; @@ -55,9 +54,7 @@ void _lf_initialize_clock(void) { stdio_init_all(); // init sync structs critical_section_init(&_lf_crit_sec); - critical_section_init(&_lf_crit_sec_atomic); sem_init(&_lf_sem_irq_event, 0, 1); - multicore_reset_core1(); } /** @@ -177,441 +174,8 @@ int _lf_unthreaded_notify_of_event() { // abstract the API so that the LF runtime remains portable. #ifdef LF_THREADED -#warning "Baremetal threaded support only allows two threads of execution" +#error "Threading for baremetal RP2040 not supported" -// If threaded is enabled set number of workers -// Compiler warning when NUMBER_OF_WORKERS > 2 -#if !defined(NUMBER_OF_WORKERS) || NUMBER_OF_WORKERS==0 -#undef NUMBER_OF_WORKERS -#define NUMBER_OF_WORKERS 2 -#endif - -static lf_function_t _lf_core0_worker; -static void *_lf_core0_args; - -static lf_function_t _lf_core1_worker; -static void *_lf_core1_args; - - -void _rp2040_core1_entry() { - void *res = _lf_core1_worker(_lf_core1_args); - // use fifo and send result - // after worker exit fill fifo with result and block - while(multicore_fifo_wready()) { - multicore_fifo_push_blocking((uint32_t) res); - } -} - -/** - * @brief Get the number of cores on the host machine. - * pico has two physical cores and runs only two worker threads - */ -int lf_available_cores() { - return 2; -} - -/** - * Create a new thread, starting with execution of lf_thread - * getting passed arguments. The new handle is stored in thread_id. - * - * @return 0 on success, platform-specific error number otherwise. - * TODO: learn more about function pointers and resolving this interface - */ -int lf_thread_create(lf_thread_t* thread, void *(*lf_thread) (void *), void* arguments) { - // this method can only be invoked twice. - // each case for each core is specially handled - static uint32_t call_cnt = 0; - if (call_cnt >= 2) { - return -1; - } - switch (call_cnt) { - case 0: - _lf_core0_worker = (lf_function_t) lf_thread; - _lf_core0_args = arguments; - *thread = CORE_0; - // cant launch first core worker - break; - case 1: - _lf_core1_worker = (lf_function_t) lf_thread; - _lf_core1_args = arguments; - *thread = CORE_1; - // launch second core worker - multicore_launch_core1(&_rp2040_core1_entry); - break; - default: - return -1; - } - call_cnt++; - return 0; -} - -/** - * Make calling thread wait for termination of the thread. The - * exit status of the thread is stored in thread_return if thread_return - * is not NULL. - * @param thread The thread. - * @param thread_return A pointer to where to store the exit status of the thread. - * - * @return 0 on success, platform-specific error number otherwise. - */ -int lf_thread_join(lf_thread_t thread, void** thread_return) { - /// TODO: implement - /// run the core0 worker method here till completion and fill thread return - switch(thread) { - case CORE_0: - // start core0 worker, block until completion - *thread_return = _lf_core0_worker(_lf_core0_args); - break; - case CORE_1: - // use multicore fifo - // remove any extraneous messages from fifo - multicore_fifo_drain(); - // block until thread return value received from fifo - *thread_return = (void *) multicore_fifo_pop_blocking(); - break; - default: - return -1; - } - return 0; -} - -/** - * Initialize a mutex. - * - * @return 0 on success, platform-specific error number otherwise. - */ -int lf_mutex_init(lf_mutex_t* mutex) { - mutex_init(mutex); - return 0; -} - -/** - * Lock a mutex. - * - * @return 0 on success, platform-specific error number otherwise. - * TODO: should this block? - */ -int lf_mutex_lock(lf_mutex_t* mutex) { - if (!mutex_is_initialized(mutex)) { - return -1; - } - mutex_enter_blocking(mutex); - return 0; -} - -/** - * Unlock a mutex. - * - * @return 0 on success, platform-specific error number otherwise. - */ -int lf_mutex_unlock(lf_mutex_t* mutex) { - if (!mutex_is_initialized(mutex)) { - return -1; - } - mutex_exit(mutex); - return 0; -} - -/** - * Initialize a conditional variable. - * @return 0 on success, platform-specific error number otherwise. - */ -int lf_cond_init(lf_cond_t* cond, lf_mutex_t* mutex) { - if (!mutex_is_initialized(mutex)) { - return -1; - } - cond->mut = mutex; - cond_init(&(cond->cv)); - return 0; -} - -/** - * Wake up all threads waiting for condition variable cond. - * - * @return 0 on success, platform-specific error number otherwise. - */ -int lf_cond_broadcast(lf_cond_t* cond) { - cond_broadcast(&(cond->cv)); - return 0; -} - -/** - * Wake up one thread waiting for condition variable cond. - * - * @return 0 on success, platform-specific error number otherwise. - */ -int lf_cond_signal(lf_cond_t* cond) { - cond_signal(&(cond->cv)); - return 0; -} - -/** - * Wait for condition variable "cond" to be signaled or broadcast. - * "mutex" is assumed to be locked before. - * - * @return 0 on success, platform-specific error number otherwise. - */ -int lf_cond_wait(lf_cond_t* cond) { - mutex_enter_blocking(cond->mut); - cond_wait(&(cond->cv), cond->mut); - mutex_exit(cond->mut); - return 0; -} - -/** - * Block current thread on the condition variable until condition variable - * pointed by "cond" is signaled or time pointed by "absolute_time_ns" in - * nanoseconds is reached. - * - * @return 0 on success, LF_TIMEOUT on timeout, and platform-specific error - * number otherwise. - */ -int lf_cond_timedwait(lf_cond_t* cond, instant_t absolute_time_ns) { - absolute_time_t target; - target = from_us_since_boot((uint64_t) (absolute_time_ns / 1000)); - mutex_enter_blocking(cond->mut); - if (!cond_wait_until(&(cond->cv), cond->mut, target)) { - mutex_exit(cond->mut); - return LF_TIMEOUT; - } - mutex_exit(cond->mut); - return 0; -} - - -// Atomics - - -/** - * @brief Add `value` to `*ptr` and return original value of `*ptr` - * - */ -int _rp2040_atomic_fetch_add(int *ptr, int value) { - critical_section_enter_blocking(&_lf_crit_sec_atomic); - int res = *ptr; - *ptr += value; - critical_section_exit(&_lf_crit_sec_atomic); - return res; -} -/** - * @brief Add `value` to `*ptr` and return new updated value of `*ptr` - */ -int _rp2040_atomic_add_fetch(int *ptr, int value) { - //lf_disable_interrupts_nested(); - critical_section_enter_blocking(&_lf_crit_sec_atomic); - int res = *ptr + value; - *ptr = res; - //lf_enable_interrupts_nested(); - critical_section_exit(&_lf_crit_sec_atomic); - return res; -} - -/** - * @brief Compare and swap for boolaen value. - * If `*ptr` is equal to `value` then overwrite it - * with `newval`. If not do nothing. Retruns true on overwrite. - */ -bool _rp2040_bool_compare_and_swap(bool *ptr, bool value, bool newval) { - //lf_disable_interrupts_nested(); - critical_section_enter_blocking(&_lf_crit_sec_atomic); - bool res = false; - if (*ptr == value) { - *ptr = newval; - res = true; - } - //lf_enable_interrupts_nested(); - critical_section_exit(&_lf_crit_sec_atomic); - return res; -} - -/** - * @brief Compare and swap for integers. If `*ptr` is equal - * to `value`, it is updated to `newval`. The function returns - * the original value of `*ptr`. - */ -int _rp2040_val_compare_and_swap(int *ptr, int value, int newval) { - //lf_disable_interrupts_nested(); - critical_section_enter_blocking(&_lf_crit_sec_atomic); - int res = *ptr; - if (*ptr == value) { - *ptr = newval; - } - //lf_enable_interrupts_nested(); - critical_section_exit(&_lf_crit_sec_atomic); - return res; -} - - -void cond_init(cond_t *cond) { - lock_init(&cond->core, next_striped_spin_lock_num()); - cond->waiter = LOCK_INVALID_OWNER_ID; - cond->broadcast_count = 0; - cond->signaled = false; - __mem_fence_release(); -} - -bool __time_critical_func(cond_wait_until)(cond_t *cond, mutex_t *mtx, absolute_time_t until) { - bool success = true; - lock_owner_id_t caller = lock_get_caller_owner_id(); - uint32_t save = save_and_disable_interrupts(); - // Acquire the mutex spin lock - spin_lock_unsafe_blocking(mtx->core.spin_lock); - assert(lock_is_owner_id_valid(mtx->owner)); - assert(caller == mtx->owner); - - // Mutex and cond spin locks can be the same as spin locks are attributed - // using `next_striped_spin_lock_num()`. To avoid any deadlock, we only - // acquire the condition variable spin lock if it is different from the - // mutex spin lock - bool same_spinlock = mtx->core.spin_lock == cond->core.spin_lock; - - // Acquire the condition variable spin_lock - if (!same_spinlock) { - spin_lock_unsafe_blocking(cond->core.spin_lock); - } - - mtx->owner = LOCK_INVALID_OWNER_ID; - - uint64_t current_broadcast = cond->broadcast_count; - - if (lock_is_owner_id_valid(cond->waiter)) { - // Release the mutex but without restoring interrupts and notify. - if (!same_spinlock) { - spin_unlock_unsafe(mtx->core.spin_lock); - } - - // There is a valid owner of the condition variable: we are not the - // first waiter. - // First iteration: notify - lock_internal_spin_unlock_with_notify(&cond->core, save); - save = spin_lock_blocking(cond->core.spin_lock); - // Further iterations: wait - do { - if (!lock_is_owner_id_valid(cond->waiter)) { - break; - } - if (cond->broadcast_count != current_broadcast) { - break; - } - if (is_at_the_end_of_time(until)) { - lock_internal_spin_unlock_with_wait(&cond->core, save); - } else { - if (lock_internal_spin_unlock_with_best_effort_wait_or_timeout(&cond->core, save, until)) { - // timed out - success = false; - break; - } - } - save = spin_lock_blocking(cond->core.spin_lock); - } while (true); - } else { - // Release the mutex but without restoring interrupts - if (!same_spinlock) { - uint32_t disabled_ints = save_and_disable_interrupts(); - lock_internal_spin_unlock_with_notify(&mtx->core, disabled_ints); - } - } - - if (success && cond->broadcast_count == current_broadcast) { - cond->waiter = caller; - - // Wait for the signal - do { - if (cond->signaled) { - cond->waiter = LOCK_INVALID_OWNER_ID; - cond->signaled = false; - break; - } - if (is_at_the_end_of_time(until)) { - lock_internal_spin_unlock_with_wait(&cond->core, save); - } else { - if (lock_internal_spin_unlock_with_best_effort_wait_or_timeout(&cond->core, save, until)) { - // timed out - cond->waiter = LOCK_INVALID_OWNER_ID; - success = false; - break; - } - } - save = spin_lock_blocking(cond->core.spin_lock); - } while (true); - } - - // We got the signal (or timed out) - - if (lock_is_owner_id_valid(mtx->owner)) { - // Acquire the mutex spin lock and release the core spin lock. - if (!same_spinlock) { - spin_lock_unsafe_blocking(mtx->core.spin_lock); - spin_unlock_unsafe(cond->core.spin_lock); - } - - // Another core holds the mutex. - // First iteration: notify - lock_internal_spin_unlock_with_notify(&mtx->core, save); - save = spin_lock_blocking(mtx->core.spin_lock); - // Further iterations: wait - do { - if (!lock_is_owner_id_valid(mtx->owner)) { - break; - } - // We always wait for the mutex. - lock_internal_spin_unlock_with_wait(&mtx->core, save); - save = spin_lock_blocking(mtx->core.spin_lock); - } while (true); - } else { - // Acquire the mutex spin lock and release the core spin lock - // with notify but without restoring interrupts - if (!same_spinlock) { - spin_lock_unsafe_blocking(mtx->core.spin_lock); - uint32_t disabled_ints = save_and_disable_interrupts(); - lock_internal_spin_unlock_with_notify(&cond->core, disabled_ints); - } - } - - // Eventually hold the mutex. - mtx->owner = caller; - - // Restore the interrupts now - spin_unlock(mtx->core.spin_lock, save); - - return success; -} - -bool __time_critical_func(cond_wait_timeout_ms)(cond_t *cond, mutex_t *mtx, uint32_t timeout_ms) { - return cond_wait_until(cond, mtx, make_timeout_time_ms(timeout_ms)); -} - -bool __time_critical_func(cond_wait_timeout_us)(cond_t *cond, mutex_t *mtx, uint32_t timeout_us) { - return cond_wait_until(cond, mtx, make_timeout_time_us(timeout_us)); -} - -void __time_critical_func(cond_wait)(cond_t *cond, mutex_t *mtx) { - cond_wait_until(cond, mtx, at_the_end_of_time); -} - -void __time_critical_func(cond_signal)(cond_t *cond) { - uint32_t save = spin_lock_blocking(cond->core.spin_lock); - if (lock_is_owner_id_valid(cond->waiter)) { - // We have a waiter, we can signal. - cond->signaled = true; - lock_internal_spin_unlock_with_notify(&cond->core, save); - } else { - spin_unlock(cond->core.spin_lock, save); - } -} - -void __time_critical_func(cond_broadcast)(cond_t *cond) { - uint32_t save = spin_lock_blocking(cond->core.spin_lock); - if (lock_is_owner_id_valid(cond->waiter)) { - // We have a waiter, we can broadcast. - cond->signaled = true; - cond->broadcast_count++; - lock_internal_spin_unlock_with_notify(&cond->core, save); - } else { - spin_unlock(cond->core.spin_lock, save); - } -} - -#endif // LF_THREADED +#endif //LF_THREADED #endif // PLATFORM_RP2040 From f1ed9aabb7fc66d210077172271a2226af492cbf Mon Sep 17 00:00:00 2001 From: Abhinav Gundrala Date: Fri, 21 Jul 2023 13:25:01 -0500 Subject: [PATCH 17/22] [refactor] remove untested thread includes --- core/platform/lf_rp2040_support.c | 2 +- .../platform/lf_freertos_threads_support.h | 48 -------- include/core/platform/lf_rp2040_support.h | 114 +----------------- 3 files changed, 3 insertions(+), 161 deletions(-) delete mode 100644 include/core/platform/lf_freertos_threads_support.h diff --git a/core/platform/lf_rp2040_support.c b/core/platform/lf_rp2040_support.c index 00eeacab0..8e388d73f 100644 --- a/core/platform/lf_rp2040_support.c +++ b/core/platform/lf_rp2040_support.c @@ -175,7 +175,7 @@ int _lf_unthreaded_notify_of_event() { #ifdef LF_THREADED #error "Threading for baremetal RP2040 not supported" - #endif //LF_THREADED + #endif // PLATFORM_RP2040 diff --git a/include/core/platform/lf_freertos_threads_support.h b/include/core/platform/lf_freertos_threads_support.h deleted file mode 100644 index 128873be2..000000000 --- a/include/core/platform/lf_freertos_threads_support.h +++ /dev/null @@ -1,48 +0,0 @@ -/* FreeRTOS threading support for the C target of Lingua Franca. */ - -/************* -Copyright (c) 2021, 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. -***************/ - -/** - * FreeRTOS thread support. - * For multicore devices, the SMP version of freertos is used. - * - * @author{Soroush Bateni } - * - * All functions return 0 on success. - */ - -#ifndef LF_FREERTOS_THREADS_SUPPORT_H -#define LF_FREERTOS_THREADS_SUPPORT_H - -#include - -typedef pthread_mutex_t lf_mutex_t; -typedef struct { - lf_mutex_t* mutex; - pthread_cond_t condition; -} lf_cond_t; -typedef pthread_t lf_thread_t; - -#endif diff --git a/include/core/platform/lf_rp2040_support.h b/include/core/platform/lf_rp2040_support.h index 53a908a62..b522d9e65 100644 --- a/include/core/platform/lf_rp2040_support.h +++ b/include/core/platform/lf_rp2040_support.h @@ -21,117 +21,7 @@ #define _LF_TIMEOUT 1 #ifdef LF_THREADED - -// pico-sdk condition variable -typedef struct __packed_aligned -{ - lock_core_t core; - lock_owner_id_t waiter; - uint32_t broadcast_count; // Overflow is unlikely - bool signaled; -} cond_t; - -// lf threading primitives -typedef void *(*lf_function_t) (void *); -typedef mutex_t lf_mutex_t; -typedef struct _lf_cond_t { - cond_t cv; - mutex_t *mut; -} lf_cond_t; -typedef enum { - CORE_0, - CORE_1, -} lf_thread_t; - -void _rp2040_core1_entry(); - -// TODO: implement optimized versions -int _rp2040_atomic_fetch_add(int *ptr, int value); -int _rp2040_atomic_add_fetch(int *ptr, int value); -bool _rp2040_bool_compare_and_swap(bool *ptr, bool value, bool newval); -int _rp2040_val_compare_and_swap(int *ptr, int value, int newval); - - -/** - * @brief Method to run on core1 of pico to load - cond_t; - -/* \brief Initialize a condition variable structure - * \ingroup cond - * - * \param cv Pointer to condition variable structure - */ -void cond_init(cond_t *cv); - -/* \brief Wait on a condition variable - * \ingroup cond - * - * Wait until a condition variable is signaled or broadcast. The mutex should - * be owned and is released atomically. It is reacquired when this function - * returns. - * - * \param cv Condition variable to wait on - * \param mtx Currently held mutex - */ -void cond_wait(cond_t *cv, mutex_t *mtx); - -/* \brief Wait on a condition variable with a timeout. - * \ingroup cond - * - * Wait until a condition variable is signaled or broadcast until a given - * time. The mutex is released atomically and reacquired even if the wait - * timed out. - * - * \param cv Condition variable to wait on - * \param mtx Currently held mutex - * \param until The time after which to return if the condition variable was - * not signaled. - * \return true if the condition variable was signaled, false otherwise - */ -bool cond_wait_until(cond_t *cv, mutex_t *mtx, absolute_time_t until); - -/* \brief Wait on a condition variable with a timeout. - * \ingroup cond - * - * Wait until a condition variable is signaled or broadcast until a given - * time. The mutex is released atomically and reacquired even if the wait - * timed out. - * - * \param cv Condition variable to wait on - * \param mtx Currently held mutex - * \param timeout_ms The timeout in milliseconds. - * \return true if the condition variable was signaled, false otherwise - */ -bool cond_wait_timeout_ms(cond_t *cv, mutex_t *mtx, uint32_t timeout_ms); - -/* \brief Wait on a condition variable with a timeout. - * \ingroup cond - * - * Wait until a condition variable is signaled or broadcast until a given - * time. The mutex is released atomically and reacquired even if the wait - * timed out. - * - * \param cv Condition variable to wait on - * \param mtx Currently held mutex - * \param timeout_ms The timeout in microseconds. - * \return true if the condition variable was signaled, false otherwise - */ -bool cond_wait_timeout_us(cond_t *cv, mutex_t *mtx, uint32_t timeout_us); - -/* \brief Signal on a condition variable and wake the waiter - * \ingroup cond - * - * \param cv Condition variable to signal - */ -void cond_signal(cond_t *cv); - -/* \brief Broadcast a condition variable and wake every waiters - * \ingroup cond - * - * \param cv Condition variable to signal - */ -void cond_broadcast(cond_t *cv); - - +#error "Threading for baremetal RP2040 not supported" #endif // LF_THREADED + #endif // LF_PICO_SUPPORT_H From 13f00e8b7e4caa9f6172a64a6a2473d97096e392 Mon Sep 17 00:00:00 2001 From: Abhinav Gundrala Date: Fri, 21 Jul 2023 13:26:47 -0500 Subject: [PATCH 18/22] [refactor] remove atomics defiinees --- include/core/platform.h | 8 -------- 1 file changed, 8 deletions(-) diff --git a/include/core/platform.h b/include/core/platform.h index 5505e2d02..a32cfb516 100644 --- a/include/core/platform.h +++ b/include/core/platform.h @@ -228,8 +228,6 @@ int lf_cond_timedwait(lf_cond_t* cond, instant_t absolute_time_ns); */ #if defined(PLATFORM_ZEPHYR) #define lf_atomic_fetch_add(ptr, value) _zephyr_atomic_fetch_add((int*) ptr, value) -#elif defined(PLATFORM_RP2040) -#define lf_atomic_fetch_add(ptr, value) _rp2040_atomic_fetch_add((int*) ptr, value) #elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) // Assume that an integer is 32 bits. #define lf_atomic_fetch_add(ptr, value) InterlockedExchangeAdd(ptr, value) @@ -247,8 +245,6 @@ int lf_cond_timedwait(lf_cond_t* cond, instant_t absolute_time_ns); */ #if defined(PLATFORM_ZEPHYR) #define lf_atomic_add_fetch(ptr, value) _zephyr_atomic_add_fetch((int*) ptr, value) -#elif defined(PLATFORM_RP2040) -#define lf_atomic_add_fetch(ptr, value) _rp2040_atomic_add_fetch((int*) ptr, value) #elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) // Assume that an integer is 32 bits. #define lf_atomic_add_fetch(ptr, value) InterlockedAdd(ptr, value) @@ -268,8 +264,6 @@ int lf_cond_timedwait(lf_cond_t* cond, instant_t absolute_time_ns); */ #if defined(PLATFORM_ZEPHYR) #define lf_bool_compare_and_swap(ptr, value, newval) _zephyr_bool_compare_and_swap((bool*) ptr, value, newval) -#elif defined(PLATFORM_RP2040) -#define lf_bool_compare_and_swap(ptr, value, newval) _rp2040_bool_compare_and_swap((bool*) ptr, value, newval) #elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) // Assume that a boolean is represented with a 32-bit integer. #define lf_bool_compare_and_swap(ptr, oldval, newval) (InterlockedCompareExchange(ptr, newval, oldval) == oldval) @@ -289,8 +283,6 @@ int lf_cond_timedwait(lf_cond_t* cond, instant_t absolute_time_ns); */ #if defined(PLATFORM_ZEPHYR) #define lf_val_compare_and_swap(ptr, value, newval) _zephyr_val_compare_and_swap((int*) ptr, value, newval) -#elif defined(PLATFORM_RP2040) -#define lf_val_compare_and_swap(ptr, value, newval) _rp2040_val_compare_and_swap((int*) ptr, value, newval) #elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) #define lf_val_compare_and_swap(ptr, oldval, newval) InterlockedCompareExchange(ptr, newval, oldval) #elif defined(__GNUC__) || defined(__clang__) From e27c7471869b4d721d8c7e79dda69ccc4b51c189 Mon Sep 17 00:00:00 2001 From: Abhinav Gundrala Date: Sat, 22 Jul 2023 15:09:30 -0500 Subject: [PATCH 19/22] [docs] modify/resolve support doc comments --- core/platform/lf_rp2040_support.c | 116 +++++++++++++----------------- 1 file changed, 49 insertions(+), 67 deletions(-) diff --git a/core/platform/lf_rp2040_support.c b/core/platform/lf_rp2040_support.c index 8e388d73f..2a9f42bcf 100644 --- a/core/platform/lf_rp2040_support.c +++ b/core/platform/lf_rp2040_support.c @@ -39,47 +39,42 @@ THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -// critical section struct binding -// TODO: maybe be more precise and use nvic interrupt mask +/* + * critical section struct + * disables external irq and core execution + * provides mutual exclusion using hardware spin-locks + */ static critical_section_t _lf_crit_sec; -// semaphore used to notify if sleep was interupted by irq -static semaphore_t _lf_sem_irq_event; -static uint32_t _lf_num_nested_critical_sections = 0; -/** - * Initialize the LF clock. Must be called before using other clock-related APIs. +/* + * binary semaphore for lf event notification + * used by external isr or core threads + * used to interact with the lf runtime thread */ +static semaphore_t _lf_sem_irq_event; + +// nested critical section counter +static uint32_t _lf_num_nested_crit_sec = 0; + void _lf_initialize_clock(void) { - // init stdlib peripherals + // init stdio lib stdio_init_all(); // init sync structs critical_section_init(&_lf_crit_sec); sem_init(&_lf_sem_irq_event, 0, 1); } -/** - * Fetch the value of an internal (and platform-specific) physical clock and - * store it in `t`. in nanoseconds - * - * Ideally, the underlying platform clock should be monotonic. However, the - * core lib tries to enforce monotonicity at higher level APIs (see tag.h). - * TODO: might want to use the RTC - * @return 0 for success, or -1 for failure - */ int _lf_clock_now(instant_t* t) { + // time struct absolute_time_t now; uint64_t ns_from_boot; + now = get_absolute_time(); ns_from_boot = to_us_since_boot(now) * 1000; *t = (instant_t) ns_from_boot; return 0; } -/** - * Pause execution for a given duration. - * - * @return 0 for success, or -1 for failure. - */ int lf_sleep(interval_t sleep_duration) { if (sleep_duration < 0) { return -1; @@ -88,91 +83,78 @@ int lf_sleep(interval_t sleep_duration) { return 0; } -/** - * @brief Sleep until the given wakeup time. - * - * @param wakeup_time The time instant at which to wake up. - * @return int 0 if sleep completed, or -1 if it was interrupted. - */ int _lf_interruptable_sleep_until_locked(environment_t* env, instant_t wakeup_time) { int ret_code = 0; + // return error if (wakeup_time < 0) { ret_code = -1; return ret_code; } + // time struct absolute_time_t target; - // reset semaphore to 0 - // TODO: leverage the semaphore permit number + + // reset event semaphore sem_reset(&_lf_sem_irq_event, 0); + // create us boot wakeup time target = from_us_since_boot((uint64_t) (wakeup_time / 1000)); + // allow interrupts lf_critical_section_exit(env); - // sleep till target or return on processor event + // blocked sleep + // return on timeout or on processor event if(sem_acquire_block_until(&_lf_sem_irq_event, target)) { ret_code = -1; } + // remove interrupts lf_critical_section_enter(env); return ret_code; } -/* -* Critical sections are only provided for an unthreaded, single core -* runtime. In the unthreaded runtime, all interactions with core1 are through -* physical actions and interupts outside of the runtime. -*/ -#ifdef LF_UNTHREADED -/** - * Enter a critical section where logical time and the event queue are guaranteed - * to not change unless they are changed within the critical section. - * this can be implemented by disabling interrupts. - * Users of this function must ensure that lf_init_critical_sections() is - * called first and that lf_critical_section_exit() is called later. - * @return 0 on success, platform-specific error number otherwise. - * TODO: needs to be used sparingly +#ifdef LF_UNTHREADED +/* + * The single thread rp2040 platform support treats second core + * routines similar to external interrupt routine threads. + * + * Second core activity is disabled at the same times as + * when interrupts are disabled. */ int lf_disable_interrupts_nested() { if (!critical_section_is_initialized(&_lf_crit_sec)) { return 1; - } - // disables irq and spin-locks core - if (_lf_num_nested_critical_sections++ == 0) { + } + // check crit sec count + // enter non-rentrant state + // prevent second core access and disable interrupts + if (_lf_num_nested_crit_sec == 0) { + // block if associated spin lock in use critical_section_enter_blocking(&_lf_crit_sec); } + // add crit sec count + _lf_num_nested_crit_sec++; return 0; } -/** - * Exit the critical section entered with lf_lock_time(). - * @return 0 on success, platform-specific error number otherwise. - * TODO: needs to be used sparingly, find a better way for event queue - * mutual exclusion for embedded platforms. better leverage the nvic - */ int lf_enable_interrupts_nested() { if (!critical_section_is_initialized(&_lf_crit_sec) || - _lf_num_nested_critical_sections <= 0) { + _lf_num_nested_crit_sec <= 0) { return 1; - } - // restores system execution state - if (--_lf_num_nested_critical_sections == 0) { + } + // remove from crit sec count + _lf_num_nested_crit_sec--; + // check crit sec count + // release spin-lock + if (_lf_num_nested_crit_sec == 0) { critical_section_exit(&_lf_crit_sec); } return 0; } -/** - * Notify any listeners that an event has been created. - * The caller should call lf_critical_section_enter() before calling this function. - * @return 0 on success, platform-specific error number otherwise. - */ int _lf_unthreaded_notify_of_event() { - // un-block threads that acquired this binary semaphore + // notify main sleep loop of event sem_release(&_lf_sem_irq_event); return 0; } #endif //LF_UNTHREADED -// For platforms with threading support, the following functions -// abstract the API so that the LF runtime remains portable. - #ifdef LF_THREADED #error "Threading for baremetal RP2040 not supported" #endif //LF_THREADED From 77ce2c42c900f1aeaeadaddec5efd53ceca69bae Mon Sep 17 00:00:00 2001 From: Abhinav Gundrala Date: Mon, 24 Jul 2023 12:23:38 -0700 Subject: [PATCH 20/22] [docs] resolve comments, add doc strings --- core/CMakeLists.txt | 7 +-- core/platform/lf_rp2040_support.c | 71 ++++++++++++++++++++++++++++--- 2 files changed, 66 insertions(+), 12 deletions(-) diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index abe9bf5b5..eb452f181 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -76,11 +76,8 @@ endif() # Link with thread library if(DEFINED LF_THREADED OR DEFINED LF_TRACE) - # FIXME make this more general for other platforms - if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Rp2040") - find_package(Threads REQUIRED) - target_link_libraries(core PUBLIC Threads::Threads) - endif() + find_package(Threads REQUIRED) + target_link_libraries(core PUBLIC Threads::Threads) endif() # Macro for translating a command-line argument into compile definition for diff --git a/core/platform/lf_rp2040_support.c b/core/platform/lf_rp2040_support.c index 2a9f42bcf..257766853 100644 --- a/core/platform/lf_rp2040_support.c +++ b/core/platform/lf_rp2040_support.c @@ -24,8 +24,9 @@ THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************/ /** - * @brief Raspberry Pi Pico support for the C target of Lingua Franca - * Uses the pico-sdk which targets the lower level peripheral layer. + * @brief RP2040 mcu support for the C target of Lingua Franca. + * This utilizes the pico-sdk which provides C methods for a light runtime + * and a hardware abstraction layer. * * @author{Abhi Gundrala } */ @@ -48,7 +49,7 @@ static critical_section_t _lf_crit_sec; /* * binary semaphore for lf event notification - * used by external isr or core threads + * used by external isr or second core thread. * used to interact with the lf runtime thread */ static semaphore_t _lf_sem_irq_event; @@ -56,6 +57,10 @@ static semaphore_t _lf_sem_irq_event; // nested critical section counter static uint32_t _lf_num_nested_crit_sec = 0; +/* + * Initialize basic runtime infrastructure and + * synchronization structs for an unthreaded runtime. + */ void _lf_initialize_clock(void) { // init stdio lib stdio_init_all(); @@ -64,7 +69,18 @@ void _lf_initialize_clock(void) { sem_init(&_lf_sem_irq_event, 0, 1); } +/* + * Write the time since boot in nanoseconds into + * the time variable pointed to by the argument + * and return 0. + * + * @param t pointer to the time variable to write to. + * @return error code or 0 on success. + */ int _lf_clock_now(instant_t* t) { + if (!t) { + return -1; + } // time struct absolute_time_t now; uint64_t ns_from_boot; @@ -75,6 +91,15 @@ int _lf_clock_now(instant_t* t) { return 0; } +/* + * Pause execution of the calling core for + * a nanosecond duration specified by the argument. + * Floor the specified duration to the nearest microsecond + * duration before sleeping and returning 0 on success. + * + * @param sleep_duration time to sleep in nanoseconds + * @return error code or 0 on success + */ int lf_sleep(interval_t sleep_duration) { if (sleep_duration < 0) { return -1; @@ -83,11 +108,22 @@ int lf_sleep(interval_t sleep_duration) { return 0; } +/* + * Sleep until the target time since boot in nanoseconds provided + * by the argument or return early if the binary + * _lf_sem_irq_event semaphore is released before timeout. + * + * The semaphore is released using the _lf_unthreaded_notify_of_event + * which is called by lf_schedule in the unthreaded runtime for physical actions. + * + * @param env pointer to environment struct this runs in. + * @param wakeup_time time in nanoseconds since boot to sleep until. + * @return -1 when interrupted or 0 on successful timeout + */ int _lf_interruptable_sleep_until_locked(environment_t* env, instant_t wakeup_time) { int ret_code = 0; - // return error + // return immediately if (wakeup_time < 0) { - ret_code = -1; return ret_code; } // time struct @@ -111,12 +147,20 @@ int _lf_interruptable_sleep_until_locked(environment_t* env, instant_t wakeup_ti #ifdef LF_UNTHREADED /* - * The single thread rp2040 platform support treats second core + * The single thread RP2040 platform support treats second core * routines similar to external interrupt routine threads. - * + * * Second core activity is disabled at the same times as * when interrupts are disabled. */ + +/* + * Enter a critical section where the second core is disabled + * and interrupts are disabled. Enter only if the critical section + * hasn't previously been entered. + * + * @return error code or 0 on success + */ int lf_disable_interrupts_nested() { if (!critical_section_is_initialized(&_lf_crit_sec)) { return 1; @@ -133,6 +177,13 @@ int lf_disable_interrupts_nested() { return 0; } +/* + * Exit a critical section and allow second core + * execution and interrupts. Exit only if no other critical + * sections are left to exit. + * + * @return error code or 0 on success + */ int lf_enable_interrupts_nested() { if (!critical_section_is_initialized(&_lf_crit_sec) || _lf_num_nested_crit_sec <= 0) { @@ -148,6 +199,12 @@ int lf_enable_interrupts_nested() { return 0; } +/* + * Release the binary event semaphore to notify + * the runtime of a physical action being scheduled. + * + * @return error code or 0 on success + */ int _lf_unthreaded_notify_of_event() { // notify main sleep loop of event sem_release(&_lf_sem_irq_event); From 44ac1b393492411436d2fdfa044054d783e25ecd Mon Sep 17 00:00:00 2001 From: Abhinav Gundrala Date: Tue, 25 Jul 2023 14:14:48 -0700 Subject: [PATCH 21/22] [docs] format comments --- core/platform/lf_rp2040_support.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/core/platform/lf_rp2040_support.c b/core/platform/lf_rp2040_support.c index 257766853..a41cef6ec 100644 --- a/core/platform/lf_rp2040_support.c +++ b/core/platform/lf_rp2040_support.c @@ -40,14 +40,14 @@ THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -/* +/** * critical section struct * disables external irq and core execution * provides mutual exclusion using hardware spin-locks */ static critical_section_t _lf_crit_sec; -/* +/** * binary semaphore for lf event notification * used by external isr or second core thread. * used to interact with the lf runtime thread @@ -57,7 +57,7 @@ static semaphore_t _lf_sem_irq_event; // nested critical section counter static uint32_t _lf_num_nested_crit_sec = 0; -/* +/** * Initialize basic runtime infrastructure and * synchronization structs for an unthreaded runtime. */ @@ -69,7 +69,7 @@ void _lf_initialize_clock(void) { sem_init(&_lf_sem_irq_event, 0, 1); } -/* +/** * Write the time since boot in nanoseconds into * the time variable pointed to by the argument * and return 0. @@ -91,7 +91,7 @@ int _lf_clock_now(instant_t* t) { return 0; } -/* +/** * Pause execution of the calling core for * a nanosecond duration specified by the argument. * Floor the specified duration to the nearest microsecond @@ -108,7 +108,7 @@ int lf_sleep(interval_t sleep_duration) { return 0; } -/* +/** * Sleep until the target time since boot in nanoseconds provided * by the argument or return early if the binary * _lf_sem_irq_event semaphore is released before timeout. @@ -146,7 +146,7 @@ int _lf_interruptable_sleep_until_locked(environment_t* env, instant_t wakeup_ti } #ifdef LF_UNTHREADED -/* +/** * The single thread RP2040 platform support treats second core * routines similar to external interrupt routine threads. * @@ -154,7 +154,7 @@ int _lf_interruptable_sleep_until_locked(environment_t* env, instant_t wakeup_ti * when interrupts are disabled. */ -/* +/** * Enter a critical section where the second core is disabled * and interrupts are disabled. Enter only if the critical section * hasn't previously been entered. @@ -177,7 +177,7 @@ int lf_disable_interrupts_nested() { return 0; } -/* +/** * Exit a critical section and allow second core * execution and interrupts. Exit only if no other critical * sections are left to exit. @@ -199,7 +199,7 @@ int lf_enable_interrupts_nested() { return 0; } -/* +/** * Release the binary event semaphore to notify * the runtime of a physical action being scheduled. * From 628c7909e1d88e71942eb6c8db3a7d310475adcd Mon Sep 17 00:00:00 2001 From: Abhinav Gundrala Date: Tue, 25 Jul 2023 14:22:46 -0700 Subject: [PATCH 22/22] [docs] minor edits --- core/platform/lf_rp2040_support.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/core/platform/lf_rp2040_support.c b/core/platform/lf_rp2040_support.c index a41cef6ec..8cbc0985e 100644 --- a/core/platform/lf_rp2040_support.c +++ b/core/platform/lf_rp2040_support.c @@ -95,7 +95,7 @@ int _lf_clock_now(instant_t* t) { * Pause execution of the calling core for * a nanosecond duration specified by the argument. * Floor the specified duration to the nearest microsecond - * duration before sleeping and returning 0 on success. + * duration before sleeping and return 0. * * @param sleep_duration time to sleep in nanoseconds * @return error code or 0 on success @@ -111,7 +111,7 @@ int lf_sleep(interval_t sleep_duration) { /** * Sleep until the target time since boot in nanoseconds provided * by the argument or return early if the binary - * _lf_sem_irq_event semaphore is released before timeout. + * _lf_sem_irq_event semaphore is released before the target time. * * The semaphore is released using the _lf_unthreaded_notify_of_event * which is called by lf_schedule in the unthreaded runtime for physical actions. @@ -166,8 +166,8 @@ int lf_disable_interrupts_nested() { return 1; } // check crit sec count - // enter non-rentrant state - // prevent second core access and disable interrupts + // enter non-rentrant state by disabling interrupts + // lock second core execution if (_lf_num_nested_crit_sec == 0) { // block if associated spin lock in use critical_section_enter_blocking(&_lf_crit_sec); @@ -178,9 +178,9 @@ int lf_disable_interrupts_nested() { } /** - * Exit a critical section and allow second core - * execution and interrupts. Exit only if no other critical - * sections are left to exit. + * Exit a critical section which will resume second core + * execution and enable interrupts. + * Exit only if no other critical sections are left to exit. * * @return error code or 0 on success */