1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
//! # Handling an interrupt
//!
//! Three independent gates all have to be open before a handler runs: the
//! CPU-level mask ([`enable_irq`]), the source
//! routed through the interrupt controller (`lic::Lic`), and the
//! peripheral itself configured to raise it.
//!
//! Dispatch is then the application's job, not this crate's. Under the
//! `rt` feature the exception vector table saves the caller-saved
//! registers and branches to one symbol, `__irq_handler`, which the
//! application defines as a plain `extern "C"` function:
//!
//! ```ignore
//! #[no_mangle]
//! pub extern "C" fn __irq_handler() {
//! let lic = Lic::new(unsafe { pac::Peripherals::steal() }.LIC);
//!
//! if lic.is_gpio_pending(BUTTON) {
//! let mut button =
//! unsafe { Pin::<BUTTON, Input>::assume_mode(pac::Peripherals::steal().GPIO) };
//! // Clearing the source is what ends this interrupt. Everything
//! // else -- reading the pin, driving an LED, waking a task --
//! // is the application's business.
//! button.clear_interrupt();
//! }
//! }
//! ```
//!
//! There is no registration call and no table to fill in: a source is
//! live once the interrupt controller routes it, and every live source
//! arrives at this one function, which tests each `is_*_pending` in turn.
//! See `examples/gpio_irq_button.rs` for the whole program, and
//! `examples/uart_rx_irq_echo.rs` for two sources dispatched from one
//! handler.
//!
//! ## Both obligations have the same symptom
//!
//! An interrupt that fires and is never cleared is still asserted when
//! the handler returns, so the core takes it again immediately, forever.
//! The program stops making progress without panicking, without faulting,
//! and without any output — it looks like a hang in whatever ran last,
//! several steps away from the real cause. Two mistakes lead there:
//!
//! - **Not defining `__irq_handler` at all.** The vector table's branch
//! target is a *weak* no-op, so the link succeeds and the program runs
//! normally right up until the first interrupt. The strong definition
//! also has to end up in the final binary: a `#[no_mangle]` function in
//! a library the binary never references may not be linked in at all,
//! which leaves the weak stub in place and behaves identically to having
//! written no handler.
//! - **Returning without clearing the source.** Including the case where
//! the handler checks the wrong pending bit, or handles one source and
//! returns while a second is still asserted. Where clearing means
//! something other than a write-1-to-clear, the driver says so — the ARM
//! generic timer, for instance, acks a fired tick by moving its
//! comparator forward rather than by writing a status bit.
//!
//! So when an IRQ-driven program hangs, suspect the handler before
//! anything the code was doing when it stopped.
//!
//! ## Async drivers
//!
//! The `async` feature's futures are woken from the same handler, by
//! calling the driver's `on_irq` (`gpio::on_irq`, `uart::on_irq`) instead
//! of clearing the source inline — those functions clear it and wake the
//! stored waker. An executor doesn't change the contract; it just means
//! the handler's job is usually one call. Drivers outside this crate that
//! need an interrupt expose their own equivalent for the same reason (the
//! `rpi-hal-embassy` crate's time driver is one).
//!
//! ## BCM2711
//!
//! There is no interrupt controller under the `bcm2711` feature yet — the
//! legacy controller this crate wraps isn't the one that chip has, so the
//! `lic` module is compiled out there. This module still builds, since the
//! CPU mask and the vector table are chip-independent, but with no way to
//! route a source there is also no way to make an interrupt fire: the
//! second of the three gates above has nothing behind it until GIC-400
//! support lands.
// The exception vector table is architecture-specific: vectors.s on
// AArch32 (8-entry, VBAR), vectors64.s on AArch64 (16-entry, VBAR_EL1).
global_asm!;
// The same table on ARMv6 -- every instruction in it exists there, but
// the `wfe` in its default fault handler is an ARMv6K one, and the
// assembler defaults to plain ARMv6 for `armv6-none-eabi` and rejects
// it. The directive is prepended here rather than written into
// vectors.s, which ARMv7-A builds share and where it would *narrow*
// what assembles.
global_asm!;
global_asm!;
// `__unhandled_exception`, the symbol every faulting slot of the table
// above branches to. Exactly one definition is compiled in: this crate's
// reporting one under `fault-report`, or a weak do-nothing otherwise.
//
// One or the other, never both. They are the same symbol, and both would
// land in this crate's single stream of `global_asm!` -- where a `.weak`
// and a `.global` definition are a duplicate to the assembler rather
// than something for the linker to resolve. The same reasoning, and the
// same shape, as `mmu_fallback.s` in `boot.rs`.
global_asm!;
global_asm!;
global_asm!;
// The `wfe` in these is an ARMv6K instruction, and the assembler
// defaults to plain ARMv6 for `armv6-none-eabi` and rejects it -- the
// same directive the vector table above needs, for the same reason.
global_asm!;
global_asm!;
global_asm!;
/// Unmasks IRQ at the CPU level (the CPSR `I` bit on AArch32, `PSTATE.I`
/// via `DAIF` on AArch64).
///
/// This is one of three independent gates that all have to be open for
/// an interrupt to actually fire: this CPU-level mask, the source being
/// routed through the interrupt controller (see `crate::lic`), and
/// the peripheral itself being configured to raise it (e.g.
/// [`crate::timer::Timer::arm_periodic_c1`]).
/// Masks IRQ at the CPU level.