1#[cfg(feature = "rt")]
13#[instability::unstable]
14pub use esp_riscv_rt::TrapFrame;
15
16#[cfg_attr(interrupt_controller = "riscv_basic", path = "riscv/basic.rs")]
17#[cfg_attr(interrupt_controller = "plic", path = "riscv/plic.rs")]
18#[cfg_attr(interrupt_controller = "clic", path = "riscv/clic.rs")]
19mod cpu_int;
20
21#[cfg(feature = "unstable")]
24pub(crate) use riscv::interrupt::free;
25
26use crate::{
27 interrupt::{PriorityError, RunLevel},
28 peripherals::Interrupt,
29 system::Cpu,
30};
31
32#[cfg_attr(feature = "defmt", derive(defmt::Format))]
34#[instability::unstable]
35pub enum InterruptKind {
36 Level,
38 Edge,
40}
41
42for_each_interrupt!(
43 (all $( ([$class:ident $idx_in_class:literal] $n:literal) ),*) => {
44 paste::paste! {
45 #[repr(u32)]
47 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
48 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
49 #[instability::unstable]
50 pub enum CpuInterrupt {
51 $(
52 #[doc = concat!(" Interrupt number ", stringify!($n), ".")]
53 [<Interrupt $n>] = $n,
54 )*
55 }
56
57 impl CpuInterrupt {
58 #[inline]
59 pub(crate) fn from_u32(n: u32) -> Option<Self> {
60 match n {
61 $(n if n == $n && n != DISABLED_CPU_INTERRUPT => Some(Self:: [<Interrupt $n>]),)*
62 _ => None
63 }
64 }
65 }
66 }
67 };
68);
69
70for_each_classified_interrupt!(
71 (direct_bindable $( ([$class:ident $idx_in_class:literal] $n:literal) ),*) => {
72 paste::paste! {
73 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
75 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
76 pub enum DirectBindableCpuInterrupt {
77 $(
78 #[doc = concat!(" Direct bindable CPU interrupt number ", stringify!($idx_in_class), ".")]
79 #[doc = " "]
80 #[doc = concat!(" Corresponds to CPU interrupt ", stringify!($n), ".")]
81 [<Interrupt $idx_in_class>] = $n,
82 )*
83 }
84
85 impl From<DirectBindableCpuInterrupt> for CpuInterrupt {
86 fn from(bindable: DirectBindableCpuInterrupt) -> CpuInterrupt {
87 match bindable {
88 $(
89 DirectBindableCpuInterrupt::[<Interrupt $idx_in_class>] => CpuInterrupt::[<Interrupt $n>],
90 )*
91 }
92 }
93 }
94 }
95 };
96);
97
98impl CpuInterrupt {
99 #[inline]
100 #[cfg(feature = "rt")]
101 pub(crate) fn is_vectored(self) -> bool {
102 const VECTORED_CPU_INTERRUPT_RANGE: core::ops::RangeInclusive<u32> = PRIORITY_TO_INTERRUPT
104 [0] as u32
105 ..=PRIORITY_TO_INTERRUPT[PRIORITY_TO_INTERRUPT.len() - 1] as u32;
106 VECTORED_CPU_INTERRUPT_RANGE.contains(&(self as u32))
107 }
108
109 #[inline]
111 #[instability::unstable]
112 pub fn enable(self) {
113 cpu_int::enable_cpu_interrupt_raw(self as u32);
114 }
115
116 #[inline]
118 #[instability::unstable]
119 pub fn clear(self) {
120 cpu_int::clear_raw(self as u32);
121 }
122
123 #[inline]
129 #[instability::unstable]
130 pub fn set_kind(self, kind: InterruptKind) {
131 cpu_int::set_kind_raw(self as u32, kind);
132 }
133
134 #[inline]
136 #[instability::unstable]
137 pub fn set_priority(self, priority: Priority) {
138 cpu_int::set_priority_raw(self as u32, priority);
139 }
140
141 #[inline]
143 #[instability::unstable]
144 pub fn priority(self) -> Priority {
145 unwrap!(Priority::try_from_u32(self.level()))
146 }
147
148 #[inline]
149 pub(crate) fn level(self) -> u32 {
150 cpu_int::cpu_interrupt_priority_raw(self as u32) as u32
151 }
152}
153
154for_each_interrupt_priority!(
155 (all $( ($idx:literal, $n:literal, $ident:ident, $level:ident) ),*) => {
156 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
161 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
162 #[repr(u8)]
163 pub enum Priority {
164 $(
165 #[doc = concat!(" Priority level ", stringify!($n), ".")]
166 $ident = $n,
167 )*
168 }
169
170 impl Priority {
171 fn iter() -> impl Iterator<Item = Priority> {
172 [$(Priority::$ident,)*].into_iter()
173 }
174 }
175
176 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
178 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
179 #[repr(u8)]
180 pub enum ElevatedRunLevel {
181 $(
182 #[doc = concat!("Run level ", stringify!($n), ".")]
183 $level = $n,
184 )*
185 }
186
187 impl ElevatedRunLevel {
188 pub const fn from_priority(priority: Priority) -> Self {
190 match priority {
191 $(Priority::$ident => Self::$level,)*
192 }
193 }
194 }
195 };
196);
197
198impl Priority {
199 #[allow(unused_assignments)]
201 #[instability::unstable]
202 pub const fn max() -> Priority {
203 const {
204 let mut last = Self::min();
205 for_each_interrupt_priority!(
206 ($_idx:literal, $_n:literal, $ident:ident, $_level:ident) => {
207 last = Self::$ident;
208 };
209 );
210 last
211 }
212 }
213
214 pub const fn min() -> Priority {
216 Priority::Priority1
217 }
218
219 pub(crate) fn try_from_u32(priority: u32) -> Result<Self, PriorityError> {
220 let result;
221 for_each_interrupt_priority!(
222 (all $( ($idx:literal, $n:literal, $ident:ident, $_level:ident) ),*) => {
223 result = match priority {
224 $($n => Ok(Priority::$ident),)*
225 _ => Err(PriorityError::InvalidInterruptPriority),
226 }
227 };
228 );
229 result
230 }
231}
232
233impl ElevatedRunLevel {
234 #[instability::unstable]
236 pub const fn max() -> ElevatedRunLevel {
237 Self::from_priority(Priority::max())
238 }
239
240 pub const fn min() -> ElevatedRunLevel {
242 Self::from_priority(Priority::min())
243 }
244
245 pub(crate) fn try_from_u32(level: u32) -> Result<Self, PriorityError> {
246 Priority::try_from_u32(level).map(Self::from_priority)
247 }
248}
249
250#[instability::unstable]
251impl TryFrom<u32> for ElevatedRunLevel {
252 type Error = PriorityError;
253
254 fn try_from(value: u32) -> Result<Self, Self::Error> {
255 Self::try_from_u32(value)
256 }
257}
258
259impl From<Priority> for ElevatedRunLevel {
260 fn from(priority: Priority) -> Self {
261 Self::from_priority(priority)
262 }
263}
264
265#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
266pub(super) static DISABLED_CPU_INTERRUPT: u32 = property!("interrupts.disabled_interrupt");
267
268const VECTOR_COUNT: usize = const {
270 let mut count = 0;
271 for_each_interrupt!(([vector $n:tt] $_:literal) => { count += 1; };);
272
273 core::assert!(count == Priority::max() as usize);
274
275 count
276};
277
278#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
280pub(super) static PRIORITY_TO_INTERRUPT: [CpuInterrupt; VECTOR_COUNT] = const {
281 let mut counter = 0;
282 let mut vector = [CpuInterrupt::Interrupt0; VECTOR_COUNT];
283
284 for_each_interrupt!(
285 ([vector $_n:tt] $interrupt:literal) => {
286 vector[counter] = paste::paste! { CpuInterrupt::[<Interrupt $interrupt>] };
287 counter += 1;
288 };
289 );
290 vector
291};
292
293#[instability::unstable]
309pub fn enable_direct(
310 interrupt: Interrupt,
311 level: Priority,
312 cpu_interrupt: DirectBindableCpuInterrupt,
313 handler: unsafe extern "C" fn(),
314) {
315 cfg_select! {
316 interrupt_controller = "clic" => {
317 let clic = unsafe { crate::soc::pac::CLIC::steal() };
318
319 clic.int_attr(cpu_interrupt as usize).modify(|_, w| {
321 w.shv().hardware();
322 w.trig().positive_level()
323 });
324
325 let mtvt_table: *mut [u32; 48];
326 unsafe { core::arch::asm!("csrr {0}, 0x307", out(reg) mtvt_table) };
327
328 let int_slot = mtvt_table
329 .cast::<u32>()
330 .wrapping_add(cpu_interrupt as usize);
331
332 let instr = handler as usize as u32;
333 }
334 _ => {
335 use riscv::register::mtvec;
336 let mt = mtvec::read();
337
338 assert_eq!(
339 mt.trap_mode().into_usize(),
340 mtvec::TrapMode::Vectored.into_usize()
341 );
342
343 let base_addr = mt.address() as usize;
344
345 let int_slot = base_addr.wrapping_add((cpu_interrupt as usize) * 4) as *mut u32;
346
347 let instr = encode_jal_x0(handler as usize, int_slot as usize);
348 }
349 }
350
351 if crate::debugger::debugger_connected() {
352 unsafe { core::ptr::write_volatile(int_slot, instr) };
353 } else {
354 crate::debugger::DEBUGGER_LOCK.lock(|| unsafe {
355 let wp = crate::debugger::clear_watchpoint(1);
356 core::ptr::write_volatile(int_slot, instr);
357 crate::debugger::restore_watchpoint(1, wp);
358 });
359 }
360 unsafe {
361 core::arch::asm!("fence.i");
362 }
363
364 #[cfg(esp32p4)]
365 unsafe {
366 crate::soc::cache_writeback_addr(mtvt_table as u32, 48 * 4);
368 crate::soc::cache_invalidate_icache_addr(mtvt_table as u32, 48 * 4);
370 }
371
372 super::map_raw(Cpu::current(), interrupt, cpu_interrupt as u32);
373 cpu_int::set_priority_raw(cpu_interrupt as u32, level);
374 cpu_int::set_kind_raw(cpu_interrupt as u32, InterruptKind::Level);
375 cpu_int::enable_cpu_interrupt_raw(cpu_interrupt as u32);
376}
377
378#[cfg(not(interrupt_controller = "clic"))]
380fn encode_jal_x0(target: usize, pc: usize) -> u32 {
381 let offset = (target as isize) - (pc as isize);
382
383 const MIN: isize = -(1isize << 20);
384 const MAX: isize = (1isize << 20) - 1;
385
386 assert!(offset % 2 == 0 && (MIN..=MAX).contains(&offset));
387
388 let imm = offset as u32;
389 let imm20 = (imm >> 20) & 0x1;
390 let imm10_1 = (imm >> 1) & 0x3ff;
391 let imm11 = (imm >> 11) & 0x1;
392 let imm19_12 = (imm >> 12) & 0xff;
393
394 (imm20 << 31)
395 | (imm19_12 << 12)
396 | (imm11 << 20)
397 | (imm10_1 << 21)
398 | 0b1101111u32
400}
401
402pub(crate) fn current_raw_runlevel() -> u32 {
406 cpu_int::current_runlevel() as u32
407}
408
409pub(crate) unsafe fn change_current_runlevel(level: RunLevel) -> RunLevel {
418 let previous = cpu_int::change_current_runlevel(level);
419 unwrap!(RunLevel::try_from_u32(previous as u32))
420}
421
422fn cpu_wait_mode_on() -> bool {
423 cfg_select! {
424 soc_has_pcr => crate::peripherals::PCR::regs()
425 .cpu_waiti_conf()
426 .read()
427 .cpu_wait_mode_force_on()
428 .bit_is_set(),
429 soc_has_hp_sys => crate::peripherals::HP_SYS::regs()
430 .cpu_waiti_conf()
431 .read()
432 .cpu_wait_mode_force_on()
433 .bit_is_set(),
434 _ => crate::peripherals::SYSTEM::regs()
435 .cpu_per_conf()
436 .read()
437 .cpu_wait_mode_force_on()
438 .bit_is_set(),
439 }
440}
441
442#[inline(always)]
451#[instability::unstable]
452pub fn wait_for_interrupt() {
453 if crate::debugger::debugger_connected() && !cpu_wait_mode_on() {
454 return;
458 }
459 unsafe { core::arch::asm!("wfi") };
460}
461
462pub(crate) fn priority_to_cpu_interrupt(_interrupt: Interrupt, level: Priority) -> CpuInterrupt {
463 PRIORITY_TO_INTERRUPT[(level as usize) - 1]
464}
465
466#[cfg(any(feature = "rt", all(feature = "unstable", multi_core)))]
472pub(crate) unsafe fn init_vectoring() {
473 use riscv::register::mtvec;
474
475 unsafe extern "C" {
476 static _vector_table: u32;
477 }
478
479 unsafe {
480 let vec_table = (&raw const _vector_table).addr();
481
482 #[cfg(not(interrupt_controller = "clic"))]
483 {
484 mtvec::write({
485 let mut mtvec = mtvec::Mtvec::from_bits(0);
486 mtvec.set_trap_mode(mtvec::TrapMode::Vectored);
487 mtvec.set_address(vec_table);
488 mtvec
489 });
490 }
491
492 #[cfg(interrupt_controller = "clic")]
493 {
494 mtvec::write({
495 let mut mtvec = mtvec::Mtvec::from_bits(0x03); mtvec.set_address(vec_table);
497 mtvec
498 });
499
500 let mtvt_table = match Cpu::current() {
502 Cpu::ProCpu => {
503 unsafe extern "C" {
504 static _mtvt_table: u32;
505 }
506 (&raw const _mtvt_table).addr()
507 }
508 #[cfg(multi_core)]
509 Cpu::AppCpu => {
510 unsafe extern "C" {
511 static _mtvt_table2: u32;
512 }
513 (&raw const _mtvt_table2).addr()
514 }
515 };
516 core::arch::asm!("csrw 0x307, {0}", in(reg) mtvt_table);
517 }
518 };
519
520 #[cfg(feature = "rt")]
523 cpu_int::init();
524
525 for (int, prio) in PRIORITY_TO_INTERRUPT.iter().copied().zip(Priority::iter()) {
527 let num = int as u32;
528 cpu_int::set_kind_raw(num, InterruptKind::Level);
529 cpu_int::set_priority_raw(num, prio);
530 cpu_int::enable_cpu_interrupt_raw(num);
531 }
532}
533
534#[cfg(feature = "rt")]
535pub(crate) mod rt {
536 use esp_riscv_rt::TrapFrame;
537 use riscv::register::mcause;
538
539 use super::*;
540 use crate::interrupt::InterruptStatus;
541
542 #[cfg(not(interrupt_controller = "clic"))]
544 const INTERRUPT_COUNT: usize = const {
545 let mut count = 0;
546 for_each_interrupt!(([$_class:tt $n:tt] $_:literal) => { count += 1; };);
547 count
548 };
549
550 #[cfg(not(interrupt_controller = "clic"))]
552 #[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
553 pub(super) static INTERRUPT_TO_PRIORITY: [Option<Priority>; INTERRUPT_COUNT] = const {
554 let mut priorities = [None; INTERRUPT_COUNT];
555
556 for_each_interrupt!(
557 ([vector $n:tt] $int:literal) => {
558 for_each_interrupt_priority!(($n, $__:tt, $ident:ident, $_level:ident) => { priorities[$int] = Some(Priority::$ident); };);
559 };
560 );
561
562 priorities
563 };
564
565 #[doc(hidden)]
569 #[unsafe(link_section = ".trap.rust")]
570 #[unsafe(export_name = "_start_trap_rust_hal")]
571 unsafe extern "C" fn start_trap_rust_hal(trap_frame: *mut TrapFrame) {
572 assert!(
573 mcause::read().is_exception(),
574 "Arrived into _start_trap_rust_hal but mcause is not an exception!"
575 );
576 unsafe extern "C" {
577 fn ExceptionHandler(tf: *mut TrapFrame);
578 }
579 unsafe {
580 ExceptionHandler(trap_frame);
581 }
582 }
583
584 #[doc(hidden)]
585 #[unsafe(no_mangle)]
586 #[unsafe(link_section = ".init")]
587 unsafe fn _setup_interrupts() {
588 crate::soc::riscv_preinit();
589 crate::interrupt::setup_interrupts();
590
591 #[cfg(interrupt_controller = "plic")]
592 unsafe {
593 core::arch::asm!("csrw mie, {0}", in(reg) u32::MAX);
594 }
595 }
596
597 #[unsafe(no_mangle)]
598 #[crate::ram]
599 unsafe fn handle_interrupts(cpu_intr: CpuInterrupt) {
600 let status = InterruptStatus::current();
601
602 cpu_intr.clear();
605
606 cfg_select! {
607 interrupt_controller = "clic" => {
608 let prio = cpu_int::current_runlevel();
609 let mcause = riscv::register::mcause::read();
610 }
611 _ => {
612 let prio = unwrap!(INTERRUPT_TO_PRIORITY[cpu_intr as usize]);
615 let level = unsafe {
616 change_current_runlevel(RunLevel::Interrupt(ElevatedRunLevel::from(prio)))
617 };
618 let prio = prio as u8;
619 }
620 }
621
622 let handle_interrupts = || unsafe {
623 for interrupt_nr in status.iterator().filter(|&interrupt_nr| {
624 crate::interrupt::should_handle(Cpu::current(), interrupt_nr as u32, prio as u32)
625 }) {
626 let handler =
627 crate::soc::pac::__EXTERNAL_INTERRUPTS[interrupt_nr as usize]._handler;
628
629 handler();
630 }
631 };
632
633 if prio != Priority::max() as u8 {
637 unsafe {
638 riscv::interrupt::nested(handle_interrupts);
639 }
640 } else {
641 handle_interrupts();
642 }
643
644 cfg_select! {
645 interrupt_controller = "clic" => {
646 unsafe { core::arch::asm!("csrw 0x342, {}", in(reg) mcause.bits()) }
651 }
652 _ => {
653 unsafe { change_current_runlevel(level) };
654 }
655 }
656 }
657}