blob: bed998d06c4c34188a268081fb50363d34e50fca [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 "hal/Notifier.h"
6
7#include <atomic>
8#include <cstdlib> // For std::atexit()
9#include <memory>
Austin Schuh1e69f942020-11-14 15:06:14 -080010#include <thread>
Brian Silverman8fce7482020-01-05 13:18:21 -080011
Austin Schuh812d0d12021-11-04 20:16:48 -070012#include <fmt/core.h>
Brian Silverman8fce7482020-01-05 13:18:21 -080013#include <wpi/condition_variable.h>
14#include <wpi/mutex.h>
15
16#include "HALInitializer.h"
Austin Schuh1e69f942020-11-14 15:06:14 -080017#include "HALInternal.h"
Brian Silverman8fce7482020-01-05 13:18:21 -080018#include "hal/ChipObject.h"
19#include "hal/Errors.h"
20#include "hal/HAL.h"
Austin Schuh812d0d12021-11-04 20:16:48 -070021#include "hal/Threads.h"
Brian Silverman8fce7482020-01-05 13:18:21 -080022#include "hal/handles/UnlimitedHandleResource.h"
James Kuszmaulcf324122023-01-14 14:07:17 -080023#include "hal/roborio/InterruptManager.h"
Brian Silverman8fce7482020-01-05 13:18:21 -080024
25using namespace hal;
26
27static constexpr int32_t kTimerInterruptNumber = 28;
28
29static wpi::mutex notifierMutex;
30static std::unique_ptr<tAlarm> notifierAlarm;
Austin Schuh1e69f942020-11-14 15:06:14 -080031static std::thread notifierThread;
Austin Schuh812d0d12021-11-04 20:16:48 -070032static HAL_Bool notifierThreadRealTime = false;
33static int32_t notifierThreadPriority = 0;
Brian Silverman8fce7482020-01-05 13:18:21 -080034static uint64_t closestTrigger{UINT64_MAX};
35
36namespace {
37
38struct Notifier {
39 uint64_t triggerTime = UINT64_MAX;
40 uint64_t triggeredTime = UINT64_MAX;
41 bool active = true;
42 wpi::mutex mutex;
43 wpi::condition_variable cond;
44};
45
46} // namespace
47
48static std::atomic_flag notifierAtexitRegistered{ATOMIC_FLAG_INIT};
49static std::atomic_int notifierRefCount{0};
Austin Schuh1e69f942020-11-14 15:06:14 -080050static std::atomic_bool notifierRunning{false};
Brian Silverman8fce7482020-01-05 13:18:21 -080051
52using namespace hal;
53
54class NotifierHandleContainer
55 : public UnlimitedHandleResource<HAL_NotifierHandle, Notifier,
56 HAL_HandleEnum::Notifier> {
57 public:
58 ~NotifierHandleContainer() {
59 ForEach([](HAL_NotifierHandle handle, Notifier* notifier) {
60 {
61 std::scoped_lock lock(notifier->mutex);
62 notifier->triggerTime = UINT64_MAX;
63 notifier->triggeredTime = 0;
64 notifier->active = false;
65 }
66 notifier->cond.notify_all(); // wake up any waiting threads
67 });
68 }
69};
70
71static NotifierHandleContainer* notifierHandles;
72
Austin Schuh1e69f942020-11-14 15:06:14 -080073static void alarmCallback() {
Brian Silverman8fce7482020-01-05 13:18:21 -080074 std::scoped_lock lock(notifierMutex);
75 int32_t status = 0;
76 uint64_t currentTime = 0;
77
78 // the hardware disables itself after each alarm
79 closestTrigger = UINT64_MAX;
80
81 // process all notifiers
82 notifierHandles->ForEach([&](HAL_NotifierHandle handle, Notifier* notifier) {
Austin Schuh812d0d12021-11-04 20:16:48 -070083 if (notifier->triggerTime == UINT64_MAX) {
84 return;
85 }
86 if (currentTime == 0) {
87 currentTime = HAL_GetFPGATime(&status);
88 }
Brian Silverman8fce7482020-01-05 13:18:21 -080089 std::unique_lock lock(notifier->mutex);
90 if (notifier->triggerTime < currentTime) {
91 notifier->triggerTime = UINT64_MAX;
92 notifier->triggeredTime = currentTime;
93 lock.unlock();
94 notifier->cond.notify_all();
95 } else if (notifier->triggerTime < closestTrigger) {
96 closestTrigger = notifier->triggerTime;
97 }
98 });
99
100 if (notifierAlarm && closestTrigger != UINT64_MAX) {
101 // Simply truncate the hardware trigger time to 32-bit.
102 notifierAlarm->writeTriggerTime(static_cast<uint32_t>(closestTrigger),
103 &status);
104 // Enable the alarm. The hardware disables itself after each alarm.
105 notifierAlarm->writeEnable(true, &status);
106 }
107}
108
Austin Schuh1e69f942020-11-14 15:06:14 -0800109static void notifierThreadMain() {
James Kuszmaulcf324122023-01-14 14:07:17 -0800110 InterruptManager& manager = InterruptManager::GetInstance();
111 NiFpga_IrqContext context = manager.GetContext();
112 uint32_t mask = 1 << kTimerInterruptNumber;
113 int32_t status = 0;
114
Austin Schuh1e69f942020-11-14 15:06:14 -0800115 while (notifierRunning) {
James Kuszmaulcf324122023-01-14 14:07:17 -0800116 status = 0;
117 auto triggeredMask =
118 manager.WaitForInterrupt(context, mask, false, 10000, &status);
Austin Schuh812d0d12021-11-04 20:16:48 -0700119 if (!notifierRunning) {
120 break;
121 }
James Kuszmaulcf324122023-01-14 14:07:17 -0800122 if (triggeredMask == 0) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700123 continue;
James Kuszmaulcf324122023-01-14 14:07:17 -0800124 }
Austin Schuh1e69f942020-11-14 15:06:14 -0800125 alarmCallback();
126 }
127}
128
Brian Silverman8fce7482020-01-05 13:18:21 -0800129static void cleanupNotifierAtExit() {
Austin Schuh1e69f942020-11-14 15:06:14 -0800130 int32_t status = 0;
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800131 if (notifierAlarm) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700132 notifierAlarm->writeEnable(false, &status);
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800133 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800134 notifierAlarm = nullptr;
Austin Schuh1e69f942020-11-14 15:06:14 -0800135 notifierRunning = false;
136 hal::ReleaseFPGAInterrupt(kTimerInterruptNumber);
Austin Schuh812d0d12021-11-04 20:16:48 -0700137 if (notifierThread.joinable()) {
138 notifierThread.join();
139 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800140}
141
Austin Schuh812d0d12021-11-04 20:16:48 -0700142namespace hal::init {
Brian Silverman8fce7482020-01-05 13:18:21 -0800143void InitializeNotifier() {
144 static NotifierHandleContainer nH;
145 notifierHandles = &nH;
146}
Austin Schuh812d0d12021-11-04 20:16:48 -0700147} // namespace hal::init
Brian Silverman8fce7482020-01-05 13:18:21 -0800148
149extern "C" {
150
151HAL_NotifierHandle HAL_InitializeNotifier(int32_t* status) {
152 hal::init::CheckInit();
Austin Schuh812d0d12021-11-04 20:16:48 -0700153 if (!notifierAtexitRegistered.test_and_set()) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800154 std::atexit(cleanupNotifierAtExit);
Austin Schuh812d0d12021-11-04 20:16:48 -0700155 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800156
157 if (notifierRefCount.fetch_add(1) == 0) {
158 std::scoped_lock lock(notifierMutex);
Austin Schuh1e69f942020-11-14 15:06:14 -0800159 notifierRunning = true;
160 notifierThread = std::thread(notifierThreadMain);
Austin Schuh812d0d12021-11-04 20:16:48 -0700161
162 auto native = notifierThread.native_handle();
163 HAL_SetThreadPriority(&native, notifierThreadRealTime,
164 notifierThreadPriority, status);
165 if (*status == HAL_THREAD_PRIORITY_ERROR) {
166 *status = 0;
167 fmt::print("{}: HAL Notifier thread\n",
168 HAL_THREAD_PRIORITY_ERROR_MESSAGE);
169 }
170 if (*status == HAL_THREAD_PRIORITY_RANGE_ERROR) {
171 *status = 0;
172 fmt::print("{}: HAL Notifier thread\n",
173 HAL_THREAD_PRIORITY_RANGE_ERROR_MESSAGE);
174 }
175
Austin Schuh1e69f942020-11-14 15:06:14 -0800176 notifierAlarm.reset(tAlarm::create(status));
Brian Silverman8fce7482020-01-05 13:18:21 -0800177 }
178
179 std::shared_ptr<Notifier> notifier = std::make_shared<Notifier>();
180 HAL_NotifierHandle handle = notifierHandles->Allocate(notifier);
181 if (handle == HAL_kInvalidHandle) {
182 *status = HAL_HANDLE_ERROR;
183 return HAL_kInvalidHandle;
184 }
185 return handle;
186}
187
Austin Schuh812d0d12021-11-04 20:16:48 -0700188HAL_Bool HAL_SetNotifierThreadPriority(HAL_Bool realTime, int32_t priority,
189 int32_t* status) {
190 std::scoped_lock lock(notifierMutex);
191 notifierThreadRealTime = realTime;
192 notifierThreadPriority = priority;
193 if (notifierThread.joinable()) {
194 auto native = notifierThread.native_handle();
195 return HAL_SetThreadPriority(&native, realTime, priority, status);
196 } else {
197 return true;
198 }
199}
200
Brian Silverman8fce7482020-01-05 13:18:21 -0800201void HAL_SetNotifierName(HAL_NotifierHandle notifierHandle, const char* name,
202 int32_t* status) {}
203
204void HAL_StopNotifier(HAL_NotifierHandle notifierHandle, int32_t* status) {
205 auto notifier = notifierHandles->Get(notifierHandle);
James Kuszmaulcf324122023-01-14 14:07:17 -0800206 if (!notifier) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700207 return;
James Kuszmaulcf324122023-01-14 14:07:17 -0800208 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800209
210 {
211 std::scoped_lock lock(notifier->mutex);
212 notifier->triggerTime = UINT64_MAX;
213 notifier->triggeredTime = 0;
214 notifier->active = false;
215 }
216 notifier->cond.notify_all(); // wake up any waiting threads
217}
218
219void HAL_CleanNotifier(HAL_NotifierHandle notifierHandle, int32_t* status) {
220 auto notifier = notifierHandles->Free(notifierHandle);
James Kuszmaulcf324122023-01-14 14:07:17 -0800221 if (!notifier) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700222 return;
James Kuszmaulcf324122023-01-14 14:07:17 -0800223 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800224
225 // Just in case HAL_StopNotifier() wasn't called...
226 {
227 std::scoped_lock lock(notifier->mutex);
228 notifier->triggerTime = UINT64_MAX;
229 notifier->triggeredTime = 0;
230 notifier->active = false;
231 }
232 notifier->cond.notify_all();
233
234 if (notifierRefCount.fetch_sub(1) == 1) {
Austin Schuh1e69f942020-11-14 15:06:14 -0800235 // if this was the last notifier, clean up alarm and thread
Brian Silverman8fce7482020-01-05 13:18:21 -0800236 // the notifier can call back into our callback, so don't hold the lock
237 // here (the atomic fetch_sub will prevent multiple parallel entries
238 // into this function)
239
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800240 if (notifierAlarm) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700241 notifierAlarm->writeEnable(false, status);
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800242 }
Austin Schuh1e69f942020-11-14 15:06:14 -0800243 notifierRunning = false;
244 hal::ReleaseFPGAInterrupt(kTimerInterruptNumber);
Austin Schuh812d0d12021-11-04 20:16:48 -0700245 if (notifierThread.joinable()) {
246 notifierThread.join();
247 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800248
Austin Schuh1e69f942020-11-14 15:06:14 -0800249 std::scoped_lock lock(notifierMutex);
250 notifierAlarm = nullptr;
251 closestTrigger = UINT64_MAX;
Brian Silverman8fce7482020-01-05 13:18:21 -0800252 }
253}
254
255void HAL_UpdateNotifierAlarm(HAL_NotifierHandle notifierHandle,
256 uint64_t triggerTime, int32_t* status) {
257 auto notifier = notifierHandles->Get(notifierHandle);
James Kuszmaulcf324122023-01-14 14:07:17 -0800258 if (!notifier) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700259 return;
James Kuszmaulcf324122023-01-14 14:07:17 -0800260 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800261
262 {
263 std::scoped_lock lock(notifier->mutex);
264 notifier->triggerTime = triggerTime;
265 notifier->triggeredTime = UINT64_MAX;
266 }
267
268 std::scoped_lock lock(notifierMutex);
269 // Update alarm time if closer than current.
270 if (triggerTime < closestTrigger) {
271 bool wasActive = (closestTrigger != UINT64_MAX);
272 closestTrigger = triggerTime;
273 // Simply truncate the hardware trigger time to 32-bit.
274 notifierAlarm->writeTriggerTime(static_cast<uint32_t>(closestTrigger),
275 status);
276 // Enable the alarm.
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800277 if (!wasActive) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700278 notifierAlarm->writeEnable(true, status);
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800279 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800280 }
281}
282
283void HAL_CancelNotifierAlarm(HAL_NotifierHandle notifierHandle,
284 int32_t* status) {
285 auto notifier = notifierHandles->Get(notifierHandle);
James Kuszmaulcf324122023-01-14 14:07:17 -0800286 if (!notifier) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700287 return;
James Kuszmaulcf324122023-01-14 14:07:17 -0800288 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800289
290 {
291 std::scoped_lock lock(notifier->mutex);
292 notifier->triggerTime = UINT64_MAX;
293 }
294}
295
296uint64_t HAL_WaitForNotifierAlarm(HAL_NotifierHandle notifierHandle,
297 int32_t* status) {
298 auto notifier = notifierHandles->Get(notifierHandle);
James Kuszmaulcf324122023-01-14 14:07:17 -0800299 if (!notifier) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700300 return 0;
James Kuszmaulcf324122023-01-14 14:07:17 -0800301 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800302 std::unique_lock lock(notifier->mutex);
303 notifier->cond.wait(lock, [&] {
304 return !notifier->active || notifier->triggeredTime != UINT64_MAX;
305 });
306 return notifier->active ? notifier->triggeredTime : 0;
307}
308
309} // extern "C"