Temporarily disable interrupts on ARM
Asked Answered
C

2

8

I am starting working with the ARM platform (specifically the TI TMS570 family).

I have some code with critical regions where I don't want an exception to occur. So I want to save the IRQ and FIR enabled flags on entering the regions and restore them on exiting.

How do I do that?

Calais answered 13/10, 2016 at 11:38 Comment(0)
D
14

To temporarily mask IRQs and FIQs at the CPU, the nicest option for ARMv7 is to use cps:

// assembly code assuming interrupts unmasked on entry

cpsid if  // mask IRQ and FIQ
...       // do critical stuff
cpsie if  // unmask

Some compilers provide a set of __disable_irq() etc. intrinsics usable from C code, but for others (like GCC) it's going to be a case of dropping to assembly.

If you want critical sections to be nested, reentrant, taken in interrupt handlers or anything else which requires restoring the previous state as opposed to just uncondionally unmasking at the end, then you'll need to copy that state out of the CPSR before masking anything, then restore it on exit. At that point the unmasking probably ends up simpler to handle the old-fashioned way of a direct read-modify-write of the CPSR. Here's one idea off the top of my head:

// int enter_critical_section(void);
enter_critical_section:
mrs r0, cpsr
cpsid if
and r0, r0, #0xc0  // leave just the I and F flags
bx lr

// void leave_critical_section(int flags);
leave_critical_section:
mrs r1, cpsr
bic r1, r1, r0
msr cpsr_c, r1
bx lr
Devious answered 13/10, 2016 at 12:59 Comment(4)
It is worth noting on Cortex-R there are various different PSR registers depending on what mode you are in. You need to ensure that you are using the correct one! See the Cortex-R Technical Reference Manuals,Aleman
@RealtimeRik Various? Sure, APSR is an alias for "the parts of CPSR accessible from user mode", but it's still the exact same register. You're not thinking of M-profile, are you (which does have 3 separate PSRs, plus aliases for all possible combinations of them)?Devious
No, I am thinking Cortex-R. If you are doing all this in user mode then the CPSR remains the same. However your are accessing it in an interrupt or in supervisor mode for example then it would be different.Aleman
@RealtimeRik It's not like disabling interrupts is relevant to user mode anyway. The CPSR is the current program status - it's not "different" between modes other than the value in the M field, which is the thing which determines which mode you're in in the first place!Devious
M
0

You can use _disable_interrupt_();_enable_interrupt_(); from Halcogen generated code (sys_core.h)

Moises answered 26/10, 2016 at 18:9 Comment(5)
Thank you for your answer. In general, you are right, but what if the interrupts were already disabled due to whatever reason? Then I'd enable them at the end when this might not be what was really wanted. So saving the CPSR and restoring the relevant parts as suggested by Notlikethat seems to be crucial.Calais
I post only one string. In real I do nesting level counting. Also If you are inside interrupt you don't need disable interrupts.Moises
That depends on if I know that I am inside an interrupt. Suppose I have something like a queue framework or whatever and I need to make sure that a change happens atomically. Then I need to unconditionally disable interrupts at the start and re-establish the situation as it was before, because the library function doesn't know if it is called from an interrupt or not.Calais
#define MODE_BITS 0x1F #define FIQ_MODE 0x11 #define IRQ_MODE 0x12 static INLINE bool isFromInterrupt (void) { register uint32_t cpsr = getCPSRValue (); register uint32_t mode = cpsr & MODE_BITS; return (mode == FIQ_MODE) || (mode == IRQ_MODE); }Moises
This seems to be much more complicated than the other solution where I just store the CPSR and restore it partially.Calais

© 2022 - 2024 — McMap. All rights reserved.