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
21use crate::{
22 interrupt::{PriorityError, RunLevel},
23 peripherals::Interrupt,
24 system::Cpu,
25};
26
27#[cfg_attr(feature = "defmt", derive(defmt::Format))]
29#[instability::unstable]
30pub enum InterruptKind {
31 Level,
33 Edge,
35}
36
37for_each_interrupt!(
38 (all $( ([$class:ident $idx_in_class:literal] $n:literal) ),*) => {
39 paste::paste! {
40 #[repr(u32)]
42 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
43 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
44 #[instability::unstable]
45 pub enum CpuInterrupt {
46 $(
47 #[doc = concat!(" Interrupt number ", stringify!($n), ".")]
48 [<Interrupt $n>] = $n,
49 )*
50 }
51
52 impl CpuInterrupt {
53 #[inline]
54 pub(crate) fn from_u32(n: u32) -> Option<Self> {
55 match n {
56 $(n if n == $n && n != DISABLED_CPU_INTERRUPT => Some(Self:: [<Interrupt $n>]),)*
57 _ => None
58 }
59 }
60 }
61 }
62 };
63);
64
65for_each_classified_interrupt!(
66 (direct_bindable $( ([$class:ident $idx_in_class:literal] $n:literal) ),*) => {
67 paste::paste! {
68 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
70 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
71 pub enum DirectBindableCpuInterrupt {
72 $(
73 #[doc = concat!(" Direct bindable CPU interrupt number ", stringify!($idx_in_class), ".")]
74 #[doc = " "]
75 #[doc = concat!(" Corresponds to CPU interrupt ", stringify!($n), ".")]
76 [<Interrupt $idx_in_class>] = $n,
77 )*
78 }
79
80 impl From<DirectBindableCpuInterrupt> for CpuInterrupt {
81 fn from(bindable: DirectBindableCpuInterrupt) -> CpuInterrupt {
82 match bindable {
83 $(
84 DirectBindableCpuInterrupt::[<Interrupt $idx_in_class>] => CpuInterrupt::[<Interrupt $n>],
85 )*
86 }
87 }
88 }
89 }
90 };
91);
92
93impl CpuInterrupt {
94 #[inline]
95 #[cfg(feature = "rt")]
96 pub(crate) fn is_vectored(self) -> bool {
97 const VECTORED_CPU_INTERRUPT_RANGE: core::ops::RangeInclusive<u32> = PRIORITY_TO_INTERRUPT
99 [0] as u32
100 ..=PRIORITY_TO_INTERRUPT[PRIORITY_TO_INTERRUPT.len() - 1] as u32;
101 VECTORED_CPU_INTERRUPT_RANGE.contains(&(self as u32))
102 }
103
104 #[inline]
106 #[instability::unstable]
107 pub fn enable(self) {
108 cpu_int::enable_cpu_interrupt_raw(self as u32);
109 }
110
111 #[inline]
113 #[instability::unstable]
114 pub fn clear(self) {
115 cpu_int::clear_raw(self as u32);
116 }
117
118 #[inline]
124 #[instability::unstable]
125 pub fn set_kind(self, kind: InterruptKind) {
126 cpu_int::set_kind_raw(self as u32, kind);
127 }
128
129 #[inline]
131 #[instability::unstable]
132 pub fn set_priority(self, priority: Priority) {
133 cpu_int::set_priority_raw(self as u32, priority);
134 }
135
136 #[inline]
138 #[instability::unstable]
139 pub fn priority(self) -> Priority {
140 unwrap!(Priority::try_from_u32(self.level()))
141 }
142
143 #[inline]
144 pub(crate) fn level(self) -> u32 {
145 cpu_int::cpu_interrupt_priority_raw(self as u32) as u32
146 }
147}
148
149for_each_interrupt_priority!(
150 (all $( ($idx:literal, $n:literal, $ident:ident, $level:ident) ),*) => {
151 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
156 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
157 #[repr(u8)]
158 pub enum Priority {
159 $(
160 #[doc = concat!(" Priority level ", stringify!($n), ".")]
161 $ident = $n,
162 )*
163 }
164
165 impl Priority {
166 fn iter() -> impl Iterator<Item = Priority> {
167 [$(Priority::$ident,)*].into_iter()
168 }
169 }
170
171 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
173 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
174 #[repr(u8)]
175 pub enum ElevatedRunLevel {
176 $(
177 #[doc = concat!("Run level ", stringify!($n), ".")]
178 $level = $n,
179 )*
180 }
181
182 impl ElevatedRunLevel {
183 pub const fn from_priority(priority: Priority) -> Self {
185 match priority {
186 $(Priority::$ident => Self::$level,)*
187 }
188 }
189 }
190 };
191);
192
193impl Priority {
194 #[allow(unused_assignments)]
196 #[instability::unstable]
197 pub const fn max() -> Priority {
198 const {
199 let mut last = Self::min();
200 for_each_interrupt_priority!(
201 ($_idx:literal, $_n:literal, $ident:ident, $_level:ident) => {
202 last = Self::$ident;
203 };
204 );
205 last
206 }
207 }
208
209 pub const fn min() -> Priority {
211 Priority::Priority1
212 }
213
214 pub(crate) fn try_from_u32(priority: u32) -> Result<Self, PriorityError> {
215 let result;
216 for_each_interrupt_priority!(
217 (all $( ($idx:literal, $n:literal, $ident:ident, $_level:ident) ),*) => {
218 result = match priority {
219 $($n => Ok(Priority::$ident),)*
220 _ => Err(PriorityError::InvalidInterruptPriority),
221 }
222 };
223 );
224 result
225 }
226}
227
228impl ElevatedRunLevel {
229 #[instability::unstable]
231 pub const fn max() -> ElevatedRunLevel {
232 Self::from_priority(Priority::max())
233 }
234
235 pub const fn min() -> ElevatedRunLevel {
237 Self::from_priority(Priority::min())
238 }
239
240 pub(crate) fn try_from_u32(level: u32) -> Result<Self, PriorityError> {
241 Priority::try_from_u32(level).map(Self::from_priority)
242 }
243}
244
245#[instability::unstable]
246impl TryFrom<u32> for ElevatedRunLevel {
247 type Error = PriorityError;
248
249 fn try_from(value: u32) -> Result<Self, Self::Error> {
250 Self::try_from_u32(value)
251 }
252}
253
254impl From<Priority> for ElevatedRunLevel {
255 fn from(priority: Priority) -> Self {
256 Self::from_priority(priority)
257 }
258}
259
260#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
261pub(super) static DISABLED_CPU_INTERRUPT: u32 = property!("interrupts.disabled_interrupt");
262
263const VECTOR_COUNT: usize = const {
265 let mut count = 0;
266 for_each_interrupt!(([vector $n:tt] $_:literal) => { count += 1; };);
267
268 core::assert!(count == Priority::max() as usize);
269
270 count
271};
272
273#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
275pub(super) static PRIORITY_TO_INTERRUPT: [CpuInterrupt; VECTOR_COUNT] = const {
276 let mut counter = 0;
277 let mut vector = [CpuInterrupt::Interrupt0; VECTOR_COUNT];
278
279 for_each_interrupt!(
280 ([vector $_n:tt] $interrupt:literal) => {
281 vector[counter] = paste::paste! { CpuInterrupt::[<Interrupt $interrupt>] };
282 counter += 1;
283 };
284 );
285 vector
286};
287
288#[instability::unstable]
304pub fn enable_direct(
305 interrupt: Interrupt,
306 level: Priority,
307 cpu_interrupt: DirectBindableCpuInterrupt,
308 handler: unsafe extern "C" fn(),
309) {
310 cfg_if::cfg_if! {
311 if #[cfg(interrupt_controller = "clic")] {
312 let clic = unsafe { crate::soc::pac::CLIC::steal() };
313
314 clic.int_attr(cpu_interrupt as usize).modify(|_, w| {
316 w.shv().hardware();
317 w.trig().positive_level()
318 });
319
320 let mtvt_table: *mut [u32; 48];
321 unsafe { core::arch::asm!("csrr {0}, 0x307", out(reg) mtvt_table) };
322
323 let int_slot = mtvt_table
324 .cast::<u32>()
325 .wrapping_add(cpu_interrupt as usize);
326
327 let instr = handler as usize as u32;
328 } else {
329 use riscv::register::mtvec;
330 let mt = mtvec::read();
331
332 assert_eq!(
333 mt.trap_mode().into_usize(),
334 mtvec::TrapMode::Vectored.into_usize()
335 );
336
337 let base_addr = mt.address() as usize;
338
339 let int_slot = base_addr.wrapping_add((cpu_interrupt as usize) * 4) as *mut u32;
340
341 let instr = encode_jal_x0(handler as usize, int_slot as usize);
342 }
343 }
344
345 if crate::debugger::debugger_connected() {
346 unsafe { core::ptr::write_volatile(int_slot, instr) };
347 } else {
348 crate::debugger::DEBUGGER_LOCK.lock(|| unsafe {
349 let wp = crate::debugger::clear_watchpoint(1);
350 core::ptr::write_volatile(int_slot, instr);
351 crate::debugger::restore_watchpoint(1, wp);
352 });
353 }
354 unsafe {
355 core::arch::asm!("fence.i");
356 }
357
358 super::map_raw(Cpu::current(), interrupt, cpu_interrupt as u32);
359 cpu_int::set_priority_raw(cpu_interrupt as u32, level);
360 cpu_int::set_kind_raw(cpu_interrupt as u32, InterruptKind::Level);
361 cpu_int::enable_cpu_interrupt_raw(cpu_interrupt as u32);
362}
363
364#[cfg(not(interrupt_controller = "clic"))]
366fn encode_jal_x0(target: usize, pc: usize) -> u32 {
367 let offset = (target as isize) - (pc as isize);
368
369 const MIN: isize = -(1isize << 20);
370 const MAX: isize = (1isize << 20) - 1;
371
372 assert!(offset % 2 == 0 && (MIN..=MAX).contains(&offset));
373
374 let imm = offset as u32;
375 let imm20 = (imm >> 20) & 0x1;
376 let imm10_1 = (imm >> 1) & 0x3ff;
377 let imm11 = (imm >> 11) & 0x1;
378 let imm19_12 = (imm >> 12) & 0xff;
379
380 (imm20 << 31)
381 | (imm19_12 << 12)
382 | (imm11 << 20)
383 | (imm10_1 << 21)
384 | 0b1101111u32
386}
387
388pub(crate) fn current_raw_runlevel() -> u32 {
392 cpu_int::current_runlevel() as u32
393}
394
395pub(crate) unsafe fn change_current_runlevel(level: RunLevel) -> RunLevel {
404 let previous = cpu_int::change_current_runlevel(level);
405 unwrap!(RunLevel::try_from_u32(previous as u32))
406}
407
408fn cpu_wait_mode_on() -> bool {
409 cfg_if::cfg_if! {
410 if #[cfg(soc_has_pcr)] {
411 crate::peripherals::PCR::regs().cpu_waiti_conf().read().cpu_wait_mode_force_on().bit_is_set()
412 } else {
413 crate::peripherals::SYSTEM::regs()
414 .cpu_per_conf()
415 .read()
416 .cpu_wait_mode_force_on()
417 .bit_is_set()
418 }
419 }
420}
421
422#[inline(always)]
431#[instability::unstable]
432pub fn wait_for_interrupt() {
433 if crate::debugger::debugger_connected() && !cpu_wait_mode_on() {
434 return;
438 }
439 unsafe { core::arch::asm!("wfi") };
440}
441
442pub(crate) fn priority_to_cpu_interrupt(_interrupt: Interrupt, level: Priority) -> CpuInterrupt {
443 PRIORITY_TO_INTERRUPT[(level as usize) - 1]
444}
445
446#[cfg(any(feature = "rt", all(feature = "unstable", multi_core)))]
452pub(crate) unsafe fn init_vectoring() {
453 use riscv::register::mtvec;
454
455 unsafe extern "C" {
456 static _vector_table: u32;
457 #[cfg(interrupt_controller = "clic")]
458 static _mtvt_table: u32;
459 }
460
461 unsafe {
462 let vec_table = (&raw const _vector_table).addr();
463
464 #[cfg(not(interrupt_controller = "clic"))]
465 {
466 mtvec::write({
467 let mut mtvec = mtvec::Mtvec::from_bits(0);
468 mtvec.set_trap_mode(mtvec::TrapMode::Vectored);
469 mtvec.set_address(vec_table);
470 mtvec
471 });
472 }
473
474 #[cfg(interrupt_controller = "clic")]
475 {
476 mtvec::write({
477 let mut mtvec = mtvec::Mtvec::from_bits(0x03); mtvec.set_address(vec_table);
479 mtvec
480 });
481
482 let mtvt_table = (&raw const _mtvt_table).addr();
484 core::arch::asm!("csrw 0x307, {0}", in(reg) mtvt_table);
485 }
486 };
487
488 for (int, prio) in PRIORITY_TO_INTERRUPT.iter().copied().zip(Priority::iter()) {
490 let num = int as u32;
491 cpu_int::set_kind_raw(num, InterruptKind::Level);
492 cpu_int::set_priority_raw(num, prio);
493 cpu_int::enable_cpu_interrupt_raw(num);
494 }
495}
496
497#[cfg(feature = "rt")]
498pub(crate) mod rt {
499 use esp_riscv_rt::TrapFrame;
500 use riscv::register::mcause;
501
502 use super::*;
503 use crate::interrupt::InterruptStatus;
504
505 #[cfg(not(interrupt_controller = "clic"))]
507 const INTERRUPT_COUNT: usize = const {
508 let mut count = 0;
509 for_each_interrupt!(([$_class:tt $n:tt] $_:literal) => { count += 1; };);
510 count
511 };
512
513 #[cfg(not(interrupt_controller = "clic"))]
515 #[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
516 pub(super) static INTERRUPT_TO_PRIORITY: [Option<Priority>; INTERRUPT_COUNT] = const {
517 let mut priorities = [None; INTERRUPT_COUNT];
518
519 for_each_interrupt!(
520 ([vector $n:tt] $int:literal) => {
521 for_each_interrupt_priority!(($n, $__:tt, $ident:ident, $_level:ident) => { priorities[$int] = Some(Priority::$ident); };);
522 };
523 );
524
525 priorities
526 };
527
528 #[doc(hidden)]
532 #[unsafe(link_section = ".trap.rust")]
533 #[unsafe(export_name = "_start_trap_rust_hal")]
534 unsafe extern "C" fn start_trap_rust_hal(trap_frame: *mut TrapFrame) {
535 assert!(
536 mcause::read().is_exception(),
537 "Arrived into _start_trap_rust_hal but mcause is not an exception!"
538 );
539 unsafe extern "C" {
540 fn ExceptionHandler(tf: *mut TrapFrame);
541 }
542 unsafe {
543 ExceptionHandler(trap_frame);
544 }
545 }
546
547 #[doc(hidden)]
548 #[unsafe(no_mangle)]
549 unsafe fn _setup_interrupts() {
550 crate::interrupt::setup_interrupts();
551
552 cpu_int::init();
553
554 #[cfg(interrupt_controller = "plic")]
555 unsafe {
556 core::arch::asm!("csrw mie, {0}", in(reg) u32::MAX);
557 }
558 }
559
560 #[unsafe(no_mangle)]
561 #[crate::ram]
562 unsafe fn handle_interrupts(cpu_intr: CpuInterrupt) {
563 let status = InterruptStatus::current();
564
565 cpu_intr.clear();
568
569 cfg_if::cfg_if! {
570 if #[cfg(interrupt_controller = "clic")] {
571 let prio = cpu_int::current_runlevel();
572 let mcause = riscv::register::mcause::read();
573 } else {
574 let prio = unwrap!(INTERRUPT_TO_PRIORITY[cpu_intr as usize]);
576 let level = unsafe { change_current_runlevel(RunLevel::Interrupt(ElevatedRunLevel::from(prio))) };
577 let prio = prio as u8;
578 }
579 }
580
581 let handle_interrupts = || unsafe {
582 for interrupt_nr in status.iterator().filter(|&interrupt_nr| {
583 crate::interrupt::should_handle(Cpu::current(), interrupt_nr as u32, prio as u32)
584 }) {
585 let handler =
586 crate::soc::pac::__EXTERNAL_INTERRUPTS[interrupt_nr as usize]._handler;
587
588 handler();
589 }
590 };
591
592 if prio != Priority::max() as u8 {
596 unsafe {
597 riscv::interrupt::nested(handle_interrupts);
598 }
599 } else {
600 handle_interrupts();
601 }
602
603 cfg_if::cfg_if! {
604 if #[cfg(interrupt_controller = "clic")] {
605 unsafe {
610 core::arch::asm!("csrw 0x342, {}", in(reg) mcause.bits())
611 }
612 } else {
613 unsafe { change_current_runlevel(level) };
614 }
615 }
616 }
617}