capsules_system/
virtual_scheduler_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 2025.
4
5//! Implementation of [`SchedulerTimer`] trait on top of a virtual alarm.
6
7use core::num::NonZeroU32;
8use kernel::hil::time::{self, Frequency, Ticks};
9use kernel::platform::scheduler_timer::SchedulerTimer;
10
11/// Implementation of [`SchedulerTimer`] trait on top of a virtual alarm.
12///
13/// Currently, this implementation depends slightly on the virtual alarm
14/// implementation in capsules -- namely it assumes that get_alarm will still
15/// return the passed value even after the timer is disarmed. Thus this should
16/// only be implemented with a virtual alarm. If a dedicated hardware timer is
17/// available, it is more performant to implement the scheduler timer directly
18/// for that hardware peripheral without the alarm abstraction in between.
19///
20/// This mostly handles conversions from wall time, the required inputs to the
21/// trait, to ticks, which are used to track time for alarms.
22pub struct VirtualSchedulerTimer<A: 'static + time::Alarm<'static>> {
23    alarm: &'static A,
24}
25
26impl<A: 'static + time::Alarm<'static>> VirtualSchedulerTimer<A> {
27    pub fn new(alarm: &'static A) -> Self {
28        Self { alarm }
29    }
30}
31
32impl<A: 'static + time::Alarm<'static>> SchedulerTimer for VirtualSchedulerTimer<A> {
33    fn reset(&self) {
34        let _ = self.alarm.disarm();
35    }
36
37    fn start(&self, us: NonZeroU32) {
38        let tics = {
39            // We need to convert from microseconds to native tics, which could overflow in 32-bit
40            // arithmetic. So we convert to 64-bit. 64-bit division is an expensive subroutine, but
41            // if `us` is a power of 10 the compiler will simplify it with the 1_000_000 divisor
42            // instead.
43            let us = us.get() as u64;
44            let hertz = A::Frequency::frequency() as u64;
45
46            (hertz * us / 1_000_000) as u32
47        };
48
49        let reference = self.alarm.now();
50        self.alarm.set_alarm(reference, A::Ticks::from(tics));
51    }
52
53    fn arm(&self) {
54        //self.alarm.arm();
55    }
56
57    fn disarm(&self) {
58        //self.alarm.disarm();
59    }
60
61    fn get_remaining_us(&self) -> Option<NonZeroU32> {
62        // We need to convert from native tics to us, multiplication could overflow in 32-bit
63        // arithmetic. So we convert to 64-bit.
64
65        let diff = self
66            .alarm
67            .get_alarm()
68            .wrapping_sub(self.alarm.now())
69            .into_u32() as u64;
70
71        // If next alarm is more than one second away from now, alarm must have expired.
72        // Use this formulation to protect against errors when now has passed alarm.
73        // 1 second was chosen because it is significantly greater than the 400ms max value allowed
74        // by start(), and requires no computational overhead (e.g. using 500ms would require
75        // dividing the returned ticks by 2)
76        // However, if the alarm frequency is slow enough relative to the cpu frequency, it is
77        // possible this will be evaluated while now() == get_alarm(), so we special case that
78        // result where the alarm has fired but the subtraction has not overflowed
79        if diff >= A::Frequency::frequency() as u64 {
80            None
81        } else {
82            let hertz = A::Frequency::frequency() as u64;
83            NonZeroU32::new(((diff * 1_000_000) / hertz) as u32)
84        }
85    }
86}