samd/clock_config: Extend the SAMD51 us-counter to 60 bit.

This removes the difference in the time.ticks_us() range between SAMD21 and
SAMD51.

The function mp_hal_ticks_us_64() is added and used for:
- SAMD51's mp_hal_ticks_us and mp_hal_delay_us().
  For SAMD21, keep the previous methods, which are faster.
- mp_hal_ticks_ms() and mp_hal_tick_ms_64(), which saves some bytes
  and removes a potential race condition every 50 days.

Also set the us-counter for SAMD51 to 16 MHz for a faster reading of the
microsecond value.

Note: With SAMD51, mp_hal_ticks_us_64() has a 60 bit range only, which is
still a long time (~36000 years).
This commit is contained in:
robert-hh 2022-09-29 16:13:23 +02:00 committed by Damien George
parent fc9d66fac6
commit e33db80a59
6 changed files with 71 additions and 35 deletions

View File

@ -190,7 +190,7 @@ void init_clocks(uint32_t cpu_freq) {
// GCLK0: 48MHz from DFLL48M or 48 - 200 MHz from DPLL0 (SAMD51)
// GCLK1: 32768 Hz from 32KULP or DFLL48M
// GCLK2: 8-48MHz from DFLL48M for Peripheral devices
// GCLK3: 8Mhz for the us-counter (TC0/TC1)
// GCLK3: 16Mhz for the us-counter (TC0/TC1)
// GCLK4: 32kHz from crystal, if present
// GCLK5: 48MHz from DFLL48M for USB
// DPLL0: 48 - 200 MHz
@ -204,7 +204,7 @@ void init_clocks(uint32_t cpu_freq) {
// Setup DPLL0 to 120MHz
// Setup GCLK0 to 120MHz
// Setup GCLK2 to 48MHz for Peripherals
// Setup GCLK3 to 8MHz for TC0/TC1
// Setup GCLK3 to 16MHz for TC0/TC1
// Setup GCLK4 to 32kHz crystal, if present
// Setup GCLK5 to 48 MHz
@ -320,8 +320,8 @@ void init_clocks(uint32_t cpu_freq) {
while (GCLK->SYNCBUSY.bit.GENCTRL2) {
}
// Setup GCLK3 for 8MHz, Used for TC0/1 counter
GCLK->GENCTRL[3].reg = GCLK_GENCTRL_DIV(6) | GCLK_GENCTRL_RUNSTDBY | GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_DFLL;
// Setup GCLK3 for 16MHz, Used for TC0/1 counter
GCLK->GENCTRL[3].reg = GCLK_GENCTRL_DIV(3) | GCLK_GENCTRL_RUNSTDBY | GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_DFLL;
while (GCLK->SYNCBUSY.bit.GENCTRL3) {
}
}

View File

@ -34,9 +34,6 @@ unsigned long trng_random_u32(void);
#endif
#endif
// Due to a limitation in the TC counter for us, the ticks period is 2**29
#define MICROPY_PY_UTIME_TICKS_PERIOD (0x20000000)
// fatfs configuration used in ffconf.h
#define MICROPY_FATFS_ENABLE_LFN (1)
#define MICROPY_FATFS_RPATH (2)

View File

@ -37,6 +37,8 @@
#define MICROPY_HW_STDIN_BUFFER_LEN 128
#endif
extern volatile uint32_t ticks_us64_upper;
STATIC uint8_t stdin_ringbuf_array[MICROPY_HW_STDIN_BUFFER_LEN];
ringbuf_t stdin_ringbuf = { stdin_ringbuf_array, sizeof(stdin_ringbuf_array), 0, 0 };
@ -111,19 +113,47 @@ void mp_hal_delay_ms(mp_uint_t ms) {
void mp_hal_delay_us(mp_uint_t us) {
if (us > 0) {
uint32_t start = mp_hal_ticks_us();
#if defined(MCU_SAMD21)
// SAMD21 counter has effective 32 bit width
uint32_t start = mp_hal_ticks_us();
while ((mp_hal_ticks_us() - start) < us) {
}
#elif defined(MCU_SAMD51)
// SAMD51 counter has effective 29 bit width
while (((mp_hal_ticks_us() - start) & (MICROPY_PY_UTIME_TICKS_PERIOD - 1)) < us) {
#else
uint64_t stop = mp_hal_ticks_us_64() + us;
while (mp_hal_ticks_us_64() < stop) {
}
#endif
}
}
uint64_t mp_hal_ticks_us_64(void) {
uint32_t us64_upper = ticks_us64_upper;
uint32_t us64_lower;
uint8_t intflag;
__disable_irq();
#if defined(MCU_SAMD21)
us64_lower = REG_TC4_COUNT32_COUNT;
intflag = TC4->COUNT32.INTFLAG.reg;
#elif defined(MCU_SAMD51)
TC0->COUNT32.CTRLBSET.reg = TC_CTRLBSET_CMD_READSYNC;
while (TC0->COUNT32.CTRLBSET.reg != 0) {
}
us64_lower = REG_TC0_COUNT32_COUNT;
intflag = TC0->COUNT32.INTFLAG.reg;
#endif
__enable_irq();
if ((intflag & TC_INTFLAG_OVF) && us64_lower < 0x10000000) {
// The timer counter overflowed before reading it but the IRQ handler
// has not yet been called, so perform the IRQ arithmetic now.
us64_upper++;
}
#if defined(MCU_SAMD21)
return ((uint64_t)us64_upper << 32) | us64_lower;
#elif defined(MCU_SAMD51)
return ((uint64_t)us64_upper << 28) | (us64_lower >> 4);
#endif
}
uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) {
uintptr_t ret = 0;

View File

@ -37,7 +37,7 @@
extern int mp_interrupt_char;
extern volatile uint32_t systick_ms;
extern volatile uint32_t systick_ms_upper;
uint64_t mp_hal_ticks_us_64(void);
void mp_hal_set_interrupt_char(int c);
@ -47,28 +47,19 @@ void mp_hal_set_interrupt_char(int c);
#define mp_hal_delay_us_fast mp_hal_delay_us
static inline mp_uint_t mp_hal_ticks_ms(void) {
return systick_ms;
static inline uint64_t mp_hal_ticks_ms_64(void) {
return mp_hal_ticks_us_64() / 1000;
}
static inline uint64_t mp_hal_ticks_ms_64(void) {
return ((uint64_t)systick_ms_upper << 32) + systick_ms;
static inline mp_uint_t mp_hal_ticks_ms(void) {
return (mp_uint_t)mp_hal_ticks_ms_64();
}
static inline mp_uint_t mp_hal_ticks_us(void) {
#if defined(MCU_SAMD21)
return REG_TC4_COUNT32_COUNT;
#elif defined(MCU_SAMD51)
TC0->COUNT32.CTRLBSET.reg = TC_CTRLBSET_CMD_READSYNC;
while (TC0->COUNT32.CTRLBSET.reg != 0) {
}
return REG_TC0_COUNT32_COUNT >> 3;
#else
return systick_ms * 1000;
return (mp_uint_t)mp_hal_ticks_us_64();
#endif
}
@ -89,7 +80,7 @@ static inline mp_uint_t mp_hal_ticks_cpu(void) {
#endif
static inline uint64_t mp_hal_time_ns(void) {
return mp_hal_ticks_ms_64() * 1000000;
return mp_hal_ticks_us_64() * 1000;
}
// C-level pin HAL

View File

@ -41,7 +41,7 @@ extern void EIC_Handler(void);
const ISR isr_vector[];
volatile uint32_t systick_ms;
volatile uint32_t systick_ms_upper;
volatile uint32_t ticks_us64_upper;
void Reset_Handler(void) __attribute__((naked));
void Reset_Handler(void) {
@ -93,15 +93,27 @@ void Default_Handler(void) {
void SysTick_Handler(void) {
uint32_t next_tick = systick_ms + 1;
systick_ms = next_tick;
if (systick_ms == 0) {
systick_ms_upper += 1;
}
if (soft_timer_next == next_tick) {
pendsv_schedule_dispatch(PENDSV_DISPATCH_SOFT_TIMER, soft_timer_handler);
}
}
void us_timer_IRQ(void) {
#if defined(MCU_SAMD21)
if (TC4->COUNT32.INTFLAG.reg & TC_INTFLAG_OVF) {
ticks_us64_upper++;
}
TC4->COUNT32.INTFLAG.reg = TC_INTFLAG_OVF;
#elif defined(MCU_SAMD51)
if (TC0->COUNT32.INTFLAG.reg & TC_INTFLAG_OVF) {
ticks_us64_upper++;
}
TC0->COUNT32.INTFLAG.reg = TC_INTFLAG_OVF;
#endif
}
// Sercom IRQ handler support
void (*sercom_irq_handler_table[SERCOM_INST_NUM])(int num) = {};
void sercom_register_irq(int sercom_id, void (*sercom_irq_handler)) {
@ -180,7 +192,7 @@ const ISR isr_vector[] __attribute__((section(".isr_vector"))) = {
0, // 16 Timer Counter Control 1 (TCC1)
0, // 17 Timer Counter Control 2 (TCC2)
0, // 18 Basic Timer Counter 3 (TC3)
0, // 19 Basic Timer Counter 4 (TC4)
&us_timer_IRQ, // 19 Basic Timer Counter 4 (TC4)
0, // 20 Basic Timer Counter 5 (TC5)
0, // 21 Basic Timer Counter 6 (TC6)
0, // 22 Basic Timer Counter 7 (TC7)
@ -316,7 +328,7 @@ const ISR isr_vector[] __attribute__((section(".isr_vector"))) = {
0, // 104 Timer Counter Control 4 (TCC4): TCC4_CNT_A ...
0, // 105 Timer Counter Control 4 (TCC4): TCC4_MC_0
0, // 106 Timer Counter Control 4 (TCC4): TCC4_MC_1
0, // 107 Basic Timer Counter 0 (TC0)
&us_timer_IRQ, // 107 Basic Timer Counter 0 (TC0)
0, // 108 Basic Timer Counter 1 (TC1)
0, // 109 Basic Timer Counter 2 (TC2)
0, // 110 Basic Timer Counter 3 (TC3)

View File

@ -68,7 +68,7 @@ static void usb_init(void) {
tusb_init();
}
// Initialize the microsecond counter on TC 0/1
// Initialize the µs counter on TC 0/1 or TC4/5
void init_us_counter(void) {
#if defined(MCU_SAMD21)
@ -89,6 +89,9 @@ void init_us_counter(void) {
TC4->COUNT32.READREQ.reg = TC_READREQ_RREQ | TC_READREQ_RCONT | 0x10;
while (TC4->COUNT32.STATUS.bit.SYNCBUSY) {
}
// Enable the IRQ
TC4->COUNT32.INTENSET.reg = TC_INTENSET_OVF;
NVIC_EnableIRQ(TC4_IRQn);
#elif defined(MCU_SAMD51)
@ -107,6 +110,9 @@ void init_us_counter(void) {
while (TC0->COUNT32.SYNCBUSY.bit.ENABLE) {
}
// Enable the IRQ
TC0->COUNT32.INTENSET.reg = TC_INTENSET_OVF;
NVIC_EnableIRQ(TC0_IRQn);
#endif
}