From 3698799bdf9b12fa2279478245e09898f4fe75ae Mon Sep 17 00:00:00 2001 From: erlingrj Date: Fri, 22 Mar 2024 22:38:47 +0100 Subject: [PATCH] Make the Zephyr Kernel Clock the default timing service. Also does following improvements: - Use timed wait on semaphore instead of a busy-wait for sleeping - Use Zephyr API for getting a monotonically increasing time. --- .../api/platform/lf_zephyr_board_support.h | 2 +- low_level_platform/impl/CMakeLists.txt | 3 +- .../impl/src/lf_zephyr_clock_counter.c | 2 +- .../impl/src/lf_zephyr_clock_kernel.c | 68 +++++++++++-------- 4 files changed, 44 insertions(+), 31 deletions(-) diff --git a/low_level_platform/api/platform/lf_zephyr_board_support.h b/low_level_platform/api/platform/lf_zephyr_board_support.h index 92ece79f6..2b6b77e09 100644 --- a/low_level_platform/api/platform/lf_zephyr_board_support.h +++ b/low_level_platform/api/platform/lf_zephyr_board_support.h @@ -35,7 +35,7 @@ THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define LF_ZEPHYR_THREAD_PRIORITY_DEFAULT 5 #define LF_ZEPHYR_STACK_SIZE_DEFAULT 2048 -#if defined(CONFIG_LF_ZEPHYR_CLOCK_COUNTER) +#if defined(LF_ZEPHYR_CLOCK_COUNTER) #if defined(CONFIG_SOC_FAMILY_NRF) #define LF_TIMER DT_NODELABEL(timer1) #define LF_WAKEUP_OVERHEAD_US 100 diff --git a/low_level_platform/impl/CMakeLists.txt b/low_level_platform/impl/CMakeLists.txt index 676756c77..2322266ec 100644 --- a/low_level_platform/impl/CMakeLists.txt +++ b/low_level_platform/impl/CMakeLists.txt @@ -48,7 +48,7 @@ endif() list(APPEND LF_LOW_LEVEL_PLATFORM_FILES ${CMAKE_CURRENT_LIST_DIR}/src/platform_internal.c) if(${CMAKE_SYSTEM_NAME} STREQUAL "Zephyr") - if(${CONFIG_LF_ZEPHYR_CLOCK_COUNTER}) + if(${LF_ZEPHYR_CLOCK_COUNTER}) message(STATUS "Building Zephyr library with Counter clock ") else() message(STATUS "Building Zephyr library with Kernel clock ") @@ -85,3 +85,4 @@ low_level_platform_define(MODAL_REACTORS) low_level_platform_define(USER_THREADS) low_level_platform_define(NUMBER_OF_WORKERS) low_level_platform_define(NUMBER_OF_WATCHDOGS) +low_level_platform_define(LF_ZEPHYR_CLOCK_COUNTER) diff --git a/low_level_platform/impl/src/lf_zephyr_clock_counter.c b/low_level_platform/impl/src/lf_zephyr_clock_counter.c index 227e01dca..0e455605f 100644 --- a/low_level_platform/impl/src/lf_zephyr_clock_counter.c +++ b/low_level_platform/impl/src/lf_zephyr_clock_counter.c @@ -1,6 +1,6 @@ #if defined(PLATFORM_ZEPHYR) #include "platform/lf_zephyr_board_support.h" -#if defined(CONFIG_LF_ZEPHYR_CLOCK_COUNTER) +#if defined(LF_ZEPHYR_CLOCK_COUNTER) /************* Copyright (c) 2023, Norwegian University of Science and Technology. diff --git a/low_level_platform/impl/src/lf_zephyr_clock_kernel.c b/low_level_platform/impl/src/lf_zephyr_clock_kernel.c index ddaaa0ca7..d5e435bfc 100644 --- a/low_level_platform/impl/src/lf_zephyr_clock_kernel.c +++ b/low_level_platform/impl/src/lf_zephyr_clock_kernel.c @@ -1,6 +1,6 @@ #if defined(PLATFORM_ZEPHYR) #include "platform/lf_zephyr_board_support.h" -#if !defined(CONFIG_LF_ZEPHYR_CLOCK_COUNTER) +#if !defined(LF_ZEPHYR_CLOCK_COUNTER) /************* Copyright (c) 2023, Norwegian University of Science and Technology. @@ -41,52 +41,62 @@ THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "low_level_platform.h" #include "logging_macros.h" -static int64_t epoch_duration_nsec; -static volatile int64_t last_epoch_nsec = 0; +// Convert Zephyr ticks into an interval_t. According to Zephyr docs the +// ticks are 100Hz for QEMU emulations, and normally a multiple of 10. +#if CONFIG_SYS_CLOCK_TICKS_PER_SEC == 100 +#define TICKS_TO_NSEC(ticks) MSEC(10 * ticks) +#elif CONFIG_SYS_CLOCK_TICKS_PER_SEC == 1000 +#define TICKS_TO_NSEC(ticks) MSEC(ticks) +#elif CONFIG_SYS_CLOCK_TICKS_PER_SEC == 10000 +#define TICKS_TO_NSEC(ticks) USEC(100 * ticks) +#elif CONFIG_SYS_CLOCK_TICKS_PER_SEC == 100000 +#define TICKS_TO_NSEC(ticks) USEC(10 * ticks) +#elif CONFIG_SYS_CLOCK_TICKS_PER_SEC == 1000000 +#define TICKS_TO_NSEC(ticks) USEC(1 * ticks) +#elif CONFIG_SYS_CLOCK_TICKS_PER_SEC == 10000000 +#define TICKS_TO_NSEC(ticks) NSEC(100 * ticks) +#else +#define TICKS_TO_NSEC(ticks) ((SECONDS(1) / CONFIG_SYS_CLOCK_TICKS_PER_SEC) * ticks) +#endif + static uint32_t timer_freq; static volatile bool async_event = false; +// Statically create an initialize the semaphore used for sleeping. +K_SEM_DEFINE(sleeping_sem, 0, 1) + void _lf_initialize_clock() { - timer_freq = CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC; + timer_freq = CONFIG_SYS_CLOCK_TICKS_PER_SEC; LF_PRINT_LOG("--- Using LF Zephyr Kernel Clock with a frequency of %u Hz\n", timer_freq); - last_epoch_nsec = 0; - epoch_duration_nsec = ((1LL << 32) * SECONDS(1)) / CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC; } -/** - * Detect wraps by storing the previous clock readout. When a clock readout is - * less than the previous we have had a wrap. This only works of `_lf_clock_gettime` - * is invoked at least once per epoch. - */ +/** Uses Zephyr's monotonic increasing uptime count. */ int _lf_clock_gettime(instant_t* t) { - static uint32_t last_read_cycles = 0; - uint32_t now_cycles = k_cycle_get_32(); - if (now_cycles < last_read_cycles) { - last_epoch_nsec += epoch_duration_nsec; - } - *t = (SECOND(1) / CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC) * now_cycles + last_epoch_nsec; - last_read_cycles = now_cycles; + interval_t uptime = k_uptime_ticks(); + *t = TICKS_TO_NSEC(uptime); return 0; } -/** - * Interruptable sleep is implemented using busy-waiting. - */ +/** Interruptable sleep is implemented by a taking a semaphore with a timeout. */ int _lf_interruptable_sleep_until_locked(environment_t* env, instant_t wakeup) { async_event = false; + interval_t duration = wakeup - lf_time_physical(); + if (wakeup <= 0) { + return 0; + } + if (lf_critical_section_exit(env)) { lf_print_error_and_exit("Failed to exit critical section."); } - instant_t now; - do { - _lf_clock_gettime(&now); - } while ((now < wakeup) && !async_event); + + int res = k_sem_take(sleeping_sem, K_NSEC(duration)); + if (lf_critical_section_enter(env)) { lf_print_error_and_exit("Failed to exit critical section."); } - if (async_event) { + if (res < 0 || async_event == true) { async_event = false; return -1; } else { @@ -95,11 +105,13 @@ int _lf_interruptable_sleep_until_locked(environment_t* env, instant_t wakeup) { } /** - * Asynchronous events are notified by setting a flag which breaks the sleeping - * thread out of the busy-wait. + * Asynchronous events are notified by signalling a semaphore which will wakeup + * the runtime if it is sleeping, and setting a flag to indicate what has + * happened. */ int _lf_single_threaded_notify_of_event() { async_event = true; + k_sem_give(sleeping_sem); return 0; }