blob: 9c818d858eda0399f9b54c65caecb5be269ad5ac [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;
Austin Schuh812d0d12021-11-04 20:16:48 -0700131 if (notifierAlarm)
132 notifierAlarm->writeEnable(false, &status);
Brian Silverman8fce7482020-01-05 13:18:21 -0800133 notifierAlarm = nullptr;
Austin Schuh1e69f942020-11-14 15:06:14 -0800134 notifierRunning = false;
135 hal::ReleaseFPGAInterrupt(kTimerInterruptNumber);
Austin Schuh812d0d12021-11-04 20:16:48 -0700136 if (notifierThread.joinable()) {
137 notifierThread.join();
138 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800139}
140
Austin Schuh812d0d12021-11-04 20:16:48 -0700141namespace hal::init {
Brian Silverman8fce7482020-01-05 13:18:21 -0800142void InitializeNotifier() {
143 static NotifierHandleContainer nH;
144 notifierHandles = &nH;
145}
Austin Schuh812d0d12021-11-04 20:16:48 -0700146} // namespace hal::init
Brian Silverman8fce7482020-01-05 13:18:21 -0800147
148extern "C" {
149
150HAL_NotifierHandle HAL_InitializeNotifier(int32_t* status) {
151 hal::init::CheckInit();
Austin Schuh812d0d12021-11-04 20:16:48 -0700152 if (!notifierAtexitRegistered.test_and_set()) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800153 std::atexit(cleanupNotifierAtExit);
Austin Schuh812d0d12021-11-04 20:16:48 -0700154 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800155
156 if (notifierRefCount.fetch_add(1) == 0) {
157 std::scoped_lock lock(notifierMutex);
Austin Schuh1e69f942020-11-14 15:06:14 -0800158 notifierRunning = true;
159 notifierThread = std::thread(notifierThreadMain);
Austin Schuh812d0d12021-11-04 20:16:48 -0700160
161 auto native = notifierThread.native_handle();
162 HAL_SetThreadPriority(&native, notifierThreadRealTime,
163 notifierThreadPriority, status);
164 if (*status == HAL_THREAD_PRIORITY_ERROR) {
165 *status = 0;
166 fmt::print("{}: HAL Notifier thread\n",
167 HAL_THREAD_PRIORITY_ERROR_MESSAGE);
168 }
169 if (*status == HAL_THREAD_PRIORITY_RANGE_ERROR) {
170 *status = 0;
171 fmt::print("{}: HAL Notifier thread\n",
172 HAL_THREAD_PRIORITY_RANGE_ERROR_MESSAGE);
173 }
174
Austin Schuh1e69f942020-11-14 15:06:14 -0800175 notifierAlarm.reset(tAlarm::create(status));
Brian Silverman8fce7482020-01-05 13:18:21 -0800176 }
177
178 std::shared_ptr<Notifier> notifier = std::make_shared<Notifier>();
179 HAL_NotifierHandle handle = notifierHandles->Allocate(notifier);
180 if (handle == HAL_kInvalidHandle) {
181 *status = HAL_HANDLE_ERROR;
182 return HAL_kInvalidHandle;
183 }
184 return handle;
185}
186
Austin Schuh812d0d12021-11-04 20:16:48 -0700187HAL_Bool HAL_SetNotifierThreadPriority(HAL_Bool realTime, int32_t priority,
188 int32_t* status) {
189 std::scoped_lock lock(notifierMutex);
190 notifierThreadRealTime = realTime;
191 notifierThreadPriority = priority;
192 if (notifierThread.joinable()) {
193 auto native = notifierThread.native_handle();
194 return HAL_SetThreadPriority(&native, realTime, priority, status);
195 } else {
196 return true;
197 }
198}
199
Brian Silverman8fce7482020-01-05 13:18:21 -0800200void HAL_SetNotifierName(HAL_NotifierHandle notifierHandle, const char* name,
201 int32_t* status) {}
202
203void HAL_StopNotifier(HAL_NotifierHandle notifierHandle, int32_t* status) {
204 auto notifier = notifierHandles->Get(notifierHandle);
James Kuszmaulcf324122023-01-14 14:07:17 -0800205 if (!notifier) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700206 return;
James Kuszmaulcf324122023-01-14 14:07:17 -0800207 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800208
209 {
210 std::scoped_lock lock(notifier->mutex);
211 notifier->triggerTime = UINT64_MAX;
212 notifier->triggeredTime = 0;
213 notifier->active = false;
214 }
215 notifier->cond.notify_all(); // wake up any waiting threads
216}
217
218void HAL_CleanNotifier(HAL_NotifierHandle notifierHandle, int32_t* status) {
219 auto notifier = notifierHandles->Free(notifierHandle);
James Kuszmaulcf324122023-01-14 14:07:17 -0800220 if (!notifier) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700221 return;
James Kuszmaulcf324122023-01-14 14:07:17 -0800222 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800223
224 // Just in case HAL_StopNotifier() wasn't called...
225 {
226 std::scoped_lock lock(notifier->mutex);
227 notifier->triggerTime = UINT64_MAX;
228 notifier->triggeredTime = 0;
229 notifier->active = false;
230 }
231 notifier->cond.notify_all();
232
233 if (notifierRefCount.fetch_sub(1) == 1) {
Austin Schuh1e69f942020-11-14 15:06:14 -0800234 // if this was the last notifier, clean up alarm and thread
Brian Silverman8fce7482020-01-05 13:18:21 -0800235 // the notifier can call back into our callback, so don't hold the lock
236 // here (the atomic fetch_sub will prevent multiple parallel entries
237 // into this function)
238
Austin Schuh812d0d12021-11-04 20:16:48 -0700239 if (notifierAlarm)
240 notifierAlarm->writeEnable(false, status);
Austin Schuh1e69f942020-11-14 15:06:14 -0800241 notifierRunning = false;
242 hal::ReleaseFPGAInterrupt(kTimerInterruptNumber);
Austin Schuh812d0d12021-11-04 20:16:48 -0700243 if (notifierThread.joinable()) {
244 notifierThread.join();
245 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800246
Austin Schuh1e69f942020-11-14 15:06:14 -0800247 std::scoped_lock lock(notifierMutex);
248 notifierAlarm = nullptr;
249 closestTrigger = UINT64_MAX;
Brian Silverman8fce7482020-01-05 13:18:21 -0800250 }
251}
252
253void HAL_UpdateNotifierAlarm(HAL_NotifierHandle notifierHandle,
254 uint64_t triggerTime, int32_t* status) {
255 auto notifier = notifierHandles->Get(notifierHandle);
James Kuszmaulcf324122023-01-14 14:07:17 -0800256 if (!notifier) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700257 return;
James Kuszmaulcf324122023-01-14 14:07:17 -0800258 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800259
260 {
261 std::scoped_lock lock(notifier->mutex);
262 notifier->triggerTime = triggerTime;
263 notifier->triggeredTime = UINT64_MAX;
264 }
265
266 std::scoped_lock lock(notifierMutex);
267 // Update alarm time if closer than current.
268 if (triggerTime < closestTrigger) {
269 bool wasActive = (closestTrigger != UINT64_MAX);
270 closestTrigger = triggerTime;
271 // Simply truncate the hardware trigger time to 32-bit.
272 notifierAlarm->writeTriggerTime(static_cast<uint32_t>(closestTrigger),
273 status);
274 // Enable the alarm.
Austin Schuh812d0d12021-11-04 20:16:48 -0700275 if (!wasActive)
276 notifierAlarm->writeEnable(true, status);
Brian Silverman8fce7482020-01-05 13:18:21 -0800277 }
278}
279
280void HAL_CancelNotifierAlarm(HAL_NotifierHandle notifierHandle,
281 int32_t* status) {
282 auto notifier = notifierHandles->Get(notifierHandle);
James Kuszmaulcf324122023-01-14 14:07:17 -0800283 if (!notifier) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700284 return;
James Kuszmaulcf324122023-01-14 14:07:17 -0800285 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800286
287 {
288 std::scoped_lock lock(notifier->mutex);
289 notifier->triggerTime = UINT64_MAX;
290 }
291}
292
293uint64_t HAL_WaitForNotifierAlarm(HAL_NotifierHandle notifierHandle,
294 int32_t* status) {
295 auto notifier = notifierHandles->Get(notifierHandle);
James Kuszmaulcf324122023-01-14 14:07:17 -0800296 if (!notifier) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700297 return 0;
James Kuszmaulcf324122023-01-14 14:07:17 -0800298 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800299 std::unique_lock lock(notifier->mutex);
300 notifier->cond.wait(lock, [&] {
301 return !notifier->active || notifier->triggeredTime != UINT64_MAX;
302 });
303 return notifier->active ? notifier->triggeredTime : 0;
304}
305
306} // extern "C"