Hi, please have a look at the following code (STM32F407):
volatile long long vllPosition; void TIM1_UP_TIM10_IRQHandler(void){ ... do interrupt stuff ... vllPosition ++; ... do interrupt stuff ... } int main( void){ ... do some init stuff ... for(;;){ ... do the loop stuff ... if( ...){ // from time to time it is necessary to assing some value to llPosition vllPosition= llSomeValue; } ... do the loop stuff ... } }
As long long is 64 bit, the access to vllPosition is non-atomic. So an assignment like vllPosition= llSomeValue is dangerous: it might be interrupted by my interrupt and then the complete thing will mix up.
In an easy access, I would like to disable the interrupt temporarily. There would be 3 different possibilities I see: 1.:
NVIC_DisableIRQ( TIM1_UP_TIM10_IRQn); vllPosition= llSomeValue; NVIC_EnableIRQ( TIM1_UP_TIM10_IRQn);
2. (The TIM1_UP_TIM10_IRQ is entered exclusively by the UIE):
TIM1->DIER = 0; vllPosition= llSomeValue; TIM1->DIER = TIM_DIER_UIE;
3.
__disable_irq(); vllPosition= llSomeValue; __enable_irq();
Is any of these methods 100% safe (of course I would prefer method 1 or 2, as 3 disables all interrupts)? (Due to the prefetch delay I am not sure about this - I did not find a guarantee anywhere and it is difficult to decide only by testing). (Or would I need some more elaborate methode to ensure this in a secure way, e. g. with semaphores or so?)
I found it meanwhile in the ARM docs:
The Disable in (1.) needs an additional ISB and DSB instruction, see infocenter.arm.com/.../index.jsp ("4.6 Disabling Interrupts using NVIC")
The Disable in (2.) is NOT recommended, see infocenter.arm.com/.../index.jsp ("4.9 Disabling interrupts at peripherals")
The Disable in (3.) works without ISB and DSB - but of course it stops all interrupts: siehe: infocenter.arm.com/.../index.jsp ("4.8. Disabling interrupts using CPS and MSR instructions")
Or would I need some more elaborate methode to ensure this in a secure way, e. g. with semaphores or so?
That's the path I'd choose. You can do it in a portable way, no need to figure out how to disable interrupts.
volatile long long vllPosition; bool update; long long new_value; void TIM1_UP_TIM10_IRQHandler(void){ vllPosition = update ? new_value : (vllPosition + 1); } int main( void){ new_value = llSomeValue; update = true; }
By the way, reading this variable isn't straightforward either. Here is how I like to do it:
long long read_var(void) { long long v[2]; v[0] = vllPosition; do { v[1] = v[0]; v[0] = vllPosition; } while (v[0] != v[1]); return v[0]; }
Hi Mike, thanks for your input.
Defining a "shadow variable" for each interrupt-used int64 is an interesting hint, but I have quite a bit of such int64's, I have a very long (Motor-control) interrupt, and I am changing them at quite many points in the interrupt. In fact I am changing these int64's much more often in the interrupt than in the main program - so in this case I prefer a solution which is straight forward in the interrupt programming and cautious in the main program.
For reading of such int64 in the main program, I use the following macro:
#define REF_LOLONG(x) (*( ((long*)(void*)&(x)) )) #define REF_HILONG(x) (*( ((long*)(void*)&(x))+1 )) #define LLONG_LoadVolatileTo( llV, ll) do{ \ REF_LOLONG(ll)= *(volatile long*)&(llV); \ REF_HILONG(ll)= *(volatile long*)((long*)&(llV)+1); \ }while( REF_LOLONG(ll) != *(volatile long*)&(llV));
This works nicely.