Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

High Performance, Zero Latency Interrupts

Generic Interrupt Handling

NuttX includes a generic interrupt handling subsystem that makes it convenient to deal with interrupts using only IRQ numbers. In order to integrate with this generic interrupt handling system, the platform specific code is expected to collect all thread state into an container, struct xcptcontext. This container represents the full state of the thread and can be saved, restored, and exchanged as a unit of thread.

...

Terminology: The concepts discussed in this Wiki are not unique to NuttX Other RTOS have similar concepts but will use different terminology. The Nucleus RTOS, for example use the terms Native and Managed interrupts.

Bypassing the Generic Interrupt Handling

Most modern MCUs (such as the ARM Cortex-M family) receive and dispatch interrupts through a vector table. The vector table is a table in memory. Each entry in the table holds the address of an interrupt handler corresponding to different interrupts. When the interrupt occurs, the hardware fetches the corresponding interrupt handler address and gives control to the interrupt handler.

...

  • Your custom interrupt handler will not have collected its state into the struct xcptcontext container. Therefore, it cannot communicate with operating system. Your custom interrupt handler has been taken "out of the game" and can no longer work with the system.
  • If your custom interrupt is truly going to be high performance then you will also have to support nested interrupts! The custom interrupt must have a high priority and must be able interrupt the generic interrupt handling logic. Otherwise, it will be occasionally delayed when there is a collision between your custom interrupt and other, lower priority interrupts.

Getting Back into the Game

As mentioned, the custom interrupt handler can not use most of the service of the OS since it has not created a struct xcptcontext container. So it needs a mechanism to "get back into the game" when it needs to interact with the operating system to, for example, post a semaphore, signal a thread, or send a message.

...

The custom logic would be needed to communicate the events of interest between the high priority interrupt handler and PendSV interrupt handler. A detailed discussion of that custom logic is beyond the scope of this Wiki page.

Nested Interrupt Handling

Some general notes about nested interrupt handling are provided in another Wiki page. In this case, handling the nested custom interrupt is simpler because the generic interrupt handler is not re-entered. Rather, the generic interrupt handler must simply be made to co-exist with the custom interrupt interrupt handler.

...

Some of these issues are complex and so you should expect some complexity in getting the nested interrupt handler to work.

Cortex-M3/4 Implementation

Such high priority, nested interrupt handler has been implemented for the Cortex-M3/4 families.
The following paragraphs will summarize that implementation.

Configuration Options

CONFIG_ARCH_HIPRI_INTERRUPT

If CONFIG_ARMV7M_USEBASEPRI is selected, then interrupts will be disabled by setting the BASEPRI register to NVIC_SYSH_DISABLE_PRIORITY so that most interrupts will not have execution priority. SVCall must have execution priority in all cases.

...

If, in addition, CONFIG_ARCH_HIPRI_INTERRUPT is defined, then special high priority interrupts are supported. These are not "nested" in the normal sense of the word. These high priority interrupts can interrupt normal processing but execute outside of OS (although they can "get back into the game" via a PendSV interrupt).

Disabling the High Priority Interrupt

In the normal course of things, interrupts must occasionally be disabled using the up_irq_save() inline function to prevent contention in use of resources that may be shared between interrupt level and non-interrupt level logic. Now the question arises, if we are using the BASEPRI to disable interrupts and have high priority interrupts enabled (CONFIG_ARCH_HIPRI_INTERRUPT=y), do we disable all interrupts except SVCall (we cannot disable SVCall interrupts)? Or do we only disable the "normal" interrupts?

...

Hence, if you need to disable the high priority interrupt, you will have to disable the interrupt either at the peripheral that generates the interrupt or at the interrupt controller, the NVIC. Disabling global interrupts via the BASEPRI register cannot affect high priority interrupts.

Dependencies

  • CONFIG_ARCH_HAVE_IRQPRIO. Support for prioritized interrupt support must be enabled.
  • Floating Point Registers. If used with a Cortex-M4 that supports hardware floating point, you cannot use hardware floating point in the high priority interrupt handler UNLESS you use the common vector logic that supports saving of floating point registers on all interrupts.

Configuring High Priority Interrupts

How do you specify a high priority interrupt? You need to do two things:

...

Second, you need to set the priority of your interrupt to NVIC to NVIC_SYSH_HIGH_PRIORITY using the standard interface: int up_prioritize_irq(int irq, int priority);

Example Code

You can find an example that tests the high priority, nested interrupts in the NuttX source:

...