Commit a80eb9a4 authored by schneider's avatar schneider
Browse files

change(pycardium): Switch systick to 32 kHz clock source

This is in perparation of epicardium being allowed to change the system
clock source. By using the alternate clock source for the systick, we
can keep accurate (tick) time on pycardium.
parent 01dab506
Pipeline #5250 passed with stages
in 31 seconds
......@@ -20,6 +20,30 @@
#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)
/*
* Copied from core_cm4.h and modified to select the
* 32768 Hz RTC crystal as the clock source */
static uint32_t systick_config(uint32_t ticks)
{
if ((ticks - 1) > SysTick_LOAD_RELOAD_Msk)
return (1); /* Reload value impossible */
SysTick->LOAD = ticks - 1; /* set reload register */
NVIC_SetPriority(
SysTick_IRQn,
(1 << __NVIC_PRIO_BITS) -
1); /* set Priority for Systick Interrupt */
SysTick->VAL = 0; /* Load the SysTick Counter Value */
SysTick->CTRL =
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */
return (0); /* Function successful */
}
/* Initialize everything for MicroPython */
void pycardium_hal_init(void)
{
......@@ -33,9 +57,9 @@ void pycardium_hal_init(void)
epic_interrupt_enable(EPIC_INT_UART_RX);
/*
* Configure SysTick timer for 1ms period.
* Configure SysTick timer for SYSTICK_INTERVAL_US period.
*/
SysTick_Config(SystemCoreClock / 1000);
systick_config(32768 / SYSTICK_FREQ_HZ);
}
/******************************************************************************
......@@ -141,14 +165,18 @@ void SysTick_Handler(void)
*/
static uint64_t systick_get_us()
{
uint32_t irqsaved = __get_PRIMASK();
__set_PRIMASK(0);
uint32_t val, count;
uint64_t counts_per_us = SystemCoreClock / 1000000;
uint64_t us = systick_count * 1000 +
(SysTick->LOAD - SysTick->VAL) / counts_per_us;
/* The asynchronous/slow clocking of the systick means that
* its value can jump to 0 before the interrupt is triggered.
* Simply wait until it is not 0 and then read the count. */
do {
val = SysTick->VAL;
count = systick_count;
} while (val == 0);
__set_PRIMASK(irqsaved);
uint64_t us = count * SYSTICK_INTERVAL_US +
(SysTick->LOAD - val) * 1000000ULL / 32768;
return us;
}
......@@ -160,14 +188,12 @@ static void systick_delay_precise(uint32_t us)
* instruction, read the current timer value to ensure as little skew as
* possible.
*
* Subtract 0.3us (constant_offset) to account for the duration of the
* calculations.
* Accuracy is about 30 us (due to the 32 kHz systick)
*/
uint32_t count_to_overflow = SysTick->VAL;
uint32_t count_reload = SysTick->LOAD;
uint32_t clocks_per_us = SystemCoreClock / 1000000;
uint32_t constant_offset = clocks_per_us * 3 / 10;
uint32_t delay_count = us * clocks_per_us - constant_offset;
uint32_t delay_count = us * 32768 / 1000000;
/*
* Calculate the final count for both paths. Marked as volatile so the
......@@ -203,14 +229,14 @@ static void systick_delay_sleep(uint32_t us)
while (1) {
uint64_t now = systick_get_us();
if (now >= final_time) {
if ((now + SYSTICK_INTERVAL_US) > final_time) {
break;
}
/*
* Sleep with WFI if more than 1ms of delay is remaining. The
* SysTick interrupt is guaranteed to happen within any timespan
* of 1ms.
* 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
......@@ -218,7 +244,7 @@ static void systick_delay_sleep(uint32_t us)
*/
uint32_t irqsaved = __get_PRIMASK();
__set_PRIMASK(0);
if ((now + 1000) < final_time) {
if ((now + SYSTICK_INTERVAL_US) < final_time) {
__WFI();
}
__set_PRIMASK(irqsaved);
......@@ -234,6 +260,11 @@ static void systick_delay_sleep(uint32_t us)
*/
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)
......@@ -243,12 +274,12 @@ 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 >1ms, use
* delays with a microsecond accuracy. For anything >SYSTICK_INTERVAL_US, 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 < 1000) {
if (us < SYSTICK_INTERVAL_US) {
systick_delay_precise(us);
} else {
systick_delay_sleep(us);
......
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