123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311 |
- //! Platform-Level Interrupt Controller (PLIC) peripheral.
- //!
- //! Specification: <https://github.com/riscv/riscv-plic-spec/blob/master/riscv-plic.adoc>
- pub mod claim;
- pub mod enables;
- pub mod pendings;
- pub mod priorities;
- pub mod threshold;
- pub use riscv_pac::{HartIdNumber, InterruptNumber, PriorityNumber}; // re-export useful riscv-pac traits
- /// Trait for a PLIC peripheral.
- ///
- /// # Safety
- ///
- /// * This trait must only be implemented on a PAC of a target with a PLIC peripheral.
- /// * The PLIC peripheral base address `BASE` must be valid for the target device.
- pub unsafe trait Plic: Copy {
- /// Base address of the PLIC peripheral.
- const BASE: usize;
- }
- /// Platform-Level Interrupt Controler (PLIC) peripheral.
- ///
- /// The RISC-V standard does not specify a fixed location for the PLIC.
- /// Thus, each platform must specify the base address of the PLIC on the platform.
- /// The base address, as well as all the associated types, are defined in the [`Plic`] trait.
- ///
- /// The PLIC standard allows up to 15_872 different contexts for interfacing the PLIC.
- /// Each context has an assigned index starting from 0 to up to 15_871.
- /// Usually, each HART uses a dedicated context. In this way, they do not interfere
- /// with each other when attending to external interruptions.
- #[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;
- /// Returns the priorities register of the PLIC.
- /// This register allows to set the priority level of each interrupt source.
- /// The priority level of each interrupt source is shared among all the contexts.
- #[inline]
- pub fn priorities() -> priorities::PRIORITIES {
- // SAFETY: valid address
- unsafe { priorities::PRIORITIES::new(P::BASE + Self::PRIORITIES_OFFSET) }
- }
- /// Returns the pendings register of the PLIC.
- /// This register allows to check if a particular interrupt source is pending.
- #[inline]
- pub fn pendings() -> pendings::PENDINGS {
- // SAFETY: valid address
- unsafe { pendings::PENDINGS::new(P::BASE + Self::PENDINGS_OFFSET) }
- }
- /// Returns a proxy to access to all the PLIC registers of a given HART context.
- #[inline]
- pub fn ctx<H: HartIdNumber>(hart_id: H) -> CTX<P> {
- // SAFETY: valid context number
- unsafe { CTX::new(hart_id.number()) }
- }
- /// Returns the PLIC HART context for the current HART.
- ///
- /// # Note
- ///
- /// This function determines the current HART ID by reading the [`riscv::register::mhartid`] CSR.
- /// Thus, it can only be used in M-mode. For S-mode, use [`PLIC::ctx`] instead.
- #[inline]
- pub fn ctx_mhartid() -> CTX<P> {
- let hart_id = riscv::register::mhartid::read();
- // SAFETY: `hart_id` is valid for the target and is the current hart
- unsafe { CTX::new(hart_id as _) }
- }
- }
- /// PLIC context proxy. It provides access to the PLIC registers of a given context.
- #[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;
- /// Creates a new PLIC context proxy
- ///
- /// # Safety
- ///
- /// The context number must be valid for the target device.
- #[inline]
- pub(crate) unsafe fn new(context: u16) -> Self {
- Self {
- context: context as _,
- _marker: core::marker::PhantomData,
- }
- }
- /// Returns the context number of this proxy.
- #[inline]
- pub const fn context(self) -> u16 {
- self.context as _
- }
- /// Returns the interrupts enable register of the context.
- #[inline]
- pub const fn enables(self) -> enables::ENABLES {
- let addr = P::BASE + Self::ENABLES_OFFSET + self.context * Self::ENABLES_SEPARATION;
- // SAFETY: valid address
- unsafe { enables::ENABLES::new(addr) }
- }
- /// Returns the interrupt threshold register of the context.
- #[inline]
- pub const fn threshold(self) -> threshold::THRESHOLD {
- let addr = P::BASE + Self::THRESHOLDS_OFFSET + self.context * Self::THRESHOLDS_SEPARATION;
- // SAFETY: valid address
- unsafe { threshold::THRESHOLD::new(addr) }
- }
- /// Returns the interrupt claim/complete register of the context.
- #[inline]
- pub const fn claim(self) -> claim::CLAIM {
- let addr = P::BASE + Self::CLAIMS_OFFSET + self.context * Self::CLAIMS_SEPARATION;
- // SAFETY: valid address
- unsafe { claim::CLAIM::new(addr) }
- }
- }
- #[cfg(test)]
- pub(crate) mod test {
- use super::{HartIdNumber, InterruptNumber, PriorityNumber};
- #[derive(Clone, Copy, Debug, Eq, PartialEq)]
- #[repr(u16)]
- pub(crate) enum Interrupt {
- I1 = 1,
- I2 = 2,
- I3 = 3,
- I4 = 4,
- }
- #[derive(Clone, Copy, Debug, Eq, PartialEq)]
- #[repr(u8)]
- pub(crate) enum Priority {
- P0 = 0,
- P1 = 1,
- P2 = 2,
- P3 = 3,
- }
- #[derive(Clone, Copy, Debug, Eq, PartialEq)]
- #[repr(u16)]
- pub(crate) enum Context {
- C0 = 0,
- C1 = 1,
- C2 = 2,
- }
- unsafe impl InterruptNumber for Interrupt {
- const MAX_INTERRUPT_NUMBER: u16 = 4;
- #[inline]
- fn number(self) -> u16 {
- self as _
- }
- #[inline]
- fn from_number(number: u16) -> Result<Self, u16> {
- if number > Self::MAX_INTERRUPT_NUMBER || number == 0 {
- Err(number)
- } else {
- // SAFETY: valid interrupt number
- Ok(unsafe { core::mem::transmute(number) })
- }
- }
- }
- unsafe impl PriorityNumber for Priority {
- const MAX_PRIORITY_NUMBER: u8 = 3;
- #[inline]
- fn number(self) -> u8 {
- self as _
- }
- #[inline]
- fn from_number(number: u8) -> Result<Self, u8> {
- if number > Self::MAX_PRIORITY_NUMBER {
- Err(number)
- } else {
- // SAFETY: valid priority number
- Ok(unsafe { core::mem::transmute(number) })
- }
- }
- }
- unsafe impl HartIdNumber for Context {
- const MAX_HART_ID_NUMBER: u16 = 2;
- #[inline]
- fn number(self) -> u16 {
- self as _
- }
- #[inline]
- fn from_number(number: u16) -> Result<Self, u16> {
- if number > Self::MAX_HART_ID_NUMBER {
- Err(number)
- } else {
- // SAFETY: valid context number
- Ok(unsafe { core::mem::transmute(number) })
- }
- }
- }
- #[test]
- fn check_interrupt_enum() {
- assert_eq!(Interrupt::I1.number(), 1);
- assert_eq!(Interrupt::I2.number(), 2);
- assert_eq!(Interrupt::I3.number(), 3);
- assert_eq!(Interrupt::I4.number(), 4);
- assert_eq!(Interrupt::from_number(1), Ok(Interrupt::I1));
- assert_eq!(Interrupt::from_number(2), Ok(Interrupt::I2));
- assert_eq!(Interrupt::from_number(3), Ok(Interrupt::I3));
- assert_eq!(Interrupt::from_number(4), Ok(Interrupt::I4));
- assert_eq!(Interrupt::from_number(0), Err(0));
- assert_eq!(Interrupt::from_number(5), Err(5));
- }
- #[test]
- fn check_priority_enum() {
- assert_eq!(Priority::P0.number(), 0);
- assert_eq!(Priority::P1.number(), 1);
- assert_eq!(Priority::P2.number(), 2);
- assert_eq!(Priority::P3.number(), 3);
- assert_eq!(Priority::from_number(0), Ok(Priority::P0));
- assert_eq!(Priority::from_number(1), Ok(Priority::P1));
- assert_eq!(Priority::from_number(2), Ok(Priority::P2));
- assert_eq!(Priority::from_number(3), Ok(Priority::P3));
- assert_eq!(Priority::from_number(4), Err(4));
- }
- #[test]
- fn check_context_enum() {
- assert_eq!(Context::C0.number(), 0);
- assert_eq!(Context::C1.number(), 1);
- assert_eq!(Context::C2.number(), 2);
- assert_eq!(Context::from_number(0), Ok(Context::C0));
- assert_eq!(Context::from_number(1), Ok(Context::C1));
- assert_eq!(Context::from_number(2), Ok(Context::C2));
- assert_eq!(Context::from_number(3), Err(3));
- }
- #[allow(dead_code)]
- #[test]
- fn check_plic() {
- crate::plic_codegen!(
- base 0x0C00_0000,
- ctxs [ctx0 = (Context::C0, "`C0`"), ctx1 = (Context::C1, "`C1`"), ctx2 = (Context::C2, "`C2`")],
- );
- let priorities = PLIC::priorities();
- let pendings = PLIC::pendings();
- assert_eq!(priorities.address(), 0x0C00_0000);
- assert_eq!(pendings.address(), 0x0C00_1000);
- for i in 0..=Context::MAX_HART_ID_NUMBER {
- let context = Context::from_number(i).unwrap();
- let i = i as usize;
- let ctx = PLIC::ctx(context);
- 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(Context::C0));
- assert_eq!(PLIC::ctx1(), PLIC::ctx(Context::C1));
- assert_eq!(PLIC::ctx2(), PLIC::ctx(Context::C2));
- }
- }
|