Skip to content

Commit bbb8fd7

Browse files
committed
alif/system_tick: Implement optional LPTIMER support for systick.
Signed-off-by: Damien George <damien@micropython.org>
1 parent cee8e11 commit bbb8fd7

4 files changed

Lines changed: 158 additions & 3 deletions

File tree

ports/alif/mpconfigport.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@
3636
#define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_FULL_FEATURES)
3737
#endif
3838

39+
// Select the low-level system tick implementation.
40+
#if !defined(MICROPY_HW_SYSTEM_TICK_USE_LPTIMER) \
41+
&& !defined(MICROPY_HW_SYSTEM_TICK_USE_UTIMER)
42+
#define MICROPY_HW_SYSTEM_TICK_USE_UTIMER (1)
43+
#endif
44+
3945
#ifndef MICROPY_HW_ENABLE_OSPI
4046
#define MICROPY_HW_ENABLE_OSPI (CORE_M55_HP)
4147
#endif

ports/alif/mphalport.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,16 +120,28 @@ mp_uint_t mp_hal_ticks_cpu(void) {
120120

121121
mp_uint_t mp_hal_ticks_us(void) {
122122
// Convert system tick to microsecond counter.
123+
#if MICROPY_HW_SYSTEM_TICK_USE_LPTIMER
124+
return system_tick_get_u64() * 1000000 / system_tick_source_hz;
125+
#else
123126
return system_tick_get_u64() / system_core_clock_mhz;
127+
#endif
124128
}
125129

126130
mp_uint_t mp_hal_ticks_ms(void) {
127131
// Convert system tick to millisecond counter.
132+
#if MICROPY_HW_SYSTEM_TICK_USE_LPTIMER
133+
return system_tick_get_u64() * 1000ULL / system_tick_source_hz;
134+
#else
128135
return system_tick_get_u64() / (SystemCoreClock / 1000);
136+
#endif
129137
}
130138

131139
void mp_hal_delay_us(mp_uint_t us) {
140+
#if MICROPY_HW_SYSTEM_TICK_USE_LPTIMER
141+
uint64_t ticks_delay = (uint64_t)us * system_tick_source_hz / 1000000;
142+
#else
132143
uint64_t ticks_delay = (uint64_t)us * system_core_clock_mhz;
144+
#endif
133145
uint64_t start = system_tick_get_u64();
134146
while (system_tick_get_u64() - start < ticks_delay) {
135147
}

ports/alif/system_tick.c

Lines changed: 135 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,141 @@
2727
#include "irq.h"
2828
#include "system_tick.h"
2929

30-
#include "utimer.h"
31-
3230
#define MIN(x, y) ((x) < (y) ? (x) : (y))
3331

32+
#if MICROPY_HW_SYSTEM_TICK_USE_LPTIMER
33+
34+
#include "lptimer.h"
35+
#include "sys_ctrl_lptimer.h"
36+
37+
// Channel 0 and 1 are cascaded to make a 64-bit counter.
38+
// Channel 2 is used for system_tick_wfe_with_timeout_us.
39+
// Channel 3 is used for system_tick_schedule_after_us.
40+
#define LPTIMER ((LPTIMER_Type *)LPTIMER_BASE)
41+
#define LPTIMER_CH_A (0)
42+
#define LPTIMER_CH_B (1)
43+
#define LPTIMER_CH_C (2)
44+
#define LPTIMER_CH_D (3)
45+
46+
uint64_t system_tick_source_hz;
47+
48+
void system_tick_init(void) {
49+
lptimer_disable_counter(LPTIMER, LPTIMER_CH_A);
50+
lptimer_disable_counter(LPTIMER, LPTIMER_CH_B);
51+
52+
ANA_REG->MISC_CTRL |= 1 << 0; // SEL_32K, select LXFO
53+
54+
select_lptimer_clk(LPTIMER_CLK_SOURCE_32K, LPTIMER_CH_A);
55+
select_lptimer_clk(LPTIMER_CLK_SOURCE_CASCADE, LPTIMER_CH_B);
56+
select_lptimer_clk(LPTIMER_CLK_SOURCE_32K, LPTIMER_CH_C);
57+
select_lptimer_clk(LPTIMER_CLK_SOURCE_32K, LPTIMER_CH_D);
58+
59+
lptimer_load_max_count(LPTIMER, LPTIMER_CH_A);
60+
lptimer_set_mode_freerunning(LPTIMER, LPTIMER_CH_A);
61+
62+
lptimer_load_max_count(LPTIMER, LPTIMER_CH_B);
63+
lptimer_set_mode_freerunning(LPTIMER, LPTIMER_CH_B);
64+
65+
lptimer_enable_counter(LPTIMER, LPTIMER_CH_B);
66+
lptimer_enable_counter(LPTIMER, LPTIMER_CH_A);
67+
68+
system_tick_source_hz = 32768;
69+
70+
NVIC_ClearPendingIRQ(LPTIMER2_IRQ_IRQn);
71+
NVIC_SetPriority(LPTIMER2_IRQ_IRQn, IRQ_PRI_SYSTEM_TICK);
72+
NVIC_EnableIRQ(LPTIMER2_IRQ_IRQn);
73+
74+
NVIC_ClearPendingIRQ(LPTIMER3_IRQ_IRQn);
75+
NVIC_SetPriority(LPTIMER3_IRQ_IRQn, IRQ_PRI_SYSTEM_TICK);
76+
NVIC_EnableIRQ(LPTIMER3_IRQ_IRQn);
77+
}
78+
79+
void LPTIMER2_IRQHandler(void) {
80+
lptimer_clear_interrupt(LPTIMER, LPTIMER_CH_C);
81+
__SEV();
82+
}
83+
84+
void LPTIMER3_IRQHandler(void) {
85+
lptimer_clear_interrupt(LPTIMER, LPTIMER_CH_D);
86+
lptimer_mask_interrupt(LPTIMER, LPTIMER_CH_D);
87+
lptimer_disable_counter(LPTIMER, LPTIMER_CH_D);
88+
system_tick_schedule_callback();
89+
__SEV();
90+
}
91+
92+
uint32_t system_tick_get_u32(void) {
93+
return 0xffffffff - lptimer_get_count(LPTIMER, LPTIMER_CH_A);
94+
}
95+
96+
uint64_t system_tick_get_u64(void) {
97+
// Get 64-bit counter value from the hardware timer.
98+
// Sample it twice in case the low counter wraps around while sampling.
99+
uint32_t irq_state = disable_irq();
100+
uint32_t lo0 = lptimer_get_count(LPTIMER, LPTIMER_CH_A);
101+
uint32_t hi0 = lptimer_get_count(LPTIMER, LPTIMER_CH_B);
102+
uint32_t lo1 = lptimer_get_count(LPTIMER, LPTIMER_CH_A);
103+
uint32_t hi1 = lptimer_get_count(LPTIMER, LPTIMER_CH_B);
104+
enable_irq(irq_state);
105+
106+
if (hi0 == hi1) {
107+
// Low counter may have wrapped around between sampling of lo0 and hi0, so prefer second sampling.
108+
lo0 = lo1;
109+
hi0 = hi1;
110+
} else {
111+
// Low counter wrapped around either between sampling of hi0 and lo1, or sampling of lo1 and hi1.
112+
// In either case use the first sampling.
113+
}
114+
115+
// Convert from descending count to ascending.
116+
lo0 = 0xffffffff - lo0;
117+
hi0 = 0xffffffff - hi0;
118+
119+
// Return a 64-bit value.
120+
return ((uint64_t)hi0 << 32) | (uint64_t)lo0;
121+
}
122+
123+
void system_tick_wfe_with_timeout_us(uint32_t timeout_us) {
124+
// Maximum 131 second timeout, to not overflow 32-bit ticks when
125+
// LPTIMER is clocked at 32768Hz.
126+
uint32_t timeout_ticks = (uint64_t)MIN(timeout_us, 131000000) * system_tick_source_hz / 1000000;
127+
128+
// Set up the LPTIMER interrupt to fire after the given timeout.
129+
lptimer_disable_counter(LPTIMER, LPTIMER_CH_C);
130+
lptimer_set_mode_userdefined(LPTIMER, LPTIMER_CH_C);
131+
lptimer_load_count(LPTIMER, LPTIMER_CH_C, &timeout_ticks);
132+
lptimer_clear_interrupt(LPTIMER, LPTIMER_CH_C);
133+
lptimer_unmask_interrupt(LPTIMER, LPTIMER_CH_C);
134+
lptimer_enable_counter(LPTIMER, LPTIMER_CH_C);
135+
136+
// Wait for an event.
137+
__WFE();
138+
139+
// Disable the LPTIMER interrupt (in case a different interrupt woke the WFE).
140+
lptimer_mask_interrupt(LPTIMER, LPTIMER_CH_C);
141+
lptimer_disable_counter(LPTIMER, LPTIMER_CH_C);
142+
}
143+
144+
void system_tick_schedule_after_us(uint32_t ticks_us) {
145+
// Disable the interrupt in case it's still active.
146+
lptimer_mask_interrupt(LPTIMER, LPTIMER_CH_D);
147+
148+
// Maximum 131 second timeout, to not overflow 32-bit ticks when
149+
// LPTIMER is clocked at 32768Hz.
150+
uint32_t timeout_ticks = (uint64_t)MIN(ticks_us, 131000000) * system_tick_source_hz / 1000000;
151+
152+
// Set up the LPTIMER interrupt to fire after the given timeout.
153+
lptimer_disable_counter(LPTIMER, LPTIMER_CH_D);
154+
lptimer_set_mode_userdefined(LPTIMER, LPTIMER_CH_D);
155+
lptimer_load_count(LPTIMER, LPTIMER_CH_D, &timeout_ticks);
156+
lptimer_clear_interrupt(LPTIMER, LPTIMER_CH_D);
157+
lptimer_unmask_interrupt(LPTIMER, LPTIMER_CH_D);
158+
lptimer_enable_counter(LPTIMER, LPTIMER_CH_D);
159+
}
160+
161+
#elif MICROPY_HW_SYSTEM_TICK_USE_UTIMER
162+
163+
#include "utimer.h"
164+
34165
#define UTIMER ((UTIMER_Type *)UTIMER_BASE)
35166
#define UTIMER_CHANNEL (11)
36167

@@ -166,3 +297,5 @@ void system_tick_schedule_after_us(uint32_t ticks_us) {
166297
}
167298
}
168299
}
300+
301+
#endif

ports/alif/system_tick.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,13 @@
2626
#ifndef MICROPY_INCLUDED_ALIF_SYSTEM_TICK_H
2727
#define MICROPY_INCLUDED_ALIF_SYSTEM_TICK_H
2828

29-
#include <stdint.h>
29+
#include "py/mpconfig.h"
3030

31+
#if MICROPY_HW_SYSTEM_TICK_USE_LPTIMER
32+
extern uint64_t system_tick_source_hz;
33+
#else
3134
extern uint64_t system_core_clock_mhz;
35+
#endif
3236

3337
void system_tick_init(void);
3438
uint32_t system_tick_get_u32(void);

0 commit comments

Comments
 (0)