rv32i/
machine_timer.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! RISC-V Generic Machine Timer
6
7use kernel::hil::time::{Ticks, Ticks64};
8use kernel::utilities::registers::interfaces::{Readable, Writeable};
9use kernel::utilities::registers::ReadWrite;
10use kernel::ErrorCode;
11
12pub struct MachineTimer<'a> {
13    compare_low: &'a ReadWrite<u32>,
14    compare_high: &'a ReadWrite<u32>,
15    value_low: &'a ReadWrite<u32>,
16    value_high: &'a ReadWrite<u32>,
17}
18
19impl<'a> MachineTimer<'a> {
20    pub const fn new(
21        compare_low: &'a ReadWrite<u32>,
22        compare_high: &'a ReadWrite<u32>,
23        value_low: &'a ReadWrite<u32>,
24        value_high: &'a ReadWrite<u32>,
25    ) -> Self {
26        MachineTimer {
27            compare_low,
28            compare_high,
29            value_low,
30            value_high,
31        }
32    }
33
34    pub fn disable_machine_timer(&self) {
35        self.compare_high.set(0xFFFF_FFFF);
36        self.compare_low.set(0xFFFF_FFFF);
37    }
38
39    pub fn now(&self) -> Ticks64 {
40        let first_low: u32 = self.value_low.get();
41        let mut high: u32 = self.value_high.get();
42        let second_low: u32 = self.value_low.get();
43
44        if second_low < first_low {
45            // Wraparound
46            high = self.value_high.get();
47        }
48
49        Ticks64::from(((high as u64) << 32) | second_low as u64)
50    }
51
52    pub fn set_alarm(&self, reference: Ticks64, dt: Ticks64) {
53        // This does not handle the 64-bit wraparound case.
54        // Because mtimer fires if the counter is >= the compare,
55        // handling wraparound requires setting compare to the
56        // maximum value, issuing a callback on the overflow client
57        // if there is one, spinning until it wraps around to 0, then
58        // setting the compare to the correct value.
59        let regs = self;
60        let now = self.now();
61        let mut expire = reference.wrapping_add(dt);
62
63        if !now.within_range(reference, expire) {
64            expire = now;
65        }
66
67        let val = expire.into_u64();
68
69        let high = (val >> 32) as u32;
70        let low = (val & 0xffffffff) as u32;
71
72        // Recommended approach for setting the two compare registers
73        // (RISC-V Privileged Architectures 3.1.15) -pal 8/6/20
74        regs.compare_low.set(0xFFFF_FFFF);
75        regs.compare_high.set(high);
76        regs.compare_low.set(low);
77    }
78
79    pub fn get_alarm(&self) -> Ticks64 {
80        let mut val: u64 = (self.compare_high.get() as u64) << 32;
81        val |= self.compare_low.get() as u64;
82        Ticks64::from(val)
83    }
84
85    pub fn disarm(&self) -> Result<(), ErrorCode> {
86        self.disable_machine_timer();
87        Ok(())
88    }
89
90    pub fn is_armed(&self) -> bool {
91        // Check if mtimecmp is the max value. If it is, then we are not armed,
92        // otherwise we assume we have a value set.
93        self.compare_high.get() != 0xFFFF_FFFF || self.compare_low.get() != 0xFFFF_FFFF
94    }
95
96    pub fn minimum_dt(&self) -> Ticks64 {
97        Ticks64::from(1u64)
98    }
99}