Exception Handling on a 16-bit PIC® MCU

Exceptions

Exceptions are asynchronous, hardware-driven events that cause the MCU to divert from normal code execution. This section provides an overview of the exception processing system on 16-bit MCUs and dsPIC® Digital Signal Controllers (DSCs).

exceptions.png

More detailed information may be found in the interrupt controller section of the appropriate family reference manual, for example: "Section 8, Interrupts in the PIC24F Family Reference Manual".

Introduction

The 16-bit MCU and DSC devices have a vectored exception scheme with support for up to eight sources of non-maskable traps and up to 246 interrupt sources. In both families, each interrupt source can be assigned to one of seven priority levels.

The basic interrupt latency is four instruction-cycles on entering and three cycles exiting an Interrupt Service Routine (ISR).

The combination of deterministic, low-latency exception processing with user-selectable interrupt priority makes the 16-bit interrupt management system superior to most other architectures.

Vectored Exception Scheme

Each interrupt source can trigger the execution of a unique piece of code, called an interrupt service routine.

Each ISR's start address (also called a vector) is stored in the Primary Interrupt Vector Table (IVT) as shown:

vector-table.png

Simplified Exception Process

When an exception event occurs:

  • Hardware (core or peripheral) detects it.
  • IF the programmed priority of the exception event is GREATER THAN the current CPU priority, current program execution is halted.
  • The exception's ISR is started.

When an ISR completes:

  • Hardware restores the previously halted program execution (at the preserved CPU priority) at the exact instruction that would have been executed had the exception event not occurred.

Types of Exceptions

The 16-bit exception processing system recognizes two types of exception:

Non-Maskable Traps

Traps can be considered as non-maskable, nestable interrupts which adhere to a fixed priority structure. They are intended to detect certain hardware and software problems. The PIC24F has four implemented sources of non-maskable traps:

  • Oscillator Failure Trap
  • Stack Error Trap
  • Address Error Trap
  • Arithmetic Error Trap

Peripheral & External Interrupts

These are the regular, maskable interrupt requests that come from a variety of implemented MCU peripherals:

  • External Interrupt Pins
  • Input Capture/Output Compare
  • Communication Interfaces (UART/SPI/I²C/USB/Ethernet)
  • Parallel Master Port (PMP)
  • Analog I/O (Comparator, ADC/DAC)
  • etc…

CPU Priority

One of the key features of the 16-bit exception processing system is the implementation of a user-programmable priority setting for peripheral and external interrupt sources. This requires an understanding of the concept of CPU priority.

The 16-bit CPU can operate at one of 16 priority levels (0-15). An interrupt or trap source must have a set priority level greater than the current CPU priority in order to initiate an exception process.

Priority levels for peripheral and external interrupt sources can be programmed to levels 0-7, while CPU priority levels 8-15 are reserved for trap sources and are fixed.

The CPU priority for the currently executing thread is indicated by the IPL bits in the CPU Status and Core Control registers as shown:

cpu-priority.png

At a device Reset, the CPU priority is set to 0 .

Configuring CPU Priority

  • IPL bits are automatically controlled by the exception processing logic based on the exception source.
  • You can manually set the user CPU priority bits (IPL<2:0>) at any time. This is useful for temporarily masking all other interrupts to perform a CPU-intensive task.
  • IPL3 is set only by the core and indicates a trap event.

Interrupt Nesting

Interrupts, by default, are nestable. Any ISR that is in progress may be interrupted by another interrupt source having a higher programmed priority level.

The following animation demonstrates a specific scenario involving the main() execution thread along with three ISR threads pre-programmed at varying levels of priority with interrupt nesting enabled.

  • t0: CPU priority is 0 and is running the main() execution thread.
  • t1: A Level-4 exception is triggered. Since the exception priority (4) is greater than the current CPU priority (0), the Level-4 exception thread is executed.
  • t2: A Level-7 exception is triggered. Since the exception priority (7) is greater than the current CPU priority (4), the Level-7 exception thread is executed.
  • t3: A Level-1 exception is triggered. Since the exception priority (1) is less than the current CPU priority (7), the Level-1 exception thread is queued for later execution.
  • t4: The Level-7 exception thread completes and issues a RETFIE (return from interrupt). The Level-4 thread's context is popped off the stack (including the saved CPU priority) and the Level-4 thread continues execution.
  • t5: The Level-4 exception thread completes and issues a RETFIE. The queued Level-1 exception is triggered.
  • t6: The Level-1 exception thread completes and issues a RETFIE. The Level-0 (main()) thread's context is popped off the stack (including the saved CPU priority) and the Level-0 thread continues execution.

Note that the latency for re-entering nested interrupts is seven instruction cycles (three for exiting the prior thread + four for re-entering the next thread).

Note that lower priority exceptions in a nested application can experience extended and variable latencies.

Disabling Nesting

Interrupt nesting may be optionally disabled by setting the NSTDIS control bit (INTCON1<15>).

When the NSTDIS bit is set, all interrupts in progress force the CPU priority to level 7 by setting IPL<2:0>=111.

This effectively masks all other sources of interrupts until a RETFIE instruction is executed.

When interrupt nesting is disabled, the user-assigned priority levels will have no effect, except to resolve conflicts between simultaneously pending interrupt requests.

Resolving Interrupt Conflicts

Since more than one interrupt request source may be assigned to a specific priority level, a means is provided to resolve priority conflicts within a given user-assigned level.

For example, on a device Reset, the IPCn registers are initialized such that all user interrupt sources are assigned to priority level 4. How does the system resolve such conflicts?

Each interrupt source has a Natural Order Priority based on its location in the Interrupt Vector Table. Lower numbered vectors have higher natural priority as shown:

natural-order-priority.png

The overall priority level for any pending interrupt source is thus determined:

  • First, by the user-assigned priority of that source in the IPCn register.
  • Then, by the natural order priority within the Interrupt Vector Table.

Configuring & Using Interrupts

There are three sets of control bits that need to be considered when working with interrupts:

  • Interrupt Flags
    • Indicate that an interrupt event has occurred
    • Set by the hardware and cleared by the programmer
  • Interrupt Enables
    • Enable or disable individual interrupt sources
  • Interrupt Priority bits
    • Set the priority of individual interrupt sources

The Flag, Enable and Priority control bits are used by the interrupt controller to receive/prioritize all exception requests and send a single vector and corresponding IPL to the CPU core as shown:

interrupt-controller.png

External Interrupt Example (INT0)

IECx Register
To enable any particular interrupt, IECx registers are to be used. By setting the corresponding bit, the particular interrupt can be enabled. The following depicts the IECx bit location for INT0 on PIC24FJ128GA010:

iec-example.png

We do not recommend changing the interrupt priority while an interrupt is enabled as this can cause conflicts. Changing the priority at the same time an interrupt occurs can lead to the interrupt being missed or incorrect priority levels to be used. An interrupt should be disabled before changing its priority.

IPCx Register
The IPCx registers are used to set the priority of any interrupt. A 3-bit field IP is provided whereby you can set the priority from 0 to 7. The following depicts the the IPCx register INT0IP bit locations for INT0 on PIC24FJ128GA010:

ipc-example.png

If priority is set to 0, the interrupt is disabled.

IFSx Register
On occurrence of a specific event, the interrupt's interrupt flag bit will be set in the IFSx register.

ifs-example.png

The user program needs to acknowledge the interrupt by clearing the IFS flag in the ISR.

Declaring an Interrupt Service Routine

The ANSI-C language specification does not specify how to declare a function as an "interrupt service routine".

The MPLAB® XC16 Compiler defines a special function attribute for declaring an interrupt service routine function, as shown here for the INT0 interrupt:

__attribute__((interrupt))
isr-declaration.png

Requirements for ISR functions:

  • No parameters
  • Void return type
  • Must use pre-defined name (see XC16 Compiler User's Guide)
  • Must not be called from main-line code

Context Saving

context-saving.png

Interrupts can occur at any time and could corrupt the variables used in your main program.

The 16-bit exception processing hardware provides a mechanism for saving and restoring CPU context from the stack memory.

The following registers are automatically saved and restored from the stack by the exception processing hardware:

  • Program Counter (PCL & PCH)
  • CPU Status Register Low Byte (SRL)

Additionally, the XC16 compiler will generate additional context-saving instructions to save and restore the following registers from the stack:

  • Repeat Count Register (RCOUNT)
  • Working Registers used in the ISR (W0-W13)
  • Program Space Visibility Page Register (PSVPAG)
  • Previous Frame Pointer (W14)

In addition to these saved registers, you can have the compiler save and restore other program variables that could be affected by the ISR via the save(x, y) attribute as shown:

isr-declaration-save-attribute.png

For Fast Interrupts use Shadow Registers

Registers W0-W3 and certain CPU status registers are shadowed, meaning there is one level of dedicated backup registers for them. Context is saved by a single PUSH.S instruction while POP.S will restore all these registers in a single shot.

shadow-registers.png

Shadow register instructions may be optionally generated for your highest priority interrupt via the shadow attribute as shown:

isr-declaration-shadow-attribute.png
  • Shadow registers are only one level deep.
  • Contents can be lost if two interrupts use shadow registers and one is a higher priority than the other.
© 2024 Microchip Technology, Inc.
Notice: ARM and Cortex are the registered trademarks of ARM Limited in the EU and other countries.
Information contained on this site regarding device applications and the like is provided only for your convenience and may be superseded by updates. It is your responsibility to ensure that your application meets with your specifications. MICROCHIP MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND WHETHER EXPRESS OR IMPLIED, WRITTEN OR ORAL, STATUTORY OR OTHERWISE, RELATED TO THE INFORMATION, INCLUDING BUT NOT LIMITED TO ITS CONDITION, QUALITY, PERFORMANCE, MERCHANTABILITY OR FITNESS FOR PURPOSE. Microchip disclaims all liability arising from this information and its use. Use of Microchip devices in life support and/or safety applications is entirely at the buyer's risk, and the buyer agrees to defend, indemnify and hold harmless Microchip from any and all damages, claims, suits, or expenses resulting from such use. No licenses are conveyed, implicitly or otherwise, under any Microchip intellectual property rights.