litex_vexriscv/
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::InterruptService;
11use kernel::utilities::registers::interfaces::{ReadWriteable, Readable};
12use rv32i::csr::{mcause, mie::mie, CSR};
13use rv32i::pmp::{kernel_protection::KernelProtectionPMP, PMPUserMPU};
14use rv32i::syscall::SysCall;
15
16use crate::interrupt_controller::VexRiscvInterruptController;
17
18/// Global static variable for the InterruptController, as it must be
19/// accessible to the raw interrupt handler functions
20static mut INTERRUPT_CONTROLLER: VexRiscvInterruptController = VexRiscvInterruptController::new();
21
22// The VexRiscv "Secure" variant of
23// [pythondata-cpu-vexriscv](https://github.com/litex-hub/pythondata-cpu-vexriscv)
24// has 16 PMP slots
25pub struct LiteXVexRiscv<I: 'static + InterruptService> {
26    soc_identifier: &'static str,
27    userspace_kernel_boundary: SysCall,
28    interrupt_controller: &'static VexRiscvInterruptController,
29    pmp_mpu: PMPUserMPU<4, KernelProtectionPMP<16>>,
30    interrupt_service: &'static I,
31}
32
33impl<I: 'static + InterruptService> LiteXVexRiscv<I> {
34    pub unsafe fn new(
35        soc_identifier: &'static str,
36        interrupt_service: &'static I,
37        pmp: KernelProtectionPMP<16>,
38    ) -> Self {
39        Self {
40            soc_identifier,
41            userspace_kernel_boundary: SysCall::new(),
42            interrupt_controller: &*addr_of!(INTERRUPT_CONTROLLER),
43            pmp_mpu: PMPUserMPU::new(pmp),
44            interrupt_service,
45        }
46    }
47
48    pub unsafe fn unmask_interrupts(&self) {
49        VexRiscvInterruptController::unmask_all_interrupts();
50    }
51
52    unsafe fn handle_interrupts(&self) {
53        while let Some(interrupt) = self.interrupt_controller.next_saved() {
54            if !self.interrupt_service.service_interrupt(interrupt as u32) {
55                debug!("Unknown interrupt: {}", interrupt);
56            }
57            self.interrupt_controller.complete_saved(interrupt);
58        }
59    }
60}
61
62impl<I: 'static + InterruptService> kernel::platform::chip::Chip for LiteXVexRiscv<I> {
63    type MPU = PMPUserMPU<4, KernelProtectionPMP<16>>;
64    type UserspaceKernelBoundary = SysCall;
65
66    fn mpu(&self) -> &Self::MPU {
67        &self.pmp_mpu
68    }
69
70    fn userspace_kernel_boundary(&self) -> &SysCall {
71        &self.userspace_kernel_boundary
72    }
73
74    fn service_pending_interrupts(&self) {
75        while self.interrupt_controller.next_saved().is_some() {
76            unsafe {
77                self.handle_interrupts();
78            }
79        }
80
81        // Re-enable all MIE interrupts that we care about. Since we
82        // looped until we handled them all, we can re-enable all of
83        // them.
84        CSR.mie.modify(mie::mext::SET);
85    }
86
87    fn has_pending_interrupts(&self) -> bool {
88        self.interrupt_controller.next_saved().is_some()
89    }
90
91    fn sleep(&self) {
92        unsafe {
93            rv32i::support::wfi();
94        }
95    }
96
97    unsafe fn atomic<F, R>(&self, f: F) -> R
98    where
99        F: FnOnce() -> R,
100    {
101        rv32i::support::atomic(f)
102    }
103
104    unsafe fn print_state(&self, writer: &mut dyn Write) {
105        let _ = writer.write_fmt(format_args!(
106            "\r\n---| LiteX configuration for {} |---",
107            self.soc_identifier,
108        ));
109        rv32i::print_riscv_state(writer);
110        let _ = writer.write_fmt(format_args!("{}", self.pmp_mpu.pmp));
111    }
112}
113
114fn handle_exception(exception: mcause::Exception) {
115    match exception {
116        mcause::Exception::UserEnvCall | mcause::Exception::SupervisorEnvCall => (),
117
118        mcause::Exception::InstructionMisaligned
119        | mcause::Exception::InstructionFault
120        | mcause::Exception::IllegalInstruction
121        | mcause::Exception::Breakpoint
122        | mcause::Exception::LoadMisaligned
123        | mcause::Exception::LoadFault
124        | mcause::Exception::StoreMisaligned
125        | mcause::Exception::StoreFault
126        | mcause::Exception::MachineEnvCall
127        | mcause::Exception::InstructionPageFault
128        | mcause::Exception::LoadPageFault
129        | mcause::Exception::StorePageFault
130        | mcause::Exception::Unknown => {
131            panic!("fatal exception");
132        }
133    }
134}
135
136unsafe fn handle_interrupt(intr: mcause::Interrupt) {
137    match intr {
138        mcause::Interrupt::UserSoft
139        | mcause::Interrupt::UserTimer
140        | mcause::Interrupt::UserExternal => {
141            debug!("unexpected user-mode interrupt");
142        }
143        mcause::Interrupt::SupervisorExternal
144        | mcause::Interrupt::SupervisorTimer
145        | mcause::Interrupt::SupervisorSoft => {
146            debug!("unexpected supervisor-mode interrupt");
147        }
148
149        mcause::Interrupt::MachineSoft => {
150            CSR.mie.modify(mie::msoft::CLEAR);
151        }
152        mcause::Interrupt::MachineTimer => {
153            CSR.mie.modify(mie::mtimer::CLEAR);
154        }
155        mcause::Interrupt::MachineExternal => {
156            // We received an interrupt, disable interrupts while we handle them
157            CSR.mie.modify(mie::mext::CLEAR);
158
159            // Save the interrupts and check whether at least one
160            // interrupt is to be handled
161            //
162            // If no interrupt was saved, reenable interrupts
163            // immediately
164            if !(*addr_of!(INTERRUPT_CONTROLLER)).save_pending() {
165                CSR.mie.modify(mie::mext::SET);
166            }
167        }
168
169        mcause::Interrupt::Unknown(_) => {
170            debug!("interrupt of unknown cause");
171        }
172    }
173}
174
175/// Trap handler for board/chip specific code.
176///
177/// This gets called when an interrupt occurs while the chip is in
178/// kernel mode.
179#[export_name = "_start_trap_rust_from_kernel"]
180pub unsafe extern "C" fn start_trap_rust() {
181    match mcause::Trap::from(CSR.mcause.extract()) {
182        mcause::Trap::Interrupt(interrupt) => {
183            handle_interrupt(interrupt);
184        }
185        mcause::Trap::Exception(exception) => {
186            handle_exception(exception);
187        }
188    }
189}
190
191/// Function that gets called if an interrupt occurs while an app was running.
192///
193/// mcause is passed in, and this function should correctly handle disabling the
194/// interrupt that fired so that it does not trigger again.
195#[export_name = "_disable_interrupt_trap_rust_from_app"]
196pub unsafe extern "C" fn disable_interrupt_trap_handler(mcause_val: u32) {
197    match mcause::Trap::from(mcause_val as usize) {
198        mcause::Trap::Interrupt(interrupt) => {
199            handle_interrupt(interrupt);
200        }
201        _ => {
202            panic!("unexpected non-interrupt\n");
203        }
204    }
205}