mirror of
https://github.com/espressif/openthread.git
synced 2026-08-28 04:49:54 +00:00
[low-power] enhance mCslFrameRequestAheadUs calculation for RCP (#10488)
Implement `otPlatRadioGetBusLatency` API, add optional `bus-latency` argument to ot-daemon and `diag radiospinel buslatency`diagnostic commands. Add callback to notify that the bus latency has been updated and update frame request ahead from runtime, by recalculating the `mCslFrameRequestAheadUs` value. Changes allow setting a bus latency while starting a new session between host and RCP device. This way, one host can be connected to different devices and vice versa, ensuring that the latency will be added to `mCslFrameRequestAheadUs` calculations and CSL tx requests will not be sent too late. Signed-off-by: Maciej Baczmanski <[email protected]>
This commit is contained in:
@@ -53,7 +53,7 @@ extern "C" {
|
||||
* @note This number versions both OpenThread platform and user APIs.
|
||||
*
|
||||
*/
|
||||
#define OPENTHREAD_API_VERSION (429)
|
||||
#define OPENTHREAD_API_VERSION (430)
|
||||
|
||||
/**
|
||||
* @addtogroup api-instance
|
||||
|
||||
@@ -757,6 +757,17 @@ uint64_t otPlatRadioGetNow(otInstance *aInstance);
|
||||
*/
|
||||
uint32_t otPlatRadioGetBusSpeed(otInstance *aInstance);
|
||||
|
||||
/**
|
||||
* Get the bus latency in microseconds between the host and the radio chip.
|
||||
*
|
||||
* @param[in] aInstance A pointer to an OpenThread instance.
|
||||
*
|
||||
* @returns The bus latency in microseconds between the host and the radio chip.
|
||||
* Return 0 when the MAC and above layer and Radio layer resides on the same chip.
|
||||
*
|
||||
*/
|
||||
uint32_t otPlatRadioGetBusLatency(otInstance *aInstance);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*
|
||||
@@ -1003,6 +1014,14 @@ otError otPlatRadioEnergyScan(otInstance *aInstance, uint8_t aScanChannel, uint1
|
||||
*/
|
||||
extern void otPlatRadioEnergyScanDone(otInstance *aInstance, int8_t aEnergyScanMaxRssi);
|
||||
|
||||
/**
|
||||
* The radio driver calls this method to notify OpenThread that the spinel bus latency has been changed.
|
||||
*
|
||||
* @param[in] aInstance The OpenThread instance structure.
|
||||
*
|
||||
*/
|
||||
extern void otPlatRadioBusLatencyChanged(otInstance *aInstance);
|
||||
|
||||
/**
|
||||
* Enable/Disable source address match feature.
|
||||
*
|
||||
|
||||
@@ -229,6 +229,12 @@ public:
|
||||
*/
|
||||
void HandleEnergyScanDone(int8_t aMaxRssi);
|
||||
|
||||
/**
|
||||
* This callback method handles "Bus Latency Changed" event from radio platform.
|
||||
*
|
||||
*/
|
||||
void HandleBusLatencyChanged(void);
|
||||
|
||||
#if OPENTHREAD_CONFIG_DIAG_ENABLE
|
||||
/**
|
||||
* This callback method handles a "Receive Done" event from radio platform when diagnostics mode is enabled.
|
||||
|
||||
@@ -58,6 +58,13 @@ void Radio::Callbacks::HandleTransmitDone(Mac::TxFrame &aFrame, Mac::RxFrame *aA
|
||||
|
||||
void Radio::Callbacks::HandleEnergyScanDone(int8_t aMaxRssi) { Get<Mac::SubMac>().HandleEnergyScanDone(aMaxRssi); }
|
||||
|
||||
void Radio::Callbacks::HandleBusLatencyChanged(void)
|
||||
{
|
||||
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
|
||||
Get<CslTxScheduler>().UpdateFrameRequestAhead();
|
||||
#endif
|
||||
}
|
||||
|
||||
#if OPENTHREAD_CONFIG_DIAG_ENABLE
|
||||
void Radio::Callbacks::HandleDiagsReceiveDone(Mac::RxFrame *aFrame, Error aError)
|
||||
{
|
||||
|
||||
@@ -116,6 +116,17 @@ exit:
|
||||
return;
|
||||
}
|
||||
|
||||
extern "C" void otPlatRadioBusLatencyChanged(otInstance *aInstance)
|
||||
{
|
||||
Instance &instance = AsCoreType(aInstance);
|
||||
|
||||
VerifyOrExit(instance.IsInitialized());
|
||||
instance.Get<Radio::Callbacks>().HandleBusLatencyChanged();
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
#if OPENTHREAD_CONFIG_DIAG_ENABLE
|
||||
extern "C" void otPlatDiagRadioReceiveDone(otInstance *aInstance, otRadioFrame *aFrame, otError aError)
|
||||
{
|
||||
@@ -153,6 +164,8 @@ extern "C" void otPlatRadioTxDone(otInstance *, otRadioFrame *, otRadioFrame *,
|
||||
|
||||
extern "C" void otPlatRadioEnergyScanDone(otInstance *, int8_t) {}
|
||||
|
||||
extern "C" void otPlatRadioBusLatencyChanged(otInstance *) {}
|
||||
|
||||
#if OPENTHREAD_CONFIG_DIAG_ENABLE
|
||||
extern "C" void otPlatDiagRadioReceiveDone(otInstance *, otRadioFrame *, otError) {}
|
||||
|
||||
@@ -250,6 +263,13 @@ OT_TOOL_WEAK uint32_t otPlatRadioGetBusSpeed(otInstance *aInstance)
|
||||
return 0;
|
||||
}
|
||||
|
||||
OT_TOOL_WEAK uint32_t otPlatRadioGetBusLatency(otInstance *aInstance)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
|
||||
OT_TOOL_WEAK otError otPlatRadioResetCsl(otInstance *aInstance)
|
||||
{
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
|
||||
#include "common/locator_getters.hpp"
|
||||
#include "common/log.hpp"
|
||||
#include "common/num_utils.hpp"
|
||||
#include "common/time.hpp"
|
||||
#include "mac/mac.hpp"
|
||||
|
||||
@@ -68,16 +69,20 @@ CslTxScheduler::CslTxScheduler(Instance &aInstance)
|
||||
, mFrameContext()
|
||||
, mCallbacks(aInstance)
|
||||
{
|
||||
InitFrameRequestAhead();
|
||||
UpdateFrameRequestAhead();
|
||||
}
|
||||
|
||||
void CslTxScheduler::InitFrameRequestAhead(void)
|
||||
void CslTxScheduler::UpdateFrameRequestAhead(void)
|
||||
{
|
||||
uint32_t busSpeedHz = otPlatRadioGetBusSpeed(&GetInstance());
|
||||
uint32_t busLatency = otPlatRadioGetBusLatency(&GetInstance());
|
||||
|
||||
// longest frame on bus is 127 bytes with some metadata, use 150 bytes for bus Tx time estimation
|
||||
uint32_t busTxTimeUs = ((busSpeedHz == 0) ? 0 : (150 * 8 * 1000000 + busSpeedHz - 1) / busSpeedHz);
|
||||
|
||||
mCslFrameRequestAheadUs = OPENTHREAD_CONFIG_MAC_CSL_REQUEST_AHEAD_US + busTxTimeUs;
|
||||
mCslFrameRequestAheadUs = OPENTHREAD_CONFIG_MAC_CSL_REQUEST_AHEAD_US + busTxTimeUs + busLatency;
|
||||
LogInfo("Bus TX Time: %lu usec, Latency: %lu usec. Calculated CSL Frame Request Ahead: %lu usec",
|
||||
ToUlong(busTxTimeUs), ToUlong(busLatency), ToUlong(mCslFrameRequestAheadUs));
|
||||
}
|
||||
|
||||
void CslTxScheduler::Update(void)
|
||||
|
||||
@@ -231,11 +231,17 @@ public:
|
||||
*/
|
||||
void Clear(void);
|
||||
|
||||
/**
|
||||
* Updates the value of `mCslFrameRequestAheadUs`, based on bus speed, bus latency
|
||||
* and `OPENTHREAD_CONFIG_MAC_CSL_REQUEST_AHEAD_US`.
|
||||
*
|
||||
*/
|
||||
void UpdateFrameRequestAhead(void);
|
||||
|
||||
private:
|
||||
// Guard time in usec to add when checking delay while preparing the CSL frame for tx.
|
||||
static constexpr uint32_t kFramePreparationGuardInterval = 1500;
|
||||
|
||||
void InitFrameRequestAhead(void);
|
||||
void RescheduleCslTx(void);
|
||||
|
||||
uint32_t GetNextCslTransmissionDelay(const Child &aChild, uint32_t &aDelayFromLastRx, uint32_t aAheadUs) const;
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
# OpenThread Diagnostics - Radio Spinel diagnostic commands
|
||||
|
||||
This module provides Spinel based radio transceiver diagnostic commands.
|
||||
|
||||
`OPENTHREAD_CONFIG_DIAG_ENABLE` is required.
|
||||
|
||||
## Command List
|
||||
|
||||
- [buslatency](#buslatency)
|
||||
|
||||
## Command Details
|
||||
|
||||
### buslatency
|
||||
|
||||
Get the bus latency in microseconds between the host and the radio chip.
|
||||
|
||||
```bash
|
||||
> diag radiospinel buslatency
|
||||
0
|
||||
Done
|
||||
```
|
||||
|
||||
#### buslatency \<buslatency\>
|
||||
|
||||
Set the bus latency in microseconds between the host and the radio chip.
|
||||
|
||||
```bash
|
||||
> diag radiospinel buslatency 1000
|
||||
Done
|
||||
```
|
||||
@@ -79,6 +79,7 @@ RadioSpinel::RadioSpinel(void)
|
||||
, mPanId(0xffff)
|
||||
, mChannel(0)
|
||||
, mRxSensitivity(0)
|
||||
, mBusLatency(0)
|
||||
, mState(kStateDisabled)
|
||||
, mIsPromiscuous(false)
|
||||
, mRxOnWhenIdle(true)
|
||||
@@ -1785,6 +1786,41 @@ void RadioSpinel::GetDiagOutputCallback(otPlatDiagOutputCallback &aCallback, voi
|
||||
aContext = mOutputContext;
|
||||
}
|
||||
|
||||
otError RadioSpinel::RadioSpinelDiagProcess(char *aArgs[], uint8_t aArgsLength)
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
|
||||
VerifyOrExit(aArgsLength > 1, error = OT_ERROR_INVALID_ARGS);
|
||||
|
||||
aArgs++;
|
||||
aArgsLength--;
|
||||
|
||||
if (strcmp(aArgs[0], "buslatency") == 0)
|
||||
{
|
||||
if (aArgsLength == 1)
|
||||
{
|
||||
PlatDiagOutput("%lu\n", ToUlong(GetBusLatency()));
|
||||
}
|
||||
else if (aArgsLength == 2)
|
||||
{
|
||||
uint32_t busLatency;
|
||||
char *endptr;
|
||||
|
||||
busLatency = static_cast<uint32_t>(strtoul(aArgs[1], &endptr, 0));
|
||||
VerifyOrExit(*endptr == '\0', error = OT_ERROR_INVALID_ARGS);
|
||||
|
||||
SetBusLatency(busLatency);
|
||||
}
|
||||
else
|
||||
{
|
||||
error = OT_ERROR_INVALID_ARGS;
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
otError RadioSpinel::PlatDiagProcess(const char *aString)
|
||||
{
|
||||
return Set(SPINEL_PROP_NEST_STREAM_MFG, SPINEL_DATATYPE_UTF8_S, aString);
|
||||
@@ -1912,6 +1948,18 @@ uint64_t RadioSpinel::GetNow(void) { return (mIsTimeSynced) ? (otPlatTimeGet() +
|
||||
|
||||
uint32_t RadioSpinel::GetBusSpeed(void) const { return GetSpinelDriver().GetSpinelInterface()->GetBusSpeed(); }
|
||||
|
||||
uint32_t RadioSpinel::GetBusLatency(void) const { return mBusLatency; }
|
||||
|
||||
void RadioSpinel::SetBusLatency(uint32_t aBusLatency)
|
||||
{
|
||||
mBusLatency = aBusLatency;
|
||||
|
||||
if (IsEnabled() && mCallbacks.mBusLatencyChanged != nullptr)
|
||||
{
|
||||
mCallbacks.mBusLatencyChanged(mInstance);
|
||||
}
|
||||
}
|
||||
|
||||
void RadioSpinel::HandleRcpUnexpectedReset(spinel_status_t aStatus)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aStatus);
|
||||
|
||||
@@ -87,6 +87,14 @@ struct RadioSpinelCallbacks
|
||||
*/
|
||||
void (*mEnergyScanDone)(otInstance *aInstance, int8_t aMaxRssi);
|
||||
|
||||
/**
|
||||
* This callback notifies user of `RadioSpinel` that the bus latency has been changed.
|
||||
*
|
||||
* @param[in] aInstance The OpenThread instance structure.
|
||||
*
|
||||
*/
|
||||
void (*mBusLatencyChanged)(otInstance *aInstance);
|
||||
|
||||
/**
|
||||
* This callback notifies user of `RadioSpinel` that the transmission has started.
|
||||
*
|
||||
@@ -682,6 +690,18 @@ public:
|
||||
*/
|
||||
bool IsDiagEnabled(void) const { return mDiagMode; }
|
||||
|
||||
/**
|
||||
* Processes RadioSpinel - specific diagnostics commands.
|
||||
*
|
||||
* @param[in] aArgsLength The number of arguments in @p aArgs.
|
||||
* @param[in] aArgs The arguments of diagnostics command line.
|
||||
*
|
||||
* @retval OT_ERROR_NONE Succeeded.
|
||||
* @retval OT_ERROR_INVALID_ARGS Failed due to invalid arguments provided.
|
||||
*
|
||||
*/
|
||||
otError RadioSpinelDiagProcess(char *aArgs[], uint8_t aArgsLength);
|
||||
|
||||
/**
|
||||
* Processes platform diagnostics commands.
|
||||
*
|
||||
@@ -859,6 +879,22 @@ public:
|
||||
*/
|
||||
uint32_t GetBusSpeed(void) const;
|
||||
|
||||
/**
|
||||
* Returns the bus latency between the host and the radio.
|
||||
*
|
||||
* @returns Bus latency in microseconds.
|
||||
*
|
||||
*/
|
||||
uint32_t GetBusLatency(void) const;
|
||||
|
||||
/**
|
||||
* Sets the bus latency between the host and the radio.
|
||||
*
|
||||
* @param[in] aBusLatency Bus latency in microseconds.
|
||||
*
|
||||
*/
|
||||
void SetBusLatency(uint32_t aBusLatency);
|
||||
|
||||
/**
|
||||
* Returns the co-processor sw version string.
|
||||
*
|
||||
@@ -1233,6 +1269,7 @@ private:
|
||||
otError mTxError;
|
||||
static otExtAddress sIeeeEui64;
|
||||
static otRadioCaps sRadioCaps;
|
||||
uint32_t mBusLatency;
|
||||
|
||||
State mState;
|
||||
bool mIsPromiscuous : 1; ///< Promiscuous mode.
|
||||
|
||||
@@ -89,10 +89,11 @@ void Radio::Init(const char *aUrl)
|
||||
callbacks.mDiagReceiveDone = otPlatDiagRadioReceiveDone;
|
||||
callbacks.mDiagTransmitDone = otPlatDiagRadioTransmitDone;
|
||||
#endif // OPENTHREAD_CONFIG_DIAG_ENABLE
|
||||
callbacks.mEnergyScanDone = otPlatRadioEnergyScanDone;
|
||||
callbacks.mReceiveDone = otPlatRadioReceiveDone;
|
||||
callbacks.mTransmitDone = otPlatRadioTxDone;
|
||||
callbacks.mTxStarted = otPlatRadioTxStarted;
|
||||
callbacks.mEnergyScanDone = otPlatRadioEnergyScanDone;
|
||||
callbacks.mBusLatencyChanged = otPlatRadioBusLatencyChanged;
|
||||
callbacks.mReceiveDone = otPlatRadioReceiveDone;
|
||||
callbacks.mTransmitDone = otPlatRadioTxDone;
|
||||
callbacks.mTxStarted = otPlatRadioTxStarted;
|
||||
|
||||
resetRadio = !mRadioUrl.HasParam("no-reset");
|
||||
skipCompatibilityCheck = mRadioUrl.HasParam("skip-rcp-compatibility-check");
|
||||
@@ -135,6 +136,13 @@ void Radio::ProcessRadioUrl(const RadioUrl &aRadioUrl)
|
||||
SuccessOrDie(otPlatRadioSetRegion(gInstance, regionCode));
|
||||
}
|
||||
|
||||
if (aRadioUrl.HasParam("bus-latency"))
|
||||
{
|
||||
uint32_t busLatency;
|
||||
SuccessOrDie(aRadioUrl.ParseUint32("bus-latency", busLatency));
|
||||
mRadioSpinel.SetBusLatency(busLatency);
|
||||
}
|
||||
|
||||
ProcessMaxPowerTable(aRadioUrl);
|
||||
|
||||
#if OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
|
||||
@@ -553,25 +561,36 @@ void otPlatDiagSetOutputCallback(otInstance *aInstance, otPlatDiagOutputCallback
|
||||
|
||||
otError otPlatDiagProcess(otInstance *aInstance, uint8_t aArgsLength, char *aArgs[])
|
||||
{
|
||||
// deliver the platform specific diags commands to radio only ncp.
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE] = {'\0'};
|
||||
char *cur = cmd;
|
||||
char *end = cmd + sizeof(cmd);
|
||||
otError error = OT_ERROR_NONE;
|
||||
char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE] = {'\0'};
|
||||
char *cur = cmd;
|
||||
char *end = cmd + sizeof(cmd);
|
||||
|
||||
#if OPENTHREAD_POSIX_CONFIG_RCP_CAPS_DIAG_ENABLE
|
||||
if (strcmp(aArgs[0], "rcpcaps") == 0)
|
||||
{
|
||||
return GetRcpCapsDiag().DiagProcess(aArgs, aArgsLength);
|
||||
error = GetRcpCapsDiag().DiagProcess(aArgs, aArgsLength);
|
||||
ExitNow();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (strcmp(aArgs[0], "radiospinel") == 0)
|
||||
{
|
||||
error = GetRadioSpinel().RadioSpinelDiagProcess(aArgs, aArgsLength);
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
for (uint8_t index = 0; (index < aArgsLength) && (cur < end); index++)
|
||||
{
|
||||
cur += snprintf(cur, static_cast<size_t>(end - cur), "%s ", aArgs[index]);
|
||||
}
|
||||
|
||||
return GetRadioSpinel().PlatDiagProcess(cmd);
|
||||
// deliver the platform specific diags commands to radio only ncp.
|
||||
error = GetRadioSpinel().PlatDiagProcess(cmd);
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
void otPlatDiagModeSet(bool aMode)
|
||||
@@ -905,6 +924,12 @@ uint32_t otPlatRadioGetBusSpeed(otInstance *aInstance)
|
||||
return GetRadioSpinel().GetBusSpeed();
|
||||
}
|
||||
|
||||
uint32_t otPlatRadioGetBusLatency(otInstance *aInstance)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
return GetRadioSpinel().GetBusLatency();
|
||||
}
|
||||
|
||||
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE || OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
|
||||
uint8_t otPlatRadioGetCslAccuracy(otInstance *aInstance)
|
||||
{
|
||||
|
||||
@@ -121,6 +121,7 @@ const char *otSysGetRadioUrlHelpString(void)
|
||||
" fem-lnagain[=dbm] Set the Rx LNA gain in dBm of the external FEM.\n"
|
||||
" no-reset Do not send Spinel reset command to RCP on initialization.\n"
|
||||
" skip-rcp-compatibility-check Skip checking RCP API version and capabilities during initialization.\n"
|
||||
" bus-latency[=usec] Communication latency in usec, default is 0.\n"
|
||||
#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE
|
||||
" iid Set the Spinel Interface ID for this process. Valid values are 0-3.\n"
|
||||
" iid-list List of IIDs a host can subscribe to receive spinel frames other than \n"
|
||||
|
||||
Reference in New Issue
Block a user