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