Allocation of software triggered interrupts

It is still a real shame that the NVIC can't do a reentrant ISR, but only ISR with a greater priority.
In some ways, this is a design mistake, as it can make for more CPU work than it should be.
In other ways, it can be a good thing, you don't have to worry about reentrance...
So, really, it is both a good and bad thing.
Too bad that there is no way to specify that an ISR can be reentrant with some sort of register setting.
I've never had any real problems with using a reentrant ISR, unless the signal was way too fast.
My guess here is that the design idea is to prevent problems such as stack crashing into heap, and possibly other problems...
This design can also allow some really sloppy hardware combinations too, but I always have solved problems like that with hardware.
A flip-flop that sampled the IRQ line in phase with the CPU clock solves the same issue, since your code has to manually clear the hardware.
The NVIC goes one or two steps beyond this, but the concept is very similar.
 
Teensy 3.1 has many unassigned IRQ slots. Frank, thanks for confirming 56 & 72 do indeed work.

Could you try IRQ # 95 and 96 with your MP3 code? According to the documentation, 94 is the last IRQ. But the ARM registers allocate them in groups of 32, so it's very likely 95 will work. My guess is 96 doesn't really exist. I don't have a setup on my bench right now to test this stuff at the moment.

Teensy 3.0 has pretty much only IRQ 5 unallocated. The last IRQ is 45. Maybe IRQ 46 to 63 will work?

Some of the allocated IRQs are very unlikely to be used. The two for the flash controller should probably be stolen first, and the LON one for UART0 is a good candidate, since the LON stuff doesn't work in these chips.

Andrew's rotating allocation idea (msg #9) is really interesting, but I'm not sure it's worth so much overhead and complexity. If you had 4 libraries competing for 3 physical interrupt slots, the rare unable to allocate condition could really suck. In the case of a high priority IRQ needing to trigger a low priority IRQ for more work to be done, attempting to wait would result in deadlock, unless one of the allocated IRQs happened to be at a higher priority. Still, maybe this could be worthwhile, with some strongly worded warning that the -1 return really means failure and trying in a loop is not an option.
 
Last edited:
@Paul: Yes, the idea is to have some generic slots reserved. I simply choose 3 as an example.
Two would be fine as well.
As far as performance, I'm not all that concerned, since it is more-or-less a one-shot SWI, and for USB, we have an entire millisecond to do the critical bits.
In CPU time on something clocked at ~100MHz, 1ms is a pretty slow signal. Even the AVR keeps up with it just fine.
In fact, with the reentrant ISR code on AVR, I see a boost in performance when reading and writing a 1MB file.
I suspect the gains on ARM should be better just because of the faster SPI alone, which is the actual bottleneck.
I can't wait to try and apply the same stuff to the teensy 3.x USB host mode, it should really kick butt.
 
I'm looking forward to see it too, of course when you're ready to put it out for beta testing.

For UHS, I see your point about the lower priority processing not being performance critical. The same is true for the audio library. But perhaps some future library will want lower latency? If there's going to be an API in the core library, the anticipated needs of future projects need to be considered.

My hope is to design a C++ object, similar to DMAChannel, which wraps up all the details. Ideally, it should be possible to implement either static or dynamic allocation inside the class, without libraries like UHS having to know how it's actually implemented. On Teensy 3.1 where lots of spare IRQs exist, it could be static. On a Cortex-M0 processor with very few extra IRQs, it could be implemented differently. For a library without low latency needs, it wouldn't matter how the core library actually does it.
 
I really like the nesting feature, but yeah, it's pretty much "ARM's way or the highway".

Personally, I avoid things like reentrant interrupts and self modifying code. You can do so much, but long-term the complexity becomes terribly difficult to maintain.
 
I'm looking forward to see it too, of course when you're ready to put it out for beta testing.

For UHS, I see your point about the lower priority processing not being performance critical. The same is true for the audio library. But perhaps some future library will want lower latency? If there's going to be an API in the core library, the anticipated needs of future projects need to be considered.

This is why you select just some from a pool for stuff that doesn't care.
The remaining ones would be static, for those that need something latency critical.
 
Teensy 3.1 has many unassigned IRQ slots. Frank, thanks for confirming 56 & 72 do indeed work.

Could you try IRQ # 95 and 96 with your MP3 code? According to the documentation, 94 is the last IRQ. But the ARM registers allocate them in groups of 32, so it's very likely 95 will work. My guess is 96 doesn't really exist. I don't have a setup on my bench right now to test this stuff at the moment.

Teensy 3.0 has pretty much only IRQ 5 unallocated. The last IRQ is 45. Maybe IRQ 46 to 63 will work?

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


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


volatile int chk;

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

const unsigned char swInterrupts[] = {33,39,44,53,54,55,56,57,58,59,67,68,69,70,71,72,91,92,93,94,95,96,98,102,108,109,IRQ_SOFTWARE+16/*,IRQ_SOFTWARE+17*/,0};

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

95 does NOT work.
I can't test Teensy 3.0....
 
Hi WMXZ, sorry, i meant 95 + 16 (the 16 first are special ones)

And you have to change teensy.h #define NVIC_NUM_INTERRUPTS to 96
, otherwise the array is too small.

EDIT: kinetis.h, not teensy.h :)
 
Hi WMXZ, sorry, i meant 95 + 16 (the 16 first are special ones)

And you have to change teensy.h #define NVIC_NUM_INTERRUPTS to 96
, otherwise the array is too small.

EDIT: kinetis.h, not teensy.h :)
I was running only your sketch and reported the printout (I know about +16)
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.
110 (94)...OK.
Done.
 
yes.. but try IRQ_SOFTWARE + 17. The last one, in comments (/* */)
Thats the one, which Paul asked for.
 
Personally, I avoid things like reentrant interrupts and self modifying code. You can do so much, but long-term the complexity becomes terribly difficult to maintain.
I wholeheartedly agree. Reentrant/nested IRQ processing is error prone and usually has rare race conditions that are almost impossible to duplicate for troubleshooting.
 
In my case, there are no tricks, it is solved very simple. There is no difference whether decode () from (possibly a loop) the main program is called from a timer interrupt or a software interrupt. In fact, decode () is called to decode the first frame also in "play (filename)" :) . This works without difficulty however, because the priority is lower than that of the update ().
There is not more "nesting" (no, in fact it is'nt nesting) than with update() alone, and no reentrance.

Just simply look into the source code at this point, or better, listen to a few songs:)
It's ready to be used, im only doing some improvements now.. next week, i'll add the next codec(s).
 
I wholeheartedly agree. Reentrant/nested IRQ processing is error prone and usually has rare race conditions that are almost impossible to duplicate for troubleshooting.

I've never ran into this in code I've written. You just need to be meticulous and focused. I've been writing code for 30 years, and that does help.
That said, the rules are pretty simple... disable irq, do atomic code, enable irq.
 
That said, the rules are pretty simple... disable irq, do atomic code, enable irq.

I guess, this is equivalent to 'don't do lengthy processing' while in ISR context, but I think, we had this discussion somewhere else.

Anyhow, in my T3.1 development, all ISR simply establish context and register a function to be scheduled after return from ISR.
So, there is no data processing in ISR.
 
I guess, this is equivalent to 'don't do lengthy processing' while in ISR context, but I think, we had this discussion somewhere else.

Anyhow, in my T3.1 development, all ISR simply establish context and register a function to be scheduled after return from ISR.
So, there is no data processing in ISR.

I actually do up to 30 seconds of processing inside a recursive ISR without any issues at all. The lower priority SWI idea will basically allow me to do the same thing with the NVIC...
 
At some point, some guidelines about assigning IRQ priorities should probably be written. Perhaps that document should also maintain a list of known usage in the core library and all libraries that don't use the default?
 
At some point, some guidelines about assigning IRQ priorities should probably be written. Perhaps that document should also maintain a list of known usage in the core library and all libraries that don't use the default?

That would be an excellent idea, since I can't do any code at all before knowing the "rules" for not stepping on any other usage.
It would be really nice if we reserve at least two for the dynamic one-shot assigning, and document the limitation.
If instead three or four can be set aside, that would be very awesome and less restricting.

Just for the record, my reentrant host code can actually accept a failure to allocate.
This is because when a device is present, the SOF generates an IRQ once every 1ms, and after the hardware is taken care of, the bottom part isn't time critical.
The result is that it will retry next later all on its own, and no harm is done.
 
Ok, I've edited the first post with an initial list.

I'm considering changing systick to 16 and the audio library to 192 or 208.

Something to keep in mind is there are only 16 distinct priorities on Teensy 3.1.

0,16,32,48,64,80,96,112,128,144,160,176,192,208,224,240

Perhaps 16 guidelines should be written?
 
Please consider this list a call for discussion.

Code:
  0:  save for use by user - do not use in libraries - never publish examples using this
 16:  urgent timing - should not normally be used
 32:  waveform generation & sampling
 48:  data acquisition, ADC, etc
 64:  communication & DMA, unbuffered or high speed, data loss if late interrupt
 80:  communication & DMA,
 96:  communication & DMA,
112:  communication & DMA, large buffers or low speed, data delayed if late interrupt
128:  default - general purpose use
144:  ???
160:  ???
176:  ???
192:  triggered background tasks, under 1 ms
208:  triggered background tasks, 1 to 4 ms
224:  triggered background tasks, 4 to 10 ms
240:  triggered background tasks, 10+ ms

I want to strongly discourage use of 0 (highest priority) in libraries. This should always be saved for the end user, so they can make their own interrupt the highest priority.

Use of 16 should also be discouraged, except for the most urgent needs. The systick interrupt, which drives all the Arduino timing functions, will use this. It's just a couple instructions, and many other things depend upon it working correctly.

Waveform generation is stuff like Servo (which I believe is still using the default) and SoftwareSerial, where any interrupt latency directly contributes to poor quality waveforms.

Data acquisition is reading ADC or other hardware at a consistent sample rate, where interrupt latency could cause the next sample to be lost, but some latency up to about 1 sample time has no effect.

Communication is a broad category of applications where buffering of data takes place, lessening the need for low-latency interrupt response. Less buffering, higher speeds, and lack of flow control require higher priority. When flow control is present, usually the lower of these priorities are acceptable, since a late interrupt only delays data rather that losing it.

Triggered background tasks are the stuff we've been talking about here. I put the rough execution time guidelines in. Perhaps there should be some guidelines about types of uses and consequences for the interrupt being delayed?
 
Last edited:
Lowest priority (240) for SWI would be fantastic for the bottom half use in the host library. Are you suggesting that we pass a priority setting too?

The hardware generates an SOF IRQ every 1ms. Other events can be shorter, but the chip is really nice and won't miss the events. and not affected by latency.
What is the priority for pin IRQ? The SWI needs to be lower priority than pin generated IRQ.
On another note, I can also share the IRQ line with > 1 MAX3421e host controller, which is pretty neat. :)
 
Currently, all interrupts from attachInterrupt() are the default priority, 128.

A version of attachInterrupt() that accepts a 4th parameter for priority is on my TODO list.... but at a low priority!

For the bottom half, currently you would use attachInterruptVector() and NVIC_SET_PRIORITY(), where you hard-code the IRQ number. Someday we might have a nicer API that dynamically handles which IRQ to use.
 
Last edited:
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.
.
 
Back
Top