riscv_peripheral/aclint/
mtimer.rs

1//! Machine-level Timer Device.
2
3pub use super::{Clint, HartIdNumber};
4use crate::common::safe_peripheral;
5use riscv::register::{mhartid, mie, mip};
6
7/// Trait for an MTIMER device.
8///
9/// # Note
10///
11/// For CLINT peripherals, this trait is automatically implemented.
12///
13/// # Safety
14///
15/// * This trait must only be implemented on a PAC of a target with an MTIMER device.
16/// * The `MTIMECMP` registers base address `MTIMECMP_BASE` must be valid for the target.
17/// * The `MTIME` registers base address `MTIME_BASE` must be valid for the target.
18/// * The `MTIME` clock frequency `MTIME_FREQ` must be valid for the target.
19pub unsafe trait Mtimer: Copy {
20    /// Base address of the MTIMECMP registers.
21    const MTIMECMP_BASE: usize;
22    /// Base address of the MTIME register.
23    const MTIME_BASE: usize;
24    /// Clock frequency of the MTIME register.
25    const MTIME_FREQ: usize;
26}
27
28// SAFETY: the offset of the MSWI peripheral is fixed in the CLINT peripheral
29unsafe impl<C: Clint> Mtimer for C {
30    const MTIMECMP_BASE: usize = C::BASE + 0x4000;
31    const MTIME_BASE: usize = C::BASE + 0xBFF8;
32    const MTIME_FREQ: usize = C::MTIME_FREQ;
33}
34
35/// MTIMER device.
36///
37/// It has a single fixed-frequency monotonic time counter ([`MTIME`])
38/// register and a time compare register ([`MTIMECMP`]) for each HART.
39#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
40pub struct MTIMER<M> {
41    _marker: core::marker::PhantomData<M>,
42}
43
44impl<M: Mtimer> MTIMER<M> {
45    /// Creates a new `MTIMER` device.
46    #[inline]
47    pub const fn new() -> Self {
48        Self {
49            _marker: core::marker::PhantomData,
50        }
51    }
52
53    /// Returns the base address of the `MTIMECMP` registers.
54    #[inline]
55    const fn mtimecmp_as_ptr(self) -> *const u64 {
56        M::MTIMECMP_BASE as *const u64
57    }
58
59    /// Returns the clock frequency of the `MTIME` register.
60    #[inline]
61    pub const fn mtime_freq(self) -> usize {
62        M::MTIME_FREQ
63    }
64
65    /// Returns `true` if a machine timer interrupt is pending.
66    #[inline]
67    pub fn is_interrupting(self) -> bool {
68        mip::read().mtimer()
69    }
70
71    /// Returns `true` if machine timer interrupts are enabled.
72    #[inline]
73    pub fn is_enabled(self) -> bool {
74        mie::read().mtimer()
75    }
76
77    /// Enables machine timer interrupts in the current HART.
78    ///
79    /// # Safety
80    ///
81    /// Enabling interrupts may break mask-based critical sections.
82    #[inline]
83    pub unsafe fn enable(self) {
84        mie::set_mtimer();
85    }
86
87    /// Disables machine timer interrupts in the current HART.
88    #[inline]
89    pub fn disable(self) {
90        // SAFETY: it is safe to disable interrupts
91        unsafe { mie::clear_mtimer() };
92    }
93
94    /// Returns the `MTIME` register.
95    #[inline]
96    pub const fn mtime(self) -> MTIME {
97        // SAFETY: valid base address
98        unsafe { MTIME::new(M::MTIME_BASE) }
99    }
100
101    /// Returns the `MTIMECMP` register for the HART which ID is `hart_id`.
102    #[inline]
103    pub fn mtimecmp<H: HartIdNumber>(self, hart_id: H) -> MTIMECMP {
104        // SAFETY: `hart_id` is valid for the target
105        unsafe { MTIMECMP::new(self.mtimecmp_as_ptr().add(hart_id.number()) as _) }
106    }
107
108    /// Returns the `MTIMECMP` register for HART 0.
109    ///
110    /// # Note
111    ///
112    /// According to the RISC-V specification, HART 0 is mandatory.
113    /// Thus, this function is specially useful in single-HART mode, where HART 0 is the only HART available.
114    /// In multi-HART mode, it is recommended to use [`MTIMER::mtimecmp`] or [`MTIMER::mtimecmp_mhartid`] instead.
115    #[inline]
116    pub const fn mtimecmp0(self) -> MTIMECMP {
117        // SAFETY: HART 0 is mandatory
118        unsafe { MTIMECMP::new(M::MTIMECMP_BASE) }
119    }
120
121    /// Returns the `MTIMECMP` register for the current HART.
122    ///
123    /// # Note
124    ///
125    /// This function determines the current HART ID by reading the [`mhartid`] CSR.
126    /// Thus, it can only be used in M-mode. For S-mode, use [`MTIMER::mtimecmp`] instead.
127    #[inline]
128    pub fn mtimecmp_mhartid(self) -> MTIMECMP {
129        let hart_id = mhartid::read();
130        // SAFETY: `hart_id` is valid for the target and is the current hart
131        unsafe { MTIMECMP::new(self.mtimecmp_as_ptr().add(hart_id) as _) }
132    }
133}
134
135// MTIMECMP register.
136safe_peripheral!(MTIMECMP, u64, RW);
137
138// MTIME register.
139safe_peripheral!(MTIME, u64, RW);