From a12ff0d0f54fd41954b45047fcdd08f302731c5f Mon Sep 17 00:00:00 2001 From: tanyanquan Date: Tue, 23 Dec 2025 05:55:41 +0800 Subject: [PATCH] [csl] account for accuracy drift for next CSL window (#11602) This commit improves the accuracy of CSL timer fire time by taking into account the accuracy drift introduced between the current CSL window and next CSL window. Original implementation: - calculate CSL window edges based on current elapsed time - CSL window edges used to obtain next CSL timer fireAt time - does not account for additional drift due to accuracy from now until next CSL window - undesired outcome: if CSL period is large (e.g. 10s) and accuracy is poor (e.g. 200ppm), then the additional accuracy drift could be significant (e.g. 2ms) and the SSED may wake up too late Suggested implementation: - calculate the additional expected drift between now and next CSL window (i.e. periodUs) and account for this difference by setting CSL timer to fireAt earlier --- src/core/mac/sub_mac.hpp | 1 + src/core/mac/sub_mac_csl_receiver.cpp | 18 +++++++++++++----- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/core/mac/sub_mac.hpp b/src/core/mac/sub_mac.hpp index 2e5298089..e05a6a46c 100644 --- a/src/core/mac/sub_mac.hpp +++ b/src/core/mac/sub_mac.hpp @@ -507,6 +507,7 @@ private: static void HandleCslTimer(Timer &aTimer); void HandleCslTimer(void); void GetCslWindowEdges(uint32_t &aAhead, uint32_t &aAfter); + uint32_t GetNextCycleDrift(void); uint32_t GetLocalTime(void); bool IsCslEnabled(void) const { return mCslPeriod > 0; } #if OPENTHREAD_CONFIG_MAC_CSL_DEBUG_ENABLE diff --git a/src/core/mac/sub_mac_csl_receiver.cpp b/src/core/mac/sub_mac_csl_receiver.cpp index 01ab254eb..bec526301 100644 --- a/src/core/mac/sub_mac_csl_receiver.cpp +++ b/src/core/mac/sub_mac_csl_receiver.cpp @@ -184,7 +184,7 @@ void SubMac::HandleCslReceiveAt(uint32_t aTimeAhead, uint32_t aTimeAfter) uint32_t winStart; uint32_t winDuration; - mCslTimer.FireAt(mCslSampleTimeLocal - aTimeAhead + periodUs); + mCslTimer.FireAt(mCslSampleTimeLocal + periodUs - aTimeAhead - GetNextCycleDrift()); aTimeAhead -= kCslReceiveTimeAhead; winStart = mCslSampleTimeRadio - aTimeAhead; winDuration = aTimeAhead + aTimeAfter; @@ -221,7 +221,7 @@ void SubMac::HandleCslReceiveOrSleep(uint32_t aTimeAhead, uint32_t aTimeAfter) if (mIsCslSampling) { mIsCslSampling = false; - mCslTimer.FireAt(mCslSampleTimeLocal - aTimeAhead); + mCslTimer.FireAt(mCslSampleTimeLocal - aTimeAhead - GetNextCycleDrift()); if (mState == kStateRadioSample) { LogDebg("CSL sleep %lu", ToUlong(mCslTimer.GetNow().GetValue())); @@ -266,15 +266,23 @@ void SubMac::GetCslWindowEdges(uint32_t &aAhead, uint32_t &aAfter) curTime = GetLocalTime(); elapsed = curTime - mCslLastSync.GetValue(); - semiWindow = - static_cast(static_cast(elapsed) * - (Get().GetCslAccuracy() + mCslParentAccuracy.GetClockAccuracy()) / 1000000); + semiWindow = static_cast(static_cast(elapsed) * + (Get().GetCslAccuracy() + mCslParentAccuracy.GetClockAccuracy()) / + Time::kOneSecondInUsec); semiWindow += mCslParentAccuracy.GetUncertaintyInMicrosec() + Get().GetCslUncertainty() * 10; aAhead = Min(semiPeriod, semiWindow + kMinReceiveOnAhead + kCslReceiveTimeAhead); aAfter = Min(semiPeriod, semiWindow + kMinReceiveOnAfter); } +uint32_t SubMac::GetNextCycleDrift(void) +{ + uint64_t periodUs = mCslPeriod * kUsPerTenSymbols; + + return static_cast(periodUs * (Get().GetCslAccuracy() + mCslParentAccuracy.GetClockAccuracy()) / + Time::kOneSecondInUsec); +} + uint32_t SubMac::GetLocalTime(void) { uint32_t now;