blob: b063c46f19db8236928addf016146b665104a814 [file] [log] [blame]
Austin Applebyfe18d192021-10-27 18:22:56 -07001//! High-level setup and interrupt mapping for the chip.
2
3use core::fmt::Write;
4use core::hint::unreachable_unchecked;
5use kernel;
6use kernel::debug;
7use kernel::hil::time::Alarm;
Austin Applebyfe18d192021-10-27 18:22:56 -07008use rv32i::csr::{mcause, mie::mie, mip::mip, mtvec::mtvec, CSR};
9use rv32i::syscall::SysCall;
10use rv32i::PMPConfigMacro;
11
Austin Applebyfe18d192021-10-27 18:22:56 -070012use crate::timer;
13use crate::uart;
Austin Applebycac7a5d2021-11-09 18:21:23 -080014use matcha_hal::plic;
Austin Applebyfe18d192021-10-27 18:22:56 -070015
16PMPConfigMacro!(4);
17
Austin Applebycac7a5d2021-11-09 18:21:23 -080018pub const CHIP_NAME: &str = "sim_verilator";
19pub const CHIP_CPU_FREQ: u32 = 500_000;
20pub const CHIP_PERIPH_FREQ: u32 = 125_000;
21pub const CHIP_UART_BPS: u32 = 9600;
22
23pub const UART0_TX_WATERMARK: u32 = 1;
24pub const UART0_RX_PARITY_ERR: u32 = 8;
25
Austin Applebyfe18d192021-10-27 18:22:56 -070026pub struct Matcha<A: 'static + Alarm<'static>> {
27 userspace_kernel_boundary: SysCall,
28 pmp: PMP,
29 scheduler_timer: kernel::VirtualSchedulerTimer<A>,
30}
31
32impl<A: 'static + Alarm<'static>> Matcha<A> {
33 pub unsafe fn new(alarm: &'static A) -> Self {
34 Self {
35 userspace_kernel_boundary: SysCall::new(),
36 pmp: PMP::new(),
37 scheduler_timer: kernel::VirtualSchedulerTimer::new(alarm),
38 }
39 }
40
41 pub unsafe fn enable_plic_interrupts(&self) {
42 plic::disable_all();
43 plic::clear_all_pending();
44 plic::enable_all();
45 }
46
47 unsafe fn handle_plic_interrupts(&self) {
48 while let Some(interrupt) = plic::next_pending() {
49 match interrupt {
Austin Applebycac7a5d2021-11-09 18:21:23 -080050 UART0_TX_WATERMARK..=UART0_RX_PARITY_ERR => uart::UART0.handle_interrupt(),
Austin Applebyfe18d192021-10-27 18:22:56 -070051 _ => debug!("Pidx {}", interrupt),
52 }
53 plic::complete(interrupt);
54 }
55 }
Austin Applebyfe18d192021-10-27 18:22:56 -070056}
57
58impl<A: 'static + Alarm<'static>> kernel::Chip for Matcha<A> {
59 type MPU = PMP;
60 type UserspaceKernelBoundary = SysCall;
61 type SchedulerTimer = kernel::VirtualSchedulerTimer<A>;
62 type WatchDog = ();
63
64 fn mpu(&self) -> &Self::MPU {
65 &self.pmp
66 }
67
68 fn scheduler_timer(&self) -> &Self::SchedulerTimer {
69 &self.scheduler_timer
70 }
71
72 fn watchdog(&self) -> &Self::WatchDog {
73 &()
74 }
75
76 fn userspace_kernel_boundary(&self) -> &SysCall {
77 &self.userspace_kernel_boundary
78 }
79
80 fn service_pending_interrupts(&self) {
81 loop {
82 let mip = CSR.mip.extract();
83
84 if mip.is_set(mip::mtimer) {
85 unsafe {
86 timer::TIMER.service_interrupt();
87 }
88 }
89 if mip.is_set(mip::mext) {
90 unsafe {
91 self.handle_plic_interrupts();
92 }
93 }
94
95 if !mip.matches_any(mip::mext::SET + mip::mtimer::SET) {
96 break;
97 }
98 }
99
100 // Re-enable all MIE interrupts that we care about. Since we looped
101 // until we handled them all, we can re-enable all of them.
102 CSR.mie.modify(mie::mext::SET + mie::mtimer::SET);
103 }
104
105 fn has_pending_interrupts(&self) -> bool {
106 let mip = CSR.mip.extract();
107 mip.matches_any(mip::mext::SET + mip::mtimer::SET)
108 }
109
110 fn sleep(&self) {
111 unsafe {
Austin Applebycac7a5d2021-11-09 18:21:23 -0800112 //pwrmgr::PWRMGR.enable_low_power();
113 //self.check_until_true_or_interrupt(|| pwrmgr::PWRMGR.check_clock_propagation(), None);
Austin Applebyfe18d192021-10-27 18:22:56 -0700114 rv32i::support::wfi();
115 }
116 }
117
118 unsafe fn atomic<F, R>(&self, f: F) -> R
119 where
120 F: FnOnce() -> R,
121 {
122 rv32i::support::atomic(f)
123 }
124
125 unsafe fn print_state(&self, writer: &mut dyn Write) {
126 let _ = writer.write_fmt(format_args!(
127 "\r\n---| Matcha configuration for {} |---",
Austin Applebycac7a5d2021-11-09 18:21:23 -0800128 crate::chip::CHIP_NAME
Austin Applebyfe18d192021-10-27 18:22:56 -0700129 ));
130 rv32i::print_riscv_state(writer);
131 }
132}
133
134fn handle_exception(exception: mcause::Exception) {
135 match exception {
136 mcause::Exception::UserEnvCall | mcause::Exception::SupervisorEnvCall => (),
137
138 mcause::Exception::InstructionMisaligned
139 | mcause::Exception::InstructionFault
140 | mcause::Exception::IllegalInstruction
141 | mcause::Exception::Breakpoint
142 | mcause::Exception::LoadMisaligned
143 | mcause::Exception::LoadFault
144 | mcause::Exception::StoreMisaligned
145 | mcause::Exception::StoreFault
146 | mcause::Exception::MachineEnvCall
147 | mcause::Exception::InstructionPageFault
148 | mcause::Exception::LoadPageFault
149 | mcause::Exception::StorePageFault
150 | mcause::Exception::Unknown => {
151 panic!("fatal exception");
152 }
153 }
154}
155
156unsafe fn handle_interrupt(intr: mcause::Interrupt) {
157 match intr {
158 mcause::Interrupt::UserSoft
159 | mcause::Interrupt::UserTimer
160 | mcause::Interrupt::UserExternal => {
161 panic!("unexpected user-mode interrupt");
162 }
163 mcause::Interrupt::SupervisorExternal
164 | mcause::Interrupt::SupervisorTimer
165 | mcause::Interrupt::SupervisorSoft => {
166 panic!("unexpected supervisor-mode interrupt");
167 }
168
169 mcause::Interrupt::MachineSoft => {
170 CSR.mie.modify(mie::msoft::CLEAR);
171 }
172 mcause::Interrupt::MachineTimer => {
173 CSR.mie.modify(mie::mtimer::CLEAR);
174 }
175 mcause::Interrupt::MachineExternal => {
176 CSR.mie.modify(mie::mext::CLEAR);
177 }
178
179 mcause::Interrupt::Unknown => {
180 panic!("interrupt of unknown cause");
181 }
182 }
183}
184
185/// Trap handler for board/chip specific code.
186///
187/// For the Ibex this gets called when an interrupt occurs while the chip is
188/// in kernel mode. All we need to do is check which interrupt occurred and
189/// disable it.
190#[export_name = "_start_trap_rust"]
191pub unsafe extern "C" fn start_trap_rust() {
192 match mcause::Trap::from(CSR.mcause.extract()) {
193 mcause::Trap::Interrupt(interrupt) => {
194 handle_interrupt(interrupt);
195 }
196 mcause::Trap::Exception(exception) => {
197 handle_exception(exception);
198 }
199 }
200}
201
202/// Function that gets called if an interrupt occurs while an app was running.
203/// mcause is passed in, and this function should correctly handle disabling the
204/// interrupt that fired so that it does not trigger again.
205#[export_name = "_disable_interrupt_trap_handler"]
206pub unsafe extern "C" fn disable_interrupt_trap_handler(mcause_val: u32) {
207 match mcause::Trap::from(mcause_val) {
208 mcause::Trap::Interrupt(interrupt) => {
209 handle_interrupt(interrupt);
210 }
211 _ => {
212 panic!("unexpected non-interrupt\n");
213 }
214 }
215}
216
217pub unsafe fn configure_trap_handler() {
218 // The Ibex CPU does not support non-vectored trap entries.
219 CSR.mtvec
220 .write(mtvec::trap_addr.val(_start_trap_vectored as u32 >> 2) + mtvec::mode::Vectored)
221}
222
223// Mock implementation for crate tests that does not include the section
224// specifier, as the test will not use our linker script, and the host
225// compilation environment may not allow the section name.
226#[cfg(not(any(target_arch = "riscv32", target_os = "none")))]
227pub extern "C" fn _start_trap_vectored() {
228 unsafe {
229 unreachable_unchecked();
230 }
231}
232
233#[cfg(all(target_arch = "riscv32", target_os = "none"))]
234#[link_section = ".riscv.trap_vectored"]
235#[export_name = "_start_trap_vectored"]
236#[naked]
237pub extern "C" fn _start_trap_vectored() -> ! {
238 unsafe {
239 // According to the Ibex user manual:
240 // [NMI] has interrupt ID 31, i.e., it has the highest priority of all
241 // interrupts and the core jumps to the trap-handler base address (in
242 // mtvec) plus 0x7C to handle the NMI.
243 //
244 // Below are 32 (non-compressed) jumps to cover the entire possible
245 // range of vectored traps.
246 #[cfg(all(target_arch = "riscv32", target_os = "none"))]
247 llvm_asm!("
248 j _start_trap
249 j _start_trap
250 j _start_trap
251 j _start_trap
252 j _start_trap
253 j _start_trap
254 j _start_trap
255 j _start_trap
256 j _start_trap
257 j _start_trap
258 j _start_trap
259 j _start_trap
260 j _start_trap
261 j _start_trap
262 j _start_trap
263 j _start_trap
264 j _start_trap
265 j _start_trap
266 j _start_trap
267 j _start_trap
268 j _start_trap
269 j _start_trap
270 j _start_trap
271 j _start_trap
272 j _start_trap
273 j _start_trap
274 j _start_trap
275 j _start_trap
276 j _start_trap
277 j _start_trap
278 j _start_trap
279 j _start_trap
280 "
281 :
282 :
283 :
284 : "volatile");
285 unreachable_unchecked()
286 }
287}