riscv_peripheral/
aclint.rs

1//! Devices for the Core Local Interruptor (CLINT) and Advanced CLINT (ACLINT) peripherals.
2//!
3//! CLINT pecification: <https://github.com/pulp-platform/clint>
4//! ACLINT Specification: <https://chromitem-soc.readthedocs.io/en/latest/clint.html>
5
6pub mod mswi;
7pub mod mtimer;
8pub mod sswi;
9
10pub use riscv_pac::HartIdNumber; // re-export useful riscv-pac traits
11
12/// Trait for a CLINT peripheral.
13///
14/// # Safety
15///
16/// * This trait must only be implemented on a PAC of a target with a CLINT peripheral.
17/// * The CLINT peripheral base address `BASE` must be valid for the target device.
18pub unsafe trait Clint: Copy {
19    /// Base address of the CLINT peripheral.
20    const BASE: usize;
21}
22
23/// Interface for a CLINT peripheral.
24///
25/// The RISC-V standard does not specify a fixed location for the CLINT.
26/// Thus, each platform must specify the base address of the CLINT on the platform.
27/// The base address, as well as all the associated types, are defined in the [`Clint`] trait.
28///
29/// The CLINT standard allows up to 4_095 different HARTs connected to the CLINT.
30/// Each HART has an assigned index starting from 0 to up to 4_094.
31/// In this way, each HART's timer and software interrupts can be independently configured.
32#[allow(clippy::upper_case_acronyms)]
33#[derive(Clone, Copy, Debug, Eq, PartialEq)]
34pub struct CLINT<C: Clint> {
35    _marker: core::marker::PhantomData<C>,
36}
37
38impl<C: Clint> CLINT<C> {
39    const MTIMECMP_OFFSET: usize = 0x4000;
40
41    const MTIME_OFFSET: usize = 0xBFF8;
42
43    /// Returns the `MSWI` peripheral.
44    #[inline]
45    pub const fn mswi() -> mswi::MSWI {
46        // SAFETY: valid base address
47        unsafe { mswi::MSWI::new(C::BASE) }
48    }
49
50    /// Returns the `MTIMER` peripheral.
51    #[inline]
52    pub const fn mtimer() -> mtimer::MTIMER {
53        // SAFETY: valid base address
54        unsafe {
55            mtimer::MTIMER::new(
56                C::BASE + Self::MTIMECMP_OFFSET,
57                C::BASE + Self::MTIME_OFFSET,
58            )
59        }
60    }
61}
62
63#[cfg(test)]
64pub(crate) mod test {
65    use super::HartIdNumber;
66    use riscv_pac::result::{Error, Result};
67
68    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
69    #[repr(usize)]
70    pub(crate) enum HartId {
71        H0 = 0,
72        H1 = 1,
73        H2 = 2,
74    }
75
76    unsafe impl HartIdNumber for HartId {
77        const MAX_HART_ID_NUMBER: usize = Self::H2 as usize;
78
79        #[inline]
80        fn number(self) -> usize {
81            self as _
82        }
83
84        #[inline]
85        fn from_number(number: usize) -> Result<Self> {
86            if number > Self::MAX_HART_ID_NUMBER {
87                Err(Error::InvalidVariant(number))
88            } else {
89                // SAFETY: valid context number
90                Ok(unsafe { core::mem::transmute::<usize, HartId>(number) })
91            }
92        }
93    }
94
95    #[test]
96    fn check_hart_id_enum() {
97        assert_eq!(HartId::H0.number(), 0);
98        assert_eq!(HartId::H1.number(), 1);
99        assert_eq!(HartId::H2.number(), 2);
100
101        assert_eq!(HartId::from_number(0), Ok(HartId::H0));
102        assert_eq!(HartId::from_number(1), Ok(HartId::H1));
103        assert_eq!(HartId::from_number(2), Ok(HartId::H2));
104
105        assert_eq!(HartId::from_number(3), Err(Error::InvalidVariant(3)));
106    }
107
108    #[allow(dead_code)]
109    #[test]
110    fn check_clint() {
111        // Call CLINT macro with a base address and a list of mtimecmps for easing access to per-HART mtimecmp regs.
112        crate::clint_codegen!(
113            base 0x0200_0000,
114            mtimecmps [mtimecmp0=(HartId::H0,"`H0`"), mtimecmp1=(HartId::H1,"`H1`"), mtimecmp2=(HartId::H2,"`H2`")],
115            msips [msip0=(HartId::H0,"`H0`"), msip1=(HartId::H1,"`H1`"), msip2=(HartId::H2,"`H2`")],
116        );
117
118        let mswi = CLINT::mswi();
119        let mtimer = CLINT::mtimer();
120
121        assert_eq!(mswi.msip0.get_ptr() as usize, 0x0200_0000);
122        assert_eq!(mtimer.mtimecmp0.get_ptr() as usize, 0x0200_4000);
123        assert_eq!(mtimer.mtime.get_ptr() as usize, 0x0200_bff8);
124
125        let mtimecmp0 = mtimer.mtimecmp(HartId::H0);
126        let mtimecmp1 = mtimer.mtimecmp(HartId::H1);
127        let mtimecmp2 = mtimer.mtimecmp(HartId::H2);
128
129        assert_eq!(mtimecmp0.get_ptr() as usize, 0x0200_4000);
130        assert_eq!(mtimecmp1.get_ptr() as usize, 0x0200_4000 + 8); // 8 bytes per register
131        assert_eq!(mtimecmp2.get_ptr() as usize, 0x0200_4000 + 2 * 8);
132
133        assert_eq!(CLINT::mtime(), mtimer.mtime);
134        assert_eq!(CLINT::mtimecmp0(), mtimer.mtimecmp(HartId::H0));
135        assert_eq!(CLINT::mtimecmp1(), mtimer.mtimecmp(HartId::H1));
136        assert_eq!(CLINT::mtimecmp2(), mtimer.mtimecmp(HartId::H2));
137
138        assert_eq!(CLINT::msip0(), mswi.msip(HartId::H0));
139        assert_eq!(CLINT::msip1(), mswi.msip(HartId::H1));
140        assert_eq!(CLINT::msip2(), mswi.msip(HartId::H2));
141    }
142}