litex_vexriscv/
interrupt_controller.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//! VexRiscv-specific interrupt controller implementation
6
7use core::cell::Cell;
8
9/// Rust wrapper around the raw CSR-based VexRiscv interrupt
10/// controller
11///
12/// The wrapper supports saving all currently pending interrupts to an
13/// internal state, which can then be used for interrupt processing.
14pub struct VexRiscvInterruptController {
15    saved_interrupts: Cell<usize>,
16}
17
18impl VexRiscvInterruptController {
19    /// Construct a new VexRiscvInterruptController instance
20    pub const fn new() -> Self {
21        VexRiscvInterruptController {
22            saved_interrupts: Cell::new(0),
23        }
24    }
25
26    /// Save the currently pending interrupts in hardware to the
27    /// internal state
28    ///
29    /// This should be accessed in an atomic context to ensure a
30    /// consistent view on the pending interrupts is saved.
31    pub unsafe fn save_pending(&self) -> bool {
32        let all_pending = vexriscv_irq_raw::irq_pending();
33        self.saved_interrupts.set(all_pending);
34
35        // return true if some interrupts were saved
36        all_pending != 0
37    }
38
39    /// Return the next pending interrupts in the saved state
40    ///
41    /// If no interrupt is pending in the saved state, this function
42    /// returns `None`.
43    ///
44    /// The ordering is determined by the interrupt number, lower
45    /// having a higher priority.
46    pub fn next_saved(&self) -> Option<usize> {
47        let saved_interrupts: usize = self.saved_interrupts.get();
48        let interrupt_bits = usize::BITS as usize;
49
50        // If there are no interrupts pending (saved_interrupts == 0),
51        // usize::trailing_zeros will return usize::BITS, in which
52        // case we need to return None
53        let trailing_zeros = usize::trailing_zeros(saved_interrupts) as usize;
54        if trailing_zeros == interrupt_bits {
55            None
56        } else {
57            Some(trailing_zeros)
58        }
59    }
60
61    /// Mark a saved interrupt as complete, removing it from the
62    /// `next_saved` queue
63    ///
64    /// If all interrupts are marked as complete, `next_saved` will
65    /// return `None`.
66    pub fn complete_saved(&self, idx: usize) {
67        self.saved_interrupts
68            .set(self.saved_interrupts.get() & !(1 << idx));
69    }
70
71    /// Suppress (mask) a specific interrupt source in the interrupt
72    /// controller
73    pub unsafe fn mask_interrupt(idx: usize) {
74        vexriscv_irq_raw::irq_setmask(vexriscv_irq_raw::irq_getmask() & !(1 << idx));
75    }
76
77    /// Unsuppress (unmask) a specific interrupt source in the
78    /// interrupt controller
79    pub unsafe fn unmask_interrupt(idx: usize) {
80        vexriscv_irq_raw::irq_setmask(vexriscv_irq_raw::irq_getmask() | (1 << idx));
81    }
82
83    /// Suppress (mask) all interrupts in the interrupt controller
84    pub unsafe fn mask_all_interrupts() {
85        vexriscv_irq_raw::irq_setmask(0);
86    }
87
88    /// Unsuppress (unmask) all interrupts in the interrupt controller
89    pub unsafe fn unmask_all_interrupts() {
90        vexriscv_irq_raw::irq_setmask(usize::MAX);
91    }
92}
93
94mod vexriscv_irq_raw {
95    //! These functions mirror those of litex/soc/cores/vexriscv/irq.h
96    //! which might be unsafe for direct use or behave unexpectedly
97    //! and are hence wrapped in this private module
98    #![allow(dead_code)]
99
100    /// defined in litex/soc/cores/cpu/vexriscv/csr-defs.h
101    const CSR_IRQ_MASK: usize = 0xBC0;
102    /// defined in litex/soc/cores/cpu/vexriscv/csr-defs.h
103    const CSR_IRQ_PENDING: usize = 0xFC0;
104
105    #[cfg(not(any(doc, all(target_arch = "riscv32", target_os = "none"))))]
106    pub unsafe fn irq_getmask() -> usize {
107        0
108    }
109
110    #[cfg(any(doc, all(target_arch = "riscv32", target_os = "none")))]
111    pub unsafe fn irq_getmask() -> usize {
112        let mask: usize;
113        use core::arch::asm;
114        asm!("csrr {mask}, {csr}", mask = out(reg) mask, csr = const CSR_IRQ_MASK);
115        mask
116    }
117
118    #[cfg(not(any(doc, all(target_arch = "riscv32", target_os = "none"))))]
119    pub unsafe fn irq_setmask(_mask: usize) {}
120
121    #[cfg(any(doc, all(target_arch = "riscv32", target_os = "none")))]
122    pub unsafe fn irq_setmask(mask: usize) {
123        use core::arch::asm;
124        asm!("csrw {csr}, {mask}", csr = const CSR_IRQ_MASK, mask = in(reg) mask);
125    }
126
127    #[cfg(not(any(doc, all(target_arch = "riscv32", target_os = "none"))))]
128    pub unsafe fn irq_pending() -> usize {
129        0
130    }
131
132    #[cfg(any(doc, all(target_arch = "riscv32", target_os = "none")))]
133    pub unsafe fn irq_pending() -> usize {
134        let pending: usize;
135        use core::arch::asm;
136        asm!("csrr {pending}, {csr}", pending = out(reg) pending, csr = const CSR_IRQ_PENDING);
137        pending
138    }
139}