0

I'm using the TC3 interrupt on my Arduino Due to run some code periodically, like this:

void TC3_Handler() {
  TC_GetStatus(TC1, 0); // Remove interrupt flag
  // My code...
}

However, as I've found out, the CPU will not service the same interrupt a second time while the interrupt is running. By editing the priorities of each interrupt source, it's possible for a higher priority interrupt to preempt and run over this interrupt (so nested interrupts are possible), but priority manipulation alone does not allow reentrant interrupts.

I'd like to know if there is any way to make this interrupt reentrant.

1

In your notion about the meaning of Reentrant interrupt, you write:

interrupt A can preempt handling of interrupt A; interrupt A's handler can be active concurrently with itself


I. The first half of the notion correctly states a condition that might trigger re-entrance of an ISR (interrupt service routine). Note, an ISR that allows re-entrance might or might not be called reentrant: an ISR's code is reentrant if it performs correctly when it is re-entered.

On most processors that use a single stack, code will be reentrant if it has no side effects and does not access variables that are not in its stack frame. It might be reentrant even if it does have side effects or accesses non-local variables, if its actions are idempotent or if some proper concurrency protocol is implemented.


II. The second half of the notion is misleadingly stated: true concurrency does not occur on a single-thread-of-execution processor. However, time sharing and task overlap may occur on such a processor via task switching using either preemptive or cooperative multitasking.

As explained in the next paragraph, to the best of my knowledge preemptive serial multitasking is what would happen if you re-enable the interrupt system within an ISR.

Let's suppose ISR A is written in a reentrant fashion, and that it clears the interrupt A interrupt flag early in its processing, and enables interrupts so more A interrupts can occur and be processed. Also suppose three interrupt A's occur in quick succession (more precisely, quickly enough that processing of one does not complete before another comes along, but not so quickly that the A flag has not been cleared, which would cause loss of an A interrupt) followed by a hiatus. In this case, the first interrupt A occurs; the ISR entry prologue creates a stack frame for the ISR code to use, then saves a number of registers, then begins executing user code, which we suppose turns interrupts back on. After a bit the second interrupt A occurs. ISR entry prologue creates stack frame and saves registers. User code enables interrupts. Third interrupt A occurs. ISR entry prologue creates stack frame and saves registers. User code enables interrupts. As we've posited a hiatus, no more A interrupts occur for a while. So processing of interrupt 3 proceeds and finishes. The ISR epilogue reloads registers, clears the stack frame, and returns from interrupt, restoring the previous interrupt state. Then processing of interrupt 2 proceeds, finishes, reloads registers, returns from interrupt. Then processing of interrupt 1 proceeds, finishes, reloads registers, returns from interrupt. Then loop() [or whatever is running in the main thread of execution] resumes its work.

As you can see from this example, interrupt processing will occur in a LIFO (last-in, first-out) order if an ISR simply accommodates re-entrance. If that is what you want, fine. If, however, you want interrupt processing to complete in order; want to do lengthy processing while avoiding interrupt misses; and want to process in a secondary thread of execution, ie, a callback-like system, then the thing to do is have the ISR make a queue entry (in a static or heap-based circular queue) each time it starts; return from interrupt if it was re-entered; and process queue entries until the queue is empty, using atomic operations when changing queue pointers.


Edit 2: In §10.20.9.1 of the SAM3x/SAM3A datasheet, “Hardware and software control of interrupts”, we find

A peripheral interrupt becomes pending for one of the following reasons:

• the NVIC detects that the interrupt signal is HIGH and the interrupt is not active
...

A pending interrupt remains pending until one of the following:

• The processor enters the ISR for the interrupt. This changes the state of the interrupt from pending to active. Then:

• For a level-sensitive interrupt, when the processor returns from the ISR, the NVIC samples the interrupt signal. If the signal is asserted, the state of the interrupt changes to pending, [...] Otherwise, the state of the interrupt changes to inactive.
• If the interrupt signal is not pulsed while the processor is in the ISR, when the processor returns from the ISR the state of the interrupt changes to inactive.

• Software writes to the corresponding interrupt clear-pending register bit.
...

[Emphasis added]

This suggests that although the ISR for a given interrupt source on a SAM3x/SAM3A system can be interrupted by a higher-priority source, a given source can not re-interrupt until “the processor returns from the ISR”. Without some research or testing, it isn't clear to me if that is correct, and whether software can simulate a return from ISR without actually returning. Setting that possibility aside, here is a workaround to allow interrupt A to recur:

Set the interrupt priority of A fairly high, say level z. Set up several selected SGI's (software generated interrupts) at levels p, q, r ..., all lower-priority than z. Initialize an A level variable to point to the least-priority SGI ISR among those selected. Upon any A interrupt, the A ISR issues an A level SGI, saves any data that SGI ISR needs, updates A level to next-higher-priority SGI [if possible], and returns from interrupt. This re-enables A interrupts and allows the most recently issued (and currently highest priority) SGI to fire and start doing its thing. When it's done, it atomically sets A level equal to its own level. Thus, while any of the SGI ISR's is processing, if another A interrupt occurs, the current SGI ISR's processing suspends; when the A interrupt is handled, the next-higher SGI ISR starts. All higher level ISRs finish before a lower level ISR resumes.

  • "If that is what you want, fine". Yes, that is exactly what I want. And that is exactly what I'd like to know how to do. – Alex Apr 13 '17 at 12:57
  • @Alex, see edit 2 – James Waldby - jwpat7 Apr 13 '17 at 20:39
  • Your edit is more to the point of my question. Thanks. Unfortunately, if I understood your workaround correctly, if the code that runs on the interrupt takes long enough for that interrupt to be fired 40 more times, then 40 SGIs are needed, no? I'm writing a library, in fact, and I have no control over how long the code in the interrupt takes to run. It seems that "software simulating a return from ISR without actually returning" is the only option. Any ideas on how to accomplish that? – Alex Apr 13 '17 at 20:52
  • @Alex, feel free to upvote/select answer if useful. ¶ Re how to accomplish software simulating return, I don't know how [on the SAM; its easy on AVR] or if its possible. ¶ Re "40 SGIs", apparently there are plenty of SGI numbers (0-239) available, and of course using task blocks for task data, only one SGI ISR needs to be instantiated, but of course there are only 16 priority levels on the SAM. ¶ You haven't said what your overall aim is; it could be that re-entrant interrupts are not the best approach to it. Maybe a threads or task-switching system would do the job. – James Waldby - jwpat7 Apr 13 '17 at 21:08
  • I'd like to build from the ground up a very simple task based RTOS implemented around a periodic tick (the timer interrupt) – Alex Apr 13 '17 at 21:12
0

does not allow reentrant interrupts.

a few suggestions:

1) understand what "reentrant interrupts" means;

2) see if you need it;

3) see what needs to be done to get it to run on your hardware;

4) see if your code does what needs to be done.

hope it helps.

  • I'm sorry, but your answer doesn't answer anything. 1) I'm perfectly aware of what a reentrant interrupt is. 2) Yes, I need it. Or if I can achieve the same effect with something else, I would like to know if someone can point me in the right direction. 3) If I knew what needed to be done I wouldn't be asking this question. 4) No, it doesn't, or I wouldn't have a question. – Alex Apr 13 '17 at 1:41
  • Interrupt nesting: interrupt A can preempt handling of interrupt B; both interrupt A and interrupt B's handlers can be "active" at the same time. Reentrant interrupt: interrupt A can preempt handling of interrupt A; interrupt A's handler can be active concurrently with itself. – Alex Apr 13 '17 at 1:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.