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);