Change getTimeNowNt to be lock free. (#3502)

Track the top two bits of the 32-bit time field, along with the bits that comprise the top half of
the 64-bit time field.  We can detect when the 32-bit field is advancing or falling back from the
global time counter as long as the change is less than about 1 billion ticks.  This shows up as
either 01 or 11 in the top 2 bits of the 32-bit time field relative to the 64-bit field.  Or is
there is no change it shows up as 00.  Changes of 2 billion or more cannot be discerned as +2
billion and -2 billion both show up as 10.

Change the simulator to use this logic to make sure it gets some exercise.
This commit is contained in:
Scott Smith 2021-11-08 05:24:20 -08:00 committed by GitHub
parent 45c4ccd4c4
commit d4132fdf01
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 19 additions and 55 deletions

View File

@ -54,7 +54,7 @@ void setMockMapVoltage(float voltage DECLARE_ENGINE_PARAMETER_SUFFIX) {
}
#endif /* EFI_ENABLE_MOCK_ADC */
#if EFI_PROD_CODE
#if !EFI_UNIT_TEST
/**
* 64-bit result would not overflow, but that's complex stuff for our 32-bit MCU
*/
@ -63,56 +63,26 @@ efitimeus_t getTimeNowUs(void) {
return NT2US(getTimeNowNt());
}
volatile uint32_t lastLowerNt = 0;
volatile uint32_t upperTimeNt = 0;
// this is bits 30-61, not 32-63. We only support 62-bit time. You can fire me in 36,533 years
// (1,461 on the simulator).
static volatile uint32_t upperTimeNt = 0;
efitick_t getTimeNowNt() {
chibios_rt::CriticalSectionLocker csl;
// Shift cannot be 31, as we wouldn't be able to tell if time is moving forward or backward
// relative to upperTimeNt. We do need to handle both directions as our "thread" can be
// racing with other "threads" in sampling stamp and updating upperTimeNt.
constexpr unsigned shift = 30;
uint32_t stamp = getTimeNowLowerNt();
uint32_t upper = upperTimeNt;
uint32_t relative_unsigned = stamp - (upper << shift);
efitick_t time64 = (efitick_t(upper) << shift) + (int32_t)relative_unsigned;
upperTimeNt = time64 >> shift;
// Lower 32 bits of the timer has wrapped - time to step upper bits
if (stamp < lastLowerNt) {
upperTimeNt++;
}
lastLowerNt = stamp;
return ((int64_t)upperTimeNt << 32) | stamp;
return time64;
}
/* //Alternative lock free implementation (probably actually slower!)
// this method is lock-free and thread-safe, that's because the 'update' method
// is atomic with a critical zone requirement.
//
// http://stackoverflow.com/questions/5162673/how-to-read-two-32bit-counters-as-a-64bit-integer-without-race-condition
etitick_t getTimeNowNt() {
efitime_t localH;
efitime_t localH2;
uint32_t localLow;
int counter = 0;
do {
localH = halTime.state.highBits;
localLow = halTime.state.lowBits;
localH2 = halTime.state.highBits;
if (counter++ == 10000)
chDbgPanic("lock-free frozen");
} while (localH != localH2);
// We need to take current counter after making a local 64 bit snapshot
uint32_t value = getTimeNowLowerNt();
if (value < localLow) {
// new value less than previous value means there was an overflow in that 32 bit counter
localH += 0x100000000LL;
}
return localH + value;
}
*/
#endif /* EFI_PROD_CODE */
#endif /* !EFI_UNIT_TEST */
static void onStartStopButtonToggle(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
engine->startStopStateToggleCounter++;

View File

@ -7,14 +7,8 @@
#include "pch.h"
efitick_t getTimeNowNt(void) {
return getTimeNowUs() * US_TO_NT_MULTIPLIER;
}
// Since all the time logic in the firmware is centered around this function, we only provide this
// function in the firmware. It forces us to exercise the functions that build on this one.
uint32_t getTimeNowLowerNt(void) {
return getTimeNowNt();
}
efitimeus_t getTimeNowUs(void) {
return chVTGetSystemTimeX() * (1000000 / CH_CFG_ST_FREQUENCY);
return US2NT(chVTGetSystemTimeX() * (1000000 / CH_CFG_ST_FREQUENCY));
}