Skip to main content

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