esp32_c3/
chip.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! High-level setup and interrupt mapping for the chip.
6
7use core::fmt::Write;
8use core::ptr::addr_of;
9
10use kernel::platform::chip::{Chip, InterruptService};
11use kernel::utilities::registers::interfaces::{ReadWriteable, Readable, Writeable};
12use kernel::utilities::StaticRef;
13
14use rv32i::csr::{self, mcause, mtvec::mtvec, CSR};
15use rv32i::pmp::{simple::SimplePMP, PMPUserMPU};
16use rv32i::syscall::SysCall;
17
18use crate::intc::{Intc, IntcRegisters};
19use crate::interrupts;
20use crate::rng;
21use crate::sysreg;
22use crate::timg;
23
24pub const INTC_BASE: StaticRef<IntcRegisters> =
25    unsafe { StaticRef::new(0x600C_2000 as *const IntcRegisters) };
26
27pub static mut INTC: Intc = Intc::new(INTC_BASE);
28
29pub struct Esp32C3<'a, I: InterruptService + 'a> {
30    userspace_kernel_boundary: SysCall,
31    pub pmp: PMPUserMPU<8, SimplePMP<16>>,
32    intc: &'a Intc,
33    pic_interrupt_service: &'a I,
34}
35
36pub struct Esp32C3DefaultPeripherals<'a> {
37    pub uart0: esp32::uart::Uart<'a>,
38    pub timg0: timg::TimG<'a>,
39    pub timg1: timg::TimG<'a>,
40    pub gpio: esp32::gpio::Port<'a>,
41    pub rtc_cntl: esp32::rtc_cntl::RtcCntl,
42    pub sysreg: sysreg::SysReg,
43    pub rng: rng::Rng<'a>,
44}
45
46impl Esp32C3DefaultPeripherals<'_> {
47    pub fn new() -> Self {
48        Self {
49            uart0: esp32::uart::Uart::new(esp32::uart::UART0_BASE),
50            timg0: timg::TimG::new(timg::TIMG0_BASE, timg::ClockSource::Pll),
51            timg1: timg::TimG::new(timg::TIMG1_BASE, timg::ClockSource::Pll),
52            gpio: esp32::gpio::Port::new(),
53            rtc_cntl: esp32::rtc_cntl::RtcCntl::new(esp32::rtc_cntl::RTC_CNTL_BASE),
54            sysreg: sysreg::SysReg::new(),
55            rng: rng::Rng::new(),
56        }
57    }
58
59    pub fn init(&'static self) {
60        kernel::deferred_call::DeferredCallClient::register(&self.rng);
61    }
62}
63
64impl InterruptService for Esp32C3DefaultPeripherals<'_> {
65    unsafe fn service_interrupt(&self, interrupt: u32) -> bool {
66        match interrupt {
67            interrupts::IRQ_UART0 => self.uart0.handle_interrupt(),
68
69            interrupts::IRQ_TIMER1 => self.timg0.handle_interrupt(),
70            interrupts::IRQ_TIMER2 => self.timg1.handle_interrupt(),
71
72            interrupts::IRQ_GPIO | interrupts::IRQ_GPIO_NMI => self.gpio.handle_interrupt(),
73
74            _ => return false,
75        }
76        true
77    }
78}
79
80impl<'a, I: InterruptService + 'a> Esp32C3<'a, I> {
81    pub unsafe fn new(pic_interrupt_service: &'a I) -> Self {
82        Self {
83            userspace_kernel_boundary: SysCall::new(),
84            pmp: PMPUserMPU::new(SimplePMP::new().unwrap()),
85            intc: &*addr_of!(INTC),
86            pic_interrupt_service,
87        }
88    }
89
90    pub fn map_pic_interrupts(&self) {
91        self.intc.map_interrupts();
92    }
93
94    pub unsafe fn enable_pic_interrupts(&self) {
95        self.intc.enable_all();
96    }
97
98    unsafe fn handle_pic_interrupts(&self) {
99        while let Some(interrupt) = self.intc.get_saved_interrupts() {
100            if !self.pic_interrupt_service.service_interrupt(interrupt) {
101                panic!("Unhandled interrupt {}", interrupt);
102            }
103            self.atomic(|| {
104                // Safe as interrupts are disabled
105                self.intc.complete(interrupt);
106            });
107        }
108    }
109}
110
111impl<'a, I: InterruptService + 'a> Chip for Esp32C3<'a, I> {
112    type MPU = PMPUserMPU<8, SimplePMP<16>>;
113    type UserspaceKernelBoundary = SysCall;
114
115    fn service_pending_interrupts(&self) {
116        loop {
117            if self.intc.get_saved_interrupts().is_some() {
118                unsafe {
119                    self.handle_pic_interrupts();
120                }
121            }
122
123            if self.intc.get_saved_interrupts().is_none() {
124                break;
125            }
126        }
127
128        self.intc.enable_all();
129    }
130
131    fn has_pending_interrupts(&self) -> bool {
132        self.intc.get_saved_interrupts().is_some()
133    }
134
135    fn mpu(&self) -> &Self::MPU {
136        &self.pmp
137    }
138
139    fn userspace_kernel_boundary(&self) -> &SysCall {
140        &self.userspace_kernel_boundary
141    }
142
143    fn sleep(&self) {
144        unsafe {
145            rv32i::support::wfi();
146        }
147    }
148
149    unsafe fn atomic<F, R>(&self, f: F) -> R
150    where
151        F: FnOnce() -> R,
152    {
153        rv32i::support::atomic(f)
154    }
155
156    unsafe fn print_state(&self, writer: &mut dyn Write) {
157        let mcval: csr::mcause::Trap = core::convert::From::from(csr::CSR.mcause.extract());
158        let _ = writer.write_fmt(format_args!("\r\n---| RISC-V Machine State |---\r\n"));
159        let _ = writer.write_fmt(format_args!("Last cause (mcause): "));
160        rv32i::print_mcause(mcval, writer);
161        let interrupt = csr::CSR.mcause.read(csr::mcause::mcause::is_interrupt);
162        let code = csr::CSR.mcause.read(csr::mcause::mcause::reason);
163        let _ = writer.write_fmt(format_args!(
164            " (interrupt={}, exception code={:#010X})",
165            interrupt, code
166        ));
167        let _ = writer.write_fmt(format_args!(
168            "\r\nLast value (mtval):  {:#010X}\
169         \r\n\
170         \r\nSystem register dump:\
171         \r\n mepc:    {:#010X}    mstatus:     {:#010X}\
172         \r\n mtvec:   {:#010X}",
173            csr::CSR.mtval.get(),
174            csr::CSR.mepc.get(),
175            csr::CSR.mstatus.get(),
176            csr::CSR.mtvec.get()
177        ));
178        let mstatus = csr::CSR.mstatus.extract();
179        let uie = mstatus.is_set(csr::mstatus::mstatus::uie);
180        let sie = mstatus.is_set(csr::mstatus::mstatus::sie);
181        let mie = mstatus.is_set(csr::mstatus::mstatus::mie);
182        let upie = mstatus.is_set(csr::mstatus::mstatus::upie);
183        let spie = mstatus.is_set(csr::mstatus::mstatus::spie);
184        let mpie = mstatus.is_set(csr::mstatus::mstatus::mpie);
185        let spp = mstatus.is_set(csr::mstatus::mstatus::spp);
186        let _ = writer.write_fmt(format_args!(
187            "\r\n mstatus: {:#010X}\
188         \r\n  uie:    {:5}  upie:   {}\
189         \r\n  sie:    {:5}  spie:   {}\
190         \r\n  mie:    {:5}  mpie:   {}\
191         \r\n  spp:    {}",
192            mstatus.get(),
193            uie,
194            upie,
195            sie,
196            spie,
197            mie,
198            mpie,
199            spp
200        ));
201    }
202}
203
204fn handle_exception(exception: mcause::Exception) {
205    match exception {
206        mcause::Exception::UserEnvCall | mcause::Exception::SupervisorEnvCall => (),
207
208        mcause::Exception::InstructionMisaligned
209        | mcause::Exception::InstructionFault
210        | mcause::Exception::IllegalInstruction
211        | mcause::Exception::Breakpoint
212        | mcause::Exception::LoadMisaligned
213        | mcause::Exception::LoadFault
214        | mcause::Exception::StoreMisaligned
215        | mcause::Exception::StoreFault
216        | mcause::Exception::MachineEnvCall
217        | mcause::Exception::InstructionPageFault
218        | mcause::Exception::LoadPageFault
219        | mcause::Exception::StorePageFault
220        | mcause::Exception::Unknown => {
221            panic!("fatal exception: {:?}: {:#x}", exception, CSR.mtval.get());
222        }
223    }
224}
225
226unsafe fn handle_interrupt(_intr: mcause::Interrupt) {
227    CSR.mstatus.modify(csr::mstatus::mstatus::mie::CLEAR);
228
229    // Claim the interrupt, unwrap() as we know an interrupt exists
230    // Once claimed this interrupt won't fire until it's completed
231    // NOTE: The interrupt is no longer pending in the PLIC
232    loop {
233        let interrupt = (*addr_of!(INTC)).next_pending();
234
235        match interrupt {
236            Some(irq) => {
237                // Safe as interrupts are disabled
238                (*addr_of!(INTC)).save_interrupt(irq);
239                (*addr_of!(INTC)).disable(irq);
240            }
241            None => {
242                // Enable generic interrupts
243                CSR.mstatus.modify(csr::mstatus::mstatus::mie::SET);
244                break;
245            }
246        }
247    }
248}
249
250/// Trap handler for board/chip specific code.
251///
252/// This gets called when an interrupt occurs while the chip is
253/// in kernel mode.
254#[export_name = "_start_trap_rust_from_kernel"]
255pub unsafe extern "C" fn start_trap_rust() {
256    match mcause::Trap::from(CSR.mcause.extract()) {
257        mcause::Trap::Interrupt(interrupt) => {
258            handle_interrupt(interrupt);
259        }
260        mcause::Trap::Exception(exception) => {
261            handle_exception(exception);
262        }
263    }
264}
265
266/// Function that gets called if an interrupt occurs while an app was running.
267///
268/// mcause is passed in, and this function should correctly handle disabling the
269/// interrupt that fired so that it does not trigger again.
270#[export_name = "_disable_interrupt_trap_rust_from_app"]
271pub unsafe extern "C" fn disable_interrupt_trap_handler(mcause_val: u32) {
272    match mcause::Trap::from(mcause_val as usize) {
273        mcause::Trap::Interrupt(interrupt) => {
274            handle_interrupt(interrupt);
275        }
276        _ => {
277            panic!("unexpected non-interrupt\n");
278        }
279    }
280}
281
282/// The ESP32C3 should support non-vectored and vectored interrupts, but
283/// vectored interrupts seem more reliable so let's use that.
284pub unsafe fn configure_trap_handler() {
285    CSR.mtvec
286        .write(mtvec::trap_addr.val(_start_trap_vectored as usize >> 2) + mtvec::mode::Vectored)
287}
288
289// Mock implementation for crate tests that does not include the section
290// specifier, as the test will not use our linker script, and the host
291// compilation environment may not allow the section name.
292#[cfg(not(any(doc, all(target_arch = "riscv32", target_os = "none"))))]
293pub extern "C" fn _start_trap_vectored() {
294    use core::hint::unreachable_unchecked;
295    unsafe {
296        unreachable_unchecked();
297    }
298}
299
300#[cfg(any(doc, all(target_arch = "riscv32", target_os = "none")))]
301extern "C" {
302    pub fn _start_trap_vectored();
303}
304
305#[cfg(any(doc, all(target_arch = "riscv32", target_os = "none")))]
306// Below are 32 (non-compressed) jumps to cover the entire possible
307// range of vectored traps.
308core::arch::global_asm!(
309    "
310            .section .riscv.trap_vectored, \"ax\"
311            .globl _start_trap_vectored
312          _start_trap_vectored:
313            j _start_trap
314            j _start_trap
315            j _start_trap
316            j _start_trap
317            j _start_trap
318            j _start_trap
319            j _start_trap
320            j _start_trap
321            j _start_trap
322            j _start_trap
323            j _start_trap
324            j _start_trap
325            j _start_trap
326            j _start_trap
327            j _start_trap
328            j _start_trap
329            j _start_trap
330            j _start_trap
331            j _start_trap
332            j _start_trap
333            j _start_trap
334            j _start_trap
335            j _start_trap
336            j _start_trap
337            j _start_trap
338            j _start_trap
339            j _start_trap
340            j _start_trap
341            j _start_trap
342            j _start_trap
343            j _start_trap
344        "
345);