1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
use d1_pac::CLINT;

/// Core Local Interruptor (ClINT) interface.
///
/// The implementation in the (single) C906 core of the D1 provides timer functionalities
/// that can be accessed from machine and supervisor mode.
pub struct Clint {
    clint: CLINT,
}

impl Clint {
    /// Create a new `Clint` from the [`CLINT`] peripheral
    #[must_use]
    pub fn new(clint: CLINT) -> Self {
        Self { clint }
    }

    /// Release the underlying [`CLINT`] peripheral
    #[must_use]
    pub fn release(self) -> CLINT {
        self.clint
    }

    /// Summon the clint peripheral
    ///
    /// # Safety
    ///
    /// This is intended for use in interrupt context. Care should be taken not to have
    /// multiple instances live at the same time that may race or cause other UB issues
    #[must_use]
    pub unsafe fn summon() -> Self {
        Self {
            clint: d1_pac::Peripherals::steal().CLINT,
        }
    }

    /// Spin until `delay_us` microseconds have elapsed (as determined by
    /// [`Self::get_mtime`]).
    // TODO: should this move into the `Clint`?
    pub(crate) fn spin_delay_us(delay_us: usize) {
        let t = Self::get_mtime();
        // TODO: verify that mtime sourced directly from DXCO (24 MHz)
        while Self::get_mtime() < (t + 24 * delay_us) {
            core::hint::spin_loop()
        }
    }

    /// Get the (machine) time value.
    #[cfg(not(any(target_arch = "riscv64", target_arch = "riscv32")))]
    pub fn get_mtime() -> usize {
        unimplemented!("called `Clint::get_mtime` on a non-RISC-V architecture, this shouldn't happen while running host tests!")
    }

    /// Get the (machine) time value.
    #[cfg(any(target_arch = "riscv64", target_arch = "riscv32"))]
    pub fn get_mtime() -> usize {
        // Note that the CLINT of the C906 core does not implement
        // the `mtime` register and we need to get the time value
        // with a CSR, which the `riscv` crate implements for us.
        riscv::register::time::read()
    }

    /// Set the machine time comparator.
    ///
    /// When `mtime` >= this value, a (machine) interrupt
    /// will be generated (if configured properly).
    pub fn set_mtimecmp(&mut self, cmp: usize) {
        let cmph = (cmp >> 32) as u32;
        let cmpl = (cmp & 0xffff_ffff) as u32;
        unsafe {
            self.clint.mtimecmph.write(|w| w.bits(cmph));
            self.clint.mtimecmpl.write(|w| w.bits(cmpl));
        }
    }

    /// Reset the machine time comparator to its default value.
    pub fn reset_mtimecmp(&mut self) {
        unsafe {
            self.clint.mtimecmph.write(|w| w.bits(0xffff_ffff));
            self.clint.mtimecmpl.write(|w| w.bits(0xffff_ffff));
        }
    }
}