blob: c811964afd6f7945e119ac5d230126fe9149370e [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>
Maxwell Henderson80bec322024-01-09 15:48:44 -08008#include <optional>
Brian Silverman8fce7482020-01-05 13:18:21 -08009
James Kuszmaulb13e13f2023-11-22 20:44:04 -080010#ifdef __FRC_ROBORIO__
11#include <stdint.h>
12#pragma GCC diagnostic push
13#pragma GCC diagnostic ignored "-Wpedantic"
14#pragma GCC diagnostic ignored "-Wignored-qualifiers"
15#include <FRC_FPGA_ChipObject/RoboRIO_FRC_ChipObject_Aliases.h>
James Kuszmaulb13e13f2023-11-22 20:44:04 -080016#include <FRC_FPGA_ChipObject/nRoboRIO_FPGANamespace/tHMB.h>
James Kuszmaulb13e13f2023-11-22 20:44:04 -080017#pragma GCC diagnostic pop
18namespace fpga {
19using namespace nFPGA;
20using namespace nRoboRIO_FPGANamespace;
21} // namespace fpga
22#include <memory>
23
24#include "dlfcn.h"
25#endif
26
Brian Silverman8fce7482020-01-05 13:18:21 -080027#ifdef _WIN32
28#include <windows.h>
29
30#include <cassert>
31#include <exception>
32#else
33#include <chrono>
34#endif
35
James Kuszmaulb13e13f2023-11-22 20:44:04 -080036#include <cstdio>
37
38#include <fmt/format.h>
39
40#ifdef __FRC_ROBORIO__
41namespace {
42static constexpr const char hmbName[] = "HMB_0_RAM";
43static constexpr int timestampLowerOffset = 0xF0;
44static constexpr int timestampUpperOffset = 0xF1;
45static constexpr int hmbTimestampOffset = 5; // 5 us offset
46using NiFpga_CloseHmbFunc = NiFpga_Status (*)(const NiFpga_Session session,
47 const char* memoryName);
48using NiFpga_OpenHmbFunc = NiFpga_Status (*)(const NiFpga_Session session,
49 const char* memoryName,
50 size_t* memorySize,
51 void** virtualAddress);
Maxwell Henderson80bec322024-01-09 15:48:44 -080052using NiFpga_FindRegisterFunc = NiFpga_Status (*)(NiFpga_Session session,
53 const char* registerName,
54 uint32_t* registerOffset);
55using NiFpga_ReadU32Func = NiFpga_Status (*)(NiFpga_Session session,
56 uint32_t indicator,
57 uint32_t* value);
58using NiFpga_WriteU32Func = NiFpga_Status (*)(NiFpga_Session session,
59 uint32_t control, uint32_t value);
60static void dlcloseWrapper(void* handle) {
61 dlclose(handle);
62}
63static std::atomic_flag hmbInitialized = ATOMIC_FLAG_INIT;
64static std::atomic_flag nowUseDefaultOnFailure = ATOMIC_FLAG_INIT;
65struct HMBLowLevel {
66 ~HMBLowLevel() { Reset(); }
67 bool Configure(const NiFpga_Session session) {
James Kuszmaulb13e13f2023-11-22 20:44:04 -080068 int32_t status = 0;
Maxwell Henderson80bec322024-01-09 15:48:44 -080069 niFpga.reset(dlopen("libNiFpga.so", RTLD_LAZY));
James Kuszmaulb13e13f2023-11-22 20:44:04 -080070 if (!niFpga) {
Maxwell Henderson80bec322024-01-09 15:48:44 -080071 fmt::print(stderr, "Could not open libNiFpga.so\n");
72 return false;
James Kuszmaulb13e13f2023-11-22 20:44:04 -080073 }
74 NiFpga_OpenHmbFunc openHmb = reinterpret_cast<NiFpga_OpenHmbFunc>(
Maxwell Henderson80bec322024-01-09 15:48:44 -080075 dlsym(niFpga.get(), "NiFpgaDll_OpenHmb"));
James Kuszmaulb13e13f2023-11-22 20:44:04 -080076 closeHmb = reinterpret_cast<NiFpga_CloseHmbFunc>(
Maxwell Henderson80bec322024-01-09 15:48:44 -080077 dlsym(niFpga.get(), "NiFpgaDll_CloseHmb"));
78 NiFpga_FindRegisterFunc findRegister =
79 reinterpret_cast<NiFpga_FindRegisterFunc>(
80 dlsym(niFpga.get(), "NiFpgaDll_FindRegister"));
81 NiFpga_ReadU32Func readU32 = reinterpret_cast<NiFpga_ReadU32Func>(
82 dlsym(niFpga.get(), "NiFpgaDll_ReadU32"));
83 NiFpga_WriteU32Func writeU32 = reinterpret_cast<NiFpga_WriteU32Func>(
84 dlsym(niFpga.get(), "NiFpgaDll_WriteU32"));
85 if (openHmb == nullptr || closeHmb == nullptr || findRegister == nullptr ||
86 writeU32 == nullptr || readU32 == nullptr) {
87 fmt::print(stderr, "Could not find HMB symbols in libNiFpga.so\n");
88 niFpga = nullptr;
89 return false;
90 }
91 uint32_t hmbConfigRegister = 0;
92 status = findRegister(session, "HMB.Config", &hmbConfigRegister);
93 if (status != 0) {
94 fmt::print(stderr, "Failed to find HMB.Config register, status code {}\n",
95 status);
James Kuszmaulb13e13f2023-11-22 20:44:04 -080096 closeHmb = nullptr;
Maxwell Henderson80bec322024-01-09 15:48:44 -080097 niFpga = nullptr;
98 return false;
James Kuszmaulb13e13f2023-11-22 20:44:04 -080099 }
100 size_t hmbBufferSize = 0;
101 status =
Maxwell Henderson80bec322024-01-09 15:48:44 -0800102 openHmb(session, hmbName, &hmbBufferSize,
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800103 reinterpret_cast<void**>(const_cast<uint32_t**>(&hmbBuffer)));
104 if (status != 0) {
Maxwell Henderson80bec322024-01-09 15:48:44 -0800105 fmt::print(stderr, "Failed to open HMB, status code {}\n", status);
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800106 closeHmb = nullptr;
Maxwell Henderson80bec322024-01-09 15:48:44 -0800107 niFpga = nullptr;
108 return false;
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800109 }
Maxwell Henderson80bec322024-01-09 15:48:44 -0800110 fpga::tHMB::tConfig cfg;
111 uint32_t read = 0;
112 status = readU32(session, hmbConfigRegister, &read);
113 cfg.value = read;
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800114 cfg.Enables_Timestamp = 1;
Maxwell Henderson80bec322024-01-09 15:48:44 -0800115 status = writeU32(session, hmbConfigRegister, cfg.value);
116 hmbSession.emplace(session);
117 hmbInitialized.test_and_set();
118 return true;
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800119 }
120 void Reset() {
Maxwell Henderson80bec322024-01-09 15:48:44 -0800121 hmbInitialized.clear();
122 std::optional<NiFpga_Session> oldSesh;
123 hmbSession.swap(oldSesh);
124 if (oldSesh.has_value()) {
125 closeHmb(oldSesh.value(), hmbName);
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800126 niFpga = nullptr;
127 }
128 }
Maxwell Henderson80bec322024-01-09 15:48:44 -0800129 std::optional<NiFpga_Session> hmbSession;
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800130 NiFpga_CloseHmbFunc closeHmb = nullptr;
131 volatile uint32_t* hmbBuffer = nullptr;
Maxwell Henderson80bec322024-01-09 15:48:44 -0800132 std::unique_ptr<void, decltype(&dlcloseWrapper)> niFpga{nullptr,
133 dlcloseWrapper};
134};
135struct HMBHolder {
136 void Configure(void* col, std::unique_ptr<fpga::tHMB> hmbObject) {
137 hmb = std::move(hmbObject);
138 chipObjectLibrary.reset(col);
139 if (!lowLevel.Configure(hmb->getSystemInterface()->getHandle())) {
140 hmb = nullptr;
141 chipObjectLibrary = nullptr;
142 }
143 }
144 void Reset() {
145 lowLevel.Reset();
146 hmb = nullptr;
147 chipObjectLibrary = nullptr;
148 }
149 HMBLowLevel lowLevel;
150 std::unique_ptr<fpga::tHMB> hmb;
151 std::unique_ptr<void, decltype(&dlcloseWrapper)> chipObjectLibrary{
152 nullptr, dlcloseWrapper};
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800153};
154static HMBHolder hmb;
155} // namespace
156#endif
157
Brian Silverman8fce7482020-01-05 13:18:21 -0800158// offset in microseconds
Austin Schuh75263e32022-02-22 18:05:32 -0800159static uint64_t time_since_epoch() noexcept {
Brian Silverman8fce7482020-01-05 13:18:21 -0800160#ifdef _WIN32
161 FILETIME ft;
162 uint64_t tmpres = 0;
163 // 100-nanosecond intervals since January 1, 1601 (UTC)
164 // which means 0.1 us
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800165 GetSystemTimePreciseAsFileTime(&ft);
Brian Silverman8fce7482020-01-05 13:18:21 -0800166 tmpres |= ft.dwHighDateTime;
167 tmpres <<= 32;
168 tmpres |= ft.dwLowDateTime;
169 tmpres /= 10u; // convert to us
170 // January 1st, 1970 - January 1st, 1601 UTC ~ 369 years
171 // or 11644473600000000 us
172 static const uint64_t deltaepoch = 11644473600000000ull;
173 tmpres -= deltaepoch;
174 return tmpres;
175#else
176 // 1-us intervals
177 return std::chrono::duration_cast<std::chrono::microseconds>(
Austin Schuh75263e32022-02-22 18:05:32 -0800178 std::chrono::system_clock::now().time_since_epoch())
Brian Silverman8fce7482020-01-05 13:18:21 -0800179 .count();
180#endif
181}
182
Austin Schuh812d0d12021-11-04 20:16:48 -0700183static uint64_t timestamp() noexcept {
Brian Silverman8fce7482020-01-05 13:18:21 -0800184#ifdef _WIN32
185 LARGE_INTEGER li;
186 QueryPerformanceCounter(&li);
187 // there is an imprecision with the initial value,
188 // but what matters is that timestamps are monotonic and consistent
189 return static_cast<uint64_t>(li.QuadPart);
190#else
191 // 1-us intervals
192 return std::chrono::duration_cast<std::chrono::microseconds>(
193 std::chrono::steady_clock::now().time_since_epoch())
194 .count();
195#endif
196}
197
198#ifdef _WIN32
199static uint64_t update_frequency() {
200 LARGE_INTEGER li;
201 if (!QueryPerformanceFrequency(&li) || !li.QuadPart) {
202 // log something
203 std::terminate();
204 }
205 return static_cast<uint64_t>(li.QuadPart);
206}
207#endif
208
Austin Schuh75263e32022-02-22 18:05:32 -0800209static const uint64_t zerotime_val = time_since_epoch();
Brian Silverman8fce7482020-01-05 13:18:21 -0800210static const uint64_t offset_val = timestamp();
211#ifdef _WIN32
212static const uint64_t frequency_val = update_frequency();
213#endif
214
215uint64_t wpi::NowDefault() {
216#ifdef _WIN32
217 assert(offset_val > 0u);
218 assert(frequency_val > 0u);
219 uint64_t delta = timestamp() - offset_val;
220 // because the frequency is in update per seconds, we have to multiply the
221 // delta by 1,000,000
222 uint64_t delta_in_us = delta * 1000000ull / frequency_val;
223 return delta_in_us + zerotime_val;
224#else
225 return zerotime_val + timestamp() - offset_val;
226#endif
227}
228
229static std::atomic<uint64_t (*)()> now_impl{wpi::NowDefault};
230
Maxwell Henderson80bec322024-01-09 15:48:44 -0800231void wpi::impl::SetupNowDefaultOnRio() {
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800232#ifdef __FRC_ROBORIO__
Maxwell Henderson80bec322024-01-09 15:48:44 -0800233 nowUseDefaultOnFailure.test_and_set();
234#endif
235}
236
237#ifdef __FRC_ROBORIO__
238template <>
239void wpi::impl::SetupNowRio(void* chipObjectLibrary,
240 std::unique_ptr<fpga::tHMB> hmbObject) {
241 if (!hmbInitialized.test()) {
242 hmb.Configure(chipObjectLibrary, std::move(hmbObject));
243 }
244}
245#endif
246
247void wpi::impl::SetupNowRio(uint32_t session) {
248#ifdef __FRC_ROBORIO__
249 if (!hmbInitialized.test()) {
250 hmb.lowLevel.Configure(session);
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800251 }
252#endif
253}
254
255void wpi::impl::ShutdownNowRio() {
256#ifdef __FRC_ROBORIO__
257 hmb.Reset();
258#endif
259}
260
Brian Silverman8fce7482020-01-05 13:18:21 -0800261void wpi::SetNowImpl(uint64_t (*func)(void)) {
262 now_impl = func ? func : NowDefault;
263}
264
Austin Schuh812d0d12021-11-04 20:16:48 -0700265uint64_t wpi::Now() {
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800266#ifdef __FRC_ROBORIO__
267 // Same code as HAL_GetFPGATime()
Maxwell Henderson80bec322024-01-09 15:48:44 -0800268 if (!hmbInitialized.test()) {
269 if (nowUseDefaultOnFailure.test()) {
270 return timestamp() - offset_val;
271 } else {
272 fmt::print(
273 stderr,
274 "FPGA not yet configured in wpi::Now(). Time will not be correct.\n");
275 std::fflush(stderr);
276 return 1;
277 }
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800278 }
279
280 asm("dmb");
Maxwell Henderson80bec322024-01-09 15:48:44 -0800281 uint64_t upper1 = hmb.lowLevel.hmbBuffer[timestampUpperOffset];
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800282 asm("dmb");
Maxwell Henderson80bec322024-01-09 15:48:44 -0800283 uint32_t lower = hmb.lowLevel.hmbBuffer[timestampLowerOffset];
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800284 asm("dmb");
Maxwell Henderson80bec322024-01-09 15:48:44 -0800285 uint64_t upper2 = hmb.lowLevel.hmbBuffer[timestampUpperOffset];
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800286
287 if (upper1 != upper2) {
288 // Rolled over between the lower call, reread lower
289 asm("dmb");
Maxwell Henderson80bec322024-01-09 15:48:44 -0800290 lower = hmb.lowLevel.hmbBuffer[timestampLowerOffset];
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800291 }
292 // 5 is added here because the time to write from the FPGA
293 // to the HMB buffer is longer then the time to read
294 // from the time register. This would cause register based
295 // timestamps to be ahead of HMB timestamps, which could
296 // be very bad.
297 return (upper2 << 32) + lower + hmbTimestampOffset;
298#else
Austin Schuh812d0d12021-11-04 20:16:48 -0700299 return (now_impl.load())();
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800300#endif
Austin Schuh812d0d12021-11-04 20:16:48 -0700301}
Brian Silverman8fce7482020-01-05 13:18:21 -0800302
Austin Schuh75263e32022-02-22 18:05:32 -0800303uint64_t wpi::GetSystemTime() {
304 return time_since_epoch();
305}
306
Brian Silverman8fce7482020-01-05 13:18:21 -0800307extern "C" {
308
Maxwell Henderson80bec322024-01-09 15:48:44 -0800309void WPI_Impl_SetupNowUseDefaultOnRio(void) {
310 return wpi::impl::SetupNowDefaultOnRio();
311}
312
313void WPI_Impl_SetupNowRioWithSession(uint32_t session) {
314 return wpi::impl::SetupNowRio(session);
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800315}
316
317void WPI_Impl_ShutdownNowRio(void) {
318 return wpi::impl::ShutdownNowRio();
319}
320
Austin Schuh812d0d12021-11-04 20:16:48 -0700321uint64_t WPI_NowDefault(void) {
322 return wpi::NowDefault();
323}
Brian Silverman8fce7482020-01-05 13:18:21 -0800324
Austin Schuh812d0d12021-11-04 20:16:48 -0700325void WPI_SetNowImpl(uint64_t (*func)(void)) {
326 wpi::SetNowImpl(func);
327}
Brian Silverman8fce7482020-01-05 13:18:21 -0800328
Austin Schuh812d0d12021-11-04 20:16:48 -0700329uint64_t WPI_Now(void) {
330 return wpi::Now();
331}
Brian Silverman8fce7482020-01-05 13:18:21 -0800332
Austin Schuh75263e32022-02-22 18:05:32 -0800333uint64_t WPI_GetSystemTime(void) {
334 return wpi::GetSystemTime();
335}
336
Brian Silverman8fce7482020-01-05 13:18:21 -0800337} // extern "C"