blob: c7e2fa92d106496a5bb027626cdb241e281a5433 [file] [log] [blame]
Austin Schuh812d0d12021-11-04 20:16:48 -07001// Copyright (c) FIRST and other WPILib contributors.
2// Open Source Software; you can modify and/or share it under the terms of
3// the WPILib BSD license file in the root directory of this project.
Brian Silverman8fce7482020-01-05 13:18:21 -08004
5#include "wpi/timestamp.h"
6
7#include <atomic>
8
James Kuszmaulb13e13f2023-11-22 20:44:04 -08009#ifdef __FRC_ROBORIO__
10#include <stdint.h>
11#pragma GCC diagnostic push
12#pragma GCC diagnostic ignored "-Wpedantic"
13#pragma GCC diagnostic ignored "-Wignored-qualifiers"
14#include <FRC_FPGA_ChipObject/RoboRIO_FRC_ChipObject_Aliases.h>
15#include <FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/nInterfaceGlobals.h>
16#include <FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tHMB.h>
17#include <FRC_NetworkCommunication/LoadOut.h>
18#pragma GCC diagnostic pop
19namespace fpga {
20using namespace nFPGA;
21using namespace nRoboRIO_FPGANamespace;
22} // namespace fpga
23#include <memory>
24
25#include "dlfcn.h"
26#endif
27
Brian Silverman8fce7482020-01-05 13:18:21 -080028#ifdef _WIN32
29#include <windows.h>
30
31#include <cassert>
32#include <exception>
33#else
34#include <chrono>
35#endif
36
James Kuszmaulb13e13f2023-11-22 20:44:04 -080037#include <cstdio>
38
39#include <fmt/format.h>
40
41#ifdef __FRC_ROBORIO__
42namespace {
43static constexpr const char hmbName[] = "HMB_0_RAM";
44static constexpr int timestampLowerOffset = 0xF0;
45static constexpr int timestampUpperOffset = 0xF1;
46static constexpr int hmbTimestampOffset = 5; // 5 us offset
47using NiFpga_CloseHmbFunc = NiFpga_Status (*)(const NiFpga_Session session,
48 const char* memoryName);
49using NiFpga_OpenHmbFunc = NiFpga_Status (*)(const NiFpga_Session session,
50 const char* memoryName,
51 size_t* memorySize,
52 void** virtualAddress);
53struct HMBHolder {
54 ~HMBHolder() {
55 if (hmb) {
56 closeHmb(hmb->getSystemInterface()->getHandle(), hmbName);
57 dlclose(niFpga);
58 }
59 }
60 explicit operator bool() const { return hmb != nullptr; }
61 void Configure() {
62 nFPGA::nRoboRIO_FPGANamespace::g_currentTargetClass =
63 nLoadOut::getTargetClass();
64 int32_t status = 0;
65 hmb.reset(fpga::tHMB::create(&status));
66 niFpga = dlopen("libNiFpga.so", RTLD_LAZY);
67 if (!niFpga) {
68 hmb = nullptr;
69 return;
70 }
71 NiFpga_OpenHmbFunc openHmb = reinterpret_cast<NiFpga_OpenHmbFunc>(
72 dlsym(niFpga, "NiFpgaDll_OpenHmb"));
73 closeHmb = reinterpret_cast<NiFpga_CloseHmbFunc>(
74 dlsym(niFpga, "NiFpgaDll_CloseHmb"));
75 if (openHmb == nullptr || closeHmb == nullptr) {
76 closeHmb = nullptr;
77 dlclose(niFpga);
78 hmb = nullptr;
79 return;
80 }
81 size_t hmbBufferSize = 0;
82 status =
83 openHmb(hmb->getSystemInterface()->getHandle(), hmbName, &hmbBufferSize,
84 reinterpret_cast<void**>(const_cast<uint32_t**>(&hmbBuffer)));
85 if (status != 0) {
86 closeHmb = nullptr;
87 dlclose(niFpga);
88 hmb = nullptr;
89 return;
90 }
91 auto cfg = hmb->readConfig(&status);
92 cfg.Enables_Timestamp = 1;
93 hmb->writeConfig(cfg, &status);
94 }
95 void Reset() {
96 if (hmb) {
97 std::unique_ptr<fpga::tHMB> oldHmb;
98 oldHmb.swap(hmb);
99 closeHmb(oldHmb->getSystemInterface()->getHandle(), hmbName);
100 closeHmb = nullptr;
101 hmbBuffer = nullptr;
102 oldHmb.reset();
103 dlclose(niFpga);
104 niFpga = nullptr;
105 }
106 }
107 std::unique_ptr<fpga::tHMB> hmb;
108 void* niFpga = nullptr;
109 NiFpga_CloseHmbFunc closeHmb = nullptr;
110 volatile uint32_t* hmbBuffer = nullptr;
111};
112static HMBHolder hmb;
113} // namespace
114#endif
115
Brian Silverman8fce7482020-01-05 13:18:21 -0800116// offset in microseconds
Austin Schuh75263e32022-02-22 18:05:32 -0800117static uint64_t time_since_epoch() noexcept {
Brian Silverman8fce7482020-01-05 13:18:21 -0800118#ifdef _WIN32
119 FILETIME ft;
120 uint64_t tmpres = 0;
121 // 100-nanosecond intervals since January 1, 1601 (UTC)
122 // which means 0.1 us
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800123 GetSystemTimePreciseAsFileTime(&ft);
Brian Silverman8fce7482020-01-05 13:18:21 -0800124 tmpres |= ft.dwHighDateTime;
125 tmpres <<= 32;
126 tmpres |= ft.dwLowDateTime;
127 tmpres /= 10u; // convert to us
128 // January 1st, 1970 - January 1st, 1601 UTC ~ 369 years
129 // or 11644473600000000 us
130 static const uint64_t deltaepoch = 11644473600000000ull;
131 tmpres -= deltaepoch;
132 return tmpres;
133#else
134 // 1-us intervals
135 return std::chrono::duration_cast<std::chrono::microseconds>(
Austin Schuh75263e32022-02-22 18:05:32 -0800136 std::chrono::system_clock::now().time_since_epoch())
Brian Silverman8fce7482020-01-05 13:18:21 -0800137 .count();
138#endif
139}
140
Austin Schuh812d0d12021-11-04 20:16:48 -0700141static uint64_t timestamp() noexcept {
Brian Silverman8fce7482020-01-05 13:18:21 -0800142#ifdef _WIN32
143 LARGE_INTEGER li;
144 QueryPerformanceCounter(&li);
145 // there is an imprecision with the initial value,
146 // but what matters is that timestamps are monotonic and consistent
147 return static_cast<uint64_t>(li.QuadPart);
148#else
149 // 1-us intervals
150 return std::chrono::duration_cast<std::chrono::microseconds>(
151 std::chrono::steady_clock::now().time_since_epoch())
152 .count();
153#endif
154}
155
156#ifdef _WIN32
157static uint64_t update_frequency() {
158 LARGE_INTEGER li;
159 if (!QueryPerformanceFrequency(&li) || !li.QuadPart) {
160 // log something
161 std::terminate();
162 }
163 return static_cast<uint64_t>(li.QuadPart);
164}
165#endif
166
Austin Schuh75263e32022-02-22 18:05:32 -0800167static const uint64_t zerotime_val = time_since_epoch();
Brian Silverman8fce7482020-01-05 13:18:21 -0800168static const uint64_t offset_val = timestamp();
169#ifdef _WIN32
170static const uint64_t frequency_val = update_frequency();
171#endif
172
173uint64_t wpi::NowDefault() {
174#ifdef _WIN32
175 assert(offset_val > 0u);
176 assert(frequency_val > 0u);
177 uint64_t delta = timestamp() - offset_val;
178 // because the frequency is in update per seconds, we have to multiply the
179 // delta by 1,000,000
180 uint64_t delta_in_us = delta * 1000000ull / frequency_val;
181 return delta_in_us + zerotime_val;
182#else
183 return zerotime_val + timestamp() - offset_val;
184#endif
185}
186
187static std::atomic<uint64_t (*)()> now_impl{wpi::NowDefault};
188
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800189void wpi::impl::SetupNowRio() {
190#ifdef __FRC_ROBORIO__
191 if (!hmb) {
192 hmb.Configure();
193 }
194#endif
195}
196
197void wpi::impl::ShutdownNowRio() {
198#ifdef __FRC_ROBORIO__
199 hmb.Reset();
200#endif
201}
202
Brian Silverman8fce7482020-01-05 13:18:21 -0800203void wpi::SetNowImpl(uint64_t (*func)(void)) {
204 now_impl = func ? func : NowDefault;
205}
206
Austin Schuh812d0d12021-11-04 20:16:48 -0700207uint64_t wpi::Now() {
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800208#ifdef __FRC_ROBORIO__
209 // Same code as HAL_GetFPGATime()
210 if (!hmb) {
211 std::fputs(
212 "FPGA not yet configured in wpi::Now(). Time will not be correct",
213 stderr);
214 std::fflush(stderr);
215 return 0;
216 }
217
218 asm("dmb");
219 uint64_t upper1 = hmb.hmbBuffer[timestampUpperOffset];
220 asm("dmb");
221 uint32_t lower = hmb.hmbBuffer[timestampLowerOffset];
222 asm("dmb");
223 uint64_t upper2 = hmb.hmbBuffer[timestampUpperOffset];
224
225 if (upper1 != upper2) {
226 // Rolled over between the lower call, reread lower
227 asm("dmb");
228 lower = hmb.hmbBuffer[timestampLowerOffset];
229 }
230 // 5 is added here because the time to write from the FPGA
231 // to the HMB buffer is longer then the time to read
232 // from the time register. This would cause register based
233 // timestamps to be ahead of HMB timestamps, which could
234 // be very bad.
235 return (upper2 << 32) + lower + hmbTimestampOffset;
236#else
Austin Schuh812d0d12021-11-04 20:16:48 -0700237 return (now_impl.load())();
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800238#endif
Austin Schuh812d0d12021-11-04 20:16:48 -0700239}
Brian Silverman8fce7482020-01-05 13:18:21 -0800240
Austin Schuh75263e32022-02-22 18:05:32 -0800241uint64_t wpi::GetSystemTime() {
242 return time_since_epoch();
243}
244
Brian Silverman8fce7482020-01-05 13:18:21 -0800245extern "C" {
246
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800247void WPI_Impl_SetupNowRio(void) {
248 return wpi::impl::SetupNowRio();
249}
250
251void WPI_Impl_ShutdownNowRio(void) {
252 return wpi::impl::ShutdownNowRio();
253}
254
Austin Schuh812d0d12021-11-04 20:16:48 -0700255uint64_t WPI_NowDefault(void) {
256 return wpi::NowDefault();
257}
Brian Silverman8fce7482020-01-05 13:18:21 -0800258
Austin Schuh812d0d12021-11-04 20:16:48 -0700259void WPI_SetNowImpl(uint64_t (*func)(void)) {
260 wpi::SetNowImpl(func);
261}
Brian Silverman8fce7482020-01-05 13:18:21 -0800262
Austin Schuh812d0d12021-11-04 20:16:48 -0700263uint64_t WPI_Now(void) {
264 return wpi::Now();
265}
Brian Silverman8fce7482020-01-05 13:18:21 -0800266
Austin Schuh75263e32022-02-22 18:05:32 -0800267uint64_t WPI_GetSystemTime(void) {
268 return wpi::GetSystemTime();
269}
270
Brian Silverman8fce7482020-01-05 13:18:21 -0800271} // extern "C"