diff options
| author | Damien George <damien@micropython.org> | 2024-08-14 16:50:28 +1000 |
|---|---|---|
| committer | Damien George <damien@micropython.org> | 2025-04-09 00:22:32 +1000 |
| commit | bbb8fd77fde3400cc1b9902e94f05889a7c1f49a (patch) | |
| tree | 43b1ca5d2e4ea029d02ed63577088d1eeaf09b6d | |
| parent | cee8e111cbaedc877fcffeb7d8166b60317f7377 (diff) | |
alif/system_tick: Implement optional LPTIMER support for systick.
Signed-off-by: Damien George <damien@micropython.org>
| -rw-r--r-- | ports/alif/mpconfigport.h | 6 | ||||
| -rw-r--r-- | ports/alif/mphalport.c | 12 | ||||
| -rw-r--r-- | ports/alif/system_tick.c | 137 | ||||
| -rw-r--r-- | ports/alif/system_tick.h | 6 |
4 files changed, 158 insertions, 3 deletions
diff --git a/ports/alif/mpconfigport.h b/ports/alif/mpconfigport.h index dfe1ec256..b1a019106 100644 --- a/ports/alif/mpconfigport.h +++ b/ports/alif/mpconfigport.h @@ -36,6 +36,12 @@ #define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_FULL_FEATURES) #endif +// Select the low-level system tick implementation. +#if !defined(MICROPY_HW_SYSTEM_TICK_USE_LPTIMER) \ + && !defined(MICROPY_HW_SYSTEM_TICK_USE_UTIMER) +#define MICROPY_HW_SYSTEM_TICK_USE_UTIMER (1) +#endif + #ifndef MICROPY_HW_ENABLE_OSPI #define MICROPY_HW_ENABLE_OSPI (CORE_M55_HP) #endif diff --git a/ports/alif/mphalport.c b/ports/alif/mphalport.c index 9769ddeae..becac8f3b 100644 --- a/ports/alif/mphalport.c +++ b/ports/alif/mphalport.c @@ -120,16 +120,28 @@ mp_uint_t mp_hal_ticks_cpu(void) { mp_uint_t mp_hal_ticks_us(void) { // Convert system tick to microsecond counter. + #if MICROPY_HW_SYSTEM_TICK_USE_LPTIMER + return system_tick_get_u64() * 1000000 / system_tick_source_hz; + #else return system_tick_get_u64() / system_core_clock_mhz; + #endif } mp_uint_t mp_hal_ticks_ms(void) { // Convert system tick to millisecond counter. + #if MICROPY_HW_SYSTEM_TICK_USE_LPTIMER + return system_tick_get_u64() * 1000ULL / system_tick_source_hz; + #else return system_tick_get_u64() / (SystemCoreClock / 1000); + #endif } void mp_hal_delay_us(mp_uint_t us) { + #if MICROPY_HW_SYSTEM_TICK_USE_LPTIMER + uint64_t ticks_delay = (uint64_t)us * system_tick_source_hz / 1000000; + #else uint64_t ticks_delay = (uint64_t)us * system_core_clock_mhz; + #endif uint64_t start = system_tick_get_u64(); while (system_tick_get_u64() - start < ticks_delay) { } diff --git a/ports/alif/system_tick.c b/ports/alif/system_tick.c index 5cdf45cba..72b8d021c 100644 --- a/ports/alif/system_tick.c +++ b/ports/alif/system_tick.c @@ -27,10 +27,141 @@ #include "irq.h" #include "system_tick.h" -#include "utimer.h" - #define MIN(x, y) ((x) < (y) ? (x) : (y)) +#if MICROPY_HW_SYSTEM_TICK_USE_LPTIMER + +#include "lptimer.h" +#include "sys_ctrl_lptimer.h" + +// Channel 0 and 1 are cascaded to make a 64-bit counter. +// Channel 2 is used for system_tick_wfe_with_timeout_us. +// Channel 3 is used for system_tick_schedule_after_us. +#define LPTIMER ((LPTIMER_Type *)LPTIMER_BASE) +#define LPTIMER_CH_A (0) +#define LPTIMER_CH_B (1) +#define LPTIMER_CH_C (2) +#define LPTIMER_CH_D (3) + +uint64_t system_tick_source_hz; + +void system_tick_init(void) { + lptimer_disable_counter(LPTIMER, LPTIMER_CH_A); + lptimer_disable_counter(LPTIMER, LPTIMER_CH_B); + + ANA_REG->MISC_CTRL |= 1 << 0; // SEL_32K, select LXFO + + select_lptimer_clk(LPTIMER_CLK_SOURCE_32K, LPTIMER_CH_A); + select_lptimer_clk(LPTIMER_CLK_SOURCE_CASCADE, LPTIMER_CH_B); + select_lptimer_clk(LPTIMER_CLK_SOURCE_32K, LPTIMER_CH_C); + select_lptimer_clk(LPTIMER_CLK_SOURCE_32K, LPTIMER_CH_D); + + lptimer_load_max_count(LPTIMER, LPTIMER_CH_A); + lptimer_set_mode_freerunning(LPTIMER, LPTIMER_CH_A); + + lptimer_load_max_count(LPTIMER, LPTIMER_CH_B); + lptimer_set_mode_freerunning(LPTIMER, LPTIMER_CH_B); + + lptimer_enable_counter(LPTIMER, LPTIMER_CH_B); + lptimer_enable_counter(LPTIMER, LPTIMER_CH_A); + + system_tick_source_hz = 32768; + + NVIC_ClearPendingIRQ(LPTIMER2_IRQ_IRQn); + NVIC_SetPriority(LPTIMER2_IRQ_IRQn, IRQ_PRI_SYSTEM_TICK); + NVIC_EnableIRQ(LPTIMER2_IRQ_IRQn); + + NVIC_ClearPendingIRQ(LPTIMER3_IRQ_IRQn); + NVIC_SetPriority(LPTIMER3_IRQ_IRQn, IRQ_PRI_SYSTEM_TICK); + NVIC_EnableIRQ(LPTIMER3_IRQ_IRQn); +} + +void LPTIMER2_IRQHandler(void) { + lptimer_clear_interrupt(LPTIMER, LPTIMER_CH_C); + __SEV(); +} + +void LPTIMER3_IRQHandler(void) { + lptimer_clear_interrupt(LPTIMER, LPTIMER_CH_D); + lptimer_mask_interrupt(LPTIMER, LPTIMER_CH_D); + lptimer_disable_counter(LPTIMER, LPTIMER_CH_D); + system_tick_schedule_callback(); + __SEV(); +} + +uint32_t system_tick_get_u32(void) { + return 0xffffffff - lptimer_get_count(LPTIMER, LPTIMER_CH_A); +} + +uint64_t system_tick_get_u64(void) { + // Get 64-bit counter value from the hardware timer. + // Sample it twice in case the low counter wraps around while sampling. + uint32_t irq_state = disable_irq(); + uint32_t lo0 = lptimer_get_count(LPTIMER, LPTIMER_CH_A); + uint32_t hi0 = lptimer_get_count(LPTIMER, LPTIMER_CH_B); + uint32_t lo1 = lptimer_get_count(LPTIMER, LPTIMER_CH_A); + uint32_t hi1 = lptimer_get_count(LPTIMER, LPTIMER_CH_B); + enable_irq(irq_state); + + if (hi0 == hi1) { + // Low counter may have wrapped around between sampling of lo0 and hi0, so prefer second sampling. + lo0 = lo1; + hi0 = hi1; + } else { + // Low counter wrapped around either between sampling of hi0 and lo1, or sampling of lo1 and hi1. + // In either case use the first sampling. + } + + // Convert from descending count to ascending. + lo0 = 0xffffffff - lo0; + hi0 = 0xffffffff - hi0; + + // Return a 64-bit value. + return ((uint64_t)hi0 << 32) | (uint64_t)lo0; +} + +void system_tick_wfe_with_timeout_us(uint32_t timeout_us) { + // Maximum 131 second timeout, to not overflow 32-bit ticks when + // LPTIMER is clocked at 32768Hz. + uint32_t timeout_ticks = (uint64_t)MIN(timeout_us, 131000000) * system_tick_source_hz / 1000000; + + // Set up the LPTIMER interrupt to fire after the given timeout. + lptimer_disable_counter(LPTIMER, LPTIMER_CH_C); + lptimer_set_mode_userdefined(LPTIMER, LPTIMER_CH_C); + lptimer_load_count(LPTIMER, LPTIMER_CH_C, &timeout_ticks); + lptimer_clear_interrupt(LPTIMER, LPTIMER_CH_C); + lptimer_unmask_interrupt(LPTIMER, LPTIMER_CH_C); + lptimer_enable_counter(LPTIMER, LPTIMER_CH_C); + + // Wait for an event. + __WFE(); + + // Disable the LPTIMER interrupt (in case a different interrupt woke the WFE). + lptimer_mask_interrupt(LPTIMER, LPTIMER_CH_C); + lptimer_disable_counter(LPTIMER, LPTIMER_CH_C); +} + +void system_tick_schedule_after_us(uint32_t ticks_us) { + // Disable the interrupt in case it's still active. + lptimer_mask_interrupt(LPTIMER, LPTIMER_CH_D); + + // Maximum 131 second timeout, to not overflow 32-bit ticks when + // LPTIMER is clocked at 32768Hz. + uint32_t timeout_ticks = (uint64_t)MIN(ticks_us, 131000000) * system_tick_source_hz / 1000000; + + // Set up the LPTIMER interrupt to fire after the given timeout. + lptimer_disable_counter(LPTIMER, LPTIMER_CH_D); + lptimer_set_mode_userdefined(LPTIMER, LPTIMER_CH_D); + lptimer_load_count(LPTIMER, LPTIMER_CH_D, &timeout_ticks); + lptimer_clear_interrupt(LPTIMER, LPTIMER_CH_D); + lptimer_unmask_interrupt(LPTIMER, LPTIMER_CH_D); + lptimer_enable_counter(LPTIMER, LPTIMER_CH_D); +} + +#elif MICROPY_HW_SYSTEM_TICK_USE_UTIMER + +#include "utimer.h" + #define UTIMER ((UTIMER_Type *)UTIMER_BASE) #define UTIMER_CHANNEL (11) @@ -166,3 +297,5 @@ void system_tick_schedule_after_us(uint32_t ticks_us) { } } } + +#endif diff --git a/ports/alif/system_tick.h b/ports/alif/system_tick.h index 2373f9421..d808b7632 100644 --- a/ports/alif/system_tick.h +++ b/ports/alif/system_tick.h @@ -26,9 +26,13 @@ #ifndef MICROPY_INCLUDED_ALIF_SYSTEM_TICK_H #define MICROPY_INCLUDED_ALIF_SYSTEM_TICK_H -#include <stdint.h> +#include "py/mpconfig.h" +#if MICROPY_HW_SYSTEM_TICK_USE_LPTIMER +extern uint64_t system_tick_source_hz; +#else extern uint64_t system_core_clock_mhz; +#endif void system_tick_init(void); uint32_t system_tick_get_u32(void); |
