diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 5ecea91f9..607f9adba 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (356) +#define OPENTHREAD_API_VERSION (357) /** * @addtogroup api-instance diff --git a/include/openthread/platform/radio.h b/include/openthread/platform/radio.h index 35dd46496..d11549e07 100644 --- a/include/openthread/platform/radio.h +++ b/include/openthread/platform/radio.h @@ -270,10 +270,26 @@ typedef struct otRadioFrame */ struct { - const otMacKeyMaterial *mAesKey; ///< The key material used for AES-CCM frame security. - otRadioIeInfo *mIeInfo; ///< The pointer to the Header IE(s) related information. - uint32_t mTxDelay; ///< The delay time for this transmission (based on `mTxDelayBaseTime`). - uint32_t mTxDelayBaseTime; ///< The base time for the transmission delay. + const otMacKeyMaterial *mAesKey; ///< The key material used for AES-CCM frame security. + otRadioIeInfo *mIeInfo; ///< The pointer to the Header IE(s) related information. + + /** + * The base time in microseconds for scheduled transmissions + * relative to the local radio clock, see `otPlatRadioGetNow` and + * `mTxDelay`. + */ + uint32_t mTxDelayBaseTime; + + /** + * The delay time in microseconds for this transmission referenced + * to `mTxDelayBaseTime`. + * + * Note: `mTxDelayBaseTime` + `mTxDelay` SHALL point to the point in + * time when the end of the SFD will be present at the local + * antenna, relative to the local radio clock. + */ + uint32_t mTxDelay; + uint8_t mMaxCsmaBackoffs; ///< Maximum number of backoffs attempts before declaring CCA failure. uint8_t mMaxFrameRetries; ///< Maximum number of retries allowed after a transmission failure. @@ -332,10 +348,8 @@ typedef struct otRadioFrame struct { /** - * The timestamp when the frame was received in microseconds. - * - * The value SHALL be the time when the SFD was received. - * + * The time of the local radio clock in microseconds when the end of + * the SFD was present at the local antenna. */ uint64_t mTimestamp; @@ -652,14 +666,25 @@ void otPlatRadioSetMacFrameCounter(otInstance *aInstance, uint32_t aMacFrameCoun void otPlatRadioSetMacFrameCounterIfLarger(otInstance *aInstance, uint32_t aMacFrameCounter); /** - * Get the current estimated time (in microseconds) of the radio chip. + * Get the current time in microseconds referenced to a continuous monotonic + * local radio clock (64 bits width). * - * This microsecond timer must be a free-running timer. The timer must continue to advance with microsecond precision - * even when the radio is in the sleep state. + * The radio clock SHALL NOT wrap during the device's uptime. Implementations + * SHALL therefore identify and compensate for internal counter overflows. The + * clock does not have a defined epoch and it SHALL NOT introduce any continuous + * or discontinuous adjustments (e.g. leap seconds). Implementations SHALL + * compensate for any sleep times of the device. + * + * Implementations MAY choose to discipline the radio clock and compensate for + * sleep times by any means (e.g. by combining a high precision/low power RTC + * with a high resolution counter) as long as the exposed combined clock + * provides continuous monotonic microsecond resolution ticks within the + * accuracy limits announced by @ref otPlatRadioGetCslAccuracy. * * @param[in] aInstance A pointer to an OpenThread instance. * - * @returns The current time in microseconds. UINT64_MAX when platform does not support or radio time is not ready. + * @returns The current time in microseconds. UINT64_MAX when platform does not + * support or radio time is not ready. * */ uint64_t otPlatRadioGetNow(otInstance *aInstance); @@ -764,8 +789,17 @@ 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 + * @param[in] aStart The receive window start time relative to the local + * radio clock, see `otPlatRadioGetNow`. The radio + * receiver SHALL be on and ready to receive the first + * symbol of a frame's SHR at the window start time. + * @param[in] aDuration The receive window duration, in microseconds, as + * measured by the local radio clock. The radio SHOULD be + * turned off (or switched to TX mode if an ACK frame + * needs to be sent) after that duration unless it is + * still actively receiving a frame. In the latter case + * the radio SHALL be kept in reception mode until frame + * reception has either succeeded or failed. * * @retval OT_ERROR_NONE Successfully scheduled receive window. * @retval OT_ERROR_FAILED The receive window could not be scheduled. @@ -1078,28 +1112,50 @@ otError otPlatRadioEnableCsl(otInstance *aInstance, /** * Update CSL sample time in radio driver. * - * Sample time is stored in radio driver as a copy to calculate phase when sending ACK with CSL IE. + * Sample time is stored in radio driver as a copy to calculate phase when + * sending ACK with CSL IE. The CSL sample (window) of the CSL receiver extends + * before and after the sample time. The CSL sample time marks a timestamp in + * the CSL sample window when a frame should be received in "ideal conditions" + * if there would be no inaccuracy/clock-drift. * * @param[in] aInstance The OpenThread instance structure. - * @param[in] aCslSampleTime The latest sample time. - * + * @param[in] aCslSampleTime The next sample time, in microseconds. It is + * the time when the first symbol of the MHR of + * the frame is expected. */ void otPlatRadioUpdateCslSampleTime(otInstance *aInstance, uint32_t aCslSampleTime); /** - * Get the current accuracy, in units of ± ppm, of the clock used for scheduling CSL operations. + * Get the current estimated worst case accuracy (maximum ± deviation from the + * nominal frequency) of the local radio clock in units of PPM. This is the + * clock used to schedule CSL operations. * - * @note Platforms may optimize this value based on operational conditions (i.e.: temperature). + * @note Implementations MAY estimate this value based on current operating + * conditions (e.g. temperature). + * + * In case the implementation does not estimate the current value but returns a + * fixed value, this value MUST be the worst-case accuracy over all possible + * foreseen operating conditions (temperature, pressure, etc) of the + * implementation. * * @param[in] aInstance A pointer to an OpenThread instance. * - * @returns The current CSL rx/tx scheduling drift, in units of ± ppm. + * @returns The current CSL rx/tx scheduling drift, in PPM. * */ uint8_t otPlatRadioGetCslAccuracy(otInstance *aInstance); /** - * The fixed uncertainty of the Device for scheduling CSL Transmissions in units of 10 microseconds. + * The fixed uncertainty (i.e. random jitter) of the arrival time of CSL + * transmissions received by this device in units of 10 microseconds. + * + * This designates the worst case constant positive or negative deviation of + * the actual arrival time of a transmission from the transmission time + * calculated relative to the local radio clock independent of elapsed time. In + * addition to uncertainty accumulated over elapsed time, the CSL channel sample + * ("RX window") must be extended by twice this deviation such that an actual + * transmission is guaranteed to be detected by the local receiver in the + * presence of random arrival time jitter. * * @param[in] aInstance A pointer to an OpenThread instance. * diff --git a/include/openthread/platform/time.h b/include/openthread/platform/time.h index eecb34b84..1766cc559 100644 --- a/include/openthread/platform/time.h +++ b/include/openthread/platform/time.h @@ -52,7 +52,20 @@ extern "C" { */ /** - * Get the current time (64bits width). + * Get the current platform time in microseconds referenced to a continuous + * monotonic local clock (64 bits width). + * + * The clock SHALL NOT wrap during the device's uptime. Implementations SHALL + * therefore identify and compensate for internal counter overflows. The clock + * does not have a defined epoch and it SHALL NOT introduce any continuous or + * discontinuous adjustments (e.g. leap seconds). Implementations SHALL + * compensate for any sleep times of the device. + * + * Implementations MAY choose to discipline the platform clock and compensate + * for sleep times by any means (e.g. by combining a high precision/low power + * RTC with a high resolution counter) as long as the exposed combined clock + * provides continuous monotonic microsecond resolution ticks within the + * accuracy limits announced by @ref otPlatTimeGetXtalAccuracy. * * @returns The current time in microseconds. * @@ -60,9 +73,18 @@ extern "C" { uint64_t otPlatTimeGet(void); /** - * Get the device's XTAL accuracy. + * Get the current estimated worst case accuracy (maximum ± deviation from the + * nominal frequency) of the local platform clock in units of PPM. * - * @returns The device's XTAL accuracy, in ppm. + * @note Implementations MAY estimate this value based on current operating + * conditions (e.g. temperature). + * + * In case the implementation does not estimate the current value but returns a + * fixed value, this value MUST be the worst-case accuracy over all possible + * foreseen operating conditions (temperature, pressure, etc) of the + * implementation. + * + * @returns The current platform clock accuracy, in PPM. * */ uint16_t otPlatTimeGetXtalAccuracy(void); diff --git a/src/core/mac/mac_frame.hpp b/src/core/mac/mac_frame.hpp index 247d11409..38bc67f80 100644 --- a/src/core/mac/mac_frame.hpp +++ b/src/core/mac/mac_frame.hpp @@ -1211,10 +1211,18 @@ public: /** * Returns the timestamp when the frame was received. - * The timestamp marks the frame detection time: the end of the last symbol of SFD. * - * @returns The timestamp when the frame SFD was received, in microseconds. + * The value SHALL be the time of the local radio clock in + * microseconds when the end of the SFD (or equivalently: the start + * of the first symbol of the PHR) was present at the local antenna, + * see the definition of a "symbol boundary" in IEEE 802.15.4-2020, + * section 6.5.2 or equivalently the RMARKER definition in section + * 6.9.1 (albeit both unrelated to OT). * + * The time is relative to the local radio clock as defined by + * `otPlatRadioGetNow`. + * + * @returns The timestamp in microseconds. */ const uint64_t &GetTimestamp(void) const { return mInfo.mRxInfo.mTimestamp; } diff --git a/src/core/mac/sub_mac.cpp b/src/core/mac/sub_mac.cpp index 3a5d83816..a5b3a5f32 100644 --- a/src/core/mac/sub_mac.cpp +++ b/src/core/mac/sub_mac.cpp @@ -316,7 +316,7 @@ void SubMac::LogReceived(RxFrame *aFrame) // Treat as a warning when the deviation is not within the margins. Neither kCslReceiveTimeAhead // or kMinReceiveOnAhead/kMinReceiveOnAfter are considered for the margin since they have no // impact on understanding possible deviation errors between transmitter and receiver. So in this - // case ahead equals after. + // case only `ahead` is used, as an allowable max deviation in both +/- directions. if ((deviation + ahead > 0) && (deviation < static_cast(ahead))) { LogDebg("%s", logString.AsCString()); @@ -468,10 +468,10 @@ void SubMac::StartCsmaBackoff(void) { if (Time(static_cast(otPlatRadioGetNow(&GetInstance()))) < Time(mTransmitFrame.mInfo.mTxInfo.mTxDelayBaseTime) + mTransmitFrame.mInfo.mTxInfo.mTxDelay - - kCcaSampleInterval - kCslTransmitTimeAhead) + kCcaSampleInterval - kCslTransmitTimeAhead - kRadioHeaderShrDuration) { mTimer.StartAt(Time(mTransmitFrame.mInfo.mTxInfo.mTxDelayBaseTime) - kCcaSampleInterval - - kCslTransmitTimeAhead, + kCslTransmitTimeAhead - kRadioHeaderShrDuration, mTransmitFrame.mInfo.mTxInfo.mTxDelay); } else // Transmit without delay diff --git a/src/core/mac/sub_mac.hpp b/src/core/mac/sub_mac.hpp index 7d85683b6..96af31303 100644 --- a/src/core/mac/sub_mac.hpp +++ b/src/core/mac/sub_mac.hpp @@ -667,7 +667,7 @@ private: bool mIsCslSampling : 1; // Indicates that the radio is receiving in CSL state for platforms not supporting delayed // reception. uint16_t mCslPeerShort; // The CSL peer short address. - TimeMicro mCslSampleTime; // The CSL sample time of the current period. + TimeMicro mCslSampleTime; // The CSL sample time of the current period relative to the local radio clock. TimeMicro mCslLastSync; // The timestamp of the last successful CSL synchronization. CslAccuracy mCslParentAccuracy; // The parent's CSL accuracy (clock accuracy and uncertainty). TimerMicro mCslTimer; diff --git a/src/core/thread/csl_tx_scheduler.cpp b/src/core/thread/csl_tx_scheduler.cpp index 45facec35..35a070c1e 100644 --- a/src/core/thread/csl_tx_scheduler.cpp +++ b/src/core/thread/csl_tx_scheduler.cpp @@ -158,9 +158,10 @@ uint32_t CslTxScheduler::GetNextCslTransmissionDelay(const Child &aChild, { uint64_t radioNow = otPlatRadioGetNow(&GetInstance()); uint32_t periodInUs = aChild.GetCslPeriod() * kUsPerTenSymbols; - uint64_t firstTxWindow = - aChild.GetLastRxTimestamp() - kRadioHeaderShrDuration + aChild.GetCslPhase() * kUsPerTenSymbols; - uint64_t nextTxWindow = radioNow - (radioNow % periodInUs) + (firstTxWindow % periodInUs); + + /* see CslTxScheduler::ChildInfo::mCslPhase */ + uint64_t firstTxWindow = aChild.GetLastRxTimestamp() + aChild.GetCslPhase() * kUsPerTenSymbols; + uint64_t nextTxWindow = radioNow - (radioNow % periodInUs) + (firstTxWindow % periodInUs); while (nextTxWindow < radioNow + aAheadUs) { diff --git a/src/core/thread/csl_tx_scheduler.hpp b/src/core/thread/csl_tx_scheduler.hpp index 3f1bfec6e..77eebcdaf 100644 --- a/src/core/thread/csl_tx_scheduler.hpp +++ b/src/core/thread/csl_tx_scheduler.hpp @@ -101,14 +101,59 @@ public: void SetLastRxTimestamp(uint64_t aLastRxTimestamp) { mLastRxTimestamp = aLastRxTimestamp; } private: - uint8_t mCslTxAttempts : 7; ///< Number of CSL triggered tx attempts. - bool mCslSynchronized : 1; ///< Indicates whether or not the child is CSL synchronized. - uint8_t mCslChannel; ///< The channel the device will listen on. - uint32_t mCslTimeout; ///< The sync timeout, in seconds. - 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 mLastRxTimestamp; ///< Time when last frame containing CSL IE was received, in microseconds. + uint8_t mCslTxAttempts : 7; ///< Number of CSL triggered tx attempts. + bool mCslSynchronized : 1; ///< Indicates whether or not the child is CSL synchronized. + uint8_t mCslChannel; ///< The channel the device will listen on. + uint32_t mCslTimeout; ///< The sync timeout, in seconds. + uint16_t mCslPeriod; ///< CSL sampled listening period between consecutive channel samples in units of 10 + ///< symbols (160 microseconds). + + /** + * The time in units of 10 symbols from the first symbol of the frame + * containing the CSL IE was transmitted until the next channel sample, + * see IEEE 802.15.4-2015, section 6.12.2. + * + * The Thread standard further defines the CSL phase (see Thread 1.3.1, + * section 3.2.6.3.4, also conforming to IEEE 802.15.4-2020, section + * 6.12.2.1): + * * The "first symbol" from the definition SHALL be interpreted as the + * first symbol of the MAC Header. + * * "until the next channel sample": + * * The CSL Receiver SHALL be ready to receive when the preamble + * time T_pa as specified below is reached. + * * The CSL Receiver SHOULD be ready to receive earlier than T_pa + * and SHOULD stay ready to receive until after the time specified + * in CSL Phase, according to the implementation and accuracy + * expectations. + * * The CSL Transmitter SHALL start transmitting the first symbol + * of the preamble of the frame to transmit at the preamble time + * T_pa = (CSL-Phase-Time – 192 us) (that is, CCA must be + * performed before time T_pa). Here, CSL-Phase-Time is the time + * duration specified by the CslPhase field value (in units of 10 + * symbol periods). + * * This implies that the CSL Transmitter SHALL start transmitting + * the first symbol of the MAC Header at the time T_mh = + * CSL-Phase-Time. + * + * Derivation of the next TX timestamp based on this definition and the + * RX timestamp of the packet containing the CSL IE: + * + * Note that RX and TX timestamps are defined to point to the end of the + * synchronization header (SHR). + * + * lastTmh = lastRxTimestamp + phrDuration + * + * nextTmh = lastTmh + symbolPeriod * 10 * (n * cslPeriod + cslPhase) + * = lastTmh + 160us * (n * cslPeriod + cslPhase) + * + * nextTxTimestamp + * = nextTmh - phrDuration + * = lastRxTimestamp + 160us * (n * cslPeriod + cslPhase) + */ + uint16_t mCslPhase; + TimeMilli mCslLastHeard; ///< Radio clock time when last frame containing CSL IE was heard. + uint64_t + mLastRxTimestamp; ///< Radio clock time when last frame containing CSL IE was received, in microseconds. static_assert(kMaxCslTriggeredTxAttempts < (1 << 7), "mCslTxAttempts cannot fit max!"); };