summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDamien George <damien@micropython.org>2024-08-14 16:50:28 +1000
committerDamien George <damien@micropython.org>2025-04-09 00:22:32 +1000
commitbbb8fd77fde3400cc1b9902e94f05889a7c1f49a (patch)
tree43b1ca5d2e4ea029d02ed63577088d1eeaf09b6d
parentcee8e111cbaedc877fcffeb7d8166b60317f7377 (diff)
alif/system_tick: Implement optional LPTIMER support for systick.
Signed-off-by: Damien George <damien@micropython.org>
-rw-r--r--ports/alif/mpconfigport.h6
-rw-r--r--ports/alif/mphalport.c12
-rw-r--r--ports/alif/system_tick.c137
-rw-r--r--ports/alif/system_tick.h6
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);