How is it that “CPSID” (used in example programs to control interrupts) not show up in the processor's register set (maybe it’s under a different name?) nor is IAR able to find a definition for it (so backwards engineering is not an option).
Specifically, I want to know if executing:
__asm("CPSID i"); |
// ...some code which needs to be protected from interrupts...
__asm("CPSIE i"); |
...inside a routine will (possibly) allow the original type of interrupt to (prematurely) interrupt the code again before the code is finished servicing the first interrupt.
---
Added later...
So, the explanations for CPS ID & IE are not in the freescale / NXP chip specifications but the ARM specifications. Also, these are some sort of special registers (PRIMASK and FAULTMASK) that are not in ordinary memory space? Going on, they are 32 bit registers all of which, with the exception of the least significant bit (LSB) are reserved!
So, my question now boils down to this:
I would like to protect my code by setting the PRIMASK LSB (i.e. by using: __asm("CPSID i");) inhibiting all priority-adjustable-interrupts. When I later clear the PRIMASK LSB (i.e. by using: __asm("CPSIE i");) enabling all priority-adjustable-interrupts, do I in fact alter any other interrupt settings or states? For example, am I allowing the current interrupt to become active again starting an endless sequence of calls into the interrupt code I am now in? Or, is setting and clearing the LSB in the PRIMASK more like putting in and taking out a 2nd dam on a river (i.e. the 1st dam remains intact and its function does not change throughout the construction and destruction of the 2nd dam).
-thanks
Your statement 'am I allowing the current interrupt...' bothers me. As a rule, one should NOT be altering PRIMASK within an interrupt-service routine -- ONLY fiddle with interrupt controls within 'main code' (to protect atomic operations): NOT in interrupt context -- and then, sure, any 'pending' interrupts that got set while PRIMASK has the 'configurable' interrupts disabled will then be evaluated at enable-time for the 'highest priority' (highest IPR ARM-priority level, thence within that group the lowest vector #) to branch to it IF the ARM-priority is higher than the current context.
Within an interrupt routine, all interrupts 'at that same or lower' IPR ARM-priority level are 'naturally' masked off, preventing further interrupts until the 'context restore' that comes as part of the return from that function (ignoring, for the moment, tail-chaining which actually 'skips' the return part of such context restore to save unstack/re-stack due to successive interrupts). If you have 'some' interrupts set to a lower IPR#, THEY can (AND SHOULD be able to) interrupt an interrupt handler, and as Mr. Paddock mentioned it is up to the designer to insure the stack-space and 'potential side effects (NEVER access the same ANYTHINGS)' are well in hand.
But to answer your 'basic question' NO, PRIMASK is NOT related to the process that prevents re-interrupt within an ongoing interrupt-level. It is simply a 'full global enable/disable' for 'new' interrupts. IF all your interrupts are at the SAME IPR ARM priority level, then 'fiddling' with that in interrupt context will have NO effect. There is a separate 'current interrupt level' register that waits for a 'higher priority' (lower ARM #) to exist to create a new interrupt (which includes, by the way, 'fault' interrupts...which have a 'negative' priority #).
I don't know what you are trying to do, but I think you are working at it too hard! For your basic 'single interrupt level' user, the hardware protections are all you need, plus the 'atomic' global enable/disable (as from Mr. Paddock's help) to protect the 'atomic' sequences (like shared-buffer pointer updates) in your main-level code.
Just to clarify, the PRIMASK 'bit' is NOT AT ALL like the '08's I interrupt-control bit. PRIMASK is NOT altered by the entry or exit of an ISR, and is NOT the 'basic hardware mechanism' that re-enables interrupts -- too many 'levels' for a single-bit! PRIMASK is entirely at firmware control.
Instructions such as IRQ Enable/Disable need to be marked as volatile to prevent obscure 'code motion' issues from the compiler optimizer (turning it off is not the correct solution) moving such instructions to useless locations.
Below is what I use.
Also attached is atomic.h which will show how to save and restore state to answer some of your other questions.
Use it like this:
ATOMIC_BLOCK( ATOMIC_RESTORESTATE )
{
// stuff that needs done with IRQs off
}
State of IRQ restored to on or off as it was before ATOMIC_BLOCK as entered
There are other variations documented in the file.
Think long and hard around recursive interrupts or nested priorities before you use them as well as the needed stack space.
This is for the GCC compiler:
#define ATTR_NO_INSTRUMENT_FUNCTION __attribute__( ( no_instrument_function ) ) /* Really needed for these inlined single instructions */
static inline ATTR_NO_INSTRUMENT_FUNCTION void irq_enable( void )
{
__asm__ __volatile__ ("cpsie i"); /* Clear PRIMASK */
}
/*
* The CPSID instruction is self-synchronized to the instruction
* stream and there is no requirement to insert memory barrier
* instructions after CPSID.
*/
static inline ATTR_NO_INSTRUMENT_FUNCTION void irq_disable( void )
{
__asm__ __volatile__ ("cpsid i");
}