1 // Copyright 2020 The Pigweed Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 4 // use this file except in compliance with the License. You may obtain a copy of 5 // the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations under 13 // the License. 14 #pragma once 15 16 #include <stddef.h> 17 #include <stdint.h> 18 19 #include "pw_preprocessor/util.h" 20 21 // The backend implements this header to provide the following SystemClock 22 // parameters, for more detail on the parameters see the SystemClock usage of 23 // them below: 24 // PW_CHRONO_SYSTEM_CLOCK_PERIOD_SECONDS_NUMERATOR 25 // PW_CHRONO_SYSTEM_CLOCK_PERIOD_SECONDS_DENOMINATOR 26 // constexpr pw::chrono::Epoch pw::chrono::backend::kSystemClockEpoch; 27 // constexpr bool pw::chrono::backend::kSystemClockFreeRunning; 28 // constexpr bool pw::chrono::backend::kSystemClockNmiSafe; 29 #include "pw_chrono_backend/system_clock_config.h" 30 31 #ifdef __cplusplus 32 33 #include <chrono> 34 #include <ratio> 35 36 #include "pw_chrono/virtual_clock.h" 37 38 namespace pw::chrono { 39 namespace backend { 40 41 /// The ARM AEBI does not permit the opaque 'time_point' to be passed via 42 /// registers, ergo the underlying fundamental type is forward declared. 43 /// A SystemCLock tick has the units of one SystemClock::period duration. 44 /// This must be thread and IRQ safe and provided by the backend. 45 /// 46 int64_t GetSystemClockTickCount(); 47 48 } // namespace backend 49 50 /// The `SystemClock` represents an unsteady, monotonic clock. 51 /// 52 /// The epoch of this clock is unspecified and may not be related to wall time 53 /// (for example, it can be time since boot). The time between ticks of this 54 /// clock may vary due to sleep modes and potential interrupt handling. 55 /// `SystemClock` meets the requirements of C++'s `TrivialClock` and Pigweed's 56 /// `PigweedClock.` 57 /// 58 /// `SystemClock` is compatible with C++'s `Clock` & `TrivialClock` including: 59 /// - `SystemClock::rep` 60 /// - `SystemClock::period` 61 /// - `SystemClock::duration` 62 /// - `SystemClock::time_point` 63 /// - `SystemClock::is_steady` 64 /// - `SystemClock::now()` 65 /// 66 /// Example: 67 /// 68 /// @code 69 /// SystemClock::time_point before = SystemClock::now(); 70 /// TakesALongTime(); 71 /// SystemClock::duration time_taken = SystemClock::now() - before; 72 /// bool took_way_too_long = false; 73 /// if (time_taken > std::chrono::seconds(42)) { 74 /// took_way_too_long = true; 75 /// } 76 /// @endcode 77 /// 78 /// This code is thread & IRQ safe, it may be NMI safe depending on is_nmi_safe. 79 /// 80 struct SystemClock { 81 using rep = int64_t; 82 /// The period must be provided by the backend. 83 using period = std::ratio<PW_CHRONO_SYSTEM_CLOCK_PERIOD_SECONDS_NUMERATOR, 84 PW_CHRONO_SYSTEM_CLOCK_PERIOD_SECONDS_DENOMINATOR>; 85 /// Alias for durations representable with this clock. 86 using duration = std::chrono::duration<rep, period>; 87 using time_point = std::chrono::time_point<SystemClock>; 88 /// The epoch must be provided by the backend. 89 static constexpr Epoch epoch = backend::kSystemClockEpoch; 90 91 /// The time points of this clock cannot decrease, however the time between 92 /// ticks of this clock may slightly vary due to sleep modes. The duration 93 /// during sleep may be ignored or backfilled with another clock. 94 static constexpr bool is_monotonic = true; 95 static constexpr bool is_steady = false; 96 97 /// The now() function may not move forward while in a critical section or 98 /// interrupt. This must be provided by the backend. 99 static constexpr bool is_free_running = backend::kSystemClockFreeRunning; 100 101 /// The clock must stop while in halting debug mode. 102 static constexpr bool is_stopped_in_halting_debug_mode = true; 103 104 /// The now() function can be invoked at any time. 105 static constexpr bool is_always_enabled = true; 106 107 /// The now() function may work in non-maskable interrupt contexts (e.g. 108 /// exception/fault handlers), depending on the backend. This must be provided 109 /// by the backend. 110 static constexpr bool is_nmi_safe = backend::kSystemClockNmiSafe; 111 112 /// This is thread and IRQ safe. This must be provided by the backend. nowSystemClock113 static time_point now() noexcept { 114 return time_point(duration(backend::GetSystemClockTickCount())); 115 } 116 117 /// This is purely a helper, identical to directly using std::chrono::ceil, to 118 /// convert a duration type which cannot be implicitly converted where the 119 /// result is rounded up. 120 template <class Rep, class Period> for_at_leastSystemClock121 static constexpr duration for_at_least(std::chrono::duration<Rep, Period> d) { 122 return std::chrono::ceil<duration>(d); 123 } 124 125 /// Computes the nearest time_point after the specified duration has elapsed. 126 /// 127 /// This is useful for translating delay or timeout durations into deadlines. 128 /// 129 /// The time_point is computed based on now() plus the specified duration 130 /// where a singular clock tick is added to handle partial ticks. This ensures 131 /// that a duration of at least 1 tick does not result in [0,1] ticks and 132 /// instead in [1,2] ticks. TimePointAfterAtLeastSystemClock133 static time_point TimePointAfterAtLeast(duration after_at_least) { 134 return now() + after_at_least + duration(1); 135 } 136 }; 137 138 // NOTE: VirtualClock here is specialized on SystemClock in order to provide 139 // the `RealClock` function. 140 // 141 // SystemClock is defined in this file, so there's no risk of an ODR violation 142 // as other libraries are unable to spell VirtualClock<SystemClock> unless 143 // they have first included this file. 144 145 /// An abstract interface representing a SystemClock. 146 /// 147 /// This interface allows decoupling code that uses time from the code that 148 /// creates a point in time. You can use this to your advantage by injecting 149 /// Clocks into interfaces rather than having implementations call 150 /// `SystemClock::now()` directly. However, this comes at a cost of a vtable per 151 /// implementation and more importantly passing and maintaining references to 152 /// the VirtualClock for all of the users. 153 /// 154 /// The `VirtualSystemClock::RealClock()` function returns a reference to the 155 /// real global SystemClock. 156 /// 157 /// Example: 158 /// 159 /// @code 160 /// void DoFoo(VirtualSystemClock& system_clock) { 161 /// SystemClock::time_point now = clock.now(); 162 /// // ... Code which consumes now. 163 /// } 164 /// 165 /// // Production code: 166 /// DoFoo(VirtualSystemCLock::RealClock); 167 /// 168 /// // Test code: 169 /// MockClock test_clock(); 170 /// DoFoo(test_clock); 171 /// @endcode 172 /// 173 /// This interface is thread and IRQ safe. 174 template <> 175 class VirtualClock<SystemClock> { 176 public: 177 /// Returns a reference to the real system clock to aid instantiation. 178 static VirtualClock<SystemClock>& RealClock(); 179 180 virtual ~VirtualClock() = default; 181 182 /// Returns the current time. 183 virtual SystemClock::time_point now() = 0; 184 }; 185 186 using VirtualSystemClock = VirtualClock<SystemClock>; 187 188 } // namespace pw::chrono 189 190 // The backend can opt to include an inlined implementation of the following: 191 // int64_t GetSystemClockTickCount(); 192 #if __has_include("pw_chrono_backend/system_clock_inline.h") 193 #include "pw_chrono_backend/system_clock_inline.h" 194 #endif // __has_include("pw_chrono_backend/system_clock_inline.h") 195 196 #endif // __cplusplus 197 198 PW_EXTERN_C_START 199 200 // C API Users should not create pw_chrono_SystemClock_Duration's directly, 201 // instead it is strongly recommended to use macros which express the duration 202 // in time units, instead of non-portable ticks. 203 // 204 // The following macros round up just like std::chrono::ceil, this is the 205 // recommended rounding to maintain the "at least" contract of timeouts and 206 // deadlines (note the *_CEIL macros are the same only more explicit): 207 // PW_SYSTEM_CLOCK_MS(milliseconds) 208 // PW_SYSTEM_CLOCK_S(seconds) 209 // PW_SYSTEM_CLOCK_MIN(minutes) 210 // PW_SYSTEM_CLOCK_H(hours) 211 // PW_SYSTEM_CLOCK_MS_CEIL(milliseconds) 212 // PW_SYSTEM_CLOCK_S_CEIL(seconds) 213 // PW_SYSTEM_CLOCK_MIN_CEIL(minutes) 214 // PW_SYSTEM_CLOCK_H_CEIL(hours) 215 // 216 // The following macros round down like std::chrono::{floor,duration_cast}, 217 // these are discouraged but sometimes necessary: 218 // PW_SYSTEM_CLOCK_MS_FLOOR(milliseconds) 219 // PW_SYSTEM_CLOCK_S_FLOOR(seconds) 220 // PW_SYSTEM_CLOCK_MIN_FLOOR(minutes) 221 // PW_SYSTEM_CLOCK_H_FLOOR(hours) 222 #include "pw_chrono/internal/system_clock_macros.h" 223 224 typedef struct { 225 int64_t ticks; 226 } pw_chrono_SystemClock_Duration; 227 228 typedef struct { 229 pw_chrono_SystemClock_Duration duration_since_epoch; 230 } pw_chrono_SystemClock_TimePoint; 231 typedef int64_t pw_chrono_SystemClock_Nanoseconds; 232 233 // Returns the current time, see SystemClock::now() for more detail. 234 pw_chrono_SystemClock_TimePoint pw_chrono_SystemClock_Now(void); 235 236 // Returns the change in time between the current_time - last_time. 237 pw_chrono_SystemClock_Duration pw_chrono_SystemClock_TimeElapsed( 238 pw_chrono_SystemClock_TimePoint last_time, 239 pw_chrono_SystemClock_TimePoint current_time); 240 241 // For lossless time unit conversion, the seconds per tick ratio that is 242 // numerator/denominator should be used: 243 // PW_CHRONO_SYSTEM_CLOCK_PERIOD_SECONDS_NUMERATOR 244 // PW_CHRONO_SYSTEM_CLOCK_PERIOD_SECONDS_DENOMINATOR 245 246 // Warning, this may be lossy due to the use of std::chrono::floor, 247 // rounding towards zero. 248 pw_chrono_SystemClock_Nanoseconds pw_chrono_SystemClock_DurationToNsFloor( 249 pw_chrono_SystemClock_Duration duration); 250 251 PW_EXTERN_C_END 252