mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-22 01:11:42 +00:00
Keep track of FPGA time rollovers with 64-bit time.
This allows both greater than 72 minute (2^32 * 1 us) timeouts and also gracefully handles notifiers across the FPGA time counter rollover. Change-Id: Ibde0b903155f60b618b0ca4d5f8f6dd49f90b020
This commit is contained in:
committed by
Brad Miller (WPI)
parent
063925e737
commit
e2ec34090a
@@ -222,7 +222,7 @@ extern "C"
|
||||
|
||||
uint16_t getFPGAVersion(int32_t *status);
|
||||
uint32_t getFPGARevision(int32_t *status);
|
||||
uint32_t getFPGATime(int32_t *status);
|
||||
uint64_t getFPGATime(int32_t *status);
|
||||
|
||||
bool getFPGAButton(int32_t *status);
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
|
||||
extern "C"
|
||||
{
|
||||
void* initializeNotifier(void (*process)(uint32_t, void*), void* param, int32_t *status);
|
||||
void* initializeNotifier(void (*process)(uint64_t, void*), void* param, int32_t *status);
|
||||
void cleanNotifier(void* notifier_pointer, int32_t *status);
|
||||
void* getNotifierParam(void* notifier_pointer, int32_t *status);
|
||||
void updateNotifierAlarm(void* notifier_pointer, uint32_t triggerTime, int32_t *status);
|
||||
void updateNotifierAlarm(void* notifier_pointer, uint64_t triggerTime, int32_t *status);
|
||||
void stopNotifierAlarm(void* notifier_pointer, int32_t *status);
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ tSPI *spiSystem;
|
||||
|
||||
struct SPIAccumulator {
|
||||
void* notifier = nullptr;
|
||||
uint32_t triggerTime;
|
||||
uint64_t triggerTime;
|
||||
uint32_t period;
|
||||
|
||||
int64_t value = 0;
|
||||
@@ -1499,7 +1499,7 @@ priority_recursive_mutex& spiGetSemaphore(uint8_t port) {
|
||||
return spiMXPSemaphore;
|
||||
}
|
||||
|
||||
static void spiAccumulatorProcess(uint32_t currentTime, void *param) {
|
||||
static void spiAccumulatorProcess(uint64_t currentTime, void *param) {
|
||||
SPIAccumulator* accum = (SPIAccumulator*)param;
|
||||
|
||||
// perform SPI transaction
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <unistd.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <signal.h> // linux for kill
|
||||
@@ -23,6 +24,11 @@ const uint32_t kSystemClockTicksPerMicrosecond = 40;
|
||||
static tGlobal *global = nullptr;
|
||||
static tSysWatchdog *watchdog = nullptr;
|
||||
|
||||
static priority_mutex timeMutex;
|
||||
static uint32_t timeEpoch = 0;
|
||||
static uint32_t prevFPGATime = 0;
|
||||
static void* rolloverNotifier = nullptr;
|
||||
|
||||
void* getPort(uint8_t pin)
|
||||
{
|
||||
Port* port = new Port();
|
||||
@@ -184,13 +190,19 @@ uint32_t getFPGARevision(int32_t *status)
|
||||
*
|
||||
* @return The current time in microseconds according to the FPGA (since FPGA reset).
|
||||
*/
|
||||
uint32_t getFPGATime(int32_t *status)
|
||||
uint64_t getFPGATime(int32_t *status)
|
||||
{
|
||||
if (!global) {
|
||||
*status = NiFpga_Status_ResourceNotInitialized;
|
||||
return 0;
|
||||
}
|
||||
return global->readLocalTime(status);
|
||||
std::lock_guard<priority_mutex> lock(timeMutex);
|
||||
uint32_t fpgaTime = global->readLocalTime(status);
|
||||
if (*status != 0) return 0;
|
||||
// check for rollover
|
||||
if (fpgaTime < prevFPGATime) ++timeEpoch;
|
||||
prevFPGATime = fpgaTime;
|
||||
return (((uint64_t)timeEpoch) << 32) | ((uint64_t)fpgaTime);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -235,6 +247,12 @@ static void HALCleanupAtExit() {
|
||||
watchdog = nullptr;
|
||||
}
|
||||
|
||||
static void timerRollover(uint64_t currentTime, void*) {
|
||||
// reschedule timer for next rollover
|
||||
int32_t status = 0;
|
||||
updateNotifierAlarm(rolloverNotifier, currentTime + 0x80000000ULL, &status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this to start up HAL. This is required for robot programs.
|
||||
*/
|
||||
@@ -256,6 +274,14 @@ int HALInitialize(int mode)
|
||||
|
||||
std::atexit(HALCleanupAtExit);
|
||||
|
||||
if (!rolloverNotifier)
|
||||
rolloverNotifier = initializeNotifier(timerRollover, nullptr, &status);
|
||||
if (status == 0) {
|
||||
uint64_t curTime = getFPGATime(&status);
|
||||
if (status == 0)
|
||||
updateNotifierAlarm(rolloverNotifier, curTime + 0x80000000ULL, &status);
|
||||
}
|
||||
|
||||
// Kill any previous robot programs
|
||||
std::fstream fs;
|
||||
// By making this both in/out, it won't give us an error if it doesnt exist
|
||||
@@ -302,6 +328,7 @@ int HALInitialize(int mode)
|
||||
pid = getpid();
|
||||
fs << pid << std::endl;
|
||||
fs.close();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,12 +12,12 @@ static priority_mutex notifierInterruptMutex;
|
||||
static priority_recursive_mutex notifierMutex;
|
||||
static tAlarm *notifierAlarm = nullptr;
|
||||
static tInterruptManager *notifierManager = nullptr;
|
||||
static uint32_t closestTrigger = UINT32_MAX;
|
||||
static uint64_t closestTrigger = UINT64_MAX;
|
||||
struct Notifier {
|
||||
Notifier *prev, *next;
|
||||
void *param;
|
||||
void (*process)(uint32_t, void*);
|
||||
uint32_t triggerTime = UINT32_MAX;
|
||||
void (*process)(uint64_t, void*);
|
||||
uint64_t triggerTime = UINT64_MAX;
|
||||
};
|
||||
static Notifier *notifiers = nullptr;
|
||||
static std::atomic_flag notifierAtexitRegistered = ATOMIC_FLAG_INIT;
|
||||
@@ -28,19 +28,19 @@ static void alarmCallback(uint32_t, void*)
|
||||
std::unique_lock<priority_recursive_mutex> sync(notifierMutex);
|
||||
|
||||
int32_t status = 0;
|
||||
uint32_t currentTime = 0;
|
||||
uint64_t currentTime = 0;
|
||||
|
||||
// the hardware disables itself after each alarm
|
||||
closestTrigger = UINT32_MAX;
|
||||
closestTrigger = UINT64_MAX;
|
||||
|
||||
// process all notifiers
|
||||
Notifier *notifier = notifiers;
|
||||
while (notifier) {
|
||||
if (notifier->triggerTime != UINT32_MAX) {
|
||||
if (notifier->triggerTime != UINT64_MAX) {
|
||||
if (currentTime == 0)
|
||||
currentTime = getFPGATime(&status);
|
||||
if (notifier->triggerTime < currentTime) {
|
||||
notifier->triggerTime = UINT32_MAX;
|
||||
notifier->triggerTime = UINT64_MAX;
|
||||
auto process = notifier->process;
|
||||
auto param = notifier->param;
|
||||
sync.unlock();
|
||||
@@ -59,7 +59,7 @@ static void cleanupNotifierAtExit() {
|
||||
notifierManager = nullptr;
|
||||
}
|
||||
|
||||
void* initializeNotifier(void (*process)(uint32_t, void*), void *param, int32_t *status)
|
||||
void* initializeNotifier(void (*process)(uint64_t, void*), void *param, int32_t *status)
|
||||
{
|
||||
if (!process) {
|
||||
*status = NULL_PARAMETER;
|
||||
@@ -124,22 +124,23 @@ void* getNotifierParam(void* notifier_pointer, int32_t *status)
|
||||
return ((Notifier*)notifier_pointer)->param;
|
||||
}
|
||||
|
||||
void updateNotifierAlarm(void* notifier_pointer, uint32_t triggerTime, int32_t *status)
|
||||
void updateNotifierAlarm(void* notifier_pointer, uint64_t triggerTime, int32_t *status)
|
||||
{
|
||||
std::lock_guard<priority_recursive_mutex> sync(notifierMutex);
|
||||
|
||||
Notifier* notifier = (Notifier*)notifier_pointer;
|
||||
notifier->triggerTime = triggerTime;
|
||||
bool wasActive = (closestTrigger != UINT32_MAX);
|
||||
bool wasActive = (closestTrigger != UINT64_MAX);
|
||||
|
||||
if (!notifierInterruptMutex.try_lock() || notifierRefCount == 0 ||
|
||||
!notifierAlarm)
|
||||
return;
|
||||
if (!notifierInterruptMutex.try_lock() || notifierRefCount == 0 ||
|
||||
!notifierAlarm)
|
||||
return;
|
||||
|
||||
// Update alarm time if closer than current.
|
||||
// Update alarm time if closer than current.
|
||||
if (triggerTime < closestTrigger) {
|
||||
closestTrigger = triggerTime;
|
||||
notifierAlarm->writeTriggerTime(triggerTime, status);
|
||||
// Simply truncate the hardware trigger time to 32-bit.
|
||||
notifierAlarm->writeTriggerTime((uint32_t)triggerTime, status);
|
||||
}
|
||||
// Enable the alarm. The hardware disables itself after each alarm.
|
||||
if (!wasActive) notifierAlarm->writeEnable(true, status);
|
||||
@@ -151,5 +152,5 @@ void stopNotifierAlarm(void* notifier_pointer, int32_t *status)
|
||||
{
|
||||
std::lock_guard<priority_recursive_mutex> sync(notifierMutex);
|
||||
Notifier* notifier = (Notifier*)notifier_pointer;
|
||||
notifier->triggerTime = UINT32_MAX;
|
||||
notifier->triggerTime = UINT64_MAX;
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ class Notifier : public ErrorBase {
|
||||
// update the HAL alarm
|
||||
void UpdateAlarm();
|
||||
// HAL callback
|
||||
static void Notify(uint32_t currentTimeInt, void *param);
|
||||
static void Notify(uint64_t currentTimeInt, void *param);
|
||||
|
||||
// held while updating process information
|
||||
priority_mutex m_processMutex;
|
||||
|
||||
@@ -43,7 +43,7 @@ Notifier::~Notifier() {
|
||||
*/
|
||||
void Notifier::UpdateAlarm() {
|
||||
int32_t status = 0;
|
||||
updateNotifierAlarm(m_notifier, (uint32_t)(m_expirationTime * 1e6), &status);
|
||||
updateNotifierAlarm(m_notifier, (uint64_t)(m_expirationTime * 1e6), &status);
|
||||
wpi_setErrorWithContext(status, getHALErrorMessage(status));
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ void Notifier::UpdateAlarm() {
|
||||
* Notify is called by the HAL layer. We simply need to pass it through to
|
||||
* the user handler.
|
||||
*/
|
||||
void Notifier::Notify(uint32_t currentTimeInt, void *param) {
|
||||
void Notifier::Notify(uint64_t currentTimeInt, void *param) {
|
||||
Notifier* notifier = static_cast<Notifier*>(param);
|
||||
|
||||
notifier->m_processMutex.lock();
|
||||
|
||||
@@ -156,9 +156,9 @@ uint32_t GetFPGARevision() {
|
||||
* @return The current time in microseconds according to the FPGA (since FPGA
|
||||
* reset).
|
||||
*/
|
||||
uint32_t GetFPGATime() {
|
||||
uint64_t GetFPGATime() {
|
||||
int32_t status = 0;
|
||||
uint32_t time = getFPGATime(&status);
|
||||
uint64_t time = getFPGATime(&status);
|
||||
wpi_setGlobalErrorWithContext(status, getHALErrorMessage(status));
|
||||
return time;
|
||||
}
|
||||
|
||||
@@ -46,6 +46,6 @@ void wpi_suspendOnAssertEnabled(bool enabled);
|
||||
|
||||
uint16_t GetFPGAVersion();
|
||||
uint32_t GetFPGARevision();
|
||||
uint32_t GetFPGATime();
|
||||
uint64_t GetFPGATime();
|
||||
bool GetUserButton();
|
||||
std::string GetStackTrace(uint32_t offset);
|
||||
|
||||
@@ -159,7 +159,7 @@ bool wpi_assertNotEqual_impl(int valueA,
|
||||
*
|
||||
* @return The current time in microseconds according to the FPGA (since FPGA reset).
|
||||
*/
|
||||
uint32_t GetFPGATime()
|
||||
uint64_t GetFPGATime()
|
||||
{
|
||||
return wpilib::internal::simTime * 1e6;
|
||||
}
|
||||
|
||||
@@ -38,14 +38,14 @@ class NotifierThreadJNI : public SafeThread {
|
||||
bool m_notify = false;
|
||||
jobject m_func = nullptr;
|
||||
jmethodID m_mid;
|
||||
uint32_t m_currentTime;
|
||||
uint64_t m_currentTime;
|
||||
};
|
||||
|
||||
class NotifierJNI : public SafeThreadOwner<NotifierThreadJNI> {
|
||||
public:
|
||||
void SetFunc(JNIEnv* env, jobject func, jmethodID mid);
|
||||
|
||||
void Notify(uint32_t currentTime) {
|
||||
void Notify(uint64_t currentTime) {
|
||||
auto thr = GetThread();
|
||||
if (!thr) return;
|
||||
thr->m_currentTime = currentTime;
|
||||
@@ -81,9 +81,9 @@ void NotifierThreadJNI::Main() {
|
||||
if (!m_func) continue;
|
||||
jobject func = m_func;
|
||||
jmethodID mid = m_mid;
|
||||
uint32_t currentTime = m_currentTime;
|
||||
uint64_t currentTime = m_currentTime;
|
||||
lock.unlock(); // don't hold mutex during callback execution
|
||||
env->CallVoidMethod(func, mid, (jint)currentTime);
|
||||
env->CallVoidMethod(func, mid, (jlong)currentTime);
|
||||
if (env->ExceptionCheck()) {
|
||||
env->ExceptionDescribe();
|
||||
env->ExceptionClear();
|
||||
@@ -97,7 +97,7 @@ void NotifierThreadJNI::Main() {
|
||||
jvm->DetachCurrentThread();
|
||||
}
|
||||
|
||||
void notifierHandler(uint32_t currentTimeInt, void* param) {
|
||||
void notifierHandler(uint64_t currentTimeInt, void* param) {
|
||||
((NotifierJNI*)param)->Notify(currentTimeInt);
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ JNIEXPORT jlong JNICALL Java_edu_wpi_first_wpilibj_hal_NotifierJNI_initializeNot
|
||||
assert(false);
|
||||
return 0;
|
||||
}
|
||||
jmethodID mid = env->GetMethodID(cls, "apply", "(I)V");
|
||||
jmethodID mid = env->GetMethodID(cls, "apply", "(J)V");
|
||||
if (mid == 0) {
|
||||
NOTIFIERJNI_LOG(logERROR) << "Error getting java method ID";
|
||||
assert(false);
|
||||
@@ -169,10 +169,10 @@ JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_hal_NotifierJNI_cleanNotifier
|
||||
/*
|
||||
* Class: edu_wpi_first_wpilibj_hal_NotifierJNI
|
||||
* Method: updateNotifierAlarm
|
||||
* Signature: (JI)V
|
||||
* Signature: (JJ)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_hal_NotifierJNI_updateNotifierAlarm
|
||||
(JNIEnv *env, jclass cls, jlong notifierPtr, jint triggerTime)
|
||||
(JNIEnv *env, jclass cls, jlong notifierPtr, jlong triggerTime)
|
||||
{
|
||||
NOTIFIERJNI_LOG(logDEBUG) << "Calling NOTIFIERJNI updateNotifierAlarm";
|
||||
|
||||
@@ -181,7 +181,7 @@ JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_hal_NotifierJNI_updateNotifier
|
||||
NOTIFIERJNI_LOG(logDEBUG) << "triggerTime = " << triggerTime;
|
||||
|
||||
int32_t status = 0;
|
||||
updateNotifierAlarm((void*)notifierPtr, (uint32_t)triggerTime, &status);
|
||||
updateNotifierAlarm((void*)notifierPtr, (uint64_t)triggerTime, &status);
|
||||
NOTIFIERJNI_LOG(logDEBUG) << "Status = " << status;
|
||||
CheckStatus(env, status);
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ public class Notifier {
|
||||
* Update the alarm hardware to reflect the next alarm.
|
||||
*/
|
||||
private void updateAlarm() {
|
||||
NotifierJNI.updateNotifierAlarm(m_notifier, (int) (m_expirationTime * 1e6));
|
||||
NotifierJNI.updateNotifierAlarm(m_notifier, (long) (m_expirationTime * 1e6));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -56,7 +56,7 @@ public class Notifier {
|
||||
* calling of the user handler.
|
||||
*/
|
||||
@Override
|
||||
public void apply(int time) {
|
||||
public void apply(long time) {
|
||||
m_processLock.lock();
|
||||
if (m_periodic) {
|
||||
m_expirationTime += m_period;
|
||||
|
||||
@@ -14,7 +14,7 @@ public class NotifierJNI extends JNIWrapper {
|
||||
* Callback function
|
||||
*/
|
||||
public interface NotifierJNIHandlerFunction {
|
||||
void apply(int curTime);
|
||||
void apply(long curTime);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -30,7 +30,7 @@ public class NotifierJNI extends JNIWrapper {
|
||||
/**
|
||||
* Sets the notifier to call the callback in another triggerTime microseconds.
|
||||
*/
|
||||
public static native void updateNotifierAlarm(long notifierPtr, int triggerTime);
|
||||
public static native void updateNotifierAlarm(long notifierPtr, long triggerTime);
|
||||
|
||||
/**
|
||||
* Tells the notifier to stop calling the callback.
|
||||
|
||||
Reference in New Issue
Block a user