1#![cfg_attr(
36 multi_core,
37 doc = "Those signals can also carry cross-core communication."
38)]
39#![cfg_attr(
40 all(feature = "rt", multi_core),
41 doc = "
42## Inter-Processor Call (IPC)
43
44The [`ipc`] module posts a function to a CPU, and raises the IPC interrupt of that CPU.
45"
46)]
47#![cfg_attr(
48 all(feature = "rt", multi_core, xtensa),
49 doc = "The HAL reserves `FROM_CPU_INTR0` and `FROM_CPU_INTR1` for this path."
50)]
51#![cfg_attr(
52 all(feature = "rt", single_core, context_switch_source = "from_cpu"),
53 doc = "The HAL reserves `FROM_CPU_INTR0` to switch tasks."
54)]
55#![cfg_attr(
56 all(feature = "rt", context_switch_source = "software0"),
57 doc = "
58## Context Switching
59
60The HAL defines the `Software0` interrupt handler, and switches tasks in that interrupt.
61"
62)]
63
64use crate::{peripherals::Interrupt, system::Cpu};
65
66cfg_select! {
67 esp32 => {
68 use crate::peripherals::{DPORT as INTERRUPT_CORE0, DPORT as INTERRUPT_CORE1};
69 }
70 _ => {
71 use crate::peripherals::INTERRUPT_CORE0;
72 #[cfg(multi_core)]
73 use crate::peripherals::INTERRUPT_CORE1;
74 }
75}
76
77#[cfg_attr(riscv, path = "riscv.rs")]
78#[cfg_attr(xtensa, path = "xtensa.rs")]
79mod arch;
80pub use arch::*;
81
82use crate::pac;
83
84unstable_module! {
85 pub mod software;
86
87 #[cfg(all(feature = "rt", multi_core))]
88 pub mod ipc;
89
90 #[cfg(feature = "rt")]
91 #[doc(hidden)]
92 pub mod __rtos_implementation;
93}
94
95#[cfg(feature = "rt")]
96#[unsafe(no_mangle)]
97extern "C" fn EspDefaultHandler() {
98 panic!("Unhandled interrupt on {:?}", crate::system::Cpu::current());
99}
100
101#[instability::unstable]
103pub const DEFAULT_INTERRUPT_HANDLER: InterruptHandler = InterruptHandler::new(
104 {
105 unsafe extern "C" {
106 fn EspDefaultHandler();
107 }
108
109 unsafe {
110 core::mem::transmute::<unsafe extern "C" fn(), extern "C" fn()>(EspDefaultHandler)
111 }
112 },
113 Priority::min(),
114);
115
116#[instability::unstable]
119pub trait InterruptConfigurable: crate::private::Sealed {
120 #[doc = cfg_select! {
121 multi_core => "Registers an interrupt handler for the peripheral on the current core.",
122 _ => "Registers an interrupt handler for the peripheral.",
123 }]
124 #[doc = ""]
125 fn set_interrupt_handler(&mut self, handler: InterruptHandler);
132}
133
134#[derive(Copy, Clone, Debug)]
136#[cfg_attr(feature = "defmt", derive(defmt::Format))]
137#[instability::unstable]
138pub struct IsrCallback {
139 f: extern "C" fn(),
140}
141
142impl IsrCallback {
143 pub fn new(f: extern "C" fn()) -> Self {
145 Self { f }
147 }
148
149 pub fn address(self) -> usize {
151 self.f as usize
152 }
153
154 pub fn callback(self) -> extern "C" fn() {
158 self.f
159 }
160}
161
162impl PartialEq for IsrCallback {
163 fn eq(&self, other: &Self) -> bool {
164 core::ptr::fn_addr_eq(self.callback(), other.callback())
165 }
166}
167
168#[cfg_attr(
170 multi_core,
171 doc = r"
172
173**Note**: Interrupts are handled on the core they were setup on. If a driver is initialized
174on core 0, and moved to core 1, core 0 will still handle the interrupt."
175)]
176#[derive(Copy, Clone, Debug)]
177#[cfg_attr(feature = "defmt", derive(defmt::Format))]
178#[instability::unstable]
179pub struct InterruptHandler {
180 f: extern "C" fn(),
181 prio: Priority,
182}
183
184impl InterruptHandler {
185 pub const fn new(f: extern "C" fn(), prio: Priority) -> Self {
188 Self { f, prio }
189 }
190
191 #[inline]
193 pub fn handler(&self) -> IsrCallback {
194 IsrCallback::new(self.f)
195 }
196
197 #[inline]
199 pub fn priority(&self) -> Priority {
200 self.prio
201 }
202}
203
204const STATUS_WORDS: usize = property!("interrupts.status_registers");
205
206#[derive(Clone, Copy, Default, Debug)]
208#[instability::unstable]
209pub struct InterruptStatus {
210 status: [u32; STATUS_WORDS],
211}
212
213impl InterruptStatus {
214 #[instability::unstable]
216 #[inline]
217 pub fn is_pending(interrupt: Interrupt) -> bool {
218 let word = (interrupt as usize) / 32;
219 let bit = (interrupt as usize) % 32;
220 let mut status_word = 0;
221 for cpu in Cpu::all() {
222 status_word |= Self::interrupt_status_word(cpu, word);
223 }
224 (status_word & (1 << bit)) != 0
225 }
226
227 #[inline]
228 fn interrupt_status_word(cpu: Cpu, word: usize) -> u32 {
229 match cpu {
230 Cpu::ProCpu => {
231 cfg_select! {
232 esp32p4 => {
233 if word == 4 {
234 return INTERRUPT_CORE0::regs().core_0_intr_status4().read().bits();
236 }
237 }
238 _ => {}
239 }
240
241 INTERRUPT_CORE0::regs()
242 .core_0_intr_status(word)
243 .read()
244 .bits()
245 }
246 #[cfg(multi_core)]
247 Cpu::AppCpu => {
248 cfg_select! {
249 esp32p4 => {
250 if word == 4 {
251 return INTERRUPT_CORE1::regs().core_1_intr_status4().read().bits();
253 }
254 }
255 _ => {}
256 }
257
258 INTERRUPT_CORE1::regs()
259 .core_1_intr_status(word)
260 .read()
261 .bits()
262 }
263 }
264 }
265
266 #[instability::unstable]
268 pub fn current() -> InterruptStatus {
269 let cpu = Cpu::current();
270 InterruptStatus {
271 status: core::array::from_fn(|word| Self::interrupt_status_word(cpu, word)),
272 }
273 }
274
275 #[instability::unstable]
277 pub fn is_set(&self, interrupt: u8) -> bool {
278 (self.status[interrupt as usize / 32] & (1 << (interrupt % 32))) != 0
279 }
280
281 #[instability::unstable]
283 pub fn set(&mut self, interrupt: u8) {
284 self.status[interrupt as usize / 32] |= 1 << (interrupt % 32);
285 }
286
287 #[instability::unstable]
289 pub fn iterator(&self) -> InterruptStatusIterator {
290 InterruptStatusIterator {
291 status: *self,
292 idx: 0,
293 }
294 }
295}
296
297#[derive(Debug, Clone)]
299#[instability::unstable]
300pub struct InterruptStatusIterator {
301 status: InterruptStatus,
302 idx: usize,
303}
304
305impl Iterator for InterruptStatusIterator {
306 type Item = u8;
307
308 fn next(&mut self) -> Option<Self::Item> {
309 for i in self.idx..STATUS_WORDS {
310 if self.status.status[i] != 0 {
311 let bit = self.status.status[i].trailing_zeros();
312 self.status.status[i] ^= 1 << bit;
313 self.idx = i;
314 return Some((bit + 32 * i as u32) as u8);
315 }
316 }
317 self.idx = usize::MAX;
318 None
319 }
320}
321
322fn vector_entry(interrupt: Interrupt) -> &'static pac::Vector {
325 unsafe extern "Rust" {
326 #[cfg(xtensa)]
327 static __INTERRUPTS: pac::Vector;
328 #[cfg(riscv)]
329 static __EXTERNAL_INTERRUPTS: pac::Vector;
330 }
331
332 unsafe {
335 cfg_select! {
336 xtensa => (&__INTERRUPTS as *const pac::Vector)
337 .add(interrupt as usize)
338 .as_ref_unchecked(),
339 riscv => (&__EXTERNAL_INTERRUPTS as *const pac::Vector)
340 .add(interrupt as usize)
341 .as_ref_unchecked(),
342 _ => {
343 compile_error!("Unsupported architecture");
344 }
345 }
346 }
347}
348
349#[instability::unstable]
351pub fn bound_handler(interrupt: Interrupt) -> Option<IsrCallback> {
352 unsafe {
353 let vector = vector_entry(interrupt);
354
355 let addr = vector._handler;
356 if addr as usize == 0 {
357 return None;
358 }
359
360 Some(IsrCallback::new(core::mem::transmute::<
361 unsafe extern "C" fn(),
362 extern "C" fn(),
363 >(addr)))
364 }
365}
366
367#[instability::unstable]
374pub fn bind_handler(interrupt: Interrupt, handler: InterruptHandler) {
375 bind_vector(interrupt, handler);
376 enable(interrupt, handler.priority());
377}
378
379pub(crate) fn bind_vector(interrupt: Interrupt, handler: InterruptHandler) {
381 unsafe {
382 let vector = vector_entry(interrupt);
383 let ptr = (&raw const vector._handler).cast::<usize>().cast_mut();
384
385 #[cfg(all(riscv, write_vec_table_monitoring))]
386 if crate::soc::trap_section_protected() {
387 crate::debugger::DEBUGGER_LOCK.lock(|| {
388 let wp = crate::debugger::clear_watchpoint(1);
389 ptr.write_volatile(handler.handler().address());
390 crate::debugger::restore_watchpoint(1, wp);
391 });
392 return;
393 }
394
395 ptr.write_volatile(handler.handler().address());
396 }
397}
398
399#[inline]
406#[instability::unstable]
407pub fn enable(interrupt: Interrupt, level: Priority) {
408 enable_on_cpu(Cpu::current(), interrupt, level);
409}
410
411pub(crate) fn enable_on_cpu(cpu: Cpu, interrupt: Interrupt, level: Priority) {
412 let cpu_interrupt = priority_to_cpu_interrupt(interrupt, level);
413 map_raw(cpu, interrupt, cpu_interrupt as u32);
414}
415
416#[inline]
420#[instability::unstable]
421pub fn disable(core: Cpu, interrupt: Interrupt) {
422 map_raw(core, interrupt, DISABLED_CPU_INTERRUPT)
423}
424
425pub(super) fn map_raw(core: Cpu, interrupt: Interrupt, cpu_interrupt: u32) {
426 match core {
427 Cpu::ProCpu => {
428 INTERRUPT_CORE0::regs()
429 .core_0_intr_map(interrupt as usize)
430 .modify(|_, w| unsafe { w.map().bits(cpu_interrupt as u8) });
431 }
432 #[cfg(multi_core)]
433 Cpu::AppCpu => {
434 INTERRUPT_CORE1::regs()
435 .core_1_intr_map(interrupt as usize)
436 .modify(|_, w| unsafe { w.map().bits(cpu_interrupt as u8) });
437 }
438 }
439}
440
441#[cfg(all(feature = "rt", gpio_driver_supported))]
443pub(crate) fn mapped_to(cpu: Cpu, interrupt: Interrupt) -> Option<CpuInterrupt> {
444 mapped_to_raw(cpu, interrupt as u32)
445}
446
447#[cfg(feature = "rt")]
448pub(crate) fn mapped_to_raw(cpu: Cpu, interrupt: u32) -> Option<CpuInterrupt> {
449 let cpu_intr = match cpu {
450 Cpu::ProCpu => INTERRUPT_CORE0::regs()
451 .core_0_intr_map(interrupt as usize)
452 .read()
453 .map()
454 .bits() as u32,
455 #[cfg(multi_core)]
456 Cpu::AppCpu => INTERRUPT_CORE1::regs()
457 .core_1_intr_map(interrupt as usize)
458 .read()
459 .map()
460 .bits() as u32,
461 };
462 CpuInterrupt::from_u32(cpu_intr)
463}
464
465#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
469#[cfg_attr(feature = "defmt", derive(defmt::Format))]
470#[non_exhaustive]
471pub enum RunLevel {
472 ThreadMode,
476
477 Interrupt(ElevatedRunLevel),
479}
480
481impl RunLevel {
482 #[procmacros::doc_replace]
483 #[inline]
496 pub fn current() -> Self {
497 let raw = current_raw_runlevel();
498 unwrap!(Self::try_from_u32(raw))
499 }
500
501 #[inline]
508 #[instability::unstable]
509 pub unsafe fn change(to: Self) -> Self {
510 unsafe { change_current_runlevel(to) }
511 }
512
513 #[procmacros::doc_replace]
514 #[inline]
534 pub fn is_thread(&self) -> bool {
535 matches!(self, RunLevel::ThreadMode)
536 }
537
538 pub(crate) fn try_from_u32(priority: u32) -> Result<Self, PriorityError> {
539 if priority == 0 {
540 Ok(RunLevel::ThreadMode)
541 } else {
542 ElevatedRunLevel::try_from_u32(priority).map(RunLevel::Interrupt)
543 }
544 }
545}
546
547impl PartialEq<ElevatedRunLevel> for RunLevel {
548 fn eq(&self, other: &ElevatedRunLevel) -> bool {
549 *self == RunLevel::Interrupt(*other)
550 }
551}
552
553impl PartialEq<Priority> for RunLevel {
554 fn eq(&self, other: &Priority) -> bool {
555 let runlevel = ElevatedRunLevel::from(*other);
556 *self == RunLevel::Interrupt(runlevel)
557 }
558}
559
560impl From<RunLevel> for u32 {
561 fn from(level: RunLevel) -> Self {
562 match level {
563 RunLevel::ThreadMode => 0,
564 RunLevel::Interrupt(priority) => priority as u32,
565 }
566 }
567}
568
569#[derive(Copy, Clone, Debug, PartialEq, Eq)]
571#[cfg_attr(feature = "defmt", derive(defmt::Format))]
572#[instability::unstable]
573pub enum PriorityError {
574 InvalidInterruptPriority,
576}
577
578#[instability::unstable]
579impl TryFrom<u32> for RunLevel {
580 type Error = PriorityError;
581
582 fn try_from(priority: u32) -> Result<Self, Self::Error> {
583 Self::try_from_u32(priority)
584 }
585}
586
587#[cfg(feature = "rt")]
588pub(crate) fn setup_interrupts() {
589 for peripheral_interrupt in 0..255 {
591 crate::peripherals::Interrupt::try_from(peripheral_interrupt)
592 .map(|intr| {
593 #[cfg(multi_core)]
594 disable(Cpu::AppCpu, intr);
595 disable(Cpu::ProCpu, intr);
596 })
597 .ok();
598 }
599
600 unsafe { crate::interrupt::init_vectoring() };
601}
602
603#[inline(always)]
604#[cfg(feature = "rt")]
605fn should_handle(core: Cpu, interrupt_nr: u32, level: u32) -> bool {
606 if let Some(cpu_interrupt) = crate::interrupt::mapped_to_raw(core, interrupt_nr)
607 && cpu_interrupt.is_vectored()
608 && cpu_interrupt.level() == level
609 {
610 true
611 } else {
612 false
613 }
614}