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.atomic(|| {
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
119    fn mpu(&self) -> &Self::MPU {
120        &self.pmp
121    }
122
123    fn userspace_kernel_boundary(&self) -> &rv32i::syscall::SysCall {
124        &self.userspace_kernel_boundary
125    }
126
127    fn service_pending_interrupts(&self) {
128        loop {
129            let mip = CSR.mip.extract();
130
131            if mip.is_set(mip::mtimer) {
132                self.timer.handle_interrupt();
133            }
134            if self.plic.get_saved_interrupts().is_some() {
135                unsafe {
136                    self.handle_plic_interrupts();
137                }
138            }
139
140            if !mip.any_matching_bits_set(mip::mtimer::SET)
141                && self.plic.get_saved_interrupts().is_none()
142            {
143                break;
144            }
145        }
146
147        // Re-enable all MIE interrupts that we care about. Since we looped
148        // until we handled them all, we can re-enable all of them.
149        CSR.mie.modify(mie::mext::SET + mie::mtimer::SET);
150    }
151
152    fn has_pending_interrupts(&self) -> bool {
153        // First check if the global machine timer interrupt is set.
154        // We would also need to check for additional global interrupt bits
155        // if there were to be used for anything in the future.
156        if CSR.mip.is_set(mip::mtimer) {
157            return true;
158        }
159
160        // Then we can check the PLIC.
161        self.plic.get_saved_interrupts().is_some()
162    }
163
164    fn sleep(&self) {
165        unsafe {
166            rv32i::support::wfi();
167        }
168    }
169
170    unsafe fn atomic<F, R>(&self, f: F) -> R
171    where
172        F: FnOnce() -> R,
173    {
174        rv32i::support::atomic(f)
175    }
176
177    unsafe fn print_state(&self, writer: &mut dyn Write) {
178        rv32i::print_riscv_state(writer);
179    }
180}
181
182fn handle_exception(exception: mcause::Exception) {
183    match exception {
184        mcause::Exception::UserEnvCall | mcause::Exception::SupervisorEnvCall => (),
185
186        mcause::Exception::InstructionMisaligned
187        | mcause::Exception::InstructionFault
188        | mcause::Exception::IllegalInstruction
189        | mcause::Exception::Breakpoint
190        | mcause::Exception::LoadMisaligned
191        | mcause::Exception::LoadFault
192        | mcause::Exception::StoreMisaligned
193        | mcause::Exception::StoreFault
194        | mcause::Exception::MachineEnvCall
195        | mcause::Exception::InstructionPageFault
196        | mcause::Exception::LoadPageFault
197        | mcause::Exception::StorePageFault
198        | mcause::Exception::Unknown => {
199            panic!("fatal exception");
200        }
201    }
202}
203
204unsafe fn handle_interrupt(intr: mcause::Interrupt) {
205    match intr {
206        mcause::Interrupt::UserSoft
207        | mcause::Interrupt::UserTimer
208        | mcause::Interrupt::UserExternal => {
209            panic!("unexpected user-mode interrupt");
210        }
211        mcause::Interrupt::SupervisorExternal
212        | mcause::Interrupt::SupervisorTimer
213        | mcause::Interrupt::SupervisorSoft => {
214            panic!("unexpected supervisor-mode interrupt");
215        }
216
217        mcause::Interrupt::MachineSoft => {
218            CSR.mie.modify(mie::msoft::CLEAR);
219        }
220        mcause::Interrupt::MachineTimer => {
221            CSR.mie.modify(mie::mtimer::CLEAR);
222        }
223        mcause::Interrupt::MachineExternal => {
224            // We received an interrupt, disable interrupts while we handle them
225            CSR.mie.modify(mie::mext::CLEAR);
226
227            // Claim the interrupt, unwrap() as we know an interrupt exists
228            // Once claimed this interrupt won't fire until it's completed
229            // NOTE: The interrupt is no longer pending in the PLIC
230            loop {
231                let interrupt = (*addr_of!(PLIC)).next_pending();
232
233                match interrupt {
234                    Some(irq) => {
235                        // Safe as interrupts are disabled
236                        (*addr_of!(PLIC)).save_interrupt(irq);
237                    }
238                    None => {
239                        // Enable generic interrupts
240                        CSR.mie.modify(mie::mext::SET);
241
242                        break;
243                    }
244                }
245            }
246        }
247
248        mcause::Interrupt::Unknown(_) => {
249            panic!("interrupt of unknown cause");
250        }
251    }
252}
253
254/// Trap handler for board/chip specific code.
255///
256/// For the e310 this gets called when an interrupt occurs while the chip is
257/// in kernel mode.
258#[export_name = "_start_trap_rust_from_kernel"]
259pub unsafe extern "C" fn start_trap_rust() {
260    match mcause::Trap::from(CSR.mcause.extract()) {
261        mcause::Trap::Interrupt(interrupt) => {
262            handle_interrupt(interrupt);
263        }
264        mcause::Trap::Exception(exception) => {
265            handle_exception(exception);
266        }
267    }
268}
269
270/// Function that gets called if an interrupt occurs while an app was running.
271///
272/// mcause is passed in, and this function should correctly handle disabling the
273/// interrupt that fired so that it does not trigger again.
274#[export_name = "_disable_interrupt_trap_rust_from_app"]
275pub unsafe extern "C" fn disable_interrupt_trap_handler(mcause_val: u32) {
276    match mcause::Trap::from(mcause_val as usize) {
277        mcause::Trap::Interrupt(interrupt) => {
278            handle_interrupt(interrupt);
279        }
280        _ => {
281            panic!("unexpected non-interrupt\n");
282        }
283    }
284}