Saturday, September 7, 2024

Understanding ARM Exceptions/Interrupts

Readers: For upcoming posts--low frequency counter, clock multiplication module, etc., I will dig deeply into ARM processor exceptions, a.k.a. "interrupts."

Exceptions can be complex in the ARM world, requiring mastery of hardware, peripherals, programming concepts, and baffling acronyms (e.g.: "ICSR","AIRCR" "NVIC" etc).

This post captures my lab notes surrounding ARM exceptions.

I will be updating/correcting/augmenting this post going forward as I work at the bench. 

Which means: this post is mostly for me--if you want to skip it, skip it. 

but! if you correct things via the comment section I would be grateful--help from experienced ARM developers is always apprecated. 

Thanks.



LINKS

Good web page for this: https://interrupt.memfault.com/blog/arm-cortex-m-exceptions-and-nvic

Video: https://www.youtube.com/watch?v=Uut2uIODlbQ&t=305s; sadly there is no source code linked--so, useful to RP2040 dev until about 8:00.

SysTick: https://www.youtube.com/watch?v=fMqCBMWZ0pk ARM Programming courses frequently use SysTick exception 15 to demostrate interrupts. SysTick is a relatively simple counter that can throw an exception over and over. The video explains how SysTick is implemented in an ARM core but does not provide code examples one can download.

C code example for Systick: https://users.ece.utexas.edu/~valvano/arm/SysTick.c.  SysTick code for PICO is here. Other forum posts indicate that the PICO SDK does not abstract SysTick functionality, hence, you have to set the registers directly:

#include <stdio.h> #include "pico/stdlib.h" #include "hardware/structs/systick.h" int main() { stdio_init_all(); systick_hw->csr = 0x5; systick_hw->rvr = 0x00FFFFFF;


PDF of the "generic Cortex Users' guide" is here--how an ARM Cortex M core works at a deep level. Section 2.3 covers a lot of what is in this post; section 4.1 covers peripherals such as NVIC (below). I find this PDF more useful than the web based version here.

---------

I hear ARM exceptions referred to as “interrupts” but in the ARM world interrupts are a type of exception.  

Exceptions have a unique number associated with them starting at 1 (not 0!)  

This number is then used as an offset in a vector table; at each offset is a memory address of a routine to be run.

A made-up example:

If the ARM core sees exception 1 run a routine beginning at 0x0002534

If the ARM core sees exception 2 run a routine beginning at 0x0002538

And so on.

Along with this unique number is “priority”.  The idea here: if 2 exceptions are recognized by the processor at the same time, which one gets handled first?  The lower the priority #, the higher the "precedence"—the processor handles the lower priority #’d exception first.

 

ISR

INTERRUPT SERVICE ROUTINE (ISR): the routine that is pointed to by the vector.   


STATES

Exceptions in the ARM world have "STATES":

Pending: the processor has seen the exception but hasn’t done anything yet. Important idea: for an exception to be recognized and eventually run, a 1 is put into a register to make the exception “pending”, then based on other factors the ARM core will process it. See REGISTER section below for details.

Active: the exception handler is running but isn’t finished yet.

Pending and active: the processor sees the same exception that it’s currently processing.

Inactive: an exception is neither pending nor active.

 

ARM EXCEPTIONS--"CORE INTERRUPTS"

These are exceptions hard coded into an ARM Cortex M core.  

Built in exceptions are used for housekeeping--you can’t change them.

The NVIC (below) peripheral is not used when processing these particular exceptions (note, is that right? I read different things about this, so I am not sure--are both core and non-core exceptions handled by the NVIC, or not? Does anyone know for sure?).

0 vector table points to reset value of stack pointer.

Reset: 1. The routine that is executed when the chip comes out of reset.

Non Maskable Interrupt or "NMI"--2: this exception cannot be disabled. It is fired when there is an error in another exception handler.  The vector points to a routine to run in this oh-shit scenario.

Hardfault: 3  Routine is run when there is a major fault: memory error, divide by 0, etc.

MemManage: 4

Busfault: 5

7-10 reserved

SVCALL: 11:  routine to handle calls to handle a service routine—for instance, a call to the OS to do something.

Debug monitor: 12

13 reserved

PendSV: 14

SysTick: 15. See below.


EXTERNAL or "NON-CORE EXCEPTIONS" 16+0 to 16+N 

These interrupts are defined by the SOC developer; they vary from chip to chip....

The Cortex M0+ (RP2040) has 32 non-core exceptions; other ARM chips can have more.

IRQ's are the external exception numbers minus 16, so exception 0x0F is IRQ 0x0, 0x10 is IRQ 0x1, etc. 

REGISTERS

Exception state is captured in registers located in the ARM System Control Space ("SCS")

Interrupt Control and State Register, ICSR

If you want to check up on what the processor is doing or is going to do exception-wise look at this register.  The active/inactive/pending/and "what exception is running now" is stored here.

Application Interrupt and reset Control Register (AIRCR)

used to further prioritize your exceptions.

Also--Writing a 1 to bit 2 fires a system reset.

System handler Priority Register:  SHPR1-3   

Used to set priorities of system faults.   You probably will never change this register.

Interrupt Controller Type Register (ICTR) - 

In cortex M0+ (RP2040) it's always set to all 1’s.  Don’t worry about this one.

 

NVIC REGISTERS

NVIC is a peripheral that handles manufacturer-specific exceptions; NVIC supports up to 496 exceptions; the Cortex M0/M0+ is limited to 32 external exceptions. 

Example: if your UART is throwing an exception when its buffer is full, it's utilizing the NVIC registers to inform the processor of this exception.

Interrupt Set-Enable (NVIC_ISER) and Clear-Enable (NVIC_ICER) Registers

  • NVIC_ISPR0-NVIC_ISPR15: << set pending
  • NVIC_ICPR0-NVIC_ICPR15:   << clear pending

Writing 1 to the correct register memory location will set or clear the pending state of the interrupt.  So—you can make an external interrupt pending, telling the processor to deal with it; or set an already pending to 0—telling the processor: nevermind. On Cortex M0+, this is the main way one sets external exceptions—drop bits into the above registers.

 These “Hello! CPU here: thanks for telling me about this exception, I will deal with this exception soon enough” situations are referred to as asynchronous exceptions.  

There are also synchronous exceptions (SVC is one, I read), but I am going to not worry about those right now.

 

Interrupt Active Bit Registers (NVIC_IABR)

Not implemented in M0 (RP2040).  Don’t worry about it.

 

Software Triggered Interrupt Register (STIR)  

Also not implemented on M0. Don’t worry about it.

 

Interrupt Priority Registers (NVIC_IPR): NVIC_IPR0-NVIC_IPR1-2-3:

Sets interrupt priority.

 

Software Triggered Interrupt Register (STIR)  

Also not implemented on M0. Don’t worry about it.

 

HOW ARM CHIPS DEAL WITH EXCEPTIONS FROM A C COMPILER STANDPOINT

The compiler is set up to conform to Arm Architecture Procedural Calling Standard (AAPCS).

It works like this (oversimplified?):

a)      An exception is called

b)      Caller register values are put on the stack

c)       $lr (link register) gets the EXC_RETURN value—where in the program to go after the exception is handled.

d)      After exception is handled the values are popped back off the stack, the program returns to $lr value and keeps executing. 

      This means the push and pop complexities associated with implementing an exception without clobbering the rest of your code is abstracted by the compiler. That's probably a good thing?

"TAIL CHAINING":

Say we are executing an ISR and another one comes in.  The processor will not redo the caller stack pushes and pulls, it will leave whatever is in the stack, in the stack. This can save CPU cycles.

No comments:

Post a Comment

Rotary Encoder Expermenter's Board: Improving the Hardware

Quick one this time....I have posted a few projects lately that incorporated a Raspberry Pi Pico, rotary encoder, and .96" OLED:  here ...