Browse Source

HAL implementations

Román Cárdenas 1 year ago
parent
commit
012aa79db4
14 changed files with 473 additions and 46 deletions
  1. 1 1
      .github/workflows/clippy.yml
  2. 5 0
      Cargo.toml
  3. 165 0
      examples/e310x.rs
  4. 9 17
      src/aclint.rs
  5. 27 0
      src/aclint/mswi.rs
  6. 34 1
      src/aclint/mtimer.rs
  7. 27 0
      src/aclint/sswi.rs
  8. 5 0
      src/hal.rs
  9. 47 0
      src/hal/aclint.rs
  10. 5 0
      src/hal_async.rs
  11. 82 0
      src/hal_async/aclint.rs
  12. 3 0
      src/lib.rs
  13. 55 4
      src/macros.rs
  14. 8 23
      src/plic.rs

+ 1 - 1
.github/workflows/clippy.yml

@@ -37,4 +37,4 @@ jobs:
     runs-on: ubuntu-latest
     if: always()
     steps:
-      - run: jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}'
+      - run: jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}'

+ 5 - 0
Cargo.toml

@@ -6,8 +6,13 @@ edition = "2021"
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
+embedded-hal = "1.0.0-rc.1"
+embedded-hal-async = { version = "1.0.0-rc.1", optional =  true }
 riscv = { git = "https://github.com/rust-embedded/riscv", branch = "master" }
 
+[features]
+hal-async = ["embedded-hal-async"]
+
 [package.metadata.docs.rs]
 default-target = "riscv64imac-unknown-none-elf"
 targets = [

+ 165 - 0
examples/e310x.rs

@@ -0,0 +1,165 @@
+use riscv_peripheral::{
+    aclint::HartIdNumber,
+    plic::{ContextNumber, InterruptNumber, PriorityNumber},
+};
+
+#[repr(u16)]
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub enum HartId {
+    H0 = 0,
+}
+
+unsafe impl HartIdNumber for HartId {
+    const MAX_HART_ID_NUMBER: u16 = 0;
+
+    #[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) })
+        }
+    }
+}
+
+unsafe impl ContextNumber for HartId {
+    const MAX_CONTEXT_NUMBER: u16 = 0;
+
+    #[inline]
+    fn number(self) -> u16 {
+        self as _
+    }
+
+    #[inline]
+    fn from_number(number: u16) -> Result<Self, u16> {
+        if number > Self::MAX_CONTEXT_NUMBER {
+            Err(number)
+        } else {
+            // SAFETY: valid context number
+            Ok(unsafe { core::mem::transmute(number) })
+        }
+    }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[repr(u16)]
+pub enum Interrupt {
+    WATCHDOG = 1,
+    RTC = 2,
+    UART0 = 3,
+    UART1 = 4,
+    QSPI0 = 5,
+    QSPI1 = 6,
+    QSPI2 = 7,
+    GPIO0 = 8,
+    GPIO1 = 9,
+    GPIO2 = 10,
+    GPIO3 = 11,
+    GPIO4 = 12,
+    GPIO5 = 13,
+    GPIO6 = 14,
+    GPIO7 = 15,
+    GPIO8 = 16,
+    GPIO9 = 17,
+    GPIO10 = 18,
+    GPIO11 = 19,
+    GPIO12 = 20,
+    GPIO13 = 21,
+    GPIO14 = 22,
+    GPIO15 = 23,
+    GPIO16 = 24,
+    GPIO17 = 25,
+    GPIO18 = 26,
+    GPIO19 = 27,
+    GPIO20 = 28,
+    GPIO21 = 29,
+    GPIO22 = 30,
+    GPIO23 = 31,
+    GPIO24 = 32,
+    GPIO25 = 33,
+    GPIO26 = 34,
+    GPIO27 = 35,
+    GPIO28 = 36,
+    GPIO29 = 37,
+    GPIO30 = 38,
+    GPIO31 = 39,
+    PWM0CMP0 = 40,
+    PWM0CMP1 = 41,
+    PWM0CMP2 = 42,
+    PWM0CMP3 = 43,
+    PWM1CMP0 = 44,
+    PWM1CMP1 = 45,
+    PWM1CMP2 = 46,
+    PWM1CMP3 = 47,
+    PWM2CMP0 = 48,
+    PWM2CMP1 = 49,
+    PWM2CMP2 = 50,
+    PWM2CMP3 = 51,
+    I2C0 = 52,
+}
+
+unsafe impl InterruptNumber for Interrupt {
+    const MAX_INTERRUPT_NUMBER: u16 = 52;
+
+    #[inline]
+    fn number(self) -> u16 {
+        self as _
+    }
+
+    #[inline]
+    fn from_number(number: u16) -> Result<Self, u16> {
+        if number == 0 || number > Self::MAX_INTERRUPT_NUMBER {
+            Err(number)
+        } else {
+            // SAFETY: valid interrupt number
+            Ok(unsafe { core::mem::transmute(number) })
+        }
+    }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[repr(u8)]
+pub enum Priority {
+    P0 = 0,
+    P1 = 1,
+    P2 = 2,
+    P3 = 3,
+    P4 = 4,
+    P5 = 5,
+    P6 = 6,
+    P7 = 7,
+}
+
+unsafe impl PriorityNumber for Priority {
+    const MAX_PRIORITY_NUMBER: u8 = 7;
+
+    #[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) })
+        }
+    }
+}
+
+riscv_peripheral::clint_codegen!(
+    base 0x0200_0000,
+    freq 32_768,
+    mtimecmps [mtimecmp0=(HartId::H0,"`H0`")],
+    msips [msip0=(HartId::H0,"`H0`")],
+);
+
+fn main() {}

+ 9 - 17
src/aclint.rs

@@ -136,6 +136,7 @@ pub(crate) mod test {
         crate::clint_codegen!(
             base 0x0200_0000,
             mtimecmps [mtimecmp0=(HartId::H0,"`H0`"), mtimecmp1=(HartId::H1,"`H1`"), mtimecmp2=(HartId::H2,"`H2`")],
+            msips [msip0=(HartId::H0,"`H0`"), msip1=(HartId::H1,"`H1`"), msip2=(HartId::H2,"`H2`")],
         );
 
         let mswi = CLINT::mswi();
@@ -150,25 +151,16 @@ pub(crate) mod test {
         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!(mtimecmp1.get_ptr() as usize, 0x0200_4000 + 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!(CLINT::mtime(), mtimer.mtime);
+        assert_eq!(CLINT::mtimecmp0(), mtimer.mtimecmp(HartId::H0));
+        assert_eq!(CLINT::mtimecmp1(), mtimer.mtimecmp(HartId::H1));
+        assert_eq!(CLINT::mtimecmp2(), mtimer.mtimecmp(HartId::H2));
 
-        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
-        );
+        assert_eq!(CLINT::msip0(), mswi.msip(HartId::H0));
+        assert_eq!(CLINT::msip1(), mswi.msip(HartId::H1));
+        assert_eq!(CLINT::msip2(), mswi.msip(HartId::H2));
     }
 }

+ 27 - 0
src/aclint/mswi.rs

@@ -58,3 +58,30 @@ impl MSIP {
         self.register.write(0);
     }
 }
+
+#[cfg(test)]
+mod test {
+    use super::super::test::HartId;
+    use super::*;
+
+    #[test]
+    fn test_mswi() {
+        // slice to emulate the interrupt pendings register
+        let raw_reg = [0u32; HartId::MAX_HART_ID_NUMBER as usize + 1];
+        // SAFETY: valid memory address
+        let mswi = unsafe { MSWI::new(raw_reg.as_ptr() as _) };
+
+        for i in 0..=HartId::MAX_HART_ID_NUMBER {
+            let hart_id = HartId::from_number(i).unwrap();
+            let msip = mswi.msip(hart_id);
+            assert!(!msip.is_pending());
+            assert_eq!(raw_reg[i as usize], 0);
+            msip.pend();
+            assert!(msip.is_pending());
+            assert_ne!(raw_reg[i as usize], 0);
+            msip.unpend();
+            assert!(!msip.is_pending());
+            assert_eq!(raw_reg[i as usize], 0);
+        }
+    }
+}

+ 34 - 1
src/aclint/mtimer.rs

@@ -18,7 +18,7 @@ impl MTIMER {
     ///
     /// # Safety
     ///
-    /// The base address must point to a valid `MTIMER` peripheral.
+    /// The base addresses must point to valid `MTIMECMP` and `MTIME` peripherals.
     #[inline]
     pub const unsafe fn new(mtimecmp: usize, mtime: usize) -> Self {
         Self {
@@ -44,3 +44,36 @@ safe_peripheral!(MTIMECMP, u64, RW);
 
 // MTIME register.
 safe_peripheral!(MTIME, u64, RW);
+
+#[cfg(test)]
+mod test {
+    use super::super::test::HartId;
+    use super::*;
+
+    #[test]
+    fn check_mtimer() {
+        // slice to emulate the mtimecmp registers
+        let raw_mtimecmp = [0u64; HartId::MAX_HART_ID_NUMBER as usize + 1];
+        let raw_mtime = 0u64;
+        // SAFETY: valid memory addresses
+        let mtimer =
+            unsafe { MTIMER::new(raw_mtimecmp.as_ptr() as _, &raw_mtime as *const u64 as _) };
+
+        assert_eq!(
+            mtimer.mtimecmp(HartId::H0).get_ptr() as usize,
+            raw_mtimecmp.as_ptr() as usize
+        );
+        assert_eq!(mtimer.mtimecmp(HartId::H1).get_ptr() as usize, unsafe {
+            raw_mtimecmp.as_ptr().offset(1)
+        }
+            as usize);
+        assert_eq!(mtimer.mtimecmp(HartId::H2).get_ptr() as usize, unsafe {
+            raw_mtimecmp.as_ptr().offset(2)
+        }
+            as usize);
+        assert_eq!(
+            mtimer.mtime.get_ptr() as usize,
+            &raw_mtime as *const u64 as _
+        );
+    }
+}

+ 27 - 0
src/aclint/sswi.rs

@@ -89,3 +89,30 @@ impl SETSSIP {
         self.register.write(0);
     }
 }
+
+#[cfg(test)]
+mod test {
+    use super::super::test::HartId;
+    use super::*;
+
+    #[test]
+    fn test_sswi() {
+        // slice to emulate the interrupt pendings register
+        let raw_reg = [0u32; HartId::MAX_HART_ID_NUMBER as usize + 1];
+        // SAFETY: valid memory address
+        let mswi = unsafe { SSWI::new(raw_reg.as_ptr() as _) };
+
+        for i in 0..=HartId::MAX_HART_ID_NUMBER {
+            let hart_id = HartId::from_number(i).unwrap();
+            let setssip = mswi.setssip(hart_id);
+            assert!(!setssip.is_pending());
+            assert_eq!(raw_reg[i as usize], 0);
+            setssip.pend();
+            assert!(setssip.is_pending());
+            assert_ne!(raw_reg[i as usize], 0);
+            setssip.unpend();
+            assert!(!setssip.is_pending());
+            assert_eq!(raw_reg[i as usize], 0);
+        }
+    }
+}

+ 5 - 0
src/hal.rs

@@ -0,0 +1,5 @@
+//! trait implementations for embedded-hal
+
+pub use embedded_hal::*; // re-export embedded-hal to allow macros to use it
+
+pub mod aclint; // ACLINT and CLINT peripherals

+ 47 - 0
src/hal/aclint.rs

@@ -0,0 +1,47 @@
+//! Delay trait implementation for (A)CLINT peripherals
+
+use crate::aclint::mtimer::MTIME;
+pub use crate::hal::delay::DelayUs;
+
+/// Delay implementation for (A)CLINT peripherals.
+pub struct Delay {
+    mtime: MTIME,
+    freq: usize,
+}
+
+impl Delay {
+    /// Creates a new `Delay` instance.
+    #[inline]
+    pub const fn new(mtime: MTIME, freq: usize) -> Self {
+        Self { mtime, freq }
+    }
+
+    /// Returns the frequency of the `MTIME` register.
+    #[inline]
+    pub const fn get_freq(&self) -> usize {
+        self.freq
+    }
+
+    /// Sets the frequency of the `MTIME` register.
+    #[inline]
+    pub fn set_freq(&mut self, freq: usize) {
+        self.freq = freq;
+    }
+
+    /// Returns the `MTIME` register.
+    #[inline]
+    pub const fn get_mtime(&self) -> MTIME {
+        self.mtime
+    }
+}
+
+impl DelayUs for Delay {
+    #[inline]
+    fn delay_us(&mut self, us: u32) {
+        let time_from = self.mtime.read();
+        let time_to = time_from.wrapping_add(us as u64 * self.freq as u64 / 1_000_000);
+
+        while time_to < self.mtime.read() {} // wait for overflow
+        while time_to > self.mtime.read() {} // wait for time to pass
+    }
+}

+ 5 - 0
src/hal_async.rs

@@ -0,0 +1,5 @@
+//! async trait implementations for embedded-hal
+
+pub use embedded_hal_async::*; // re-export embedded-hal-async to allow macros to use it
+
+pub mod aclint; // ACLINT and CLINT peripherals

+ 82 - 0
src/hal_async/aclint.rs

@@ -0,0 +1,82 @@
+//! Asynchronous delay implementation for the (A)CLINT peripheral.
+
+use crate::aclint::mtimer::MTIME;
+pub use crate::hal::aclint::Delay;
+pub use crate::hal_async::delay::DelayUs;
+use core::{
+    cmp::Ordering,
+    future::Future,
+    pin::Pin,
+    task::{Context, Poll},
+};
+
+enum DelayAsyncState {
+    WaitOverflow(u64),
+    Wait(u64),
+    Ready,
+}
+
+struct FSMDelay {
+    mtime: MTIME,
+    state: DelayAsyncState,
+}
+
+impl FSMDelay {
+    pub fn new(n_ticks: u64, mtime: MTIME) -> Self {
+        let t_from = mtime.read();
+        let t_to = t_from.wrapping_add(n_ticks);
+
+        let state = match t_to.cmp(&t_from) {
+            Ordering::Less => DelayAsyncState::WaitOverflow(t_to),
+            Ordering::Greater => DelayAsyncState::Wait(t_to),
+            Ordering::Equal => DelayAsyncState::Ready,
+        };
+
+        Self { mtime, state }
+    }
+}
+
+impl Future for FSMDelay {
+    type Output = ();
+
+    fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
+        match self.state {
+            DelayAsyncState::WaitOverflow(t_to) => match t_to.cmp(&self.mtime.read()) {
+                Ordering::Less => Poll::Pending,
+                Ordering::Greater => {
+                    self.state = DelayAsyncState::Wait(t_to);
+                    Poll::Pending
+                }
+                Ordering::Equal => {
+                    self.state = DelayAsyncState::Ready;
+                    Poll::Ready(())
+                }
+            },
+            DelayAsyncState::Wait(t_to) => {
+                if self.mtime.read() < t_to {
+                    Poll::Pending
+                } else {
+                    self.state = DelayAsyncState::Ready;
+                    Poll::Ready(())
+                }
+            }
+            DelayAsyncState::Ready => Poll::Ready(()),
+        }
+    }
+}
+
+impl DelayUs for Delay {
+    #[inline]
+    async fn delay_us(&mut self, us: u32) {
+        let n_ticks = us as u64 * self.get_freq() as u64 / 1_000_000;
+        let state = FSMDelay::new(n_ticks, self.get_mtime());
+        state.await;
+    }
+
+    #[inline]
+    async fn delay_ms(&mut self, ms: u32) {
+        let n_ticks = ms as u64 * self.get_freq() as u64 / 1_000;
+        let state = FSMDelay::new(n_ticks, self.get_mtime());
+        state.await;
+    }
+}

+ 3 - 0
src/lib.rs

@@ -6,6 +6,9 @@
 pub use riscv; // re-export riscv crate to allow macros to use it
 
 pub mod common; // common definitions for all peripherals
+pub mod hal; // trait implementations for embedded-hal
+#[cfg(feature = "hal-async")]
+pub mod hal_async; // async trait implementations for embedded-hal
 pub mod macros; // macros for easing the definition of peripherals in PACs
 
 pub mod aclint; // ACLINT and CLINT peripherals

+ 55 - 4
src/macros.rs

@@ -3,10 +3,12 @@
 /// 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:
+/// This macro expects 4 different argument types:
 ///
 /// - Base address (**MANDATORY**): base address of the CLINT peripheral of the target.
+/// - Frequency (**OPTIONAL**): clock frequency (in Hz) of the `MTIME` register. It enables the `delay` method of the `CLINT` struct.
 /// - Per-HART mtimecmp registers (**OPTIONAL**): a list of `mtimecmp` registers for easing access to per-HART mtimecmp regs.
+/// - Per-HART msip registers (**OPTIONAL**): a list of `msip` registers for easing access to per-HART msip regs.
 ///
 /// Check the examples below for more details about the usage and syntax of this macro.
 ///
@@ -17,10 +19,11 @@
 /// ```
 /// use riscv_peripheral::clint_codegen;
 ///
-/// clint_codegen!(base 0x0200_0000,); // do not forget the ending comma!
+/// clint_codegen!(base 0x0200_0000, freq 32_768,); // do not forget the ending comma!
 ///
 /// let mswi = CLINT::mswi(); // MSWI peripheral
 /// let mtimer = CLINT::mtimer(); // MTIMER peripheral
+/// let delay = CLINT::delay(); // For the `embedded_hal::delay::DelayUs` and `embedded_hal_async::delay::DelayUs` traits
 /// ```
 ///
 /// ## Base address and per-HART mtimecmp registers
@@ -49,7 +52,8 @@
 ///
 /// clint_codegen!(
 ///     base 0x0200_0000,
-///     mtimecmps [mtimecmp0 = (HartId::H0, "`H0`"), mtimecmp1 = (HartId::H1, "`H1`"), mtimecmp2 = (HartId::H2, "`H2`")], // do not forget the ending comma!
+///     mtimecmps [mtimecmp0 = (HartId::H0, "`H0`"), mtimecmp1 = (HartId::H1, "`H1`"), mtimecmp2 = (HartId::H2, "`H2`")],
+///     msips [msip0=(HartId::H0,"`H0`"), msip1=(HartId::H1,"`H1`"), msip2=(HartId::H2,"`H2`")], // do not forget the ending comma!
 /// );
 ///
 /// let mswi = CLINT::mswi(); // MSWI peripheral
@@ -58,6 +62,10 @@
 /// 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
+///
+/// let msip0 = CLINT::msip0(); // msip register for HART 0
+/// let msip1 = CLINT::msip1(); // msip register for HART 1
+/// let msip2 = CLINT::msip2(); // msip register for HART 2
 /// ```
 #[macro_export]
 macro_rules! clint_codegen {
@@ -67,6 +75,7 @@ macro_rules! clint_codegen {
     };
     (base $addr:literal, $($tail:tt)*) => {
         /// CLINT peripheral
+        #[allow(clippy::upper_case_acronyms)]
         #[derive(Clone, Copy, Debug, Eq, PartialEq)]
         pub struct CLINT;
 
@@ -175,13 +184,54 @@ macro_rules! clint_codegen {
             pub const fn mtimer() -> $crate::aclint::mtimer::MTIMER {
                 $crate::aclint::CLINT::<CLINT>::mtimer()
             }
+
+            /// Returns the `MTIME` register of the `MTIMER` peripheral.
+            #[inline]
+            pub const fn mtime() -> $crate::aclint::mtimer::MTIME {
+                Self::mtimer().mtime
+            }
+        }
+        $crate::clint_codegen!($($tail)*);
+    };
+    (freq $freq:literal, $($tail:tt)*) => {
+        impl CLINT {
+            /// Returns the frequency of the `MTIME` register.
+            #[inline]
+            pub const fn freq() -> usize {
+                $freq
+            }
+
+            /// Delay implementation for CLINT peripherals.
+            ///
+            /// # Note
+            ///
+            /// You must export the `riscv_peripheral::hal::delay::DelayUs` trait in order to use delay methods.
+            /// You must export the `riscv_peripheral::hal_async::delay::DelayUs` trait in order to use async delay methods.
+            #[inline]
+            pub const fn delay() -> $crate::hal::aclint::Delay {
+                $crate::hal::aclint::Delay::new(Self::mtime(), Self::freq())
+            }
+        }
+        $crate::clint_codegen!($($tail)*);
+    };
+    (msips [$($fn:ident = ($hart:expr , $shart:expr)),+], $($tail:tt)*) => {
+        impl CLINT {
+            $(
+                #[doc = "Returns the `msip` register for HART "]
+                #[doc = $shart]
+                #[doc = "."]
+                #[inline]
+                pub fn $fn() -> $crate::aclint::mswi::MSIP {
+                    Self::mswi().msip($hart)
+                }
+            )*
         }
         $crate::clint_codegen!($($tail)*);
     };
     (mtimecmps [$($fn:ident = ($hart:expr , $shart:expr)),+], $($tail:tt)*) => {
         impl CLINT {
             $(
-                #[doc = "Returns the `mtimecmp` peripheral HART "]
+                #[doc = "Returns the `mtimecmp` register for HART "]
                 #[doc = $shart]
                 #[doc = "."]
                 #[inline]
@@ -203,6 +253,7 @@ macro_rules! plic_codegen {
     };
     (base $addr:literal, $($tail:tt)*) => {
         /// PLIC peripheral
+        #[allow(clippy::upper_case_acronyms)]
         #[derive(Clone, Copy, Debug, Eq, PartialEq)]
         pub struct PLIC;
 

+ 8 - 23
src/plic.rs

@@ -361,38 +361,23 @@ pub(crate) mod test {
 
         for i in 0..=Context::MAX_CONTEXT_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 as usize * 0x80
-            );
+            assert_eq!(ctx.enables().address(), 0x0C00_0000 + 0x2000 + i * 0x80);
             assert_eq!(
                 ctx.threshold().get_ptr() as usize,
-                0x0C00_0000 + 0x20_0000 + i as usize * 0x1000
+                0x0C00_0000 + 0x20_0000 + i * 0x1000
             );
             assert_eq!(
                 ctx.claim().get_ptr() as usize,
-                0x0C00_0000 + 0x20_0004 + i as usize * 0x1000
+                0x0C00_0000 + 0x20_0004 + i * 0x1000
             );
         }
 
-        let ctx0 = PLIC::ctx0();
-        let ctx_0_ = PLIC::ctx(Context::C0);
-        assert_eq!(ctx0.enables().address(), ctx_0_.enables().address());
-        assert_eq!(ctx0.threshold().get_ptr(), ctx_0_.threshold().get_ptr());
-        assert_eq!(ctx0.claim().get_ptr(), ctx_0_.claim().get_ptr());
-
-        let ctx1 = PLIC::ctx1();
-        let ctx_1_ = PLIC::ctx(Context::C1);
-        assert_eq!(ctx1.enables().address(), ctx_1_.enables().address());
-        assert_eq!(ctx1.threshold().get_ptr(), ctx_1_.threshold().get_ptr());
-        assert_eq!(ctx1.claim().get_ptr(), ctx_1_.claim().get_ptr());
-
-        let ctx2 = PLIC::ctx2();
-        let ctx_2_ = PLIC::ctx(Context::C2);
-        assert_eq!(ctx2.enables().address(), ctx_2_.enables().address());
-        assert_eq!(ctx2.threshold().get_ptr(), ctx_2_.threshold().get_ptr());
-        assert_eq!(ctx2.claim().get_ptr(), ctx_2_.claim().get_ptr());
+        assert_eq!(PLIC::ctx0(), PLIC::ctx(Context::C0));
+        assert_eq!(PLIC::ctx1(), PLIC::ctx(Context::C1));
+        assert_eq!(PLIC::ctx2(), PLIC::ctx(Context::C2));
     }
 }