Add memory barrier, optimal interrupt on-off

Disabling an ISR on ARM has 3 instructions of latency. A Memory barrier is REQUIRED to ensure proper and predictable disabling. Memory barriers are expensive, so avoid disabling if already disabled (See https://mcuoneclipse.com/2015/10/16/nvic-disabling-interrupts-on-arm-cortex-m-and-the-need-for-a-memory-barrier-instruction/)
This commit is contained in:
etagle 2018-05-16 16:38:17 -03:00 committed by Scott Lahteine
parent c2fb2f54a1
commit 0566badcef
11 changed files with 87 additions and 6 deletions

View File

@ -46,6 +46,11 @@ static void TXBegin(void) {
// Disable UART interrupt in NVIC // Disable UART interrupt in NVIC
NVIC_DisableIRQ( UART_IRQn ); NVIC_DisableIRQ( UART_IRQn );
// We NEED memory barriers to ensure Interrupts are actually disabled!
// ( https://dzone.com/articles/nvic-disabling-interrupts-on-arm-cortex-m-and-the )
__DSB();
__ISB();
// Disable clock // Disable clock
pmc_disable_periph_clk( ID_UART ); pmc_disable_periph_clk( ID_UART );

View File

@ -99,6 +99,11 @@ void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency) {
// Disable interrupt, just in case it was already enabled // Disable interrupt, just in case it was already enabled
NVIC_DisableIRQ(irq); NVIC_DisableIRQ(irq);
// We NEED memory barriers to ensure Interrupts are actually disabled!
// ( https://dzone.com/articles/nvic-disabling-interrupts-on-arm-cortex-m-and-the )
__DSB();
__ISB();
// Disable timer interrupt // Disable timer interrupt
tc->TC_CHANNEL[channel].TC_IDR = TC_IDR_CPCS; tc->TC_CHANNEL[channel].TC_IDR = TC_IDR_CPCS;
@ -133,6 +138,11 @@ void HAL_timer_enable_interrupt(const uint8_t timer_num) {
void HAL_timer_disable_interrupt(const uint8_t timer_num) { void HAL_timer_disable_interrupt(const uint8_t timer_num) {
IRQn_Type irq = TimerConfig[timer_num].IRQ_Id; IRQn_Type irq = TimerConfig[timer_num].IRQ_Id;
NVIC_DisableIRQ(irq); NVIC_DisableIRQ(irq);
// We NEED memory barriers to ensure Interrupts are actually disabled!
// ( https://dzone.com/articles/nvic-disabling-interrupts-on-arm-cortex-m-and-the )
__DSB();
__ISB();
} }
// missing from CMSIS: Check if interrupt is enabled or not // missing from CMSIS: Check if interrupt is enabled or not

View File

@ -245,6 +245,11 @@
// Disable UART interrupt in NVIC // Disable UART interrupt in NVIC
NVIC_DisableIRQ( HWUART_IRQ ); NVIC_DisableIRQ( HWUART_IRQ );
// We NEED memory barriers to ensure Interrupts are actually disabled!
// ( https://dzone.com/articles/nvic-disabling-interrupts-on-arm-cortex-m-and-the )
__DSB();
__ISB();
// Disable clock // Disable clock
pmc_disable_periph_clk( HWUART_IRQ_ID ); pmc_disable_periph_clk( HWUART_IRQ_ID );
@ -290,6 +295,11 @@
// Disable UART interrupt in NVIC // Disable UART interrupt in NVIC
NVIC_DisableIRQ( HWUART_IRQ ); NVIC_DisableIRQ( HWUART_IRQ );
// We NEED memory barriers to ensure Interrupts are actually disabled!
// ( https://dzone.com/articles/nvic-disabling-interrupts-on-arm-cortex-m-and-the )
__DSB();
__ISB();
pmc_disable_periph_clk( HWUART_IRQ_ID ); pmc_disable_periph_clk( HWUART_IRQ_ID );
} }

View File

@ -68,6 +68,11 @@ void watchdogSetup(void) {
// Disable WDT interrupt (just in case, to avoid triggering it!) // Disable WDT interrupt (just in case, to avoid triggering it!)
NVIC_DisableIRQ(WDT_IRQn); NVIC_DisableIRQ(WDT_IRQn);
// We NEED memory barriers to ensure Interrupts are actually disabled!
// ( https://dzone.com/articles/nvic-disabling-interrupts-on-arm-cortex-m-and-the )
__DSB();
__ISB();
// Initialize WDT with the given parameters // Initialize WDT with the given parameters
WDT_Enable(WDT, value); WDT_Enable(WDT, value);

View File

@ -143,6 +143,11 @@ FORCE_INLINE static void HAL_timer_disable_interrupt(const uint8_t timer_num) {
case 0: NVIC_DisableIRQ(TIMER0_IRQn); // Disable interrupt handler case 0: NVIC_DisableIRQ(TIMER0_IRQn); // Disable interrupt handler
case 1: NVIC_DisableIRQ(TIMER1_IRQn); // Disable interrupt handler case 1: NVIC_DisableIRQ(TIMER1_IRQn); // Disable interrupt handler
} }
// We NEED memory barriers to ensure Interrupts are actually disabled!
// ( https://dzone.com/articles/nvic-disabling-interrupts-on-arm-cortex-m-and-the )
__DSB();
__ISB();
} }
// This function is missing from CMSIS // This function is missing from CMSIS

View File

@ -258,6 +258,11 @@ bool LPC1768_PWM_attach_pin(pin_t pin, uint32_t min /* = 1 */, uint32_t max /* =
// OK to update the active table because the // OK to update the active table because the
// ISR doesn't use any of the changed items // ISR doesn't use any of the changed items
// We NEED memory barriers to ensure Interrupts are actually disabled!
// ( https://dzone.com/articles/nvic-disabling-interrupts-on-arm-cortex-m-and-the )
__DSB();
__ISB();
if (ISR_table_update) //use work table if that's the newest if (ISR_table_update) //use work table if that's the newest
temp_table = work_table; temp_table = work_table;
else else
@ -342,6 +347,11 @@ bool LPC1768_PWM_detach_pin(pin_t pin) {
//// interrupt controlled PWM code //// interrupt controlled PWM code
NVIC_DisableIRQ(HAL_PWM_TIMER_IRQn); NVIC_DisableIRQ(HAL_PWM_TIMER_IRQn);
// We NEED memory barriers to ensure Interrupts are actually disabled!
// ( https://dzone.com/articles/nvic-disabling-interrupts-on-arm-cortex-m-and-the )
__DSB();
__ISB();
if (ISR_table_update) { if (ISR_table_update) {
ISR_table_update = false; // don't update yet - have another update to do ISR_table_update = false; // don't update yet - have another update to do
NVIC_EnableIRQ(HAL_PWM_TIMER_IRQn); // re-enable PWM interrupts NVIC_EnableIRQ(HAL_PWM_TIMER_IRQn); // re-enable PWM interrupts
@ -428,6 +438,12 @@ bool LPC1768_PWM_write(pin_t pin, uint32_t value) {
//// interrupt controlled PWM code //// interrupt controlled PWM code
NVIC_DisableIRQ(HAL_PWM_TIMER_IRQn); NVIC_DisableIRQ(HAL_PWM_TIMER_IRQn);
// We NEED memory barriers to ensure Interrupts are actually disabled!
// ( https://dzone.com/articles/nvic-disabling-interrupts-on-arm-cortex-m-and-the )
__DSB();
__ISB();
if (!ISR_table_update) // use the most up to date table if (!ISR_table_update) // use the most up to date table
COPY_ACTIVE_TABLE; // copy active table into work table COPY_ACTIVE_TABLE; // copy active table into work table
@ -456,6 +472,11 @@ bool useable_hardware_PWM(pin_t pin) {
NVIC_DisableIRQ(HAL_PWM_TIMER_IRQn); NVIC_DisableIRQ(HAL_PWM_TIMER_IRQn);
// We NEED memory barriers to ensure Interrupts are actually disabled!
// ( https://dzone.com/articles/nvic-disabling-interrupts-on-arm-cortex-m-and-the )
__DSB();
__ISB();
bool return_flag = false; bool return_flag = false;
for (uint8_t i = 0; i < NUM_ISR_PWMS; i++) // see if it's already setup for (uint8_t i = 0; i < NUM_ISR_PWMS; i++) // see if it's already setup
if (active_table[i].pin == pin) return_flag = true; if (active_table[i].pin == pin) return_flag = true;

View File

@ -123,6 +123,11 @@ void HAL_timer_enable_interrupt(const uint8_t timer_num) {
void HAL_timer_disable_interrupt(const uint8_t timer_num) { void HAL_timer_disable_interrupt(const uint8_t timer_num) {
HAL_NVIC_DisableIRQ(timerConfig[timer_num].IRQ_Id); HAL_NVIC_DisableIRQ(timerConfig[timer_num].IRQ_Id);
// We NEED memory barriers to ensure Interrupts are actually disabled!
// ( https://dzone.com/articles/nvic-disabling-interrupts-on-arm-cortex-m-and-the )
__DSB();
__ISB();
} }
hal_timer_t HAL_timer_get_compare(const uint8_t timer_num) { hal_timer_t HAL_timer_get_compare(const uint8_t timer_num) {

View File

@ -127,6 +127,11 @@ void HAL_timer_enable_interrupt(const uint8_t timer_num) {
void HAL_timer_disable_interrupt(const uint8_t timer_num) { void HAL_timer_disable_interrupt(const uint8_t timer_num) {
HAL_NVIC_DisableIRQ(timerConfig[timer_num].IRQ_Id); HAL_NVIC_DisableIRQ(timerConfig[timer_num].IRQ_Id);
// We NEED memory barriers to ensure Interrupts are actually disabled!
// ( https://dzone.com/articles/nvic-disabling-interrupts-on-arm-cortex-m-and-the )
__DSB();
__ISB();
} }
hal_timer_t HAL_timer_get_compare(const uint8_t timer_num) { hal_timer_t HAL_timer_get_compare(const uint8_t timer_num) {

View File

@ -29,6 +29,22 @@
#include "HAL.h" #include "HAL.h"
#include "HAL_timers_Teensy.h" #include "HAL_timers_Teensy.h"
/** \brief Instruction Synchronization Barrier
Instruction Synchronization Barrier flushes the pipeline in the processor,
so that all instructions following the ISB are fetched from cache or
memory, after the instruction has been completed.
*/
FORCE_INLINE static void __ISB(void) {
__asm__ __volatile__("isb 0xF":::"memory");
}
/** \brief Data Synchronization Barrier
This function acts as a special kind of Data Memory Barrier.
It completes when all explicit memory accesses before this instruction complete.
*/
FORCE_INLINE static void __DSB(void) {
__asm__ __volatile__("dsb 0xF":::"memory");
}
void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency) { void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency) {
switch (timer_num) { switch (timer_num) {
@ -65,6 +81,11 @@ void HAL_timer_disable_interrupt(const uint8_t timer_num) {
case 0: NVIC_DISABLE_IRQ(IRQ_FTM0); break; case 0: NVIC_DISABLE_IRQ(IRQ_FTM0); break;
case 1: NVIC_DISABLE_IRQ(IRQ_FTM1); break; case 1: NVIC_DISABLE_IRQ(IRQ_FTM1); break;
} }
// We NEED memory barriers to ensure Interrupts are actually disabled!
// ( https://dzone.com/articles/nvic-disabling-interrupts-on-arm-cortex-m-and-the )
__DSB();
__ISB();
} }
bool HAL_timer_interrupt_enabled(const uint8_t timer_num) { bool HAL_timer_interrupt_enabled(const uint8_t timer_num) {

View File

@ -73,7 +73,6 @@ static uint8_t LEDs[8] = { 0 };
#endif #endif
void Max7219_PutByte(uint8_t data) { void Max7219_PutByte(uint8_t data) {
CRITICAL_SECTION_START;
for (uint8_t i = 8; i--;) { for (uint8_t i = 8; i--;) {
SIG_DELAY(); SIG_DELAY();
WRITE(MAX7219_CLK_PIN, LOW); // tick WRITE(MAX7219_CLK_PIN, LOW); // tick
@ -84,12 +83,10 @@ void Max7219_PutByte(uint8_t data) {
SIG_DELAY(); SIG_DELAY();
data <<= 1; data <<= 1;
} }
CRITICAL_SECTION_END;
} }
void Max7219(const uint8_t reg, const uint8_t data) { void Max7219(const uint8_t reg, const uint8_t data) {
SIG_DELAY(); SIG_DELAY();
CRITICAL_SECTION_START;
WRITE(MAX7219_LOAD_PIN, LOW); // begin WRITE(MAX7219_LOAD_PIN, LOW); // begin
SIG_DELAY(); SIG_DELAY();
Max7219_PutByte(reg); // specify register Max7219_PutByte(reg); // specify register
@ -99,7 +96,6 @@ void Max7219(const uint8_t reg, const uint8_t data) {
WRITE(MAX7219_LOAD_PIN, LOW); // and tell the chip to load the data WRITE(MAX7219_LOAD_PIN, LOW); // and tell the chip to load the data
SIG_DELAY(); SIG_DELAY();
WRITE(MAX7219_LOAD_PIN, HIGH); WRITE(MAX7219_LOAD_PIN, HIGH);
CRITICAL_SECTION_END;
SIG_DELAY(); SIG_DELAY();
} }

View File

@ -1085,9 +1085,7 @@ void Temperature::updateTemperaturesFromRawValues() {
watchdog_reset(); watchdog_reset();
#endif #endif
CRITICAL_SECTION_START;
temp_meas_ready = false; temp_meas_ready = false;
CRITICAL_SECTION_END;
} }