nrf5x/
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//! Timer driver, nRF5X-family
6//!
7//! The nRF51822 timer system operates off of the high frequency clock
8//! (HFCLK) and provides three timers from the clock. Timer0 is tied
9//! to the radio through some hard-coded peripheral linkages (e.g., there
10//! are dedicated PPI connections between Timer0's compare events and
11//! radio tasks, its capture tasks and radio events).
12//!
13//! This implementation provides a full-fledged Timer interface to
14//! timers 0 and 2, and exposes Timer1 as an HIL Alarm, for a Tock
15//! timer system. It may be that the Tock timer system should be ultimately
16//! placed on top of the RTC (from the low frequency clock). It's currently
17//! implemented this way as a demonstration that it can be and because
18//! the full RTC/clock interface hasn't been finalized yet.
19//!
20//! This approach should be rewritten, such that the timer system uses
21//! the RTC from the low frequency clock (lower power) and the scheduler
22//! uses the high frequency clock.
23//!
24//! Authors
25//! --------
26//! * Philip Levis <pal@cs.stanford.edu>
27//! * Date: August 18, 2016
28
29use kernel::hil;
30use kernel::hil::time::{Alarm, Ticks, Time};
31use kernel::utilities::cells::OptionalCell;
32use kernel::utilities::registers::interfaces::{Readable, Writeable};
33use kernel::utilities::registers::{register_bitfields, ReadWrite, WriteOnly};
34use kernel::utilities::StaticRef;
35use kernel::ErrorCode;
36
37const INSTANCES: [StaticRef<TimerRegisters>; 3] = unsafe {
38    [
39        StaticRef::new(0x40008000 as *const TimerRegisters),
40        StaticRef::new(0x40009000 as *const TimerRegisters),
41        StaticRef::new(0x4000A000 as *const TimerRegisters),
42    ]
43};
44
45#[repr(C)]
46struct TimerRegisters {
47    /// Start Timer
48    tasks_start: WriteOnly<u32, Task::Register>,
49    /// Stop Timer
50    tasks_stop: WriteOnly<u32, Task::Register>,
51    /// Increment Timer (Counter mode only)
52    tasks_count: WriteOnly<u32, Task::Register>,
53    /// Clear time
54    tasks_clear: WriteOnly<u32, Task::Register>,
55    /// Shut down timer
56    tasks_shutdown: WriteOnly<u32, Task::Register>,
57    _reserved0: [u8; 44],
58    /// Capture Timer value
59    tasks_capture: [WriteOnly<u32, Task::Register>; 4],
60    _reserved1: [u8; 240],
61    /// Compare event
62    events_compare: [ReadWrite<u32, Event::Register>; 4],
63    _reserved2: [u8; 176],
64    /// Shortcut register
65    shorts: ReadWrite<u32, Shorts::Register>,
66    _reserved3: [u8; 256],
67    /// Enable interrupt
68    intenset: ReadWrite<u32, Inte::Register>,
69    /// Disable interrupt
70    intenclr: ReadWrite<u32, Inte::Register>,
71    _reserved4: [u8; 504],
72    /// Timer mode selection
73    mode: ReadWrite<u32>,
74    /// Configure the number of bits used by the TIMER
75    bitmode: ReadWrite<u32, Bitmode::Register>,
76    _reserved5: [u8; 4],
77    /// Timer prescaler register
78    prescaler: ReadWrite<u32>,
79    _reserved6: [u8; 44],
80    /// Capture/Compare
81    cc: [ReadWrite<u32, CC::Register>; 4],
82}
83
84register_bitfields![u32,
85    Shorts [
86        /// Shortcut between EVENTS_COMPARE\[0\] event and TASKS_CLEAR task
87        COMPARE0_CLEAR OFFSET(0) NUMBITS(1) [
88            /// Disable shortcut
89            DisableShortcut = 0,
90            /// Enable shortcut
91            EnableShortcut = 1
92        ],
93        /// Shortcut between EVENTS_COMPARE\[1\] event and TASKS_CLEAR task
94        COMPARE1_CLEAR OFFSET(1) NUMBITS(1) [
95            /// Disable shortcut
96            DisableShortcut = 0,
97            /// Enable shortcut
98            EnableShortcut = 1
99        ],
100        /// Shortcut between EVENTS_COMPARE\[2\] event and TASKS_CLEAR task
101        COMPARE2_CLEAR OFFSET(2) NUMBITS(1) [
102            /// Disable shortcut
103            DisableShortcut = 0,
104            /// Enable shortcut
105            EnableShortcut = 1
106        ],
107        /// Shortcut between EVENTS_COMPARE\[3\] event and TASKS_CLEAR task
108        COMPARE3_CLEAR OFFSET(3) NUMBITS(1) [
109            /// Disable shortcut
110            DisableShortcut = 0,
111            /// Enable shortcut
112            EnableShortcut = 1
113        ],
114        /// Shortcut between EVENTS_COMPARE\[4\] event and TASKS_CLEAR task
115        COMPARE4_CLEAR OFFSET(4) NUMBITS(1) [
116            /// Disable shortcut
117            DisableShortcut = 0,
118            /// Enable shortcut
119            EnableShortcut = 1
120        ],
121        /// Shortcut between EVENTS_COMPARE\[5\] event and TASKS_CLEAR task
122        COMPARE5_CLEAR OFFSET(5) NUMBITS(1) [
123            /// Disable shortcut
124            DisableShortcut = 0,
125            /// Enable shortcut
126            EnableShortcut = 1
127        ],
128        /// Shortcut between EVENTS_COMPARE\[0\] event and TASKS_STOP task
129        COMPARE0_STOP OFFSET(8) NUMBITS(1) [
130            /// Disable shortcut
131            DisableShortcut = 0,
132            /// Enable shortcut
133            EnableShortcut = 1
134        ],
135        /// Shortcut between EVENTS_COMPARE\[1\] event and TASKS_STOP task
136        COMPARE1_STOP OFFSET(9) NUMBITS(1) [
137            /// Disable shortcut
138            DisableShortcut = 0,
139            /// Enable shortcut
140            EnableShortcut = 1
141        ],
142        /// Shortcut between EVENTS_COMPARE\[2\] event and TASKS_STOP task
143        COMPARE2_STOP OFFSET(10) NUMBITS(1) [
144            /// Disable shortcut
145            DisableShortcut = 0,
146            /// Enable shortcut
147            EnableShortcut = 1
148        ],
149        /// Shortcut between EVENTS_COMPARE\[3\] event and TASKS_STOP task
150        COMPARE3_STOP OFFSET(11) NUMBITS(1) [
151            /// Disable shortcut
152            DisableShortcut = 0,
153            /// Enable shortcut
154            EnableShortcut = 1
155        ],
156        /// Shortcut between EVENTS_COMPARE\[4\] event and TASKS_STOP task
157        COMPARE4_STOP OFFSET(12) NUMBITS(1) [
158            /// Disable shortcut
159            DisableShortcut = 0,
160            /// Enable shortcut
161            EnableShortcut = 1
162        ],
163        /// Shortcut between EVENTS_COMPARE\[5\] event and TASKS_STOP task
164        COMPARE5_STOP OFFSET(13) NUMBITS(1) [
165            /// Disable shortcut
166            DisableShortcut = 0,
167            /// Enable shortcut
168            EnableShortcut = 1
169        ]
170    ],
171    Inte [
172        /// Write '1' to Enable interrupt on EVENTS_COMPARE\[0\] event
173        COMPARE0 16,
174        /// Write '1' to Enable interrupt on EVENTS_COMPARE\[1\] event
175        COMPARE1 17,
176        /// Write '1' to Enable interrupt on EVENTS_COMPARE\[2\] event
177        COMPARE2 18,
178        /// Write '1' to Enable interrupt on EVENTS_COMPARE\[3\] event
179        COMPARE3 19,
180        /// Write '1' to Enable interrupt on EVENTS_COMPARE\[4\] event
181        COMPARE4 20,
182        /// Write '1' to Enable interrupt on EVENTS_COMPARE\[5\] event
183        COMPARE5 21
184    ],
185    Bitmode [
186        /// Timer bit width
187        BITMODE OFFSET(0) NUMBITS(2) [
188            Bit16 = 0,
189            Bit08 = 1,
190            Bit24 = 2,
191            Bit32 = 3
192        ]
193    ],
194    Task [
195        ENABLE 0
196    ],
197    Event [
198        READY 0
199    ],
200    CC [
201        CC OFFSET(0) NUMBITS(32)
202    ]
203];
204
205pub enum BitmodeValue {
206    Size16Bits = 0,
207    Size8Bits = 1,
208    Size24Bits = 2,
209    Size32Bits = 3,
210}
211
212pub trait CompareClient {
213    /// Passes a bitmask of which of the 4 compares/captures fired (0x0-0xf).
214    fn compare(&self, bitmask: u8);
215}
216
217pub struct Timer {
218    registers: StaticRef<TimerRegisters>,
219    client: OptionalCell<&'static dyn CompareClient>,
220}
221
222impl Timer {
223    pub const fn new(instance: usize) -> Timer {
224        Timer {
225            registers: INSTANCES[instance],
226            client: OptionalCell::empty(),
227        }
228    }
229
230    pub fn set_client(&self, client: &'static dyn CompareClient) {
231        self.client.set(client);
232    }
233
234    /// When an interrupt occurs, check if any of the 4 compares have
235    /// created an event, and if so, add it to the bitmask of triggered
236    /// events that is passed to the client.
237
238    pub fn handle_interrupt(&self) {
239        self.client.map(|client| {
240            let mut val = 0;
241            // For each of 4 possible compare events, if it's happened,
242            // clear it and store its bit in val to pass in callback.
243            for i in 0..4 {
244                if self.registers.events_compare[i].is_set(Event::READY) {
245                    val |= 1 << i;
246                    self.registers.events_compare[i].write(Event::READY::CLEAR);
247                    // Disable corresponding interrupt
248                    let interrupt_bit = match i {
249                        0 => Inte::COMPARE0::SET,
250                        1 => Inte::COMPARE1::SET,
251                        2 => Inte::COMPARE2::SET,
252                        3 => Inte::COMPARE3::SET,
253                        4 => Inte::COMPARE4::SET,
254                        _ => Inte::COMPARE5::SET,
255                    };
256                    self.registers.intenclr.write(interrupt_bit);
257                }
258            }
259            client.compare(val as u8);
260        });
261    }
262}
263
264pub struct TimerAlarm<'a> {
265    registers: StaticRef<TimerRegisters>,
266    client: OptionalCell<&'a dyn hil::time::AlarmClient>,
267}
268
269// CC0 is used for capture
270// CC1 is used for compare/interrupts
271const CC_CAPTURE: usize = 0;
272const CC_COMPARE: usize = 1;
273
274impl<'a> TimerAlarm<'a> {
275    pub const fn new(instance: usize) -> TimerAlarm<'a> {
276        TimerAlarm {
277            registers: INSTANCES[instance],
278            client: OptionalCell::empty(),
279        }
280    }
281
282    fn clear_alarm(&self) {
283        self.registers.events_compare[CC_COMPARE].write(Event::READY::CLEAR);
284        self.registers.tasks_stop.write(Task::ENABLE::SET);
285        self.registers.tasks_clear.write(Task::ENABLE::SET);
286        self.disable_interrupts();
287    }
288
289    pub fn handle_interrupt(&self) {
290        self.clear_alarm();
291        self.client.map(|client| {
292            client.alarm();
293        });
294    }
295
296    fn enable_interrupts(&self) {
297        self.registers.intenset.write(Inte::COMPARE1::SET);
298    }
299
300    fn disable_interrupts(&self) {
301        self.registers.intenclr.write(Inte::COMPARE1::SET);
302    }
303
304    fn interrupts_enabled(&self) -> bool {
305        self.registers.intenset.is_set(Inte::COMPARE1)
306    }
307
308    fn value(&self) -> u32 {
309        self.registers.tasks_capture[CC_CAPTURE].write(Task::ENABLE::SET);
310        self.registers.cc[CC_CAPTURE].get()
311    }
312}
313
314impl Time for TimerAlarm<'_> {
315    type Frequency = hil::time::Freq16KHz;
316    // Note: we always use BITMODE::32.
317    type Ticks = hil::time::Ticks32;
318
319    fn now(&self) -> Self::Ticks {
320        Self::Ticks::from(self.value())
321    }
322}
323
324impl<'a> Alarm<'a> for TimerAlarm<'a> {
325    fn set_alarm_client(&self, client: &'a dyn hil::time::AlarmClient) {
326        self.client.set(client);
327    }
328
329    fn set_alarm(&self, reference: Self::Ticks, dt: Self::Ticks) {
330        self.disable_interrupts();
331
332        const SYNC_TICS: u32 = 2;
333        let regs = &*self.registers;
334
335        let mut expire = reference.wrapping_add(dt);
336
337        let now = self.now();
338        let earliest_possible = now.wrapping_add(Self::Ticks::from(SYNC_TICS));
339
340        if !now.within_range(reference, expire) || expire.wrapping_sub(now).into_u32() <= SYNC_TICS
341        {
342            expire = earliest_possible;
343        }
344
345        regs.bitmode.write(Bitmode::BITMODE::Bit32);
346        regs.cc[CC_COMPARE].write(CC::CC.val(expire.into_u32()));
347        regs.tasks_start.write(Task::ENABLE::SET);
348        self.enable_interrupts();
349    }
350
351    fn get_alarm(&self) -> Self::Ticks {
352        Self::Ticks::from(self.registers.cc[CC_COMPARE].read(CC::CC))
353    }
354
355    fn disarm(&self) -> Result<(), ErrorCode> {
356        self.disable_interrupts();
357        Ok(())
358    }
359
360    fn is_armed(&self) -> bool {
361        self.interrupts_enabled()
362    }
363
364    fn minimum_dt(&self) -> Self::Ticks {
365        // TODO: not tested, arbitrary value
366        Self::Ticks::from(10)
367    }
368}