arty_e21_chip/
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
5use core::fmt::Write;
6use kernel::debug;
7use kernel::hil::time::Freq32KHz;
8use kernel::platform::chip::InterruptService;
9use kernel::utilities::registers::interfaces::Readable;
10
11use crate::clint;
12use crate::interrupts;
13use rv32i::pmp::{simple::SimplePMP, PMPUserMPU};
14
15pub type ArtyExxClint<'a> = sifive::clint::Clint<'a, Freq32KHz>;
16
17pub 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}
24
25pub struct ArtyExxDefaultPeripherals<'a> {
26    pub machinetimer: ArtyExxClint<'a>,
27    pub gpio_port: crate::gpio::Port<'a>,
28    pub uart0: sifive::uart::Uart<'a>,
29}
30
31impl ArtyExxDefaultPeripherals<'_> {
32    pub fn new() -> Self {
33        Self {
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    }
39
40    // Resolves any circular dependencies and sets up deferred calls
41    pub fn init(&'static self) {
42        kernel::deferred_call::DeferredCallClient::register(&self.uart0);
43    }
44}
45
46impl InterruptService for ArtyExxDefaultPeripherals<'_> {
47    unsafe fn service_interrupt(&self, interrupt: u32) -> bool {
48        match interrupt {
49            interrupts::MTIP => self.machinetimer.handle_interrupt(),
50
51            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(),
67
68            interrupts::UART0 => self.uart0.handle_interrupt(),
69
70            _ => return false,
71        }
72        true
73    }
74}
75
76impl<'a, I: InterruptService + 'a> ArtyExx<'a, I> {
77    pub 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
81        let in_use_interrupts: u64 = 0x1FFFF0080;
82
83        Self {
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    }
91
92    pub fn enable_all_interrupts(&self) {
93        self.clic.enable_all();
94    }
95
96    /// 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.
100    pub unsafe fn disable_machine_timer(&self) {
101        self.machinetimer.disable_machine_timer();
102    }
103
104    /// 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")))]
110    pub unsafe fn configure_trap_handler(&self) {
111        use core::arch::asm;
112        asm!(
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    }
130
131    // Mock implementation for tests on Travis-CI.
132    #[cfg(not(any(doc, all(target_arch = "riscv32", target_os = "none"))))]
133    pub unsafe fn configure_trap_handler(&self) {
134        unimplemented!()
135    }
136
137    /// 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.
140    pub unsafe fn initialize(&self) {
141        self.disable_machine_timer();
142        self.configure_trap_handler();
143    }
144}
145
146impl<'a, I: InterruptService + 'a> kernel::platform::chip::Chip for ArtyExx<'a, I> {
147    type MPU = PMPUserMPU<2, SimplePMP<4>>;
148    type UserspaceKernelBoundary = rv32i::syscall::SysCall;
149    type ThreadIdProvider = rv32i::thread_id::RiscvThreadIdProvider;
150
151    fn mpu(&self) -> &Self::MPU {
152        &self.pmp
153    }
154
155    fn userspace_kernel_boundary(&self) -> &rv32i::syscall::SysCall {
156        &self.userspace_kernel_boundary
157    }
158
159    fn service_pending_interrupts(&self) {
160        unsafe {
161            while let Some(interrupt) = self.clic.next_pending() {
162                if !self.interrupt_service.service_interrupt(interrupt) {
163                    debug!("unhandled interrupt: {:?}", interrupt);
164                }
165
166                // Mark that we are done with this interrupt and the hardware
167                // can clear it.
168                self.clic.complete(interrupt);
169            }
170        }
171    }
172
173    fn has_pending_interrupts(&self) -> bool {
174        self.clic.has_pending()
175    }
176
177    fn sleep(&self) {
178        unsafe {
179            rv32i::support::wfi();
180        }
181    }
182
183    unsafe fn with_interrupts_disabled<F, R>(&self, f: F) -> R
184    where
185        F: FnOnce() -> R,
186    {
187        rv32i::support::with_interrupts_disabled(f)
188    }
189
190    unsafe fn print_state(&self, write: &mut dyn Write) {
191        rv32i::print_riscv_state(write);
192    }
193}
194
195/// Trap handler for board/chip specific code.
196///
197/// For the arty-e21 this gets called when an interrupt occurs while the chip is
198/// in kernel mode. All we need to do is check which interrupt occurred and
199/// disable it.
200#[export_name = "_start_trap_rust_from_kernel"]
201pub extern "C" fn start_trap_rust() {
202    let mcause = rv32i::csr::CSR.mcause.extract();
203
204    match rv32i::csr::mcause::Trap::from(mcause) {
205        rv32i::csr::mcause::Trap::Interrupt(_interrupt) => {
206            // Since we are using the CLIC, the hardware includes the interrupt
207            // index in the mcause register. The interrupt number is the lowest
208            // 8 bits.
209            let interrupt_index = mcause.read(rv32i::csr::mcause::mcause::reason) & 0xFF;
210            unsafe {
211                rv32i::clic::disable_interrupt(interrupt_index as u32);
212            }
213        }
214
215        rv32i::csr::mcause::Trap::Exception(_exception) => {
216            // Otherwise, the kernel encountered a fault...so panic!()?
217            panic!("kernel exception");
218        }
219    }
220}
221
222/// Function that gets called if an interrupt occurs while an app was running.
223///
224/// mcause is passed in, and this function should correctly handle disabling the
225/// interrupt that fired so that it does not trigger again.
226#[export_name = "_disable_interrupt_trap_rust_from_app"]
227pub extern "C" fn disable_interrupt_trap_handler(mcause: u32) {
228    // The interrupt number is then the lowest 8
229    // bits.
230    let interrupt_index = mcause & 0xFF;
231    unsafe {
232        rv32i::clic::disable_interrupt(interrupt_index);
233    }
234}
235
236/// Array used to track the "trap handler active" state per hart.
237///
238/// The `riscv` crate requires chip crates to allocate an array to
239/// track whether any given hart is currently in a trap handler. The
240/// array must be zero-initialized.
241#[export_name = "_trap_handler_active"]
242static mut TRAP_HANDLER_ACTIVE: [usize; 1] = [0; 1];