esp-hal 1.1.0

Bare-metal HAL for Espressif devices
Documentation
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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
//! Interrupt handling
//!
//! Peripheral interrupts go through the interrupt matrix, which routes them to the appropriate CPU
//! interrupt line. The interrupt matrix is largely the same across devices, but CPU interrupts are
//! device-specific before CLIC.
//!
//! Peripheral interrupts can be bound directly to CPU interrupts for better performance, but
//! due to the limited number of CPU interrupts, the preferred mechanism is to use the vectored
//! interrupts. The vectored interrupt handlers will call the appropriate interrupt handlers.
//! The configuration of vectored interrupt handlers cannot be changed in runtime.

#[cfg(feature = "rt")]
#[instability::unstable]
pub use esp_riscv_rt::TrapFrame;

#[cfg_attr(interrupt_controller = "riscv_basic", path = "riscv/basic.rs")]
#[cfg_attr(interrupt_controller = "plic", path = "riscv/plic.rs")]
#[cfg_attr(interrupt_controller = "clic", path = "riscv/clic.rs")]
mod cpu_int;

use crate::{
    interrupt::{PriorityError, RunLevel},
    peripherals::Interrupt,
    system::Cpu,
};

/// Interrupt kind
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[instability::unstable]
pub enum InterruptKind {
    /// Level interrupt
    Level,
    /// Edge interrupt
    Edge,
}

for_each_interrupt!(
    (all $( ([$class:ident $idx_in_class:literal] $n:literal) ),*) => {
        paste::paste! {
            /// Enumeration of available CPU interrupts.
            #[repr(u32)]
            #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
            #[cfg_attr(feature = "defmt", derive(defmt::Format))]
            #[instability::unstable]
            pub enum CpuInterrupt {
                $(
                    #[doc = concat!(" Interrupt number ", stringify!($n), ".")]
                    [<Interrupt $n>] = $n,
                )*
            }

            impl CpuInterrupt {
                #[inline]
                pub(crate) fn from_u32(n: u32) -> Option<Self> {
                    match n {
                        $(n if n == $n && n != DISABLED_CPU_INTERRUPT => Some(Self:: [<Interrupt $n>]),)*
                        _ => None
                    }
                }
            }
        }
    };
);

for_each_classified_interrupt!(
    (direct_bindable $( ([$class:ident $idx_in_class:literal] $n:literal) ),*) => {
        paste::paste! {
            /// Enumeration of CPU interrupts available for direct binding.
            #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
            #[cfg_attr(feature = "defmt", derive(defmt::Format))]
            pub enum DirectBindableCpuInterrupt {
                $(
                    #[doc = concat!(" Direct bindable CPU interrupt number ", stringify!($idx_in_class), ".")]
                    #[doc = " "]
                    #[doc = concat!(" Corresponds to CPU interrupt ", stringify!($n), ".")]
                    [<Interrupt $idx_in_class>] = $n,
                )*
            }

            impl From<DirectBindableCpuInterrupt> for CpuInterrupt {
                fn from(bindable: DirectBindableCpuInterrupt) -> CpuInterrupt {
                    match bindable {
                        $(
                            DirectBindableCpuInterrupt::[<Interrupt $idx_in_class>] => CpuInterrupt::[<Interrupt $n>],
                        )*
                    }
                }
            }
        }
    };
);

impl CpuInterrupt {
    #[inline]
    #[cfg(feature = "rt")]
    pub(crate) fn is_vectored(self) -> bool {
        // Assumes contiguous interrupt allocation.
        const VECTORED_CPU_INTERRUPT_RANGE: core::ops::RangeInclusive<u32> = PRIORITY_TO_INTERRUPT
            [0] as u32
            ..=PRIORITY_TO_INTERRUPT[PRIORITY_TO_INTERRUPT.len() - 1] as u32;
        VECTORED_CPU_INTERRUPT_RANGE.contains(&(self as u32))
    }

    /// Enable the CPU interrupt
    #[inline]
    #[instability::unstable]
    pub fn enable(self) {
        cpu_int::enable_cpu_interrupt_raw(self as u32);
    }

    /// Clear the CPU interrupt status bit
    #[inline]
    #[instability::unstable]
    pub fn clear(self) {
        cpu_int::clear_raw(self as u32);
    }

    /// Set the interrupt kind (i.e. level or edge) of an CPU interrupt
    ///
    /// This is safe to call when the `vectored` feature is enabled. The
    /// vectored interrupt handler will take care of clearing edge interrupt
    /// bits.
    #[inline]
    #[instability::unstable]
    pub fn set_kind(self, kind: InterruptKind) {
        cpu_int::set_kind_raw(self as u32, kind);
    }

    /// Set the priority level of a CPU interrupt
    #[inline]
    #[instability::unstable]
    pub fn set_priority(self, priority: Priority) {
        cpu_int::set_priority_raw(self as u32, priority);
    }

    /// Get interrupt priority for the CPU
    #[inline]
    #[instability::unstable]
    pub fn priority(self) -> Priority {
        unwrap!(Priority::try_from_u32(self.level()))
    }

    #[inline]
    pub(crate) fn level(self) -> u32 {
        cpu_int::cpu_interrupt_priority_raw(self as u32) as u32
    }
}

for_each_interrupt_priority!(
    (all $( ($idx:literal, $n:literal, $ident:ident, $level:ident) ),*) => {
        /// Interrupt priority levels.
        ///
        /// A higher numeric value means higher priority. Interrupt requests at higher priority
        /// levels will be able to preempt code running at a lower [`RunLevel`][super::RunLevel].
        #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
        #[cfg_attr(feature = "defmt", derive(defmt::Format))]
        #[repr(u8)]
        pub enum Priority {
            $(
                #[doc = concat!(" Priority level ", stringify!($n), ".")]
                $ident = $n,
            )*
        }

        impl Priority {
            fn iter() -> impl Iterator<Item = Priority> {
                [$(Priority::$ident,)*].into_iter()
            }
        }

        /// Interrupt run levels.
        #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
        #[cfg_attr(feature = "defmt", derive(defmt::Format))]
        #[repr(u8)]
        pub enum ElevatedRunLevel {
            $(
                #[doc = concat!("Run level ", stringify!($n), ".")]
                $level = $n,
            )*
        }

        impl ElevatedRunLevel {
            /// Converts a [`Priority`] into an [`ElevatedRunLevel`].
            pub const fn from_priority(priority: Priority) -> Self {
                match priority {
                    $(Priority::$ident => Self::$level,)*
                }
            }
        }
    };
);

impl Priority {
    /// Maximum interrupt priority
    #[allow(unused_assignments)]
    #[instability::unstable]
    pub const fn max() -> Priority {
        const {
            let mut last = Self::min();
            for_each_interrupt_priority!(
                ($_idx:literal, $_n:literal, $ident:ident, $_level:ident) => {
                    last = Self::$ident;
                };
            );
            last
        }
    }

    /// Minimum interrupt priority
    pub const fn min() -> Priority {
        Priority::Priority1
    }

    pub(crate) fn try_from_u32(priority: u32) -> Result<Self, PriorityError> {
        let result;
        for_each_interrupt_priority!(
            (all $( ($idx:literal, $n:literal, $ident:ident, $_level:ident) ),*) => {
                result = match priority {
                    $($n => Ok(Priority::$ident),)*
                    _ => Err(PriorityError::InvalidInterruptPriority),
                }
            };
        );
        result
    }
}

impl ElevatedRunLevel {
    /// Returns the highest run level
    #[instability::unstable]
    pub const fn max() -> ElevatedRunLevel {
        Self::from_priority(Priority::max())
    }

    /// Minimum elevated run level
    pub const fn min() -> ElevatedRunLevel {
        Self::from_priority(Priority::min())
    }

    pub(crate) fn try_from_u32(level: u32) -> Result<Self, PriorityError> {
        Priority::try_from_u32(level).map(Self::from_priority)
    }
}

#[instability::unstable]
impl TryFrom<u32> for ElevatedRunLevel {
    type Error = PriorityError;

    fn try_from(value: u32) -> Result<Self, Self::Error> {
        Self::try_from_u32(value)
    }
}

impl From<Priority> for ElevatedRunLevel {
    fn from(priority: Priority) -> Self {
        Self::from_priority(priority)
    }
}

#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
pub(super) static DISABLED_CPU_INTERRUPT: u32 = property!("interrupts.disabled_interrupt");

/// The number of vectored interrupts / The number of priority levels.
const VECTOR_COUNT: usize = const {
    let mut count = 0;
    for_each_interrupt!(([vector $n:tt] $_:literal) => { count += 1; };);

    core::assert!(count == Priority::max() as usize);

    count
};

/// Maps priority levels to their corresponding interrupt vectors.
#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
pub(super) static PRIORITY_TO_INTERRUPT: [CpuInterrupt; VECTOR_COUNT] = const {
    let mut counter = 0;
    let mut vector = [CpuInterrupt::Interrupt0; VECTOR_COUNT];

    for_each_interrupt!(
        ([vector $_n:tt] $interrupt:literal) => {
            vector[counter] = paste::paste! { CpuInterrupt::[<Interrupt $interrupt>] };
            counter += 1;
        };
    );
    vector
};

/// Enable an interrupt by directly binding it to an available CPU interrupt
///
/// ⚠️ This installs a *raw trap handler*, the `handler` user provides is written directly into the
/// CPU interrupt vector table. That means:
///
/// - Provided handler will be used as an actual trap-handler
/// - It is user's responsibility to:
///   - Save and restore all registers they use.
///   - Clear the interrupt source if necessary.
///   - Return using the `mret` instruction.
/// - The handler should be declared as naked function. The compiler will not insert a function
///   prologue/epilogue for the user, normal Rust `fn` will result in an error.
///
/// Unless you are sure that you need such low-level control to achieve the lowest possible latency,
/// you most likely want to use [`enable`][crate::interrupt::enable] instead.
#[instability::unstable]
pub fn enable_direct(
    interrupt: Interrupt,
    level: Priority,
    cpu_interrupt: DirectBindableCpuInterrupt,
    handler: unsafe extern "C" fn(),
) {
    cfg_if::cfg_if! {
        if #[cfg(interrupt_controller = "clic")] {
            let clic = unsafe { crate::soc::pac::CLIC::steal() };

            // Enable hardware vectoring
            clic.int_attr(cpu_interrupt as usize).modify(|_, w| {
                w.shv().hardware();
                w.trig().positive_level()
            });

            let mtvt_table: *mut [u32; 48];
            unsafe { core::arch::asm!("csrr {0}, 0x307", out(reg) mtvt_table) };

            let int_slot = mtvt_table
                .cast::<u32>()
                .wrapping_add(cpu_interrupt as usize);

            let instr = handler as usize as u32;
        } else {
            use riscv::register::mtvec;
            let mt = mtvec::read();

            assert_eq!(
                mt.trap_mode().into_usize(),
                mtvec::TrapMode::Vectored.into_usize()
            );

            let base_addr = mt.address() as usize;

            let int_slot = base_addr.wrapping_add((cpu_interrupt as usize) * 4) as *mut u32;

            let instr = encode_jal_x0(handler as usize, int_slot as usize);
        }
    }

    if crate::debugger::debugger_connected() {
        unsafe { core::ptr::write_volatile(int_slot, instr) };
    } else {
        crate::debugger::DEBUGGER_LOCK.lock(|| unsafe {
            let wp = crate::debugger::clear_watchpoint(1);
            core::ptr::write_volatile(int_slot, instr);
            crate::debugger::restore_watchpoint(1, wp);
        });
    }
    unsafe {
        core::arch::asm!("fence.i");
    }

    super::map_raw(Cpu::current(), interrupt, cpu_interrupt as u32);
    cpu_int::set_priority_raw(cpu_interrupt as u32, level);
    cpu_int::set_kind_raw(cpu_interrupt as u32, InterruptKind::Level);
    cpu_int::enable_cpu_interrupt_raw(cpu_interrupt as u32);
}

// helper: returns correctly encoded RISC-V `jal` instruction
#[cfg(not(interrupt_controller = "clic"))]
fn encode_jal_x0(target: usize, pc: usize) -> u32 {
    let offset = (target as isize) - (pc as isize);

    const MIN: isize = -(1isize << 20);
    const MAX: isize = (1isize << 20) - 1;

    assert!(offset % 2 == 0 && (MIN..=MAX).contains(&offset));

    let imm = offset as u32;
    let imm20 = (imm >> 20) & 0x1;
    let imm10_1 = (imm >> 1) & 0x3ff;
    let imm11 = (imm >> 11) & 0x1;
    let imm19_12 = (imm >> 12) & 0xff;

    (imm20 << 31)
        | (imm19_12 << 12)
        | (imm11 << 20)
        | (imm10_1 << 21)
        // https://lhtin.github.io/01world/app/riscv-isa/?xlen=32&insn_name=jal
        | 0b1101111u32
}

// Runlevel APIs

/// Get the current run level (the level below which interrupts are masked).
pub(crate) fn current_raw_runlevel() -> u32 {
    cpu_int::current_runlevel() as u32
}

/// Changes the current run level (the level below which interrupts are
/// masked), and returns the previous run level.
///
/// # Safety
///
/// This function must only be used to raise the runlevel and to restore it
/// to a previous value. It must not be used to arbitrarily lower the
/// runlevel.
pub(crate) unsafe fn change_current_runlevel(level: RunLevel) -> RunLevel {
    let previous = cpu_int::change_current_runlevel(level);
    unwrap!(RunLevel::try_from_u32(previous as u32))
}

fn cpu_wait_mode_on() -> bool {
    cfg_if::cfg_if! {
        if #[cfg(soc_has_pcr)] {
            crate::peripherals::PCR::regs().cpu_waiti_conf().read().cpu_wait_mode_force_on().bit_is_set()
        } else {
            crate::peripherals::SYSTEM::regs()
                .cpu_per_conf()
                .read()
                .cpu_wait_mode_force_on()
                .bit_is_set()
        }
    }
}

/// Wait for an interrupt to occur.
///
/// This function causes the current CPU core to execute its Wait For Interrupt
/// (WFI or equivalent) instruction. After executing this function, the CPU core
/// will stop execution until an interrupt occurs.
///
/// This function will return immediately when a debugger is attached, so it is intended to be
/// called in a loop.
#[inline(always)]
#[instability::unstable]
pub fn wait_for_interrupt() {
    if crate::debugger::debugger_connected() && !cpu_wait_mode_on() {
        // when SYSTEM_CPU_WAIT_MODE_FORCE_ON is disabled in WFI mode SBA access to memory does not
        // work for debugger, so do not enter that mode when debugger is connected.
        // https://github.com/espressif/esp-idf/blob/b9a308a47ca4128d018495662b009a7c461b6780/components/esp_hw_support/cpu.c#L57-L60
        return;
    }
    unsafe { core::arch::asm!("wfi") };
}

pub(crate) fn priority_to_cpu_interrupt(_interrupt: Interrupt, level: Priority) -> CpuInterrupt {
    PRIORITY_TO_INTERRUPT[(level as usize) - 1]
}

/// Setup interrupts ready for vectoring
///
/// # Safety
///
/// This function must be called only during core startup.
#[cfg(any(feature = "rt", all(feature = "unstable", multi_core)))]
pub(crate) unsafe fn init_vectoring() {
    use riscv::register::mtvec;

    unsafe extern "C" {
        static _vector_table: u32;
        #[cfg(interrupt_controller = "clic")]
        static _mtvt_table: u32;
    }

    unsafe {
        let vec_table = (&raw const _vector_table).addr();

        #[cfg(not(interrupt_controller = "clic"))]
        {
            mtvec::write({
                let mut mtvec = mtvec::Mtvec::from_bits(0);
                mtvec.set_trap_mode(mtvec::TrapMode::Vectored);
                mtvec.set_address(vec_table);
                mtvec
            });
        }

        #[cfg(interrupt_controller = "clic")]
        {
            mtvec::write({
                let mut mtvec = mtvec::Mtvec::from_bits(0x03); // MODE = CLIC
                mtvec.set_address(vec_table);
                mtvec
            });

            // set mtvt (hardware vector base)
            let mtvt_table = (&raw const _mtvt_table).addr();
            core::arch::asm!("csrw 0x307, {0}", in(reg) mtvt_table);
        }
    };

    // Configure and enable vectored interrupts
    for (int, prio) in PRIORITY_TO_INTERRUPT.iter().copied().zip(Priority::iter()) {
        let num = int as u32;
        cpu_int::set_kind_raw(num, InterruptKind::Level);
        cpu_int::set_priority_raw(num, prio);
        cpu_int::enable_cpu_interrupt_raw(num);
    }
}

#[cfg(feature = "rt")]
pub(crate) mod rt {
    use esp_riscv_rt::TrapFrame;
    use riscv::register::mcause;

    use super::*;
    use crate::interrupt::InterruptStatus;

    /// The total number of interrupts.
    #[cfg(not(interrupt_controller = "clic"))]
    const INTERRUPT_COUNT: usize = const {
        let mut count = 0;
        for_each_interrupt!(([$_class:tt $n:tt] $_:literal) => { count += 1; };);
        count
    };

    /// Maps interrupt numbers to their vector priority levels.
    #[cfg(not(interrupt_controller = "clic"))]
    #[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
    pub(super) static INTERRUPT_TO_PRIORITY: [Option<Priority>; INTERRUPT_COUNT] = const {
        let mut priorities = [None; INTERRUPT_COUNT];

        for_each_interrupt!(
            ([vector $n:tt] $int:literal) => {
                for_each_interrupt_priority!(($n, $__:tt, $ident:ident, $_level:ident) => { priorities[$int] = Some(Priority::$ident); };);
            };
        );

        priorities
    };

    /// # Safety
    ///
    /// This function is called from an assembly trap handler.
    #[doc(hidden)]
    #[unsafe(link_section = ".trap.rust")]
    #[unsafe(export_name = "_start_trap_rust_hal")]
    unsafe extern "C" fn start_trap_rust_hal(trap_frame: *mut TrapFrame) {
        assert!(
            mcause::read().is_exception(),
            "Arrived into _start_trap_rust_hal but mcause is not an exception!"
        );
        unsafe extern "C" {
            fn ExceptionHandler(tf: *mut TrapFrame);
        }
        unsafe {
            ExceptionHandler(trap_frame);
        }
    }

    #[doc(hidden)]
    #[unsafe(no_mangle)]
    unsafe fn _setup_interrupts() {
        crate::interrupt::setup_interrupts();

        cpu_int::init();

        #[cfg(interrupt_controller = "plic")]
        unsafe {
            core::arch::asm!("csrw mie, {0}", in(reg) u32::MAX);
        }
    }

    #[unsafe(no_mangle)]
    #[crate::ram]
    unsafe fn handle_interrupts(cpu_intr: CpuInterrupt) {
        let status = InterruptStatus::current();

        // this has no effect on level interrupts, but the interrupt may be an edge one
        // so we clear it anyway
        cpu_intr.clear();

        cfg_if::cfg_if! {
            if #[cfg(interrupt_controller = "clic")] {
                let prio = cpu_int::current_runlevel();
                let mcause = riscv::register::mcause::read();
            } else {
                // Change the current runlevel so that interrupt handlers can access the correct runlevel.
                let prio = unwrap!(INTERRUPT_TO_PRIORITY[cpu_intr as usize]);
                let level = unsafe { change_current_runlevel(RunLevel::Interrupt(ElevatedRunLevel::from(prio))) };
                let prio = prio as u8;
            }
        }

        let handle_interrupts = || unsafe {
            for interrupt_nr in status.iterator().filter(|&interrupt_nr| {
                crate::interrupt::should_handle(Cpu::current(), interrupt_nr as u32, prio as u32)
            }) {
                let handler =
                    crate::soc::pac::__EXTERNAL_INTERRUPTS[interrupt_nr as usize]._handler;

                handler();
            }
        };

        // Do not enable nesting on the highest priority level. Older interrupt controllers couldn't
        // properly mask the highest priority interrupt, and for CLIC we don't want to waste
        // the cycles it takes to enable nesting unnecessarily.
        if prio != Priority::max() as u8 {
            unsafe {
                riscv::interrupt::nested(handle_interrupts);
            }
        } else {
            handle_interrupts();
        }

        cfg_if::cfg_if! {
            if #[cfg(interrupt_controller = "clic")] {
                // In case the target uses the CLIC, it is mandatory to restore `mcause` register
                // since it contains the former CPU priority. When executing `mret`,
                // the hardware will restore the former threshold, from `mcause` to
                // `mintstatus` CSR
                unsafe {
                    core::arch::asm!("csrw 0x342, {}", in(reg) mcause.bits())
                }
            } else {
                unsafe { change_current_runlevel(level) };
            }
        }
    }
}