From 676fa2bc556d06750374fe456484a1338c9e7bd9 Mon Sep 17 00:00:00 2001 From: Rahix <rahix@rahix.de> Date: Thu, 3 Oct 2019 14:43:21 +0200 Subject: [PATCH] fix(pycardium): Implement proper delays The old delay implementation is based on `mxc_delay`, provided by the SDK. This implementation has issues, as details in #177. To fix these issues, `mxc_delay` is replaced with a re-implementation which polls the MicroPython scheduler and calls `wfi` when we can safely sleep. The SysTick timer is configured globally (on core 1) to tick every 80ms. This seems to be a reasonable value to allow at least some `wfi`-sleep in the bounds used by most apps. Signed-off-by: Rahix <rahix@rahix.de> --- pycardium/mphalport.c | 88 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 86 insertions(+), 2 deletions(-) diff --git a/pycardium/mphalport.c b/pycardium/mphalport.c index 60cd9df0..61ea9def 100644 --- a/pycardium/mphalport.c +++ b/pycardium/mphalport.c @@ -31,6 +31,17 @@ void pycardium_hal_init(void) * a character becomes available. */ epic_interrupt_enable(EPIC_INT_UART_RX); + + /* + * Configure SysTick timer. + * + * Reload is configured for 80ms. This seems to be a reasonable value + * to allow at least some sleep for common sleep times in apps. + */ + SysTick->LOAD = 7679999 & SysTick_LOAD_RELOAD_Msk; + SysTick->VAL = 1; + SysTick->CTRL = SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_CLKSOURCE_Msk | + SysTick_CTRL_TICKINT_Msk; } /****************************************************************************** @@ -124,16 +135,80 @@ void mp_hal_set_interrupt_char(char c) * Time & Delay */ +static volatile uint32_t delay_overflows = 0; + +/* + * A delay implementation based on the SysTick timer. + */ +static void systick_delay_us(uint32_t us) +{ + if (us == 0) { + return; + } + + uint32_t starttick = SysTick->VAL; + uint32_t ticks = (uint32_t)( + ((uint64_t)us * (uint64_t)SystemCoreClock) / 1000000 + ); + uint32_t reload = SysTick->LOAD + 1; + delay_overflows = ticks / reload; + uint32_t lasttick = ticks % reload; + + uint32_t endtick; + if (lasttick >= starttick) { + delay_overflows += 1; + endtick = reload - (lasttick - starttick); + } else { + endtick = starttick - lasttick; + } + + while (delay_overflows > 0) { + mp_handle_pending(); + /* + * Overflow triggers the SysTick interrupt so we can sleep here. + * + * Note that this does **not** apply to the while-loop following + * this one. + */ + __WFI(); + } + + while (SysTick->VAL > endtick) { + mp_handle_pending(); + } +} + +/* + * Interrupt handler needed for delay implementation. If we ever define a more + * sophisticated SysTick_Handler, make sure to include a call to this function! + */ +static void systick_delay_handler(void) +{ + if (SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) { + if (delay_overflows > 0) { + delay_overflows--; + } + } +} + +/* HAL function for MicroPython */ void mp_hal_delay_ms(mp_uint_t ms) { - mxc_delay(ms * 1000); + systick_delay_us(ms * 1000); } +/* HAL function for MicroPython */ void mp_hal_delay_us(mp_uint_t us) { - mxc_delay(us); + systick_delay_us(us); } +/* + * HAL function for MicroPython + * + * This function is not really used except in a debug code-path of + * `parse_compile_execute`. Thus we need to define it as a stub. + */ mp_uint_t mp_hal_ticks_ms(void) { return 0; @@ -163,3 +238,12 @@ int mp_hal_trng_read_int(void) epic_trng_read((uint8_t *)&result, sizeof(result)); return result; } + +/****************************************************************************** + * SysTick Handler + */ +void SysTick_Handler(void) +{ + /* Needed for delay implementation */ + systick_delay_handler(); +} -- GitLab