Nesting interrupt locks (a.k.a. mutexes in single threaded applications)

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Nesting interrupt locks (a.k.a. mutexes in single threaded applications)

741 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by OldManVimes on Mon Apr 13 11:27:00 MST 2015
Hi all,

I have code that disables normal interrupts in places and then calls other code (while interrupts are disabled) that can do the same. That deeper code must then not change the interrupt enable/disable status.

So the pattern is:
void deepCode(void)
{
  uint32_t lockState = irqLock(); // When called from higherLevelCode() this code disables interrupts while already disabled.
  // Do stuff
  irqUnlock(lockState); // When called from higherLevelCode() this code leaves interrupts disabled.
}

void higherLevelCode(void)
{
  uint32_t lockState = irqLock();
  deepCode();
  irqUnlock(lockState);
}


Anyway, there is a method that allows this to work and my question to the hive mind is whether the implementation I'm using is 100% correct or not. So here goes:

// From the CMSIS header files (for an M0 or M3)
__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_PRIMASK(void)
{
  uint32_t result;

  __ASM volatile ("MRS %0, primask" : "=r" (result) );
  return(result);
}

__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_PRIMASK(uint32_t priMask)
{
  __ASM volatile ("MSR primask, %0" : : "r" (priMask) : "memory");
}

__attribute__( ( always_inline ) ) __STATIC_INLINE void __disable_irq(void)
{
  __ASM volatile ("cpsid i" : : : "memory");
}


And now the nested locking and unlocking code:

__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t irqLock(void)
{
uint32_t lockState = __get_PRIMASK();
__disable_irq();
return (lockState);
}

__attribute__( ( always_inline ) ) __STATIC_INLINE void irqUnlock(uint32_t lockState)
{
__set_PRIMASK(lockState);
}


The assembly code that is generated is exactly as you would expect. Example:

// Lock
   xxxda:f3ef 8910 mrsr9, PRIMASK // Get the status of interrupts (enabled or disabled)
   xxxde:b672      cpsidi // Disable regular interrupts

// Unlock
   xxxyy:f389 8810 msrPRIMASK, r9 // Restore interrupt handling back to the state it had when we read the PRIMASK


I am fairly certain there is no way a single core M0 to M3 can fail when using this code correctly and I have no reason to doubt the correctness of the code. Still it would be nice to get some confirmation.

Thanks in advance,
Vimes

0 Kudos
Reply
0 Replies