plic.rs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. //! Platform-Level Interrupt Controller (PLIC) peripheral.
  2. //!
  3. //! Specification: <https://github.com/riscv/riscv-plic-spec/blob/master/riscv-plic.adoc>
  4. pub mod claim;
  5. pub mod enables;
  6. pub mod pendings;
  7. pub mod priorities;
  8. pub mod threshold;
  9. /// Trait for enums of interrupt numbers.
  10. ///
  11. /// This trait should be implemented by a peripheral access crate (PAC)
  12. /// on its enum of available external interrupts for a specific device.
  13. /// Each variant must convert to a `u16` of its interrupt number.
  14. ///
  15. /// # Note
  16. ///
  17. /// Recall that the interrupt number `0` is reserved as "no interrupt".
  18. ///
  19. /// # Safety
  20. ///
  21. /// * This trait must only be implemented on a PAC of a target with a PLIC peripheral.
  22. /// * This trait must only be implemented on enums of external interrupts.
  23. /// * Each enum variant must represent a distinct value (no duplicates are permitted),
  24. /// * Each enum variant must always return the same value (do not change at runtime).
  25. /// * All the interrupt numbers must be less than or equal to `MAX_INTERRUPT_NUMBER`.
  26. /// * `MAX_INTERRUPT_NUMBER` must coincide with the highest allowed interrupt number.
  27. pub unsafe trait InterruptNumber: Copy {
  28. /// Highest number assigned to an interrupt source.
  29. const MAX_INTERRUPT_NUMBER: u16;
  30. /// Converts an interrupt source to its corresponding number.
  31. fn number(self) -> u16;
  32. /// Tries to convert a number to a valid interrupt source.
  33. /// If the conversion fails, it returns an error with the number back.
  34. fn from_number(value: u16) -> Result<Self, u16>;
  35. }
  36. /// Trait for enums of priority levels.
  37. ///
  38. /// This trait should be implemented by a peripheral access crate (PAC)
  39. /// on its enum of available priority numbers for a specific device.
  40. /// Each variant must convert to a `u8` of its priority level.
  41. ///
  42. /// # Note
  43. ///
  44. /// Recall that the priority number `0` is reserved as "never interrupt".
  45. ///
  46. /// # Safety
  47. ///
  48. /// * This trait must only be implemented on a PAC of a target with a PLIC peripheral.
  49. /// * This trait must only be implemented on enums of priority levels.
  50. /// * Each enum variant must represent a distinct value (no duplicates are permitted).
  51. /// * Each enum variant must always return the same value (do not change at runtime).
  52. /// * There must be a valid priority number set to 0 (i.e., never interrupt).
  53. /// * All the priority level numbers must be less than or equal to `MAX_PRIORITY_NUMBER`.
  54. /// * `MAX_PRIORITY_NUMBER` must coincide with the highest allowed priority number.
  55. pub unsafe trait PriorityNumber: Copy {
  56. /// Number assigned to the highest priority level.
  57. const MAX_PRIORITY_NUMBER: u8;
  58. /// Converts a priority level to its corresponding number.
  59. fn number(self) -> u8;
  60. /// Tries to convert a number to a valid priority level.
  61. /// If the conversion fails, it returns an error with the number back.
  62. fn from_number(value: u8) -> Result<Self, u8>;
  63. }
  64. /// Trait for enums of PLIC contexts.
  65. ///
  66. /// This trait should be implemented by a peripheral access crate (PAC)
  67. /// on its enum of available contexts for a specific device.
  68. /// Each variant must convert to a `u16` of its context number.
  69. ///
  70. /// # Safety
  71. ///
  72. /// * This trait must only be implemented on a PAC of a target with a PLIC peripheral.
  73. /// * This trait must only be implemented on enums of contexts.
  74. /// * Each enum variant must represent a distinct value (no duplicates are permitted),
  75. /// * Each anum variant must always return the same value (do not change at runtime).
  76. /// * All the context numbers must be less than or equal to `MAX_CONTEXT_NUMBER`.
  77. /// * `MAX_CONTEXT_NUMBER` must coincide with the highest allowed context number.
  78. pub unsafe trait ContextNumber: Copy {
  79. /// Highest number assigned to a context.
  80. const MAX_CONTEXT_NUMBER: u16;
  81. /// Converts an context to its corresponding number.
  82. fn number(self) -> u16;
  83. /// Tries to convert a number to a valid context.
  84. /// If the conversion fails, it returns an error with the number back.
  85. fn from_number(value: u16) -> Result<Self, u16>;
  86. }
  87. /// Trait for a PLIC peripheral.
  88. ///
  89. /// # Safety
  90. ///
  91. /// * This trait must only be implemented on a PAC of a target with a PLIC peripheral.
  92. /// * The PLIC peripheral base address `BASE` must be valid for the target device.
  93. pub unsafe trait Plic: Copy {
  94. /// Base address of the PLIC peripheral.
  95. const BASE: usize;
  96. }
  97. /// Platform-Level Interrupt Controler (PLIC) peripheral.
  98. ///
  99. /// The RISC-V standard does not specify a fixed location for the PLIC.
  100. /// Thus, each platform must specify the base address of the PLIC on the platform.
  101. /// The base address, as well as all the associated types, are defined in the [`Plic`] trait.
  102. ///
  103. /// The PLIC standard allows up to 15_872 different contexts for interfacing the PLIC.
  104. /// Each context has an assigned index starting from 0 to up to 15_871.
  105. /// Usually, each HART uses a dedicated context. In this way, they do not interfere
  106. /// with each other when attending to external interruptions.
  107. #[allow(clippy::upper_case_acronyms)]
  108. #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
  109. pub struct PLIC<P: Plic> {
  110. _marker: core::marker::PhantomData<P>,
  111. }
  112. impl<P: Plic> PLIC<P> {
  113. const PRIORITIES_OFFSET: usize = 0;
  114. const PENDINGS_OFFSET: usize = 0x1000;
  115. /// Returns the priorities register of the PLIC.
  116. /// This register allows to set the priority level of each interrupt source.
  117. /// The priority level of each interrupt source is shared among all the contexts.
  118. #[inline]
  119. pub fn priorities() -> priorities::PRIORITIES {
  120. // SAFETY: valid address
  121. unsafe { priorities::PRIORITIES::new(P::BASE + Self::PRIORITIES_OFFSET) }
  122. }
  123. /// Returns the pendings register of the PLIC.
  124. /// This register allows to check if a particular interrupt source is pending.
  125. #[inline]
  126. pub fn pendings() -> pendings::PENDINGS {
  127. // SAFETY: valid address
  128. unsafe { pendings::PENDINGS::new(P::BASE + Self::PENDINGS_OFFSET) }
  129. }
  130. /// Returns a proxy to access to all the PLIC registers of a given context.
  131. #[inline]
  132. pub fn ctx<C: ContextNumber>(context: C) -> CTX<P> {
  133. // SAFETY: valid context number
  134. unsafe { CTX::new(context.number()) }
  135. }
  136. }
  137. /// PLIC context proxy. It provides access to the PLIC registers of a given context.
  138. #[allow(clippy::upper_case_acronyms)]
  139. #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
  140. pub struct CTX<P: Plic> {
  141. context: usize,
  142. _marker: core::marker::PhantomData<P>,
  143. }
  144. impl<P: Plic> CTX<P> {
  145. const ENABLES_OFFSET: usize = 0x2000;
  146. const ENABLES_SEPARATION: usize = 0x80;
  147. const THRESHOLDS_OFFSET: usize = 0x20_0000;
  148. const THRESHOLDS_SEPARATION: usize = 0x1000;
  149. const CLAIMS_OFFSET: usize = 0x20_0004;
  150. const CLAIMS_SEPARATION: usize = 0x1000;
  151. /// Creates a new PLIC context proxy
  152. ///
  153. /// # Safety
  154. ///
  155. /// The context number must be valid for the target device.
  156. #[inline]
  157. pub(crate) unsafe fn new(context: u16) -> Self {
  158. Self {
  159. context: context as _,
  160. _marker: core::marker::PhantomData,
  161. }
  162. }
  163. /// Returns the context number of this proxy.
  164. #[inline]
  165. pub const fn context(self) -> u16 {
  166. self.context as _
  167. }
  168. /// Returns the interrupts enable register of the context.
  169. #[inline]
  170. pub const fn enables(self) -> enables::ENABLES {
  171. let addr = P::BASE + Self::ENABLES_OFFSET + self.context * Self::ENABLES_SEPARATION;
  172. // SAFETY: valid address
  173. unsafe { enables::ENABLES::new(addr) }
  174. }
  175. /// Returns the interrupt threshold register of the context.
  176. #[inline]
  177. pub const fn threshold(self) -> threshold::THRESHOLD {
  178. let addr = P::BASE + Self::THRESHOLDS_OFFSET + self.context * Self::THRESHOLDS_SEPARATION;
  179. // SAFETY: valid address
  180. unsafe { threshold::THRESHOLD::new(addr) }
  181. }
  182. /// Returns the interrupt claim/complete register of the context.
  183. #[inline]
  184. pub const fn claim(self) -> claim::CLAIM {
  185. let addr = P::BASE + Self::CLAIMS_OFFSET + self.context * Self::CLAIMS_SEPARATION;
  186. // SAFETY: valid address
  187. unsafe { claim::CLAIM::new(addr) }
  188. }
  189. }
  190. #[cfg(test)]
  191. pub(crate) mod test {
  192. use super::{ContextNumber, InterruptNumber, PriorityNumber};
  193. #[derive(Clone, Copy, Debug, Eq, PartialEq)]
  194. #[repr(u16)]
  195. pub(crate) enum Interrupt {
  196. I1 = 1,
  197. I2 = 2,
  198. I3 = 3,
  199. I4 = 4,
  200. }
  201. #[derive(Clone, Copy, Debug, Eq, PartialEq)]
  202. #[repr(u8)]
  203. pub(crate) enum Priority {
  204. P0 = 0,
  205. P1 = 1,
  206. P2 = 2,
  207. P3 = 3,
  208. }
  209. #[derive(Clone, Copy, Debug, Eq, PartialEq)]
  210. #[repr(u16)]
  211. pub(crate) enum Context {
  212. C0 = 0,
  213. C1 = 1,
  214. C2 = 2,
  215. }
  216. unsafe impl InterruptNumber for Interrupt {
  217. const MAX_INTERRUPT_NUMBER: u16 = 4;
  218. #[inline]
  219. fn number(self) -> u16 {
  220. self as _
  221. }
  222. #[inline]
  223. fn from_number(number: u16) -> Result<Self, u16> {
  224. if number > Self::MAX_INTERRUPT_NUMBER || number == 0 {
  225. Err(number)
  226. } else {
  227. // SAFETY: valid interrupt number
  228. Ok(unsafe { core::mem::transmute(number) })
  229. }
  230. }
  231. }
  232. unsafe impl PriorityNumber for Priority {
  233. const MAX_PRIORITY_NUMBER: u8 = 3;
  234. #[inline]
  235. fn number(self) -> u8 {
  236. self as _
  237. }
  238. #[inline]
  239. fn from_number(number: u8) -> Result<Self, u8> {
  240. if number > Self::MAX_PRIORITY_NUMBER {
  241. Err(number)
  242. } else {
  243. // SAFETY: valid priority number
  244. Ok(unsafe { core::mem::transmute(number) })
  245. }
  246. }
  247. }
  248. unsafe impl ContextNumber for Context {
  249. const MAX_CONTEXT_NUMBER: u16 = 2;
  250. #[inline]
  251. fn number(self) -> u16 {
  252. self as _
  253. }
  254. #[inline]
  255. fn from_number(number: u16) -> Result<Self, u16> {
  256. if number > Self::MAX_CONTEXT_NUMBER {
  257. Err(number)
  258. } else {
  259. // SAFETY: valid context number
  260. Ok(unsafe { core::mem::transmute(number) })
  261. }
  262. }
  263. }
  264. #[test]
  265. fn check_interrupt_enum() {
  266. assert_eq!(Interrupt::I1.number(), 1);
  267. assert_eq!(Interrupt::I2.number(), 2);
  268. assert_eq!(Interrupt::I3.number(), 3);
  269. assert_eq!(Interrupt::I4.number(), 4);
  270. assert_eq!(Interrupt::from_number(1), Ok(Interrupt::I1));
  271. assert_eq!(Interrupt::from_number(2), Ok(Interrupt::I2));
  272. assert_eq!(Interrupt::from_number(3), Ok(Interrupt::I3));
  273. assert_eq!(Interrupt::from_number(4), Ok(Interrupt::I4));
  274. assert_eq!(Interrupt::from_number(0), Err(0));
  275. assert_eq!(Interrupt::from_number(5), Err(5));
  276. }
  277. #[test]
  278. fn check_priority_enum() {
  279. assert_eq!(Priority::P0.number(), 0);
  280. assert_eq!(Priority::P1.number(), 1);
  281. assert_eq!(Priority::P2.number(), 2);
  282. assert_eq!(Priority::P3.number(), 3);
  283. assert_eq!(Priority::from_number(0), Ok(Priority::P0));
  284. assert_eq!(Priority::from_number(1), Ok(Priority::P1));
  285. assert_eq!(Priority::from_number(2), Ok(Priority::P2));
  286. assert_eq!(Priority::from_number(3), Ok(Priority::P3));
  287. assert_eq!(Priority::from_number(4), Err(4));
  288. }
  289. #[test]
  290. fn check_context_enum() {
  291. assert_eq!(Context::C0.number(), 0);
  292. assert_eq!(Context::C1.number(), 1);
  293. assert_eq!(Context::C2.number(), 2);
  294. assert_eq!(Context::from_number(0), Ok(Context::C0));
  295. assert_eq!(Context::from_number(1), Ok(Context::C1));
  296. assert_eq!(Context::from_number(2), Ok(Context::C2));
  297. assert_eq!(Context::from_number(3), Err(3));
  298. }
  299. #[allow(dead_code)]
  300. #[test]
  301. fn check_plic() {
  302. crate::plic_codegen!(
  303. base 0x0C00_0000,
  304. ctxs [ctx0 = (Context::C0, "`C0`"), ctx1 = (Context::C1, "`C1`"), ctx2 = (Context::C2, "`C2`")],
  305. );
  306. let priorities = PLIC::priorities();
  307. let pendings = PLIC::pendings();
  308. assert_eq!(priorities.address(), 0x0C00_0000);
  309. assert_eq!(pendings.address(), 0x0C00_1000);
  310. for i in 0..=Context::MAX_CONTEXT_NUMBER {
  311. let context = Context::from_number(i).unwrap();
  312. let i = i as usize;
  313. let ctx = PLIC::ctx(context);
  314. assert_eq!(ctx.enables().address(), 0x0C00_0000 + 0x2000 + i * 0x80);
  315. assert_eq!(
  316. ctx.threshold().get_ptr() as usize,
  317. 0x0C00_0000 + 0x20_0000 + i * 0x1000
  318. );
  319. assert_eq!(
  320. ctx.claim().get_ptr() as usize,
  321. 0x0C00_0000 + 0x20_0004 + i * 0x1000
  322. );
  323. }
  324. assert_eq!(PLIC::ctx0(), PLIC::ctx(Context::C0));
  325. assert_eq!(PLIC::ctx1(), PLIC::ctx(Context::C1));
  326. assert_eq!(PLIC::ctx2(), PLIC::ctx(Context::C2));
  327. }
  328. }