e310x/
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;
9use kernel::debug;
10use kernel::platform::chip::Chip;
11use kernel::utilities::registers::interfaces::{ReadWriteable, Readable};
12use rv32i::csr;
13use rv32i::csr::{mcause, mie::mie, mip::mip, CSR};
14use rv32i::pmp::{simple::SimplePMP, PMPUserMPU};
15
16use crate::plic::PLIC;
17use kernel::hil::time::Freq32KHz;
18use kernel::platform::chip::InterruptService;
19use sifive::plic::Plic;
20
21pub type E310xClint<'a> = sifive::clint::Clint<'a, Freq32KHz>;
22
23pub struct E310x<'a, I: InterruptService + 'a> {
24    userspace_kernel_boundary: rv32i::syscall::SysCall,
25    pmp: PMPUserMPU<4, SimplePMP<8>>,
26    plic: &'a Plic,
27    timer: &'a E310xClint<'a>,
28    plic_interrupt_service: &'a I,
29}
30
31pub struct E310xDefaultPeripherals<'a> {
32    pub uart0: sifive::uart::Uart<'a>,
33    pub uart1: sifive::uart::Uart<'a>,
34    pub gpio_port: crate::gpio::Port<'a>,
35    pub prci: sifive::prci::Prci,
36    pub pwm0: sifive::pwm::Pwm,
37    pub pwm1: sifive::pwm::Pwm,
38    pub pwm2: sifive::pwm::Pwm,
39    pub rtc: sifive::rtc::Rtc,
40    pub watchdog: sifive::watchdog::Watchdog,
41}
42
43impl E310xDefaultPeripherals<'_> {
44    pub fn new(clock_frequency: u32) -> Self {
45        Self {
46            uart0: sifive::uart::Uart::new(crate::uart::UART0_BASE, clock_frequency),
47            uart1: sifive::uart::Uart::new(crate::uart::UART1_BASE, clock_frequency),
48            gpio_port: crate::gpio::Port::new(),
49            prci: sifive::prci::Prci::new(crate::prci::PRCI_BASE),
50            pwm0: sifive::pwm::Pwm::new(crate::pwm::PWM0_BASE),
51            pwm1: sifive::pwm::Pwm::new(crate::pwm::PWM1_BASE),
52            pwm2: sifive::pwm::Pwm::new(crate::pwm::PWM2_BASE),
53            rtc: sifive::rtc::Rtc::new(crate::rtc::RTC_BASE),
54            watchdog: sifive::watchdog::Watchdog::new(crate::watchdog::WATCHDOG_BASE),
55        }
56    }
57
58    // Resolve any circular dependencies and register deferred calls
59    pub fn init(&'static self) {
60        kernel::deferred_call::DeferredCallClient::register(&self.uart0);
61        kernel::deferred_call::DeferredCallClient::register(&self.uart1);
62    }
63}
64
65impl InterruptService for E310xDefaultPeripherals<'_> {
66    unsafe fn service_interrupt(&self, _interrupt: u32) -> bool {
67        false
68    }
69}
70
71impl<'a, I: InterruptService + 'a> E310x<'a, I> {
72    pub unsafe fn new(plic_interrupt_service: &'a I, timer: &'a E310xClint<'a>) -> Self {
73        Self {
74            userspace_kernel_boundary: rv32i::syscall::SysCall::new(),
75            pmp: PMPUserMPU::new(SimplePMP::new().unwrap()),
76            plic: &*addr_of!(PLIC),
77            timer,
78            plic_interrupt_service,
79        }
80    }
81
82    pub unsafe fn enable_plic_interrupts(&self) {
83        /* E31 core manual
84         * https://sifive.cdn.prismic.io/sifive/c29f9c69-5254-4f9a-9e18-24ea73f34e81_e31_core_complex_manual_21G2.pdf
85         * PLIC Chapter 9.4 p.114: A pending bit in the PLIC core can be cleared
86         * by setting the associated enable bit then performing a claim.
87         */
88
89        // first disable interrupts globally
90        let old_mie = csr::CSR
91            .mstatus
92            .read_and_clear_field(csr::mstatus::mstatus::mie);
93
94        self.plic.enable_all();
95        self.plic.clear_all_pending();
96
97        // restore the old external interrupt enable bit
98        csr::CSR
99            .mstatus
100            .modify(csr::mstatus::mstatus::mie.val(old_mie));
101    }
102
103    unsafe fn handle_plic_interrupts(&self) {
104        while let Some(interrupt) = self.plic.get_saved_interrupts() {
105            if !self.plic_interrupt_service.service_interrupt(interrupt) {
106                debug!("Pidx {}", interrupt);
107            }
108            self.with_interrupts_disabled(|| {
109                self.plic.complete(interrupt);
110            });
111        }
112    }
113}
114
115impl<'a, I: InterruptService + 'a> kernel::platform::chip::Chip for E310x<'a, I> {
116    type MPU = PMPUserMPU<4, SimplePMP<8>>;
117    type UserspaceKernelBoundary = rv32i::syscall::SysCall;
118    type ThreadIdProvider = rv32i::thread_id::RiscvThreadIdProvider;
119
120    fn mpu(&self) -> &Self::MPU {
121        &self.pmp
122    }
123
124    fn userspace_kernel_boundary(&self) -> &rv32i::syscall::SysCall {
125        &self.userspace_kernel_boundary
126    }
127
128    fn service_pending_interrupts(&self) {
129        loop {
130            let mip = CSR.mip.extract();
131
132            if mip.is_set(mip::mtimer) {
133                self.timer.handle_interrupt();
134            }
135            if self.plic.get_saved_interrupts().is_some() {
136                unsafe {
137                    self.handle_plic_interrupts();
138                }
139            }
140
141            if !mip.any_matching_bits_set(mip::mtimer::SET)
142                && self.plic.get_saved_interrupts().is_none()
143            {
144                break;
145            }
146        }
147
148        // Re-enable all MIE interrupts that we care about. Since we looped
149        // until we handled them all, we can re-enable all of them.
150        CSR.mie.modify(mie::mext::SET + mie::mtimer::SET);
151    }
152
153    fn has_pending_interrupts(&self) -> bool {
154        // First check if the global machine timer interrupt is set.
155        // We would also need to check for additional global interrupt bits
156        // if there were to be used for anything in the future.
157        if CSR.mip.is_set(mip::mtimer) {
158            return true;
159        }
160
161        // Then we can check the PLIC.
162        self.plic.get_saved_interrupts().is_some()
163    }
164
165    fn sleep(&self) {
166        unsafe {
167            rv32i::support::wfi();
168        }
169    }
170
171    unsafe fn with_interrupts_disabled<F, R>(&self, f: F) -> R
172    where
173        F: FnOnce() -> R,
174    {
175        rv32i::support::with_interrupts_disabled(f)
176    }
177
178    unsafe fn print_state(&self, writer: &mut dyn Write) {
179        rv32i::print_riscv_state(writer);
180    }
181}
182
183fn handle_exception(exception: mcause::Exception) {
184    match exception {
185        mcause::Exception::UserEnvCall | mcause::Exception::SupervisorEnvCall => (),
186
187        mcause::Exception::InstructionMisaligned
188        | mcause::Exception::InstructionFault
189        | mcause::Exception::IllegalInstruction
190        | mcause::Exception::Breakpoint
191        | mcause::Exception::LoadMisaligned
192        | mcause::Exception::LoadFault
193        | mcause::Exception::StoreMisaligned
194        | mcause::Exception::StoreFault
195        | mcause::Exception::MachineEnvCall
196        | mcause::Exception::InstructionPageFault
197        | mcause::Exception::LoadPageFault
198        | mcause::Exception::StorePageFault
199        | mcause::Exception::Unknown => {
200            panic!("fatal exception");
201        }
202    }
203}
204
205unsafe fn handle_interrupt(intr: mcause::Interrupt) {
206    match intr {
207        mcause::Interrupt::UserSoft
208        | mcause::Interrupt::UserTimer
209        | mcause::Interrupt::UserExternal => {
210            panic!("unexpected user-mode interrupt");
211        }
212        mcause::Interrupt::SupervisorExternal
213        | mcause::Interrupt::SupervisorTimer
214        | mcause::Interrupt::SupervisorSoft => {
215            panic!("unexpected supervisor-mode interrupt");
216        }
217
218        mcause::Interrupt::MachineSoft => {
219            CSR.mie.modify(mie::msoft::CLEAR);
220        }
221        mcause::Interrupt::MachineTimer => {
222            CSR.mie.modify(mie::mtimer::CLEAR);
223        }
224        mcause::Interrupt::MachineExternal => {
225            // We received an interrupt, disable interrupts while we handle them
226            CSR.mie.modify(mie::mext::CLEAR);
227
228            // Claim the interrupt, unwrap() as we know an interrupt exists
229            // Once claimed this interrupt won't fire until it's completed
230            // NOTE: The interrupt is no longer pending in the PLIC
231            loop {
232                let interrupt = (*addr_of!(PLIC)).next_pending();
233
234                match interrupt {
235                    Some(irq) => {
236                        // Safe as interrupts are disabled
237                        (*addr_of!(PLIC)).save_interrupt(irq);
238                    }
239                    None => {
240                        // Enable generic interrupts
241                        CSR.mie.modify(mie::mext::SET);
242
243                        break;
244                    }
245                }
246            }
247        }
248
249        mcause::Interrupt::Unknown(_) => {
250            panic!("interrupt of unknown cause");
251        }
252    }
253}
254
255/// Trap handler for board/chip specific code.
256///
257/// For the e310 this gets called when an interrupt occurs while the chip is
258/// in kernel mode.
259#[export_name = "_start_trap_rust_from_kernel"]
260pub unsafe extern "C" fn start_trap_rust() {
261    match mcause::Trap::from(CSR.mcause.extract()) {
262        mcause::Trap::Interrupt(interrupt) => {
263            handle_interrupt(interrupt);
264        }
265        mcause::Trap::Exception(exception) => {
266            handle_exception(exception);
267        }
268    }
269}
270
271/// Function that gets called if an interrupt occurs while an app was running.
272///
273/// mcause is passed in, and this function should correctly handle disabling the
274/// interrupt that fired so that it does not trigger again.
275#[export_name = "_disable_interrupt_trap_rust_from_app"]
276pub unsafe extern "C" fn disable_interrupt_trap_handler(mcause_val: u32) {
277    match mcause::Trap::from(mcause_val as usize) {
278        mcause::Trap::Interrupt(interrupt) => {
279            handle_interrupt(interrupt);
280        }
281        _ => {
282            panic!("unexpected non-interrupt\n");
283        }
284    }
285}
286
287/// Array used to track the "trap handler active" state per hart.
288///
289/// The `riscv` crate requires chip crates to allocate an array to
290/// track whether any given hart is currently in a trap handler. The
291/// array must be zero-initialized.
292#[export_name = "_trap_handler_active"]
293static mut TRAP_HANDLER_ACTIVE: [usize; 1] = [0; 1];