We are running a survey to help us improve the experience for all of our members. If you see the survey appear, please take the time to tell us about your experience if you can.
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.