diff --git a/firmware/controllers/engine_controller.cpp b/firmware/controllers/engine_controller.cpp index 8b85626212..88dcc0445f 100644 --- a/firmware/controllers/engine_controller.cpp +++ b/firmware/controllers/engine_controller.cpp @@ -159,7 +159,7 @@ uint64_t getTimeNowUs(void) { } uint64_t getTimeNowNt(void) { - return halTime.get(hal_lld_get_counter_value(), false); + return halTime.get(); } //uint64_t getHalTimer(void) { @@ -218,7 +218,11 @@ static void onEvenyGeneralMilliseconds(Engine *engine) { /** * We need to push current value into the 64 bit counter often enough so that we do not miss an overflow */ - halTime.get(hal_lld_get_counter_value(), true); + bool alreadyLocked = lockAnyContext(); + updateAndSet(&halTime.state, hal_lld_get_counter_value()); + if (!alreadyLocked) { + unlockAnyContext(); + } if (!engine->rpmCalculator.isRunning()) writeToFlashIfPending(); @@ -390,7 +394,6 @@ void initEngineContoller(Engine *engine) { */ prepareShapes(engine); - initMalfunctionCentral(); #if EFI_ELECTRONIC_THROTTLE_BODY diff --git a/firmware/controllers/trigger/trigger_central.cpp b/firmware/controllers/trigger/trigger_central.cpp index 77f26f4d1b..aee76d7c75 100644 --- a/firmware/controllers/trigger/trigger_central.cpp +++ b/firmware/controllers/trigger/trigger_central.cpp @@ -232,7 +232,7 @@ static void triggerInfo(Engine *engine) { #endif #if EFI_PROD_CODE - scheduleMsg(&logger, "maxLockTime=%d", maxLockTime); + scheduleMsg(&logger, "maxLockTime=%d / maxTriggerReentraint=%d", maxLockTime, maxTriggerReentraint); scheduleMsg(&logger, "primary trigger simulator: %s %s freq=%d", hwPortname(boardConfiguration->triggerSimulatorPins[0]), pinModeToString(boardConfiguration->triggerSimulatorPinModes[0]), diff --git a/firmware/emulation/rfi_perftest.cpp b/firmware/emulation/rfi_perftest.cpp index 65cec5092b..c3b52aa0a2 100644 --- a/firmware/emulation/rfi_perftest.cpp +++ b/firmware/emulation/rfi_perftest.cpp @@ -252,7 +252,7 @@ static int rtcStartTime; static void timeInfo(void) { scheduleMsg(&logger, "chTimeNow as seconds = %d", getTimeNowSeconds()); - scheduleMsg(&logger, "hal seconds = %d", halTime.get(hal_lld_get_counter_value(), false) / 168000000LL); + scheduleMsg(&logger, "hal seconds = %d", halTime.get() / 168000000LL); #if EFI_RTC int unix = rtcGetTimeUnixSec(&RTCD1) - rtcStartTime; diff --git a/firmware/util/efilib2.cpp b/firmware/util/efilib2.cpp index 2145009c0b..0071c76b17 100644 --- a/firmware/util/efilib2.cpp +++ b/firmware/util/efilib2.cpp @@ -5,8 +5,16 @@ * @author Andrey Belomutskiy, (c) 2012-2014 */ +#include "main.h" +#if (EFI_PROD_CODE || EFI_SIMULATOR) + #define GET_VALUE() hal_lld_get_counter_value() +#else + #define GET_VALUE() 0 +#endif + #include "efilib2.h" + /** * The main use-case of this class is to keep track of a 64-bit global number of CPU ticks from reset. * @@ -18,24 +26,54 @@ * In order for this to function, it's your responsibility to invoke offer() method at least once a second. */ Overflow64Counter::Overflow64Counter() { - currentBase = 0; - currentValue = 0; + state.highBits = 0; + state.lowBits = 0; } -uint64_t Overflow64Counter::get(uint32_t value, int isPrimaryThread) { - // this method is lock-free, only one thread is allowed to commit state +/** + * in order to have atomic writes this should be invoked within a critical section + */ +void updateAndSet(State64 *state, uint32_t value) { + if (value < state->lowBits) { + // new value less than previous value means there was an overflow in that 32 bit counter + state->highBits += 0x100000000LL; + } + state->lowBits = value; +} + +uint64_t Overflow64Counter::update(uint32_t value) { + updateAndSet(&state, value); + return state.highBits + state.lowBits; +} + +// todo: make this a macro? always inline? +uint64_t Overflow64Counter::get() { + /** + * 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 + */ // these are local copies for thread-safery // todo: this is still not atomic, so technically not thread safe. - uint32_t localValue = currentValue; - uint64_t localBase = currentBase; - if (value < localValue) { - // new value less than previous value means there was an overflow in that 32 bit counter - localBase += 0x100000000LL; + uint64_t localH; + uint32_t localLow; + while (true) { + localH = state.highBits; + localLow = state.lowBits; + uint64_t localH2 = state.highBits; + if (localH == localH2) + break; } - if (isPrimaryThread) { - currentValue = value; - currentBase = localBase; + /** + * We need to take current counter after making a local 64 bit snapshot + */ + uint32_t value = GET_VALUE(); + + if (value < localLow) { + // new value less than previous value means there was an overflow in that 32 bit counter + localH += 0x100000000LL; } - return localBase + value; + return localH + value; } diff --git a/firmware/util/efilib2.h b/firmware/util/efilib2.h index e2e76b60ee..70b7dc233e 100644 --- a/firmware/util/efilib2.h +++ b/firmware/util/efilib2.h @@ -10,18 +10,22 @@ #include +typedef struct { + uint64_t highBits; + uint32_t lowBits; +} State64; + +void updateAndSet(State64 *state, uint32_t value); class Overflow64Counter { public: Overflow64Counter(); - uint64_t get(uint32_t value, int isPrimaryThread); + uint64_t get(); + uint64_t update(uint32_t value); - private: - uint64_t currentBase; - uint32_t currentValue; + State64 state; }; - #endif /* EFILIB2_H_ */ diff --git a/unit_tests/test_util.cpp b/unit_tests/test_util.cpp index 733a6cb7a7..70b2d431c2 100644 --- a/unit_tests/test_util.cpp +++ b/unit_tests/test_util.cpp @@ -41,13 +41,13 @@ void testOverflow64Counter(void) { print("*************************************** testOverflow64Counter\r\n"); Overflow64Counter o; - assertEquals(0, o.get(0, true)); - assertEquals(10, o.get(10, true)); + assertEquals(0, o.update(0)); + assertEquals(10, o.update(10)); - assertEquals(20, o.get(20, true)); + assertEquals(20, o.update(20)); // overflow - assertEquals(4294967296, o.get(0, true)); + assertEquals(4294967296, o.update(0)); } void testCyclicBuffer(void) {