[low-power] add receive timing capability to CSL receiver (#6343)

This commit uses the receive timing capability from the radio to
schedule CSL receive operations with a higher precision, allowing for
shorter receive windows.
This commit is contained in:
Eduardo Montoya
2021-04-22 11:22:30 -07:00
committed by GitHub
parent 1b118d7216
commit f344098266
13 changed files with 205 additions and 23 deletions
@@ -160,6 +160,16 @@
#define OPENTHREAD_CONFIG_MAC_SOFTWARE_TX_TIMING_ENABLE (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
#endif
/**
* @def OPENTHREAD_CONFIG_MAC_SOFTWARE_RX_TIMING_ENABLE
*
* Define to 1 to enable software reception target time logic.
*
*/
#ifndef OPENTHREAD_CONFIG_MAC_SOFTWARE_RX_TIMING_ENABLE
#define OPENTHREAD_CONFIG_MAC_SOFTWARE_RX_TIMING_ENABLE (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
#endif
/**
* @def OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE
*
@@ -226,6 +236,27 @@
#define RADIO_CONFIG_SRC_MATCH_EXT_ENTRY_NUM 0
#endif
/**
* @def OPENTHREAD_CONFIG_CSL_RECEIVE_TIME_AHEAD
*
* 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 2000
#endif
/**
* @def OPENTHREAD_CONFIG_CSL_MIN_RECEIVE_ON
*
* The minimum CSL receive window (in microseconds) required to receive an IEEE 802.15.4 frame.
* - Frame preamble: 6*2 symbols + margin
*
*/
#ifndef OPENTHREAD_CONFIG_CSL_MIN_RECEIVE_ON
#define OPENTHREAD_CONFIG_CSL_MIN_RECEIVE_ON 12 * 16
#endif
/*
* Suppress the ARMCC warning on unreachable statement,
* e.g. break after assert(false) or ExitNow() macro.
@@ -182,6 +182,16 @@
#define OPENTHREAD_CONFIG_MAC_SOFTWARE_TX_TIMING_ENABLE (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
#endif
/**
* @def OPENTHREAD_CONFIG_MAC_SOFTWARE_RX_TIMING_ENABLE
*
* Define to 1 to enable software reception target time logic.
*
*/
#ifndef OPENTHREAD_CONFIG_MAC_SOFTWARE_RX_TIMING_ENABLE
#define OPENTHREAD_CONFIG_MAC_SOFTWARE_RX_TIMING_ENABLE (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
#endif
/**
* @def OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE
*
@@ -278,6 +288,27 @@
#define RADIO_CONFIG_SRC_MATCH_EXT_ENTRY_NUM 0
#endif
/**
* @def OPENTHREAD_CONFIG_CSL_RECEIVE_TIME_AHEAD
*
* 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 2000
#endif
/**
* @def OPENTHREAD_CONFIG_CSL_MIN_RECEIVE_ON
*
* The minimum CSL receive window (in microseconds) required to receive an IEEE 802.15.4 frame.
* - Frame preamble: 6*2 symbols + margin
*
*/
#ifndef OPENTHREAD_CONFIG_CSL_MIN_RECEIVE_ON
#define OPENTHREAD_CONFIG_CSL_MIN_RECEIVE_ON 12 * 16
#endif
/*
* Suppress the ARMCC warning on unreachable statement,
* e.g. break after assert(false) or ExitNow() macro.
@@ -182,6 +182,16 @@
#define OPENTHREAD_CONFIG_MAC_SOFTWARE_TX_TIMING_ENABLE (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
#endif
/**
* @def OPENTHREAD_CONFIG_MAC_SOFTWARE_RX_TIMING_ENABLE
*
* Define to 1 to enable software reception target time logic.
*
*/
#ifndef OPENTHREAD_CONFIG_MAC_SOFTWARE_RX_TIMING_ENABLE
#define OPENTHREAD_CONFIG_MAC_SOFTWARE_RX_TIMING_ENABLE (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
#endif
/**
* @def OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE
*
@@ -285,14 +295,13 @@
*
*/
#ifndef OPENTHREAD_CONFIG_CSL_RECEIVE_TIME_AHEAD
#define OPENTHREAD_CONFIG_CSL_RECEIVE_TIME_AHEAD 800
#define OPENTHREAD_CONFIG_CSL_RECEIVE_TIME_AHEAD 2000
#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.
* The minimum CSL receive window (in microseconds) required to receive an IEEE 802.15.4 frame.
* - Frame preamble: 6*2 symbols + margin
*
*/
+29 -3
View File
@@ -79,6 +79,7 @@
#define SECURITY_ENABLED_BIT (1 << 3) ///< Security enabled bit.
#define RSSI_SETTLE_TIME_US 40 ///< RSSI settle time in microseconds.
#define SAFE_DELTA 1000 ///< A safe value for the `dt` parameter of delayed operations.
#if defined(__ICCARM__)
_Pragma("diag_suppress=Pe167")
@@ -488,13 +489,27 @@ otError otPlatRadioReceive(otInstance *aInstance, uint8_t aChannel)
}
nrf_802154_tx_power_set(GetTransmitPowerForChannel(aChannel));
result = nrf_802154_receive();
clearPendingEvents();
return result ? OT_ERROR_NONE : OT_ERROR_INVALID_STATE;
}
#if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
otError otPlatRadioReceiveAt(otInstance *aInstance, uint8_t aChannel, uint32_t aStart, uint32_t aDuration)
{
OT_UNUSED_VARIABLE(aInstance);
bool result;
nrf_802154_tx_power_set(GetTransmitPowerForChannel(aChannel));
result = nrf_802154_receive_at(aStart - SAFE_DELTA, SAFE_DELTA, aDuration, aChannel);
clearPendingEvents();
return result ? OT_ERROR_NONE : OT_ERROR_FAILED;
}
#endif
otError otPlatRadioTransmit(otInstance *aInstance, otRadioFrame *aFrame)
{
bool result = true;
@@ -575,7 +590,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_TIMING |
OT_RADIO_CAPS_TRANSMIT_SEC | OT_RADIO_CAPS_TRANSMIT_TIMING | OT_RADIO_CAPS_RECEIVE_TIMING |
#endif
OT_RADIO_CAPS_SLEEP_TO_TX);
}
@@ -1053,6 +1068,7 @@ void nrf_802154_receive_failed(nrf_802154_rx_error_t error)
case NRF_802154_RX_ERROR_ABORTED:
case NRF_802154_RX_ERROR_DELAYED_TIMESLOT_DENIED:
case NRF_802154_RX_ERROR_INVALID_LENGTH:
case NRF_802154_RX_ERROR_DELAYED_ABORTED:
sReceiveError = OT_ERROR_FAILED;
break;
@@ -1065,7 +1081,17 @@ void nrf_802154_receive_failed(nrf_802154_rx_error_t error)
sAckedWithSecEnhAck = false;
#endif
setPendingEvent(kPendingEventReceiveFailed);
#if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
if ((error == NRF_802154_RX_ERROR_DELAYED_TIMEOUT) || (error == NRF_802154_RX_ERROR_TIMESLOT_ENDED))
{
sReceiveError = OT_ERROR_NONE;
setPendingEvent(kPendingEventSleep);
}
else
#endif
{
setPendingEvent(kPendingEventReceiveFailed);
}
}
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
+1 -1
View File
@@ -129,7 +129,7 @@ void otCryptoAesCcm(const uint8_t *aKey,
* @param[out] aOutput An output buffer where ECDSA sign should be stored.
* @param[inout] aOutputLength The length of the @p aOutput buffer.
* @param[in] aInputHash An input hash.
* @param[in] aInputHashLength The length of the @p aClaims buffer.
* @param[in] aInputHashLength The length of the @p aInputHash buffer.
* @param[in] aPrivateKey A private key in PEM format.
* @param[in] aPrivateKeyLength The length of the @p aPrivateKey buffer.
*
+1 -1
View File
@@ -53,7 +53,7 @@ extern "C" {
* @note This number versions both OpenThread platform and user APIs.
*
*/
#define OPENTHREAD_API_VERSION (104)
#define OPENTHREAD_API_VERSION (105)
/**
* @addtogroup api-instance
+13
View File
@@ -127,6 +127,7 @@ enum
OT_RADIO_CAPS_SLEEP_TO_TX = 1 << 4, ///< Radio supports direct transition from sleep to TX with CSMA.
OT_RADIO_CAPS_TRANSMIT_SEC = 1 << 5, ///< Radio supports tx security.
OT_RADIO_CAPS_TRANSMIT_TIMING = 1 << 6, ///< Radio supports tx at specific time.
OT_RADIO_CAPS_RECEIVE_TIMING = 1 << 7, ///< Radio supports rx at specific time.
};
#define OT_PANID_BROADCAST 0xffff ///< IEEE 802.15.4 Broadcast PAN ID
@@ -680,6 +681,18 @@ otError otPlatRadioSleep(otInstance *aInstance);
*/
otError otPlatRadioReceive(otInstance *aInstance, uint8_t aChannel);
/**
* Schedule a radio reception window at a specific time and duration.
*
* @param[in] aChannel The radio channel on which to receive.
* @param[in] aStart The receive window start time, in microseconds.
* @param[in] aDuration The receive window duration, in microseconds
*
* @retval OT_ERROR_NONE Successfully scheduled receive window.
* @retval OT_ERROR_FAILED The receive window could not be scheduled.
*/
otError otPlatRadioReceiveAt(otInstance *aInstance, uint8_t aChannel, uint32_t aStart, uint32_t aDuration);
/**
* The radio driver calls this method to notify OpenThread of a received frame.
*
+11 -2
View File
@@ -315,6 +315,16 @@
#define OPENTHREAD_CONFIG_MAC_SOFTWARE_TX_TIMING_ENABLE 0
#endif
/**
* @def OPENTHREAD_CONFIG_MAC_SOFTWARE_RX_TIMING_ENABLE
*
* Define to 1 to enable software reception target time logic.
*
*/
#ifndef OPENTHREAD_CONFIG_MAC_SOFTWARE_RX_TIMING_ENABLE
#define OPENTHREAD_CONFIG_MAC_SOFTWARE_RX_TIMING_ENABLE 0
#endif
/**
* @def OPENTHREAD_CONFIG_MAC_SOFTWARE_ENERGY_SCAN_ENABLE
*
@@ -399,8 +409,7 @@
/**
* @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.
* The minimum CSL receive window (in microseconds) required to receive an IEEE 802.15.4 frame.
* - Maximum frame size with preamble: 6*2+127*2 symbols
* - AIFS: 12 symbols
* - Maximum ACK size with preamble: 6*2+33*2 symbols
+33 -13
View File
@@ -113,9 +113,14 @@ otRadioCaps SubMac::GetCaps(void) const
caps |= OT_RADIO_CAPS_TRANSMIT_TIMING;
#endif
#if OPENTHREAD_CONFIG_MAC_SOFTWARE_RX_TIMING_ENABLE
caps |= OT_RADIO_CAPS_RECEIVE_TIMING;
#endif
#else
caps = OT_RADIO_CAPS_ACK_TIMEOUT | OT_RADIO_CAPS_CSMA_BACKOFF | OT_RADIO_CAPS_TRANSMIT_RETRIES |
OT_RADIO_CAPS_ENERGY_SCAN | OT_RADIO_CAPS_TRANSMIT_SEC | OT_RADIO_CAPS_TRANSMIT_TIMING;
OT_RADIO_CAPS_ENERGY_SCAN | OT_RADIO_CAPS_TRANSMIT_SEC | OT_RADIO_CAPS_TRANSMIT_TIMING |
OT_RADIO_CAPS_RECEIVE_TIMING;
#endif
return caps;
@@ -949,9 +954,7 @@ void SubMac::SetCslPeriod(uint16_t aPeriod)
if (mCslPeriod > 0)
{
mCslSampleTime = TimeMicro(static_cast<uint32_t>(otPlatRadioGetNow(&GetInstance())));
Get<Radio>().UpdateCslSampleTime(mCslSampleTime.GetValue());
mCslState = kCslSleep;
mCslState = kCslSleep;
HandleCslTimer();
}
else
@@ -984,11 +987,12 @@ void SubMac::HandleCslTimer(void)
* | | |
* |<--Ahead-->|<--UnCert-->|<--Drift-->|<--Drift-->|<--UnCert-->|<--MinWin-->| |
* | | | | | | | |
* ---|-----------|------------|-----------|-----------|------------|------------|----------//------------|--
* -Ahead CslPhase +After -Ahead
* ---|-----------|------------|-----------|-----------|------------|------------|----------//------------|---
* -timeAhead CslPhase +timeAfter -timeAhead
*/
uint32_t timeAhead;
uint32_t timeAfter;
uint32_t periodUs = mCslPeriod * kUsPerTenSymbols;
uint32_t timeAhead, timeAfter;
GetCslWindowEdges(timeAhead, timeAfter);
switch (mCslState)
@@ -1007,16 +1011,32 @@ void SubMac::HandleCslTimer(void)
break;
case kCslSleep:
mCslState = kCslSample;
mCslSampleTime += periodUs;
mCslTimer.FireAt(mCslSampleTime + timeAfter);
mCslSampleTime += mCslPeriod * kUsPerTenSymbols;
if (RadioSupportsReceiveTiming())
{
mCslTimer.FireAt(mCslSampleTime - timeAhead);
timeAhead -= kCslReceiveTimeAhead;
}
else
{
mCslTimer.FireAt(mCslSampleTime + timeAfter);
mCslState = kCslSample;
}
Get<Radio>().UpdateCslSampleTime(mCslSampleTime.GetValue());
if (mState == kStateCslSample)
{
IgnoreError(Get<Radio>().Receive(mCslChannel));
otLogDebgMac("CSL sample %u, duration %u", mCslTimer.GetNow().GetValue(), timeAhead + timeAfter);
if (RadioSupportsReceiveTiming())
{
IgnoreError(Get<Radio>().ReceiveAt(mCslChannel, mCslSampleTime.GetValue() - periodUs - timeAhead,
timeAhead + timeAfter));
}
else
{
IgnoreError(Get<Radio>().Receive(mCslChannel));
otLogDebgMac("CSL sample %u, duration %u", mCslTimer.GetNow().GetValue(), timeAhead + timeAfter);
}
}
break;
+1
View File
@@ -572,6 +572,7 @@ private:
bool RadioSupportsAckTimeout(void) const { return ((mRadioCaps & OT_RADIO_CAPS_ACK_TIMEOUT) != 0); }
bool RadioSupportsEnergyScan(void) const { return ((mRadioCaps & OT_RADIO_CAPS_ENERGY_SCAN) != 0); }
bool RadioSupportsTransmitTiming(void) const { return ((mRadioCaps & OT_RADIO_CAPS_TRANSMIT_TIMING) != 0); }
bool RadioSupportsReceiveTiming(void) const { return ((mRadioCaps & OT_RADIO_CAPS_RECEIVE_TIMING) != 0); }
bool ShouldHandleTransmitSecurity(void) const;
bool ShouldHandleCsmaBackOff(void) const;
+23
View File
@@ -413,6 +413,19 @@ public:
*/
void UpdateCslSampleTime(uint32_t aCslSampleTime);
/**
* This method schedules a radio reception window at a specific time and duration.
*
* @param[in] aChannel The radio channel on which to receive.
* @param[in] aStart The receive window start time, in microseconds.
* @param[in] aDuration The receive window duration, in microseconds.
*
* @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);
/** This method enables CSL sampling in radio.
*
* @param[in] aCslPeriod CSL period, 0 for disabling CSL.
@@ -741,6 +754,11 @@ inline void Radio::UpdateCslSampleTime(uint32_t aCslSampleTime)
otPlatRadioUpdateCslSampleTime(GetInstancePtr(), aCslSampleTime);
}
inline Error Radio::ReceiveAt(uint8_t aChannel, uint32_t aStart, uint32_t aDuration)
{
return otPlatRadioReceiveAt(GetInstancePtr(), aChannel, aStart, aDuration);
}
inline Error Radio::EnableCsl(uint32_t aCslPeriod, const otExtAddress *aExtAddr)
{
return otPlatRadioEnableCsl(GetInstancePtr(), aCslPeriod, aExtAddr);
@@ -894,6 +912,11 @@ inline void Radio::UpdateCslSampleTime(uint32_t)
{
}
inline Error Radio::ReceiveAt(uint8_t, uint32_t, uint32_t)
{
return kErrorNone;
}
inline Error Radio::EnableCsl(uint32_t, const otExtAddress *)
{
return kErrorNotImplemented;
+10
View File
@@ -289,3 +289,13 @@ OT_TOOL_WEAK Error otPlatRadioGetRegion(otInstance *aInstance, uint16_t *aRegion
return kErrorNotImplemented;
}
OT_TOOL_WEAK Error otPlatRadioReceiveAt(otInstance *aInstance, uint8_t aChannel, uint32_t aStart, uint32_t aDuration)
{
OT_UNUSED_VARIABLE(aInstance);
OT_UNUSED_VARIABLE(aChannel);
OT_UNUSED_VARIABLE(aStart);
OT_UNUSED_VARIABLE(aDuration);
return kErrorNotImplemented;
}
+9
View File
@@ -579,3 +579,12 @@ otError otPlatRadioGetRegion(otInstance *aInstance, uint16_t *aRegionCode)
OT_UNUSED_VARIABLE(aInstance);
return sRadioSpinel.GetRadioRegion(aRegionCode);
}
otError otPlatRadioReceiveAt(otInstance *aInstance, uint8_t aChannel, uint32_t aStart, uint32_t aDuration)
{
OT_UNUSED_VARIABLE(aInstance);
OT_UNUSED_VARIABLE(aChannel);
OT_UNUSED_VARIABLE(aStart);
OT_UNUSED_VARIABLE(aDuration);
return OT_ERROR_NOT_IMPLEMENTED;
}