[radio] introduce dedicated RadioTime types to clarify clock domain (#13268)

This commit introduces dedicated type definitions `otRadioTime64`
(`RadioTime64`) and `otRadioTime32` (`RadioTime32`) to represent radio
hardware microsecond timestamps.

Key benefits and changes:
- Self-Documenting Clock Domain: Transceiver hardware clocks operate in a
  distinct clock domain (`otPlatRadioGetNow()`) from core system timers
  (`TimerMilli::GetNow()`, `TimerMicro::GetNow()`). Using dedicated
  types prevents developers from accidentally passing core system time
  into radio scheduling APIs.
- Explicit Rollover Handling: Introduced `IsRadioTimeStrictlyBefore()` to
  compare 32-bit radio timestamps using modulo serial arithmetic, cleanly
  accounting for `uint32_t` microsecond counter wrap-around.
- Core Module Adoption: Updated `otRadioFrame::mTxInfo` / `mRxInfo`,
  `otPlatRadioGetNow()`, `otPlatRadioReceiveAt()`, `SubMac`, and
  `CslTxScheduler` to adopt the new radio time types.
This commit is contained in:
Abtin Keshavarzian
2026-07-03 12:32:11 -07:00
committed by GitHub
parent 593fc53fcf
commit 4c44ff1edb
26 changed files with 259 additions and 90 deletions
+2 -2
View File
@@ -1041,7 +1041,7 @@ exit:
}
#endif
uint64_t otPlatRadioGetNow(otInstance *aInstance)
otRadioTime64 otPlatRadioGetNow(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
@@ -1109,7 +1109,7 @@ otError otPlatRadioResetCsl(otInstance *aInstance)
return OT_ERROR_NONE;
}
void otPlatRadioUpdateCslSampleTime(otInstance *aInstance, uint32_t aCslSampleTime)
void otPlatRadioUpdateCslSampleTime(otInstance *aInstance, otRadioTime32 aCslSampleTime)
{
OT_UNUSED_VARIABLE(aInstance);
+1 -1
View File
@@ -345,7 +345,7 @@ typedef struct otRadioContext
otExtAddress mExtAddress; ///< In little-endian byte order.
uint32_t mMacFrameCounter;
uint32_t mPrevMacFrameCounter;
uint32_t mCslSampleTime; ///< The sample time based on the microsecond timer.
otRadioTime32 mCslSampleTime; ///< The sample time based on the microsecond timer.
uint16_t mCslPeriod; ///< In unit of 10 symbols.
otShortAddress mCslShortAddress; ///< The short address of the CSL receiver's peer.
otExtAddress mCslExtAddress; ///< The extended address of the CSL receiver's peer.
+1 -1
View File
@@ -52,7 +52,7 @@ extern "C" {
*
* @note This number versions both OpenThread platform and user APIs.
*/
#define OPENTHREAD_API_VERSION (609)
#define OPENTHREAD_API_VERSION (610)
/**
* @addtogroup api-instance
+25 -6
View File
@@ -183,6 +183,25 @@ struct otExtAddress
*/
typedef struct otExtAddress otExtAddress;
/**
* Represents a 64-bit radio time in microseconds referenced to a continuous monotonic local radio clock.
*
* This type is returned by `otPlatRadioGetNow()` and is used as the timestamp field (`mTimestamp`) in radio frames
* (`otRadioFrame`).
*/
typedef uint64_t otRadioTime64;
/**
* Represents a 32-bit radio time in microseconds.
*
* This type holds the lower 32 bits (least significant bits) of a full 64-bit radio time (`otRadioTime64`).
*
* It is used in APIs such as `otPlatRadioReceiveAt()` and `otPlatRadioUpdateCslSampleTime()` and as the transmission
* delay base time (`mTxDelayBaseTime`) in `otRadioFrame`. It is important for radio platform implementations to
* correctly account for its roll-over.
*/
typedef uint32_t otRadioTime32;
#define OT_MAC_KEY_SIZE 16 ///< Size of the MAC Key in bytes.
/**
@@ -273,7 +292,7 @@ typedef struct otRadioFrame
*
* This field does not affect CCA behavior which is controlled by `mCsmaCaEnabled`.
*/
uint32_t mTxDelayBaseTime;
otRadioTime32 mTxDelayBaseTime;
/**
* The delay time in microseconds for this transmission referenced
@@ -381,7 +400,7 @@ typedef struct otRadioFrame
*
* The platform should update this field before otPlatRadioTxStarted() is fired for each transmit attempt.
*/
uint64_t mTimestamp;
otRadioTime64 mTimestamp;
} mTxInfo;
/**
@@ -393,7 +412,7 @@ typedef struct otRadioFrame
* The time of the local radio clock in microseconds when the end of
* the SFD was present at the local antenna.
*/
uint64_t mTimestamp;
otRadioTime64 mTimestamp;
uint32_t mAckFrameCounter; ///< ACK security frame counter (applicable when `mAckedWithSecEnhAck` is set).
uint8_t mAckKeyId; ///< ACK security key index (applicable when `mAckedWithSecEnhAck` is set).
@@ -765,7 +784,7 @@ void otPlatRadioSetMacFrameCounterIfLarger(otInstance *aInstance, uint32_t aMacF
* @returns The current time in microseconds. UINT64_MAX when platform does not
* support or radio time is not ready.
*/
uint64_t otPlatRadioGetNow(otInstance *aInstance);
otRadioTime64 otPlatRadioGetNow(otInstance *aInstance);
/**
* Get the bus speed in bits/second between the host and the radio chip.
@@ -894,7 +913,7 @@ otError otPlatRadioReceive(otInstance *aInstance, uint8_t aChannel);
* @retval OT_ERROR_NONE Successfully scheduled receive window.
* @retval OT_ERROR_FAILED The receive window could not be scheduled. For example, if @p aStart is in the past.
*/
otError otPlatRadioReceiveAt(otInstance *aInstance, uint8_t aChannel, uint32_t aStart, uint32_t aDuration);
otError otPlatRadioReceiveAt(otInstance *aInstance, uint8_t aChannel, otRadioTime32 aStart, uint32_t aDuration);
/**
* The radio driver calls this function to notify OpenThread of a received frame.
@@ -1225,7 +1244,7 @@ otError otPlatRadioResetCsl(otInstance *aInstance);
* the time when the first symbol of the MHR of
* the frame is expected.
*/
void otPlatRadioUpdateCslSampleTime(otInstance *aInstance, uint32_t aCslSampleTime);
void otPlatRadioUpdateCslSampleTime(otInstance *aInstance, otRadioTime32 aCslSampleTime);
/**
* Get the current estimated worst case accuracy (maximum ± deviation from the
+3
View File
@@ -663,6 +663,8 @@ openthread_core_files = [
"radio/radio.hpp",
"radio/radio_callbacks.cpp",
"radio/radio_platform.cpp",
"radio/radio_types.cpp",
"radio/radio_types.hpp",
"radio/trel_interface.cpp",
"radio/trel_interface.hpp",
"radio/trel_link.cpp",
@@ -857,6 +859,7 @@ openthread_radio_sources = [
"radio/radio.cpp",
"radio/radio_callbacks.cpp",
"radio/radio_platform.cpp",
"radio/radio_types.cpp",
"thread/link_quality.cpp",
"utils/parse_cmdline.cpp",
"utils/power_calibration.cpp",
+2
View File
@@ -224,6 +224,7 @@ set(COMMON_SOURCES
radio/radio.cpp
radio/radio_callbacks.cpp
radio/radio_platform.cpp
radio/radio_types.cpp
radio/trel_interface.cpp
radio/trel_link.cpp
radio/trel_packet.cpp
@@ -339,6 +340,7 @@ set(RADIO_COMMON_SOURCES
radio/radio.cpp
radio/radio_callbacks.cpp
radio/radio_platform.cpp
radio/radio_types.cpp
thread/link_quality.cpp
utils/otns.cpp
utils/parse_cmdline.cpp
+6 -6
View File
@@ -2498,7 +2498,7 @@ void Mac::ProcessCsl(const RxFrame &aFrame, const Address &aSrcAddr)
neighbor->SetCslLastHeard(TimerMilli::GetNow());
neighbor->SetLastRxTimestamp(aFrame.GetTimestamp());
LogDebg("Timestamp=%lu Sequence=%u CslPeriod=%u CslPhase=%u TransmitPhase=%u",
ToUlong(static_cast<uint32_t>(aFrame.GetTimestamp())), aFrame.GetSequence(), csl->GetPeriod(),
ToUlong(ConvertRadioTime64To32(aFrame.GetTimestamp())), aFrame.GetSequence(), csl->GetPeriod(),
csl->GetPhase(), neighbor->GetCslPhase());
#if OPENTHREAD_FTD
@@ -2622,9 +2622,9 @@ Error Mac::HandleWakeupFrame(const RxFrame &aFrame)
const ConnectionIe *connectionIe;
Address srcAddress;
WakeupInfo wakeupInfo;
uint32_t rvTimeUs;
uint64_t rvTimestampUs;
uint64_t radioNowUs;
RadioTime32 rvTimeUs;
RadioTime64 rvTimestampUs;
RadioTime64 radioNowUs;
VerifyOrExit(mWakeupListenEnabled && aFrame.IsWakeupFrame());
@@ -2637,13 +2637,13 @@ Error Mac::HandleWakeupFrame(const RxFrame &aFrame)
wakeupInfo.mRetryCount = connectionIe->GetRetryCount();
VerifyOrExit(wakeupInfo.mRetryInterval > 0 && wakeupInfo.mRetryCount > 0, error = kErrorInvalidArgs);
radioNowUs = otPlatRadioGetNow(&GetInstance());
radioNowUs = Get<Radio>().GetNow();
rvTimeUs = aFrame.Find<RendezvousTimeIe>()->GetRendezvousTime() * kUsPerTenSymbols;
rvTimestampUs = aFrame.GetTimestamp() + kRadioHeaderPhrDuration + aFrame.GetLength() * kOctetDuration + rvTimeUs;
if (rvTimestampUs > radioNowUs + kCslRequestAhead)
{
wakeupInfo.mAttachDelayMs = static_cast<uint32_t>(rvTimestampUs - radioNowUs - kCslRequestAhead);
wakeupInfo.mAttachDelayMs = ConvertRadioTime64To32(rvTimestampUs - radioNowUs - kCslRequestAhead);
wakeupInfo.mAttachDelayMs = wakeupInfo.mAttachDelayMs / Time::kOneMsecInUsec;
}
else
+17 -2
View File
@@ -43,6 +43,7 @@
#include "mac/mac_header_ie.hpp"
#include "mac/mac_types.hpp"
#include "meshcop/network_name.hpp"
#include "radio/radio_types.hpp"
namespace ot {
namespace Mac {
@@ -945,7 +946,7 @@ public:
*
* @returns The timestamp in microseconds.
*/
const uint64_t &GetTimestamp(void) const { return mInfo.mRxInfo.mTimestamp; }
const RadioTime64 &GetTimestamp(void) const { return mInfo.mRxInfo.mTimestamp; }
/**
* Performs AES CCM on the frame which is received.
@@ -1283,6 +1284,13 @@ public:
#endif
#if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
/**
* Gets the TX delay field for the frame.
*
* @returns The delay time for the TX frame in microseconds.
*/
uint32_t GetTxDelay(void) const { return mInfo.mTxInfo.mTxDelay; }
/**
* Set TX delay field for the frame.
*
@@ -1290,12 +1298,19 @@ public:
*/
void SetTxDelay(uint32_t aTxDelay) { mInfo.mTxInfo.mTxDelay = aTxDelay; }
/**
* Gets the TX delay base time field for the frame.
*
* @returns The delay base time for the TX frame as a `RadioTime32`.
*/
RadioTime32 GetTxDelayBaseTime(void) const { return mInfo.mTxInfo.mTxDelayBaseTime; }
/**
* 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; }
void SetTxDelayBaseTime(RadioTime32 aTxDelayBaseTime) { mInfo.mTxInfo.mTxDelayBaseTime = aTxDelayBaseTime; }
#endif
};
+9 -12
View File
@@ -427,32 +427,29 @@ void SubMac::StartCsmaBackoff(void)
uint8_t backoffExponent = kCsmaMinBe + mCsmaBackoffs;
#if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
if (mTransmitFrame.mInfo.mTxInfo.mTxDelay != 0 || mTransmitFrame.mInfo.mTxInfo.mTxDelayBaseTime != 0)
if (mTransmitFrame.GetTxDelay() != 0 || mTransmitFrame.GetTxDelayBaseTime() != 0)
{
SetState(kStateCslTransmit);
if (ShouldHandleTransmitTargetTime())
{
static constexpr uint32_t kAheadTime = kCcaSampleInterval + kCslTransmitTimeAhead + kRadioHeaderShrDuration;
Time txStartTime = Time(mTransmitFrame.mInfo.mTxInfo.mTxDelayBaseTime);
Time radioNow = Time(static_cast<uint32_t>(Get<Radio>().GetNow()));
RadioTime32 radioNow = Get<Radio>().GetNowAsRadioTime32();
RadioTime32 txStartTime = mTransmitFrame.GetTxDelayBaseTime();
txStartTime += (mTransmitFrame.mInfo.mTxInfo.mTxDelay - kAheadTime);
if (radioNow < txStartTime)
if (IsRadioTimeStrictlyBefore(radioNow, txStartTime))
{
StartTimer(txStartTime - radioNow);
ExitNow();
}
else // Transmit without delay
{
BeginTransmit();
}
}
else
{
BeginTransmit();
// Transmit without delay
}
BeginTransmit();
ExitNow();
}
#endif // !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
+10 -10
View File
@@ -677,7 +677,7 @@ private:
bool mIsCslSampling : 1; // Indicates that the current time is in CSL sample window
// for platforms not supporting `Radio::ReceiveAt()`.
uint16_t mCslPeerShort; // The CSL peer short address.
uint32_t mCslSampleTimeRadio; // The CSL sample time of the current period based on radio time (lower 32-bit).
RadioTime32 mCslSampleTimeRadio; // The CSL sample time of the current period based on radio time (lower 32-bit).
TimeMicro mCslSampleTimeLocal; // The CSL sample time of the current period based on local time.
TimeMicro mCslLastSync; // The timestamp of the last successful CSL synchronization.
CslAccuracy mCslParentAccuracy; // The parent's CSL accuracy (clock accuracy and uncertainty).
@@ -685,15 +685,15 @@ private:
#endif
#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE
bool mIsWedSampling : 1; // Indicates that the current time is in WED's sample window
// for platforms not supporting `Radio::ReceiveAt()`.
bool mIsWedEnabled : 1; // Indicates if the WED is enabled.
uint32_t mWakeupListenInterval; // The wake-up listen interval, in microseconds.
uint32_t mWakeupListenDuration; // The wake-up listen duration, in microseconds.
uint8_t mWakeupChannel; // The wake-up sample channel.
TimeMicro mWedSampleTime; // The WED sample time of the current interval in local time.
uint64_t mWedSampleTimeRadio; // The WED sample time of the current interval in radio time.
TimerMicro mWedTimer;
bool mIsWedSampling : 1; // Indicates that the current time is in WED's sample window
// for platforms not supporting `Radio::ReceiveAt()`.
bool mIsWedEnabled : 1; // Indicates if the WED is enabled.
uint32_t mWakeupListenInterval; // The wake-up listen interval, in microseconds.
uint32_t mWakeupListenDuration; // The wake-up listen duration, in microseconds.
uint8_t mWakeupChannel; // The wake-up sample channel.
TimeMicro mWedSampleTime; // The WED sample time of the current interval in local time.
RadioTime64 mWedSampleTimeRadio; // The WED sample time of the current interval in radio time.
TimerMicro mWedTimer;
#endif
};
+8 -9
View File
@@ -99,7 +99,7 @@ void SubMac::UpdateCslLastSyncTimestamp(RxFrame *aFrame, Error aError)
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_LOCAL_TIME_SYNC
mCslLastSync = TimerMicro::GetNow();
#else
mCslLastSync = TimeMicro(static_cast<uint32_t>(aFrame->mInfo.mRxInfo.mTimestamp));
mCslLastSync = TimeMicro(ConvertRadioTime64To32(aFrame->GetTimestamp()));
#endif
}
@@ -129,7 +129,7 @@ void SubMac::SetCslParams(uint16_t aPeriod, uint8_t aChannel, ShortAddress aShor
mCslTimer.Stop();
if (mCslPeriod > 0)
{
mCslSampleTimeRadio = static_cast<uint32_t>(Get<Radio>().GetNow());
mCslSampleTimeRadio = Get<Radio>().GetNowAsRadioTime32();
mCslSampleTimeLocal = TimerMicro::GetNow();
// Update CSL sync time whenever CSL parameters are re-initialized.
mCslLastSync = mCslSampleTimeLocal;
@@ -180,9 +180,9 @@ void SubMac::HandleCslReceiveAt(uint32_t aTimeAhead, uint32_t aTimeAfter)
* x-|------------|-------------------------------------x-|------------|---------------------------------------|
* sample sleep sample sleep
*/
uint32_t periodUs = mCslPeriod * kUsPerTenSymbols;
uint32_t winStart;
uint32_t winDuration;
uint32_t periodUs = mCslPeriod * kUsPerTenSymbols;
RadioTime32 winStart;
uint32_t winDuration;
mCslTimer.FireAt(mCslSampleTimeLocal + periodUs - aTimeAhead - GetNextCycleDrift());
aTimeAhead -= kCslReceiveTimeAhead;
@@ -290,7 +290,7 @@ uint32_t SubMac::GetLocalTime(void)
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_LOCAL_TIME_SYNC
now = TimerMicro::GetNow().GetValue();
#else
now = static_cast<uint32_t>(Get<Radio>().GetNow());
now = Get<Radio>().GetNowAsRadioTime32();
#endif
return now;
@@ -321,8 +321,7 @@ void SubMac::LogReceived(RxFrame *aFrame)
(dst.GetType() == Address::kTypeExtended && dst.GetExtended() == GetExtAddress()));
LogDebg("Received frame in state (SubMac %s, CSL %s), timestamp %lu", StateToString(mState),
mIsCslSampling ? "CslSample" : "CslSleep",
ToUlong(static_cast<uint32_t>(aFrame->mInfo.mRxInfo.mTimestamp)));
mIsCslSampling ? "CslSample" : "CslSleep", ToUlong(ConvertRadioTime64To32(aFrame->GetTimestamp())));
VerifyOrExit(mState == kStateRadioSample);
@@ -330,7 +329,7 @@ void SubMac::LogReceived(RxFrame *aFrame)
ahead -= kMinReceiveOnAhead + kCslReceiveTimeAhead;
sampleTime = mCslSampleTimeRadio - mCslPeriod * kUsPerTenSymbols;
deviation = static_cast<uint32_t>(aFrame->mInfo.mRxInfo.mTimestamp) + kRadioHeaderPhrDuration - sampleTime;
deviation = ConvertRadioTime64To32(aFrame->GetTimestamp()) + kRadioHeaderPhrDuration - sampleTime;
// This logs three values (all in microseconds):
// - Absolute sample time in which the CSL receiver expected the MHR of the received frame.
+1 -1
View File
@@ -96,7 +96,7 @@ void SubMac::HandleWedReceiveAt(void)
if (mState != kStateDisabled)
{
IgnoreError(
Get<Radio>().ReceiveAt(mWakeupChannel, static_cast<uint32_t>(mWedSampleTimeRadio), mWakeupListenDuration));
Get<Radio>().ReceiveAt(mWakeupChannel, ConvertRadioTime64To32(mWedSampleTimeRadio), mWakeupListenDuration));
}
}
+1 -1
View File
@@ -100,7 +100,7 @@ Mac::TxFrame *WakeupTxScheduler::PrepareWakeupFrame(Mac::TxFrames &aTxFrames)
VerifyOrExit(frame->GenerateWakeupFrame(Get<Mac::Mac>().GetPanId(), mWakeupRequest, source) == kErrorNone,
frame = nullptr);
frame->SetTxDelayBaseTime(static_cast<uint32_t>(Get<Radio>().GetNow()));
frame->SetTxDelayBaseTime(Get<Radio>().GetNowAsRadioTime32());
frame->SetTxDelay(radioTxDelay);
frame->SetCsmaCaEnabled(kWakeupFrameTxCca);
frame->SetMaxCsmaBackoffs(0);
+2
View File
@@ -128,6 +128,8 @@ Error Radio::Transmit(Mac::TxFrame &aFrame)
}
#endif // OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
//---------------------------------------------------------------------------------------------------------------------
#if OPENTHREAD_CONFIG_RADIO_STATS_ENABLE && (OPENTHREAD_FTD || OPENTHREAD_MTD)
inline uint64_t UintSafeMinus(uint64_t aLhs, uint64_t aRhs) { return aLhs > aRhs ? (aLhs - aRhs) : 0; }
+16 -8
View File
@@ -46,6 +46,7 @@
#include "common/numeric_limits.hpp"
#include "common/time.hpp"
#include "mac/mac_frame.hpp"
#include "radio/radio_types.hpp"
namespace ot {
@@ -523,7 +524,7 @@ public:
* @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);
Error ReceiveAt(uint8_t aChannel, RadioTime32 aStart, uint32_t aDuration);
#endif
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
@@ -532,7 +533,7 @@ public:
*
* @param[in] aCslSampleTime The CSL sample time.
*/
void UpdateCslSampleTime(uint32_t aCslSampleTime);
void UpdateCslSampleTime(RadioTime32 aCslSampleTime);
/**
* Enables CSL sampling in radio.
@@ -567,7 +568,14 @@ public:
*
* @returns The current radio clock time.
*/
uint64_t GetNow(void);
RadioTime64 GetNow(void);
/**
* Get the current radio time in microseconds as a 32-bit value (lower 32 bits of the full radio time).
*
* @returns The current radio clock time as a `RadioTime32`.
*/
RadioTime32 GetNowAsRadioTime32(void) { return ConvertRadioTime64To32(GetNow()); }
/**
* Get the current accuracy, in units of ± ppm, of the clock used for scheduling CSL operations.
@@ -984,7 +992,7 @@ inline Error Radio::Receive(uint8_t aChannel)
}
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE || OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE
inline Error Radio::ReceiveAt(uint8_t aChannel, uint32_t aStart, uint32_t aDuration)
inline Error Radio::ReceiveAt(uint8_t aChannel, RadioTime32 aStart, uint32_t aDuration)
{
Error error = otPlatRadioReceiveAt(GetInstancePtr(), aChannel, aStart, aDuration);
#if OPENTHREAD_CONFIG_RADIO_STATS_ENABLE && (OPENTHREAD_FTD || OPENTHREAD_MTD)
@@ -998,7 +1006,7 @@ inline Error Radio::ReceiveAt(uint8_t aChannel, uint32_t aStart, uint32_t aDurat
#endif
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
inline void Radio::UpdateCslSampleTime(uint32_t aCslSampleTime)
inline void Radio::UpdateCslSampleTime(RadioTime32 aCslSampleTime)
{
otPlatRadioUpdateCslSampleTime(GetInstancePtr(), aCslSampleTime);
}
@@ -1013,7 +1021,7 @@ inline Error Radio::ResetCsl(void) { return otPlatRadioResetCsl(GetInstancePtr()
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE || OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE || \
OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
inline uint64_t Radio::GetNow(void) { return otPlatRadioGetNow(GetInstancePtr()); }
inline RadioTime64 Radio::GetNow(void) { return otPlatRadioGetNow(GetInstancePtr()); }
inline uint8_t Radio::GetCslAccuracy(void) { return otPlatRadioGetCslAccuracy(GetInstancePtr()); }
@@ -1112,7 +1120,7 @@ inline Error Radio::ReceiveAt(uint8_t, uint32_t, uint32_t) { return kErrorNone;
#endif
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
inline void Radio::UpdateCslSampleTime(uint32_t) {}
inline void Radio::UpdateCslSampleTime(RadioTime32) {}
inline Error Radio::EnableCsl(uint32_t, Mac::ShortAddress, const Mac::ExtAddress &) { return kErrorNotImplemented; }
@@ -1121,7 +1129,7 @@ inline Error Radio::ResetCsl(void) { return kErrorNotImplemented; }
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE || OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE || \
OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
inline uint64_t Radio::GetNow(void) { return NumericLimits<uint64_t>::kMax; }
inline RadioTime64 Radio::GetNow(void) { return NumericLimits<uint64_t>::kMax; }
inline uint8_t Radio::GetCslAccuracy(void) { return NumericLimits<uint8_t>::kMax; }
+5 -5
View File
@@ -281,7 +281,7 @@ extern "C" OT_TOOL_WEAK void otPlatRadioSetMacFrameCounterIfLarger(otInstance *a
extern "C" OT_TOOL_WEAK uint64_t otPlatTimeGet(void) { return UINT64_MAX; }
extern "C" OT_TOOL_WEAK uint64_t otPlatRadioGetNow(otInstance *aInstance)
extern "C" OT_TOOL_WEAK otRadioTime64 otPlatRadioGetNow(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
@@ -366,10 +366,10 @@ extern "C" OT_TOOL_WEAK otError otPlatRadioGetRegion(otInstance *aInstance, uint
return kErrorNotImplemented;
}
extern "C" OT_TOOL_WEAK otError otPlatRadioReceiveAt(otInstance *aInstance,
uint8_t aChannel,
uint32_t aStart,
uint32_t aDuration)
extern "C" OT_TOOL_WEAK otError otPlatRadioReceiveAt(otInstance *aInstance,
uint8_t aChannel,
otRadioTime32 aStart,
uint32_t aDuration)
{
OT_UNUSED_VARIABLE(aInstance);
OT_UNUSED_VARIABLE(aChannel);
+48
View File
@@ -0,0 +1,48 @@
/*
* Copyright (c) 2026, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* This file includes implementation of OpenThread radio types.
*/
#include "radio_types.hpp"
#include "common/time.hpp"
namespace ot {
bool IsRadioTimeStrictlyBefore(RadioTime32 aFirstTime, RadioTime32 aSecondTime)
{
Time firstTime(aFirstTime);
Time secondTime(aSecondTime);
return (firstTime < secondTime);
}
} // namespace ot
+77
View File
@@ -0,0 +1,77 @@
/*
* Copyright (c) 2026, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* This file includes definitions for OpenThread radio types.
*/
#ifndef OT_CORE_RADIO_RADIO_TYPES_HPP_
#define OT_CORE_RADIO_RADIO_TYPES_HPP_
#include "openthread-core-config.h"
#include <openthread/platform/radio.h>
namespace ot {
/**
* Represents a 64-bit radio time in microseconds referenced to a continuous monotonic local radio clock.
*/
typedef otRadioTime64 RadioTime64;
/**
* Represents a 32-bit radio time in microseconds (holds the lower 32 bits of a `RadioTime64`).
*/
typedef otRadioTime32 RadioTime32;
/**
* Converts a 64-bit radio time to a 32-bit radio time.
*
* @param[in] aRadioTime64 The 64-bit radio time to convert.
*
* @returns The converted 32-bit radio time (lower 32 bits of @p aRadioTime64).
*/
inline RadioTime32 ConvertRadioTime64To32(RadioTime64 aRadioTime64) { return static_cast<RadioTime32>(aRadioTime64); }
/**
* Indicates whether a given 32-bit radio time is strictly before another 32-bit radio time.
*
* This function correctly accounts for 32-bit microsecond counter roll-over.
*
* @param[in] aFirstTime The first 32-bit radio time to compare.
* @param[in] aSecondTime The second 32-bit radio time to compare.
*
* @retval TRUE @p aFirstTime is strictly before @p aSecondTime.
* @retval FALSE @p aFirstTime is not strictly before @p aSecondTime.
*/
bool IsRadioTimeStrictlyBefore(RadioTime32 aFirstTime, RadioTime32 aSecondTime);
} // namespace ot
#endif // OT_CORE_RADIO_RADIO_TYPES_HPP_
+6 -7
View File
@@ -135,12 +135,12 @@ uint32_t CslTxScheduler::GetNextCslTransmissionDelay(const CslNeighbor &aCslNeig
uint32_t &aDelayFromLastRx,
uint32_t aAheadUs) const
{
uint64_t radioNow = Get<Radio>().GetNow();
uint32_t periodInUs = aCslNeighbor.GetCslPeriod() * kUsPerTenSymbols;
// See CslTxScheduler::NeighborInfo::mCslPhase
/* see CslTxScheduler::NeighborInfo::mCslPhase */
uint64_t firstTxWindow = aCslNeighbor.GetLastRxTimestamp() + aCslNeighbor.GetCslPhase() * kUsPerTenSymbols;
uint64_t nextTxWindow = radioNow - (radioNow % periodInUs) + (firstTxWindow % periodInUs);
RadioTime64 radioNow = Get<Radio>().GetNow();
uint32_t periodInUs = aCslNeighbor.GetCslPeriod() * kUsPerTenSymbols;
RadioTime64 firstTxWindow = aCslNeighbor.GetLastRxTimestamp() + aCslNeighbor.GetCslPhase() * kUsPerTenSymbols;
RadioTime64 nextTxWindow = radioNow - (radioNow % periodInUs) + (firstTxWindow % periodInUs);
while (nextTxWindow < radioNow + aAheadUs)
{
@@ -222,8 +222,7 @@ Mac::TxFrame *CslTxScheduler::HandleFrameRequest(Mac::TxFrames &aTxFrames)
VerifyOrExit(delay <= mCslFrameRequestAheadUs + kFramePreparationGuardInterval, frame = nullptr);
frame->SetTxDelay(txDelay);
frame->SetTxDelayBaseTime(
static_cast<uint32_t>(mCslTxNeighbor->GetLastRxTimestamp())); // Only LSB part of the time is required.
frame->SetTxDelayBaseTime(ConvertRadioTime64To32(mCslTxNeighbor->GetLastRxTimestamp()));
frame->SetCsmaCaEnabled(true);
exit:
+2 -2
View File
@@ -95,8 +95,8 @@ public:
TimeMilli GetCslLastHeard(void) const { return mCslLastHeard; }
void SetCslLastHeard(TimeMilli aCslLastHeard) { mCslLastHeard = aCslLastHeard; }
uint64_t GetLastRxTimestamp(void) const { return mLastRxTimestamp; }
void SetLastRxTimestamp(uint64_t aLastRxTimestamp) { mLastRxTimestamp = aLastRxTimestamp; }
RadioTime64 GetLastRxTimestamp(void) const { return mLastRxTimestamp; }
void SetLastRxTimestamp(RadioTime64 aLastRxTimestamp) { mLastRxTimestamp = aLastRxTimestamp; }
private:
uint8_t mCslTxAttempts : 7; ///< Number of CSL triggered tx attempts.
+9 -9
View File
@@ -1627,15 +1627,15 @@ otError RadioSpinel::Transmit(otRadioFrame &aFrame)
#if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT && OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
if (mTransmitFrame->mInfo.mTxInfo.mIeInfo->mTimeIeOffset != 0)
{
uint64_t netRadioTime = otPlatRadioGetNow(mInstance);
uint64_t netSyncTime;
uint8_t *timeIe = mTransmitFrame->mPsdu + mTransmitFrame->mInfo.mTxInfo.mIeInfo->mTimeIeOffset;
otRadioTime64 netRadioTime = otPlatRadioGetNow(mInstance);
otRadioTime64 netSyncTime;
uint8_t *timeIe = mTransmitFrame->mPsdu + mTransmitFrame->mInfo.mTxInfo.mIeInfo->mTimeIeOffset;
if (netRadioTime == UINT64_MAX)
{
// If we can't get the radio time, get the platform time
netSyncTime = static_cast<uint64_t>(static_cast<int64_t>(otPlatTimeGet()) +
mTransmitFrame->mInfo.mTxInfo.mIeInfo->mNetworkTimeOffset);
netSyncTime = static_cast<otRadioTime64>(static_cast<int64_t>(otPlatTimeGet()) +
mTransmitFrame->mInfo.mTxInfo.mIeInfo->mNetworkTimeOffset);
}
else
{
@@ -1644,16 +1644,16 @@ otError RadioSpinel::Transmit(otRadioFrame &aFrame)
// If supported, add a delay and transmit the network time at a precise moment
#if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
transmitDelay = kTxWaitUs / 10;
mTransmitFrame->mInfo.mTxInfo.mTxDelayBaseTime = static_cast<uint32_t>(netRadioTime);
mTransmitFrame->mInfo.mTxInfo.mTxDelayBaseTime = static_cast<otRadioTime32>(netRadioTime);
mTransmitFrame->mInfo.mTxInfo.mTxDelay = transmitDelay;
#endif
netSyncTime = static_cast<uint64_t>(static_cast<int64_t>(netRadioTime) + transmitDelay +
mTransmitFrame->mInfo.mTxInfo.mIeInfo->mNetworkTimeOffset);
netSyncTime = static_cast<otRadioTime64>(static_cast<int64_t>(netRadioTime) + transmitDelay +
mTransmitFrame->mInfo.mTxInfo.mIeInfo->mNetworkTimeOffset);
}
*(timeIe++) = mTransmitFrame->mInfo.mTxInfo.mIeInfo->mTimeSyncSeq;
for (uint8_t i = 0; i < sizeof(uint64_t); i++)
for (uint8_t i = 0; i < sizeof(otRadioTime64); i++)
{
*(timeIe++) = static_cast<uint8_t>(netSyncTime & 0xff);
netSyncTime = netSyncTime >> 8;
+2 -2
View File
@@ -1709,8 +1709,8 @@ template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_MAC_RX_AT>(void)
SuccessOrExit(error = mDecoder.ReadUint8(channel));
{
uint64_t now = otPlatRadioGetNow(mInstance);
uint32_t start;
otRadioTime64 now = otPlatRadioGetNow(mInstance);
uint32_t start;
VerifyOrExit(when > now && (when - now) < UINT32_MAX, error = OT_ERROR_INVALID_ARGS);
+2 -2
View File
@@ -978,7 +978,7 @@ void otPlatRadioSetMacFrameCounterIfLarger(otInstance *aInstance, uint32_t aMacF
OT_UNUSED_VARIABLE(aInstance);
}
uint64_t otPlatRadioGetNow(otInstance *aInstance)
otRadioTime64 otPlatRadioGetNow(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
return GetRadioSpinel().GetNow();
@@ -1097,7 +1097,7 @@ otError otPlatRadioConfigureEnhAckProbing(otInstance *aInstance,
}
#endif
otError otPlatRadioReceiveAt(otInstance *aInstance, uint8_t aChannel, uint32_t aStart, uint32_t aDuration)
otError otPlatRadioReceiveAt(otInstance *aInstance, uint8_t aChannel, otRadioTime32 aStart, uint32_t aDuration)
{
OT_UNUSED_VARIABLE(aInstance);
OT_UNUSED_VARIABLE(aChannel);
+1 -1
View File
@@ -385,7 +385,7 @@ otError otPlatRadioSleep(otInstance *) { return OT_ERROR_NONE; }
otError otPlatRadioReceive(otInstance *, uint8_t aChannel) { return FakePlatform::CurrentPlatform().Receive(aChannel); }
otError otPlatRadioReceiveAt(otInstance *, uint8_t aChannel, uint32_t aStart, uint32_t aDuration)
otError otPlatRadioReceiveAt(otInstance *, uint8_t aChannel, otRadioTime32 aStart, uint32_t aDuration)
{
return FakePlatform::CurrentPlatform().ReceiveAt(aChannel, aStart, aDuration);
}
+2 -2
View File
@@ -156,7 +156,7 @@ exit:
return error;
}
uint64_t otPlatRadioGetNow(otInstance *aInstance)
otRadioTime64 otPlatRadioGetNow(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
@@ -332,7 +332,7 @@ otError otPlatRadioResetCsl(otInstance *aInstance)
return kErrorNone;
}
void otPlatRadioUpdateCslSampleTime(otInstance *aInstance, uint32_t aCslSampleTime)
void otPlatRadioUpdateCslSampleTime(otInstance *aInstance, otRadioTime32 aCslSampleTime)
{
AsNode(aInstance).mRadio.mRadioContext.mCslSampleTime = aCslSampleTime;
}
+1 -1
View File
@@ -457,7 +457,7 @@ OT_TOOL_WEAK otError otPlatRadioEnableCsl(otInstance *, uint32_t, otShortAddress
OT_TOOL_WEAK otError otPlatRadioResetCsl(otInstance *) { return OT_ERROR_NONE; }
OT_TOOL_WEAK void otPlatRadioUpdateCslSampleTime(otInstance *, uint32_t) {}
OT_TOOL_WEAK void otPlatRadioUpdateCslSampleTime(otInstance *, otRadioTime32) {}
OT_TOOL_WEAK uint8_t otPlatRadioGetCslAccuracy(otInstance *)
{