From ab0d6ae564919f595be3c8707d91409a31afbf4b Mon Sep 17 00:00:00 2001 From: Maciej Baczmanski Date: Thu, 11 Jul 2024 16:44:38 +0200 Subject: [PATCH] [low-power] enhance `mCslFrameRequestAheadUs` calculation for RCP Implement `otPlatRadioGetBusLatency` API, add optional `bus-latency` argument to ot-daemon and `diag rcp buslatency` diagnostic commands. Add APIs to update frame request ahead from runtime, which recalculate `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 --- include/openthread/instance.h | 2 +- include/openthread/platform/radio.h | 11 ++++++ include/openthread/thread_ftd.h | 9 +++++ src/core/api/thread_ftd_api.cpp | 8 ++++ src/core/diags/README.md | 20 ++++++++++ src/core/radio/radio_platform.cpp | 7 ++++ src/core/thread/csl_tx_scheduler.cpp | 11 ++++-- src/core/thread/csl_tx_scheduler.hpp | 8 +++- src/lib/spinel/radio_spinel.cpp | 57 ++++++++++++++++++++++++++++ src/lib/spinel/radio_spinel.hpp | 31 +++++++++++++++ src/posix/platform/radio.cpp | 24 +++++++----- src/posix/platform/radio_url.cpp | 1 + 12 files changed, 174 insertions(+), 15 deletions(-) diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 7f18df9028be..ef6e2d4f85b8 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 (427) +#define OPENTHREAD_API_VERSION (428) /** * @addtogroup api-instance diff --git a/include/openthread/platform/radio.h b/include/openthread/platform/radio.h index b1afdaa8352a..9cc5daf44e58 100644 --- a/include/openthread/platform/radio.h +++ b/include/openthread/platform/radio.h @@ -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); + /** * @} * diff --git a/include/openthread/thread_ftd.h b/include/openthread/thread_ftd.h index 14aab130762c..e758386e5388 100644 --- a/include/openthread/thread_ftd.h +++ b/include/openthread/thread_ftd.h @@ -953,6 +953,15 @@ void otThreadGetNextHopAndPathCost(otInstance *aInstance, uint16_t *aNextHopRloc16, uint8_t *aPathCost); +/** + * Calculates and updates value of CSL Frame Request Ahead, based on bus speed, bus latency and + * `OPENTHREAD_CONFIG_MAC_CSL_REQUEST_AHEAD_US`. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + */ +void otThreadUpdateFrameRequestAhead(otInstance *aInstance); + /** * @} * diff --git a/src/core/api/thread_ftd_api.cpp b/src/core/api/thread_ftd_api.cpp index 6b3f554ee063..d0dc59f9779e 100644 --- a/src/core/api/thread_ftd_api.cpp +++ b/src/core/api/thread_ftd_api.cpp @@ -428,4 +428,12 @@ void otThreadGetNextHopAndPathCost(otInstance *aInstance, (aPathCost != nullptr) ? *aPathCost : pathcost); } +#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE +void otThreadUpdateFrameRequestAhead(otInstance *aInstance) +{ + AsCoreType(aInstance).Get().UpdateFrameRequestAhead(); +} + +#endif // OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE + #endif // OPENTHREAD_FTD diff --git a/src/core/diags/README.md b/src/core/diags/README.md index ee447b3f9aaf..78ff352e8c68 100644 --- a/src/core/diags/README.md +++ b/src/core/diags/README.md @@ -20,6 +20,7 @@ The diagnostics module supports common diagnostics features that are listed belo - [diag stats](#diag-stats) - [diag gpio](#diag-gpio-get-gpio) - [diag stop](#diag-stop) +- [diag rcp](#diag-rcp) ### diag @@ -359,6 +360,25 @@ Stop RCP diagnostics mode. Done ``` +### diag rcp buslatency + +Get the bus latency in microseconds between the host and the radio chip. + +```bash +> rcp buslatency +0 +Done +``` + +### diag rcp buslatency \ + +Set the bus latency in microseconds between the host and the radio chip. + +```bash +> rcp buslatency 1000 +Done +``` + #### diag rcp channel \ Set the RCP IEEE 802.15.4 Channel value for diagnostics module. diff --git a/src/core/radio/radio_platform.cpp b/src/core/radio/radio_platform.cpp index 0f6001246f76..05f6198a0150 100644 --- a/src/core/radio/radio_platform.cpp +++ b/src/core/radio/radio_platform.cpp @@ -250,6 +250,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) { diff --git a/src/core/thread/csl_tx_scheduler.cpp b/src/core/thread/csl_tx_scheduler.cpp index 35a070c1ec42..0f28c43a0d6c 100644 --- a/src/core/thread/csl_tx_scheduler.cpp +++ b/src/core/thread/csl_tx_scheduler.cpp @@ -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) diff --git a/src/core/thread/csl_tx_scheduler.hpp b/src/core/thread/csl_tx_scheduler.hpp index 7b3d12940911..5073706c7e85 100644 --- a/src/core/thread/csl_tx_scheduler.hpp +++ b/src/core/thread/csl_tx_scheduler.hpp @@ -231,11 +231,17 @@ class CslTxScheduler : public InstanceLocator, private NonCopyable */ 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; diff --git a/src/lib/spinel/radio_spinel.cpp b/src/lib/spinel/radio_spinel.cpp index 8b3e2b5ea6f0..ce96d6d02ec7 100644 --- a/src/lib/spinel/radio_spinel.cpp +++ b/src/lib/spinel/radio_spinel.cpp @@ -39,6 +39,7 @@ #include #include +#include #include #include @@ -78,6 +79,7 @@ RadioSpinel::RadioSpinel(void) , mPanId(0xffff) , mChannel(0) , mRxSensitivity(0) + , mBusLatency(0) , mState(kStateDisabled) , mIsPromiscuous(false) , mRxOnWhenIdle(true) @@ -1774,6 +1776,48 @@ void RadioSpinel::GetDiagOutputCallback(otPlatDiagOutputCallback &aCallback, voi aContext = mOutputContext; } +otError RadioSpinel::PlatDiagProcess(uint8_t aArgsLength, char *aArgs[]) +{ + otError error = OT_ERROR_NONE; + char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE] = {'\0'}; + char *cur = cmd; + char *end = cmd + sizeof(cmd); + + if (strcmp(aArgs[0], "rcp") == 0) + { + if (strcmp(aArgs[1], "buslatency") == 0) + { + if (aArgsLength == 2) + { + uint32_t latency; + char *endptr; + + latency = (uint32_t)strtoul(aArgs[2], &endptr, 0); + VerifyOrExit(*endptr == '\0', error = OT_ERROR_INVALID_ARGS); + + SetBusLatency(latency); + ExitNow(); + } + else + { + uint32_t latency = GetBusLatency(); + PlatDiagOutput("%lu\n", ToUlong(latency)); + ExitNow(); + } + } + } + + for (uint8_t index = 0; (index < aArgsLength) && (cur < end); index++) + { + cur += snprintf(cur, static_cast(end - cur), "%s ", aArgs[index]); + } + + SuccessOrExit(error = PlatDiagProcess(cmd)); + +exit: + return error; +} + otError RadioSpinel::PlatDiagProcess(const char *aString) { return Set(SPINEL_PROP_NEST_STREAM_MFG, SPINEL_DATATYPE_UTF8_S, aString); @@ -1903,6 +1947,19 @@ 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 OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE + if (IsEnabled()) + { + otThreadUpdateFrameRequestAhead(mInstance); + } +#endif // OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE +} + void RadioSpinel::HandleRcpUnexpectedReset(spinel_status_t aStatus) { OT_UNUSED_VARIABLE(aStatus); diff --git a/src/lib/spinel/radio_spinel.hpp b/src/lib/spinel/radio_spinel.hpp index bfee961760a0..aa105ea74220 100644 --- a/src/lib/spinel/radio_spinel.hpp +++ b/src/lib/spinel/radio_spinel.hpp @@ -680,6 +680,20 @@ class RadioSpinel : private Logger */ bool IsDiagEnabled(void) const { return mDiagMode; } + /** + * Processes platform 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. + * @retval OT_ERROR_BUSY Failed due to another operation is on going. + * @retval OT_ERROR_RESPONSE_TIMEOUT Failed due to no response received from the transceiver. + * + */ + otError PlatDiagProcess(uint8_t aArgsLength, char *aArgs[]); + /** * Processes platform diagnostics commands. * @@ -857,6 +871,22 @@ class RadioSpinel : private Logger */ 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. * @@ -1253,6 +1283,7 @@ class RadioSpinel : private Logger otError mTxError; static otExtAddress sIeeeEui64; static otRadioCaps sRadioCaps; + uint32_t mBusLatency; State mState; bool mIsPromiscuous : 1; ///< Promiscuous mode. diff --git a/src/posix/platform/radio.cpp b/src/posix/platform/radio.cpp index c82b07ebfd13..aa80bcb75258 100644 --- a/src/posix/platform/radio.cpp +++ b/src/posix/platform/radio.cpp @@ -130,6 +130,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 @@ -548,11 +555,7 @@ 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); #if OPENTHREAD_POSIX_CONFIG_RCP_CAPS_DIAG_ENABLE if (strcmp(aArgs[0], "rcpcaps") == 0) @@ -561,12 +564,7 @@ otError otPlatDiagProcess(otInstance *aInstance, uint8_t aArgsLength, char *aArg } #endif - for (uint8_t index = 0; (index < aArgsLength) && (cur < end); index++) - { - cur += snprintf(cur, static_cast(end - cur), "%s ", aArgs[index]); - } - - return GetRadioSpinel().PlatDiagProcess(cmd); + return GetRadioSpinel().PlatDiagProcess(aArgsLength, aArgs); } void otPlatDiagModeSet(bool aMode) @@ -900,6 +898,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) { diff --git a/src/posix/platform/radio_url.cpp b/src/posix/platform/radio_url.cpp index db0c13461c10..57e1df0c25a6 100644 --- a/src/posix/platform/radio_url.cpp +++ b/src/posix/platform/radio_url.cpp @@ -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"