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.
45use core::fmt::Write;
6use kernel::debug;
7use kernel::hil::time::Freq32KHz;
8use kernel::platform::chip::InterruptService;
9use kernel::utilities::registers::interfaces::Readable;
1011use crate::clint;
12use crate::interrupts;
13use rv32i::pmp::{simple::SimplePMP, PMPUserMPU};
1415pub type ArtyExxClint<'a> = sifive::clint::Clint<'a, Freq32KHz>;
1617pub struct ArtyExx<'a, I: InterruptService + 'a> {
18 pmp: PMPUserMPU<2, SimplePMP<4>>,
19 userspace_kernel_boundary: rv32i::syscall::SysCall,
20 clic: rv32i::clic::Clic,
21 machinetimer: &'a ArtyExxClint<'a>,
22 interrupt_service: &'a I,
23}
2425pub struct ArtyExxDefaultPeripherals<'a> {
26pub machinetimer: ArtyExxClint<'a>,
27pub gpio_port: crate::gpio::Port<'a>,
28pub uart0: sifive::uart::Uart<'a>,
29}
3031impl ArtyExxDefaultPeripherals<'_> {
32pub fn new() -> Self {
33Self {
34 machinetimer: ArtyExxClint::new(&clint::CLINT_BASE),
35 gpio_port: crate::gpio::Port::new(),
36 uart0: sifive::uart::Uart::new(crate::uart::UART0_BASE, 32_000_000),
37 }
38 }
3940// Resolves any circular dependencies and sets up deferred calls
41pub fn init(&'static self) {
42 kernel::deferred_call::DeferredCallClient::register(&self.uart0);
43 }
44}
4546impl InterruptService for ArtyExxDefaultPeripherals<'_> {
47unsafe fn service_interrupt(&self, interrupt: u32) -> bool {
48match interrupt {
49 interrupts::MTIP => self.machinetimer.handle_interrupt(),
5051 interrupts::GPIO0 => self.gpio_port[0].handle_interrupt(),
52 interrupts::GPIO1 => self.gpio_port[1].handle_interrupt(),
53 interrupts::GPIO2 => self.gpio_port[2].handle_interrupt(),
54 interrupts::GPIO3 => self.gpio_port[3].handle_interrupt(),
55 interrupts::GPIO4 => self.gpio_port[4].handle_interrupt(),
56 interrupts::GPIO5 => self.gpio_port[5].handle_interrupt(),
57 interrupts::GPIO6 => self.gpio_port[6].handle_interrupt(),
58 interrupts::GPIO7 => self.gpio_port[7].handle_interrupt(),
59 interrupts::GPIO8 => self.gpio_port[8].handle_interrupt(),
60 interrupts::GPIO9 => self.gpio_port[9].handle_interrupt(),
61 interrupts::GPIO10 => self.gpio_port[10].handle_interrupt(),
62 interrupts::GPIO11 => self.gpio_port[11].handle_interrupt(),
63 interrupts::GPIO12 => self.gpio_port[12].handle_interrupt(),
64 interrupts::GPIO13 => self.gpio_port[13].handle_interrupt(),
65 interrupts::GPIO14 => self.gpio_port[14].handle_interrupt(),
66 interrupts::GPIO15 => self.gpio_port[15].handle_interrupt(),
6768 interrupts::UART0 => self.uart0.handle_interrupt(),
6970_ => return false,
71 }
72true
73}
74}
7576impl<'a, I: InterruptService + 'a> ArtyExx<'a, I> {
77pub unsafe fn new(machinetimer: &'a ArtyExxClint<'a>, interrupt_service: &'a I) -> Self {
78// Make a bit-vector of all interrupt locations that we actually intend
79 // to use on this chip.
80 // 0001 1111 1111 1111 1111 0000 0000 1000 0000
81let in_use_interrupts: u64 = 0x1FFFF0080;
8283Self {
84 pmp: PMPUserMPU::new(SimplePMP::new().unwrap()),
85 userspace_kernel_boundary: rv32i::syscall::SysCall::new(),
86 clic: rv32i::clic::Clic::new(in_use_interrupts),
87 machinetimer,
88 interrupt_service,
89 }
90 }
9192pub fn enable_all_interrupts(&self) {
93self.clic.enable_all();
94 }
9596/// By default the machine timer is enabled and will trigger interrupts. To
97 /// prevent that we can make the compare register very large to effectively
98 /// stop the interrupt from triggering, and then the machine timer can be
99 /// used later as needed.
100pub unsafe fn disable_machine_timer(&self) {
101self.machinetimer.disable_machine_timer();
102 }
103104/// Setup the function that should run when a trap happens.
105 ///
106 /// This needs to be chip specific because how the CLIC works is configured
107 /// when the trap handler address is specified in mtvec, and that is only
108 /// valid for platforms with a CLIC.
109#[cfg(any(doc, all(target_arch = "riscv32", target_os = "none")))]
110pub unsafe fn configure_trap_handler(&self) {
111use core::arch::asm;
112asm!(
113"
114 // The csrw instruction writes a Control and Status Register (CSR)
115 // with a new value.
116 //
117 // CSR 0x305 (mtvec, 'Machine trap-handler base address.') sets the
118 // address of the trap handler. We do not care about its old value,
119 // so we don't bother reading it. We want to enable direct CLIC mode
120 // so we set the second lowest bit.
121 lui t0, %hi({start_trap})
122 addi t0, t0, %lo({start_trap})
123 ori t0, t0, 0x02 // Set CLIC direct mode
124 csrw 0x305, t0 // Write the mtvec CSR.
125 ",
126 start_trap = sym rv32i::_start_trap,
127 out("t0") _
128);
129 }
130131// Mock implementation for tests on Travis-CI.
132#[cfg(not(any(doc, all(target_arch = "riscv32", target_os = "none"))))]
133pub unsafe fn configure_trap_handler(&self) {
134unimplemented!()
135 }
136137/// Generic helper initialize function to setup all of the chip specific
138 /// operations. Different boards can call the functions that `initialize()`
139 /// calls directly if it needs to use a custom setup operation.
140pub unsafe fn initialize(&self) {
141self.disable_machine_timer();
142self.configure_trap_handler();
143 }
144}
145146impl<'a, I: InterruptService + 'a> kernel::platform::chip::Chip for ArtyExx<'a, I> {
147type MPU = PMPUserMPU<2, SimplePMP<4>>;
148type UserspaceKernelBoundary = rv32i::syscall::SysCall;
149150fn mpu(&self) -> &Self::MPU {
151&self.pmp
152 }
153154fn userspace_kernel_boundary(&self) -> &rv32i::syscall::SysCall {
155&self.userspace_kernel_boundary
156 }
157158fn service_pending_interrupts(&self) {
159unsafe {
160while let Some(interrupt) = self.clic.next_pending() {
161if !self.interrupt_service.service_interrupt(interrupt) {
162debug!("unhandled interrupt: {:?}", interrupt);
163 }
164165// Mark that we are done with this interrupt and the hardware
166 // can clear it.
167self.clic.complete(interrupt);
168 }
169 }
170 }
171172fn has_pending_interrupts(&self) -> bool {
173self.clic.has_pending()
174 }
175176fn sleep(&self) {
177unsafe {
178 rv32i::support::wfi();
179 }
180 }
181182unsafe fn atomic<F, R>(&self, f: F) -> R
183where
184F: FnOnce() -> R,
185 {
186 rv32i::support::atomic(f)
187 }
188189unsafe fn print_state(&self, write: &mut dyn Write) {
190 rv32i::print_riscv_state(write);
191 }
192}
193194/// Trap handler for board/chip specific code.
195///
196/// For the arty-e21 this gets called when an interrupt occurs while the chip is
197/// in kernel mode. All we need to do is check which interrupt occurred and
198/// disable it.
199#[export_name = "_start_trap_rust_from_kernel"]
200pub extern "C" fn start_trap_rust() {
201let mcause = rv32i::csr::CSR.mcause.extract();
202203match rv32i::csr::mcause::Trap::from(mcause) {
204 rv32i::csr::mcause::Trap::Interrupt(_interrupt) => {
205// Since we are using the CLIC, the hardware includes the interrupt
206 // index in the mcause register. The interrupt number is the lowest
207 // 8 bits.
208let interrupt_index = mcause.read(rv32i::csr::mcause::mcause::reason) & 0xFF;
209unsafe {
210 rv32i::clic::disable_interrupt(interrupt_index as u32);
211 }
212 }
213214 rv32i::csr::mcause::Trap::Exception(_exception) => {
215// Otherwise, the kernel encountered a fault...so panic!()?
216panic!("kernel exception");
217 }
218 }
219}
220221/// Function that gets called if an interrupt occurs while an app was running.
222///
223/// mcause is passed in, and this function should correctly handle disabling the
224/// interrupt that fired so that it does not trigger again.
225#[export_name = "_disable_interrupt_trap_rust_from_app"]
226pub extern "C" fn disable_interrupt_trap_handler(mcause: u32) {
227// The interrupt number is then the lowest 8
228 // bits.
229let interrupt_index = mcause & 0xFF;
230unsafe {
231 rv32i::clic::disable_interrupt(interrupt_index);
232 }
233}