RTC needed to wait for sync. NeoPixel on SAMD doesn't need disabled
caches. It just needed timing adjustment for 120mhz clock speed.
This commit is contained in:
Scott Shawcroft 2021-05-13 11:04:07 -07:00
parent 1a0b4193b7
commit 5c33c9d597
No known key found for this signature in database
GPG Key ID: 0DFD512649C052DA
5 changed files with 28 additions and 44 deletions

3
main.c
View File

@ -485,11 +485,10 @@ STATIC bool run_code_py(safe_mode_t safe_mode) {
time_to_next_change = MIN(time_to_next_change, time_to_epaper_refresh); time_to_next_change = MIN(time_to_next_change, time_to_epaper_refresh);
} }
#endif #endif
if (time_to_next_change > 0) {
// time_to_next_change is in ms and ticks are slightly shorter so // time_to_next_change is in ms and ticks are slightly shorter so
// we'll undersleep just a little. It shouldn't matter. // we'll undersleep just a little. It shouldn't matter.
port_interrupt_after_ticks(time_to_next_change); port_interrupt_after_ticks(time_to_next_change);
}
#endif #endif
port_idle_until_interrupt(); port_idle_until_interrupt();
} }

View File

@ -12,7 +12,7 @@
// QSPI Data pins // QSPI Data pins
#define MICROPY_PORT_A (PORT_PA08 | PORT_PA09 | PORT_PA10 | PORT_PA11) #define MICROPY_PORT_A (PORT_PA08 | PORT_PA09 | PORT_PA10 | PORT_PA11)
// QSPI CS, QSPI SCK and NeoPixel pin // QSPI CS, QSPI SCK and NeoPixel pin
#define MICROPY_PORT_B (PORT_PB03 | PORT_PB10 | PORT_PB11) #define MICROPY_PORT_B (PORT_PB10 | PORT_PB11)
#define MICROPY_PORT_C (0) #define MICROPY_PORT_C (0)
#define MICROPY_PORT_D (0) #define MICROPY_PORT_D (0)

View File

@ -60,7 +60,7 @@ static void neopixel_send_buffer_core(volatile uint32_t *clraddr, uint32_t pinMa
" movs r6, #3; d2: sub r6, #1; bne d2;" // delay 3 " movs r6, #3; d2: sub r6, #1; bne d2;" // delay 3
#endif #endif
#ifdef SAM_D5X_E5X #ifdef SAM_D5X_E5X
" movs r6, #3; d2: subs r6, #1; bne d2;" // delay 3 " movs r6, #16; d2: subs r6, #1; bne d2;" // delay 3
#endif #endif
" tst r4, r5;" // mask&r5 " tst r4, r5;" // mask&r5
" bne skipclr;" " bne skipclr;"
@ -70,7 +70,7 @@ static void neopixel_send_buffer_core(volatile uint32_t *clraddr, uint32_t pinMa
" movs r6, #6; d0: sub r6, #1; bne d0;" // delay 6 " movs r6, #6; d0: sub r6, #1; bne d0;" // delay 6
#endif #endif
#ifdef SAM_D5X_E5X #ifdef SAM_D5X_E5X
" movs r6, #6; d0: subs r6, #1; bne d0;" // delay 6 " movs r6, #16; d0: subs r6, #1; bne d0;" // delay 6
#endif #endif
" str r1, [r0, #0];" // clr (possibly again, doesn't matter) " str r1, [r0, #0];" // clr (possibly again, doesn't matter)
#ifdef SAMD21 #ifdef SAMD21
@ -85,10 +85,13 @@ static void neopixel_send_buffer_core(volatile uint32_t *clraddr, uint32_t pinMa
" movs r6, #2; d1: sub r6, #1; bne d1;" // delay 2 " movs r6, #2; d1: sub r6, #1; bne d1;" // delay 2
#endif #endif
#ifdef SAM_D5X_E5X #ifdef SAM_D5X_E5X
" movs r6, #2; d1: subs r6, #1; bne d1;" // delay 2 " movs r6, #15; d1: subs r6, #1; bne d1;" // delay 2
#endif #endif
" b loopBit;" " b loopBit;"
"nextbyte:" "nextbyte:"
#ifdef SAM_D5X_E5X
" movs r6, #12; d3: subs r6, #1; bne d3;" // delay 2
#endif
" cmp r2, r3;" " cmp r2, r3;"
" bcs neopixel_stop;" " bcs neopixel_stop;"
" b loopLoad;" " b loopLoad;"
@ -114,42 +117,12 @@ void common_hal_neopixel_write(const digitalio_digitalinout_obj_t *digitalinout,
// Turn off interrupts of any kind during timing-sensitive code. // Turn off interrupts of any kind during timing-sensitive code.
mp_hal_disable_all_interrupts(); mp_hal_disable_all_interrupts();
#ifdef SAM_D5X_E5X
// When this routine is positioned at certain addresses, the timing logic
// below can be too fast by about 2.5x. This is some kind of (un)fortunate code
// positioning with respect to a cache line.
// Theoretically we should turn on off the CMCC caches and the
// NVM caches to ensure consistent timing. Testing shows the the NVMCTRL
// cache disabling seems to make the difference. But turn both off to make sure.
// It's difficult to test because additions to the code before the timing loop
// below change instruction placement. (though this should be less true now that
// the main code is in the cache-aligned function neopixel_send_buffer_core)
// Testing was done by adding cache changes below the loop (so only the
// first time through is wrong).
//
// Turn off instruction, data, and NVM caches to force consistent timing.
// Invalidate existing cache entries.
hri_cmcc_set_CFG_reg(CMCC, CMCC_CFG_DCDIS | CMCC_CFG_ICDIS);
hri_cmcc_write_MAINT0_reg(CMCC, CMCC_MAINT0_INVALL);
hri_nvmctrl_set_CTRLA_CACHEDIS0_bit(NVMCTRL);
hri_nvmctrl_set_CTRLA_CACHEDIS1_bit(NVMCTRL);
#endif
uint32_t pin = digitalinout->pin->number; uint32_t pin = digitalinout->pin->number;
port = &PORT->Group[GPIO_PORT(pin)]; // Convert GPIO # to port register port = &PORT->Group[GPIO_PORT(pin)]; // Convert GPIO # to port register
pinMask = (1UL << (pin % 32)); // From port_pin_set_output_level ASF code. pinMask = (1UL << (pin % 32)); // From port_pin_set_output_level ASF code.
volatile uint32_t *clr = &(port->OUTCLR.reg); volatile uint32_t *clr = &(port->OUTCLR.reg);
neopixel_send_buffer_core(clr, pinMask, pixels, numBytes); neopixel_send_buffer_core(clr, pinMask, pixels, numBytes);
#ifdef SAM_D5X_E5X
// Turn instruction, data, and NVM caches back on.
hri_cmcc_clear_CFG_reg(CMCC, CMCC_CFG_DCDIS | CMCC_CFG_ICDIS);
hri_nvmctrl_clear_CTRLA_CACHEDIS0_bit(NVMCTRL);
hri_nvmctrl_clear_CTRLA_CACHEDIS1_bit(NVMCTRL);
#endif
// Update the next start. // Update the next start.
next_start_raw_ticks = port_get_raw_ticks(NULL) + 4; next_start_raw_ticks = port_get_raw_ticks(NULL) + 4;

View File

@ -462,6 +462,8 @@ static uint32_t _get_count(uint64_t *overflow_count) {
return count; return count;
} }
volatile bool _woken_up;
static void _port_interrupt_after_ticks(uint32_t ticks) { static void _port_interrupt_after_ticks(uint32_t ticks) {
uint32_t current_ticks = _get_count(NULL); uint32_t current_ticks = _get_count(NULL);
if (ticks > 1 << 28) { if (ticks > 1 << 28) {
@ -473,9 +475,16 @@ static void _port_interrupt_after_ticks(uint32_t ticks) {
return; return;
} }
#endif #endif
RTC->MODE0.COMP[0].reg = current_ticks + (ticks << 4); 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.INTFLAG.reg = RTC_MODE0_INTFLAG_CMP0;
RTC->MODE0.INTENSET.reg = RTC_MODE0_INTENSET_CMP0; RTC->MODE0.INTENSET.reg = RTC_MODE0_INTENSET_CMP0;
current_ticks = _get_count(NULL);
_woken_up = current_ticks >= target;
} }
void RTC_Handler(void) { void RTC_Handler(void) {
@ -485,15 +494,18 @@ void RTC_Handler(void) {
// Our RTC is 32 bits and we're clocking it at 16.384khz which is 16 (2 ** 4) subticks per // Our RTC is 32 bits and we're clocking it at 16.384khz which is 16 (2 ** 4) subticks per
// tick. // tick.
overflowed_ticks += (1L << (32 - 4)); overflowed_ticks += (1L << (32 - 4));
}
#ifdef SAM_D5X_E5X #ifdef SAM_D5X_E5X
} else if (intflag & RTC_MODE0_INTFLAG_PER2) { if (intflag & RTC_MODE0_INTFLAG_PER2) {
RTC->MODE0.INTFLAG.reg = RTC_MODE0_INTFLAG_PER2; RTC->MODE0.INTFLAG.reg = RTC_MODE0_INTFLAG_PER2;
// Do things common to all ports when the tick occurs // Do things common to all ports when the tick occurs
supervisor_tick(); supervisor_tick();
}
#endif #endif
} else if (intflag & RTC_MODE0_INTFLAG_CMP0) { 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 and _ticks_enabled
RTC->MODE0.INTFLAG.reg = RTC_MODE0_INTFLAG_CMP0; RTC->MODE0.INTFLAG.reg = RTC_MODE0_INTFLAG_CMP0;
_woken_up = true;
#ifdef SAMD21 #ifdef SAMD21
if (_ticks_enabled) { if (_ticks_enabled) {
// Do things common to all ports when the tick occurs. // Do things common to all ports when the tick occurs.
@ -565,7 +577,7 @@ void port_idle_until_interrupt(void) {
} }
#endif #endif
common_hal_mcu_disable_interrupts(); common_hal_mcu_disable_interrupts();
if (!tud_task_event_ready() && !hold_interrupt) { if (!tud_task_event_ready() && !hold_interrupt && !_woken_up) {
__DSB(); __DSB();
__WFI(); __WFI();
} }

View File

@ -30,7 +30,7 @@
#define MICROPY_HW_BOARD_NAME "Adafruit Feather nRF52840 Express" #define MICROPY_HW_BOARD_NAME "Adafruit Feather nRF52840 Express"
#define MICROPY_HW_MCU_NAME "nRF52840" #define MICROPY_HW_MCU_NAME "nRF52840"
// #define MICROPY_HW_NEOPIXEL (&pin_P0_16) #define MICROPY_HW_NEOPIXEL (&pin_P0_16)
#define MICROPY_HW_LED_STATUS (&pin_P1_15) #define MICROPY_HW_LED_STATUS (&pin_P1_15)