blob: 905c4401d6460fd1e569a80c654295216d018146 [file] [log] [blame]
Brian Silverman8fce7482020-01-05 13:18:21 -08001/*----------------------------------------------------------------------------*/
Austin Schuh1e69f942020-11-14 15:06:14 -08002/* Copyright (c) 2016-2020 FIRST. All Rights Reserved. */
Brian Silverman8fce7482020-01-05 13:18:21 -08003/* Open Source Software - may be modified and shared by FRC teams. The code */
4/* must be accompanied by the FIRST BSD license file in the root directory of */
5/* the project. */
6/*----------------------------------------------------------------------------*/
7
8#include "hal/Notifier.h"
9
10#include <atomic>
11#include <cstdlib> // For std::atexit()
12#include <memory>
Austin Schuh1e69f942020-11-14 15:06:14 -080013#include <thread>
Brian Silverman8fce7482020-01-05 13:18:21 -080014
15#include <wpi/condition_variable.h>
16#include <wpi/mutex.h>
17
18#include "HALInitializer.h"
Austin Schuh1e69f942020-11-14 15:06:14 -080019#include "HALInternal.h"
Brian Silverman8fce7482020-01-05 13:18:21 -080020#include "hal/ChipObject.h"
21#include "hal/Errors.h"
22#include "hal/HAL.h"
23#include "hal/handles/UnlimitedHandleResource.h"
24
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;
Brian Silverman8fce7482020-01-05 13:18:21 -080032static uint64_t closestTrigger{UINT64_MAX};
33
34namespace {
35
36struct Notifier {
37 uint64_t triggerTime = UINT64_MAX;
38 uint64_t triggeredTime = UINT64_MAX;
39 bool active = true;
40 wpi::mutex mutex;
41 wpi::condition_variable cond;
42};
43
44} // namespace
45
46static std::atomic_flag notifierAtexitRegistered{ATOMIC_FLAG_INIT};
47static std::atomic_int notifierRefCount{0};
Austin Schuh1e69f942020-11-14 15:06:14 -080048static std::atomic_bool notifierRunning{false};
Brian Silverman8fce7482020-01-05 13:18:21 -080049
50using namespace hal;
51
52class NotifierHandleContainer
53 : public UnlimitedHandleResource<HAL_NotifierHandle, Notifier,
54 HAL_HandleEnum::Notifier> {
55 public:
56 ~NotifierHandleContainer() {
57 ForEach([](HAL_NotifierHandle handle, Notifier* notifier) {
58 {
59 std::scoped_lock lock(notifier->mutex);
60 notifier->triggerTime = UINT64_MAX;
61 notifier->triggeredTime = 0;
62 notifier->active = false;
63 }
64 notifier->cond.notify_all(); // wake up any waiting threads
65 });
66 }
67};
68
69static NotifierHandleContainer* notifierHandles;
70
Austin Schuh1e69f942020-11-14 15:06:14 -080071static void alarmCallback() {
Brian Silverman8fce7482020-01-05 13:18:21 -080072 std::scoped_lock lock(notifierMutex);
73 int32_t status = 0;
74 uint64_t currentTime = 0;
75
76 // the hardware disables itself after each alarm
77 closestTrigger = UINT64_MAX;
78
79 // process all notifiers
80 notifierHandles->ForEach([&](HAL_NotifierHandle handle, Notifier* notifier) {
81 if (notifier->triggerTime == UINT64_MAX) return;
82 if (currentTime == 0) currentTime = HAL_GetFPGATime(&status);
83 std::unique_lock lock(notifier->mutex);
84 if (notifier->triggerTime < currentTime) {
85 notifier->triggerTime = UINT64_MAX;
86 notifier->triggeredTime = currentTime;
87 lock.unlock();
88 notifier->cond.notify_all();
89 } else if (notifier->triggerTime < closestTrigger) {
90 closestTrigger = notifier->triggerTime;
91 }
92 });
93
94 if (notifierAlarm && closestTrigger != UINT64_MAX) {
95 // Simply truncate the hardware trigger time to 32-bit.
96 notifierAlarm->writeTriggerTime(static_cast<uint32_t>(closestTrigger),
97 &status);
98 // Enable the alarm. The hardware disables itself after each alarm.
99 notifierAlarm->writeEnable(true, &status);
100 }
101}
102
Austin Schuh1e69f942020-11-14 15:06:14 -0800103static void notifierThreadMain() {
104 tRioStatusCode status = 0;
105 tInterruptManager manager{1 << kTimerInterruptNumber, true, &status};
106 while (notifierRunning) {
107 auto triggeredMask = manager.watch(10000, false, &status);
108 if (!notifierRunning) break;
109 if (triggeredMask == 0) continue;
110 alarmCallback();
111 }
112}
113
Brian Silverman8fce7482020-01-05 13:18:21 -0800114static void cleanupNotifierAtExit() {
Austin Schuh1e69f942020-11-14 15:06:14 -0800115 int32_t status = 0;
116 if (notifierAlarm) notifierAlarm->writeEnable(false, &status);
Brian Silverman8fce7482020-01-05 13:18:21 -0800117 notifierAlarm = nullptr;
Austin Schuh1e69f942020-11-14 15:06:14 -0800118 notifierRunning = false;
119 hal::ReleaseFPGAInterrupt(kTimerInterruptNumber);
120 if (notifierThread.joinable()) notifierThread.join();
Brian Silverman8fce7482020-01-05 13:18:21 -0800121}
122
123namespace hal {
124namespace init {
125void InitializeNotifier() {
126 static NotifierHandleContainer nH;
127 notifierHandles = &nH;
128}
129} // namespace init
130} // namespace hal
131
132extern "C" {
133
134HAL_NotifierHandle HAL_InitializeNotifier(int32_t* status) {
135 hal::init::CheckInit();
136 if (!notifierAtexitRegistered.test_and_set())
137 std::atexit(cleanupNotifierAtExit);
138
139 if (notifierRefCount.fetch_add(1) == 0) {
140 std::scoped_lock lock(notifierMutex);
Austin Schuh1e69f942020-11-14 15:06:14 -0800141 notifierRunning = true;
142 notifierThread = std::thread(notifierThreadMain);
143 notifierAlarm.reset(tAlarm::create(status));
Brian Silverman8fce7482020-01-05 13:18:21 -0800144 }
145
146 std::shared_ptr<Notifier> notifier = std::make_shared<Notifier>();
147 HAL_NotifierHandle handle = notifierHandles->Allocate(notifier);
148 if (handle == HAL_kInvalidHandle) {
149 *status = HAL_HANDLE_ERROR;
150 return HAL_kInvalidHandle;
151 }
152 return handle;
153}
154
155void HAL_SetNotifierName(HAL_NotifierHandle notifierHandle, const char* name,
156 int32_t* status) {}
157
158void HAL_StopNotifier(HAL_NotifierHandle notifierHandle, int32_t* status) {
159 auto notifier = notifierHandles->Get(notifierHandle);
160 if (!notifier) return;
161
162 {
163 std::scoped_lock lock(notifier->mutex);
164 notifier->triggerTime = UINT64_MAX;
165 notifier->triggeredTime = 0;
166 notifier->active = false;
167 }
168 notifier->cond.notify_all(); // wake up any waiting threads
169}
170
171void HAL_CleanNotifier(HAL_NotifierHandle notifierHandle, int32_t* status) {
172 auto notifier = notifierHandles->Free(notifierHandle);
173 if (!notifier) return;
174
175 // Just in case HAL_StopNotifier() wasn't called...
176 {
177 std::scoped_lock lock(notifier->mutex);
178 notifier->triggerTime = UINT64_MAX;
179 notifier->triggeredTime = 0;
180 notifier->active = false;
181 }
182 notifier->cond.notify_all();
183
184 if (notifierRefCount.fetch_sub(1) == 1) {
Austin Schuh1e69f942020-11-14 15:06:14 -0800185 // if this was the last notifier, clean up alarm and thread
Brian Silverman8fce7482020-01-05 13:18:21 -0800186 // the notifier can call back into our callback, so don't hold the lock
187 // here (the atomic fetch_sub will prevent multiple parallel entries
188 // into this function)
189
Austin Schuh1e69f942020-11-14 15:06:14 -0800190 if (notifierAlarm) notifierAlarm->writeEnable(false, status);
191 notifierRunning = false;
192 hal::ReleaseFPGAInterrupt(kTimerInterruptNumber);
193 if (notifierThread.joinable()) notifierThread.join();
Brian Silverman8fce7482020-01-05 13:18:21 -0800194
Austin Schuh1e69f942020-11-14 15:06:14 -0800195 std::scoped_lock lock(notifierMutex);
196 notifierAlarm = nullptr;
197 closestTrigger = UINT64_MAX;
Brian Silverman8fce7482020-01-05 13:18:21 -0800198 }
199}
200
201void HAL_UpdateNotifierAlarm(HAL_NotifierHandle notifierHandle,
202 uint64_t triggerTime, int32_t* status) {
203 auto notifier = notifierHandles->Get(notifierHandle);
204 if (!notifier) return;
205
206 {
207 std::scoped_lock lock(notifier->mutex);
208 notifier->triggerTime = triggerTime;
209 notifier->triggeredTime = UINT64_MAX;
210 }
211
212 std::scoped_lock lock(notifierMutex);
213 // Update alarm time if closer than current.
214 if (triggerTime < closestTrigger) {
215 bool wasActive = (closestTrigger != UINT64_MAX);
216 closestTrigger = triggerTime;
217 // Simply truncate the hardware trigger time to 32-bit.
218 notifierAlarm->writeTriggerTime(static_cast<uint32_t>(closestTrigger),
219 status);
220 // Enable the alarm.
221 if (!wasActive) notifierAlarm->writeEnable(true, status);
222 }
223}
224
225void HAL_CancelNotifierAlarm(HAL_NotifierHandle notifierHandle,
226 int32_t* status) {
227 auto notifier = notifierHandles->Get(notifierHandle);
228 if (!notifier) return;
229
230 {
231 std::scoped_lock lock(notifier->mutex);
232 notifier->triggerTime = UINT64_MAX;
233 }
234}
235
236uint64_t HAL_WaitForNotifierAlarm(HAL_NotifierHandle notifierHandle,
237 int32_t* status) {
238 auto notifier = notifierHandles->Get(notifierHandle);
239 if (!notifier) return 0;
240 std::unique_lock lock(notifier->mutex);
241 notifier->cond.wait(lock, [&] {
242 return !notifier->active || notifier->triggeredTime != UINT64_MAX;
243 });
244 return notifier->active ? notifier->triggeredTime : 0;
245}
246
247} // extern "C"