diff --git a/ports/atmel-samd/common-hal/microcontroller/__init__.c b/ports/atmel-samd/common-hal/microcontroller/__init__.c index fa057facac..c57b6939ce 100644 --- a/ports/atmel-samd/common-hal/microcontroller/__init__.c +++ b/ports/atmel-samd/common-hal/microcontroller/__init__.c @@ -42,13 +42,31 @@ void common_hal_mcu_delay_us(uint32_t delay) { // Interrupt flags that will be saved and restored during disable/Enable // interrupt functions below. -volatile hal_atomic_t flags; + +// ASF4's interrupt disable doesn't handle duplicate calls +volatile uint32_t interrupt_flags; +volatile uint32_t nesting_count = 0; void common_hal_mcu_disable_interrupts(void) { - atomic_enter_critical(&flags); + if (nesting_count == 0) { + interrupt_flags = __get_PRIMASK(); + __disable_irq(); + __DMB(); + } + nesting_count++; } void common_hal_mcu_enable_interrupts(void) { - atomic_leave_critical(&flags); + if (nesting_count == 0) { + // This is very very bad because it means there was mismatched disable/enables so we + // "HardFault". + HardFault_Handler(); + } + nesting_count--; + if (nesting_count > 0) { + return; + } + __DMB(); + __set_PRIMASK(interrupt_flags); } extern uint32_t _ezero; diff --git a/ports/atmel-samd/tick.c b/ports/atmel-samd/tick.c index 27b5f05b44..6cd7c7833f 100644 --- a/ports/atmel-samd/tick.c +++ b/ports/atmel-samd/tick.c @@ -30,6 +30,7 @@ #include "supervisor/shared/autoreload.h" #include "shared-module/gamepad/__init__.h" +#include "shared-bindings/microcontroller/__init__.h" #include "shared-bindings/microcontroller/Processor.h" // Global millisecond tick count @@ -38,8 +39,13 @@ volatile uint64_t ticks_ms = 0; void SysTick_Handler(void) { // SysTick interrupt handler called when the SysTick timer reaches zero // (every millisecond). + common_hal_mcu_disable_interrupts(); ticks_ms += 1; + // Read the control register to reset the COUNTFLAG. + (void) SysTick->CTRL; + common_hal_mcu_enable_interrupts(); + #ifdef CIRCUITPY_AUTORELOAD_DELAY_MS autoreload_tick(); #endif @@ -61,7 +67,7 @@ void tick_delay(uint32_t us) { uint32_t us_until_next_tick = SysTick->VAL / ticks_per_us; uint32_t start_tick; while (us >= us_until_next_tick) { - start_tick=SysTick->VAL; // wait for SysTick->VAL to RESET + start_tick = SysTick->VAL; // wait for SysTick->VAL to RESET while (SysTick->VAL < start_tick) {} us -= us_until_next_tick; us_until_next_tick = 1000; @@ -72,11 +78,25 @@ void tick_delay(uint32_t us) { // us counts down! void current_tick(uint64_t* ms, uint32_t* us_until_ms) { uint32_t ticks_per_us = common_hal_mcu_processor_get_frequency() / 1000 / 1000; - *ms = ticks_ms; - *us_until_ms = SysTick->VAL / ticks_per_us; + + // We disable interrupts to prevent ticks_ms from changing while we grab it. + common_hal_mcu_disable_interrupts(); + uint32_t tick_status = SysTick->CTRL; + uint32_t current_us = SysTick->VAL; + uint32_t tick_status2 = SysTick->CTRL; + uint64_t current_ms = ticks_ms; + // The second clause ensures our value actually rolled over. Its possible it hit zero between + // the VAL read and CTRL read. + if ((tick_status & SysTick_CTRL_COUNTFLAG_Msk) != 0 || + ((tick_status2 & SysTick_CTRL_COUNTFLAG_Msk) != 0 && current_us > ticks_per_us)) { + current_ms++; + } + common_hal_mcu_enable_interrupts(); + *ms = current_ms; + *us_until_ms = current_us / ticks_per_us; } void wait_until(uint64_t ms, uint32_t us_until_ms) { uint32_t ticks_per_us = common_hal_mcu_processor_get_frequency() / 1000 / 1000; - while(ticks_ms <= ms && SysTick->VAL / ticks_per_us >= us_until_ms) {} + while (ticks_ms <= ms && SysTick->VAL / ticks_per_us >= us_until_ms) {} }