From 54a55b8b532b7c286eaf60f7541d1c21df65e64a Mon Sep 17 00:00:00 2001 From: Thad House Date: Fri, 8 Dec 2023 23:22:59 -0800 Subject: [PATCH] [wpiutil,hal] Update image; init Rio Now() HMB with a FPGA session (#6016) --- buildSrc/build.gradle | 2 +- hal/src/main/native/athena/HAL.cpp | 35 +++- ntcore/src/dev/native/cpp/main.cpp | 2 +- ntcore/src/test/native/cpp/main.cpp | 2 +- ntcoreffi/src/main/native/symbols.txt | 3 +- shared/config.gradle | 2 +- wpiutil/build.gradle | 11 +- wpiutil/src/main/native/cpp/timestamp.cpp | 164 ++++++++++++------ .../src/main/native/include/wpi/timestamp.h | 34 +++- 9 files changed, 177 insertions(+), 78 deletions(-) diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index 9e3a89aa8b..ed2748ed66 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -9,5 +9,5 @@ repositories { } } dependencies { - implementation "edu.wpi.first:native-utils:2024.5.1" + implementation "edu.wpi.first:native-utils:2024.5.2" } diff --git a/hal/src/main/native/athena/HAL.cpp b/hal/src/main/native/athena/HAL.cpp index d00bf9f370..f8acb3322c 100644 --- a/hal/src/main/native/athena/HAL.cpp +++ b/hal/src/main/native/athena/HAL.cpp @@ -4,6 +4,7 @@ #include "hal/HAL.h" +#include #include // linux for kill #include #include @@ -522,6 +523,35 @@ static bool killExistingProgram(int timeout, int mode) { return true; } +static void SetupNowRio(void) { + nFPGA::nRoboRIO_FPGANamespace::g_currentTargetClass = + nLoadOut::getTargetClass(); + + int32_t status = 0; + + Dl_info info; + status = dladdr(reinterpret_cast(tHMB::create), &info); + if (status == 0) { + fmt::print(stderr, "Failed to call dladdr on chipobject {}\n", dlerror()); + return; + } + + void* chipObjectLibrary = dlopen(info.dli_fname, RTLD_LAZY); + if (chipObjectLibrary == nullptr) { + fmt::print(stderr, "Failed to call dlopen on chipobject {}\n", dlerror()); + return; + } + + std::unique_ptr hmb; + hmb.reset(tHMB::create(&status)); + if (hmb == nullptr) { + fmt::print(stderr, "Failed to open HMB on chipobject {}\n", status); + dlclose(chipObjectLibrary); + return; + } + wpi::impl::SetupNowRio(chipObjectLibrary, std::move(hmb)); +} + HAL_Bool HAL_Initialize(int32_t timeout, int32_t mode) { static std::atomic_bool initialized{false}; static wpi::mutex initializeMutex; @@ -562,14 +592,13 @@ HAL_Bool HAL_Initialize(int32_t timeout, int32_t mode) { setNewDataSem(nullptr); }); - // Setup WPI_Now to use FPGA timestamp - // this also sets nFPGA::nRoboRIO_FPGANamespace::g_currentTargetClass - wpi::impl::SetupNowRio(); + SetupNowRio(); int32_t status = 0; HAL_InitializeHMB(&status); if (status != 0) { + fmt::print(stderr, "Failed to open HAL HMB, status code {}\n", status); return false; } hmbBuffer = HAL_GetHMBBuffer(); diff --git a/ntcore/src/dev/native/cpp/main.cpp b/ntcore/src/dev/native/cpp/main.cpp index 6e43fdb41e..d724bbe428 100644 --- a/ntcore/src/dev/native/cpp/main.cpp +++ b/ntcore/src/dev/native/cpp/main.cpp @@ -24,7 +24,7 @@ void bench2(); void stress(); int main(int argc, char* argv[]) { - wpi::impl::SetupNowRio(); + wpi::impl::SetupNowDefaultOnRio(); if (argc == 2 && std::string_view{argv[1]} == "bench") { bench(); diff --git a/ntcore/src/test/native/cpp/main.cpp b/ntcore/src/test/native/cpp/main.cpp index 0f060b0e92..20bd583d2d 100644 --- a/ntcore/src/test/native/cpp/main.cpp +++ b/ntcore/src/test/native/cpp/main.cpp @@ -10,7 +10,7 @@ #include "ntcore.h" int main(int argc, char** argv) { - wpi::impl::SetupNowRio(); + wpi::impl::SetupNowDefaultOnRio(); nt::AddLogger(nt::GetDefaultInstance(), 0, UINT_MAX, [](auto& event) { if (auto msg = event.GetLogMessage()) { std::fputs(msg->message.c_str(), stderr); diff --git a/ntcoreffi/src/main/native/symbols.txt b/ntcoreffi/src/main/native/symbols.txt index c18014b71c..6700936804 100644 --- a/ntcoreffi/src/main/native/symbols.txt +++ b/ntcoreffi/src/main/native/symbols.txt @@ -251,7 +251,8 @@ WPI_DestroyEvent WPI_DestroySemaphore WPI_DestroySignalObject WPI_GetSystemTime -WPI_Impl_SetupNowRio +WPI_Impl_SetupNowUseDefaultOnRio +WPI_Impl_SetupNowRioWithSession WPI_Impl_ShutdownNowRio WPI_Now WPI_NowDefault diff --git a/shared/config.gradle b/shared/config.gradle index 36aee7f5cd..202e63c497 100644 --- a/shared/config.gradle +++ b/shared/config.gradle @@ -15,7 +15,7 @@ nativeUtils { configureDependencies { opencvYear = "frc2024" googleTestYear = "frc2024" - niLibVersion = "2024.1.1" + niLibVersion = "2024.2.0" opencvVersion = "4.8.0-2" googleTestVersion = "1.14.0-1" } diff --git a/wpiutil/build.gradle b/wpiutil/build.gradle index ab13092a70..e456a19777 100644 --- a/wpiutil/build.gradle +++ b/wpiutil/build.gradle @@ -147,12 +147,6 @@ ext { } } } - - exeSplitSetup = { - if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) { - nativeUtils.useRequiredLibrary(it, 'ni_link_libraries', 'ni_runtime_libraries') - } - } } def examplesMap = [:]; @@ -252,9 +246,6 @@ model { targetBuildTypes 'debug' binaries.all { lib library: 'wpiutil', linkage: 'shared' - if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio) { - nativeUtils.useRequiredLibrary(it, 'ni_link_libraries', 'ni_runtime_libraries') - } } sources { cpp { @@ -275,7 +266,7 @@ model { if (!(it instanceof NativeBinarySpec)) return if (it.component.name != 'wpiutil' && it.component.name != 'wpiutilBase') return if (it.targetPlatform.name != nativeUtils.wpi.platforms.roborio) return - nativeUtils.useRequiredLibrary(it, 'ni_link_libraries') + nativeUtils.useRequiredLibrary(it, 'chipobject_headers') } } } diff --git a/wpiutil/src/main/native/cpp/timestamp.cpp b/wpiutil/src/main/native/cpp/timestamp.cpp index e030adc81e..acf5d9bfad 100644 --- a/wpiutil/src/main/native/cpp/timestamp.cpp +++ b/wpiutil/src/main/native/cpp/timestamp.cpp @@ -5,6 +5,7 @@ #include "wpi/timestamp.h" #include +#include #ifdef __FRC_ROBORIO__ #include @@ -12,9 +13,7 @@ #pragma GCC diagnostic ignored "-Wpedantic" #pragma GCC diagnostic ignored "-Wignored-qualifiers" #include -#include #include -#include #pragma GCC diagnostic pop namespace fpga { using namespace nFPGA; @@ -50,70 +49,107 @@ using NiFpga_OpenHmbFunc = NiFpga_Status (*)(const NiFpga_Session session, const char* memoryName, size_t* memorySize, void** virtualAddress); +using NiFpga_FindRegisterFunc = NiFpga_Status (*)(NiFpga_Session session, + const char* registerName, + uint32_t* registerOffset); +using NiFpga_ReadU32Func = NiFpga_Status (*)(NiFpga_Session session, + uint32_t indicator, + uint32_t* value); +using NiFpga_WriteU32Func = NiFpga_Status (*)(NiFpga_Session session, + uint32_t control, uint32_t value); +static void dlcloseWrapper(void* handle) { + dlclose(handle); +} static std::atomic_flag hmbInitialized = ATOMIC_FLAG_INIT; -struct HMBHolder { - ~HMBHolder() { - hmbInitialized.clear(); - if (hmb) { - closeHmb(hmb->getSystemInterface()->getHandle(), hmbName); - dlclose(niFpga); - } - } - void Configure() { - nFPGA::nRoboRIO_FPGANamespace::g_currentTargetClass = - nLoadOut::getTargetClass(); +static std::atomic_flag nowUseDefaultOnFailure = ATOMIC_FLAG_INIT; +struct HMBLowLevel { + ~HMBLowLevel() { Reset(); } + bool Configure(const NiFpga_Session session) { int32_t status = 0; - hmb.reset(fpga::tHMB::create(&status)); - niFpga = dlopen("libNiFpga.so", RTLD_LAZY); + niFpga.reset(dlopen("libNiFpga.so", RTLD_LAZY)); if (!niFpga) { fmt::print(stderr, "Could not open libNiFpga.so\n"); - hmb = nullptr; - return; + return false; } NiFpga_OpenHmbFunc openHmb = reinterpret_cast( - dlsym(niFpga, "NiFpgaDll_OpenHmb")); + dlsym(niFpga.get(), "NiFpgaDll_OpenHmb")); closeHmb = reinterpret_cast( - dlsym(niFpga, "NiFpgaDll_CloseHmb")); - if (openHmb == nullptr || closeHmb == nullptr) { + dlsym(niFpga.get(), "NiFpgaDll_CloseHmb")); + NiFpga_FindRegisterFunc findRegister = + reinterpret_cast( + dlsym(niFpga.get(), "NiFpgaDll_FindRegister")); + NiFpga_ReadU32Func readU32 = reinterpret_cast( + dlsym(niFpga.get(), "NiFpgaDll_ReadU32")); + NiFpga_WriteU32Func writeU32 = reinterpret_cast( + dlsym(niFpga.get(), "NiFpgaDll_WriteU32")); + if (openHmb == nullptr || closeHmb == nullptr || findRegister == nullptr || + writeU32 == nullptr || readU32 == nullptr) { fmt::print(stderr, "Could not find HMB symbols in libNiFpga.so\n"); + niFpga = nullptr; + return false; + } + uint32_t hmbConfigRegister = 0; + status = findRegister(session, "HMB.Config", &hmbConfigRegister); + if (status != 0) { + fmt::print(stderr, "Failed to find HMB.Config register, status code {}\n", + status); closeHmb = nullptr; - dlclose(niFpga); - hmb = nullptr; - return; + niFpga = nullptr; + return false; } size_t hmbBufferSize = 0; status = - openHmb(hmb->getSystemInterface()->getHandle(), hmbName, &hmbBufferSize, + openHmb(session, hmbName, &hmbBufferSize, reinterpret_cast(const_cast(&hmbBuffer))); if (status != 0) { fmt::print(stderr, "Failed to open HMB, status code {}\n", status); closeHmb = nullptr; - dlclose(niFpga); - hmb = nullptr; - return; + niFpga = nullptr; + return false; } - auto cfg = hmb->readConfig(&status); + fpga::tHMB::tConfig cfg; + uint32_t read = 0; + status = readU32(session, hmbConfigRegister, &read); + cfg.value = read; cfg.Enables_Timestamp = 1; - hmb->writeConfig(cfg, &status); + status = writeU32(session, hmbConfigRegister, cfg.value); + hmbSession.emplace(session); hmbInitialized.test_and_set(); + return true; } void Reset() { hmbInitialized.clear(); - if (hmb) { - std::unique_ptr oldHmb; - oldHmb.swap(hmb); - closeHmb(oldHmb->getSystemInterface()->getHandle(), hmbName); - closeHmb = nullptr; - hmbBuffer = nullptr; - oldHmb.reset(); - dlclose(niFpga); + std::optional oldSesh; + hmbSession.swap(oldSesh); + if (oldSesh.has_value()) { + closeHmb(oldSesh.value(), hmbName); niFpga = nullptr; } } - std::unique_ptr hmb; - void* niFpga = nullptr; + std::optional hmbSession; NiFpga_CloseHmbFunc closeHmb = nullptr; volatile uint32_t* hmbBuffer = nullptr; + std::unique_ptr niFpga{nullptr, + dlcloseWrapper}; +}; +struct HMBHolder { + void Configure(void* col, std::unique_ptr hmbObject) { + hmb = std::move(hmbObject); + chipObjectLibrary.reset(col); + if (!lowLevel.Configure(hmb->getSystemInterface()->getHandle())) { + hmb = nullptr; + chipObjectLibrary = nullptr; + } + } + void Reset() { + lowLevel.Reset(); + hmb = nullptr; + chipObjectLibrary = nullptr; + } + HMBLowLevel lowLevel; + std::unique_ptr hmb; + std::unique_ptr chipObjectLibrary{ + nullptr, dlcloseWrapper}; }; static HMBHolder hmb; } // namespace @@ -192,10 +228,26 @@ uint64_t wpi::NowDefault() { static std::atomic now_impl{wpi::NowDefault}; -void wpi::impl::SetupNowRio() { +void wpi::impl::SetupNowDefaultOnRio() { +#ifdef __FRC_ROBORIO__ + nowUseDefaultOnFailure.test_and_set(); +#endif +} + +#ifdef __FRC_ROBORIO__ +template <> +void wpi::impl::SetupNowRio(void* chipObjectLibrary, + std::unique_ptr hmbObject) { + if (!hmbInitialized.test()) { + hmb.Configure(chipObjectLibrary, std::move(hmbObject)); + } +} +#endif + +void wpi::impl::SetupNowRio(uint32_t session) { #ifdef __FRC_ROBORIO__ if (!hmbInitialized.test()) { - hmb.Configure(); + hmb.lowLevel.Configure(session); } #endif } @@ -214,24 +266,28 @@ uint64_t wpi::Now() { #ifdef __FRC_ROBORIO__ // Same code as HAL_GetFPGATime() if (!hmbInitialized.test()) { - fmt::print( - stderr, - "FPGA not yet configured in wpi::Now(). Time will not be correct.\n"); - std::fflush(stderr); - return 1; + if (nowUseDefaultOnFailure.test()) { + return (now_impl.load())(); + } else { + fmt::print( + stderr, + "FPGA not yet configured in wpi::Now(). Time will not be correct.\n"); + std::fflush(stderr); + return 1; + } } asm("dmb"); - uint64_t upper1 = hmb.hmbBuffer[timestampUpperOffset]; + uint64_t upper1 = hmb.lowLevel.hmbBuffer[timestampUpperOffset]; asm("dmb"); - uint32_t lower = hmb.hmbBuffer[timestampLowerOffset]; + uint32_t lower = hmb.lowLevel.hmbBuffer[timestampLowerOffset]; asm("dmb"); - uint64_t upper2 = hmb.hmbBuffer[timestampUpperOffset]; + uint64_t upper2 = hmb.lowLevel.hmbBuffer[timestampUpperOffset]; if (upper1 != upper2) { // Rolled over between the lower call, reread lower asm("dmb"); - lower = hmb.hmbBuffer[timestampLowerOffset]; + lower = hmb.lowLevel.hmbBuffer[timestampLowerOffset]; } // 5 is added here because the time to write from the FPGA // to the HMB buffer is longer then the time to read @@ -250,8 +306,12 @@ uint64_t wpi::GetSystemTime() { extern "C" { -void WPI_Impl_SetupNowRio(void) { - return wpi::impl::SetupNowRio(); +void WPI_Impl_SetupNowUseDefaultOnRio(void) { + return wpi::impl::SetupNowDefaultOnRio(); +} + +void WPI_Impl_SetupNowRioWithSession(uint32_t session) { + return wpi::impl::SetupNowRio(session); } void WPI_Impl_ShutdownNowRio(void) { diff --git a/wpiutil/src/main/native/include/wpi/timestamp.h b/wpiutil/src/main/native/include/wpi/timestamp.h index c232481eb7..4d61055d3c 100644 --- a/wpiutil/src/main/native/include/wpi/timestamp.h +++ b/wpiutil/src/main/native/include/wpi/timestamp.h @@ -8,15 +8,13 @@ #include #ifdef __cplusplus -extern "C" { +#include // NOLINT + #endif -/** - * Initialize the on-Rio Now() implementation to use the FPGA timestamp. - * No effect on non-Rio platforms. This is called by HAL_Initialize() and - * thus should generally not be called by user code. - */ -void WPI_Impl_SetupNowRio(void); +#ifdef __cplusplus +extern "C" { +#endif /** * De-initialize the on-Rio Now() implementation. No effect on non-Rio @@ -62,12 +60,32 @@ uint64_t WPI_GetSystemTime(void); namespace wpi { namespace impl { +/** + * Initialize the on-Rio Now() implementation to use the desktop timestamp. + * No effect on non-Rio platforms. This should only be used for testing + * purposes if the HAL is not available. + */ +void SetupNowDefaultOnRio(); + /** * Initialize the on-Rio Now() implementation to use the FPGA timestamp. * No effect on non-Rio platforms. This is called by HAL_Initialize() and * thus should generally not be called by user code. */ -void SetupNowRio(); +#ifdef __FRC_ROBORIO__ +template +void SetupNowRio(void* chipObjectLibrary, std::unique_ptr hmbObject); +#else +template +inline void SetupNowRio(void*, std::unique_ptr) {} +#endif + +/** + * Initialize the on-Rio Now() implementation to use the FPGA timestamp. + * No effect on non-Rio platforms. This take an FPGA session that has + * already been initialized, and is used from LabVIEW. + */ +void SetupNowRio(uint32_t session); /** * De-initialize the on-Rio Now() implementation. No effect on non-Rio