cortexm/
systick.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//! ARM Cortex-M SysTick peripheral.
6
7use core::cell::Cell;
8use kernel::utilities::registers::interfaces::{Readable, Writeable};
9use kernel::utilities::registers::{register_bitfields, FieldValue, ReadOnly, ReadWrite};
10use kernel::utilities::StaticRef;
11
12use core::num::NonZeroU32;
13
14/// The `SysTickFrequencyCapability` allows the holder to change the Cortex M
15/// SysTick `hertz` field.
16pub unsafe trait SysTickFrequencyCapability {}
17
18#[repr(C)]
19struct SystickRegisters {
20    syst_csr: ReadWrite<u32, ControlAndStatus::Register>,
21    syst_rvr: ReadWrite<u32, ReloadValue::Register>,
22    syst_cvr: ReadWrite<u32, CurrentValue::Register>,
23    syst_calib: ReadOnly<u32, CalibrationValue::Register>,
24}
25
26register_bitfields![u32,
27    ControlAndStatus [
28        /// Returns 1 if timer counted to 0 since last time this was read.
29        COUNTFLAG 16,
30
31        /// Clock source is (0) External Clock or (1) Processor Clock.
32        CLKSOURCE 2,
33
34        /// Set to 1 to enable SysTick exception request.
35        TICKINT 1,
36
37        /// Enable the counter (1 == Enabled).
38        ENABLE 0
39    ],
40
41    ReloadValue [
42        /// Value loaded to `syst_csr` when counter is enabled and reaches 0.
43        RELOAD          OFFSET(0)  NUMBITS(24)
44    ],
45
46    CurrentValue [
47        /// Reads current value. Write of any value sets to 0.
48        CURRENT         OFFSET(0)  NUMBITS(24)
49    ],
50
51    CalibrationValue [
52        /// 0 if device provides reference clock to processor.
53        NOREF           OFFSET(31) NUMBITS(1),
54
55        /// 0 if TENMS value is exact, 1 if inexact or not given.
56        SKEW            OFFSET(30) NUMBITS(1),
57
58        /// Reload value for 10ms ticks, or 0 if no calibration.
59        TENMS           OFFSET(0)  NUMBITS(24)
60    ]
61];
62
63/// The ARM Cortex-M SysTick peripheral
64///
65/// Documented in the Cortex-MX Devices Generic User Guide, Chapter 4.4
66pub struct SysTick {
67    hertz: Cell<u32>,
68    external_clock: bool,
69}
70
71const BASE_ADDR: *const SystickRegisters = 0xE000E010 as *const SystickRegisters;
72const SYSTICK_BASE: StaticRef<SystickRegisters> = unsafe { StaticRef::new(BASE_ADDR) };
73
74impl SysTick {
75    /// Initialize the `SysTick` with default values
76    ///
77    /// Use this constructor if the core implementation has a pre-calibration
78    /// value in hardware.
79    pub unsafe fn new() -> SysTick {
80        SysTick {
81            hertz: Cell::new(0),
82            external_clock: false,
83        }
84    }
85
86    /// Initialize the `SysTick` with an explicit clock speed
87    ///
88    /// Use this constructor if the core implementation does not have a
89    /// pre-calibration value.
90    ///
91    ///   * `clock_speed` - the frequency of SysTick tics in Hertz. For example,
92    ///   if the SysTick is driven by the CPU clock, it is simply the CPU speed.
93    pub unsafe fn new_with_calibration(clock_speed: u32) -> SysTick {
94        let res = SysTick::new();
95        res.hertz.set(clock_speed);
96        res
97    }
98
99    /// Initialize the `SysTick` with an explicit clock speed and external source
100    ///
101    /// Use this constructor if the core implementation does not have a
102    /// pre-calibration value and you need an external clock source for
103    /// the Systick.
104    ///
105    ///   * `clock_speed` - the frequency of SysTick tics in Hertz. For example,
106    ///   if the SysTick is driven by the CPU clock, it is simply the CPU speed.
107    pub unsafe fn new_with_calibration_and_external_clock(clock_speed: u32) -> SysTick {
108        let mut res = SysTick::new();
109        res.hertz.set(clock_speed);
110        res.external_clock = true;
111        res
112    }
113
114    // Return the tic frequency in hertz. If the value is configured by the
115    // user using the `new_with_calibration` constructor return `self.hertz`.
116    // Otherwise, compute the frequncy using the calibration value that is set
117    // in hardware.
118    fn hertz(&self) -> u32 {
119        let hz = self.hertz.get();
120        if hz != 0 {
121            hz
122        } else {
123            // The `tenms` register is the reload value for 10ms, so
124            // Hertz = number of tics in 1 second = tenms * 100
125            let tenms = SYSTICK_BASE.syst_calib.read(CalibrationValue::TENMS);
126            tenms * 100
127        }
128    }
129
130    /// Modifies the locally stored frequncy
131    ///
132    /// # Important
133    ///
134    /// This function does not change the actual systick frequency.
135    /// This function must be called only while the clock is not armed.
136    /// When changing the hardware systick frequency, the reload value register
137    /// should be updated and the current value register should be reset, in
138    /// order for the tick count to match the current frequency.
139    pub fn set_hertz(&self, clock_speed: u32, _capability: &dyn SysTickFrequencyCapability) {
140        self.hertz.set(clock_speed);
141    }
142}
143
144impl kernel::platform::scheduler_timer::SchedulerTimer for SysTick {
145    fn start(&self, us: NonZeroU32) {
146        let reload = {
147            // We need to convert from microseconds to native tics, which could overflow in 32-bit
148            // arithmetic. So we convert to 64-bit. 64-bit division is an expensive subroutine, but
149            // if `us` is a power of 10 the compiler will simplify it with the 1_000_000 divisor
150            // instead.
151            let us = us.get() as u64;
152            let hertz = self.hertz() as u64;
153
154            hertz * us / 1_000_000
155        };
156        let clock_source: FieldValue<u32, self::ControlAndStatus::Register> = if self.external_clock
157        {
158            // CLKSOURCE 0 --> external clock
159            ControlAndStatus::CLKSOURCE::CLEAR
160        } else {
161            // CLKSOURCE 1 --> internal clock
162            ControlAndStatus::CLKSOURCE::SET
163        };
164
165        // n.b.: 4.4.5 'hints and tips' suggests setting reload before value
166        SYSTICK_BASE
167            .syst_rvr
168            .write(ReloadValue::RELOAD.val(reload as u32));
169        SYSTICK_BASE.syst_cvr.set(0);
170
171        // OK, arm it
172        // We really just need to set the TICKINT bit here, but can't use modify() because
173        // readying the CSR register will throw away evidence of expiration if one
174        // occurred, so we re-write entire value instead.
175        SYSTICK_BASE
176            .syst_csr
177            .write(ControlAndStatus::TICKINT::SET + ControlAndStatus::ENABLE::SET + clock_source);
178    }
179
180    fn reset(&self) {
181        SYSTICK_BASE.syst_csr.set(0);
182        SYSTICK_BASE.syst_rvr.set(0);
183        SYSTICK_BASE.syst_cvr.set(0);
184    }
185
186    fn arm(&self) {
187        let clock_source: FieldValue<u32, self::ControlAndStatus::Register> = if self.external_clock
188        {
189            // CLKSOURCE 0 --> external clock
190            ControlAndStatus::CLKSOURCE::CLEAR
191        } else {
192            // CLKSOURCE 1 --> internal clock
193            ControlAndStatus::CLKSOURCE::SET
194        };
195
196        // We really just need to set the TICKINT bit here, but can't use modify() because
197        // readying the CSR register will throw away evidence of expiration if one
198        // occurred, so we re-write entire value instead.
199        SYSTICK_BASE
200            .syst_csr
201            .write(ControlAndStatus::TICKINT::SET + ControlAndStatus::ENABLE::SET + clock_source);
202    }
203
204    fn disarm(&self) {
205        let clock_source: FieldValue<u32, self::ControlAndStatus::Register> = if self.external_clock
206        {
207            // CLKSOURCE 0 --> external clock
208            ControlAndStatus::CLKSOURCE::CLEAR
209        } else {
210            // CLKSOURCE 1 --> internal clock
211            ControlAndStatus::CLKSOURCE::SET
212        };
213
214        // We really just need to set the TICKINT bit here, but can't use modify() because
215        // readying the CSR register will throw away evidence of expiration if one
216        // occurred, so we re-write entire value instead.
217        SYSTICK_BASE
218            .syst_csr
219            .write(ControlAndStatus::TICKINT::CLEAR + ControlAndStatus::ENABLE::SET + clock_source);
220    }
221
222    fn get_remaining_us(&self) -> Option<NonZeroU32> {
223        // use u64 in case of overflow when multiplying by 1,000,000
224        let tics = SYSTICK_BASE.syst_cvr.read(CurrentValue::CURRENT) as u64;
225        if SYSTICK_BASE.syst_csr.is_set(ControlAndStatus::COUNTFLAG) {
226            None
227        } else {
228            let hertz = self.hertz() as u64;
229            NonZeroU32::new(((tics * 1_000_000) / hertz) as u32)
230        }
231    }
232}