[low-power] enhance CSL transmission on NRF52840 using transmit_at (#5545)

This commit is contained in:
Li Cao
2020-10-27 23:26:34 +08:00
committed by GitHub
parent 3de6b9a86a
commit 3fd161e4d4
14 changed files with 133 additions and 73 deletions
@@ -285,7 +285,18 @@
*
*/
#ifndef OPENTHREAD_CONFIG_CSL_SAMPLE_WINDOW
#define OPENTHREAD_CONFIG_CSL_SAMPLE_WINDOW 30
#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.
*
*/
#ifndef OPENTHREAD_CONFIG_CSL_RECEIVE_TIME_AHEAD
#define OPENTHREAD_CONFIG_CSL_RECEIVE_TIME_AHEAD 3
#endif
/*
+30 -15
View File
@@ -452,19 +452,34 @@ otError otPlatRadioReceive(otInstance *aInstance, uint8_t aChannel)
otError otPlatRadioTransmit(otInstance *aInstance, otRadioFrame *aFrame)
{
bool result = true;
bool result = true;
otError error = OT_ERROR_NONE;
aFrame->mPsdu[-1] = aFrame->mLength;
nrf_802154_channel_set(aFrame->mChannel);
if (aFrame->mInfo.mTxInfo.mCsmaCaEnabled)
#if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
if (aFrame->mInfo.mTxInfo.mTxDelay != 0)
{
nrf_802154_transmit_csma_ca_raw(&aFrame->mPsdu[-1]);
if (!nrf_802154_transmit_raw_at(&aFrame->mPsdu[-1], aFrame->mInfo.mTxInfo.mCsmaCaEnabled,
aFrame->mInfo.mTxInfo.mTxDelayBaseTime, aFrame->mInfo.mTxInfo.mTxDelay,
aFrame->mChannel))
{
error = OT_ERROR_INVALID_STATE;
}
}
else
#endif
{
result = nrf_802154_transmit_raw(&aFrame->mPsdu[-1], false);
if (aFrame->mInfo.mTxInfo.mCsmaCaEnabled)
{
nrf_802154_transmit_csma_ca_raw(&aFrame->mPsdu[-1]);
}
else
{
result = nrf_802154_transmit_raw(&aFrame->mPsdu[-1], false);
}
}
clearPendingEvents();
@@ -475,7 +490,7 @@ otError otPlatRadioTransmit(otInstance *aInstance, otRadioFrame *aFrame)
setPendingEvent(kPendingEventChannelAccessFailure);
}
return OT_ERROR_NONE;
return error;
}
otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *aInstance)
@@ -504,7 +519,7 @@ otRadioCaps otPlatRadioGetCaps(otInstance *aInstance)
return (otRadioCaps)(OT_RADIO_CAPS_ENERGY_SCAN | OT_RADIO_CAPS_ACK_TIMEOUT | OT_RADIO_CAPS_CSMA_BACKOFF |
#if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
OT_RADIO_CAPS_TRANSMIT_SEC |
OT_RADIO_CAPS_TRANSMIT_SEC | OT_RADIO_CAPS_TRANSMIT_TIMING |
#endif
OT_RADIO_CAPS_SLEEP_TO_TX);
}
@@ -997,9 +1012,9 @@ static uint16_t getCslPhase()
{
uint32_t curTime = otPlatAlarmMicroGetNow();
uint32_t cslPeriodInUs = sCslPeriod * OT_US_PER_TEN_SYMBOLS;
uint32_t diff = ((sCslSampleTime % cslPeriodInUs) - (curTime % cslPeriodInUs) + cslPeriodInUs) % cslPeriodInUs;
uint32_t diff = (cslPeriodInUs - (curTime % cslPeriodInUs) + (sCslSampleTime % cslPeriodInUs)) % cslPeriodInUs;
return (uint16_t)(diff / OT_US_PER_TEN_SYMBOLS);
return (uint16_t)(diff / OT_US_PER_TEN_SYMBOLS + 1);
}
#endif
@@ -1096,6 +1111,13 @@ void nrf_802154_tx_started(const uint8_t *aFrame)
bool processSecurity = false;
assert(aFrame == sTransmitPsdu);
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
if (sCslPeriod > 0)
{
otMacFrameSetCslIe(&sTransmitFrame, (uint16_t)sCslPeriod, getCslPhase());
}
#endif
// Update IE and secure transmit frame
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
if (sTransmitFrame.mInfo.mTxInfo.mIeInfo->mTimeIeOffset != 0)
@@ -1116,13 +1138,6 @@ void nrf_802154_tx_started(const uint8_t *aFrame)
}
#endif // OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
if (sCslPeriod > 0)
{
otMacFrameSetCslIe(&sTransmitFrame, (uint16_t)sCslPeriod, getCslPhase());
}
#endif
#if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
otEXPECT(otMacFrameIsSecurityEnabled(&sTransmitFrame) && otMacFrameIsKeyIdMode1(&sTransmitFrame) &&
!sTransmitFrame.mInfo.mTxInfo.mIsSecurityProcessed);
+1 -1
View File
@@ -53,7 +53,7 @@ extern "C" {
* @note This number versions both OpenThread platform and user APIs.
*
*/
#define OPENTHREAD_API_VERSION (37)
#define OPENTHREAD_API_VERSION (38)
/**
* @addtogroup api-instance
+2 -2
View File
@@ -228,8 +228,8 @@ typedef struct otRadioFrame
{
const otMacKey *mAesKey; ///< The key used for AES-CCM frame security.
otRadioIeInfo * mIeInfo; ///< The pointer to the Header IE(s) related information.
uint16_t mPeriod; ///< The transmit time period.
uint16_t mPhase; ///< The transmit time phase.
uint32_t mTxDelay; ///< The delay time for this transmission (based on `mTxDelayBaseTime`).
uint32_t mTxDelayBaseTime; ///< The base time for the transmission delay.
uint8_t mMaxCsmaBackoffs; ///< Maximum number of backoffs attempts before declaring CCA failure.
uint8_t mMaxFrameRetries; ///< Maximum number of retries allowed after a transmission failure.
bool mIsARetx : 1; ///< True if this frame is a retransmission (ignored by radio driver).
+1 -1
View File
@@ -136,7 +136,7 @@ size_nrf52840_version()
"BACKBONE_ROUTER=1"
"DUA=1"
"MLR=1"
# "CSL_RECEIVER=1" not supported on nrf52840 yet
"CSL_RECEIVER=1"
)
fi
+11
View File
@@ -395,4 +395,15 @@
#define OPENTHREAD_CONFIG_MAC_CSL_DEBUG_ENABLE 0
#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.
*
*/
#ifndef OPENTHREAD_CONFIG_CSL_RECEIVE_TIME_AHEAD
#define OPENTHREAD_CONFIG_CSL_RECEIVE_TIME_AHEAD 1
#endif
#endif // CONFIG_MAC_H_
+5 -3
View File
@@ -1086,8 +1086,10 @@ void Mac::BeginTransmit(void)
sendFrame.SetIsARetransmission(false);
sendFrame.SetIsSecurityProcessed(false);
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
sendFrame.SetTxPeriod(0);
sendFrame.SetTxDelay(0);
sendFrame.SetTxDelayBaseTime(0);
#endif
sendFrame.SetCsmaCaEnabled(true); // Set to true by default, only set to `false` for CSL transmission
switch (mOperation)
{
@@ -2348,10 +2350,10 @@ void Mac::ProcessCsl(const RxFrame &aFrame, const Address &aSrcAddr)
child->SetCslPeriod(csl->GetPeriod());
// Use ceiling to ensure the the time diff will be within kUsPerTenSymbols
child->SetCslPhase(((aFrame.GetTimestamp() + kUsPerTenSymbols - 1) / kUsPerTenSymbols + csl->GetPhase()) %
csl->GetPeriod());
child->SetCslPhase(csl->GetPhase());
child->SetCslSynchronized(true);
child->SetCslLastHeard(TimerMilli::GetNow());
child->SetLastRxTimestamp(aFrame.GetTimestamp());
otLogDebgMac("Timestamp=%u Sequence=%u CslPeriod=%hu CslPhase=%hu TransmitPhase=%hu",
static_cast<uint32_t>(aFrame.GetTimestamp()), aFrame.GetSequence(), csl->GetPeriod(), csl->GetPhase(),
child->GetCslPhase());
+16 -3
View File
@@ -1310,9 +1310,22 @@ public:
*/
otError GenerateEnhAck(const RxFrame &aFrame, bool aIsFramePending, const uint8_t *aIeData, uint8_t aIeLength);
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
void SetTxPhase(uint16_t aPhase) { mInfo.mTxInfo.mPhase = aPhase; }
void SetTxPeriod(uint16_t aPeriod) { mInfo.mTxInfo.mPeriod = aPeriod; }
#if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
/**
* Set TX delay field for the frame.
*
* @param[in] aTxDelay The delay time for the TX frame.
*
*/
void SetTxDelay(uint32_t aTxDelay) { mInfo.mTxInfo.mTxDelay = aTxDelay; }
/**
* 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; }
#endif
};
+15 -15
View File
@@ -44,6 +44,7 @@
#include "common/locator-getters.hpp"
#include "common/logging.hpp"
#include "common/random.hpp"
#include "common/time.hpp"
namespace ot {
namespace Mac {
@@ -349,25 +350,19 @@ void SubMac::StartCsmaBackoff(void)
uint32_t backoffExponent = kMinBE + mTransmitRetries + mCsmaBackoffs;
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
if (mTransmitFrame.mInfo.mTxInfo.mPeriod != 0)
if (mTransmitFrame.mInfo.mTxInfo.mTxDelay != 0)
{
SetState(kStateCslTransmit);
if (ShouldHandleTransmitTargetTime())
{
uint32_t phaseNow =
(otPlatRadioGetNow(&GetInstance()) / kUsPerTenSymbols) % mTransmitFrame.mInfo.mTxInfo.mPeriod;
uint32_t phaseDesired = mTransmitFrame.mInfo.mTxInfo.mPhase;
if (phaseNow < phaseDesired)
if (otPlatRadioGetNow(&GetInstance()) <
mTransmitFrame.mInfo.mTxInfo.mTxDelayBaseTime + mTransmitFrame.mInfo.mTxInfo.mTxDelay)
{
mTimer.Start((phaseDesired - phaseNow) * kUsPerTenSymbols);
mTimer.StartAt(Time(mTransmitFrame.mInfo.mTxInfo.mTxDelayBaseTime),
mTransmitFrame.mInfo.mTxInfo.mTxDelay);
}
else if (phaseNow > phaseDesired)
{
mTimer.Start((phaseDesired + mTransmitFrame.mInfo.mTxInfo.mPeriod - phaseNow) * kUsPerTenSymbols);
}
else
else // Transmit without delay
{
BeginTransmit();
}
@@ -423,8 +418,6 @@ void SubMac::BeginTransmit(void)
VerifyOrExit(mState == kStateCsmaBackoff);
#endif
mTransmitFrame.SetCsmaCaEnabled(mTransmitFrame.mInfo.mTxInfo.mPeriod == 0);
if ((mRadioCaps & OT_RADIO_CAPS_SLEEP_TO_TX) == 0)
{
error = Get<Radio>().Receive(mTransmitFrame.GetChannel());
@@ -439,6 +432,13 @@ void SubMac::BeginTransmit(void)
}
error = Get<Radio>().Transmit(mTransmitFrame);
if (error == OT_ERROR_INVALID_STATE && mTransmitFrame.mInfo.mTxInfo.mTxDelay > 0)
{
// Platform `transmit_at` fails and we send the frame directly.
mTransmitFrame.mInfo.mTxInfo.mTxDelay = 0;
mTransmitFrame.mInfo.mTxInfo.mTxDelayBaseTime = 0;
error = Get<Radio>().Transmit(mTransmitFrame);
}
OT_ASSERT(error == OT_ERROR_NONE);
exit:
@@ -997,7 +997,7 @@ void SubMac::HandleCslTimer(void)
case kCslSample:
mCslState = kCslSleep;
// kUsPerTenSymbols: computing CSL Phase using floor division.
mCslTimer.StartAt(mCslSampleTime, mCslPeriod * kUsPerTenSymbols - kUsPerTenSymbols);
mCslTimer.StartAt(mCslSampleTime, (mCslPeriod - kCslReceiveTimeAhead) * kUsPerTenSymbols);
if (mState == kStateCslSample)
{
#if !OPENTHREAD_CONFIG_MAC_CSL_DEBUG_ENABLE
+6 -5
View File
@@ -539,12 +539,13 @@ private:
};
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
/**
* The SSED sample window in units of 10 symbols.
*
*/
enum : uint32_t{
kCslSampleWindow = OPENTHREAD_CONFIG_CSL_SAMPLE_WINDOW * kUsPerTenSymbols,
kCslSampleWindow =
OPENTHREAD_CONFIG_CSL_SAMPLE_WINDOW * kUsPerTenSymbols, ///< The SSED sample window in units of 10 symbols.
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.
};
/**
+21 -18
View File
@@ -128,6 +128,7 @@ void CslTxScheduler::RescheduleCslTx(void)
for (Child &child : Get<ChildTable>().Iterate(Child::kInStateAnyExceptInvalid))
{
uint32_t delay;
uint32_t cslTxDelay;
if (!child.IsCslSynchronized() || child.GetIndirectMessageCount() == 0 ||
child.GetCslTxAttempts() >= kMaxCslTriggeredTxAttempts)
@@ -135,7 +136,8 @@ void CslTxScheduler::RescheduleCslTx(void)
continue;
}
delay = GetNextCslTransmissionDelay(child, radioNow);
delay = GetNextCslTransmissionDelay(child, radioNow, cslTxDelay);
if (delay < minDelayTime)
{
minDelayTime = delay;
@@ -151,28 +153,25 @@ void CslTxScheduler::RescheduleCslTx(void)
mCslTxChild = bestChild;
}
uint32_t CslTxScheduler::GetNextCslTransmissionDelay(const Child &aChild, uint64_t aRadioNow) const
uint32_t CslTxScheduler::GetNextCslTransmissionDelay(const Child &aChild,
uint64_t aRadioNow,
uint32_t & aDelayFromLastRx) const
{
uint32_t delay;
uint16_t period_offset = (aRadioNow / kUsPerTenSymbols) % aChild.GetCslPeriod();
uint32_t periodInUs = aChild.GetCslPeriod() * kUsPerTenSymbols;
uint64_t firstTxWindow = aChild.GetLastRxTimestamp() + aChild.GetCslPhase() * kUsPerTenSymbols;
uint64_t nextTxWindow = aRadioNow - (aRadioNow % periodInUs) + (firstTxWindow % periodInUs);
if (aChild.GetCslPhase() > period_offset + mCslFrameRequestAhead)
{
delay = static_cast<uint16_t>(aChild.GetCslPhase() - period_offset - mCslFrameRequestAhead) * kUsPerTenSymbols;
}
else
{
delay = static_cast<uint16_t>(aChild.GetCslPeriod() + aChild.GetCslPhase() - period_offset -
mCslFrameRequestAhead) *
kUsPerTenSymbols;
}
while (aRadioNow + mCslFrameRequestAhead >= nextTxWindow) nextTxWindow += periodInUs;
return delay;
aDelayFromLastRx = static_cast<uint32_t>(nextTxWindow - aChild.GetLastRxTimestamp());
return static_cast<uint32_t>(nextTxWindow - aRadioNow);
}
otError CslTxScheduler::HandleFrameRequest(Mac::TxFrame &aFrame)
{
otError error = OT_ERROR_NONE;
otError error = OT_ERROR_NONE;
uint32_t txDelay;
VerifyOrExit(mCslTxChild != nullptr, error = OT_ERROR_ABORT);
@@ -202,8 +201,12 @@ otError CslTxScheduler::HandleFrameRequest(Mac::TxFrame &aFrame)
aFrame.SetChannel(mCslTxChild->GetCslChannel() == 0 ? Get<Mac::Mac>().GetPanChannel()
: mCslTxChild->GetCslChannel());
aFrame.SetTxPhase(mCslTxChild->GetCslPhase());
aFrame.SetTxPeriod(mCslTxChild->GetCslPeriod());
GetNextCslTransmissionDelay(*mCslTxChild, otPlatRadioGetNow(&GetInstance()), txDelay);
aFrame.SetTxDelay(txDelay);
aFrame.SetTxDelayBaseTime(
static_cast<uint32_t>(mCslTxChild->GetLastRxTimestamp())); // Only LSB part of the time is required.
aFrame.SetCsmaCaEnabled(false);
exit:
return error;
+5 -1
View File
@@ -101,6 +101,9 @@ public:
TimeMilli GetCslLastHeard(void) const { return mCslLastHeard; }
void SetCslLastHeard(TimeMilli aCslLastHeard) { mCslLastHeard = aCslLastHeard; }
uint64_t GetLastRxTimestamp(void) const { return mLastRxTimstamp; }
void SetLastRxTimestamp(uint64_t aLastRxTimestamp) { mLastRxTimstamp = aLastRxTimestamp; }
private:
uint8_t mCslTxAttempts : 7; ///< Number of CSL triggered tx attempts.
bool mCslSynchronized : 1; ///< Indicates whether or not the child is CSL synchronized.
@@ -109,6 +112,7 @@ public:
uint16_t mCslPeriod; ///< CSL sampled listening period in units of 10 symbols (160 microseconds).
uint16_t mCslPhase; ///< The time when the next CSL sample will start.
TimeMilli mCslLastHeard; ///< Time when last frame containing CSL IE was heard.
uint64_t mLastRxTimstamp; ///< Time when last frame containing CSL IE was received, in microseconds.
static_assert(kMaxCslTriggeredTxAttempts < (1 << 7), "mCslTxAttempts cannot fit max!");
};
@@ -190,7 +194,7 @@ private:
void InitFrameRequestAhead(void);
void RescheduleCslTx(void);
uint32_t GetNextCslTransmissionDelay(const Child &aChild, uint64_t aRadioNow) const;
uint32_t GetNextCslTransmissionDelay(const Child &aChild, uint64_t aRadioNow, uint32_t &aDelayFromLastRx) const;
// Callbacks from `Mac`
otError HandleFrameRequest(Mac::TxFrame &aFrame);
+4 -4
View File
@@ -1585,13 +1585,13 @@ otError RadioSpinel<InterfaceType, ProcessContextType>::Transmit(otRadioFrame &a
SPINEL_DATATYPE_BOOL_S // CsmaCaEnabled
SPINEL_DATATYPE_BOOL_S // IsARetx
SPINEL_DATATYPE_BOOL_S // SkipAes
SPINEL_DATATYPE_UINT16_S // Period
SPINEL_DATATYPE_UINT16_S, // Phase
SPINEL_DATATYPE_UINT32_S // TxDelay
SPINEL_DATATYPE_UINT32_S, // TxDelayBaseTime
mTransmitFrame->mPsdu, mTransmitFrame->mLength, mTransmitFrame->mChannel,
mTransmitFrame->mInfo.mTxInfo.mMaxCsmaBackoffs, mTransmitFrame->mInfo.mTxInfo.mMaxFrameRetries,
mTransmitFrame->mInfo.mTxInfo.mCsmaCaEnabled, mTransmitFrame->mInfo.mTxInfo.mIsARetx,
mTransmitFrame->mInfo.mTxInfo.mIsSecurityProcessed, mTransmitFrame->mInfo.mTxInfo.mPeriod,
mTransmitFrame->mInfo.mTxInfo.mPhase);
mTransmitFrame->mInfo.mTxInfo.mIsSecurityProcessed, mTransmitFrame->mInfo.mTxInfo.mTxDelay,
mTransmitFrame->mInfo.mTxInfo.mTxDelayBaseTime);
if (error == OT_ERROR_NONE)
{
+4 -4
View File
@@ -398,8 +398,8 @@ otError NcpBase::DecodeStreamRawTxRequest(otRadioFrame &aFrame)
aFrame.mInfo.mTxInfo.mCsmaCaEnabled = true;
aFrame.mInfo.mTxInfo.mIsARetx = false;
aFrame.mInfo.mTxInfo.mIsSecurityProcessed = false;
aFrame.mInfo.mTxInfo.mPeriod = 0;
aFrame.mInfo.mTxInfo.mPhase = 0;
aFrame.mInfo.mTxInfo.mTxDelay = 0;
aFrame.mInfo.mTxInfo.mTxDelayBaseTime = 0;
// All the next parameters are optional. Note that even if the
// decoder fails to parse any of optional parameters we still want to
@@ -411,8 +411,8 @@ otError NcpBase::DecodeStreamRawTxRequest(otRadioFrame &aFrame)
SuccessOrExit(mDecoder.ReadBool(csmaEnable));
SuccessOrExit(mDecoder.ReadBool(isARetx));
SuccessOrExit(mDecoder.ReadBool(isSecurityProcessed));
SuccessOrExit(mDecoder.ReadUint16(aFrame.mInfo.mTxInfo.mPeriod));
SuccessOrExit(mDecoder.ReadUint16(aFrame.mInfo.mTxInfo.mPhase));
SuccessOrExit(mDecoder.ReadUint32(aFrame.mInfo.mTxInfo.mTxDelay));
SuccessOrExit(mDecoder.ReadUint32(aFrame.mInfo.mTxInfo.mTxDelayBaseTime));
aFrame.mInfo.mTxInfo.mCsmaCaEnabled = csmaEnable;
aFrame.mInfo.mTxInfo.mIsARetx = isARetx;
aFrame.mInfo.mTxInfo.mIsSecurityProcessed = isSecurityProcessed;