Skip to content

Commit eacec3d

Browse files
fg-cfhcarlescufi
authored andcommitted
modules: openthread: radio: encapsulate OT 32-bit timestamp
OT does not have 64 bit timestamp support. This is a limitation of OT and not of the IEEE 802.15.4 driver API. Therefore any workaround related to such OT idiosyncracies should be encapsulated inside the OT adapatation layer. This change moves the OT-specific conversion of OT 32 bit timestamps to Zephyr 64 bit timestamps into the OT adaptation layer. Signed-off-by: Florian Grandel <[email protected]>
1 parent 2bf231f commit eacec3d

File tree

3 files changed

+100
-91
lines changed

3 files changed

+100
-91
lines changed

drivers/ieee802154/ieee802154_nrf5.c

Lines changed: 4 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -484,77 +484,6 @@ static bool nrf5_tx_csma_ca(struct net_pkt *pkt, uint8_t *payload)
484484
#endif
485485

486486
#if defined(CONFIG_NET_PKT_TXTIME)
487-
/**
488-
* @brief Convert 32-bit target time to absolute 64-bit target time.
489-
*
490-
* @param target_time_ns_wrapped time in nanoseconds referred to the radio clock
491-
* modulo (UINT32_MAX * NSEC_PER_USEC).
492-
*/
493-
static uint64_t target_time_convert_to_64_bits(uint64_t target_time_ns_wrapped)
494-
{
495-
/**
496-
* Target time is provided as two 32-bit integers defining a moment in time
497-
* in microsecond domain. In order to use bit-shifting instead of modulo
498-
* division, calculations are performed in microsecond domain, not in RTC ticks.
499-
*
500-
* The target time can point to a moment in the future, but can be overdue
501-
* as well. In order to determine what's the case and correctly set the
502-
* absolute target time, it's necessary to compare the least significant
503-
* 32 bits of the current time, 64-bit time with the provided 32-bit target
504-
* time. Let's assume that half of the 32-bit range can be used for specifying
505-
* target times in the future, and the other half - in the past.
506-
*/
507-
uint32_t target_time_us_wrapped = target_time_ns_wrapped / NSEC_PER_USEC;
508-
uint64_t now_us = nrf_802154_time_get();
509-
uint32_t now_us_wrapped = (uint32_t)now_us;
510-
uint32_t time_diff = target_time_us_wrapped - now_us_wrapped;
511-
uint64_t result = UINT64_C(0);
512-
513-
if (time_diff < 0x80000000) {
514-
/**
515-
* Target time is assumed to be in the future. Check if a 32-bit overflow
516-
* occurs between the current time and the target time.
517-
*/
518-
if (now_us_wrapped > target_time_us_wrapped) {
519-
/**
520-
* Add a 32-bit overflow and replace the least significant 32 bits
521-
* with the provided target time.
522-
*/
523-
result = now_us + UINT32_MAX + 1;
524-
result &= ~(uint64_t)UINT32_MAX;
525-
result |= target_time_us_wrapped;
526-
} else {
527-
/**
528-
* Leave the most significant 32 bits and replace the least significant
529-
* 32 bits with the provided target time.
530-
*/
531-
result = (now_us & (~(uint64_t)UINT32_MAX)) | target_time_us_wrapped;
532-
}
533-
} else {
534-
/**
535-
* Target time is assumed to be in the past. Check if a 32-bit overflow
536-
* occurs between the target time and the current time.
537-
*/
538-
if (now_us_wrapped > target_time_us_wrapped) {
539-
/**
540-
* Leave the most significant 32 bits and replace the least significant
541-
* 32 bits with the provided target time.
542-
*/
543-
result = (now_us & (~(uint64_t)UINT32_MAX)) | target_time_us_wrapped;
544-
} else {
545-
/**
546-
* Subtract a 32-bit overflow and replace the least significant
547-
* 32 bits with the provided target time.
548-
*/
549-
result = now_us - UINT32_MAX - 1;
550-
result &= ~(uint64_t)UINT32_MAX;
551-
result |= target_time_us_wrapped;
552-
}
553-
}
554-
555-
return result;
556-
}
557-
558487
static bool nrf5_tx_at(struct nrf5_802154_data *nrf5_radio, struct net_pkt *pkt,
559488
uint8_t *payload, enum ieee802154_tx_mode mode)
560489
{
@@ -598,7 +527,7 @@ static bool nrf5_tx_at(struct nrf5_802154_data *nrf5_radio, struct net_pkt *pkt,
598527
.extra_cca_attempts = max_extra_cca_attempts,
599528
#endif
600529
};
601-
uint64_t tx_at = target_time_convert_to_64_bits(net_ptp_time_to_ns(net_pkt_timestamp(pkt)));
530+
uint64_t tx_at = net_ptp_time_to_ns(net_pkt_timestamp(pkt)) / NSEC_PER_USEC;
602531

603532
return nrf_802154_transmit_raw_at(payload, tx_at, &metadata);
604533
}
@@ -984,13 +913,7 @@ static int nrf5_configure(const struct device *dev,
984913

985914
#if defined(CONFIG_IEEE802154_CSL_ENDPOINT)
986915
case IEEE802154_CONFIG_CSL_RX_TIME: {
987-
/*
988-
* `target_time_convert_to_64_bits()` is a workaround until OpenThread (the only
989-
* CSL user in Zephyr so far) is able to schedule RX windows using 64-bit time.
990-
*/
991-
uint64_t csl_rx_time = target_time_convert_to_64_bits(config->csl_rx_time);
992-
993-
nrf_802154_csl_writer_anchor_time_set(csl_rx_time);
916+
nrf_802154_csl_writer_anchor_time_set(config->csl_rx_time / NSEC_PER_USEC);
994917
} break;
995918

996919
case IEEE802154_CONFIG_RX_SLOT: {
@@ -1000,13 +923,9 @@ static int nrf5_configure(const struct device *dev,
1000923
* calculated as if the following reception windows were at times
1001924
* anchor_time + n * csl_period. The previously set
1002925
* anchor_time will be used for calculations.
1003-
*
1004-
* `target_time_convert_to_64_bits()` is a workaround until OpenThread
1005-
* is able to schedule RX windows using 64-bit time.
1006926
*/
1007-
uint64_t start = target_time_convert_to_64_bits(config->rx_slot.start);
1008-
1009-
nrf_802154_receive_at(start, config->rx_slot.duration / NSEC_PER_USEC,
927+
nrf_802154_receive_at(config->rx_slot.start / NSEC_PER_USEC,
928+
config->rx_slot.duration / NSEC_PER_USEC,
1010929
config->rx_slot.channel, DRX_SLOT_RX);
1011930
} break;
1012931

modules/openthread/platform/radio.c

Lines changed: 87 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_OPENTHREAD_L2_LOG_LEVEL);
3333
#include <openthread/instance.h>
3434
#include <openthread/platform/radio.h>
3535
#include <openthread/platform/diag.h>
36+
#include <openthread/platform/time.h>
3637
#include <openthread/message.h>
3738

3839
#include "platform-zephyr.h"
@@ -237,6 +238,84 @@ void handle_radio_event(const struct device *dev, enum ieee802154_event evt,
237238
}
238239
}
239240

241+
#if defined(CONFIG_NET_PKT_TXTIME) || defined(CONFIG_OPENTHREAD_CSL_RECEIVER)
242+
/**
243+
* @brief Convert 32-bit (potentially wrapped) OpenThread microsecond timestamps
244+
* to 64-bit Zephyr network subsystem nanosecond timestamps.
245+
*
246+
* This is a workaround until OpenThread is able to schedule 64-bit RX/TX time.
247+
*
248+
* @param target_time_ns_wrapped time in nanoseconds referred to the radio clock
249+
* modulo UINT32_MAX.
250+
*
251+
* @return 64-bit nanosecond timestamp
252+
*/
253+
static net_time_t convert_32bit_us_wrapped_to_64bit_ns(uint32_t target_time_us_wrapped)
254+
{
255+
/**
256+
* OpenThread provides target time as a (potentially wrapped) 32-bit
257+
* integer defining a moment in time in the microsecond domain.
258+
*
259+
* The target time can point to a moment in the future, but can be
260+
* overdue as well. In order to determine what's the case and correctly
261+
* set the absolute (non-wrapped) target time, it's necessary to compare
262+
* the least significant 32 bits of the current 64-bit network subsystem
263+
* time with the provided 32-bit target time. Let's assume that half of
264+
* the 32-bit range can be used for specifying target times in the
265+
* future, and the other half - in the past.
266+
*/
267+
uint64_t now_us = otPlatTimeGet();
268+
uint32_t now_us_wrapped = (uint32_t)now_us;
269+
uint32_t time_diff = target_time_us_wrapped - now_us_wrapped;
270+
uint64_t result = UINT64_C(0);
271+
272+
if (time_diff < 0x80000000) {
273+
/**
274+
* Target time is assumed to be in the future. Check if a 32-bit overflow
275+
* occurs between the current time and the target time.
276+
*/
277+
if (now_us_wrapped > target_time_us_wrapped) {
278+
/**
279+
* Add a 32-bit overflow and replace the least significant 32 bits
280+
* with the provided target time.
281+
*/
282+
result = now_us + UINT32_MAX + 1;
283+
result &= ~(uint64_t)UINT32_MAX;
284+
result |= target_time_us_wrapped;
285+
} else {
286+
/**
287+
* Leave the most significant 32 bits and replace the least significant
288+
* 32 bits with the provided target time.
289+
*/
290+
result = (now_us & (~(uint64_t)UINT32_MAX)) | target_time_us_wrapped;
291+
}
292+
} else {
293+
/**
294+
* Target time is assumed to be in the past. Check if a 32-bit overflow
295+
* occurs between the target time and the current time.
296+
*/
297+
if (now_us_wrapped > target_time_us_wrapped) {
298+
/**
299+
* Leave the most significant 32 bits and replace the least significant
300+
* 32 bits with the provided target time.
301+
*/
302+
result = (now_us & (~(uint64_t)UINT32_MAX)) | target_time_us_wrapped;
303+
} else {
304+
/**
305+
* Subtract a 32-bit overflow and replace the least significant
306+
* 32 bits with the provided target time.
307+
*/
308+
result = now_us - UINT32_MAX - 1;
309+
result &= ~(uint64_t)UINT32_MAX;
310+
result |= target_time_us_wrapped;
311+
}
312+
}
313+
314+
__ASSERT_NO_MSG(result <= INT64_MAX / NSEC_PER_USEC);
315+
return (net_time_t)result * NSEC_PER_USEC;
316+
}
317+
#endif /* CONFIG_NET_PKT_TXTIME || CONFIG_OPENTHREAD_CSL_RECEIVER */
318+
240319
static void dataInit(void)
241320
{
242321
tx_pkt = net_pkt_alloc(K_NO_WAIT);
@@ -315,11 +394,13 @@ void transmit_message(struct k_work *tx_job)
315394

316395
if ((radio_api->get_capabilities(radio_dev) & IEEE802154_HW_TXTIME) &&
317396
(sTransmitFrame.mInfo.mTxInfo.mTxDelay != 0)) {
318-
uint64_t tx_at = sTransmitFrame.mInfo.mTxInfo.mTxDelayBaseTime +
397+
#if defined(CONFIG_NET_PKT_TXTIME)
398+
uint32_t tx_at = sTransmitFrame.mInfo.mTxInfo.mTxDelayBaseTime +
319399
sTransmitFrame.mInfo.mTxInfo.mTxDelay;
320-
struct net_ptp_time timestamp = ns_to_net_ptp_time(tx_at * NSEC_PER_USEC);
321-
400+
struct net_ptp_time timestamp =
401+
ns_to_net_ptp_time(convert_32bit_us_wrapped_to_64bit_ns(tx_at));
322402
net_pkt_set_timestamp(tx_pkt, &timestamp);
403+
#endif
323404
tx_err =
324405
radio_api->tx(radio_dev, IEEE802154_TX_MODE_TXTIME_CCA, tx_pkt, tx_payload);
325406
} else if (sTransmitFrame.mInfo.mTxInfo.mCsmaCaEnabled) {
@@ -666,7 +747,7 @@ otError otPlatRadioReceiveAt(otInstance *aInstance, uint8_t aChannel,
666747

667748
struct ieee802154_config config = {
668749
.rx_slot.channel = aChannel,
669-
.rx_slot.start = (net_time_t)aStart * NSEC_PER_USEC,
750+
.rx_slot.start = convert_32bit_us_wrapped_to_64bit_ns(aStart),
670751
.rx_slot.duration = (net_time_t)aDuration * NSEC_PER_USEC,
671752
};
672753

@@ -1195,7 +1276,8 @@ void otPlatRadioUpdateCslSampleTime(otInstance *aInstance, uint32_t aCslSampleTi
11951276
{
11961277
ARG_UNUSED(aInstance);
11971278

1198-
struct ieee802154_config config = { .csl_rx_time = aCslSampleTime * NSEC_PER_USEC };
1279+
struct ieee802154_config config = {
1280+
.csl_rx_time = convert_32bit_us_wrapped_to_64bit_ns(aCslSampleTime)};
11991281

12001282
(void)radio_api->configure(radio_dev, IEEE802154_CONFIG_CSL_RX_TIME, &config);
12011283
}

tests/subsys/openthread/radio_test.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ FAKE_VALUE_FUNC(int, set_channel_mock, const struct device *, uint16_t);
4646
FAKE_VALUE_FUNC(int, filter_mock, const struct device *, bool, enum ieee802154_filter_type,
4747
const struct ieee802154_filter *);
4848
FAKE_VALUE_FUNC(int, set_txpower_mock, const struct device *, int16_t);
49+
FAKE_VALUE_FUNC(int64_t, get_time_mock, const struct device *);
4950
FAKE_VALUE_FUNC(int, tx_mock, const struct device *, enum ieee802154_tx_mode, struct net_pkt *,
5051
struct net_buf *);
5152
FAKE_VALUE_FUNC(int, start_mock, const struct device *);
@@ -62,6 +63,7 @@ static struct ieee802154_radio_api rapi = {.get_capabilities = get_capabilities,
6263
.set_channel = set_channel_mock,
6364
.filter = filter_mock,
6465
.set_txpower = set_txpower_mock,
66+
.get_time = get_time_mock,
6567
.tx = tx_mock,
6668
.start = start_mock,
6769
.stop = stop_mock,
@@ -284,11 +286,17 @@ ZTEST(openthread_radio, test_tx_test)
284286
FFF_RESET_HISTORY();
285287

286288
if (IS_ENABLED(CONFIG_NET_PKT_TXTIME)) {
289+
/* cover dealing with wrapped scheduling time:
290+
* current time: (UINT32_MAX + 1) us
291+
* target time wrapped: (3 + 5) us, unwrapped: (UINT32_MAX + 3 + 5) us
292+
*/
293+
get_time_mock_fake.return_val = (int64_t)UINT32_MAX * NSEC_PER_USEC + 1000;
287294
frm->mInfo.mTxInfo.mTxDelayBaseTime = 3U;
288295
frm->mInfo.mTxInfo.mTxDelay = 5U;
289296
expected_target_time =
297+
get_time_mock_fake.return_val +
290298
(frm->mInfo.mTxInfo.mTxDelayBaseTime + frm->mInfo.mTxInfo.mTxDelay) *
291-
NSEC_PER_USEC;
299+
NSEC_PER_USEC;
292300
}
293301

294302
/* ACKed frame */

0 commit comments

Comments
 (0)