Parcourir la source

Macro for creating CLINT peripherals

Román Cárdenas il y a 1 an
Parent
commit
42f56420b2
3 fichiers modifiés avec 198 ajouts et 4 suppressions
  1. 47 0
      src/aclint.rs
  2. 147 0
      src/macros.rs
  3. 4 4
      src/plic.rs

+ 47 - 0
src/aclint.rs

@@ -65,12 +65,14 @@ impl<C: Clint> CLINT<C> {
     const MTIME_OFFSET: usize = 0xBFF8;
 
     /// Returns the `MSWI` peripheral.
+    #[inline]
     pub const fn mswi() -> mswi::MSWI {
         // SAFETY: valid base address
         unsafe { mswi::MSWI::new(C::BASE) }
     }
 
     /// Returns the `MTIMER` peripheral.
+    #[inline]
     pub const fn mtimer() -> mtimer::MTIMER {
         // SAFETY: valid base address
         unsafe {
@@ -81,3 +83,48 @@ impl<C: Clint> CLINT<C> {
         }
     }
 }
+
+#[cfg(test)]
+pub(crate) mod test {
+    use super::*;
+
+    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+    #[repr(u16)]
+    pub(crate) enum HartId {
+        H0 = 0,
+        H1 = 1,
+        H2 = 2,
+    }
+
+    unsafe impl HartIdNumber for HartId {
+        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_hart_id_enum() {
+        assert_eq!(HartId::H0.number(), 0);
+        assert_eq!(HartId::H1.number(), 1);
+        assert_eq!(HartId::H2.number(), 2);
+
+        assert_eq!(HartId::from_number(0), Ok(HartId::H0));
+        assert_eq!(HartId::from_number(1), Ok(HartId::H1));
+        assert_eq!(HartId::from_number(2), Ok(HartId::H2));
+
+        assert_eq!(HartId::from_number(3), Err(3));
+    }
+}

+ 147 - 0
src/macros.rs

@@ -1 +1,148 @@
+/// Macro to create interfaces to CLINT peripherals in PACs.
+/// The resulting struct will be named `CLINT`, and will provide safe access to the CLINT registers.
+///
+/// This macro expects 2 different argument types:
+///
+/// - Base address (**MANDATORY**): base address of the CLINT peripheral of the target.
+/// - Per-HART mtimecmp registers (**OPTIONAL**): a list of `mtimecmp` registers for easing access to per-HART mtimecmp regs.
+///
+/// Check the examples below for more details about the usage and syntax of this macro.
+///
+/// # Example
+///
+/// ## Base address only
+///
+/// ```
+/// use riscv_peripheral::clint_codegen;
+///
+/// clint_codegen!(base 0x0200_0000;);
+///
+/// let mswi = CLINT::mswi(); // MSWI peripheral
+/// let mtimer = CLINT::mtimer(); // MTIMER peripheral
+/// ```
+///
+/// ## Base address and per-HART mtimecmp registers
+///
+/// ```
+/// use riscv_peripheral::clint_codegen;
+///
+/// /// HART IDs for the target CLINT peripheral
+/// #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+/// #[repr(u16)]
+/// pub enum HartId { H0 = 0, H1 = 1, H2 = 2 }
+///
+/// // Implement `HartIdNumber` for `HartId`
+/// unsafe impl riscv_peripheral::aclint::HartIdNumber for HartId {
+///   const MAX_HART_ID_NUMBER: u16 = 2;
+///   fn number(self) -> u16 { self as _ }
+///   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) })
+///     }
+///   }
+/// }
+///
+/// clint_codegen!(base 0x0200_0000;
+///   mtimecmp mtimecmp0 = HartId::H0;
+///   mtimecmp mtimecmp1 = HartId::H1;
+///   mtimecmp mtimecmp2 = HartId::H2;
+/// );
+///
+/// let mswi = CLINT::mswi(); // MSWI peripheral
+/// let mtimer = CLINT::mtimer(); // MTIMER peripheral
+///
+/// let mtimecmp0 = CLINT::mtimecmp0(); // mtimecmp register for HART 0
+/// let mtimecmp1 = CLINT::mtimecmp1(); // mtimecmp register for HART 1
+/// let mtimecmp2 = CLINT::mtimecmp2(); // mtimecmp register for HART 2
+/// ```
+#[macro_export]
+macro_rules! clint_codegen {
+    () => {};
+    (base $addr:literal; $($tail:tt)*) => {
+        /// CLINT peripheral
+        #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+        pub struct CLINT;
 
+        unsafe impl $crate::aclint::Clint for CLINT {
+            const BASE: usize = $addr;
+        }
+
+        impl CLINT {
+            /// Returns the `MSWI` peripheral.
+            #[inline]
+            pub const fn mswi() -> $crate::aclint::mswi::MSWI {
+                $crate::aclint::CLINT::<CLINT>::mswi()
+            }
+
+            /// Returns the `MTIMER` peripheral.
+            #[inline]
+            pub const fn mtimer() -> $crate::aclint::mtimer::MTIMER {
+                $crate::aclint::CLINT::<CLINT>::mtimer()
+            }
+        }
+
+        clint_codegen!($($tail)*);
+    };
+    (mtimecmp $fn:ident = $hart:expr; $($tail:tt)*) => {
+        impl CLINT {
+            /// Returns the `MTIMECMP` register for the given HART.
+            #[inline]
+            pub fn $fn() -> $crate::aclint::mtimer::MTIMECMP {
+                Self::mtimer().mtimecmp($hart)
+            }
+        }
+
+        clint_codegen!($($tail)*);
+    };
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::aclint::test::HartId;
+
+    #[test]
+    fn test_clint_mtimecmp() {
+        // Call CLINT macro with a base address and a list of mtimecmps for easing access to per-HART mtimecmp regs.
+        clint_codegen!(base 0x0200_0000;
+            mtimecmp mtimecmp0 = HartId::H0;
+            mtimecmp mtimecmp1 = HartId::H1;
+            mtimecmp mtimecmp2 = HartId::H2;
+        );
+
+        let mswi = CLINT::mswi();
+        let mtimer = CLINT::mtimer();
+
+        assert_eq!(mswi.msip0.get_ptr() as usize, 0x0200_0000);
+        assert_eq!(mtimer.mtimecmp0.get_ptr() as usize, 0x0200_4000);
+        assert_eq!(mtimer.mtime.get_ptr() as usize, 0x0200_bff8);
+
+        let mtimecmp0 = mtimer.mtimecmp(HartId::H0);
+        let mtimecmp1 = mtimer.mtimecmp(HartId::H1);
+        let mtimecmp2 = mtimer.mtimecmp(HartId::H2);
+
+        assert_eq!(mtimecmp0.get_ptr() as usize, 0x0200_4000);
+        assert_eq!(mtimecmp1.get_ptr() as usize, 0x0200_4000 + 1 * 8); // 8 bytes per register
+        assert_eq!(mtimecmp2.get_ptr() as usize, 0x0200_4000 + 2 * 8);
+
+        // Check that the mtimecmpX functions are equivalent to the mtimer.mtimecmp(X) function.
+        let mtimecmp0 = CLINT::mtimecmp0();
+        let mtimecmp1 = CLINT::mtimecmp1();
+        let mtimecmp2 = CLINT::mtimecmp2();
+
+        assert_eq!(
+            mtimecmp0.get_ptr() as usize,
+            mtimer.mtimecmp(HartId::H0).get_ptr() as usize
+        );
+        assert_eq!(
+            mtimecmp1.get_ptr() as usize,
+            mtimer.mtimecmp(HartId::H1).get_ptr() as usize
+        );
+        assert_eq!(
+            mtimecmp2.get_ptr() as usize,
+            mtimer.mtimecmp(HartId::H2).get_ptr() as usize
+        );
+    }
+}

+ 4 - 4
src/plic.rs

@@ -206,12 +206,12 @@ impl<P: Plic> PLIC<P> {
 }
 
 #[cfg(test)]
-pub(self) mod test {
+pub(crate) mod test {
     use super::*;
 
     #[derive(Clone, Copy, Debug, Eq, PartialEq)]
     #[repr(u16)]
-    pub(super) enum Interrupt {
+    pub(crate) enum Interrupt {
         I1 = 1,
         I2 = 2,
         I3 = 3,
@@ -220,7 +220,7 @@ pub(self) mod test {
 
     #[derive(Clone, Copy, Debug, Eq, PartialEq)]
     #[repr(u8)]
-    pub(super) enum Priority {
+    pub(crate) enum Priority {
         P0 = 0,
         P1 = 1,
         P2 = 2,
@@ -229,7 +229,7 @@ pub(self) mod test {
 
     #[derive(Clone, Copy, Debug, Eq, PartialEq)]
     #[repr(u16)]
-    pub(super) enum Context {
+    pub(crate) enum Context {
         C0 = 0,
         C1 = 1,
         C2 = 2,