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
150    fn mpu(&self) -> &Self::MPU {
151        &self.pmp
152    }
153
154    fn userspace_kernel_boundary(&self) -> &rv32i::syscall::SysCall {
155        &self.userspace_kernel_boundary
156    }
157
158    fn service_pending_interrupts(&self) {
159        unsafe {
160            while let Some(interrupt) = self.clic.next_pending() {
161                if !self.interrupt_service.service_interrupt(interrupt) {
162                    debug!("unhandled interrupt: {:?}", interrupt);
163                }
164
165                // Mark that we are done with this interrupt and the hardware
166                // can clear it.
167                self.clic.complete(interrupt);
168            }
169        }
170    }
171
172    fn has_pending_interrupts(&self) -> bool {
173        self.clic.has_pending()
174    }
175
176    fn sleep(&self) {
177        unsafe {
178            rv32i::support::wfi();
179        }
180    }
181
182    unsafe fn atomic<F, R>(&self, f: F) -> R
183    where
184        F: FnOnce() -> R,
185    {
186        rv32i::support::atomic(f)
187    }
188
189    unsafe fn print_state(&self, write: &mut dyn Write) {
190        rv32i::print_riscv_state(write);
191    }
192}
193
194/// 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() {
201    let mcause = rv32i::csr::CSR.mcause.extract();
202
203    match 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.
208            let interrupt_index = mcause.read(rv32i::csr::mcause::mcause::reason) & 0xFF;
209            unsafe {
210                rv32i::clic::disable_interrupt(interrupt_index as u32);
211            }
212        }
213
214        rv32i::csr::mcause::Trap::Exception(_exception) => {
215            // Otherwise, the kernel encountered a fault...so panic!()?
216            panic!("kernel exception");
217        }
218    }
219}
220
221/// 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.
229    let interrupt_index = mcause & 0xFF;
230    unsafe {
231        rv32i::clic::disable_interrupt(interrupt_index);
232    }
233}