plic.rs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  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 `true` if a machine external interrupt is pending.
  116. #[inline]
  117. pub fn is_interrupting() -> bool {
  118. riscv::register::mip::read().mext()
  119. }
  120. /// Returns true if Machine External Interrupts are enabled.
  121. #[inline]
  122. pub fn is_enabled() -> bool {
  123. riscv::register::mie::read().mext()
  124. }
  125. /// Sets the Machine External Interrupt bit of the `mie` CSR.
  126. /// This bit must be set for the PLIC to trigger machine external interrupts.
  127. ///
  128. /// # Safety
  129. ///
  130. /// Enabling the `PLIC` may break mask-based critical sections.
  131. #[inline]
  132. pub unsafe fn enable() {
  133. riscv::register::mie::set_mext();
  134. }
  135. /// Clears the Machine External Interrupt bit of the `mie` CSR.
  136. /// When cleared, the PLIC does not trigger machine external interrupts.
  137. #[inline]
  138. pub fn disable() {
  139. // SAFETY: it is safe to disable interrupts
  140. unsafe { riscv::register::mie::clear_mext() };
  141. }
  142. /// Returns the priorities register of the PLIC.
  143. /// This register allows to set the priority level of each interrupt source.
  144. /// The priority level of each interrupt source is shared among all the contexts.
  145. #[inline]
  146. pub fn priorities() -> priorities::PRIORITIES {
  147. // SAFETY: valid address
  148. unsafe { priorities::PRIORITIES::new(P::BASE + Self::PRIORITIES_OFFSET) }
  149. }
  150. /// Returns the pendings register of the PLIC.
  151. /// This register allows to check if a particular interrupt source is pending.
  152. #[inline]
  153. pub fn pendings() -> pendings::PENDINGS {
  154. // SAFETY: valid address
  155. unsafe { pendings::PENDINGS::new(P::BASE + Self::PENDINGS_OFFSET) }
  156. }
  157. /// Returns a proxy to access to all the PLIC registers of a given context.
  158. #[inline]
  159. pub fn ctx<C: ContextNumber>(context: C) -> CTX<P> {
  160. // SAFETY: valid context number
  161. unsafe { CTX::new(context.number()) }
  162. }
  163. }
  164. /// PLIC context proxy. It provides access to the PLIC registers of a given context.
  165. #[allow(clippy::upper_case_acronyms)]
  166. #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
  167. pub struct CTX<P: Plic> {
  168. context: usize,
  169. _marker: core::marker::PhantomData<P>,
  170. }
  171. impl<P: Plic> CTX<P> {
  172. const ENABLES_OFFSET: usize = 0x2000;
  173. const ENABLES_SEPARATION: usize = 0x80;
  174. const THRESHOLDS_OFFSET: usize = 0x20_0000;
  175. const THRESHOLDS_SEPARATION: usize = 0x1000;
  176. const CLAIMS_OFFSET: usize = 0x20_0004;
  177. const CLAIMS_SEPARATION: usize = 0x1000;
  178. /// Creates a new PLIC context proxy
  179. ///
  180. /// # Safety
  181. ///
  182. /// The context number must be valid for the target device.
  183. #[inline]
  184. pub(crate) unsafe fn new(context: u16) -> Self {
  185. Self {
  186. context: context as _,
  187. _marker: core::marker::PhantomData,
  188. }
  189. }
  190. /// Returns the context number of this proxy.
  191. #[inline]
  192. pub const fn context(self) -> u16 {
  193. self.context as _
  194. }
  195. /// Returns the interrupts enable register of the context.
  196. #[inline]
  197. pub const fn enables(self) -> enables::ENABLES {
  198. let addr = P::BASE + Self::ENABLES_OFFSET + self.context * Self::ENABLES_SEPARATION;
  199. // SAFETY: valid address
  200. unsafe { enables::ENABLES::new(addr) }
  201. }
  202. /// Returns the interrupt threshold register of the context.
  203. #[inline]
  204. pub const fn threshold(self) -> threshold::THRESHOLD {
  205. let addr = P::BASE + Self::THRESHOLDS_OFFSET + self.context * Self::THRESHOLDS_SEPARATION;
  206. // SAFETY: valid address
  207. unsafe { threshold::THRESHOLD::new(addr) }
  208. }
  209. /// Returns the interrupt claim/complete register of the context.
  210. #[inline]
  211. pub const fn claim(self) -> claim::CLAIM {
  212. let addr = P::BASE + Self::CLAIMS_OFFSET + self.context * Self::CLAIMS_SEPARATION;
  213. // SAFETY: valid address
  214. unsafe { claim::CLAIM::new(addr) }
  215. }
  216. }
  217. #[cfg(test)]
  218. pub(crate) mod test {
  219. use super::{ContextNumber, InterruptNumber, PriorityNumber};
  220. #[derive(Clone, Copy, Debug, Eq, PartialEq)]
  221. #[repr(u16)]
  222. pub(crate) enum Interrupt {
  223. I1 = 1,
  224. I2 = 2,
  225. I3 = 3,
  226. I4 = 4,
  227. }
  228. #[derive(Clone, Copy, Debug, Eq, PartialEq)]
  229. #[repr(u8)]
  230. pub(crate) enum Priority {
  231. P0 = 0,
  232. P1 = 1,
  233. P2 = 2,
  234. P3 = 3,
  235. }
  236. #[derive(Clone, Copy, Debug, Eq, PartialEq)]
  237. #[repr(u16)]
  238. pub(crate) enum Context {
  239. C0 = 0,
  240. C1 = 1,
  241. C2 = 2,
  242. }
  243. unsafe impl InterruptNumber for Interrupt {
  244. const MAX_INTERRUPT_NUMBER: u16 = 4;
  245. #[inline]
  246. fn number(self) -> u16 {
  247. self as _
  248. }
  249. #[inline]
  250. fn from_number(number: u16) -> Result<Self, u16> {
  251. if number > Self::MAX_INTERRUPT_NUMBER || number == 0 {
  252. Err(number)
  253. } else {
  254. // SAFETY: valid interrupt number
  255. Ok(unsafe { core::mem::transmute(number) })
  256. }
  257. }
  258. }
  259. unsafe impl PriorityNumber for Priority {
  260. const MAX_PRIORITY_NUMBER: u8 = 3;
  261. #[inline]
  262. fn number(self) -> u8 {
  263. self as _
  264. }
  265. #[inline]
  266. fn from_number(number: u8) -> Result<Self, u8> {
  267. if number > Self::MAX_PRIORITY_NUMBER {
  268. Err(number)
  269. } else {
  270. // SAFETY: valid priority number
  271. Ok(unsafe { core::mem::transmute(number) })
  272. }
  273. }
  274. }
  275. unsafe impl ContextNumber for Context {
  276. const MAX_CONTEXT_NUMBER: u16 = 2;
  277. #[inline]
  278. fn number(self) -> u16 {
  279. self as _
  280. }
  281. #[inline]
  282. fn from_number(number: u16) -> Result<Self, u16> {
  283. if number > Self::MAX_CONTEXT_NUMBER {
  284. Err(number)
  285. } else {
  286. // SAFETY: valid context number
  287. Ok(unsafe { core::mem::transmute(number) })
  288. }
  289. }
  290. }
  291. #[test]
  292. fn check_interrupt_enum() {
  293. assert_eq!(Interrupt::I1.number(), 1);
  294. assert_eq!(Interrupt::I2.number(), 2);
  295. assert_eq!(Interrupt::I3.number(), 3);
  296. assert_eq!(Interrupt::I4.number(), 4);
  297. assert_eq!(Interrupt::from_number(1), Ok(Interrupt::I1));
  298. assert_eq!(Interrupt::from_number(2), Ok(Interrupt::I2));
  299. assert_eq!(Interrupt::from_number(3), Ok(Interrupt::I3));
  300. assert_eq!(Interrupt::from_number(4), Ok(Interrupt::I4));
  301. assert_eq!(Interrupt::from_number(0), Err(0));
  302. assert_eq!(Interrupt::from_number(5), Err(5));
  303. }
  304. #[test]
  305. fn check_priority_enum() {
  306. assert_eq!(Priority::P0.number(), 0);
  307. assert_eq!(Priority::P1.number(), 1);
  308. assert_eq!(Priority::P2.number(), 2);
  309. assert_eq!(Priority::P3.number(), 3);
  310. assert_eq!(Priority::from_number(0), Ok(Priority::P0));
  311. assert_eq!(Priority::from_number(1), Ok(Priority::P1));
  312. assert_eq!(Priority::from_number(2), Ok(Priority::P2));
  313. assert_eq!(Priority::from_number(3), Ok(Priority::P3));
  314. assert_eq!(Priority::from_number(4), Err(4));
  315. }
  316. #[test]
  317. fn check_context_enum() {
  318. assert_eq!(Context::C0.number(), 0);
  319. assert_eq!(Context::C1.number(), 1);
  320. assert_eq!(Context::C2.number(), 2);
  321. assert_eq!(Context::from_number(0), Ok(Context::C0));
  322. assert_eq!(Context::from_number(1), Ok(Context::C1));
  323. assert_eq!(Context::from_number(2), Ok(Context::C2));
  324. assert_eq!(Context::from_number(3), Err(3));
  325. }
  326. #[allow(dead_code)]
  327. #[test]
  328. fn check_plic() {
  329. crate::plic_codegen!(
  330. base 0x0C00_0000,
  331. ctxs [ctx0 = (Context::C0, "`C0`"), ctx1 = (Context::C1, "`C1`"), ctx2 = (Context::C2, "`C2`")],
  332. );
  333. let priorities = PLIC::priorities();
  334. let pendings = PLIC::pendings();
  335. assert_eq!(priorities.address(), 0x0C00_0000);
  336. assert_eq!(pendings.address(), 0x0C00_1000);
  337. for i in 0..=Context::MAX_CONTEXT_NUMBER {
  338. let context = Context::from_number(i).unwrap();
  339. let ctx = PLIC::ctx(context);
  340. assert_eq!(
  341. ctx.enables().address(),
  342. 0x0C00_0000 + 0x2000 + i as usize * 0x80
  343. );
  344. assert_eq!(
  345. ctx.threshold().get_ptr() as usize,
  346. 0x0C00_0000 + 0x20_0000 + i as usize * 0x1000
  347. );
  348. assert_eq!(
  349. ctx.claim().get_ptr() as usize,
  350. 0x0C00_0000 + 0x20_0004 + i as usize * 0x1000
  351. );
  352. }
  353. let ctx0 = PLIC::ctx0();
  354. let ctx_0_ = PLIC::ctx(Context::C0);
  355. assert_eq!(ctx0.enables().address(), ctx_0_.enables().address());
  356. assert_eq!(ctx0.threshold().get_ptr(), ctx_0_.threshold().get_ptr());
  357. assert_eq!(ctx0.claim().get_ptr(), ctx_0_.claim().get_ptr());
  358. let ctx1 = PLIC::ctx1();
  359. let ctx_1_ = PLIC::ctx(Context::C1);
  360. assert_eq!(ctx1.enables().address(), ctx_1_.enables().address());
  361. assert_eq!(ctx1.threshold().get_ptr(), ctx_1_.threshold().get_ptr());
  362. assert_eq!(ctx1.claim().get_ptr(), ctx_1_.claim().get_ptr());
  363. let ctx2 = PLIC::ctx2();
  364. let ctx_2_ = PLIC::ctx(Context::C2);
  365. assert_eq!(ctx2.enables().address(), ctx_2_.enables().address());
  366. assert_eq!(ctx2.threshold().get_ptr(), ctx_2_.threshold().get_ptr());
  367. assert_eq!(ctx2.claim().get_ptr(), ctx_2_.claim().get_ptr());
  368. }
  369. }