the ARM Cortex-M3 Processor - 7

이재하·2023년 6월 12일

The Nested Vectored Interrupt Controller and Interrupt Control

8.1 NESTED VECTORED INTERRUPT CONTROLLER OVERVIEW

As we've seen, The Nested Vectored Interrupt Controller(NVIC) is an intergated part of the Cortex-M3 processor. It is closely linked to the Cortex-M3 CPU core logic. Its control registers are accessible as memory mapped devices. Besides control reigsters and control logic cfor interrupt processing, the NVIC unit also contains control registers for the SYSTICK Timer, and debugging controls. In this chapter, we'll examine the control logic for interrupt processing. Memory Protection Unit and debugging control logic are discussed in later chapters.

The NVIC supports 1-240 external interrupt inputs(commonly known as interrupt request).

The exact number of supported interrupts is determined by the chip manufacturers when they develop their Cortex-M3 chips. In addtion, the NVIC also has a Nonmakable Interrupt(NMI) input.

The NVIC can be accessed in the System Control Space(SCS) address range, which is memory location 0xE000E000. Most of the interrupt control/status reigsters are accessible only in privileged mode, except the Software Trigger Interrupt register(STIR), which can be set up to be accessible . The interrupt contorol/Status reigsters can be accessed in word, half word, word, or byte transfer.

In addtion, a few other interrupt-masking registers are also involved in the interrupts. They are the special register and are accesssed through special registers access instructions : move sepcial register to general purpose register and move to sepcial register from general purpo

8.2 THE BASIC INTERRUPT CONFIGURATION

Each external interrupt has several registers associated with it.

  • Enable and Clear Enable reigsters
  • Set-Pending and Clear-Pending registers
  • Priority level
  • Active Stauts

In addotion, a number of other registers can also affect the interrupt processing:

Exception-masking registers(PRIMASK, FAULTMASK and BASEPRI)
Vector Table Offset register
STIR
Prioirty Group

8.2.1 Interrupt Enable and Clear Enable

The Interrupt Eable register is progmammed through two addresses. To set the enable bit, you need to write to the SETENA register address; to clear the enable bit, you need to write to the CLRENA register address. In this way, enabling or disabling an interrupt will not affect other interrupt ebale states.

8.2.2 Interrupt Set Pending and Clear Pending

If an interrupt takes place but cannot be executed immediately (for instance, if another higher-priority interrupt handler is running), it will be pended. The interrupt-pending status can be accessed through the interrupt Set Pending(SETPEND) and Interrupt Clear Pending(CLRPEND) registers.

8.2.3 Priority Levels

Each external interrupt has an associated prioirty-level register, which has a maximum width of 8bits and a minimum width of 3 bits.

Table 8.1

AddressNameTypeReset ValueDescription
0xE00E100SETENA0R/W0Enable for external interrupt #0-31
...............
0xE00E180CLRENAR/W0Clear Enable

Table 8.2 Interrupt Set Pending Registers and Interrupt Clear Pending Registers

Table 8.3 Interrupt Priority Level Registers

8.2.4 Active Status

Each external interrupt has an active satues bit. When processoer starts the interrupt handler, the bit is set to 1 and cleared when the interrupt return is executed. However, during an interrupt Service Routine excution, a higer priority interrupt might occur and casuse preemption. During this period, although the processor is executing another interrupt handler, the previous interrupt is still defined as active. The active registers are 32 bit but can also be accessed using half word or byte-size transfers.

8.2.5 PRIMASK and FAULTMASK Special Register

The PRIMASK register is used to disable all exceptions except NMI and hard fault. It effecively changes the currnet priority level to 0.

void __enable_irq(); // Clear PRIMASK
void __disable_irq(); // Set PRIMASK
void __set_PRIMASK(uint32_t priMask); // Set PRIMASK to value
uint32_t __get_PRIMASK(void); // Read the PRIMASK Value

For assemblt language users, you can change the current status of PRIMASK using Change Process State(CPS) instructions :
CPSIE I; Clear PRIMASK (Enable interrupts)
CPSID I; Set PRIMASK(Disable Interrupts)

This register is also programmable using MRS and MSR instructions.

MOV R0, #1
MSR PRIMASK, R0 ; Write 1 to PRIMASK to disable all interrupts


MOV R0, #0

MSR PRIMASK, R0 l Write 0 to PRIMASK to allow interrupts

PRIMASK is useful for temporarily disabling all interrupts for critical tasks. When PRIMASK is set, if a fault takes place, the hard fault handler will be executed.

FAULTMASK is just like PRIMASK except that it changes the effective current priority level to -1, so that even the hard fault handler is blocked.

Only the NMI can be executed when FAULT-MASK is set. It can be used by fault handlers to raise its priority to -1, so that they can have access to some features for hard fault exception( more information on this is provieded in Chapter 12 ).

8.2.6 The BASEPRI Special Register

In some cases, you might want to disable interrupts only with priority lower than a certain level. In this case, you could use the BASEPRI register.

__set_BASEPRI(0x60); // Disable interrupts with priority
// 0x60-0xFF using CMSIS

8.2.7 Configuration Registers for Other Exceptions
Usage faults, memory management faults, and bus fault exceptions are enabled by the system handler

Table 8.5 The System handler Control and State Register

BitsNameTypeReset ValueDescription
18USGFAULTENAR/W0Usage fault handler enable
17BUSFAULTENAR/W0Bus fault handler enable
16MEMFAULTENAR/W0Memory Management fault handler enable
15SVCALLPENDEDR/WSVC pended; SVC was started but was replaced by a higher-priority exception
14BUSFAULTPENDEDR/W0Bus fault pended; bus fault handler was started but was replaced by a higher-priority exception
13MEMFAULTPENDEDR/W0Memory management fault pended; memory management fault started but was replaced by a higher priority exception
12USGFAULTPENDEDR/W0Usage fault pended; usage fault started but was replaced by a higher priority exception
11SYSTICKACTR/W0Read as 1 if SYSTICK exception is active
10PENDSVACTR/W0Read as if PendSV exception is active
8MONITORACTR/W0Read as 1 debug monitor exception is active

...

8.3 EXAMPLE PROCEDURES IN SETTING UP AN INTERRUPT

For most simple applications, the application is stored in ROM and there is no change the exception handlers, we can have the whole vector table coded in the beginning of ROM in the Code region(0x00000000). This way, the vector table offset will always be 0 and the interrupt vector is already in ROM. The only steps required to set up an interrupt will be as follows :

  1. Set up the priority group setting. This step is optional. By default priority group setting zero only bit 0 of the priority level register is used for subpriority.

  2. Set up the priority level of the interrupt. This step is optional. By default, all interrupts are at priority level 0.

  3. Enable the interrupt/

Here is a simple example procedure for setting up an interrupt:

NVIC_SetPriorityGrouping(5);
NVIC_SetPriority(7, 0xC0); // Set IRQ#7

In addition, make sure that you have enough stack memory if you allow a large number of nested interrupt levels. Because exception handlers always use the Main Stack Pointer, the main stack memory should contain enough speace for the largest number of nesting interrupts.

If the interrupt handlers need to be changed at different stage of the application, we might need to relocate the vector table to Statuc Random Access Memory(SRAM), so that we can modify the exception vectors. In this case, the following extra steps would be required:

  1. When the system boots up, the priority group register might need to be set up. By default, the priority group 0 is used(bit[7:1] of priority level is the preemption level and bit[0] is the subpriority level).

  2. Copy the hard fault. NMI handlers and other required vector to a new vector table location in SRAM.

  3. Set up the Vector Table Offset register to point to the new vector table.

  4. Set up the interrupt vector for the interrupt in the new vector table.

  5. Set up the priority level for the interrupt.

  6. Enable the interrupt.

For example, this can be done in C programming with a CMSIS complicant device driver libarary, assume the starting address of the new vector table is defined as "NEW_VECT_TABLE" :

// HW_REG is a macro to convert address value to point

#define HW_REG(addr) (((volatile unsigned long)addr)))
#define NEW_VECT_TABLE 0x20008000 // An SRAM region for vector table.

NVIC_SetPriorityGrouping(5);
...
HW_REG((NEW_VEC_TABLE + 0x8)) = HW_REG(0x8); // copy NMI Vector
HW_REG((NEW_VECT_TABLE + 0xC)) = HW_REG(0xC); //
...
SCB->VTOR = NEW_VECT_TABLE; // Relocate vector table to SRAM
...
HW_REG(4*(7+16)) = (unsigned) IRQ#7 priority level to 0xC0
...
NVIC_EnableIRQ(7);

The program in assembly might be something like this:

LDR R0, = 0xE000ED0C ; Application interrupt and Reset Control Register

0개의 댓글