Skip to content

Commit 3c3f413

Browse files
Merge #15
15: Start review of exceptions r=japaric a=andre-richter Co-authored-by: Andre Richter <andre-richter@users.noreply.github.com>
2 parents 757e0c3 + c518588 commit 3c3f413

File tree

1 file changed

+38
-19
lines changed

1 file changed

+38
-19
lines changed

src/exceptions.md

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,36 @@
11
# Exception handling
22

3-
During the "Memory layout" section we decided to start out simple and leave out handling of
4-
exceptions. In this section we'll add support for handling exceptions; this serves as an example of
3+
During the "Memory layout" section, we decided to start out simple and leave out handling of
4+
exceptions. In this section, we'll add support for handling them; this serves as an example of
55
how to achieve compile time overridable behavior in stable Rust (i.e. without relying on the
66
unstable `#[linkage = "weak"]` attribute, which makes a symbol weak).
77

88
## Background information
99

1010
In a nutshell, *exceptions* are a mechanism the Cortex-M and other architectures provide to let
11-
applications respond to asynchronous, usually external, events. The exception mechanism works like
12-
this: when the processor receives a signal or event associated to a type of exception it suspends
11+
applications respond to asynchronous, usually external, events. The most prominent type of exception,
12+
that most people will know, is the classical (hardware) interrupt.
13+
14+
The Cortex-M exception mechanism works like this:
15+
When the processor receives a signal or event associated to a type of exception, it suspends
1316
the execution of the current subroutine (by stashing the state in the call stack) and then proceeds
1417
to execute the corresponding exception handler, another subroutine, in a new stack frame. After
15-
finishing the execution of the exception handler (i.e. returning from it) the processor resumes the
18+
finishing the execution of the exception handler (i.e. returning from it), the processor resumes the
1619
execution of the suspended subroutine.
1720

1821
The processor uses the vector table to decide what handler to execute. Each entry in the table
1922
contains a pointer to a handler, and each entry corresponds to a different exception type. For
2023
example, the second entry is the reset handler, the third entry is the NMI (Non Maskable Interrupt)
2124
handler, and so on.
2225

23-
As mentioned before the processor expects the vector table to be at some specific location and each
24-
entry in it can potentially be used by the processor at runtime so they must always contain valid
25-
values. Furthermore, we want the `rt` crate to be flexible so the end user can customize the
26-
behavior of each exception handler. Finally, the vector table is read only memory, or rather in not
26+
As mentioned before, the processor expects the vector table to be at some specific location in memory,
27+
and each entry in it can potentially be used by the processor at runtime. Hence, the entries must always
28+
contain valid values. Furthermore, we want the `rt` crate to be flexible so the end user can customize the
29+
behavior of each exception handler. Finally, the vector table resides in read only memory, or rather in not
2730
easily modified memory, so the user has to register the handler statically, rather than at runtime.
2831

29-
To satisfy all these constraints we'll assign a *default* value to all the entries of the vector
30-
table in the `rt` crate but make these values kind of *weak* to let the end user override them
32+
To satisfy all these constraints, we'll assign a *default* value to all the entries of the vector
33+
table in the `rt` crate, but make these values kind of *weak* to let the end user override them
3134
at compile time.
3235

3336
## Rust side
@@ -36,8 +39,8 @@ Let's see how all this can be implemented. For simplicity, we'll only work with
3639
of the vector table; these entries are not device specific so they have the same function on any
3740
kind of Cortex-M microcontroller.
3841

39-
The first thing we'll do is create an array of vectors (pointers to exception handlers) in the Rust
40-
code:
42+
The first thing we'll do is create an array of vectors (pointers to exception handlers) in the
43+
`rt` crate's code:
4144

4245
``` rust
4346
pub union Vector {
@@ -83,7 +86,7 @@ should be assigned the value `0` so we use a union to do exactly that. The entri
8386
to a handler make use of *external* functions; this is important because it lets the end user
8487
*provide* the actual function definition.
8588

86-
Next we define a default exception handler in the Rust code. Exceptions that have not been assigned
89+
Next, we define a default exception handler in the Rust code. Exceptions that have not been assigned
8790
a handler by the end user will make use of this default handler.
8891

8992
``` rust
@@ -130,8 +133,9 @@ PROVIDE(PendSV = DefaultExceptionHandler);
130133
PROVIDE(SysTick = DefaultExceptionHandler);
131134
```
132135

133-
`PROVIDE` only takes effect when symbol on the RHS is still undefined after inspecting all the input
134-
object files. This is the scenario where the user didn't assign a handler to the exception.
136+
`PROVIDE` only takes effect when the symbol to the left of the equal sign is still undefined after
137+
inspecting all the input object files. This is the scenario where the user didn't implement the
138+
handler for the respective exception.
135139

136140
## Testing it
137141

@@ -214,9 +218,24 @@ Contents of section .vector_table:
214218
0030 00000000 00000000 79000000 79000000 ........y...y...
215219
```
216220

221+
The vector table now resembles the results of all the code snippets in this book so far. To summarize:
222+
- In the [_Inspecting it_] section of the earlier memory chapter, we learned that:
223+
- The first entry in the vector table contains the initial value of the stack pointer.
224+
- Objdump prints in `little endian` format, so the stack starts at `0x2001_0000`.
225+
- The second entry points to address `0x0000_0041`, the Reset handler.
226+
- The address of the Reset handler can be seen in the disassembly above, being `0x40`.
227+
- The first bit being set to 1 does not alter the address due to alignment requirements. Instead, it causes the function to be executed in _thumb mode_.
228+
- Afterwards, a pattern of addresses alternating between `0x79` and `0x00` is visible.
229+
- Looking at the disassembly above, it is clear that `0x79` refers to the `DefaultExceptionHandler` (`0x78` executed in thumb mode).
230+
- Cross referencing the pattern to the vector table that was set up earlier in this chapter (see the definition of `pub static EXCEPTIONS`) with [the vector table layout for the Cortex-M], it is clear that the address of the `DefaultExceptionHandler` is present each time a respective handler entry is present in the table.
231+
- In turn, it is also visibile that the layout of the vector table data structure in the Rust code is aligned with all the reserved slots in the Cortex-M vector table. Hence, all reserved slots are correctly set to a value of zero.
232+
233+
[_Inspecting it_]: https://rust-embedded.github.io/embedonomicon/memory-layout.html#inspecting-it
234+
[the vector table layout for the Cortex-M]: https://developer.arm.com/docs/dui0552/latest/the-cortex-m3-processor/exception-model/vector-table
235+
217236
## Overriding a handler
218237

219-
To override an exception handler the user has to provide a function whose symbol name exactly
238+
To override an exception handler, the user has to provide a function whose symbol name exactly
220239
matches the name we used in `EXCEPTIONS`.
221240

222241
``` rust
@@ -262,9 +281,9 @@ Process 1 stopped
262281
The program now executes the user defined `HardFault` function instead of the
263282
`DefaultExceptionHandler` in the `rt` crate.
264283

265-
Like our first attempt at a `main` interface this first implementation has the problem of having no
284+
Like our first attempt at a `main` interface, this first implementation has the problem of having no
266285
type safety. It's also easy to mistype the name of the exception, but that doesn't produce an error
267-
or warning instead the user defined handler is simply ignored. Those problems can be fixed using a
286+
or warning. Instead the user defined handler is simply ignored. Those problems can be fixed using a
268287
macro like the [`exception!`] macro defined in `cortex-m-rt`.
269288

270289
[`exception!`]: https://github.com/japaric/cortex-m-rt/blob/v0.5.1/src/lib.rs#L79

0 commit comments

Comments
 (0)