(Übertragung dieses Textes von der originalen angegebenen Website nach Restructured Text von H. Högl <Hubert.Hoegl@hs-augsburg.de>, Oktober 2020)

URL: http://hhoegl.informatik.hs-augsburg.de/es2/exceptions/bertolotti/

Inhalt

1   Cortex-M Exception Handling (Part 1)

Ivan Cibrario Bertolotti, November 28, 2015

https://www.embeddedrelated.com/showarticle/878.php

This article describes how Cortex-M processors handle interrupts and, more generally, exceptions, a concept that plays a central role in the design and implementation of most embedded systems. The main reason of discussing this topic in detail is that, in the past few years, the degree of sophistication (and complexity) of microcontrollers in handling interrupts steadily increased, bringing them on a par with general-purpose processors.

This first part of the article focuses on general information, and then on what happens before an exception request is accepted by the processor and its handling begins. The second part of the article will discuss instead the Cortex-M behavior after an exception request is accepted and provide additional details.

1.1   Overview

Like it happens in many other processors, the Cortex-M also handles other kinds of events, such as faults, in the same way as interrupts, that is, in a unified way. All these events are collectively referred to as exceptions. For this reason, in the following we will, more generally, talk about exception rather than interrupt handling.

To get started, let us examine more in detail what exceptions are, and describe their purpose and origin. A property that all exceptions have in common is that their occurrence may disrupt the normal instruction execution flow and direct the processor to execute a fragment of code associated with them, called exception handler.

In the statement above we say “may” because the mere occurrence of an exception is necessary, but not sufficient, to ensure that the processor will handle it immediately, if at all. In fact, as it will become clearer in the following, there is a long way between the origin, or source, of the exception — which is often hardware-related — and the first action visible to software, that is, the execution of its handler. The main categories of exceptions are described in the following.

1.1.1   Interrupt requests

This kind of exception is raised by a peripheral device, in order to signal to the processor, and eventually to the software, the occurrence of an event of interest concerning the device itself. For instance, an Ethernet controller may use an interrupt request to indicate that one or more network frames have been received.

On Cortex-M processors, interrupt requests can also be issued by means of a software action. In this case, they are often referred to as software interrupts. Regardless of their origin (hardware or software) interrupt requests are all handled in the same way and no further distinction will be made on this aspect.

Like ordinary interrupt requests, also Non-Maskable Interrupt (NMI) requests can be issued by either hardware or software. The main difference is that their priority is extremely high, namely, the highest in the system below the reset exception. The concept of exception priority plays a central role in Cortex-M exception handling and will be examined in detail in the following.

For the time being, it is enough to say that a higher exception priority ensures that the handling latency — that is, the time elapsed between the occurrence of an exception and the execution of its interrupt handler — decreases because the processor handles this exception in preference of others.

Also belonging to this category are two more exceptions that are generated within the processor itself, rather than coming from external peripheral devices. They are:

  • The SysTick exception, generated periodically by the 24-bit count-down system timer and often used by operating systems to drive time slicing. If needed, the same exception can also be issued by software.
  • The PendSV exception, which can only be triggered by software. It is often used by operating systems to indicate that a context switch is due and perform it at a future time, when no other exceptions must be handled.

1.1.2   Faults

Generally speaking, all faults are generated as a consequence of an abnormal event detected by the processor, either internally or while communicating with memory and other devices. These events are of great interest (and concern) to software because most often they indicate serious hardware or software issues that likely prevent the software itself to continue with normal activities. More specifically, the following kinds of fault are foreseen in Cortex-M processors.

  • UsageFault. This fault occurs when an instruction cannot be executed for various reasons. For instance, the instruction may be undefined or may contain a misaligned address that prevents it from accessing memory correctly. For divide instructions, another reason for raising a UsageFault is an attempt to divide by zero.

    Some of the above-mentioned fault sources (like divide by zero) can be masked in software, that is, the processor can be instructed to just ignore them without generating any fault, whereas others (such as encountering an undefined instruction) cannot, for obvious reasons.

  • BusFault. This fault is generated when an error occurs on the data or instruction bus while accessing memory. In other words, it can be generated as a consequence of an explicit memory access performed by an instruction during its execution, and also by fetching an instruction from memory. On the other hand, this fault does not cover errors generated by the memory protection mechanisms, which instead trigger a MemManage fault, to be discussed next.

    It should also be noted that, being the Cortex-M a memory-mapped input-output (I/O) architecture, whenever we refer to a “memory” address here and in the following, we actually mean an address within the processor’s address space that may refer to either a memory location or an I/O register.

  • MemManage. This fault occurs when a memory access is blocked by the memory protection mechanism. An optional Memory Protection Unit (MPU) provides a programmable way of protecting memory regions against data read and write operations, as well as instruction fetches, also depending on the current privilege mode of the processor.

  • Even on processors not equipped with a MMU, hardware may still set forth some predefined, and non-programmable, constraints on memory accesses. For instance, the processor forbids instruction fetch operations from address space areas that contain I/O registers.

A special kind of fault is HardFault. It can be generated for two different reasons. First of all, the processor generates a HardFault when an error occurs during exception processing, thus disrupting the normal exception handling flow.

The second reason why a HardFault can be generated is a mechanism known as fault escalation. Under certain conditions this mechanism may transform, or escalate, some other exceptions into a HardFault. More details, as well as a few examples of how this mechanism works, will be given in the second part of this article.

1.1.3   Supervisor call (SVC)

This exception is raised by the execution of a SVC assembly instruction. It is commonly used as a convenient way to enter the operating system kernel and request it to perform a function on behalf of the application.

1.1.4   Reset

This exception is triggered by the processor power-up sequence or a warm reset. It is handled as other exceptions for the most part, except that instruction execution can stop at an arbitrary point. As it will be better explained in the second part of this article, a few other aspects are different, too, for instance the processor execution mode.

1.2   Exceptions Priority in the Cortex-M

Long gone are the days in which, when using a typical microcontroller, accepting and handling an exception was an “all or nothing” affair mainly depending, in the case of interrupts, on whether or not they were enabled. As shown in the figure below, in the Cortex-M the decision of whether or not an incoming exception request should be accepted and become active, that is, whether or not the processor immediately starts handling it, depends on a number of rather complex factors.

cortex_m_priority_logic_2.png

More specifically:

  • On one hand, each kind of exception (listed on the left of the figure) has its own exception priority value. Somewhat counterintuitively, numerically lower numbers correspond to higher priorities. Priority values can be either fixed or programmable. For instance, the priority of a NMI request is always −2, whereas for interrupt requests the priority can be set to any non-negative value between 0 and 255 included.

    Programmable priority values are denoted as E in the figure and it should be noted that, as further explained later, depending on the processor implementation, only some high-order bits of these values may be significant.

    As also shown in the figure, when there are multiple, simultaneous incoming exceptions, the one with the numerically lowest value (corresponding to the highest priority, as stated above) prevails on the others and determines the incoming exception priority IP.

    When there are two or more simultaneous, incoming exceptions with the same priority, the one with the numerically lowest exception number, a unique and fixed number assigned by the processor to each exception, takes precedence.

  • On the other hand, the processor keeps track of its current execution priority EP. As depicted on the right part of the figure, the execution priority is the minimum of several values, namely:

    • The base level of execution priority, which is one more than the highest priority value supported by the processor, 256 in this case.
    • The minimum priority value A among all exceptions that became active in the past and whose handling has not been completed yet, if any.
    • The value of the 8-bit register BASEPRI, if it is not zero. BASEPRI can only be set to non-negative values.
    • The values 0 and/or −1, depending on whether or not the 1-bit registers PRIMASK and/or FAULTMASK are set, respectively.

The values of IP and EP are then compared to determine if the incoming exception must become active or it must stay pending. In particular, the incoming exception becomes active if IP < EP (strictly) and stays pending otherwise. Afterwards:

  • When an incoming exception becomes active, the processor starts handling it immediately, through a mechanism further detailed in the second part of this article. As a consequence, if the processor was already handling an exception, the handling of the new exception is nested into the old one. In other words, the processor temporarily stops handling the old exception in favor of the new one, and will go back to it at a later time.
  • Otherwise, the incoming exception stays pending. The processor will evaluate the possibility of making it active whenever EP changes, possibly becoming numerically higher than it was. The evaluation is still carried out as described above, also incorporating into IP the priority of any further exception request that arrived in the meantime. For instance, re-evaluation takes place when the handling of an active exception terminates, because this may increase the value of A and, in turn, of EP.

Stating again the same concept more informally, programmers can set BASEPRI, PRIMASK and/or FAULTMASK to postpone handling of some exceptions. For instance, setting BASEPRI to a non-zero, positive value T prevents the processor from accepting any exceptions whose priority value IP is above that threshold, that is, IP ≥ T, unless priority escalation takes place.

Priority escalation, which will also be one of the subjects of the second part of this article, is a mechanism that may raise the priority of some faults to the same priority of HardFault. This is indicated by the dashed arrows in the figure. Since the HardFault priority is fixed to −1, no settings of BASEPRI can have any effect on its acceptance.

In extreme cases, it is also possible that that processor encounters an unrecoverable exception. Generally speaking, this takes place when an exception occurs while the processor is already handling another exception, priority escalation cannot be applied, and the processor is neither able to continue execution of the current exception handler, nor start handling the new exception. As a consequence, the processor enters a special lockup state and stops normal instruction execution completely.

For the sake of completeness, even though a thorough discussion of this aspect is beyond the scope of this article, we must also mention that the comparison between IP and EP is possibly affected by a processor feature known as priority grouping.

In short, it is possible to instruct the processor to “mask off” some low-order bits of the priority value so that priority levels that are distinct in principle “look the same” in the comparison. In other words, neglecting some low-order bits of the priority value groups together exceptions whose priorities may differ in those low-order bits, but are the same in the others.

As a result, handlers corresponding to exceptions with different priorities, but belonging to the same group, will not be nested into each other.

1.3   Gathering further documentation

At the time of this writing, the Cortex-M family of processors comprises several members, namely, the Cortex-M0, M0+, M1, M3, M4, and M7. As can be expected, the main differences among them are in the area of processing power, namely clock speed and instruction set capabilities.

As such, gathering information about a specific member of the family implies perusing more than one manual or data sheet. To further complicate the matter, it is well known that ARM itself does not physically make chips, and leaves to chip makers some freedom to configure several features of Cortex-M processors for a specific implementation.

In increasing order of specificity, the main sources of information to gain a thorough knowledge about the Cortex-M family are:

  • The official ARM documentation, which is the most comprehensive and authoritative source of information about the Cortex-M family as a whole, because it comes directly from the designers of the processor itself. Depending on the processor, the architecture is specified in:

    • Version 6 of the ARM architecture, Microcontroller profile (ARMv6-M), described in the ARMv6-M Architecture Reference Manual (Cortex-M0, M0+, and M1).
    • Version 7 of the ARM architecture, Microcontroller profile (either ARMv7E-M or ARMv7-M, depending on whether or not Digital Signal Processing (DSP) instructions are implemented), presented in the ARMv7-M Architecture Reference Manual (Cortex-M3, M4, and M7).
  • Further information about specific members of the family are available in other manuals, still published by ARM. For instance, in-depth information about the Cortex-M3 are provided in:

    • The Cortex-M3 Devices Generic User Guide. It is much shorter than the Architecture Reference Manuals referenced above and focuses only on the specific features of the Cortex-M3, with respect to the Cortex-M family as a whole.
    • The Cortex-M3 Technical Reference Manual. This manual contains the ultimate technical information about a specific revision of the Cortex-M3 core. Moreover, it contains details about some processor features not covered by the Generic User Guide, for instance, debugging-related components, or macrocells.
  • Last, but not least, it is important to distinguish between a Cortex-M processor (as supplied by ARM) and a Cortex-M device (as implemented by a specific vendor on a specific chip it produces). The distinction is important because several choices about how the processor will “look like” are made at implementation time.

    These choices are called implementation options in the Generic User Guide and configuration options in the Technical Reference Manual. Configuration options mainly control which optional processor components are available in a specific implementation, whereas implementation options are related to finer, inner details of the processor itself, for instance, how many interrupt lines, or sources, it is able to handle.

    Unlike the previous ones, device-related manuals are published by the chip vendor rather than ARM. For the sake of example, all details about how the Cortex-M3 processor has been implemented in the NXP Semiconductors LPC176x range of microcontroller are available in the LPC176x/5x User Manual.

    Further information from the chip vendor is usually made available in the Product Data Sheet, like this one, which applies to the LPC1763 through LPC1769. However, its main focus is on packaging and electrical characteristics of the chip. For this reason, data sheets are of most interest to hardware designers rather than software developers.

In order to better explain how and why all these sources of information are important to gain a thorough (and correct) understanding about how a certain implementation of a member of the Cortex-M processor family works, let us consider a specific example, concerning how many bits are significant in an exception priority value.

There is no need to highlight how important this information is because it determines the ability of the processor to “tell apart” two distinct priority values we may use in our code and differ only in a few low-order bits. In turn, this deeply affects several aspects of exception handling, like nesting.

Considering the LPC1768 microcontroller, incorporating a Cortex-M3 processor, about this topic:

  • The ARMv7-M Architecture Reference Manual (which applies to the Cortex-M3) says that the number of priorities is an implementation-defined power of two in the range from 8 to 256. In any case, priority values are always represented using 8-bit fields, of which only high-order bits may be significant.
  • Both the Generic User Guide and the Technical Reference Manual of the Cortex-M3 repeat the same concept. Moreover, the Generic User Guide remarks that the number of significant priority bits is an implementation option.
  • Eventually, the LPC17xx User Manual states that the LPC1768 implementation of the Cortex-M3 supports 32 distinct priority levels, that is, the 5 high-order bits of 8-bit priority fields are significant. The others are ignored on writes and read as zero.
  • For the sake of completeness, the Product Data Sheet also mentions, in passing, that 32 interrupt priority levels are available for use.

It is also worth mentioning that, recently, additional high-quality literature about Cortex-M processors became available. For instance, this book (The Definitive Guide to ARM® CORTEX®-M3 and CORTEX®-M4 Processors by Joseph Yiu) is a thorough and insightful description of the Cortex-M3 and M4. Although it is not official ARM documentation, it is nonetheless a very authoritative guide because it has been written by one of the engineers who helped develop those processors.

Moreover, instead of focusing only on the processor itself, it also contains information about other relevant topics, like software development toolchains and operating system support.

2   Cortex-M Exception Handling (Part 2)

Ivan Cibrario Bertolotti, February 1, 2016

https://www.embeddedrelated.com/showarticle/912.php

The first part of this article described the conditions for an exception request to be accepted by a Cortex-M processor, mainly concerning the relationship of its priority with respect to the current execution priority. This part will describe instead what happens after an exception request is accepted and becomes active.

2.1   Processor Operation and Privilege Mode

Before discussing in detail the sequence of actions that occurs within the processor after an exception request has been accepted it is necessary to delve deeper into the concept of processor execution mode.

From this point of view the Cortex-M processor’s approach is fortunately much simpler than the ones of its predecessors. For instance, the ARM7TDMI processor, once very popular as a microcontroller’s building block and designed according to version 5 of the ARM architecture, has seven distinct modes of operation (of which two, interrupt and fast interrupt, are for interrupt handling). By contrast, Cortex-M processors only have two modes in total, called thread and handler mode.

Another point worth mentioning is that Cortex-M processors implement two distinct stack pointers, called Main Stack Pointer (MSP) and Process Stack Pointer (PSP) and referring to distinct stacks in memory. At any given time, the processor makes use of one of them, and the choice also depends on the execution mode.

Knowing exactly which stack pointer is in use is extremely important for embedded software developers, because stacks hold critical data structures, such as function arguments and return values. Moreover, when the system supports the concurrent execution of multiple tasks, proper stack management (which is a per-task data structure) is crucial to avoid memory corruption and ensure that the system operates correctly.

2.1.1   Thread mode

Thread mode is the normal task execution mode and—as an exception to the general rule to be discussed later—is also the mode entered by the processor when it accepts a reset exception. From the point of view of execution privilege, thread mode can be either unprivileged or privileged, depending on how the nPRIV bit of the CONTROL register is set.

Execution privilege determines the extent to which the currently executing code is allowed to access and make use of system resources, namely, some critically-important instructions, registers, and memory areas. For instance, the CONTROL register itself can be written only by privileged code. This makes sense, otherwise non-privileged code could easily raise its privilege level in an uncontrollable (and even undetectable) way by overwriting the current contents of CONTROL with a more favorable value.

Instead, only the processor hardware itself—according to mechanisms we will more thoroughly describe in the following—as well as privileged code are allowed to modify CONTROL. When this feature is accompanied by a suitable memory protection mechanism (like the MPU briefly outlined in the first part of this article), it becomes possible to keep a clear and unbreakable separation between code meant to be unprivileged (untrusted application-level code) and privileged code with full access to the system (trusted components, usually part of the operating system).

Another bit of the CONTROL register—namely, the SPSEL bit—decides whether the processor uses the MSP or the PSP as stack pointer when in thread mode.

2.1.2   Handler mode

As its name says, this mode is used by the processor to execute all exception handlers except—as noted above—the reset handler. Code executed in handler mode is inherently privileged and always makes use of the MSP, regardless of the setting of the CONTROL register.

As described in more details in the following, transitions from thread to handler mode take place automatically when the processor accepts an exception request, making it become active, while it is executing in thread mode. The reverse transition, back to thread mode, occurs when an exception handler returns, there are no other active exceptions, and exception handling interrupted the processor while it was executing in thread mode.

A peculiar case of mode transition happens when unprivileged code running in thread mode executes a SVC assembly instruction that, as described in the first part of this article, triggers an exception request unconditionally. The exception is accepted synchronously with respect to the current instruction flow and grants unprivileged code controlled access to privileged execution mode, by means of trusted software routines usually implemented within the operating system.

2.2   What happens when an exception becomes active?

The main phases that make up the exception handling entry sequence—that is, the sequence of actions performed by the processor to start handling an exception—are depicted in the figure that follows and then discussed in detail. The numbers in the figure correspond to the ones in the explanation.

cortex_m_exception_entry_2.png
  1. The first action performed by the processor is to save part of the current execution context. This information is stored on the current stack, that is, the stack that the processor is using when the exception request is accepted. The minimum amount of information that is saved into a basic stack frame consists of registers R0 through R3, R12, the link register LR (also accessible as R14), the program counter PC (R15), and the program status register xPSR, for a total of 32 bytes.

    When the processor implements the optional floating-point extension, the context of the floating-point unit is saved as well, into an extended stack frame. This part of the context may either be saved immediately during the entry sequence, as described above, or be postponed as much as possible, until when the context is about to be modified by the execution of a floating-point instruction. This is because the floating-point context is rather large and requires 68 additional bytes.

    More information about this form of lazy context save and restore technique—which is also widespread, with minor variations, in other families of processors—is left as a topic of a future post. Instead, in the following, we will focus only on how processors without floating-point extension behave.

    In both cases, depending on the value of the STKALIGN bit of the Configuration and Control Register CCR, the processor may adjusts the stack pointer to make sure that the saved stack frame is aligned to a multiple of 8 bytes.

    The reason behind saving the execution context is that accepting and handling an exception shall not necessarily prevent the processor from going back to its current activity at a later time. This is particularly true for interrupts and other exception requests that occur asynchronously with respect to current processor activities and are most often totally unrelated to them. Thus, they shall be handled transparently with respect to any code that happens to be executing when they arrive.

    On the other hand, the choice of which part of the context is saved is motivated by the goal of making the resulting stack frame layout compliant with the ARM Architecture Procedure Calling Standard (AAPCS). In this way, any AAPCS-compliant function can be used as an exception handler. This is especially important when exception handlers are written in a high-level language because compilers are able to generate AAPCS-compliant code by default, and hence, they can also generate exception handling code without treating it as a special case.

    In other words, the processor hardware saves the context on the stack exactly like an AAPCS-complaint software procedure does when it is about to call another. As a result, an exception handler call performed by hardware is indistinguishable from a regular software-managed function call.

  2. Set the link register LR to an appropriate exception return value (called EXC_RETURN value in the ARM documentation). As it will be better discussed in the following, when an exception return value is loaded into the program counter PC, as part of a function epilogue, it directs the processor to initiate an exception handler return sequence instead of simply returning to the caller.

    In fact, the AAPCS stipulates that a function call must save into the link register LR the return address, before setting the program counter PC to the function entry point. This is typically accomplished by executing a branch and link instruction BL with a PC-relative target address. It is worth mentioning that, when the processor supports multiple instruction sets, bit 0 of LR assumes a special meaning and indicates the instruction set in use at the time of the call. However, this is not the case in Cortex-M processors, which only support the Thumb-2 instruction set.

    In the epilogue of the called function, it is then possible to return to the caller by storing back into PC the value stored into LR at the time of the call. This can be done, for instance, by means of a branch and exchange instruction BX, using LR as argument. Hence, once again this aspect of the exception entry sequence has been architected to permit any AAPCS-compliant function to be used directly as an exception handler. The information provided by the EXC_RETURN value allows the processor to locate the stack frame to be restored, interpret it in the right way, and bring back the processor to the execution mode in effect when the exception was accepted. To this purpose, as shown in Table 1, the 5 low-order bits of the EXC_RETURN value encode:

    • whether the processor is using the main (MSP) or process stack pointer (PSP);
    • whether the saved stack frame is basic (not including room for the floating-point context) or extended (including it);
    • the current processor execution mode (thread or handler mode).

    The 4 higher-order bits of EXC_RETURN must be set to 0xF. They specify that the value being loaded into PC is not a regular memory address, but it is indeed an EXC_RETURN code that the processor must handle specially.

    The remaining bits, namely, bits 27 down to 5, are unused at this time. They all read as one currently, but software must preserve their value when writing. It should be noted that the processor analyzes the value being loaded into PC and possibly interprets it as a EXC_RETURN value only in specific cases, better detailed in the Cortex-M architectural documentation and associated with exception handler epilogues.

    In other cases, for instance when PC is loaded while the processor is in thread mode (and hence, no exception handler can possibly be active), the value is taken literally, as a memory address. To avoid improper behavior if an EXC_RETURN value is mistakenly loaded into PC in these cases, the hardware protects address range 0xF0000000–0xFFFFFFFF against instruction execution.

  3. Possibly switch to handler execution mode, if the processor was executing in thread mode when the exception was accepted. A notable deviation from this general rule is the reset exception, which is handled in thread mode instead. Associated to the execution mode switch, the processor may also transition to use a new stack. In fact, as mentioned previously, handler mode execution always makes use of MSP, whereas thread mode execution may use either MSP or PSP, depending on processor configuration. Upon reset, execution starts in thread mode and the processor is automatically configured to use MSP.

    Additional operations performed by the processor, neither shown in the figure above, nor further discussed here, include storing the exception number of the exception just accepted in the IPSR sub-register—that is part of the xPSR register—and updating several System Control Space (SCS) registers to reflect exception acceptance. Yet another action is that accepting an exception clears the local per-core state of any pending synchronization instructions, namely, LDREX and STREX. Therefore, any synchronization procedure using those instruction that was pending upon exception entry will need to be repeated after execution resumes. This topic is extremely important for proper inter-core synchronization in multicore systems.

  4. The very last action performed by the processor upon exception entry is to retrieve the target PC—that is, the entry point of the exception handler—from the vector table and jump to it.

    The vector table is a very simple, memory-resident data structure and consists of an array of 32-bit integers, holding memory addresses called vectors. More specifically, the i-th entry of the table holds the entry point of the handler for exception number i. No ambiguity can arise from this assignment because exception numbers are fixed and, unlike priorities, uniquely identify each exception.

    Only the first 16 exception numbers are explicitly defined by the architecture specification. Also, the total number of vectors is not fixed and depends on the number of exceptions supported by specific members of the Cortex-M family, as well as configuration and implementation options. The very first entry (at index 0) is used in a special way because, in fact, no exception is ever assigned exception number zero. Instead, this entry contains the initial value loaded into MSP upon reset.

    The starting address of the vector table is held in a register called Vector Table Offset Register (VTOR). The 7 low-order bits of VTOR are reserved and are always interpreted as zero, because the minimum alignment of the vector table in memory is 128 bytes. In addition, further alignment constraints may come into effect, depending on the total number of entries in the table.

    Unlike, for instance, the previously mentioned CONTROL register—which is tightly coupled to the processor and is directly accessible to it by means of the specialized instructions MRS and MSR—VTOR is a memory-mapped register and the processor gets access to it through regular memory load and store instructions, like LDR and STR.

    For this reason, to protect VTOR against unauthorized write accesses (for instance, write attempts made by unprivileged code) it is necessary to enable a suitable memory protection mechanism, mentioned in the first part of this article, and configure it appropriately. Alternatively some processors, like the Cortex-M3, which do not implement those programmable mechanisms, may still support non-programmable protection, and prevent uncontrolled access to VTOR, as well as other critical memory-mapped registers, from unprivileged code.

    Another point worth mentioning is that the VTOR register is reset to zero when the processor accepts a reset exception. As a consequence, the initial values of PC and MSP upon reset are not retrieved from the current vector table, that is, the one in effect when the reset exception was accepted, but from the vector table at address zero.

    It should also be noted that, depending on the specific device, the physical address of the vector table may be further affected by address remapping, external to the processor. In those cases, it is necessary to refer to the device—rather than the processor—documentation to ascertain which registers control the mapping and how.

    For instance, in the the NXP LPC17xx microcontroller family it is possible to map at address 0x00000000 (where the vector table begins by default) the bootstrap ROM (which is normally accessible at physical address 0x1FFF0000) instead of the on-chip flash memory (which actually resides at address 0x00000000). Remapping is controlled by bit 0 of the device-specific MEMMAP register.

Table 1: Encoding of the EXC_RETURN Value

table1.png

2.3   Exception Return

As stated previously, the processor starts an exception return sequence when a special value—characterized by the pattern 0xF in the 4 higher-order bits and called EXC_RETURN in the following—is loaded into the PC at the end of an exception handler.

Informally speaking, in these cases, instead of simply overwriting the old contents of the PC and jump to the new address, the processor “undoes” the exception handler activation steps described previously in order to transparently resume the activity it was performing when the exception became active. More specifically:

  1. First of all, the processor examines and interprets the EXC_RETURN value according to Table 1 above. More specifically, the value determines which stack pointer (MSP or PSP) will be used to restore the processor context in the next steps, the struc- ture and contents of the stack frame to be restored (basic or extended), and the processor mode (handler or thread) after restoration.

  2. Then, the processor performs several integrity checks to ensure that returning from an exception is legal considering the current execution context. Any failed check raises a UsageFault exception, which is then handled as usual.

    For example, the exception currently being handled, whose number has been recorded in IPSR upon exception entry, must be active in order to legitimately return from it. Furthermore, the processor must currently be executing in handler mode and, if it is about to return to thread execution mode, the value to be restored into IPSR must be zero, thus indicating that no exceptions are active any more. A thorough discussion of all checks is beyond the scope of this book, and interested readers should refer to the Cortex-M architectural documentation for further details.

  3. Finally, the processor restores the context stored in the stack frame indicated by the stack pointer identified in the previous step. Among other things, the context includes the exception number being handled when the current exception was accepted in the IPSR sub-register of xPSR, and the PC where the exception being concluded was accepted.

    Therefore, a direct consequence of context restoration is that the processor resumes execution from where is was previously suspended and IPSR contains the exception number of the exception whose handling is being resumed, if any. If exceptions were not nested, IPSR is restored to zero instead.

[END]