#include "HAL/Notifier.hpp" #include "ChipObject.h" #include "HAL/HAL.hpp" #include "HAL/cpp/priority_mutex.h" #include #include #include static const uint32_t kTimerInterruptNumber = 28; static priority_mutex notifierInterruptMutex; static priority_recursive_mutex notifierMutex; static tAlarm *notifierAlarm = nullptr; static tInterruptManager *notifierManager = nullptr; static uint32_t closestTrigger = UINT32_MAX; struct Notifier { Notifier *prev, *next; void *param; void (*process)(uint32_t, void*); uint32_t triggerTime = UINT32_MAX; }; static Notifier *notifiers = nullptr; static std::atomic_flag notifierAtexitRegistered = ATOMIC_FLAG_INIT; static std::atomic_int notifierRefCount = ATOMIC_VAR_INIT(0); static void alarmCallback(uint32_t, void*) { std::unique_lock sync(notifierMutex); int32_t status = 0; uint32_t currentTime = 0; // the hardware disables itself after each alarm closestTrigger = UINT32_MAX; // process all notifiers Notifier *notifier = notifiers; while (notifier) { if (notifier->triggerTime != UINT32_MAX) { if (currentTime == 0) currentTime = getFPGATime(&status); if (notifier->triggerTime < currentTime) { notifier->triggerTime = UINT32_MAX; auto process = notifier->process; auto param = notifier->param; sync.unlock(); process(currentTime, param); sync.lock(); } else if (notifier->triggerTime < closestTrigger) { updateNotifierAlarm(notifier, notifier->triggerTime, &status); } } notifier = notifier->next; } } static void cleanupNotifierAtExit() { notifierAlarm = nullptr; notifierManager = nullptr; } void* initializeNotifier(void (*ProcessQueue)(uint32_t, void*), void *param, int32_t *status) { if (!ProcessQueue) { *status = NULL_PARAMETER; return nullptr; } if (!notifierAtexitRegistered.test_and_set()) std::atexit(cleanupNotifierAtExit); if (notifierRefCount.fetch_add(1) == 0) { std::lock_guard sync(notifierInterruptMutex); // create manager and alarm if not already created if (!notifierManager) { notifierManager = new tInterruptManager(1 << kTimerInterruptNumber, false, status); notifierManager->registerHandler(alarmCallback, NULL, status); notifierManager->enable(status); } if (!notifierAlarm) notifierAlarm = tAlarm::create(status); } std::lock_guard sync(notifierMutex); // create notifier structure and add to list Notifier* notifier = new Notifier(); notifier->prev = nullptr; notifier->next = notifiers; if (notifier->next) notifier->next->prev = notifier; notifier->param = param; notifier->process = ProcessQueue; notifiers = notifier; return notifier; } void cleanNotifier(void* notifier_pointer, int32_t *status) { { std::lock_guard sync(notifierMutex); Notifier* notifier = (Notifier*)notifier_pointer; // remove from list and delete if (notifier->prev) notifier->prev->next = notifier->next; if (notifier->next) notifier->next->prev = notifier->prev; if (notifiers == notifier) notifiers = notifier->next; delete notifier; } if (notifierRefCount.fetch_sub(1) == 1) { std::lock_guard sync(notifierInterruptMutex); // if this was the last notifier, clean up alarm and manager if (notifierAlarm) { notifierAlarm->writeEnable(false, status); delete notifierAlarm; notifierAlarm = nullptr; } if (notifierManager) { notifierManager->disable(status); delete notifierManager; notifierManager = nullptr; } } } void updateNotifierAlarm(void* notifier_pointer, uint32_t triggerTime, int32_t *status) { std::lock_guard sync(notifierMutex); Notifier* notifier = (Notifier*)notifier_pointer; notifier->triggerTime = triggerTime; bool wasActive = (closestTrigger != UINT32_MAX); if (!notifierInterruptMutex.try_lock() || notifierRefCount == 0 || !notifierAlarm) return; // Update alarm time if closer than current. if (triggerTime < closestTrigger) { closestTrigger = triggerTime; notifierAlarm->writeTriggerTime(triggerTime, status); } // Enable the alarm. The hardware disables itself after each alarm. if (!wasActive) notifierAlarm->writeEnable(true, status); notifierInterruptMutex.unlock(); }