[low-power] adaptative CSL window duration (#6311)

Make CSL receive window dependent on the accuracy of the crystals
used for scheduling and on the elapsed time since last synchronization.

CSL Receive window scheduling
=============================

          a   b  c              d      e              f  g
          |   |  |              |      |              |  |
-------------------------------------------------------------------->

a-b:  kCslReceiveTimeAhead
b-c:  -mCslParentUncert
c-d:  -(mCslParentDrift + otPlatRadioGetCslAccuracy)
d:    mCslSampleTime
d-e:  kMinCslWindow
e-f:  +(mCslParentDrift + otPlatRadioGetCslAccuracy)
f-g:  +mCslParentUncert
This commit is contained in:
Eduardo Montoya
2021-03-30 18:39:09 -07:00
committed by GitHub
parent e1a5cca4b3
commit fa171812d3
13 changed files with 185 additions and 64 deletions
@@ -278,25 +278,26 @@
#define RADIO_CONFIG_SRC_MATCH_EXT_ENTRY_NUM 0
#endif
/**
* @def OPENTHREAD_CONFIG_CSL_SAMPLE_WINDOW
*
* CSL sample window in units of 10 symbols.
*
*/
#ifndef OPENTHREAD_CONFIG_CSL_SAMPLE_WINDOW
#define OPENTHREAD_CONFIG_CSL_SAMPLE_WINDOW 5
#endif
/**
* @def OPENTHREAD_CONFIG_CSL_RECEIVE_TIME_AHEAD
*
* For some reasons, CSL receivers wake up a little later than expected. This variable specifies how much time that
* CSL receiver would wake up earlier than the expected sample window. The time is in unit of 10 symbols.
* Reception scheduling and ramp up time needed for the CSL receiver to be ready, in units of microseconds.
*
*/
#ifndef OPENTHREAD_CONFIG_CSL_RECEIVE_TIME_AHEAD
#define OPENTHREAD_CONFIG_CSL_RECEIVE_TIME_AHEAD 3
#define OPENTHREAD_CONFIG_CSL_RECEIVE_TIME_AHEAD 800
#endif
/**
* @def OPENTHREAD_CONFIG_CSL_MIN_RECEIVE_ON
*
* The total duration, in units of microseconds, required for the CSL receiver to fully receive and acknowledge a frame
* of maximum possible length.
* - Frame preamble: 6*2 symbols + margin
*
*/
#ifndef OPENTHREAD_CONFIG_CSL_MIN_RECEIVE_ON
#define OPENTHREAD_CONFIG_CSL_MIN_RECEIVE_ON 12 * 16
#endif
/*
+11 -7
View File
@@ -118,10 +118,9 @@ static uint8_t sEnergyDetectionChannel;
static int8_t sEnergyDetected;
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
static uint32_t sCslPeriod;
static uint32_t sCslSampleTime;
static const uint32_t sCslSampleDur = OPENTHREAD_CONFIG_CSL_SAMPLE_WINDOW * OT_US_PER_TEN_SYMBOLS;
static const uint8_t sCslIeHeader[OT_IE_HEADER_SIZE] = {CSL_IE_HEADER_BYTES_LO, CSL_IE_HEADER_BYTES_HI};
static uint32_t sCslPeriod;
static uint32_t sCslSampleTime;
static const uint8_t sCslIeHeader[OT_IE_HEADER_SIZE] = {CSL_IE_HEADER_BYTES_LO, CSL_IE_HEADER_BYTES_HI};
#endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
typedef enum
@@ -1068,9 +1067,7 @@ static uint16_t getCslPhase()
{
uint32_t curTime = otPlatAlarmMicroGetNow();
uint32_t cslPeriodInUs = sCslPeriod * OT_US_PER_TEN_SYMBOLS;
uint32_t diff =
(cslPeriodInUs - (curTime % cslPeriodInUs) + (sCslSampleTime % cslPeriodInUs) + (sCslSampleDur / 2)) %
cslPeriodInUs;
uint32_t diff = (cslPeriodInUs - (curTime % cslPeriodInUs) + (sCslSampleTime % cslPeriodInUs)) % cslPeriodInUs;
return (uint16_t)(diff / OT_US_PER_TEN_SYMBOLS + 1);
}
#endif
@@ -1369,6 +1366,13 @@ void otPlatRadioUpdateCslSampleTime(otInstance *aInstance, uint32_t aCslSampleTi
sCslSampleTime = aCslSampleTime;
}
uint8_t otPlatRadioGetCslAccuracy(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
return otPlatTimeGetXtalAccuracy() / 2;
}
#endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
@@ -155,16 +155,6 @@
*/
#define CLI_COAP_SECURE_USE_COAP_DEFAULT_HANDLER 1
/**
* @def OPENTHREAD_CONFIG_CSL_SAMPLE_WINDOW
*
* The CSL sample window in units of 10 symbols.
*
*/
#ifndef OPENTHREAD_CONFIG_CSL_SAMPLE_WINDOW
#define OPENTHREAD_CONFIG_CSL_SAMPLE_WINDOW 5
#endif
/**
* @def OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
*
+7
View File
@@ -1183,6 +1183,13 @@ void otPlatRadioUpdateCslSampleTime(otInstance *aInstance, uint32_t aCslSampleTi
sCslSampleTime = aCslSampleTime;
}
uint8_t otPlatRadioGetCslAccuracy(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
return 0;
}
#endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
void otPlatRadioSetMacKey(otInstance * aInstance,
+1 -1
View File
@@ -53,7 +53,7 @@ extern "C" {
* @note This number versions both OpenThread platform and user APIs.
*
*/
#define OPENTHREAD_API_VERSION (89)
#define OPENTHREAD_API_VERSION (90)
/**
* @addtogroup api-instance
+12
View File
@@ -969,6 +969,18 @@ otError otPlatRadioEnableCsl(otInstance *aInstance, uint32_t aCslPeriod, const o
*/
void otPlatRadioUpdateCslSampleTime(otInstance *aInstance, uint32_t aCslSampleTime);
/**
* Get the current accuracy, in units of ± ppm, of the clock used for scheduling CSL operations.
*
* @note Platforms may optimize this value based on operational conditions (i.e.: temperature).
*
* @param[in] aInstance A pointer to an OpenThread instance.
*
* @returns The current CSL rx/tx scheduling drift, in units of ± ppm.
*
*/
uint8_t otPlatRadioGetCslAccuracy(otInstance *aInstance);
/**
* Set the max transmit power for a specific channel.
*
+16 -13
View File
@@ -375,16 +375,6 @@
#define OPENTHREAD_CONFIG_CSL_TIMEOUT 100
#endif
/**
* @def OPENTHREAD_CONFIG_CSL_SAMPLE_WINDOW
*
* The CSL sample window in 10 symbols.
*
*/
#ifndef OPENTHREAD_CONFIG_CSL_SAMPLE_WINDOW
#define OPENTHREAD_CONFIG_CSL_SAMPLE_WINDOW 5
#endif
/**
* @def OPENTHREAD_CONFIG_MAC_CSL_DEBUG_ENABLE
*
@@ -399,12 +389,25 @@
/**
* @def OPENTHREAD_CONFIG_CSL_RECEIVE_TIME_AHEAD
*
* For some reasons, CSL receivers wake up a little later than expected. This variable specifies how much time that
* CSL receiver would wake up earlier than the expected sample window. The time is in unit of 10 symbols.
* Reception scheduling and ramp up time needed for the CSL receiver to be ready, in units of microseconds.
*
*/
#ifndef OPENTHREAD_CONFIG_CSL_RECEIVE_TIME_AHEAD
#define OPENTHREAD_CONFIG_CSL_RECEIVE_TIME_AHEAD 2
#define OPENTHREAD_CONFIG_CSL_RECEIVE_TIME_AHEAD 320
#endif
/**
* @def OPENTHREAD_CONFIG_CSL_MIN_RECEIVE_ON
*
* The total duration, in units of microseconds, required for the CSL receiver to fully receive and acknowledge a frame
* of maximum possible length.
* - Maximum frame size with preamble: 6*2+127*2 symbols
* - AIFS: 12 symbols
* - Maximum ACK size with preamble: 6*2+33*2 symbols
*
*/
#ifndef OPENTHREAD_CONFIG_CSL_MIN_RECEIVE_ON
#define OPENTHREAD_CONFIG_CSL_MIN_RECEIVE_ON 356 * 16
#endif
/**
+62 -10
View File
@@ -71,6 +71,9 @@ SubMac::SubMac(Instance &aInstance)
, mCslPeriod(0)
, mCslChannel(0)
, mIsCslChannelSpecified(false)
, mCslLastSync(0)
, mCslParentDrift(kCslWorstCrystalPpm)
, mCslParentUncert(kCslWorstUncertainty)
, mCslState(kCslIdle)
, mCslTimer(aInstance, SubMac::HandleCslTimer)
#endif
@@ -261,14 +264,22 @@ void SubMac::HandleReceiveDone(RxFrame *aFrame, Error aError)
UpdateFrameCounter(aFrame->mInfo.mRxInfo.mAckFrameCounter);
}
#if OPENTHREAD_CONFIG_MAC_CSL_DEBUG_ENABLE
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
if (aFrame != nullptr && aError == kErrorNone)
{
// Assuming the risk of the parent missing the Enh-ACK in favor of smaller CSL receive window
if ((mCslPeriod > 0) && aFrame->mInfo.mRxInfo.mAckedWithSecEnhAck)
{
mCslLastSync = TimeMicro(static_cast<uint32_t>(aFrame->mInfo.mRxInfo.mTimestamp));
}
#if OPENTHREAD_CONFIG_MAC_CSL_DEBUG_ENABLE
// Split the log into two lines for RTT to output
otLogDebgMac("Received frame in state (SubMac %s, CSL %s), timestamp %u", StateToString(mState),
CslStateToString(mCslState), static_cast<uint32_t>(aFrame->mInfo.mRxInfo.mTimestamp));
otLogDebgMac("Target sample start time %u, time drift %d", mCslSampleTime.GetValue(),
static_cast<uint32_t>(aFrame->mInfo.mRxInfo.mTimestamp) - mCslSampleTime.GetValue());
#endif
}
#endif
@@ -487,7 +498,14 @@ void SubMac::HandleTransmitDone(TxFrame &aFrame, RxFrame *aAckFrame, Error aErro
{
mCallbacks.RecordCcaStatus(ccaSuccess, aFrame.GetChannel());
}
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
// Actual synchronization timestamp should be from the sent frame instead of the current time.
// Assuming the error here since it is bounded and has very small effect on the final window duration.
if (mCslPeriod > 0)
{
mCslLastSync = TimeMicro(static_cast<uint32_t>(otPlatRadioGetNow(&GetInstance())));
}
#endif
break;
default:
@@ -931,10 +949,10 @@ void SubMac::SetCslPeriod(uint16_t aPeriod)
if (mCslPeriod > 0)
{
mCslSampleTime = TimerMicro::GetNow();
mCslSampleTime = TimeMicro(static_cast<uint32_t>(otPlatRadioGetNow(&GetInstance())));
Get<Radio>().UpdateCslSampleTime(mCslSampleTime.GetValue());
mCslState = kCslSample;
mCslState = kCslSleep;
HandleCslTimer();
}
else
@@ -966,12 +984,25 @@ void SubMac::HandleCslTimer(Timer &aTimer)
void SubMac::HandleCslTimer(void)
{
/*
* CSL sample timing diagram
* |<---------------------------------Sample--------------------------------->|<--------Sleep--------->|
* | | |
* |<--Ahead-->|<--UnCert-->|<--Drift-->|<--Drift-->|<--UnCert-->|<--MinWin-->| |
* | | | | | | | |
* ---|-----------|------------|-----------|-----------|------------|------------|----------//------------|--
* -Ahead CslPhase +After -Ahead
*/
uint32_t timeAhead;
uint32_t timeAfter;
GetCslWindowEdges(timeAhead, timeAfter);
switch (mCslState)
{
case kCslSample:
mCslState = kCslSleep;
// kUsPerTenSymbols: computing CSL Phase using floor division.
mCslTimer.StartAt(mCslSampleTime, (mCslPeriod - kCslReceiveTimeAhead) * kUsPerTenSymbols);
mCslTimer.FireAt(mCslSampleTime - timeAhead);
if (mState == kStateCslSample)
{
#if !OPENTHREAD_CONFIG_MAC_CSL_DEBUG_ENABLE
@@ -984,15 +1015,14 @@ void SubMac::HandleCslTimer(void)
case kCslSleep:
mCslState = kCslSample;
mCslTimer.FireAt(mCslSampleTime + timeAfter);
mCslSampleTime += mCslPeriod * kUsPerTenSymbols;
Get<Radio>().UpdateCslSampleTime(mCslSampleTime.GetValue());
mCslTimer.StartAt(mCslSampleTime, kCslSampleWindow);
if (mState == kStateCslSample)
{
IgnoreError(Get<Radio>().Receive(mCslChannel));
otLogDebgMac("CSL sample %u", mCslTimer.GetNow().GetValue());
otLogDebgMac("CSL sample %u, duration %u", mCslTimer.GetNow().GetValue(), timeAhead + timeAfter);
}
break;
@@ -1004,6 +1034,28 @@ void SubMac::HandleCslTimer(void)
break;
}
}
void SubMac::GetCslWindowEdges(uint32_t &ahead, uint32_t &after)
{
uint32_t semiPeriod = mCslPeriod * kUsPerTenSymbols / 2;
uint64_t curTime = otPlatRadioGetNow(&GetInstance());
uint32_t elapsed, semiWindow;
if (mCslLastSync.GetValue() > curTime)
{
elapsed = static_cast<uint32_t>(UINT64_MAX - mCslLastSync.GetValue() + curTime);
}
else
{
elapsed = static_cast<uint32_t>(curTime - mCslLastSync.GetValue());
}
semiWindow = elapsed * (Get<Radio>().GetCslAccuracy() + mCslParentDrift) / 1000000;
semiWindow += mCslParentUncert * kUsPerUncertUnit;
ahead = (semiWindow + kCslReceiveTimeAhead > semiPeriod) ? semiPeriod : semiWindow + kCslReceiveTimeAhead;
after = (semiWindow + kMinCslWindow > semiPeriod) ? semiPeriod : semiWindow + kMinCslWindow;
}
#endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
} // namespace Mac
+20 -9
View File
@@ -517,6 +517,7 @@ private:
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
static void HandleCslTimer(Timer &aTimer);
void HandleCslTimer(void);
void GetCslWindowEdges(uint32_t &ahead, uint32_t &after);
#endif
enum
@@ -553,12 +554,18 @@ private:
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
enum : uint32_t{
kCslSampleWindow = OPENTHREAD_CONFIG_CSL_SAMPLE_WINDOW *
kUsPerTenSymbols, ///< The SSED sample window in units of microseconds.
kMinCslWindow = OPENTHREAD_CONFIG_CSL_MIN_RECEIVE_ON, ///< CSL receive window for the longest possible
///< frame and ack duration.
kCslReceiveTimeAhead =
OPENTHREAD_CONFIG_CSL_RECEIVE_TIME_AHEAD, ///< CSL receivers would wake up `kCslReceiveTimeAhead` earlier
///< than expected sample window. The time is in unit of 10
///< symbols.
///< than expected sample window. The time is in unit of
///< microseconds.
};
enum : uint8_t{
kCslWorstCrystalPpm = 255, ///< Worst possible crystal accuracy, in units of ± ppm.
kCslWorstUncertainty = 255, ///< Worst possible scheduling uncertainty, in units of 100 us.
kUsPerUncertUnit = 100, ///< Number of microseconds by uncertainty unit.
};
/**
@@ -636,14 +643,18 @@ private:
#endif
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
uint32_t mCslTimeout; ///< The CSL synchronized timeout in seconds.
TimeMicro mCslSampleTime; ///< The CSL sample time of the current period.
uint16_t mCslPeriod; ///< The CSL sample period, in units of 10 symbols (160 microseconds).
uint8_t mCslChannel : 7; ///< The actually CSL sample channel. If `mIsCslChannelSpecified` is 0, this should be
///< equal to the Pan channel of `Mac`.
uint32_t mCslTimeout; ///< The CSL synchronized timeout in seconds.
uint16_t mCslPeriod; ///< The CSL sample period, in units of 10 symbols (160 microseconds).
uint8_t mCslChannel : 7; ///< The actually CSL sample channel. If `mIsCslChannelSpecified` is 0, this should be
///< equal to the Pan channel of `Mac`.
uint8_t mIsCslChannelSpecified : 1; ///< Indicates whether or not the CSL channel was explicitly specified by
///< the user.
TimeMicro mCslSampleTime; ///< The CSL sample time of the current period.
TimeMicro mCslLastSync; ///< The timestamp of the last successful CSL syncronization.
uint8_t mCslParentDrift; ///< Drift of timer used for scheduling CSL transmission by the parent, in ± ppm.
uint8_t mCslParentUncert; ///< Uncertainty of the scheduling CSL of transmission by the parent, in ±100 us units.
CslState mCslState;
TimerMicro mCslTimer;
+20
View File
@@ -425,6 +425,16 @@ public:
*
*/
Error EnableCsl(uint32_t aCslPeriod, const otExtAddress *aExtAddr);
/**
* Get the current accuracy, in units of ± ppm, of the clock used for scheduling CSL operations.
*
* @note Platforms may optimize this value based on operational conditions (i.e.: temperature).
*
* @returns The current CSL rx/tx scheduling drift, in units of ± ppm.
*
*/
uint8_t GetCslAccuracy(void);
#endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
/**
@@ -735,6 +745,11 @@ inline Error Radio::EnableCsl(uint32_t aCslPeriod, const otExtAddress *aExtAddr)
{
return otPlatRadioEnableCsl(GetInstancePtr(), aCslPeriod, aExtAddr);
}
inline uint8_t Radio::GetCslAccuracy()
{
return otPlatRadioGetCslAccuracy(GetInstancePtr());
}
#endif
inline Mac::TxFrame &Radio::GetTransmitBuffer(void)
@@ -883,6 +898,11 @@ inline Error Radio::EnableCsl(uint32_t, const otExtAddress *)
{
return kErrorNotImplemented;
}
inline uint8_t Radio::GetCslAccuracy(void)
{
return UINT8_MAX;
}
#endif
inline Mac::TxFrame &Radio::GetTransmitBuffer(void)
+7
View File
@@ -242,6 +242,13 @@ OT_TOOL_WEAK uint32_t otPlatRadioGetBusSpeed(otInstance *aInstance)
return 0;
}
OT_TOOL_WEAK uint8_t otPlatRadioGetCslAccuracy(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
return UINT8_MAX;
}
OT_TOOL_WEAK Error otPlatRadioGetFemLnaGain(otInstance *aInstance, int8_t *aGain)
{
OT_UNUSED_VARIABLE(aInstance);
+7
View File
@@ -555,6 +555,13 @@ uint32_t otPlatRadioGetBusSpeed(otInstance *aInstance)
return sRadioSpinel.GetBusSpeed();
}
uint8_t otPlatRadioGetCslAccuracy(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
return 0;
}
otError otPlatRadioSetChannelMaxTransmitPower(otInstance *aInstance, uint8_t aChannel, int8_t aMaxPower)
{
OT_UNUSED_VARIABLE(aInstance);
+8 -1
View File
@@ -599,7 +599,7 @@ void otPlatFlashWrite(otInstance *aInstance, uint8_t aSwapIndex, uint32_t aOffse
}
}
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE || OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
uint16_t otPlatTimeGetXtalAccuracy(void)
{
return 0;
@@ -621,6 +621,13 @@ void otPlatRadioUpdateCslSampleTime(otInstance *aInstance, uint32_t aCslSampleTi
OT_UNUSED_VARIABLE(aInstance);
OT_UNUSED_VARIABLE(aCslSampleTime);
}
uint8_t otPlatRadioGetCslAccuracy(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
return static_cast<uint8_t>(otPlatTimeGetXtalAccuracy() / 2);
}
#endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
#if OPENTHREAD_CONFIG_OTNS_ENABLE