alif/system_tick: Implement optional LPTIMER support for systick.

Signed-off-by: Damien George <damien@micropython.org>
This commit is contained in:
Damien George 2024-08-14 16:50:28 +10:00
parent cee8e111cb
commit bbb8fd77fd
4 changed files with 158 additions and 3 deletions

View file

@ -36,6 +36,12 @@
#define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_FULL_FEATURES) #define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_FULL_FEATURES)
#endif #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 #ifndef MICROPY_HW_ENABLE_OSPI
#define MICROPY_HW_ENABLE_OSPI (CORE_M55_HP) #define MICROPY_HW_ENABLE_OSPI (CORE_M55_HP)
#endif #endif

View file

@ -120,16 +120,28 @@ mp_uint_t mp_hal_ticks_cpu(void) {
mp_uint_t mp_hal_ticks_us(void) { mp_uint_t mp_hal_ticks_us(void) {
// Convert system tick to microsecond counter. // 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; return system_tick_get_u64() / system_core_clock_mhz;
#endif
} }
mp_uint_t mp_hal_ticks_ms(void) { mp_uint_t mp_hal_ticks_ms(void) {
// Convert system tick to millisecond counter. // 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); return system_tick_get_u64() / (SystemCoreClock / 1000);
#endif
} }
void mp_hal_delay_us(mp_uint_t us) { 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; uint64_t ticks_delay = (uint64_t)us * system_core_clock_mhz;
#endif
uint64_t start = system_tick_get_u64(); uint64_t start = system_tick_get_u64();
while (system_tick_get_u64() - start < ticks_delay) { while (system_tick_get_u64() - start < ticks_delay) {
} }

View file

@ -27,10 +27,141 @@
#include "irq.h" #include "irq.h"
#include "system_tick.h" #include "system_tick.h"
#include "utimer.h"
#define MIN(x, y) ((x) < (y) ? (x) : (y)) #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 ((UTIMER_Type *)UTIMER_BASE)
#define UTIMER_CHANNEL (11) #define UTIMER_CHANNEL (11)
@ -166,3 +297,5 @@ void system_tick_schedule_after_us(uint32_t ticks_us) {
} }
} }
} }
#endif

View file

@ -26,9 +26,13 @@
#ifndef MICROPY_INCLUDED_ALIF_SYSTEM_TICK_H #ifndef MICROPY_INCLUDED_ALIF_SYSTEM_TICK_H
#define 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; extern uint64_t system_core_clock_mhz;
#endif
void system_tick_init(void); void system_tick_init(void);
uint32_t system_tick_get_u32(void); uint32_t system_tick_get_u32(void);