Skip to content

Commit fd4b9c0

Browse files
committed
fw/driver/lptim_systick: improve stop mode stability
HXT48 clock is required for deepsleep mode. it effective LCPU and wakeup time. If HXT48 clock is disable, LCPU may enter undifinied state, then BLE meet issue. For wakeup time, HXT48 ready spend nearly 6.2 ms. Signed-off-by: HaiLong Yang <cameled@outlook.com>
1 parent bbcae15 commit fd4b9c0

3 files changed

Lines changed: 105 additions & 41 deletions

File tree

src/fw/drivers/sf32lb52/lptim_systick.c

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@
2424
#include "bf0_hal_lptim.h"
2525
#include "bf0_hal_aon.h"
2626

27-
/* LPRC10K frequency nearly 8~9 KHz, fixed to 8KHz for systick */
28-
#define SYSTICK_CLOCK_HZ 8000
27+
/* LPRC10K frequency nearly 9 KHz, fixed to 9KHz for systick */
28+
#define SYSTICK_CLOCK_HZ 9000
2929
#define SYSTICK_ONE_TICK_HZ (SYSTICK_CLOCK_HZ / RTC_TICKS_HZ)
3030

3131
#if !defined(configUSE_TICKLESS_IDLE) || (configUSE_TICKLESS_IDLE != 2)
@@ -58,6 +58,9 @@ void lptim_systick_init(void)
5858
HAL_HPAON_EnableWakeupSrc(HPAON_WAKEUP_SRC_LP2HP_IRQ, AON_PIN_MODE_HIGH); // LP2HP mailbox interrupt
5959
HAL_HPAON_EnableWakeupSrc(HPAON_WAKEUP_SRC_LP2HP_REQ, AON_PIN_MODE_HIGH); // LP2HP manual wakeup
6060

61+
// Explicit enable hxt48 for LCPU
62+
hwp_hpsys_aon->DSCR |= HPSYS_AON_DSCR_HXT48_REQ;
63+
6164
s_lptim_systick_initialized = true;
6265
}
6366

@@ -77,16 +80,6 @@ void lptim_systick_enable(void)
7780
__HAL_LPTIM_START_CONTINUOUS(&s_lptim1_handle);
7881
}
7982

80-
void lptim_systick_pause(void)
81-
{
82-
/* NOP */
83-
}
84-
85-
void lptim_systick_resume(void)
86-
{
87-
/* NOP */
88-
}
89-
9083
void lptim_systick_tickless_idle(uint32_t ticks_from_now)
9184
{
9285
// In tickless idle mode, use OCWE instead.

src/fw/freertos_application.c

Lines changed: 94 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -84,31 +84,24 @@ static const RtcTicks EARLY_WAKEUP_TICKS = 2;
8484
static const RtcTicks MIN_STOP_TICKS = 5;
8585
#elif defined(MICRO_FAMILY_SF32LB52)
8686
//! Stop mode until this number of ticks before the next scheduled task
87-
static const RtcTicks EARLY_WAKEUP_TICKS = 10; // relative large to avid tasks.c:1960 assert
87+
static const RtcTicks EARLY_WAKEUP_TICKS = 2;
8888
//! Stop mode until this number of ticks before the next scheduled task
89-
static const RtcTicks MIN_STOP_TICKS = 15;
89+
static const RtcTicks MIN_STOP_TICKS = 5;
9090
#else
9191
#error "Unknown micro family"
9292
#endif
9393

9494
// 1 second ticks so that we only wake up once every regular timer interval.
9595
static const RtcTicks MAX_STOP_TICKS = RTC_TICKS_HZ;
9696

97-
extern void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) {
9897
#if !defined(MICRO_FAMILY_SF32LB52)
98+
extern void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) {
9999
if (!rtc_alarm_is_initialized() || !sleep_mode_is_allowed()) {
100100
// the RTC is not yet initialized to the point where it can wake us from sleep or sleep/stop
101101
// is disabled. Just returning will cause a busy loop where the caller thought we slept for
102102
// 0 ticks and will reevaluate what to do next (probably just try again).
103103
return;
104104
}
105-
#else
106-
if (!lptim_systick_is_initialized() || !sleep_mode_is_allowed() ||
107-
!ipc_queue_check_idle()) {
108-
// To avoid LCPU enter incorrect state, make sure ipc queue is empty before enter stop mode.
109-
return;
110-
}
111-
#endif
112105

113106
// Note: all tasks are suspended at this point, but we can still be interrupted
114107
// so the critical section is necessary. taskENTER_CRITICAL() is not used here
@@ -155,8 +148,6 @@ extern void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) {
155148
// (and RTC0 is shut down), then we just end up measuring 0 here -- no
156149
// harm, no foul.
157150
uint32_t rtc_start = NRF_RTC0->COUNTER;
158-
#elif defined(MICRO_FAMILY_SF32LB52)
159-
uint32_t counter_start = LPTIM1->CNT;
160151
#else
161152
// We assume that a WFI to trigger sleep mode will not last longer than 1
162153
// SysTick. (The SysTick INT doesn't automatically get suppressed) Thus,
@@ -181,13 +172,6 @@ extern void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) {
181172
rtc_end += 0x1000000;
182173
uint32_t rtc_elapsed = rtc_end - rtc_start;
183174
uint32_t cycles_elapsed = rtc_elapsed * SystemCoreClock / 32768;
184-
#elif defined(MICRO_FAMILY_SF32LB52)
185-
uint32_t counter_stop = LPTIM1->CNT;
186-
if (counter_stop < counter_start) {
187-
counter_stop += 0x10000;
188-
}
189-
uint32_t counter_elapsed = counter_stop - counter_start;
190-
uint32_t cycles_elapsed = (counter_elapsed * RTC_TICKS_HZ) / 8000;
191175
#else
192176
uint32_t systick_stop = SysTick->VAL;
193177
uint32_t cycles_elapsed;
@@ -205,17 +189,12 @@ extern void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) {
205189

206190
// Go into stop mode until the wakeup_tick.
207191
s_last_ticks_commanded_in_stop = stop_duration;
208-
#if !defined(MICRO_FAMILY_SF32LB52)
192+
209193
rtc_alarm_set(stop_duration);
210194
enter_stop_mode();
211195

212196
RtcTicks ticks_elapsed = rtc_alarm_get_elapsed_ticks();
213-
#else
214-
lptim_systick_tickless_idle((uint32_t)stop_duration);
215-
enter_stop_mode();
216197

217-
uint32_t ticks_elapsed = lptim_systick_get_elapsed_ticks();
218-
#endif
219198
s_last_ticks_elapsed_in_stop = ticks_elapsed;
220199
vTaskStepTick(ticks_elapsed);
221200

@@ -238,6 +217,96 @@ extern void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) {
238217
__enable_irq();
239218
}
240219

220+
#else
221+
extern void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) {
222+
if (!lptim_systick_is_initialized() || !sleep_mode_is_allowed() ||
223+
!ipc_queue_check_idle()) {
224+
// To avoid LCPU enter incorrect state, make sure ipc queue is empty before enter stop mode.
225+
return;
226+
}
227+
228+
// Note: all tasks are suspended at this point, but we can still be interrupted
229+
// so the critical section is necessary. taskENTER_CRITICAL() is not used here
230+
// as that method would mask interrupts that should exit the low-power mode.
231+
// The __disable_irq() function sets the PRIMASK bit which globally prevents
232+
// interrupt execution while still allowing interrupts to wake the processor
233+
// from WFI.
234+
// Conversely, taskEnter_CRITICAL() sets the BASEPRI register, which masks
235+
// interrupts with priorities lower than configMAX_SYSCALL_INTERRUPT_PRIORITY
236+
// from executing and from waking the processor.
237+
// See: http://infocenter.arm.com/help/topic/com.arm.doc.dui0552a/BABGGICD.html#BGBHDHAI
238+
__disable_irq();
239+
240+
power_tracking_stop(PowerSystemMcuCoreRun);
241+
242+
if (eTaskConfirmSleepModeStatus() != eAbortSleep) {
243+
if (xExpectedIdleTime < MIN_STOP_TICKS || !stop_mode_is_allowed()) {
244+
uint32_t counter_start = LPTIM1->CNT;
245+
246+
power_tracking_start(PowerSystemMcuCoreSleep);
247+
// HAL_GPIO_WritePin(DEBUG_PIN_CONFIG.gpio, DEBUG_PIN_CONFIG.gpio_pin, false);
248+
249+
__DSB(); // Drain any pending memory writes before entering sleep.
250+
251+
__WFI();
252+
__NOP();
253+
__NOP();
254+
__NOP();
255+
__NOP();
256+
__NOP();
257+
__NOP();
258+
__NOP();
259+
__NOP();
260+
__NOP();
261+
__NOP();
262+
263+
__ISB(); // Let the pipeline catch up (force the WFI to activate before moving on).
264+
265+
// HAL_GPIO_WritePin(DEBUG_PIN_CONFIG.gpio, DEBUG_PIN_CONFIG.gpio_pin, true);
266+
power_tracking_stop(PowerSystemMcuCoreSleep);
267+
268+
uint32_t counter_stop = LPTIM1->CNT;
269+
if (counter_stop < counter_start) {
270+
counter_stop += 0x10000;
271+
}
272+
uint32_t counter_elapsed = counter_stop - counter_start;
273+
uint32_t cycles_elapsed = (counter_elapsed * RTC_TICKS_HZ) / 9000;
274+
275+
s_analytics_device_sleep_cpu_cycles += cycles_elapsed;
276+
s_analytics_app_sleep_cpu_cycles += cycles_elapsed;
277+
} else {
278+
const RtcTicks stop_duration = MIN(xExpectedIdleTime - EARLY_WAKEUP_TICKS, MAX_STOP_TICKS);
279+
280+
// Go into stop mode until the wakeup_tick.
281+
s_last_ticks_commanded_in_stop = stop_duration;
282+
283+
lptim_systick_tickless_idle((uint32_t)stop_duration);
284+
285+
enter_stop_mode();
286+
287+
uint32_t ticks_elapsed = lptim_systick_get_elapsed_ticks();
288+
289+
s_last_ticks_elapsed_in_stop = ticks_elapsed;
290+
vTaskStepTick(ticks_elapsed);
291+
292+
PBL_LOG(LOG_LEVEL_DEBUG, "Expected idle time remain: %lu", (uint32_t)xExpectedIdleTime - ticks_elapsed);
293+
294+
// Update the task watchdog every time we come out of STOP mode (which is
295+
// at least once/second) since the timer peripheral will not have been
296+
// incremented
297+
task_watchdog_step_elapsed_time_ms((ticks_elapsed * 1000) / RTC_TICKS_HZ);
298+
299+
s_analytics_device_stop_ticks += ticks_elapsed;
300+
s_analytics_app_stop_ticks += ticks_elapsed;
301+
}
302+
}
303+
304+
power_tracking_start(PowerSystemMcuCoreRun);
305+
306+
__enable_irq();
307+
}
308+
#endif
309+
241310
void vApplicationStackOverflowHook(TaskHandle_t task_handle, signed char *name) {
242311
PebbleTask task = pebble_task_get_task_for_handle(task_handle);
243312

src/fw/kernel/util/stop.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,6 @@ void enter_stop_mode(void) {
107107
uint32_t dll2_freq;
108108
int clk_src;
109109

110-
lptim_systick_pause();
111-
112110
clear_interrupt_setting();
113111

114112
/* Wait flash cache idle */
@@ -125,6 +123,9 @@ void enter_stop_mode(void) {
125123
HAL_RCC_HCPU_DisableDLL1();
126124
HAL_RCC_HCPU_DisableDLL2();
127125

126+
HAL_HPAON_DISABLE_PAD();
127+
HAL_HPAON_DISABLE_VHP();
128+
128129
HAL_HPAON_CLEAR_HP_ACTIVE();
129130
HAL_HPAON_SET_POWER_MODE(AON_PMR_DEEP_SLEEP);
130131

@@ -139,6 +140,9 @@ void enter_stop_mode(void) {
139140
__NOP();
140141
__NOP();
141142
__NOP();
143+
144+
HAL_HPAON_ENABLE_PAD();
145+
HAL_HPAON_ENABLE_VHP();
142146

143147
HAL_HPAON_SET_HP_ACTIVE();
144148
HAL_HPAON_CLEAR_POWER_MODE();
@@ -157,8 +161,6 @@ void enter_stop_mode(void) {
157161
HAL_Delay_us(0);
158162

159163
restore_interrupt_setting();
160-
161-
lptim_systick_resume();
162164
}
163165
#else /* STM32 */
164166
void enter_stop_mode(void) {

0 commit comments

Comments
 (0)