Commit 46791795 authored by Rahix's avatar Rahix
Browse files

Merge 'pycardium: Use sleep API call while sleeping'

See merge request !480
parents 77bd73ba 09e61d97
Pipeline #5261 passed with stages
in 1 minute and 45 seconds
......@@ -15,6 +15,7 @@
#include "mxc_pins.h"
#include <stdint.h>
#include <limits.h>
/* Most code is taken and adapted rom EvKitExamples/LP/main.c */
......@@ -207,3 +208,16 @@ void sleep_deepsleep(void)
SystemCoreClockUpdate();
MAX77650_setEN_SBB2(0b110);
}
int epic_sleep(uint32_t ms)
{
/* Allow the interrupt module to break us out of a call to
* epic_sleep() */
uint32_t count = ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(ms));
if (count == 0) {
return 0;
} else {
return INT_MAX;
}
}
......@@ -33,6 +33,7 @@ typedef _Bool bool;
#define API_SYSTEM_EXEC 0x2
#define API_SYSTEM_RESET 0x3
#define API_BATTERY_VOLTAGE 0x4
#define API_SLEEP 0x5
#define API_INTERRUPT_ENABLE 0xA
#define API_INTERRUPT_DISABLE 0xB
......@@ -335,6 +336,25 @@ API(API_SYSTEM_EXEC, int __epic_exec(char *name));
*/
API(API_SYSTEM_RESET, void epic_system_reset(void));
/**
* Sleep for the specified amount of time.
*
* This call will block for at most the specified amount of time. It allows epicardium to
* reduce clock speed of the system until this call is finished.
*
* This call returns early if an interrupt is signaled from epicardium.
*
* The clock source of epicardium has a limited amount of accuracy. Tolerances
* of +- 10% have been observed. This means that the sleep time also has a
* tolarance of at least +- 10%. The exact amount varies from device to device and
* also with temperature. You should take this into consideration when selecting
* the time you want to sleep.
*
* :param ms: Time to wait in milliseconds
* :returns: 0 if no interrupt happened, ``INT_MAX`` if an interrupt happened and the sleep ended early.
*/
API(API_SLEEP, int epic_sleep(uint32_t ms));
/**
* PMIC API
* ===============
......
......@@ -2,6 +2,7 @@
#include "os/core.h"
#include "epicardium.h"
#include "api/interrupt-sender.h"
#include "user_core/user_core.h"
#include <assert.h>
struct interrupt_priv {
......@@ -45,6 +46,10 @@ void interrupt_trigger_sync(api_int_id_t id)
;
api_interrupt_trigger(id);
/* Break the dispatcher task out of a potential call
* to epic_sleep() */
xTaskNotifyGive(dispatcher_task_id);
out:
mutex_unlock(&interrupt_mutex);
}
......@@ -171,6 +176,10 @@ void vInterruptsTask(void *pvParameters)
interrupt_data.has_pending = false;
} else if (interrupt_data.int_enabled[current_irq]) {
api_interrupt_trigger(current_irq);
/* Break the dispatcher task out of a potential call
* to epic_sleep() */
xTaskNotifyGive(dispatcher_task_id);
}
mutex_unlock(&interrupt_mutex);
......
......@@ -20,9 +20,13 @@
#include <stdio.h>
#include <string.h>
// Smallest interval which can be reached exactly
#define SYSTICK_INTERVAL_US 15625ULL
#define SYSTICK_FREQ_HZ (1000000 / SYSTICK_INTERVAL_US)
/* Smallest integer us interval which can be reached exactly due
* to the 32768 Hz systick frequency */
#define SYSTICK_INTERVAL_US_MIN 15625
/* Target systick interval is 1 second */
#define SYSTICK_INTERVAL_US \
(SYSTICK_INTERVAL_US_MIN * (1000000 / SYSTICK_INTERVAL_US_MIN))
/*
* Copied from core_cm4.h and modified to select the
......@@ -59,7 +63,7 @@ void pycardium_hal_init(void)
/*
* Configure SysTick timer for SYSTICK_INTERVAL_US period.
*/
systick_config(32768 / SYSTICK_FREQ_HZ);
systick_config(SYSTICK_INTERVAL_US * 32768LL / 1000000);
}
/******************************************************************************
......@@ -153,7 +157,7 @@ void mp_hal_set_interrupt_char(char c)
* SysTick timer at 1000 Hz
*/
static volatile uint64_t systick_count = 0;
static volatile int64_t systick_count = 0;
void SysTick_Handler(void)
{
......@@ -163,7 +167,7 @@ void SysTick_Handler(void)
/*
* Get an absolute "timestamp" in microseconds.
*/
static uint64_t systick_get_us()
static int64_t systick_get_us()
{
uint32_t val, count;
uint32_t irqsaved = __get_PRIMASK();
......@@ -178,8 +182,8 @@ static uint64_t systick_get_us()
__set_PRIMASK(irqsaved);
} while (val == 0);
uint64_t us = count * SYSTICK_INTERVAL_US +
(SysTick->LOAD - val) * 1000000ULL / 32768;
int64_t us = count * SYSTICK_INTERVAL_US +
(SysTick->LOAD - val) * 1000000LL / 32768;
return us;
}
......@@ -225,51 +229,6 @@ static void systick_delay_precise(uint32_t us)
}
}
static void systick_delay_sleep(uint32_t us)
{
uint64_t final_time = systick_get_us() + (uint64_t)us - 2;
while (1) {
uint64_t now = systick_get_us();
if ((now + SYSTICK_INTERVAL_US) > final_time) {
break;
}
/*
* Sleep with WFI if more than SYSTICK_INTERVAL_US of delay
* is remaining. The SysTick interrupt is guaranteed to
* happen within any timespan of SYSTICK_INTERVAL_US.
*
* Use a critical section encompassing both the check and the
* WFI to prevent a race-condition where the interrupt happens
* just in between the check and WFI.
*/
uint32_t irqsaved = __get_PRIMASK();
__set_PRIMASK(0);
if ((now + SYSTICK_INTERVAL_US) < final_time) {
__WFI();
}
__set_PRIMASK(irqsaved);
/*
* Handle pending MicroPython 'interrupts'. This call could
* potentially not return here when a handler raises an
* exception. Those will propagate outwards and thus make the
* delay return early.
*
* One example of this happeing is the KeyboardInterrupt
* (CTRL+C) which will abort the running code and exit to REPL.
*/
mp_handle_pending(true);
}
uint64_t now = systick_get_us();
if (now < final_time) {
systick_delay_precise(final_time - now);
}
}
static void systick_delay(uint32_t us)
{
if (us == 0)
......@@ -277,15 +236,42 @@ static void systick_delay(uint32_t us)
/*
* For very short delays, use the systick_delay_precise() function which
* delays with a microsecond accuracy. For anything >SYSTICK_INTERVAL_US, use
* delays with a microsecond accuracy. For anything > 10 ms, use
* systick_delay_sleep() which puts the CPU to sleep when nothing is
* happening and also checks for MicroPython interrupts every now and
* then.
*/
if (us < SYSTICK_INTERVAL_US) {
if (us < 10000) {
systick_delay_precise(us);
} else {
systick_delay_sleep(us);
int64_t now = systick_get_us();
int64_t final_time = now + us;
while (final_time - systick_get_us() > 10000) {
uint32_t sleep_time =
(final_time - systick_get_us()) / 1000;
/* We need to wake up at least in SYSTICK_INTERVAL_US to make
* sure we serve the systick interrupt. */
if (sleep_time > SYSTICK_INTERVAL_US / 1000) {
sleep_time = SYSTICK_INTERVAL_US / 1000;
}
/* Add some error margin to avoid issues with the clock accuracy
* of epicardium. We will account for the actual time via our
* (accurate) systick */
epic_sleep(sleep_time * 8 / 10);
/* epic_sleep() can return early if there was an interrupt
* coming from epicardium side.
* Give MP a chance to handle them. */
mp_handle_pending(true);
}
now = systick_get_us();
if (final_time > now) {
systick_delay_precise(final_time - now);
}
}
}
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment