pub mod claim;
pub mod enables;
pub mod pendings;
pub mod priorities;
pub mod threshold;
pub use riscv::{HartIdNumber, InterruptNumber, PriorityNumber};
use riscv::register::{mhartid, mie, mip};
pub unsafe trait Plic: Copy {
const BASE: usize;
}
#[allow(clippy::upper_case_acronyms)]
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct PLIC<P: Plic> {
_marker: core::marker::PhantomData<P>,
}
impl<P: Plic> PLIC<P> {
const PRIORITIES_OFFSET: usize = 0;
const PENDINGS_OFFSET: usize = 0x1000;
#[inline]
pub const fn new() -> Self {
Self {
_marker: core::marker::PhantomData,
}
}
#[inline]
pub fn is_interrupting(self) -> bool {
mip::read().mext()
}
#[inline]
pub fn is_enabled(self) -> bool {
mie::read().mext()
}
#[inline]
pub unsafe fn enable(self) {
mie::set_mext();
}
#[inline]
pub fn disable(self) {
unsafe { mie::clear_mext() };
}
#[inline]
pub const fn priorities(self) -> priorities::PRIORITIES {
unsafe { priorities::PRIORITIES::new(P::BASE + Self::PRIORITIES_OFFSET) }
}
#[inline]
pub const fn pendings(self) -> pendings::PENDINGS {
unsafe { pendings::PENDINGS::new(P::BASE + Self::PENDINGS_OFFSET) }
}
#[inline]
pub fn ctx<H: HartIdNumber>(self, hart_id: H) -> CTX<P> {
unsafe { CTX::new(hart_id.number() as _) }
}
#[inline]
pub const fn ctx0(self) -> CTX<P> {
unsafe { CTX::new(0) }
}
#[inline]
pub fn ctx_mhartid(self) -> CTX<P> {
let hart_id = mhartid::read();
unsafe { CTX::new(hart_id as _) }
}
}
#[allow(clippy::upper_case_acronyms)]
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct CTX<P: Plic> {
context: usize,
_marker: core::marker::PhantomData<P>,
}
impl<P: Plic> CTX<P> {
const ENABLES_OFFSET: usize = 0x2000;
const ENABLES_SEPARATION: usize = 0x80;
const THRESHOLDS_OFFSET: usize = 0x20_0000;
const THRESHOLDS_SEPARATION: usize = 0x1000;
const CLAIMS_OFFSET: usize = 0x20_0004;
const CLAIMS_SEPARATION: usize = 0x1000;
#[inline]
const unsafe fn new(context: u16) -> Self {
Self {
context: context as _,
_marker: core::marker::PhantomData,
}
}
#[inline]
pub const fn context(self) -> u16 {
self.context as _
}
#[inline]
pub const fn enables(self) -> enables::ENABLES {
let addr = P::BASE + Self::ENABLES_OFFSET + self.context * Self::ENABLES_SEPARATION;
unsafe { enables::ENABLES::new(addr) }
}
#[inline]
pub const fn threshold(self) -> threshold::THRESHOLD {
let addr = P::BASE + Self::THRESHOLDS_OFFSET + self.context * Self::THRESHOLDS_SEPARATION;
unsafe { threshold::THRESHOLD::new(addr) }
}
#[inline]
pub const fn claim(self) -> claim::CLAIM {
let addr = P::BASE + Self::CLAIMS_OFFSET + self.context * Self::CLAIMS_SEPARATION;
unsafe { claim::CLAIM::new(addr) }
}
}
#[cfg(test)]
pub(crate) mod test {
use crate::test::HartId;
use riscv::HartIdNumber;
#[allow(dead_code)]
#[test]
fn check_plic() {
crate::plic_codegen!(
PLIC,
base 0x0C00_0000,
harts [HartId::H1 => 1, HartId::H2 => 2]
);
let plic = PLIC::new();
let priorities = plic.priorities();
let pendings = plic.pendings();
assert_eq!(priorities.address(), 0x0C00_0000);
assert_eq!(pendings.address(), 0x0C00_1000);
for i in 0..=HartId::MAX_HART_ID_NUMBER {
let hart_id = HartId::from_number(i).unwrap();
let ctx = plic.ctx(hart_id);
assert_eq!(ctx.enables().address(), 0x0C00_0000 + 0x2000 + i * 0x80);
assert_eq!(
ctx.threshold().get_ptr() as usize,
0x0C00_0000 + 0x20_0000 + i * 0x1000
);
assert_eq!(
ctx.claim().get_ptr() as usize,
0x0C00_0000 + 0x20_0004 + i * 0x1000
);
}
assert_eq!(plic.ctx0(), plic.ctx(HartId::H0));
assert_eq!(plic.ctx1(), plic.ctx(HartId::H1));
assert_eq!(plic.ctx2(), plic.ctx(HartId::H2));
}
}