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:
parent
45c4ccd4c4
commit
d4132fdf01
|
@ -54,7 +54,7 @@ void setMockMapVoltage(float voltage DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
||||||
}
|
}
|
||||||
#endif /* EFI_ENABLE_MOCK_ADC */
|
#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
|
* 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());
|
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() {
|
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 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
|
return time64;
|
||||||
if (stamp < lastLowerNt) {
|
|
||||||
upperTimeNt++;
|
|
||||||
}
|
|
||||||
|
|
||||||
lastLowerNt = stamp;
|
|
||||||
|
|
||||||
return ((int64_t)upperTimeNt << 32) | stamp;
|
|
||||||
}
|
}
|
||||||
|
#endif /* !EFI_UNIT_TEST */
|
||||||
/* //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 */
|
|
||||||
|
|
||||||
static void onStartStopButtonToggle(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
|
static void onStartStopButtonToggle(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
|
||||||
engine->startStopStateToggleCounter++;
|
engine->startStopStateToggleCounter++;
|
||||||
|
|
|
@ -7,14 +7,8 @@
|
||||||
|
|
||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
|
|
||||||
efitick_t getTimeNowNt(void) {
|
// Since all the time logic in the firmware is centered around this function, we only provide this
|
||||||
return getTimeNowUs() * US_TO_NT_MULTIPLIER;
|
// function in the firmware. It forces us to exercise the functions that build on this one.
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t getTimeNowLowerNt(void) {
|
uint32_t getTimeNowLowerNt(void) {
|
||||||
return getTimeNowNt();
|
return US2NT(chVTGetSystemTimeX() * (1000000 / CH_CFG_ST_FREQUENCY));
|
||||||
}
|
|
||||||
|
|
||||||
efitimeus_t getTimeNowUs(void) {
|
|
||||||
return chVTGetSystemTimeX() * (1000000 / CH_CFG_ST_FREQUENCY);
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue