From 7e34a2c2242d6e75a75d5479ea65ca2aeac5cea2 Mon Sep 17 00:00:00 2001 From: Joseph Hickey Date: Wed, 30 Dec 2020 13:47:48 -0500 Subject: [PATCH] Fix #429, update OS_time_t definition to 64-bit ticks Use a single 64-bit tick counter as OS_time_t, rather than a split 32 bit seconds + 32 bit microseconds counter. This benefits in several ways: - increases the timing precision by 10x (0.1us ticks) - increases the representable range by 400x (+/-14000 yrs) - simplifies addition/subtraction (no carry over) - avoids "year 2038" bug w/32-bit timestamps --- src/os/inc/osapi-clock.h | 78 ++++++++++--------- .../shared/src/coveragetest-clock.c | 12 +-- 2 files changed, 47 insertions(+), 43 deletions(-) diff --git a/src/os/inc/osapi-clock.h b/src/os/inc/osapi-clock.h index 30b7215e0..fa854209f 100644 --- a/src/os/inc/osapi-clock.h +++ b/src/os/inc/osapi-clock.h @@ -44,10 +44,29 @@ */ typedef struct { - uint32 seconds; - uint32 microsecs; + int64 ticks; /**< Ticks elapsed since reference point */ } OS_time_t; + +/** + * @brief Multipliers/divisors to convert ticks into standardized units + * + * Various fixed conversion factor constants used by the conversion routines + * + * A 100ns tick time allows max intervals of about +/- 14000 years in + * a 64-bit signed integer value. + * + * @note Applications should not directly use these values, but rather use + * conversion routines below to obtain standardized units (seconds/microseconds/etc). + */ +enum +{ + OS_TIME_TICK_RESOLUTION_NS = 100, + OS_TIME_TICKS_PER_SECOND = 1000000000 / OS_TIME_TICK_RESOLUTION_NS, + OS_TIME_TICKS_PER_MSEC = 1000000 / OS_TIME_TICK_RESOLUTION_NS, + OS_TIME_TICKS_PER_USEC = 1000 / OS_TIME_TICK_RESOLUTION_NS +}; + /** @defgroup OSAPIClock OSAL Real Time Clock APIs * @{ */ @@ -108,7 +127,7 @@ int32 OS_SetLocalTime(const OS_time_t *time_struct); */ static inline int64 OS_TimeGetTotalSeconds(OS_time_t tm) { - return (tm.seconds); + return (tm.ticks / OS_TIME_TICKS_PER_SECOND); } /*-------------------------------------------------------------------------------------*/ @@ -122,7 +141,7 @@ static inline int64 OS_TimeGetTotalSeconds(OS_time_t tm) */ static inline int64 OS_TimeGetTotalMilliseconds(OS_time_t tm) { - return (((int64)tm.seconds * 1000) + (tm.microsecs / 1000)); + return (tm.ticks / OS_TIME_TICKS_PER_MSEC); } /*-------------------------------------------------------------------------------------*/ @@ -136,7 +155,7 @@ static inline int64 OS_TimeGetTotalMilliseconds(OS_time_t tm) */ static inline int64 OS_TimeGetTotalMicroseconds(OS_time_t tm) { - return (((int64)tm.seconds * 1000000) + tm.microsecs); + return (tm.ticks / OS_TIME_TICKS_PER_USEC); } /*-------------------------------------------------------------------------------------*/ @@ -154,7 +173,7 @@ static inline int64 OS_TimeGetTotalMicroseconds(OS_time_t tm) */ static inline int64 OS_TimeGetTotalNanoseconds(OS_time_t tm) { - return (((int64)tm.seconds * 1000000000) + (tm.microsecs * 1000)); + return (tm.ticks * OS_TIME_TICK_RESOLUTION_NS); } /*-------------------------------------------------------------------------------------*/ @@ -169,7 +188,7 @@ static inline int64 OS_TimeGetTotalNanoseconds(OS_time_t tm) */ static inline int64 OS_TimeGetFractionalPart(OS_time_t tm) { - return (tm.microsecs); + return (tm.ticks % OS_TIME_TICKS_PER_SECOND); } /*-------------------------------------------------------------------------------------*/ @@ -194,7 +213,8 @@ static inline uint32 OS_TimeGetSubsecondsPart(OS_time_t tm) * It also must round up, otherwise this may result in a value one * less than the original when converted back to usec again. */ - return (((OS_TimeGetFractionalPart(tm) << 26) + 15624) / 15625); + int64 frac = (OS_TimeGetFractionalPart(tm) << 30) + (OS_TIME_TICKS_PER_SECOND >> 2); + return (uint32)((frac - 1) / (OS_TIME_TICKS_PER_SECOND >> 2)); } @@ -212,7 +232,7 @@ static inline uint32 OS_TimeGetSubsecondsPart(OS_time_t tm) */ static inline uint32 OS_TimeGetMillisecondsPart(OS_time_t tm) { - return OS_TimeGetFractionalPart(tm) / 1000; + return (uint32)OS_TimeGetFractionalPart(tm) / OS_TIME_TICKS_PER_MSEC; } /*-------------------------------------------------------------------------------------*/ @@ -237,7 +257,7 @@ static inline uint32 OS_TimeGetMillisecondsPart(OS_time_t tm) */ static inline uint32 OS_TimeGetMicrosecondsPart(OS_time_t tm) { - return OS_TimeGetFractionalPart(tm); + return (uint32)OS_TimeGetFractionalPart(tm) / OS_TIME_TICKS_PER_USEC; } /*-------------------------------------------------------------------------------------*/ @@ -256,7 +276,7 @@ static inline uint32 OS_TimeGetMicrosecondsPart(OS_time_t tm) */ static inline uint32 OS_TimeGetNanosecondsPart(OS_time_t tm) { - return OS_TimeGetFractionalPart(tm) * 1000; + return (uint32)OS_TimeGetFractionalPart(tm) * OS_TIME_TICK_RESOLUTION_NS; } /*-------------------------------------------------------------------------------------*/ @@ -278,8 +298,8 @@ static inline uint32 OS_TimeGetNanosecondsPart(OS_time_t tm) static inline OS_time_t OS_TimeAssembleFromNanoseconds(int64 seconds, uint32 nanoseconds) { OS_time_t result; - result.seconds = seconds; - result.microsecs = nanoseconds / 1000; + result.ticks = seconds * OS_TIME_TICKS_PER_SECOND; + result.ticks += nanoseconds / OS_TIME_TICK_RESOLUTION_NS; return result; } @@ -302,8 +322,8 @@ static inline OS_time_t OS_TimeAssembleFromNanoseconds(int64 seconds, uint32 nan static inline OS_time_t OS_TimeAssembleFromMicroseconds(int64 seconds, uint32 microseconds) { OS_time_t result; - result.seconds = seconds; - result.microsecs = microseconds; + result.ticks = seconds * OS_TIME_TICKS_PER_SECOND; + result.ticks += microseconds * OS_TIME_TICKS_PER_USEC; return result; } @@ -326,8 +346,8 @@ static inline OS_time_t OS_TimeAssembleFromMicroseconds(int64 seconds, uint32 mi static inline OS_time_t OS_TimeAssembleFromMilliseconds(int64 seconds, uint32 milliseconds) { OS_time_t result; - result.seconds = seconds; - result.microsecs = milliseconds * 1000; + result.ticks = seconds * OS_TIME_TICKS_PER_SECOND; + result.ticks += milliseconds * OS_TIME_TICKS_PER_MSEC; return result; } @@ -350,9 +370,9 @@ static inline OS_time_t OS_TimeAssembleFromMilliseconds(int64 seconds, uint32 mi static inline OS_time_t OS_TimeAssembleFromSubseconds(int64 seconds, uint32 subseconds) { OS_time_t result; - result.seconds = seconds; + result.ticks = seconds * OS_TIME_TICKS_PER_SECOND; /* this should not round in any way, as the 32-bit input value has higher precision */ - result.microsecs = ((int64)subseconds * 15625) >> 26; + result.ticks += ((int64)subseconds * (OS_TIME_TICKS_PER_SECOND >> 2)) >> 30; return result; } @@ -367,15 +387,7 @@ static inline OS_time_t OS_TimeAssembleFromSubseconds(int64 seconds, uint32 subs */ static inline OS_time_t OS_TimeAdd(OS_time_t time1, OS_time_t time2) { - OS_time_t result = time1; - result.seconds += time2.seconds; - result.microsecs += time2.microsecs; - if (result.microsecs >= 1000000) - { - ++result.seconds; - result.microsecs -= 1000000; - } - return result; + return ((OS_time_t) { time1.ticks + time2.ticks }); } /*-------------------------------------------------------------------------------------*/ @@ -389,15 +401,7 @@ static inline OS_time_t OS_TimeAdd(OS_time_t time1, OS_time_t time2) */ static inline OS_time_t OS_TimeSubtract(OS_time_t time1, OS_time_t time2) { - OS_time_t result = time1; - result.seconds -= time2.seconds; - result.microsecs -= time2.microsecs; - if (result.microsecs >= 1000000) - { - --result.seconds; - result.microsecs += 1000000; - } - return result; + return ((OS_time_t) { time1.ticks - time2.ticks }); } diff --git a/src/unit-test-coverage/shared/src/coveragetest-clock.c b/src/unit-test-coverage/shared/src/coveragetest-clock.c index 20959c987..8c7685c9f 100644 --- a/src/unit-test-coverage/shared/src/coveragetest-clock.c +++ b/src/unit-test-coverage/shared/src/coveragetest-clock.c @@ -118,14 +118,14 @@ void Test_OS_TimeAccessConversions(void) UtAssert_UINT32_EQ(OS_TimeGetTotalMicroseconds(t4), 1901000); /* Note: Nanoseconds/Subseconds may not be exact due to limitations of OS_time_t resolution */ - UtAssert_UINT32_EQ(OS_TimeGetTotalNanoseconds(t1), 1234567000); - UtAssert_UINT32_EQ(OS_TimeGetTotalNanoseconds(t2), 2528888000); + UtAssert_UINT32_EQ(OS_TimeGetTotalNanoseconds(t1), 1234567800); + UtAssert_UINT32_EQ(OS_TimeGetTotalNanoseconds(t2), 2528888800); UtAssert_UINT32_EQ(OS_TimeGetTotalNanoseconds(t3), 45678000); UtAssert_UINT32_EQ(OS_TimeGetTotalNanoseconds(t4), 1901000000); /* These functions only return the fractional part, not the whole part */ - UtAssert_UINT32_EQ(OS_TimeGetSubsecondsPart(t1), 0x3c0c953a); - UtAssert_UINT32_EQ(OS_TimeGetSubsecondsPart(t2), 0x87653438); + UtAssert_UINT32_EQ(OS_TimeGetSubsecondsPart(t1), 0x3c0ca2a6); + UtAssert_UINT32_EQ(OS_TimeGetSubsecondsPart(t2), 0x876541a4); UtAssert_UINT32_EQ(OS_TimeGetSubsecondsPart(t3), 0x0bb18dad); UtAssert_UINT32_EQ(OS_TimeGetSubsecondsPart(t4), 0xe6a7ef9e); @@ -139,8 +139,8 @@ void Test_OS_TimeAccessConversions(void) UtAssert_UINT32_EQ(OS_TimeGetMicrosecondsPart(t3), 45678); UtAssert_UINT32_EQ(OS_TimeGetMicrosecondsPart(t4), 901000); - UtAssert_UINT32_EQ(OS_TimeGetNanosecondsPart(t1), 234567000); - UtAssert_UINT32_EQ(OS_TimeGetNanosecondsPart(t2), 528888000); + UtAssert_UINT32_EQ(OS_TimeGetNanosecondsPart(t1), 234567800); + UtAssert_UINT32_EQ(OS_TimeGetNanosecondsPart(t2), 528888800); UtAssert_UINT32_EQ(OS_TimeGetNanosecondsPart(t3), 45678000); UtAssert_UINT32_EQ(OS_TimeGetNanosecondsPart(t4), 901000000);