Allocation of software triggered interrupts

I'll write a dynamic IRQ library for testing this weekend. What I want at this point is just a list of IRQs that will be considered reserved for the SWIs.
.
Can you explain this planned lib in more detail ? What is it supposed to do ?
 
I'm confident ARM has implemented nested interrupts correctly with the NVIC. It's entirely in hardware.
Yes, if nested means one interrupt cause interrupting a lower priority interrupt cause - in hardware. I was referring to where any one ISR reads some I/O status to clear the interrupt request for that level, then reenables interrupts at that same level. This can lead to reentry risks.
 
Paul, did you notice my post #32 ?

Yes, most excellent. :)


... where any one ISR reads some I/O status to clear the interrupt request for that level, then reenables interrupts at that same level. This can lead to reentry risks.

ARM designed the NVIC so that absolutely can not happen. It tracks which interrupts are active, as well as the current priority level. Until the interrupt returns, the same interrupt simply can not be serviced again (reentrant). This is rigidly enforced by the NVIC in hardware. It can not be disabled or overridden.

The only way to cheat appears to be rewriting the data on the stack, causing the return from the interrupt to branch to somewhere else in memory. Even then, you are returning from the interrupt, to reenable it. That's absolutely the only way to allow the interrupt to be serviced again. It's all implemented in hardware.
 
95 doesn't work here on the 3.1 either (yes, I changed the NVIC_NUM_INTERRUPTS...)
I did a modified variant for the 3.0...
Reserved Interrupt tester
21 (5)...OK.
61 (45)...OK.
62 (46)...NOT OK.
Done.
This means that the NVIC does indeed adhere to the actual counts of ISR bits.
Teensy 3.0 has only two.
21(5) and 61(45)
 
Paul mentioned that IRQ_UART0_LON is unused too.

Here is the revised sketch. It will work for both the 3.0 and 3.1.

Code:
#define NVIC_STIR (*(volatile uint32_t *)0xE000EF00)
#define NVIC_TRIGGER_INTERRUPT(x) NVIC_STIR=(x);

volatile int chk;

void softISR() {
        chk = 1;
        digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
        Serial.println("...OK.");
}

const unsigned char swInterrupts[] = {
#if defined(__MK20DX128__)
5,
#elif defined(__MK20DX256__)
17,23,28,37,38,39,40,41,42,43,51,52,53,54,55,56,75,76,77,78,79,80,82,86,92,93,
#endif
IRQ_UART0_LON,
IRQ_SOFTWARE,
0};

void setup() {
        while(!Serial);
        pinMode(LED_BUILTIN, OUTPUT);
        Serial.begin(115200);
}

void loop() {    
        Serial.println("Reserved Interrupt tester");
        unsigned char n = 0;  
        while (swInterrupts[n] > 0) {    
                int interrupt = swInterrupts[n];
                Serial.printf("%d (%d)",interrupt + 16, interrupt);
                _VectorsRam[interrupt+16] = softISR;    
                NVIC_ENABLE_IRQ(interrupt);                        
                chk = 0;
                //Trigger swi this way :    
                //NVIC_SET_PENDING(interrupt);
                //Or this way (faster):
                NVIC_TRIGGER_INTERRUPT(interrupt);
                delay(50);
                if (!chk) Serial.println("...NOT OK.");    
                n++;
        }
        Serial.println("Done.");
        while(1){;}
}

3.0 output:
Code:
Reserved Interrupt tester
21 (5)...OK.
31 (15)...OK.
61 (45)...OK.
Done.

3.1 output:
Code:
Reserved Interrupt tester
33 (17)...OK.
39 (23)...OK.
44 (28)...OK.
53 (37)...OK.
54 (38)...OK.
55 (39)...OK.
56 (40)...OK.
57 (41)...OK.
58 (42)...OK.
59 (43)...OK.
67 (51)...OK.
68 (52)...OK.
69 (53)...OK.
70 (54)...OK.
71 (55)...OK.
72 (56)...OK.
91 (75)...OK.
92 (76)...OK.
93 (77)...OK.
94 (78)...OK.
95 (79)...OK.
96 (80)...OK.
98 (82)...OK.
102 (86)...OK.
108 (92)...OK.
109 (93)...OK.
60 (44)...OK.
110 (94)...OK.
Done.
 
@Paul
I have been thinking about the entire 'running out of slots' with dynamically allocated...
I have an idea to solve that. I could use just one SWI, and use a FIFO queue :)
 
Ok, here's a simple fixed allocation, to serve as a temporary measure until a nice dynamic system is made.

Code:
        Teensy 3.0    Teensy 3.1
        ----------    ----------
Audio       45            94
MP3                       56
UHS          5            17

If more allocations are needed, I'll just add to this list.

I've edited the first post, which will serve as the "official" list if more allocations are needed before we have a dynamic system.
 
This is the basic API I'm imagining for dynamically allocating software interrupts.

Code:
#include "SoftwareInterrupt.h"

SoftwareInterrupt myIntr;

void setup() {
  // required:
  myIntr.attach(function); // clear, attach function, and enable, so ready to trigger
  myIntr.trigger();
  // optional:
  myIntr.priority(number); // defaults to TBD if this is not used
  myIntr.clear();    // clear pending, if triggered but not yet run
  myIntr.disable();  // disable this interrupt
  myIntr.enable();   // reenable it
}

At this very early stage, everything is open for discussion. My main hope is a simple, Arduino-style API, which is capable of implementing either static or dynamic allocation inside the C++ object.
 
I'm thinking on passing a class instance. :)
That way it does not have to be static!

Here's the general idea... I've already started writing it.
Code:
/**
 * Use this class to extend your class, in order to provide
 * a C++ context callable SWI.
 */
class dyn_SWI {
        /**
         * Override this method with your code.
         */
        virtual void dyn_SWISR(void) {};
};
 
Ok, here's a simple fixed allocation, to serve as a temporary measure until a nice dynamic system is made.

Code:
        Teensy 3.0    Teensy 3.1
        ----------    ----------
Audio       45            94
MP3                       56
UHS          5            17

I would leave 94 to the user, since it is documentated in the Datasheets. This leads to less questions.. :)
 
I'm thinking on passing a class instance. :)

I don't quite follow what you're trying to do with that.

That way it does not have to be static!

The simple API can be either static or dynamic. Each call to trigger() could do stuff like you proposed in msg #9. The function to give to attach() doesn't have to know whether it was called directly by the NVIC or if some other intermediate handler called it.

I really want to keep the user-visible API as simple as possible. If more complexity is needed, there has to be a very compelling reason. I'm trying to keep an open mind, but I really do want API simplicity to be the primary goal.


I would leave 94 to the user, since it is documentated in the Datasheets. This leads to less questions.. :)

Yes, good point. I've created an issue on github, so I'll remember this in a few months when I'm working on the audio library.

https://github.com/PaulStoffregen/Audio/issues/101


I'll use 5/17 for the library. I should only need one now.

Good. Focusing on developing awesome libraries like UHS is the most important thing.

The last thing I want to do is rush a software interrupt API. If more allocations are needed, it should be pretty easy to just add them to the table.
 
I don't quite follow what you're trying to do with that.



The simple API can be either static or dynamic. Each call to trigger() could do stuff like you proposed in msg #9. The function to give to attach() doesn't have to know whether it was called directly by the NVIC or if some other intermediate handler called it.

Function pointers are C-like. Generally they also are in a fixed position, and contain no context.. While this isn't a totally bad thing, it isn't easy to use when you have multiple classes with context information. The idea is that I can use a base class that has a known method name for calling, and extend it with what ever class I choose. That way I only pass 'this' which points to the correct context in which to execute. :cool:
 
Just to remind: We can use ANY interrupt, if it is not used by a device.
I would say, we could add flash_error_isr or flash_cmd_isr to our list too, for teensy 3.0.

Correct. The goal is to use ones not normally used, in order to avoid conflicts.

Also, my code is about to be tested here. I'll post it when it is done/working. It's nice to pass a class instance pointer, as it eliminates any need to pass args...
just store such args in your class if you need to do that. :cool:
 
Given the fact that we are talking about software interrupts , I would like to propose the following macro :
Code:
#define NVIC_GET_ACTIVE(n)	(*((volatile uint32_t *)0xE000E300 + ((n) >> 5)) & (1 << ((n) & 31)))
(similar to the other NVIC_ macros)

Thus, it can be determined from an interrupt with a higher priority , whether a certain other was interrupted.

For my codecs. e.g. that makes sense.
 
Last edited:
@Paul Quick question: When is the nvic ISR pending bit cleared? After ISR completes? When it is triggered?
 
Given the fact that we are talking about software interrupts , I would like to propose the following macro :
Code:
#define NVIC_GET_ACTIVE(n)	(*((volatile uint32_t *)0xE000E300 + ((n) >> 5)) & (1 << ((n) & 31)))
(similar to the other NVIC_ macros)

Thus, it can be determined from an interrupt with a higher priority , whether a certain other was interrupted.

For my codecs. e.g. that makes sense.

Can't we use bitbanding instead of all the shifting?
 
Back
Top