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:
Peter Johnson
2015-12-30 19:06:47 -08:00
committed by Brad Miller (WPI)
parent 063925e737
commit e2ec34090a
13 changed files with 71 additions and 43 deletions

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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();

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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.