This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

long long in interrupts: how to write safe in main? (disable int or semaphore?)

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.