diff --git a/examples/platforms/simulation/radio.c b/examples/platforms/simulation/radio.c index 5d5cc3d5c..2b007b677 100644 --- a/examples/platforms/simulation/radio.c +++ b/examples/platforms/simulation/radio.c @@ -1041,7 +1041,7 @@ exit: } #endif -uint64_t otPlatRadioGetNow(otInstance *aInstance) +otRadioTime64 otPlatRadioGetNow(otInstance *aInstance) { OT_UNUSED_VARIABLE(aInstance); @@ -1109,7 +1109,7 @@ otError otPlatRadioResetCsl(otInstance *aInstance) return OT_ERROR_NONE; } -void otPlatRadioUpdateCslSampleTime(otInstance *aInstance, uint32_t aCslSampleTime) +void otPlatRadioUpdateCslSampleTime(otInstance *aInstance, otRadioTime32 aCslSampleTime) { OT_UNUSED_VARIABLE(aInstance); diff --git a/examples/platforms/utils/mac_frame.h b/examples/platforms/utils/mac_frame.h index fe46dbcc4..b4b09a616 100644 --- a/examples/platforms/utils/mac_frame.h +++ b/examples/platforms/utils/mac_frame.h @@ -345,7 +345,7 @@ typedef struct otRadioContext otExtAddress mExtAddress; ///< In little-endian byte order. uint32_t mMacFrameCounter; uint32_t mPrevMacFrameCounter; - uint32_t mCslSampleTime; ///< The sample time based on the microsecond timer. + otRadioTime32 mCslSampleTime; ///< The sample time based on the microsecond timer. uint16_t mCslPeriod; ///< In unit of 10 symbols. otShortAddress mCslShortAddress; ///< The short address of the CSL receiver's peer. otExtAddress mCslExtAddress; ///< The extended address of the CSL receiver's peer. diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 1a9f14415..c6e7c3692 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -52,7 +52,7 @@ extern "C" { * * @note This number versions both OpenThread platform and user APIs. */ -#define OPENTHREAD_API_VERSION (609) +#define OPENTHREAD_API_VERSION (610) /** * @addtogroup api-instance diff --git a/include/openthread/platform/radio.h b/include/openthread/platform/radio.h index 27550a1f8..abcbe937f 100644 --- a/include/openthread/platform/radio.h +++ b/include/openthread/platform/radio.h @@ -183,6 +183,25 @@ struct otExtAddress */ typedef struct otExtAddress otExtAddress; +/** + * Represents a 64-bit radio time in microseconds referenced to a continuous monotonic local radio clock. + * + * This type is returned by `otPlatRadioGetNow()` and is used as the timestamp field (`mTimestamp`) in radio frames + * (`otRadioFrame`). + */ +typedef uint64_t otRadioTime64; + +/** + * Represents a 32-bit radio time in microseconds. + * + * This type holds the lower 32 bits (least significant bits) of a full 64-bit radio time (`otRadioTime64`). + * + * It is used in APIs such as `otPlatRadioReceiveAt()` and `otPlatRadioUpdateCslSampleTime()` and as the transmission + * delay base time (`mTxDelayBaseTime`) in `otRadioFrame`. It is important for radio platform implementations to + * correctly account for its roll-over. + */ +typedef uint32_t otRadioTime32; + #define OT_MAC_KEY_SIZE 16 ///< Size of the MAC Key in bytes. /** @@ -273,7 +292,7 @@ typedef struct otRadioFrame * * This field does not affect CCA behavior which is controlled by `mCsmaCaEnabled`. */ - uint32_t mTxDelayBaseTime; + otRadioTime32 mTxDelayBaseTime; /** * The delay time in microseconds for this transmission referenced @@ -381,7 +400,7 @@ typedef struct otRadioFrame * * The platform should update this field before otPlatRadioTxStarted() is fired for each transmit attempt. */ - uint64_t mTimestamp; + otRadioTime64 mTimestamp; } mTxInfo; /** @@ -393,7 +412,7 @@ typedef struct otRadioFrame * The time of the local radio clock in microseconds when the end of * the SFD was present at the local antenna. */ - uint64_t mTimestamp; + otRadioTime64 mTimestamp; uint32_t mAckFrameCounter; ///< ACK security frame counter (applicable when `mAckedWithSecEnhAck` is set). uint8_t mAckKeyId; ///< ACK security key index (applicable when `mAckedWithSecEnhAck` is set). @@ -765,7 +784,7 @@ void otPlatRadioSetMacFrameCounterIfLarger(otInstance *aInstance, uint32_t aMacF * @returns The current time in microseconds. UINT64_MAX when platform does not * support or radio time is not ready. */ -uint64_t otPlatRadioGetNow(otInstance *aInstance); +otRadioTime64 otPlatRadioGetNow(otInstance *aInstance); /** * Get the bus speed in bits/second between the host and the radio chip. @@ -894,7 +913,7 @@ otError otPlatRadioReceive(otInstance *aInstance, uint8_t aChannel); * @retval OT_ERROR_NONE Successfully scheduled receive window. * @retval OT_ERROR_FAILED The receive window could not be scheduled. For example, if @p aStart is in the past. */ -otError otPlatRadioReceiveAt(otInstance *aInstance, uint8_t aChannel, uint32_t aStart, uint32_t aDuration); +otError otPlatRadioReceiveAt(otInstance *aInstance, uint8_t aChannel, otRadioTime32 aStart, uint32_t aDuration); /** * The radio driver calls this function to notify OpenThread of a received frame. @@ -1225,7 +1244,7 @@ otError otPlatRadioResetCsl(otInstance *aInstance); * the time when the first symbol of the MHR of * the frame is expected. */ -void otPlatRadioUpdateCslSampleTime(otInstance *aInstance, uint32_t aCslSampleTime); +void otPlatRadioUpdateCslSampleTime(otInstance *aInstance, otRadioTime32 aCslSampleTime); /** * Get the current estimated worst case accuracy (maximum ± deviation from the diff --git a/src/core/BUILD.gn b/src/core/BUILD.gn index 921164396..0ac206a7e 100644 --- a/src/core/BUILD.gn +++ b/src/core/BUILD.gn @@ -663,6 +663,8 @@ openthread_core_files = [ "radio/radio.hpp", "radio/radio_callbacks.cpp", "radio/radio_platform.cpp", + "radio/radio_types.cpp", + "radio/radio_types.hpp", "radio/trel_interface.cpp", "radio/trel_interface.hpp", "radio/trel_link.cpp", @@ -857,6 +859,7 @@ openthread_radio_sources = [ "radio/radio.cpp", "radio/radio_callbacks.cpp", "radio/radio_platform.cpp", + "radio/radio_types.cpp", "thread/link_quality.cpp", "utils/parse_cmdline.cpp", "utils/power_calibration.cpp", diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index c970102e5..277d4f3b9 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -224,6 +224,7 @@ set(COMMON_SOURCES radio/radio.cpp radio/radio_callbacks.cpp radio/radio_platform.cpp + radio/radio_types.cpp radio/trel_interface.cpp radio/trel_link.cpp radio/trel_packet.cpp @@ -339,6 +340,7 @@ set(RADIO_COMMON_SOURCES radio/radio.cpp radio/radio_callbacks.cpp radio/radio_platform.cpp + radio/radio_types.cpp thread/link_quality.cpp utils/otns.cpp utils/parse_cmdline.cpp diff --git a/src/core/mac/mac.cpp b/src/core/mac/mac.cpp index 2695588d1..827432e1c 100644 --- a/src/core/mac/mac.cpp +++ b/src/core/mac/mac.cpp @@ -2498,7 +2498,7 @@ void Mac::ProcessCsl(const RxFrame &aFrame, const Address &aSrcAddr) neighbor->SetCslLastHeard(TimerMilli::GetNow()); neighbor->SetLastRxTimestamp(aFrame.GetTimestamp()); LogDebg("Timestamp=%lu Sequence=%u CslPeriod=%u CslPhase=%u TransmitPhase=%u", - ToUlong(static_cast(aFrame.GetTimestamp())), aFrame.GetSequence(), csl->GetPeriod(), + ToUlong(ConvertRadioTime64To32(aFrame.GetTimestamp())), aFrame.GetSequence(), csl->GetPeriod(), csl->GetPhase(), neighbor->GetCslPhase()); #if OPENTHREAD_FTD @@ -2622,9 +2622,9 @@ Error Mac::HandleWakeupFrame(const RxFrame &aFrame) const ConnectionIe *connectionIe; Address srcAddress; WakeupInfo wakeupInfo; - uint32_t rvTimeUs; - uint64_t rvTimestampUs; - uint64_t radioNowUs; + RadioTime32 rvTimeUs; + RadioTime64 rvTimestampUs; + RadioTime64 radioNowUs; VerifyOrExit(mWakeupListenEnabled && aFrame.IsWakeupFrame()); @@ -2637,13 +2637,13 @@ Error Mac::HandleWakeupFrame(const RxFrame &aFrame) wakeupInfo.mRetryCount = connectionIe->GetRetryCount(); VerifyOrExit(wakeupInfo.mRetryInterval > 0 && wakeupInfo.mRetryCount > 0, error = kErrorInvalidArgs); - radioNowUs = otPlatRadioGetNow(&GetInstance()); + radioNowUs = Get().GetNow(); rvTimeUs = aFrame.Find()->GetRendezvousTime() * kUsPerTenSymbols; rvTimestampUs = aFrame.GetTimestamp() + kRadioHeaderPhrDuration + aFrame.GetLength() * kOctetDuration + rvTimeUs; if (rvTimestampUs > radioNowUs + kCslRequestAhead) { - wakeupInfo.mAttachDelayMs = static_cast(rvTimestampUs - radioNowUs - kCslRequestAhead); + wakeupInfo.mAttachDelayMs = ConvertRadioTime64To32(rvTimestampUs - radioNowUs - kCslRequestAhead); wakeupInfo.mAttachDelayMs = wakeupInfo.mAttachDelayMs / Time::kOneMsecInUsec; } else diff --git a/src/core/mac/mac_frame.hpp b/src/core/mac/mac_frame.hpp index d992dffb6..4a281343b 100644 --- a/src/core/mac/mac_frame.hpp +++ b/src/core/mac/mac_frame.hpp @@ -43,6 +43,7 @@ #include "mac/mac_header_ie.hpp" #include "mac/mac_types.hpp" #include "meshcop/network_name.hpp" +#include "radio/radio_types.hpp" namespace ot { namespace Mac { @@ -945,7 +946,7 @@ public: * * @returns The timestamp in microseconds. */ - const uint64_t &GetTimestamp(void) const { return mInfo.mRxInfo.mTimestamp; } + const RadioTime64 &GetTimestamp(void) const { return mInfo.mRxInfo.mTimestamp; } /** * Performs AES CCM on the frame which is received. @@ -1283,6 +1284,13 @@ public: #endif #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 + /** + * Gets the TX delay field for the frame. + * + * @returns The delay time for the TX frame in microseconds. + */ + uint32_t GetTxDelay(void) const { return mInfo.mTxInfo.mTxDelay; } + /** * Set TX delay field for the frame. * @@ -1290,12 +1298,19 @@ public: */ void SetTxDelay(uint32_t aTxDelay) { mInfo.mTxInfo.mTxDelay = aTxDelay; } + /** + * Gets the TX delay base time field for the frame. + * + * @returns The delay base time for the TX frame as a `RadioTime32`. + */ + RadioTime32 GetTxDelayBaseTime(void) const { return mInfo.mTxInfo.mTxDelayBaseTime; } + /** * Set TX delay base time field for the frame. * * @param[in] aTxDelayBaseTime The delay base time for the TX frame. */ - void SetTxDelayBaseTime(uint32_t aTxDelayBaseTime) { mInfo.mTxInfo.mTxDelayBaseTime = aTxDelayBaseTime; } + void SetTxDelayBaseTime(RadioTime32 aTxDelayBaseTime) { mInfo.mTxInfo.mTxDelayBaseTime = aTxDelayBaseTime; } #endif }; diff --git a/src/core/mac/sub_mac.cpp b/src/core/mac/sub_mac.cpp index 75f712cb6..299cfc86a 100644 --- a/src/core/mac/sub_mac.cpp +++ b/src/core/mac/sub_mac.cpp @@ -427,32 +427,29 @@ void SubMac::StartCsmaBackoff(void) uint8_t backoffExponent = kCsmaMinBe + mCsmaBackoffs; #if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE - if (mTransmitFrame.mInfo.mTxInfo.mTxDelay != 0 || mTransmitFrame.mInfo.mTxInfo.mTxDelayBaseTime != 0) + if (mTransmitFrame.GetTxDelay() != 0 || mTransmitFrame.GetTxDelayBaseTime() != 0) { SetState(kStateCslTransmit); if (ShouldHandleTransmitTargetTime()) { static constexpr uint32_t kAheadTime = kCcaSampleInterval + kCslTransmitTimeAhead + kRadioHeaderShrDuration; - Time txStartTime = Time(mTransmitFrame.mInfo.mTxInfo.mTxDelayBaseTime); - Time radioNow = Time(static_cast(Get().GetNow())); + + RadioTime32 radioNow = Get().GetNowAsRadioTime32(); + RadioTime32 txStartTime = mTransmitFrame.GetTxDelayBaseTime(); txStartTime += (mTransmitFrame.mInfo.mTxInfo.mTxDelay - kAheadTime); - if (radioNow < txStartTime) + if (IsRadioTimeStrictlyBefore(radioNow, txStartTime)) { StartTimer(txStartTime - radioNow); + ExitNow(); } - else // Transmit without delay - { - BeginTransmit(); - } - } - else - { - BeginTransmit(); + + // Transmit without delay } + BeginTransmit(); ExitNow(); } #endif // !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE diff --git a/src/core/mac/sub_mac.hpp b/src/core/mac/sub_mac.hpp index e05a6a46c..9fb76b7b7 100644 --- a/src/core/mac/sub_mac.hpp +++ b/src/core/mac/sub_mac.hpp @@ -677,7 +677,7 @@ private: bool mIsCslSampling : 1; // Indicates that the current time is in CSL sample window // for platforms not supporting `Radio::ReceiveAt()`. uint16_t mCslPeerShort; // The CSL peer short address. - uint32_t mCslSampleTimeRadio; // The CSL sample time of the current period based on radio time (lower 32-bit). + RadioTime32 mCslSampleTimeRadio; // The CSL sample time of the current period based on radio time (lower 32-bit). TimeMicro mCslSampleTimeLocal; // The CSL sample time of the current period based on local time. TimeMicro mCslLastSync; // The timestamp of the last successful CSL synchronization. CslAccuracy mCslParentAccuracy; // The parent's CSL accuracy (clock accuracy and uncertainty). @@ -685,15 +685,15 @@ private: #endif #if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE - bool mIsWedSampling : 1; // Indicates that the current time is in WED's sample window - // for platforms not supporting `Radio::ReceiveAt()`. - bool mIsWedEnabled : 1; // Indicates if the WED is enabled. - uint32_t mWakeupListenInterval; // The wake-up listen interval, in microseconds. - uint32_t mWakeupListenDuration; // The wake-up listen duration, in microseconds. - uint8_t mWakeupChannel; // The wake-up sample channel. - TimeMicro mWedSampleTime; // The WED sample time of the current interval in local time. - uint64_t mWedSampleTimeRadio; // The WED sample time of the current interval in radio time. - TimerMicro mWedTimer; + bool mIsWedSampling : 1; // Indicates that the current time is in WED's sample window + // for platforms not supporting `Radio::ReceiveAt()`. + bool mIsWedEnabled : 1; // Indicates if the WED is enabled. + uint32_t mWakeupListenInterval; // The wake-up listen interval, in microseconds. + uint32_t mWakeupListenDuration; // The wake-up listen duration, in microseconds. + uint8_t mWakeupChannel; // The wake-up sample channel. + TimeMicro mWedSampleTime; // The WED sample time of the current interval in local time. + RadioTime64 mWedSampleTimeRadio; // The WED sample time of the current interval in radio time. + TimerMicro mWedTimer; #endif }; diff --git a/src/core/mac/sub_mac_csl_receiver.cpp b/src/core/mac/sub_mac_csl_receiver.cpp index 1b15758ab..34f7aa833 100644 --- a/src/core/mac/sub_mac_csl_receiver.cpp +++ b/src/core/mac/sub_mac_csl_receiver.cpp @@ -99,7 +99,7 @@ void SubMac::UpdateCslLastSyncTimestamp(RxFrame *aFrame, Error aError) #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_LOCAL_TIME_SYNC mCslLastSync = TimerMicro::GetNow(); #else - mCslLastSync = TimeMicro(static_cast(aFrame->mInfo.mRxInfo.mTimestamp)); + mCslLastSync = TimeMicro(ConvertRadioTime64To32(aFrame->GetTimestamp())); #endif } @@ -129,7 +129,7 @@ void SubMac::SetCslParams(uint16_t aPeriod, uint8_t aChannel, ShortAddress aShor mCslTimer.Stop(); if (mCslPeriod > 0) { - mCslSampleTimeRadio = static_cast(Get().GetNow()); + mCslSampleTimeRadio = Get().GetNowAsRadioTime32(); mCslSampleTimeLocal = TimerMicro::GetNow(); // Update CSL sync time whenever CSL parameters are re-initialized. mCslLastSync = mCslSampleTimeLocal; @@ -180,9 +180,9 @@ void SubMac::HandleCslReceiveAt(uint32_t aTimeAhead, uint32_t aTimeAfter) * x-|------------|-------------------------------------x-|------------|---------------------------------------| * sample sleep sample sleep */ - uint32_t periodUs = mCslPeriod * kUsPerTenSymbols; - uint32_t winStart; - uint32_t winDuration; + uint32_t periodUs = mCslPeriod * kUsPerTenSymbols; + RadioTime32 winStart; + uint32_t winDuration; mCslTimer.FireAt(mCslSampleTimeLocal + periodUs - aTimeAhead - GetNextCycleDrift()); aTimeAhead -= kCslReceiveTimeAhead; @@ -290,7 +290,7 @@ uint32_t SubMac::GetLocalTime(void) #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_LOCAL_TIME_SYNC now = TimerMicro::GetNow().GetValue(); #else - now = static_cast(Get().GetNow()); + now = Get().GetNowAsRadioTime32(); #endif return now; @@ -321,8 +321,7 @@ void SubMac::LogReceived(RxFrame *aFrame) (dst.GetType() == Address::kTypeExtended && dst.GetExtended() == GetExtAddress())); LogDebg("Received frame in state (SubMac %s, CSL %s), timestamp %lu", StateToString(mState), - mIsCslSampling ? "CslSample" : "CslSleep", - ToUlong(static_cast(aFrame->mInfo.mRxInfo.mTimestamp))); + mIsCslSampling ? "CslSample" : "CslSleep", ToUlong(ConvertRadioTime64To32(aFrame->GetTimestamp()))); VerifyOrExit(mState == kStateRadioSample); @@ -330,7 +329,7 @@ void SubMac::LogReceived(RxFrame *aFrame) ahead -= kMinReceiveOnAhead + kCslReceiveTimeAhead; sampleTime = mCslSampleTimeRadio - mCslPeriod * kUsPerTenSymbols; - deviation = static_cast(aFrame->mInfo.mRxInfo.mTimestamp) + kRadioHeaderPhrDuration - sampleTime; + deviation = ConvertRadioTime64To32(aFrame->GetTimestamp()) + kRadioHeaderPhrDuration - sampleTime; // This logs three values (all in microseconds): // - Absolute sample time in which the CSL receiver expected the MHR of the received frame. diff --git a/src/core/mac/sub_mac_wed.cpp b/src/core/mac/sub_mac_wed.cpp index 9cbd32560..96e97cce9 100644 --- a/src/core/mac/sub_mac_wed.cpp +++ b/src/core/mac/sub_mac_wed.cpp @@ -96,7 +96,7 @@ void SubMac::HandleWedReceiveAt(void) if (mState != kStateDisabled) { IgnoreError( - Get().ReceiveAt(mWakeupChannel, static_cast(mWedSampleTimeRadio), mWakeupListenDuration)); + Get().ReceiveAt(mWakeupChannel, ConvertRadioTime64To32(mWedSampleTimeRadio), mWakeupListenDuration)); } } diff --git a/src/core/mac/wakeup_tx_scheduler.cpp b/src/core/mac/wakeup_tx_scheduler.cpp index 4ee3024f1..16bc56149 100644 --- a/src/core/mac/wakeup_tx_scheduler.cpp +++ b/src/core/mac/wakeup_tx_scheduler.cpp @@ -100,7 +100,7 @@ Mac::TxFrame *WakeupTxScheduler::PrepareWakeupFrame(Mac::TxFrames &aTxFrames) VerifyOrExit(frame->GenerateWakeupFrame(Get().GetPanId(), mWakeupRequest, source) == kErrorNone, frame = nullptr); - frame->SetTxDelayBaseTime(static_cast(Get().GetNow())); + frame->SetTxDelayBaseTime(Get().GetNowAsRadioTime32()); frame->SetTxDelay(radioTxDelay); frame->SetCsmaCaEnabled(kWakeupFrameTxCca); frame->SetMaxCsmaBackoffs(0); diff --git a/src/core/radio/radio.cpp b/src/core/radio/radio.cpp index 05ed0c1dd..191460364 100644 --- a/src/core/radio/radio.cpp +++ b/src/core/radio/radio.cpp @@ -128,6 +128,8 @@ Error Radio::Transmit(Mac::TxFrame &aFrame) } #endif // OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE +//--------------------------------------------------------------------------------------------------------------------- + #if OPENTHREAD_CONFIG_RADIO_STATS_ENABLE && (OPENTHREAD_FTD || OPENTHREAD_MTD) inline uint64_t UintSafeMinus(uint64_t aLhs, uint64_t aRhs) { return aLhs > aRhs ? (aLhs - aRhs) : 0; } diff --git a/src/core/radio/radio.hpp b/src/core/radio/radio.hpp index 2da8c8286..bfb8ad1a5 100644 --- a/src/core/radio/radio.hpp +++ b/src/core/radio/radio.hpp @@ -46,6 +46,7 @@ #include "common/numeric_limits.hpp" #include "common/time.hpp" #include "mac/mac_frame.hpp" +#include "radio/radio_types.hpp" namespace ot { @@ -523,7 +524,7 @@ public: * @retval kErrorNone Successfully scheduled receive window. * @retval kErrorFailed The receive window could not be scheduled. */ - Error ReceiveAt(uint8_t aChannel, uint32_t aStart, uint32_t aDuration); + Error ReceiveAt(uint8_t aChannel, RadioTime32 aStart, uint32_t aDuration); #endif #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE @@ -532,7 +533,7 @@ public: * * @param[in] aCslSampleTime The CSL sample time. */ - void UpdateCslSampleTime(uint32_t aCslSampleTime); + void UpdateCslSampleTime(RadioTime32 aCslSampleTime); /** * Enables CSL sampling in radio. @@ -567,7 +568,14 @@ public: * * @returns The current radio clock time. */ - uint64_t GetNow(void); + RadioTime64 GetNow(void); + + /** + * Get the current radio time in microseconds as a 32-bit value (lower 32 bits of the full radio time). + * + * @returns The current radio clock time as a `RadioTime32`. + */ + RadioTime32 GetNowAsRadioTime32(void) { return ConvertRadioTime64To32(GetNow()); } /** * Get the current accuracy, in units of ± ppm, of the clock used for scheduling CSL operations. @@ -984,7 +992,7 @@ inline Error Radio::Receive(uint8_t aChannel) } #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE || OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE -inline Error Radio::ReceiveAt(uint8_t aChannel, uint32_t aStart, uint32_t aDuration) +inline Error Radio::ReceiveAt(uint8_t aChannel, RadioTime32 aStart, uint32_t aDuration) { Error error = otPlatRadioReceiveAt(GetInstancePtr(), aChannel, aStart, aDuration); #if OPENTHREAD_CONFIG_RADIO_STATS_ENABLE && (OPENTHREAD_FTD || OPENTHREAD_MTD) @@ -998,7 +1006,7 @@ inline Error Radio::ReceiveAt(uint8_t aChannel, uint32_t aStart, uint32_t aDurat #endif #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE -inline void Radio::UpdateCslSampleTime(uint32_t aCslSampleTime) +inline void Radio::UpdateCslSampleTime(RadioTime32 aCslSampleTime) { otPlatRadioUpdateCslSampleTime(GetInstancePtr(), aCslSampleTime); } @@ -1013,7 +1021,7 @@ inline Error Radio::ResetCsl(void) { return otPlatRadioResetCsl(GetInstancePtr() #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE || OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE || \ OPENTHREAD_CONFIG_TIME_SYNC_ENABLE -inline uint64_t Radio::GetNow(void) { return otPlatRadioGetNow(GetInstancePtr()); } +inline RadioTime64 Radio::GetNow(void) { return otPlatRadioGetNow(GetInstancePtr()); } inline uint8_t Radio::GetCslAccuracy(void) { return otPlatRadioGetCslAccuracy(GetInstancePtr()); } @@ -1112,7 +1120,7 @@ inline Error Radio::ReceiveAt(uint8_t, uint32_t, uint32_t) { return kErrorNone; #endif #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE -inline void Radio::UpdateCslSampleTime(uint32_t) {} +inline void Radio::UpdateCslSampleTime(RadioTime32) {} inline Error Radio::EnableCsl(uint32_t, Mac::ShortAddress, const Mac::ExtAddress &) { return kErrorNotImplemented; } @@ -1121,7 +1129,7 @@ inline Error Radio::ResetCsl(void) { return kErrorNotImplemented; } #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE || OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE || \ OPENTHREAD_CONFIG_TIME_SYNC_ENABLE -inline uint64_t Radio::GetNow(void) { return NumericLimits::kMax; } +inline RadioTime64 Radio::GetNow(void) { return NumericLimits::kMax; } inline uint8_t Radio::GetCslAccuracy(void) { return NumericLimits::kMax; } diff --git a/src/core/radio/radio_platform.cpp b/src/core/radio/radio_platform.cpp index 976f8abd4..fb09cdb70 100644 --- a/src/core/radio/radio_platform.cpp +++ b/src/core/radio/radio_platform.cpp @@ -281,7 +281,7 @@ extern "C" OT_TOOL_WEAK void otPlatRadioSetMacFrameCounterIfLarger(otInstance *a extern "C" OT_TOOL_WEAK uint64_t otPlatTimeGet(void) { return UINT64_MAX; } -extern "C" OT_TOOL_WEAK uint64_t otPlatRadioGetNow(otInstance *aInstance) +extern "C" OT_TOOL_WEAK otRadioTime64 otPlatRadioGetNow(otInstance *aInstance) { OT_UNUSED_VARIABLE(aInstance); @@ -366,10 +366,10 @@ extern "C" OT_TOOL_WEAK otError otPlatRadioGetRegion(otInstance *aInstance, uint return kErrorNotImplemented; } -extern "C" OT_TOOL_WEAK otError otPlatRadioReceiveAt(otInstance *aInstance, - uint8_t aChannel, - uint32_t aStart, - uint32_t aDuration) +extern "C" OT_TOOL_WEAK otError otPlatRadioReceiveAt(otInstance *aInstance, + uint8_t aChannel, + otRadioTime32 aStart, + uint32_t aDuration) { OT_UNUSED_VARIABLE(aInstance); OT_UNUSED_VARIABLE(aChannel); diff --git a/src/core/radio/radio_types.cpp b/src/core/radio/radio_types.cpp new file mode 100644 index 000000000..5ec6d5090 --- /dev/null +++ b/src/core/radio/radio_types.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2026, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file includes implementation of OpenThread radio types. + */ + +#include "radio_types.hpp" + +#include "common/time.hpp" + +namespace ot { + +bool IsRadioTimeStrictlyBefore(RadioTime32 aFirstTime, RadioTime32 aSecondTime) +{ + Time firstTime(aFirstTime); + Time secondTime(aSecondTime); + + return (firstTime < secondTime); +} + +} // namespace ot diff --git a/src/core/radio/radio_types.hpp b/src/core/radio/radio_types.hpp new file mode 100644 index 000000000..5cf03f8a3 --- /dev/null +++ b/src/core/radio/radio_types.hpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2026, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file includes definitions for OpenThread radio types. + */ + +#ifndef OT_CORE_RADIO_RADIO_TYPES_HPP_ +#define OT_CORE_RADIO_RADIO_TYPES_HPP_ + +#include "openthread-core-config.h" + +#include + +namespace ot { + +/** + * Represents a 64-bit radio time in microseconds referenced to a continuous monotonic local radio clock. + */ +typedef otRadioTime64 RadioTime64; + +/** + * Represents a 32-bit radio time in microseconds (holds the lower 32 bits of a `RadioTime64`). + */ +typedef otRadioTime32 RadioTime32; + +/** + * Converts a 64-bit radio time to a 32-bit radio time. + * + * @param[in] aRadioTime64 The 64-bit radio time to convert. + * + * @returns The converted 32-bit radio time (lower 32 bits of @p aRadioTime64). + */ +inline RadioTime32 ConvertRadioTime64To32(RadioTime64 aRadioTime64) { return static_cast(aRadioTime64); } + +/** + * Indicates whether a given 32-bit radio time is strictly before another 32-bit radio time. + * + * This function correctly accounts for 32-bit microsecond counter roll-over. + * + * @param[in] aFirstTime The first 32-bit radio time to compare. + * @param[in] aSecondTime The second 32-bit radio time to compare. + * + * @retval TRUE @p aFirstTime is strictly before @p aSecondTime. + * @retval FALSE @p aFirstTime is not strictly before @p aSecondTime. + */ +bool IsRadioTimeStrictlyBefore(RadioTime32 aFirstTime, RadioTime32 aSecondTime); + +} // namespace ot + +#endif // OT_CORE_RADIO_RADIO_TYPES_HPP_ diff --git a/src/core/thread/csl_tx_scheduler.cpp b/src/core/thread/csl_tx_scheduler.cpp index 6e4ba0832..9b2a89d5c 100644 --- a/src/core/thread/csl_tx_scheduler.cpp +++ b/src/core/thread/csl_tx_scheduler.cpp @@ -135,12 +135,12 @@ uint32_t CslTxScheduler::GetNextCslTransmissionDelay(const CslNeighbor &aCslNeig uint32_t &aDelayFromLastRx, uint32_t aAheadUs) const { - uint64_t radioNow = Get().GetNow(); - uint32_t periodInUs = aCslNeighbor.GetCslPeriod() * kUsPerTenSymbols; + // See CslTxScheduler::NeighborInfo::mCslPhase - /* see CslTxScheduler::NeighborInfo::mCslPhase */ - uint64_t firstTxWindow = aCslNeighbor.GetLastRxTimestamp() + aCslNeighbor.GetCslPhase() * kUsPerTenSymbols; - uint64_t nextTxWindow = radioNow - (radioNow % periodInUs) + (firstTxWindow % periodInUs); + RadioTime64 radioNow = Get().GetNow(); + uint32_t periodInUs = aCslNeighbor.GetCslPeriod() * kUsPerTenSymbols; + RadioTime64 firstTxWindow = aCslNeighbor.GetLastRxTimestamp() + aCslNeighbor.GetCslPhase() * kUsPerTenSymbols; + RadioTime64 nextTxWindow = radioNow - (radioNow % periodInUs) + (firstTxWindow % periodInUs); while (nextTxWindow < radioNow + aAheadUs) { @@ -222,8 +222,7 @@ Mac::TxFrame *CslTxScheduler::HandleFrameRequest(Mac::TxFrames &aTxFrames) VerifyOrExit(delay <= mCslFrameRequestAheadUs + kFramePreparationGuardInterval, frame = nullptr); frame->SetTxDelay(txDelay); - frame->SetTxDelayBaseTime( - static_cast(mCslTxNeighbor->GetLastRxTimestamp())); // Only LSB part of the time is required. + frame->SetTxDelayBaseTime(ConvertRadioTime64To32(mCslTxNeighbor->GetLastRxTimestamp())); frame->SetCsmaCaEnabled(true); exit: diff --git a/src/core/thread/csl_tx_scheduler.hpp b/src/core/thread/csl_tx_scheduler.hpp index 78e655627..85fa43b28 100644 --- a/src/core/thread/csl_tx_scheduler.hpp +++ b/src/core/thread/csl_tx_scheduler.hpp @@ -95,8 +95,8 @@ public: TimeMilli GetCslLastHeard(void) const { return mCslLastHeard; } void SetCslLastHeard(TimeMilli aCslLastHeard) { mCslLastHeard = aCslLastHeard; } - uint64_t GetLastRxTimestamp(void) const { return mLastRxTimestamp; } - void SetLastRxTimestamp(uint64_t aLastRxTimestamp) { mLastRxTimestamp = aLastRxTimestamp; } + RadioTime64 GetLastRxTimestamp(void) const { return mLastRxTimestamp; } + void SetLastRxTimestamp(RadioTime64 aLastRxTimestamp) { mLastRxTimestamp = aLastRxTimestamp; } private: uint8_t mCslTxAttempts : 7; ///< Number of CSL triggered tx attempts. diff --git a/src/lib/spinel/radio_spinel.cpp b/src/lib/spinel/radio_spinel.cpp index 8c09a97b7..fa83bf938 100644 --- a/src/lib/spinel/radio_spinel.cpp +++ b/src/lib/spinel/radio_spinel.cpp @@ -1627,15 +1627,15 @@ otError RadioSpinel::Transmit(otRadioFrame &aFrame) #if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT && OPENTHREAD_CONFIG_TIME_SYNC_ENABLE if (mTransmitFrame->mInfo.mTxInfo.mIeInfo->mTimeIeOffset != 0) { - uint64_t netRadioTime = otPlatRadioGetNow(mInstance); - uint64_t netSyncTime; - uint8_t *timeIe = mTransmitFrame->mPsdu + mTransmitFrame->mInfo.mTxInfo.mIeInfo->mTimeIeOffset; + otRadioTime64 netRadioTime = otPlatRadioGetNow(mInstance); + otRadioTime64 netSyncTime; + uint8_t *timeIe = mTransmitFrame->mPsdu + mTransmitFrame->mInfo.mTxInfo.mIeInfo->mTimeIeOffset; if (netRadioTime == UINT64_MAX) { // If we can't get the radio time, get the platform time - netSyncTime = static_cast(static_cast(otPlatTimeGet()) + - mTransmitFrame->mInfo.mTxInfo.mIeInfo->mNetworkTimeOffset); + netSyncTime = static_cast(static_cast(otPlatTimeGet()) + + mTransmitFrame->mInfo.mTxInfo.mIeInfo->mNetworkTimeOffset); } else { @@ -1644,16 +1644,16 @@ otError RadioSpinel::Transmit(otRadioFrame &aFrame) // If supported, add a delay and transmit the network time at a precise moment #if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE transmitDelay = kTxWaitUs / 10; - mTransmitFrame->mInfo.mTxInfo.mTxDelayBaseTime = static_cast(netRadioTime); + mTransmitFrame->mInfo.mTxInfo.mTxDelayBaseTime = static_cast(netRadioTime); mTransmitFrame->mInfo.mTxInfo.mTxDelay = transmitDelay; #endif - netSyncTime = static_cast(static_cast(netRadioTime) + transmitDelay + - mTransmitFrame->mInfo.mTxInfo.mIeInfo->mNetworkTimeOffset); + netSyncTime = static_cast(static_cast(netRadioTime) + transmitDelay + + mTransmitFrame->mInfo.mTxInfo.mIeInfo->mNetworkTimeOffset); } *(timeIe++) = mTransmitFrame->mInfo.mTxInfo.mIeInfo->mTimeSyncSeq; - for (uint8_t i = 0; i < sizeof(uint64_t); i++) + for (uint8_t i = 0; i < sizeof(otRadioTime64); i++) { *(timeIe++) = static_cast(netSyncTime & 0xff); netSyncTime = netSyncTime >> 8; diff --git a/src/ncp/ncp_base.cpp b/src/ncp/ncp_base.cpp index 23d80f2b4..1fbdec903 100644 --- a/src/ncp/ncp_base.cpp +++ b/src/ncp/ncp_base.cpp @@ -1709,8 +1709,8 @@ template <> otError NcpBase::HandlePropertySet(void) SuccessOrExit(error = mDecoder.ReadUint8(channel)); { - uint64_t now = otPlatRadioGetNow(mInstance); - uint32_t start; + otRadioTime64 now = otPlatRadioGetNow(mInstance); + uint32_t start; VerifyOrExit(when > now && (when - now) < UINT32_MAX, error = OT_ERROR_INVALID_ARGS); diff --git a/src/posix/platform/radio.cpp b/src/posix/platform/radio.cpp index 05689f8d1..0804e1d4a 100644 --- a/src/posix/platform/radio.cpp +++ b/src/posix/platform/radio.cpp @@ -978,7 +978,7 @@ void otPlatRadioSetMacFrameCounterIfLarger(otInstance *aInstance, uint32_t aMacF OT_UNUSED_VARIABLE(aInstance); } -uint64_t otPlatRadioGetNow(otInstance *aInstance) +otRadioTime64 otPlatRadioGetNow(otInstance *aInstance) { OT_UNUSED_VARIABLE(aInstance); return GetRadioSpinel().GetNow(); @@ -1097,7 +1097,7 @@ otError otPlatRadioConfigureEnhAckProbing(otInstance *aInstance, } #endif -otError otPlatRadioReceiveAt(otInstance *aInstance, uint8_t aChannel, uint32_t aStart, uint32_t aDuration) +otError otPlatRadioReceiveAt(otInstance *aInstance, uint8_t aChannel, otRadioTime32 aStart, uint32_t aDuration) { OT_UNUSED_VARIABLE(aInstance); OT_UNUSED_VARIABLE(aChannel); diff --git a/tests/gtest/fake_platform.cpp b/tests/gtest/fake_platform.cpp index fe2b6d932..4ef602680 100644 --- a/tests/gtest/fake_platform.cpp +++ b/tests/gtest/fake_platform.cpp @@ -385,7 +385,7 @@ otError otPlatRadioSleep(otInstance *) { return OT_ERROR_NONE; } otError otPlatRadioReceive(otInstance *, uint8_t aChannel) { return FakePlatform::CurrentPlatform().Receive(aChannel); } -otError otPlatRadioReceiveAt(otInstance *, uint8_t aChannel, uint32_t aStart, uint32_t aDuration) +otError otPlatRadioReceiveAt(otInstance *, uint8_t aChannel, otRadioTime32 aStart, uint32_t aDuration) { return FakePlatform::CurrentPlatform().ReceiveAt(aChannel, aStart, aDuration); } diff --git a/tests/nexus/platform/nexus_radio.cpp b/tests/nexus/platform/nexus_radio.cpp index 45fed2c80..c78a63080 100644 --- a/tests/nexus/platform/nexus_radio.cpp +++ b/tests/nexus/platform/nexus_radio.cpp @@ -156,7 +156,7 @@ exit: return error; } -uint64_t otPlatRadioGetNow(otInstance *aInstance) +otRadioTime64 otPlatRadioGetNow(otInstance *aInstance) { OT_UNUSED_VARIABLE(aInstance); @@ -332,7 +332,7 @@ otError otPlatRadioResetCsl(otInstance *aInstance) return kErrorNone; } -void otPlatRadioUpdateCslSampleTime(otInstance *aInstance, uint32_t aCslSampleTime) +void otPlatRadioUpdateCslSampleTime(otInstance *aInstance, otRadioTime32 aCslSampleTime) { AsNode(aInstance).mRadio.mRadioContext.mCslSampleTime = aCslSampleTime; } diff --git a/tests/unit/test_platform.cpp b/tests/unit/test_platform.cpp index 4d0d8dbce..8a919dc8b 100644 --- a/tests/unit/test_platform.cpp +++ b/tests/unit/test_platform.cpp @@ -457,7 +457,7 @@ OT_TOOL_WEAK otError otPlatRadioEnableCsl(otInstance *, uint32_t, otShortAddress OT_TOOL_WEAK otError otPlatRadioResetCsl(otInstance *) { return OT_ERROR_NONE; } -OT_TOOL_WEAK void otPlatRadioUpdateCslSampleTime(otInstance *, uint32_t) {} +OT_TOOL_WEAK void otPlatRadioUpdateCslSampleTime(otInstance *, otRadioTime32) {} OT_TOOL_WEAK uint8_t otPlatRadioGetCslAccuracy(otInstance *) {