Switch SAMD21 ticks to PER event
The EVSYS is used to generate an interrupt from the event. This simplifies timing used in pulseio that conflicted with the auto-reload countdown. Fixes #3890
This commit is contained in:
parent
f805e63297
commit
f0859ac954
@ -149,7 +149,7 @@ STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input
|
||||
mp_hal_stdout_tx_strn("\x04", 1);
|
||||
}
|
||||
// check for SystemExit
|
||||
if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(((mp_obj_base_t *)nlr.ret_val)->type), MP_OBJ_FROM_PTR(&mp_type_SystemExit))) {
|
||||
if (mp_obj_is_subclass_fast(mp_obj_get_type((mp_obj_t)nlr.ret_val), MP_OBJ_FROM_PTR(&mp_type_SystemExit))) {
|
||||
// at the moment, the value of SystemExit is unused
|
||||
ret = pyexec_system_exit;
|
||||
#if CIRCUITPY_ALARM
|
||||
|
@ -265,13 +265,13 @@ audio_dma_result audio_dma_setup_playback(audio_dma_t *dma,
|
||||
|
||||
#ifdef SAM_D5X_E5X
|
||||
int irq = dma->event_channel < 4 ? EVSYS_0_IRQn + dma->event_channel : EVSYS_4_IRQn;
|
||||
// Only disable and clear on SAMD51 because the SAMD21 shares EVSYS with ticks.
|
||||
NVIC_DisableIRQ(irq);
|
||||
NVIC_ClearPendingIRQ(irq);
|
||||
#else
|
||||
int irq = EVSYS_IRQn;
|
||||
#endif
|
||||
|
||||
NVIC_DisableIRQ(irq);
|
||||
NVIC_ClearPendingIRQ(irq);
|
||||
|
||||
DmacDescriptor *first_descriptor = dma_descriptor(dma_channel);
|
||||
setup_audio_descriptor(first_descriptor, dma->beat_size, output_spacing, output_register_address);
|
||||
if (single_buffer) {
|
||||
@ -366,7 +366,7 @@ STATIC void dma_callback_fun(void *arg) {
|
||||
audio_dma_load_next_block(dma);
|
||||
}
|
||||
|
||||
void evsyshandler_common(void) {
|
||||
void audio_evsys_handler(void) {
|
||||
for (uint8_t i = 0; i < AUDIO_DMA_CHANNEL_COUNT; i++) {
|
||||
audio_dma_t *dma = audio_dma_state[i];
|
||||
if (dma == NULL) {
|
||||
@ -380,26 +380,4 @@ void evsyshandler_common(void) {
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SAM_D5X_E5X
|
||||
void EVSYS_0_Handler(void) {
|
||||
evsyshandler_common();
|
||||
}
|
||||
void EVSYS_1_Handler(void) {
|
||||
evsyshandler_common();
|
||||
}
|
||||
void EVSYS_2_Handler(void) {
|
||||
evsyshandler_common();
|
||||
}
|
||||
void EVSYS_3_Handler(void) {
|
||||
evsyshandler_common();
|
||||
}
|
||||
void EVSYS_4_Handler(void) {
|
||||
evsyshandler_common();
|
||||
}
|
||||
#else
|
||||
void EVSYS_Handler(void) {
|
||||
evsyshandler_common();
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -99,4 +99,6 @@ void audio_dma_background(void);
|
||||
|
||||
uint8_t find_sync_event_channel_raise(void);
|
||||
|
||||
void audio_evsys_handler(void);
|
||||
|
||||
#endif // MICROPY_INCLUDED_ATMEL_SAMD_AUDIO_DMA_H
|
||||
|
@ -91,26 +91,19 @@
|
||||
#if CIRCUITPY_PEW
|
||||
#include "common-hal/_pew/PewPew.h"
|
||||
#endif
|
||||
volatile bool hold_interrupt = false;
|
||||
static volatile bool sleep_ok = true;
|
||||
#ifdef SAMD21
|
||||
static void rtc_set_continuous(bool continuous) {
|
||||
while (RTC->MODE0.STATUS.bit.SYNCBUSY) {
|
||||
;
|
||||
}
|
||||
RTC->MODE0.READREQ.reg = (continuous ? RTC_READREQ_RCONT : 0) | 0x0010;
|
||||
while (RTC->MODE0.STATUS.bit.SYNCBUSY) {
|
||||
;
|
||||
}
|
||||
}
|
||||
static uint8_t _tick_event_channel = 0;
|
||||
|
||||
// Sleeping requires a register write that can stall interrupt handling. Turning
|
||||
// off sleeps allows for more accurate interrupt timing. (Python still thinks
|
||||
// it is sleeping though.)
|
||||
void rtc_start_pulse(void) {
|
||||
rtc_set_continuous(true);
|
||||
hold_interrupt = true;
|
||||
sleep_ok = false;
|
||||
}
|
||||
|
||||
void rtc_end_pulse(void) {
|
||||
hold_interrupt = false;
|
||||
rtc_set_continuous(false);
|
||||
sleep_ok = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -161,6 +154,20 @@ static void save_usb_clock_calibration(void) {
|
||||
}
|
||||
#endif
|
||||
|
||||
static void rtc_continuous_mode(void) {
|
||||
#ifdef SAMD21
|
||||
while (RTC->MODE0.STATUS.bit.SYNCBUSY) {
|
||||
}
|
||||
RTC->MODE0.READREQ.reg = RTC_READREQ_RCONT | 0x0010;
|
||||
while (RTC->MODE0.STATUS.bit.SYNCBUSY) {
|
||||
}
|
||||
// Do the first request and wait for it.
|
||||
RTC->MODE0.READREQ.reg = RTC_READREQ_RREQ | RTC_READREQ_RCONT | 0x0010;
|
||||
while (RTC->MODE0.STATUS.bit.SYNCBUSY) {
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void rtc_init(void) {
|
||||
#ifdef SAMD21
|
||||
_gclk_enable_channel(RTC_GCLK_ID, GCLK_CLKCTRL_GEN_GCLK2_Val);
|
||||
@ -168,9 +175,17 @@ static void rtc_init(void) {
|
||||
while (RTC->MODE0.CTRL.bit.SWRST != 0) {
|
||||
}
|
||||
|
||||
// Turn on periodic events to use as tick. We control whether it interrupts
|
||||
// us with the EVSYS INTEN register.
|
||||
RTC->MODE0.EVCTRL.reg = RTC_MODE0_EVCTRL_PEREO2;
|
||||
|
||||
RTC->MODE0.CTRL.reg = RTC_MODE0_CTRL_ENABLE |
|
||||
RTC_MODE0_CTRL_MODE_COUNT32 |
|
||||
RTC_MODE0_CTRL_PRESCALER_DIV2;
|
||||
|
||||
// Turn on continuous sync of the count register. This will speed up all
|
||||
// tick reads.
|
||||
rtc_continuous_mode();
|
||||
#endif
|
||||
#ifdef SAM_D5X_E5X
|
||||
hri_mclk_set_APBAMASK_RTC_bit(MCLK);
|
||||
@ -363,6 +378,9 @@ void reset_port(void) {
|
||||
#endif
|
||||
|
||||
reset_event_system();
|
||||
#ifdef SAMD21
|
||||
_tick_event_channel = EVSYS_SYNCH_NUM;
|
||||
#endif
|
||||
|
||||
reset_all_pins();
|
||||
|
||||
@ -430,21 +448,14 @@ uint32_t port_get_saved_word(void) {
|
||||
// TODO: Move this to an RTC backup register so we can preserve it when only the BACKUP power domain
|
||||
// is enabled.
|
||||
static volatile uint64_t overflowed_ticks = 0;
|
||||
#ifdef SAMD21
|
||||
static volatile bool _ticks_enabled = false;
|
||||
#endif
|
||||
|
||||
static uint32_t _get_count(uint64_t *overflow_count) {
|
||||
#ifdef SAM_D5X_E5X
|
||||
while ((RTC->MODE0.SYNCBUSY.reg & (RTC_MODE0_SYNCBUSY_COUNTSYNC | RTC_MODE0_SYNCBUSY_COUNT)) != 0) {
|
||||
}
|
||||
#endif
|
||||
#ifdef SAMD21
|
||||
// Request a read so we don't stall the bus later. See section 14.3.1.5 Read Request
|
||||
RTC->MODE0.READREQ.reg = RTC_READREQ_RREQ | 0x0010;
|
||||
while (RTC->MODE0.STATUS.bit.SYNCBUSY != 0) {
|
||||
}
|
||||
#endif
|
||||
// SAMD21 does continuous sync so we don't need to wait here.
|
||||
|
||||
// Disable interrupts so we can grab the count and the overflow.
|
||||
common_hal_mcu_disable_interrupts();
|
||||
uint32_t count = RTC->MODE0.COUNT.reg;
|
||||
@ -458,29 +469,6 @@ static uint32_t _get_count(uint64_t *overflow_count) {
|
||||
|
||||
volatile bool _woken_up;
|
||||
|
||||
static void _port_interrupt_after_ticks(uint32_t ticks) {
|
||||
uint32_t current_ticks = _get_count(NULL);
|
||||
if (ticks > 1 << 28) {
|
||||
// We'll interrupt sooner with an overflow.
|
||||
return;
|
||||
}
|
||||
#ifdef SAMD21
|
||||
if (hold_interrupt) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
uint32_t target = current_ticks + (ticks << 4);
|
||||
RTC->MODE0.COMP[0].reg = target;
|
||||
#ifdef SAM_D5X_E5X
|
||||
while ((RTC->MODE0.SYNCBUSY.reg & (RTC_MODE0_SYNCBUSY_COMP0)) != 0) {
|
||||
}
|
||||
#endif
|
||||
RTC->MODE0.INTFLAG.reg = RTC_MODE0_INTFLAG_CMP0;
|
||||
RTC->MODE0.INTENSET.reg = RTC_MODE0_INTENSET_CMP0;
|
||||
current_ticks = _get_count(NULL);
|
||||
_woken_up = current_ticks >= target;
|
||||
}
|
||||
|
||||
void RTC_Handler(void) {
|
||||
uint32_t intflag = RTC->MODE0.INTFLAG.reg;
|
||||
if (intflag & RTC_MODE0_INTFLAG_OVF) {
|
||||
@ -497,19 +485,10 @@ void RTC_Handler(void) {
|
||||
}
|
||||
#endif
|
||||
if (intflag & RTC_MODE0_INTFLAG_CMP0) {
|
||||
// Clear the interrupt because we may have hit a sleep and _ticks_enabled
|
||||
// Clear the interrupt because we may have hit a sleep
|
||||
RTC->MODE0.INTFLAG.reg = RTC_MODE0_INTFLAG_CMP0;
|
||||
_woken_up = true;
|
||||
#ifdef SAMD21
|
||||
if (_ticks_enabled) {
|
||||
// Do things common to all ports when the tick occurs.
|
||||
supervisor_tick();
|
||||
// Check _ticks_enabled again because a tick handler may have turned it off.
|
||||
if (_ticks_enabled) {
|
||||
_port_interrupt_after_ticks(1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// SAMD21 ticks are handled by EVSYS
|
||||
#ifdef SAM_D5X_E5X
|
||||
RTC->MODE0.INTENCLR.reg = RTC_MODE0_INTENCLR_CMP0;
|
||||
#endif
|
||||
@ -526,6 +505,39 @@ uint64_t port_get_raw_ticks(uint8_t *subticks) {
|
||||
return overflow_count + current_ticks / 16;
|
||||
}
|
||||
|
||||
void evsyshandler_common(void) {
|
||||
#ifdef SAMD21
|
||||
if (_tick_event_channel < EVSYS_SYNCH_NUM && event_interrupt_active(_tick_event_channel)) {
|
||||
supervisor_tick();
|
||||
}
|
||||
#endif
|
||||
#if CIRCUITPY_AUDIOIO || CIRCUITPY_AUDIOBUSIO
|
||||
audio_evsys_handler();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef SAM_D5X_E5X
|
||||
void EVSYS_0_Handler(void) {
|
||||
evsyshandler_common();
|
||||
}
|
||||
void EVSYS_1_Handler(void) {
|
||||
evsyshandler_common();
|
||||
}
|
||||
void EVSYS_2_Handler(void) {
|
||||
evsyshandler_common();
|
||||
}
|
||||
void EVSYS_3_Handler(void) {
|
||||
evsyshandler_common();
|
||||
}
|
||||
void EVSYS_4_Handler(void) {
|
||||
evsyshandler_common();
|
||||
}
|
||||
#else
|
||||
void EVSYS_Handler(void) {
|
||||
evsyshandler_common();
|
||||
}
|
||||
#endif
|
||||
|
||||
// Enable 1/1024 second tick.
|
||||
void port_enable_tick(void) {
|
||||
#ifdef SAM_D5X_E5X
|
||||
@ -533,9 +545,23 @@ void port_enable_tick(void) {
|
||||
RTC->MODE0.INTENSET.reg = RTC_MODE0_INTENSET_PER2;
|
||||
#endif
|
||||
#ifdef SAMD21
|
||||
// TODO: Switch to using the PER *event* from the RTC to generate an interrupt via EVSYS.
|
||||
_ticks_enabled = true;
|
||||
_port_interrupt_after_ticks(1);
|
||||
// SAMD21 ticks won't survive port_reset(). This *should* be ok since it'll
|
||||
// be triggered by ticks and no Python will be running.
|
||||
if (_tick_event_channel >= EVSYS_SYNCH_NUM) {
|
||||
turn_on_event_system();
|
||||
_tick_event_channel = find_sync_event_channel();
|
||||
}
|
||||
// This turns on both the event detected interrupt (EVD) and overflow (OVR).
|
||||
init_event_channel_interrupt(_tick_event_channel, CORE_GCLK, EVSYS_ID_GEN_RTC_PER_2);
|
||||
// Disable overflow interrupt because we ignore it.
|
||||
if (_tick_event_channel >= 8) {
|
||||
uint8_t value = 1 << (_tick_event_channel - 8);
|
||||
EVSYS->INTENCLR.reg = EVSYS_INTENSET_OVRp8(value);
|
||||
} else {
|
||||
uint8_t value = 1 << _tick_event_channel;
|
||||
EVSYS->INTENCLR.reg = EVSYS_INTENSET_OVR(value);
|
||||
}
|
||||
NVIC_EnableIRQ(EVSYS_IRQn);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -545,21 +571,46 @@ void port_disable_tick(void) {
|
||||
RTC->MODE0.INTENCLR.reg = RTC_MODE0_INTENCLR_PER2;
|
||||
#endif
|
||||
#ifdef SAMD21
|
||||
_ticks_enabled = false;
|
||||
RTC->MODE0.INTENCLR.reg = RTC_MODE0_INTENCLR_CMP0;
|
||||
if (_tick_event_channel >= 8) {
|
||||
uint8_t value = 1 << (_tick_event_channel - 8);
|
||||
EVSYS->INTENCLR.reg = EVSYS_INTENSET_EVDp8(value);
|
||||
} else {
|
||||
uint8_t value = 1 << _tick_event_channel;
|
||||
EVSYS->INTENCLR.reg = EVSYS_INTENSET_EVD(value);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// This is called by sleep, we ignore it when our ticks are enabled because
|
||||
// they'll wake us up earlier. If we don't, we'll mess up ticks by overwriting
|
||||
// the next RTC wake up time.
|
||||
void port_interrupt_after_ticks(uint32_t ticks) {
|
||||
uint32_t current_ticks = _get_count(NULL);
|
||||
if (ticks > 1 << 28) {
|
||||
// We'll interrupt sooner with an overflow.
|
||||
return;
|
||||
}
|
||||
#ifdef SAMD21
|
||||
if (_ticks_enabled) {
|
||||
if (!sleep_ok) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
_port_interrupt_after_ticks(ticks);
|
||||
|
||||
uint32_t target = current_ticks + (ticks << 4);
|
||||
// Try and avoid a bus stall when writing COMP by checking for an obvious
|
||||
// existing sync.
|
||||
while (RTC->MODE0.STATUS.bit.SYNCBUSY == 1) {
|
||||
}
|
||||
// Writing the COMP register can take up to 180us to synchronize. During
|
||||
// this time, the bus will stall and no interrupts will be serviced.
|
||||
RTC->MODE0.COMP[0].reg = target;
|
||||
#ifdef SAM_D5X_E5X
|
||||
while ((RTC->MODE0.SYNCBUSY.reg & (RTC_MODE0_SYNCBUSY_COMP0)) != 0) {
|
||||
}
|
||||
#endif
|
||||
RTC->MODE0.INTFLAG.reg = RTC_MODE0_INTFLAG_CMP0;
|
||||
RTC->MODE0.INTENSET.reg = RTC_MODE0_INTENSET_CMP0;
|
||||
// Set continuous mode again because setting COMP may disable it.
|
||||
rtc_continuous_mode();
|
||||
current_ticks = _get_count(NULL);
|
||||
_woken_up = current_ticks >= target;
|
||||
}
|
||||
|
||||
void port_idle_until_interrupt(void) {
|
||||
@ -571,7 +622,7 @@ void port_idle_until_interrupt(void) {
|
||||
}
|
||||
#endif
|
||||
common_hal_mcu_disable_interrupts();
|
||||
if (!tud_task_event_ready() && !hold_interrupt && !_woken_up) {
|
||||
if (!tud_task_event_ready() && sleep_ok && !_woken_up) {
|
||||
__DSB();
|
||||
__WFI();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user