Backup the Interrupt Enable State and Restore it on the T4.x

Status
Not open for further replies.

mjs513

Senior Member+
On the Using Interrupts you can use the following code snippet to save and restore the interrupt state, for the T3.x's:
Code:
unsigned char sreg_backup;

sreg_backup = SREG;   /* save interrupt enable/disable state */
cli();
overflow_count = 0;
SREG = sreg_backup    /* restore interrupt state */

However, on the T4.x's SREG is not available. So question is how would you do this for the T4.x's.

Yes I know I can wrap it in noInterrputs()/interrupts() but if this might interfere with other things.
 
Since some library code obnoxiously turns on interrupts that should be left off, it's important that there is a standard, portable, well documented way to do this.

From private functions in EventResponder.h:

Code:
bool disableInterrupts() {
                uint32_t primask;
                __asm__ volatile("mrs %0, primask\n" : "=r" (primask)::);
                __disable_irq();
                return (primask == 0) ? true : false;
}

void enableInterrupts(bool doit) {
                if (doit) __enable_irq();
}


So to answer the question:

Code:
bool interruptState = disableInterrupts();

// critical code here

enableInterrupts(interruptState);
 
Finer resolution interrupt enable/disable

One API that I have wished to implement is to change from "disableInterrupts / enableInterrupts" to "curLevel = setPriority(newLeveli)"; one option for "newPri" should be "such a high priority that interrupts are effectively disabled". This API intrinsically and naturally allows backup/restore of the original priority level. I have used RTOS and other systems (and built a few little ones) that use this technique. It's a bit like reserving one priority level to serve as an effective nonmaskable interrupt.

In a recent thread a developer wanted to emulate an EPROM (DMA/eDMA Guidance), using an interrupt service function to perform the address->data lookup, output drive enable, etc. The results were mostly positive, getting response time down to about 80 ns on a T4.1. There was a problem with extended response times on rare occasions, likely related to USB transactions.

If it were possible to arrange interrupt priorities such that one essentially "private" subsystem could always be enabled while preventing others, then the EPROM emulation would be able to achieve better responsiveness.
 
Last edited:
A good idea and easy to write with "basepri".

Seems like some coding standards are needed. Perhaps:

a) don't enable interrupts if they were off
b) don't disable interrupts when a mutex would work
c) don't disable more interrupts than needed
 
Last edited:
What's the best way to do a mutex? About like this (but it's missing a type)?

Code:
#include <stdatomic.h>

volatile atomic_bool lock;

if (atomic_flag_test_and_set(&lock)) 
   return;   // was already running

// critical code

atomic_flag_clear(&lock);

or this (which does compile)?

Code:
static volatile char lock;

if (__atomic_test_and_set(&lock,__ATOMIC_RELAXED))
   return;    // fail, was already locked

// critical code

__atomic_clear(&lock,__ATOMIC_RELAXED);
 
Last edited:
Since some library code obnoxiously turns on interrupts that should be left off, it's important that there is a standard, portable, well documented way to do this.

From private functions in EventResponder.h:

Code:
bool disableInterrupts() {
                uint32_t primask;
                __asm__ volatile("mrs %0, primask\n" : "=r" (primask)::);
                __disable_irq();
                return (primask == 0) ? true : false;
}

void enableInterrupts(bool doit) {
                if (doit) __enable_irq();
}


So to answer the question:

Code:
bool interruptState = disableInterrupts();

// critical code here

enableInterrupts(interruptState);

Wow - the question is fairly old now. Did find the same solution as you posted and implemented. Works like a charm. Think I found it in EventResponder as well. Thanks for posting.

You also mentioned about implmenting mutex locks - you might want to check out how TeensyThreads implemented it as well.
 
As we know from the northeast blackout of 2003, data race conditions can be serious and hard to spot. So I propose an additional coding standard:

If your code has static/global variables, might be called from other threads or ISRs and you aren't sure it's re-entrant and thread safe, put a mutex lock around it.
 
My understanding is that all normally used interrupts have a priority > 16. __disable_irq()/__enable_irq() blocks are used in many places and this is arguably overkill. So what if the below was done to decrease latency when it is really critical? The idea is that instead of disabling all interrupts, disable all normal interrupts - and let the highest priority ones happen immediately.

Caution - this code (in imxrt.h) may not be correct. But the idea is there.

Code:
#if 0
#define __disable_irq() __asm__ volatile("CPSID i":::"memory");
#define __enable_irq()  __asm__ volatile("CPSIE i":::"memory");
#else
#define __disable_irq() __asm__ volatile ("MSR basepri, %0" : : "r" (16) : "memory");
#define __enable_irq()  __asm__ volatile ("MSR basepri, %0" : : "r" (0): "memory");
#endif
 
Status
Not open for further replies.
Back
Top