Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: fix typo

...

  1. Some low-level logic, usually in assembly language, that catches the interrupt and determines the IRQ number. Consider arch/arm/src/armv7-m/up_exception.S as an example for the Cortex-M family.
  2. That low-level logic than calls some MCU-specific, intermediate level function usually called up_doirq(). An example is arch/arm/src/armv7-m/up_doirq.c.
  3. That MCU-specific function then calls the NuttX common interrupt dispatching logic irq_dispatch() that can be found at sched/irq_dispatch.c

How to Implement Nested Interrupts in the Layered Interrupt Handling

...

Architecture

The logic in these first two levels that would have to change to support nested interrupt handling. Here is one technical approach to do that:

...

A generic up_doirq() might look like the following. It can be very simple because interrupts are disabled:

Code Block

  uint32_t *up_doirq(int irq, uint32_t *regs)
  {
    /* Current regs non-zero indicates that we are processing an interrupt;
   * current_regs is also used to manage interrupt level context switches.

...

Code Block
   */

  current_regs = regs;
Code Block
    /* Deliver the IRQ */
Code Block
    irq_dispatch(irq, regs);
Code Block
    /* If a context switch occurred while processing the interrupt then
   * current_regs may have change value.  If we return any value

...

Code Block

    different
   * from the input regs, then the lower level will know that a context
   * switch occurred during interrupt processing.
   */

  regs = (uint32_t*)current_regs;
    current_regs = NULL;
    return regs;
  }

What has to change to support nested interrupts is:

...

So the modified version of up_doirq() would be as follows. Here we assume that interrupts are enabled.

Code Block

  uint32_t *up_doirq(int irq, uint32_t *regs)
  {
    irqstate_t flags;
Code Block
    /* Current regs non-zero indicates that we are processing an interrupt;
   * regs holds the state of the interrupted logic; current_regs holds

...

 the
   * state of the interrupted user task.  current_regs should, therefor,

...


   * only be modified for outermost interrupt handler (when g_nestlevel == 0)

...

Code Block
   */

  flags = irqsave();
    if (g_nestlevel == 0)
      {
        current_regs = regs;
      }
    g_nestlevel++
    irqrestore(flags);
Code Block
    /* Deliver the IRQ */
Code Block
    irq_dispatch(irq, regs);
Code Block
    /* Context switches are indicated by the returned value of this function.
   * If a context switch occurred while processing the interrupt

...

 then
   * current_regs may have change value.  If we return any value

...

Code Block

     different
   * from the input regs, then the lower level will know that a context
   * switch occurred during interrupt processing.  Context switching should
   * only be performed when the outermost interrupt handler returns.
   */

  flags = irqsave();
    g_nestlevel--;
    if (g_nestlevel == 0)
      {
        regs = (uint32_t*)current_regs;
        current_regs = NULL;
      }
Code Block
    /* Note that interrupts are left disabled.  This needed if context switch
   * will be performed.  But, any case, the correct interrupt state

...

Code Block
 should
   * be restored when returning from the interrupt.
   */

  return regs;
  }

NOTE: An alternative, cleaner design might also be possible. If one were to defer all context switching to a PendSV handler, then the interrupts could vector to the do_irq() logic and then all interrupts would be naturally nestable.

...