Skip to content

Commit

Permalink
samd/clock_config: Extend the SAMD51 us-counter to 60 bit.
Browse files Browse the repository at this point in the history
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).
  • Loading branch information
robert-hh authored and dpgeorge committed Oct 25, 2022
1 parent fc9d66f commit e33db80
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 35 deletions.
8 changes: 4 additions & 4 deletions ports/samd/mcu/samd51/clock_config.c
Expand Up @@ -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
Expand All @@ -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

Expand Down Expand Up @@ -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) {
}
}
Expand Down
3 changes: 0 additions & 3 deletions ports/samd/mcu/samd51/mpconfigmcu.h
Expand Up @@ -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)
Expand Down
40 changes: 35 additions & 5 deletions ports/samd/mphalport.c
Expand Up @@ -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 };

Expand Down Expand Up @@ -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;

Expand Down
23 changes: 7 additions & 16 deletions ports/samd/mphalport.h
Expand Up @@ -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);

Expand All @@ -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
}

Expand All @@ -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
Expand Down
24 changes: 18 additions & 6 deletions ports/samd/samd_isr.c
Expand Up @@ -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) {
Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
8 changes: 7 additions & 1 deletion ports/samd/samd_soc.c
Expand Up @@ -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)

Expand All @@ -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)

Expand All @@ -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
}

Expand Down

0 comments on commit e33db80

Please sign in to comment.