atmel-samd: Fix neopixel timing by forcing the NVM cache to be deterministicly timed. Otherwise, bit order changes code timing. Fixes #74

This commit is contained in:
Scott Shawcroft 2017-01-17 19:27:48 -08:00
parent 42926c6159
commit 1a86968912

View File

@ -40,6 +40,9 @@ void common_hal_neopixel_write(const nativeio_digitalinout_obj_t* digitalinout,
// Turn off interrupts of any kind during timing-sensitive code.
mp_hal_disable_all_interrupts();
// Make sure the NVM cache is consistently timed.
NVMCTRL->CTRLB.bit.READMODE = NVMCTRL_CTRLB_READMODE_DETERMINISTIC_Val;
uint32_t pin = digitalinout->pin->pin;
port = port_get_group_from_gpio_pin(pin);
pinMask = (1UL << (pin % 32)); // From port_pin_set_output_level ASF code.
@ -56,17 +59,14 @@ void common_hal_neopixel_write(const nativeio_digitalinout_obj_t* digitalinout,
*set = pinMask;
asm("nop; nop;");
if(p & bitMask) {
asm("nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop;");
asm("nop; nop; nop; nop; nop; nop; nop;");
*clr = pinMask;
} else {
*clr = pinMask;
asm("nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop;");
asm("nop; nop;");
}
if((bitMask >>= 1) != 0) {
asm("nop; nop; nop; nop; nop; nop; nop; nop; nop;");
asm("nop; nop; nop; nop; nop;");
} else {
if(ptr >= end) break;
p = *ptr++;
@ -76,12 +76,10 @@ void common_hal_neopixel_write(const nativeio_digitalinout_obj_t* digitalinout,
} else { // 400 KHz bitstream
for(;;) {
*set = pinMask;
// 11 cycles high regardless
asm("nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop;");
asm("nop; nop; nop; nop; nop; nop; nop;");
if(p & bitMask) {
// 27 cycles high
asm("nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop;");
*clr = pinMask;
@ -106,6 +104,9 @@ void common_hal_neopixel_write(const nativeio_digitalinout_obj_t* digitalinout,
}
}
// Speed up! (But inconsistent timing.)
NVMCTRL->CTRLB.bit.READMODE = NVMCTRL_CTRLB_READMODE_NO_MISS_PENALTY_Val;
// Turn on interrupts after timing-sensitive code.
mp_hal_enable_all_interrupts();