John Park | 33858a3 | 2018-09-28 23:05:48 -0700 | [diff] [blame] | 1 | #include "aos/time/time.h" |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 2 | |
Brian Silverman | d057569 | 2015-02-21 16:24:02 -0500 | [diff] [blame] | 3 | #include <inttypes.h> |
Austin Schuh | f2a50ba | 2016-12-24 16:16:26 -0800 | [diff] [blame] | 4 | #include <string.h> |
| 5 | |
| 6 | #include <atomic> |
| 7 | #include <chrono> |
Brian Silverman | d057569 | 2015-02-21 16:24:02 -0500 | [diff] [blame] | 8 | |
Brian Silverman | c03a30c | 2019-02-16 18:21:56 -0800 | [diff] [blame^] | 9 | #ifdef __linux__ |
| 10 | |
Brian Silverman | d057569 | 2015-02-21 16:24:02 -0500 | [diff] [blame] | 11 | // We only use global_core from here, which is weak, so we don't really have a |
| 12 | // dependency on it. |
John Park | 398c74a | 2018-10-20 21:17:39 -0700 | [diff] [blame] | 13 | #include "aos/ipc_lib/shared_mem.h" |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 14 | |
John Park | 33858a3 | 2018-09-28 23:05:48 -0700 | [diff] [blame] | 15 | #include "aos/logging/logging.h" |
| 16 | #include "aos/mutex/mutex.h" |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 17 | |
Brian Silverman | c03a30c | 2019-02-16 18:21:56 -0800 | [diff] [blame^] | 18 | #else // __linux__ |
| 19 | |
| 20 | #include "motors/core/kinetis.h" |
| 21 | |
| 22 | // The systick interrupt increments this every 1ms. |
| 23 | extern "C" volatile uint32_t systick_millis_count; |
| 24 | |
| 25 | #endif // __linux__ |
| 26 | |
Austin Schuh | f2a50ba | 2016-12-24 16:16:26 -0800 | [diff] [blame] | 27 | namespace chrono = ::std::chrono; |
| 28 | |
Brian Silverman | c03a30c | 2019-02-16 18:21:56 -0800 | [diff] [blame^] | 29 | #ifdef __linux__ |
| 30 | |
Austin Schuh | 793d6b9 | 2016-05-01 13:28:14 -0700 | [diff] [blame] | 31 | namespace std { |
| 32 | namespace this_thread { |
| 33 | template <> |
| 34 | void sleep_until(const ::aos::monotonic_clock::time_point &end_time) { |
| 35 | struct timespec end_time_timespec; |
| 36 | ::std::chrono::seconds sec = |
| 37 | ::std::chrono::duration_cast<::std::chrono::seconds>( |
| 38 | end_time.time_since_epoch()); |
| 39 | ::std::chrono::nanoseconds nsec = |
| 40 | ::std::chrono::duration_cast<::std::chrono::nanoseconds>( |
| 41 | end_time.time_since_epoch() - sec); |
| 42 | end_time_timespec.tv_sec = sec.count(); |
| 43 | end_time_timespec.tv_nsec = nsec.count(); |
| 44 | int returnval; |
| 45 | do { |
| 46 | returnval = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, |
| 47 | &end_time_timespec, nullptr); |
| 48 | if (returnval != EINTR && returnval != 0) { |
| 49 | PLOG(FATAL, "clock_nanosleep(%jd, TIMER_ABSTIME, %p, nullptr) failed", |
| 50 | static_cast<uintmax_t>(CLOCK_MONOTONIC), &end_time_timespec); |
| 51 | } |
| 52 | } while (returnval != 0); |
| 53 | } |
| 54 | |
| 55 | } // namespace this_thread |
| 56 | } // namespace std |
| 57 | |
Brian Silverman | c03a30c | 2019-02-16 18:21:56 -0800 | [diff] [blame^] | 58 | #endif // __linux__ |
Austin Schuh | 793d6b9 | 2016-05-01 13:28:14 -0700 | [diff] [blame] | 59 | |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 60 | namespace aos { |
| 61 | namespace time { |
| 62 | |
Brian Silverman | c03a30c | 2019-02-16 18:21:56 -0800 | [diff] [blame^] | 63 | #ifdef __linux__ |
| 64 | |
Austin Schuh | d78ab54 | 2013-03-01 22:22:19 -0800 | [diff] [blame] | 65 | // State required to enable and use mock time. |
| 66 | namespace { |
| 67 | // True if mock time is enabled. |
| 68 | // This does not need to be checked with the mutex held because setting time to |
| 69 | // be enabled or disabled is atomic, and all future operations are atomic |
| 70 | // anyways. If there is a race condition setting or clearing whether time is |
| 71 | // enabled or not, it will still be a race condition if current_mock_time is |
| 72 | // also set atomically with enabled. |
Brian Silverman | 0308f16 | 2016-01-02 13:32:49 -0800 | [diff] [blame] | 73 | ::std::atomic<bool> mock_time_enabled{false}; |
Austin Schuh | d78ab54 | 2013-03-01 22:22:19 -0800 | [diff] [blame] | 74 | // Mutex to make time reads and writes thread safe. |
| 75 | Mutex time_mutex; |
| 76 | // Current time when time is mocked. |
Austin Schuh | 6a6f90c | 2016-11-25 21:36:42 -0800 | [diff] [blame] | 77 | monotonic_clock::time_point current_mock_time = monotonic_clock::epoch(); |
Austin Schuh | d78ab54 | 2013-03-01 22:22:19 -0800 | [diff] [blame] | 78 | |
Brian Silverman | b407c67 | 2014-04-09 11:58:37 -0700 | [diff] [blame] | 79 | } // namespace |
| 80 | |
Austin Schuh | 6a6f90c | 2016-11-25 21:36:42 -0800 | [diff] [blame] | 81 | void EnableMockTime(monotonic_clock::time_point now) { |
Austin Schuh | d78ab54 | 2013-03-01 22:22:19 -0800 | [diff] [blame] | 82 | MutexLocker time_mutex_locker(&time_mutex); |
Brian Silverman | b407c67 | 2014-04-09 11:58:37 -0700 | [diff] [blame] | 83 | mock_time_enabled = true; |
Austin Schuh | d78ab54 | 2013-03-01 22:22:19 -0800 | [diff] [blame] | 84 | current_mock_time = now; |
| 85 | } |
| 86 | |
Austin Schuh | 6a6f90c | 2016-11-25 21:36:42 -0800 | [diff] [blame] | 87 | void UpdateMockTime() { SetMockTime(monotonic_clock::now()); } |
Brian Silverman | b407c67 | 2014-04-09 11:58:37 -0700 | [diff] [blame] | 88 | |
Austin Schuh | 6a6f90c | 2016-11-25 21:36:42 -0800 | [diff] [blame] | 89 | void DisableMockTime() { |
Austin Schuh | d78ab54 | 2013-03-01 22:22:19 -0800 | [diff] [blame] | 90 | MutexLocker time_mutex_locker(&time_mutex); |
| 91 | mock_time_enabled = false; |
| 92 | } |
| 93 | |
Austin Schuh | 6a6f90c | 2016-11-25 21:36:42 -0800 | [diff] [blame] | 94 | void SetMockTime(monotonic_clock::time_point now) { |
Austin Schuh | d78ab54 | 2013-03-01 22:22:19 -0800 | [diff] [blame] | 95 | MutexLocker time_mutex_locker(&time_mutex); |
Brian Silverman | b407c67 | 2014-04-09 11:58:37 -0700 | [diff] [blame] | 96 | if (__builtin_expect(!mock_time_enabled, 0)) { |
Austin Schuh | d78ab54 | 2013-03-01 22:22:19 -0800 | [diff] [blame] | 97 | LOG(FATAL, "Tried to set mock time and mock time is not enabled\n"); |
| 98 | } |
| 99 | current_mock_time = now; |
| 100 | } |
| 101 | |
Austin Schuh | 6a6f90c | 2016-11-25 21:36:42 -0800 | [diff] [blame] | 102 | void IncrementMockTime(monotonic_clock::duration amount) { |
Brian Silverman | 6659bc3 | 2013-10-16 10:31:32 -0700 | [diff] [blame] | 103 | static ::aos::Mutex mutex; |
| 104 | ::aos::MutexLocker sync(&mutex); |
Austin Schuh | 6a6f90c | 2016-11-25 21:36:42 -0800 | [diff] [blame] | 105 | SetMockTime(monotonic_clock::now() + amount); |
Brian Silverman | 6659bc3 | 2013-10-16 10:31:32 -0700 | [diff] [blame] | 106 | } |
| 107 | |
Austin Schuh | f2a50ba | 2016-12-24 16:16:26 -0800 | [diff] [blame] | 108 | void OffsetToNow(monotonic_clock::time_point now) { |
Brian Silverman | 0c715e6 | 2015-10-24 16:49:46 -0400 | [diff] [blame] | 109 | CHECK_NOTNULL(&global_core); |
Brian Silverman | d057569 | 2015-02-21 16:24:02 -0500 | [diff] [blame] | 110 | CHECK_NOTNULL(global_core); |
Brian Silverman | 0c715e6 | 2015-10-24 16:49:46 -0400 | [diff] [blame] | 111 | CHECK_NOTNULL(global_core->mem_struct); |
Austin Schuh | f2a50ba | 2016-12-24 16:16:26 -0800 | [diff] [blame] | 112 | const auto offset = now - monotonic_clock::now(); |
| 113 | global_core->mem_struct->time_offset = |
| 114 | chrono::duration_cast<chrono::nanoseconds>(offset).count(); |
Brian Silverman | d057569 | 2015-02-21 16:24:02 -0500 | [diff] [blame] | 115 | } |
| 116 | |
Brian Silverman | c03a30c | 2019-02-16 18:21:56 -0800 | [diff] [blame^] | 117 | #endif // __linux__ |
| 118 | |
Neil Balch | 229001a | 2018-01-07 18:22:52 -0800 | [diff] [blame] | 119 | struct timespec to_timespec( |
| 120 | const ::aos::monotonic_clock::duration duration) { |
| 121 | struct timespec time_timespec; |
| 122 | ::std::chrono::seconds sec = |
| 123 | ::std::chrono::duration_cast<::std::chrono::seconds>(duration); |
| 124 | ::std::chrono::nanoseconds nsec = |
| 125 | ::std::chrono::duration_cast<::std::chrono::nanoseconds>(duration - sec); |
| 126 | time_timespec.tv_sec = sec.count(); |
| 127 | time_timespec.tv_nsec = nsec.count(); |
| 128 | return time_timespec; |
| 129 | } |
| 130 | |
| 131 | struct timespec to_timespec( |
| 132 | const ::aos::monotonic_clock::time_point time) { |
| 133 | return to_timespec(time.time_since_epoch()); |
| 134 | } |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 135 | } // namespace time |
Austin Schuh | 858c021 | 2016-11-25 17:23:30 -0800 | [diff] [blame] | 136 | |
| 137 | constexpr monotonic_clock::time_point monotonic_clock::min_time; |
| 138 | |
| 139 | monotonic_clock::time_point monotonic_clock::now() noexcept { |
Brian Silverman | c03a30c | 2019-02-16 18:21:56 -0800 | [diff] [blame^] | 140 | #ifdef __linux__ |
| 141 | |
| 142 | if (time::mock_time_enabled.load(::std::memory_order_relaxed)) { |
| 143 | MutexLocker time_mutex_locker(&time::time_mutex); |
| 144 | return time::current_mock_time; |
Austin Schuh | 858c021 | 2016-11-25 17:23:30 -0800 | [diff] [blame] | 145 | } |
| 146 | |
| 147 | struct timespec current_time; |
| 148 | if (clock_gettime(CLOCK_MONOTONIC, ¤t_time) != 0) { |
| 149 | PLOG(FATAL, "clock_gettime(%jd, %p) failed", |
| 150 | static_cast<uintmax_t>(CLOCK_MONOTONIC), ¤t_time); |
| 151 | } |
Austin Schuh | f2a50ba | 2016-12-24 16:16:26 -0800 | [diff] [blame] | 152 | const chrono::nanoseconds offset = |
| 153 | (&global_core == nullptr || global_core == nullptr || |
| 154 | global_core->mem_struct == nullptr) |
| 155 | ? chrono::nanoseconds(0) |
| 156 | : chrono::nanoseconds(global_core->mem_struct->time_offset); |
| 157 | |
Austin Schuh | 858c021 | 2016-11-25 17:23:30 -0800 | [diff] [blame] | 158 | return time_point(::std::chrono::seconds(current_time.tv_sec) + |
Austin Schuh | f2a50ba | 2016-12-24 16:16:26 -0800 | [diff] [blame] | 159 | ::std::chrono::nanoseconds(current_time.tv_nsec)) + offset; |
Brian Silverman | c03a30c | 2019-02-16 18:21:56 -0800 | [diff] [blame^] | 160 | |
| 161 | #else // __linux__ |
| 162 | |
| 163 | __disable_irq(); |
| 164 | const uint32_t current_counter = SYST_CVR; |
| 165 | uint32_t ms_count = systick_millis_count; |
| 166 | const uint32_t istatus = SCB_ICSR; |
| 167 | __enable_irq(); |
| 168 | // If the interrupt is pending and the timer has already wrapped from 0 back |
| 169 | // up to its max, then add another ms. |
| 170 | if ((istatus & SCB_ICSR_PENDSTSET) && current_counter > 50) { |
| 171 | ++ms_count; |
| 172 | } |
| 173 | |
| 174 | // It counts down, but everything we care about counts up. |
| 175 | const uint32_t counter_up = ((F_CPU / 1000) - 1) - current_counter; |
| 176 | |
| 177 | // "3.2.1.2 System Tick Timer" in the TRM says "The System Tick Timer's clock |
| 178 | // source is always the core clock, FCLK". |
| 179 | using systick_duration = |
| 180 | std::chrono::duration<uint32_t, std::ratio<1, F_CPU>>; |
| 181 | |
| 182 | return time_point(aos::time::round<std::chrono::nanoseconds>( |
| 183 | std::chrono::milliseconds(ms_count) + systick_duration(counter_up))); |
| 184 | |
| 185 | #endif // __linux__ |
Austin Schuh | 858c021 | 2016-11-25 17:23:30 -0800 | [diff] [blame] | 186 | } |
| 187 | |
| 188 | |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 189 | } // namespace aos |