1717//! [`PeriodicTimer`](super::PeriodicTimer). Using the System timer directly is
1818//! only possible through the low level [`Timer`](crate::timer::Timer) trait.
1919
20- use core:: { fmt:: Debug , marker:: PhantomData } ;
20+ use core:: { fmt:: Debug , marker:: PhantomData , num :: NonZeroU32 } ;
2121
2222use esp_sync:: RawMutex ;
2323
@@ -26,10 +26,50 @@ use crate::{
2626 asynch:: AtomicWaker ,
2727 interrupt:: { self , InterruptHandler } ,
2828 peripherals:: { Interrupt , SYSTIMER } ,
29+ soc:: clocks:: ClockTree ,
2930 system:: { Cpu , Peripheral as PeripheralEnable , PeripheralClockControl } ,
3031 time:: { Duration , Instant } ,
3132} ;
3233
34+ // System Timer is only clocked by XTAL divided by 2 or 2.5 (or RC_FAST_CLK which is not supported
35+ // yet). Some XTAL options (most, in fact) divided by this value may not be an integer multiple of
36+ // 1_000_000. Because the timer API works with microseconds, we need to correct for this. To avoid
37+ // u64 division as much as possible, we use the two highest bits of the divisor to determine the
38+ // division method.
39+ // - If these flags are 0b00, we divide by the divisor.
40+ // - If these flags are 0b01, we multiply by a constant before dividing by the divisor to improve
41+ // accuracy.
42+ // - If these flags are 0b10, we shift the timestamp by the lower bits.
43+ //
44+ // Apart from the S2, the System Timer clock divider outputs a 16MHz timer clock when using
45+ // the "default" XTAL configuration, so this method will commonly use the shifting based
46+ // division.
47+ //
48+ // On a 26MHz C2, the divider outputs 10.4MHz. On a 32MHz C3, the divider outputs 12.8MHz.
49+ //
50+ // Time is unreliable before `init_timestamp_scaler` is called.
51+ //
52+ // Because a single crate version can have "rt" enabled, `ESP_HAL_SYSTIMER_CORRECTION` needs
53+ // to be shared between versions. This aspect of this driver must be therefore kept stable.
54+ #[ unsafe( no_mangle) ]
55+ #[ cfg( feature = "rt" ) ]
56+ static mut ESP_HAL_SYSTIMER_CORRECTION : NonZeroU32 = NonZeroU32 :: new ( SHIFT_TIMESTAMP_FLAG ) . unwrap ( ) ; // Shift-by-0 = no-op
57+
58+ #[ cfg( not( feature = "rt" ) ) ]
59+ unsafe extern "Rust" {
60+ static mut ESP_HAL_SYSTIMER_CORRECTION : NonZeroU32 ;
61+ }
62+
63+ const SHIFT_TIMESTAMP_FLAG : u32 = 0x8000_0000 ;
64+ const SHIFT_MASK : u32 = 0x0000_FFFF ;
65+ // If the tick rate is not an integer number of microseconds: Since the divider is 2.5,
66+ // and we assume XTAL is an integer number of MHz, we can multiply by 5, then divide by
67+ // 5 to improve accuracy. On H2 the divider is 2, so we can multiply by 2, then divide by
68+ // 2.
69+ const UNEVEN_DIVIDER_FLAG : u32 = 0x4000_0000 ;
70+ const UNEVEN_MULTIPLIER : u32 = if cfg ! ( esp32h2) { 2 } else { 5 } ;
71+ const UNEVEN_DIVIDER_MASK : u32 = 0x0000_FFFF ;
72+
3373/// The configuration of a unit.
3474#[ derive( Copy , Clone ) ]
3575pub enum UnitConfig {
@@ -70,29 +110,107 @@ impl<'d> SystemTimer<'d> {
70110 }
71111 }
72112
113+ /// One-time initialization for the timestamp conversion/scaling.
114+ #[ cfg( feature = "rt" ) ]
115+ pub ( crate ) fn init_timestamp_scaler ( ) {
116+ // Maximum tick rate is 80MHz (S2), which fits in a u32, so let's narrow the type.
117+ let systimer_rate = Self :: ticks_per_second ( ) ;
118+
119+ // Select the optimal way to divide timestamps.
120+ let packed_rate_and_method = if systimer_rate. is_multiple_of ( 1_000_000 ) {
121+ let ticks_per_us = systimer_rate as u32 / 1_000_000 ;
122+ if ticks_per_us. is_power_of_two ( ) {
123+ // Turn the division into a shift
124+ SHIFT_TIMESTAMP_FLAG | ( ticks_per_us. ilog2 ( ) & SHIFT_MASK )
125+ } else {
126+ // We need to divide by an integer :(
127+ ticks_per_us
128+ }
129+ } else {
130+ // The rate is not a multiple of 1 MHz, we need to scale it up to prevent precision
131+ // loss.
132+ let multiplied_ticks_per_us = ( systimer_rate * UNEVEN_MULTIPLIER as u64 ) / 1_000_000 ;
133+ UNEVEN_DIVIDER_FLAG | ( multiplied_ticks_per_us as u32 )
134+ } ;
135+
136+ // Safety: we only ever write ESP_HAL_SYSTIMER_CORRECTION in `init_timestamp_scaler`, which
137+ // is called once and only once during startup, from `time_init`.
138+ unsafe {
139+ let correction_ptr = & raw mut ESP_HAL_SYSTIMER_CORRECTION ;
140+ * correction_ptr = unwrap ! ( NonZeroU32 :: new( packed_rate_and_method) ) ;
141+ }
142+ }
143+
144+ #[ inline]
145+ pub ( crate ) fn ticks_to_us ( ticks : u64 ) -> u64 {
146+ // Safety: we only ever write ESP_HAL_SYSTIMER_CORRECTION in `init_timestamp_scaler`, which
147+ // is called once and only once during startup.
148+ let correction = unsafe { ESP_HAL_SYSTIMER_CORRECTION } ;
149+
150+ let correction = correction. get ( ) ;
151+ match correction & ( SHIFT_TIMESTAMP_FLAG | UNEVEN_DIVIDER_FLAG ) {
152+ v if v == SHIFT_TIMESTAMP_FLAG => ticks >> ( correction & SHIFT_MASK ) ,
153+ v if v == UNEVEN_DIVIDER_FLAG => {
154+ // Not only that, but we need to multiply the timestamp first otherwise
155+ // we'd count slower than the timer.
156+ let multiplied = if UNEVEN_MULTIPLIER . is_power_of_two ( ) {
157+ ticks << UNEVEN_MULTIPLIER . ilog2 ( )
158+ } else {
159+ ticks * UNEVEN_MULTIPLIER as u64
160+ } ;
161+
162+ let divider = correction & UNEVEN_DIVIDER_MASK ;
163+ multiplied / divider as u64
164+ }
165+ _ => ticks / correction as u64 ,
166+ }
167+ }
168+
169+ #[ inline]
170+ fn us_to_ticks ( us : u64 ) -> u64 {
171+ // Safety: we only ever write ESP_HAL_SYSTIMER_CORRECTION in `init_timestamp_scaler`, which
172+ // is called once and only once during startup.
173+ let correction = unsafe { ESP_HAL_SYSTIMER_CORRECTION } ;
174+
175+ let correction = correction. get ( ) ;
176+ match correction & ( SHIFT_TIMESTAMP_FLAG | UNEVEN_DIVIDER_FLAG ) {
177+ v if v == SHIFT_TIMESTAMP_FLAG => us << ( correction & SHIFT_MASK ) ,
178+ v if v == UNEVEN_DIVIDER_FLAG => {
179+ let multiplier = correction & UNEVEN_DIVIDER_MASK ;
180+ let multiplied = us * multiplier as u64 ;
181+
182+ // Not only that, but we need to divide the timestamp first otherwise
183+ // we'd return a slightly too-big value.
184+ if UNEVEN_MULTIPLIER . is_power_of_two ( ) {
185+ multiplied >> UNEVEN_MULTIPLIER . ilog2 ( )
186+ } else {
187+ multiplied / UNEVEN_MULTIPLIER as u64
188+ }
189+ }
190+ _ => us * correction as u64 ,
191+ }
192+ }
193+
73194 /// Returns the tick frequency of the underlying timer unit.
74195 #[ inline]
75196 pub fn ticks_per_second ( ) -> u64 {
76- cfg_if:: cfg_if! {
77- if #[ cfg( esp32s2) ] {
78- const MULTIPLIER : u32 = 2 ;
79- const DIVIDER : u32 = 1 ;
80- } else if #[ cfg( esp32h2) ] {
81- // The counters and comparators are driven using `XTAL_CLK`.
82- // The average clock frequency is fXTAL_CLK/2, which is 16 MHz.
83- // The timer counting is incremented by 1/16 μs on each `CNT_CLK` cycle.
84- const MULTIPLIER : u32 = 1 ;
85- const DIVIDER : u32 = 2 ;
86- } else {
87- // The counters and comparators are driven using `XTAL_CLK`.
88- // The average clock frequency is fXTAL_CLK/2.5, which is 16 MHz.
89- // The timer counting is incremented by 1/16 μs on each `CNT_CLK` cycle.
90- const MULTIPLIER : u32 = 4 ;
91- const DIVIDER : u32 = 10 ;
197+ // FIXME: this requires a critical section. We can probably do better, if we can formulate
198+ // invariants well.
199+ ClockTree :: with ( |clocks| {
200+ cfg_if:: cfg_if! {
201+ if #[ cfg( esp32s2) ] {
202+ crate :: soc:: clocks:: apb_clk_frequency( clocks) as u64
203+ } else if #[ cfg( esp32h2) ] {
204+ // The counters and comparators are driven using `XTAL_CLK`.
205+ // The average clock frequency is fXTAL_CLK/2, (16 MHz for XTAL = 32 MHz)
206+ ( crate :: soc:: clocks:: xtal_clk_frequency( clocks) / 2 ) as u64
207+ } else {
208+ // The counters and comparators are driven using `XTAL_CLK`.
209+ // The average clock frequency is fXTAL_CLK/2.5 (16 MHz for XTAL = 40 MHz)
210+ ( crate :: soc:: clocks:: xtal_clk_frequency( clocks) * 10 / 25 ) as u64
211+ }
92212 }
93- }
94- let xtal_freq_mhz = crate :: clock:: Clocks :: xtal_freq ( ) . as_hz ( ) ;
95- ( ( xtal_freq_mhz * MULTIPLIER ) / DIVIDER ) as u64
213+ } )
96214 }
97215
98216 /// Create a new instance.
@@ -519,7 +637,7 @@ impl super::Timer for Alarm<'_> {
519637
520638 let ticks = self . unit . read_count ( ) ;
521639
522- let us = ticks / ( SystemTimer :: ticks_per_second ( ) / 1_000_000 ) ;
640+ let us = SystemTimer :: ticks_to_us ( ticks ) ;
523641
524642 Instant :: from_ticks ( us)
525643 }
@@ -528,7 +646,7 @@ impl super::Timer for Alarm<'_> {
528646 let mode = self . mode ( ) ;
529647
530648 let us = value. as_micros ( ) ;
531- let ticks = us * ( SystemTimer :: ticks_per_second ( ) / 1_000_000 ) ;
649+ let ticks = SystemTimer :: us_to_ticks ( us ) ;
532650
533651 if matches ! ( mode, ComparatorMode :: Period ) {
534652 // Period mode
0 commit comments