From 8d2184a4e72659486010f16c5b4634bb92189dc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hubert=20Mi=C5=9B?= Date: Fri, 17 Aug 2018 18:03:55 +0200 Subject: [PATCH] [nrf52840] update nRF52840 radio driver to version 1.2.0 (#2968) --- examples/platforms/nrf52840/Makefile.am | 9 +- examples/platforms/nrf52840/alarm.c | 344 ++++++--- .../drivers/radio/fem/nrf_fem_control.c | 12 +- .../drivers/radio/hal/nrf_radio.h | 1 + .../mac_features/nrf_802154_ack_timeout.c | 46 +- .../radio/mac_features/nrf_802154_csma_ca.c | 60 +- .../mac_features/nrf_802154_delayed_trx.c | 167 ++++ .../mac_features/nrf_802154_delayed_trx.h | 77 ++ .../radio/mac_features/nrf_802154_filter.c | 85 ++- .../drivers/radio/nrf_802154.c | 190 ++--- .../drivers/radio/nrf_802154.h | 207 +++-- .../radio/nrf_802154_ack_pending_bit.c | 35 +- .../drivers/radio/nrf_802154_config.h | 26 +- .../drivers/radio/nrf_802154_const.h | 5 +- .../drivers/radio/nrf_802154_core.c | 716 +++++++++++------- .../drivers/radio/nrf_802154_core.h | 32 +- .../radio/nrf_802154_critical_section.c | 297 ++++++-- .../radio/nrf_802154_critical_section.h | 27 + .../drivers/radio/nrf_802154_debug.h | 25 + .../radio/nrf_802154_notification_direct.c | 2 +- .../radio/nrf_802154_notification_swi.c | 1 - .../drivers/radio/nrf_802154_pib.c | 12 + .../drivers/radio/nrf_802154_pib.h | 15 + .../radio/nrf_802154_priority_drop_direct.c | 4 +- .../radio/nrf_802154_procedures_duration.h | 15 +- .../drivers/radio/nrf_802154_request.h | 14 +- .../drivers/radio/nrf_802154_request_direct.c | 35 +- .../drivers/radio/nrf_802154_request_swi.c | 58 +- .../drivers/radio/nrf_802154_rsch.c | 466 ++++++++++++ .../drivers/radio/nrf_802154_rsch.h | 215 ++++++ .../drivers/radio/nrf_802154_swi.c | 255 +++---- .../drivers/radio/nrf_802154_swi.h | 9 +- .../drivers/radio/nrf_802154_timer_coord.c | 249 ++++++ .../drivers/radio/nrf_802154_timer_coord.h | 115 +++ .../drivers/radio/nrf_802154_types.h | 9 + .../drivers/radio/nrf_802154_utils.h | 145 ++++ .../radio/platform/clock/nrf_802154_clock.h | 3 +- .../platform/hp_timer/nrf_802154_hp_timer.c | 142 ++++ .../platform/hp_timer/nrf_802154_hp_timer.h | 146 ++++ .../nrf_802154_lp_timer.h} | 104 ++- .../lp_timer/nrf_802154_lp_timer_nodrv.c | 576 ++++++++++++++ .../nrf_802154_lp_timer_none.c} | 10 +- .../platform/timer/nrf_802154_timer_nodrv.c | 411 ---------- .../drivers/radio/raal/nrf_raal_api.h | 8 - .../radio/raal/simulator/nrf_raal_simulator.c | 246 ++++++ .../radio/raal/single_phy/single_phy.c | 41 +- .../raal/softdevice/nrf_raal_softdevice.c | 423 ++++------- .../raal/softdevice/nrf_raal_softdevice.h | 63 +- .../timer_scheduler/nrf_802154_timer_sched.c | 82 +- .../timer_scheduler/nrf_802154_timer_sched.h | 17 +- 50 files changed, 4502 insertions(+), 1750 deletions(-) create mode 100644 third_party/NordicSemiconductor/drivers/radio/mac_features/nrf_802154_delayed_trx.c create mode 100644 third_party/NordicSemiconductor/drivers/radio/mac_features/nrf_802154_delayed_trx.h create mode 100644 third_party/NordicSemiconductor/drivers/radio/nrf_802154_rsch.c create mode 100644 third_party/NordicSemiconductor/drivers/radio/nrf_802154_rsch.h create mode 100644 third_party/NordicSemiconductor/drivers/radio/nrf_802154_timer_coord.c create mode 100644 third_party/NordicSemiconductor/drivers/radio/nrf_802154_timer_coord.h create mode 100644 third_party/NordicSemiconductor/drivers/radio/nrf_802154_utils.h create mode 100644 third_party/NordicSemiconductor/drivers/radio/platform/hp_timer/nrf_802154_hp_timer.c create mode 100644 third_party/NordicSemiconductor/drivers/radio/platform/hp_timer/nrf_802154_hp_timer.h rename third_party/NordicSemiconductor/drivers/radio/platform/{timer/nrf_802154_timer.h => lp_timer/nrf_802154_lp_timer.h} (53%) create mode 100644 third_party/NordicSemiconductor/drivers/radio/platform/lp_timer/nrf_802154_lp_timer_nodrv.c rename third_party/NordicSemiconductor/drivers/radio/platform/{timer/nrf_802154_timer_none.c => lp_timer/nrf_802154_lp_timer_none.c} (90%) delete mode 100644 third_party/NordicSemiconductor/drivers/radio/platform/timer/nrf_802154_timer_nodrv.c create mode 100644 third_party/NordicSemiconductor/drivers/radio/raal/simulator/nrf_raal_simulator.c diff --git a/examples/platforms/nrf52840/Makefile.am b/examples/platforms/nrf52840/Makefile.am index f4fa65b08..e6b37bb2c 100644 --- a/examples/platforms/nrf52840/Makefile.am +++ b/examples/platforms/nrf52840/Makefile.am @@ -147,13 +147,16 @@ RADIO_DRIVER_SOURCES @top_builddir@/third_party/NordicSemiconductor/drivers/radio/nrf_802154_debug.c \ @top_builddir@/third_party/NordicSemiconductor/drivers/radio/nrf_802154_pib.c \ @top_builddir@/third_party/NordicSemiconductor/drivers/radio/nrf_802154_revision.c \ + @top_builddir@/third_party/NordicSemiconductor/drivers/radio/nrf_802154_rsch.c \ @top_builddir@/third_party/NordicSemiconductor/drivers/radio/nrf_802154_rssi.c \ @top_builddir@/third_party/NordicSemiconductor/drivers/radio/nrf_802154_rx_buffer.c \ + @top_builddir@/third_party/NordicSemiconductor/drivers/radio/nrf_802154_timer_coord.c \ @top_builddir@/third_party/NordicSemiconductor/drivers/radio/fem/nrf_fem_control.c \ @top_builddir@/third_party/NordicSemiconductor/drivers/radio/mac_features/nrf_802154_filter.c \ @top_builddir@/third_party/NordicSemiconductor/drivers/radio/mac_features/nrf_802154_ack_timeout.c \ @top_builddir@/third_party/NordicSemiconductor/drivers/radio/mac_features/nrf_802154_csma_ca.c \ @top_builddir@/third_party/NordicSemiconductor/drivers/radio/platform/clock/nrf_802154_clock_sdk.c \ + @top_builddir@/third_party/NordicSemiconductor/drivers/radio/platform/hp_timer/nrf_802154_hp_timer.c \ @top_builddir@/third_party/NordicSemiconductor/drivers/radio/timer_scheduler/nrf_802154_timer_sched.c \ $(NULL) @@ -263,9 +266,12 @@ noinst_HEADERS $(top_srcdir)/third_party/NordicSemiconductor/drivers/radio/nrf_802154_procedures_duration.h \ $(top_srcdir)/third_party/NordicSemiconductor/drivers/radio/nrf_802154_request.h \ $(top_srcdir)/third_party/NordicSemiconductor/drivers/radio/nrf_802154_revision.h \ + $(top_srcdir)/third_party/NordicSemiconductor/drivers/radio/nrf_802154_rsch.h \ $(top_srcdir)/third_party/NordicSemiconductor/drivers/radio/nrf_802154_rssi.h \ $(top_srcdir)/third_party/NordicSemiconductor/drivers/radio/nrf_802154_rx_buffer.h \ + $(top_srcdir)/third_party/NordicSemiconductor/drivers/radio/nrf_802154_timer_coord.h \ $(top_srcdir)/third_party/NordicSemiconductor/drivers/radio/nrf_802154_swi.h \ + $(top_srcdir)/third_party/NordicSemiconductor/drivers/radio/nrf_802154_utils.h \ $(top_srcdir)/third_party/NordicSemiconductor/drivers/radio/fem/nrf_fem_control_api.h \ $(top_srcdir)/third_party/NordicSemiconductor/drivers/radio/fem/nrf_fem_control_config.h \ $(top_srcdir)/third_party/NordicSemiconductor/drivers/radio/hal/nrf_radio.h \ @@ -274,7 +280,8 @@ noinst_HEADERS $(top_srcdir)/third_party/NordicSemiconductor/drivers/radio/mac_features/nrf_802154_filter.h \ $(top_srcdir)/third_party/NordicSemiconductor/drivers/radio/platform/clock/nrf_802154_clock.h \ $(top_srcdir)/third_party/NordicSemiconductor/drivers/radio/platform/temperature/nrf_802154_temperature.h \ - $(top_srcdir)/third_party/NordicSemiconductor/drivers/radio/platform/timer/nrf_802154_timer.h \ + $(top_srcdir)/third_party/NordicSemiconductor/drivers/radio/platform/hp_timer/nrf_802154_hp_timer.h \ + $(top_srcdir)/third_party/NordicSemiconductor/drivers/radio/platform/lp_timer/nrf_802154_lp_timer.h \ $(top_srcdir)/third_party/NordicSemiconductor/drivers/radio/raal/nrf_raal_api.h \ $(top_srcdir)/third_party/NordicSemiconductor/drivers/radio/raal/nrf_raal_config.h \ $(top_srcdir)/third_party/NordicSemiconductor/drivers/radio/raal/softdevice/nrf_raal_softdevice.h \ diff --git a/examples/platforms/nrf52840/alarm.c b/examples/platforms/nrf52840/alarm.c index 65d9395a3..aa668d0ef 100644 --- a/examples/platforms/nrf52840/alarm.c +++ b/examples/platforms/nrf52840/alarm.c @@ -52,26 +52,30 @@ #include "cmsis/core_cmFunc.h" #include -#include +#include +#include #include #include // clang-format off -#define RTC_FREQUENCY 32768ULL +#define RTC_FREQUENCY NRF_802154_RTC_FREQUENCY #define US_PER_MS 1000ULL -#define US_PER_S 1000000ULL -#define US_PER_TICK CEIL_DIV(US_PER_S, RTC_FREQUENCY) -#define US_PER_OVERFLOW (512UL * US_PER_S) ///< Time that has passed between overflow events. On full RTC speed, it occurs every 512 s. +#define US_PER_S NRF_802154_US_PER_S +#define US_PER_OVERFLOW (512UL * NRF_802154_US_PER_S) ///< Time that has passed between overflow events. On full RTC speed, it occurs every 512 s. #define MS_PER_S 1000UL +#define MIN_RTC_COMPARE_EVENT_DT (2 * NRF_802154_US_PER_TICK) ///< Minimum time delta from now before RTC compare event is guaranteed to fire. +#define EPOCH_32BIT_US (1ULL << 32) +#define EPOCH_FROM_TIME(time) ((time) & ((uint64_t)UINT32_MAX << 32)) + #define XTAL_ACCURACY 40 // The crystal used on nRF52840PDK has ±20ppm accuracy. // clang-format on -typedef enum { kMsTimer, kUsTimer, k802154Timer, kNumTimers } AlarmIndex; +typedef enum { kMsTimer, kUsTimer, k802154Timer, k802154Sync, kNumTimers } AlarmIndex; typedef struct { @@ -108,15 +112,20 @@ static const AlarmChannelData sChannelData[kNumTimers] = // .mCompareEvent = NRF_RTC_EVENT_COMPARE_1, .mCompareInt = NRF_RTC_INT_COMPARE1_MASK, }, - [k802154Timer] = { - .mChannelNumber = 2, - .mCompareEventMask = RTC_EVTEN_COMPARE2_Msk, - .mCompareEvent = NRF_RTC_EVENT_COMPARE_2, + [k802154Timer] = + { + .mChannelNumber = 2, + .mCompareEventMask = RTC_EVTEN_COMPARE2_Msk, + .mCompareEvent = NRF_RTC_EVENT_COMPARE_2, + .mCompareInt = NRF_RTC_INT_COMPARE2_MASK, + }, + [k802154Sync] = { + .mChannelNumber = 3, + .mCompareEventMask = RTC_EVTEN_COMPARE3_Msk, + .mCompareEvent = NRF_RTC_EVENT_COMPARE_3, .mCompareInt = NRF_RTC_INT_COMPARE2_MASK, }}; -static uint32_t OverflowCounterGet(void); - static inline bool MutexGet(void) { do @@ -148,42 +157,26 @@ static inline void MutexRelease(void) sMutex = 0; } -static inline uint32_t TimeToTicks(uint64_t aTime, AlarmIndex aIndex) +static inline uint64_t TimeToTicks(uint64_t aTime, AlarmIndex aIndex) { - uint32_t ticks; + if (aIndex == kMsTimer) + { + aTime *= US_PER_MS; + } + + return NRF_802154_US_TO_RTC_TICKS(aTime) & RTC_CC_COMPARE_Msk; +} + +static inline uint64_t TicksToTime(uint64_t aTicks, AlarmIndex aIndex) +{ + uint64_t result = NRF_802154_RTC_TICKS_TO_US(aTicks); if (aIndex == kMsTimer) { - ticks = (uint32_t)CEIL_DIV((aTime * US_PER_MS * RTC_FREQUENCY), US_PER_S) & RTC_CC_COMPARE_Msk; - } - else - { - ticks = (uint32_t)CEIL_DIV((aTime * RTC_FREQUENCY), US_PER_S) & RTC_CC_COMPARE_Msk; + result /= US_PER_MS; } - return ticks; -} - -static inline uint64_t TicksToTime(uint32_t aTicks) -{ - return CEIL_DIV((US_PER_S * (uint64_t)aTicks), RTC_FREQUENCY); -} - -static inline uint64_t AlarmGetCurrentTimeRtcProtected(AlarmIndex aIndex) -{ - uint64_t usecTime = nrf5AlarmGetCurrentTime() + 2 * US_PER_TICK; - uint64_t currentTime; - - if (aIndex == kMsTimer) - { - currentTime = usecTime / US_PER_MS; - } - else - { - currentTime = usecTime; - } - - return currentTime; + return result; } static inline bool AlarmShallStrike(uint64_t aNow, AlarmIndex aIndex) @@ -191,37 +184,7 @@ static inline bool AlarmShallStrike(uint64_t aNow, AlarmIndex aIndex) return aNow >= sTimerData[aIndex].mTargetTime; } -static void HandleCompareMatch(AlarmIndex aIndex, bool aSkipCheck) -{ - nrf_rtc_event_clear(RTC_INSTANCE, sChannelData[aIndex].mCompareEvent); - - uint64_t now = nrf5AlarmGetCurrentTime(); - - if (aIndex == kMsTimer) - { - now /= US_PER_MS; - } - - // In case the target time was larger than single overflow, - // we should only strike the timer on final compare event. - if (aSkipCheck || AlarmShallStrike(now, aIndex)) - { - nrf_rtc_event_disable(RTC_INSTANCE, sChannelData[aIndex].mCompareEventMask); - nrf_rtc_int_disable(RTC_INSTANCE, sChannelData[aIndex].mCompareInt); - - if (aIndex == k802154Timer) - { - nrf_802154_timer_fired(); - } - else - { - sTimerData[aIndex].mFireAlarm = true; - otSysEventSignalPending(); - } - } -} - -static uint32_t OverflowCounterGet(void) +static uint32_t GetOverflowCounter(void) { uint32_t overflowCounter; @@ -284,36 +247,141 @@ static uint32_t OverflowCounterGet(void) return overflowCounter; } -static void AlarmStartAt(uint32_t aT0, uint32_t aDt, AlarmIndex aIndex) +static uint32_t GetRtcCounter(void) +{ + return nrf_rtc_counter_get(RTC_INSTANCE); +} + +static void GetOffsetAndCounter(uint32_t *aOffset, uint32_t *aCounter) +{ + uint32_t offset1 = GetOverflowCounter(); + + __DMB(); + + uint32_t rtcValue1 = GetRtcCounter(); + + __DMB(); + + uint32_t offset2 = GetOverflowCounter(); + + *aOffset = offset2; + *aCounter = (offset1 == offset2) ? rtcValue1 : GetRtcCounter(); +} + +static uint64_t GetTime(uint32_t aOffset, uint32_t aCounter, AlarmIndex aIndex) +{ + uint64_t result = (uint64_t)aOffset * US_PER_OVERFLOW + TicksToTime(aCounter, kUsTimer); + + if (aIndex == kMsTimer) + { + result /= US_PER_MS; + } + + return result; +} + +static uint64_t GetCurrentTime(AlarmIndex aIndex) +{ + uint32_t offset; + uint32_t rtc_counter; + + GetOffsetAndCounter(&offset, &rtc_counter); + + return GetTime(offset, rtc_counter, aIndex); +} + +static void HandleCompareMatch(AlarmIndex aIndex, bool aSkipCheck) +{ + nrf_rtc_event_clear(RTC_INSTANCE, sChannelData[aIndex].mCompareEvent); + + uint64_t now = GetCurrentTime(aIndex); + + // In case the target time was larger than single overflow, + // we should only strike the timer on final compare event. + if (aSkipCheck || AlarmShallStrike(now, aIndex)) + { + nrf_rtc_event_disable(RTC_INSTANCE, sChannelData[aIndex].mCompareEventMask); + nrf_rtc_int_disable(RTC_INSTANCE, sChannelData[aIndex].mCompareInt); + + switch (aIndex) + { + case k802154Timer: + nrf_802154_lp_timer_fired(); + break; + + case k802154Sync: + nrf_802154_lp_timer_synchronized(); + break; + + case kMsTimer: + case kUsTimer: + sTimerData[aIndex].mFireAlarm = true; + otSysEventSignalPending(); + break; + + default: + assert(false); + } + } +} + +static uint64_t ConvertT0AndDtTo64BitTime(uint32_t aT0, uint32_t aDt, const uint64_t *aNow) { uint64_t now; - uint32_t targetCounter; + now = *aNow; + + if (((uint32_t)now < aT0) && ((aT0 - (uint32_t)now) > (UINT32_MAX / 2))) + { + now -= EPOCH_32BIT_US; + } + else if (((uint32_t)now > aT0) && (((uint32_t)now) - aT0 > (UINT32_MAX / 2))) + { + now += EPOCH_32BIT_US; + } + + return (EPOCH_FROM_TIME(now)) + aT0 + aDt; +} + +static uint64_t RoundUpTimeToTimerTicksMultiply(uint64_t aTime, AlarmIndex aIndex) +{ + uint64_t ticks = TimeToTicks(aTime, aIndex); + uint64_t result = TicksToTime(ticks, aIndex); + return result; +} + +static void TimerStartAt(uint32_t aT0, uint32_t aDt, AlarmIndex aIndex, const uint64_t *aNow) +{ + uint64_t targetCounter; + uint64_t targetTime; nrf_rtc_int_disable(RTC_INSTANCE, sChannelData[aIndex].mCompareInt); nrf_rtc_event_enable(RTC_INSTANCE, sChannelData[aIndex].mCompareEventMask); - now = nrf5AlarmGetCurrentTime(); + targetTime = ConvertT0AndDtTo64BitTime(aT0, aDt, aNow); + targetCounter = TimeToTicks(targetTime, aIndex); - if (aIndex == kMsTimer) - { - now /= US_PER_MS; - } - - // Check if 32 LSB of `now` overflowed between getting aT0 and loading `now` value. - if ((uint32_t)now < aT0) - { - now -= 0x0000000100000000; - } - - sTimerData[aIndex].mTargetTime = (now & 0xffffffff00000000) + aT0 + aDt; - - targetCounter = TimeToTicks(sTimerData[aIndex].mTargetTime, aIndex); + sTimerData[aIndex].mTargetTime = RoundUpTimeToTimerTicksMultiply(targetTime, aIndex); nrf_rtc_cc_set(RTC_INSTANCE, sChannelData[aIndex].mChannelNumber, targetCounter); +} - now = AlarmGetCurrentTimeRtcProtected(aIndex); +static void AlarmStartAt(uint32_t aT0, uint32_t aDt, AlarmIndex aIndex) +{ + uint32_t offset; + uint32_t rtc_value; + uint64_t now; - if (AlarmShallStrike(now, aIndex)) + GetOffsetAndCounter(&offset, &rtc_value); + now = GetTime(offset, rtc_value, aIndex); + + TimerStartAt(aT0, aDt, aIndex, &now); + + if (rtc_value != GetRtcCounter()) + { + now = GetCurrentTime(aIndex); + } + + if (AlarmShallStrike(now + MIN_RTC_COMPARE_EVENT_DT, aIndex)) { HandleCompareMatch(aIndex, true); @@ -330,6 +398,13 @@ static void AlarmStartAt(uint32_t aT0, uint32_t aDt, AlarmIndex aIndex) } } +static void TimerSyncStartAt(uint32_t aT0, uint32_t aDt, const uint64_t *aNow) +{ + TimerStartAt(aT0, aDt, k802154Sync, aNow); + + nrf_rtc_int_enable(RTC_INSTANCE, sChannelData[k802154Sync].mCompareInt); +} + static void AlarmStop(AlarmIndex aIndex) { nrf_rtc_event_disable(RTC_INSTANCE, sChannelData[aIndex].mCompareEventMask); @@ -389,6 +464,8 @@ void nrf5AlarmDeinit(void) nrf_rtc_event_disable(RTC_INSTANCE, RTC_EVTEN_OVRFLW_Msk); nrf_rtc_event_clear(RTC_INSTANCE, NRF_RTC_EVENT_OVERFLOW); + nrf_802154_lp_timer_sync_stop(); + NVIC_DisableIRQ(RTC_IRQN); NVIC_ClearPendingIRQ(RTC_IRQN); NVIC_SetPriority(RTC_IRQN, 0); @@ -425,28 +502,7 @@ void nrf5AlarmProcess(otInstance *aInstance) inline uint64_t nrf5AlarmGetCurrentTime(void) { - uint32_t offset1 = OverflowCounterGet(); - - __DMB(); - - uint32_t rtcValue1 = nrf_rtc_counter_get(RTC_INSTANCE); - - __DMB(); - - uint32_t offset2 = OverflowCounterGet(); - - __DMB(); - - uint32_t rtcValue2 = nrf_rtc_counter_get(RTC_INSTANCE); - - if (offset1 == offset2) - { - return (uint64_t)offset1 * US_PER_OVERFLOW + TicksToTime(rtcValue1); - } - else - { - return (uint64_t)offset2 * US_PER_OVERFLOW + TicksToTime(rtcValue2); - } + return GetCurrentTime(kUsTimer); } uint32_t otPlatAlarmMilliGetNow(void) @@ -491,51 +547,87 @@ void otPlatAlarmMicroStop(otInstance *aInstance) * Radio driver timer abstraction API */ -void nrf_802154_timer_init(void) +void nrf_802154_lp_timer_init(void) { // Intentionally empty } -void nrf_802154_timer_deinit(void) +void nrf_802154_lp_timer_deinit(void) { // Intentionally empty } -void nrf_802154_timer_critical_section_enter(void) +void nrf_802154_lp_timer_critical_section_enter(void) { nrf_rtc_int_disable(RTC_INSTANCE, sChannelData[k802154Timer].mCompareInt); __DSB(); __ISB(); } -void nrf_802154_timer_critical_section_exit(void) +void nrf_802154_lp_timer_critical_section_exit(void) { nrf_rtc_int_enable(RTC_INSTANCE, sChannelData[k802154Timer].mCompareInt); } -uint32_t nrf_802154_timer_time_get(void) +uint32_t nrf_802154_lp_timer_time_get(void) { return (uint32_t)nrf5AlarmGetCurrentTime(); } -uint32_t nrf_802154_timer_granularity_get(void) +uint32_t nrf_802154_lp_timer_granularity_get(void) { - return US_PER_TICK; + return NRF_802154_US_PER_TICK; } -void nrf_802154_timer_start(uint32_t t0, uint32_t dt) +void nrf_802154_lp_timer_start(uint32_t t0, uint32_t dt) { AlarmStartAt(t0, dt, k802154Timer); } -void nrf_802154_timer_stop(void) +bool nrf_802154_lp_timer_is_running(void) +{ + return nrf_rtc_int_is_enabled(RTC_INSTANCE, sChannelData[k802154Timer].mCompareInt); +} + +void nrf_802154_lp_timer_stop(void) { AlarmStop(k802154Timer); } -bool nrf_802154_timer_is_running(void) +void nrf_802154_lp_timer_sync_start_now(void) { - return nrf_rtc_int_is_enabled(RTC_INSTANCE, sChannelData[k802154Timer].mCompareInt); + uint32_t counter; + uint32_t offset; + uint64_t now; + + do + { + GetOffsetAndCounter(&offset, &counter); + now = GetTime(offset, counter, k802154Sync); + TimerSyncStartAt((uint32_t)now, MIN_RTC_COMPARE_EVENT_DT, &now); + } while (GetRtcCounter() != counter); +} + +void nrf_802154_lp_timer_sync_start_at(uint32_t t0, uint32_t dt) +{ + uint64_t now = GetCurrentTime(k802154Sync); + + TimerSyncStartAt(t0, dt, &now); +} + +void nrf_802154_lp_timer_sync_stop(void) +{ + AlarmStop(k802154Sync); +} + +uint32_t nrf_802154_lp_timer_sync_event_get(void) +{ + return (uint32_t)nrf_rtc_event_address_get(RTC_INSTANCE, sChannelData[k802154Sync].mCompareEvent); +} + +uint32_t nrf_802154_lp_timer_sync_time_get(void) +{ + return (uint32_t)sTimerData[k802154Sync].mTargetTime; } /** @@ -553,7 +645,7 @@ void RTC_IRQ_HANDLER(void) nrf_rtc_int_disable(RTC_INSTANCE, NRF_RTC_INT_OVERFLOW_MASK); // Handle OVERFLOW event by reading current value of overflow counter. - (void)OverflowCounterGet(); + (void)GetOverflowCounter(); } // Handle compare match. diff --git a/third_party/NordicSemiconductor/drivers/radio/fem/nrf_fem_control.c b/third_party/NordicSemiconductor/drivers/radio/fem/nrf_fem_control.c index 47c81dedc..3b4d7f9ef 100644 --- a/third_party/NordicSemiconductor/drivers/radio/fem/nrf_fem_control.c +++ b/third_party/NordicSemiconductor/drivers/radio/fem/nrf_fem_control.c @@ -257,14 +257,14 @@ void nrf_fem_control_pin_clear(void) { if (pin_is_enabled(NRF_FEM_CONTROL_PA_PIN)) { - nrf_gpio_pin_write(m_nrf_fem_control_cfg.pa_cfg.gpio_pin, - !m_nrf_fem_control_cfg.pa_cfg.active_high); + nrf_gpiote_task_force(m_nrf_fem_control_cfg.pa_gpiote_ch_id, + (nrf_gpiote_outinit_t)!m_nrf_fem_control_cfg.pa_cfg.active_high); } if (pin_is_enabled(NRF_FEM_CONTROL_LNA_PIN)) { - nrf_gpio_pin_write(m_nrf_fem_control_cfg.lna_cfg.gpio_pin, - !m_nrf_fem_control_cfg.lna_cfg.active_high); + nrf_gpiote_task_force(m_nrf_fem_control_cfg.lna_gpiote_ch_id, + (nrf_gpiote_outinit_t)!m_nrf_fem_control_cfg.lna_cfg.active_high); } } @@ -284,8 +284,8 @@ void nrf_fem_control_timer_reset(nrf_fem_control_pin_t pin, nrf_timer_short_mask { if (pin_is_enabled(pin)) { - nrf_timer_task_trigger(NRF_FEM_TIMER_INSTANCE, NRF_TIMER_TASK_STOP); - nrf_timer_task_trigger(NRF_FEM_TIMER_INSTANCE, NRF_TIMER_TASK_CLEAR); + // Anomaly 78: use SHUTDOWN instead of STOP and CLEAR. + nrf_timer_task_trigger(NRF_FEM_TIMER_INSTANCE, NRF_TIMER_TASK_SHUTDOWN); nrf_timer_shorts_disable(NRF_FEM_TIMER_INSTANCE, short_mask); } } diff --git a/third_party/NordicSemiconductor/drivers/radio/hal/nrf_radio.h b/third_party/NordicSemiconductor/drivers/radio/hal/nrf_radio.h index 4da06ca09..adc3daecc 100644 --- a/third_party/NordicSemiconductor/drivers/radio/hal/nrf_radio.h +++ b/third_party/NordicSemiconductor/drivers/radio/hal/nrf_radio.h @@ -67,6 +67,7 @@ typedef enum /*lint -save -e30 -esym(628,__INTADDR__) */ NRF_RADIO_TASK_CCASTART = offsetof(NRF_RADIO_Type, TASKS_CCASTART), /**< Start Clear Channel Assessment procedure. */ NRF_RADIO_TASK_CCASTOP = offsetof(NRF_RADIO_Type, TASKS_CCASTOP), /**< Stop Clear Channel Assessment procedure. */ NRF_RADIO_TASK_EDSTART = offsetof(NRF_RADIO_Type, TASKS_EDSTART), /**< Start Energy Detection procedure. */ + NRF_RADIO_TASK_EDSTOP = offsetof(NRF_RADIO_Type, TASKS_EDSTOP), /**< Stop Energy Detection procedure. */ NRF_RADIO_TASK_RSSISTART = offsetof(NRF_RADIO_Type, TASKS_RSSISTART), /**< Start the RSSI and take one single sample of received signal strength. */ } nrf_radio_task_t; /*lint -restore */ diff --git a/third_party/NordicSemiconductor/drivers/radio/mac_features/nrf_802154_ack_timeout.c b/third_party/NordicSemiconductor/drivers/radio/mac_features/nrf_802154_ack_timeout.c index 4ec971a9a..09ddc1737 100644 --- a/third_party/NordicSemiconductor/drivers/radio/mac_features/nrf_802154_ack_timeout.c +++ b/third_party/NordicSemiconductor/drivers/radio/mac_features/nrf_802154_ack_timeout.c @@ -44,6 +44,9 @@ #include "nrf_802154_request.h" #include "timer_scheduler/nrf_802154_timer_sched.h" +#define RETRY_DELAY 500 ///< Procedure is delayed by this time if cannot be performed at the moment. +#define MAX_RETRY_DELAY 1000000 ///< Maximal allowed delay of procedure retry. + static void timeout_timer_retry(void); static uint32_t m_timeout = NRF_802154_ACK_TIMEOUT_DEFAULT_TIMEOUT; ///< ACK timeout in us. @@ -65,9 +68,14 @@ static void timeout_timer_fired(void * p_context) if (m_procedure_is_active) { - if (!nrf_802154_request_receive(NRF_802154_TERM_802154, - REQ_ORIG_ACK_TIMEOUT, - notify_tx_error)) + if (nrf_802154_request_receive(NRF_802154_TERM_802154, + REQ_ORIG_ACK_TIMEOUT, + notify_tx_error, + false)) + { + m_procedure_is_active = false; + } + else { timeout_timer_retry(); } @@ -76,12 +84,8 @@ static void timeout_timer_fired(void * p_context) static void timeout_timer_retry(void) { - /* - * Fire on next timer tick. dt value will be rounded up to nearest timer granularity - * by call to nrf_802154_timer_sched_add this will prevent potential infinite - * recursion when short delays are called from same context as nrf_802154_timer_sched_add. - */ - m_timer.dt++; + m_timer.dt += RETRY_DELAY; + assert(m_timer.dt <= MAX_RETRY_DELAY); nrf_802154_timer_sched_add(&m_timer, true); } @@ -101,6 +105,12 @@ static void timeout_timer_start(void) static void timeout_timer_stop(void) { m_procedure_is_active = false; + + // To make sure `timeout_timer_fired()` detects that procedure is being stopped if it preempts + // this function. + __DMB(); + + nrf_802154_timer_sched_remove(&m_timer); } void nrf_802154_ack_timeout_time_set(uint32_t time) @@ -118,14 +128,26 @@ bool nrf_802154_ack_timeout_tx_started_hook(const uint8_t * p_frame) bool nrf_802154_ack_timeout_abort(nrf_802154_term_t term_lvl, req_originator_t req_orig) { - (void)term_lvl; + bool result; - if (req_orig != REQ_ORIG_ACK_TIMEOUT) + if (!m_procedure_is_active || req_orig == REQ_ORIG_ACK_TIMEOUT) { + // Ignore if procedure is not running or self-request. + result = true; + } + else if (term_lvl >= NRF_802154_TERM_802154) + { + // Stop procedure only if termination level is high enough. timeout_timer_stop(); + + result = true; + } + else + { + result = false; } - return true; + return result; } void nrf_802154_ack_timeout_transmitted_hook(const uint8_t * p_frame) diff --git a/third_party/NordicSemiconductor/drivers/radio/mac_features/nrf_802154_csma_ca.c b/third_party/NordicSemiconductor/drivers/radio/mac_features/nrf_802154_csma_ca.c index c5c46e584..b3433a332 100644 --- a/third_party/NordicSemiconductor/drivers/radio/mac_features/nrf_802154_csma_ca.c +++ b/third_party/NordicSemiconductor/drivers/radio/mac_features/nrf_802154_csma_ca.c @@ -43,6 +43,7 @@ #include "nrf_802154_config.h" #include "nrf_802154_const.h" +#include "nrf_802154_debug.h" #include "nrf_802154_notification.h" #include "nrf_802154_request.h" #include "timer_scheduler/nrf_802154_timer_sched.h" @@ -111,19 +112,22 @@ static void frame_transmit(void * p_context) { (void)p_context; - if (!procedure_is_running()) + nrf_802154_log(EVENT_TRACE_ENTER, FUNCTION_CSMA_FRAME_TRANSMIT); + + if (procedure_is_running()) { - return; + if (!nrf_802154_request_transmit(NRF_802154_TERM_NONE, + REQ_ORIG_CSMA_CA, + mp_psdu, + true, + true, + notify_busy_channel)) + { + (void)channel_busy(); + } } - if (!nrf_802154_request_transmit(NRF_802154_TERM_NONE, - REQ_ORIG_CSMA_CA, - mp_psdu, - true, - notify_busy_channel)) - { - (void)channel_busy(); - } + nrf_802154_log(EVENT_TRACE_EXIT, FUNCTION_CSMA_FRAME_TRANSMIT); } /** @@ -147,6 +151,8 @@ static bool channel_busy(void) if (procedure_is_running()) { + nrf_802154_log(EVENT_TRACE_ENTER, FUNCTION_CSMA_CHANNEL_BUSY); + m_nb++; if (m_be < NRF_802154_CSMA_CA_MAX_BE) @@ -163,6 +169,8 @@ static bool channel_busy(void) { procedure_stop(); } + + nrf_802154_log(EVENT_TRACE_EXIT, FUNCTION_CSMA_CHANNEL_BUSY); } return result; @@ -182,29 +190,33 @@ void nrf_802154_csma_ca_start(const uint8_t * p_data) bool nrf_802154_csma_ca_abort(nrf_802154_term_t term_lvl, req_originator_t req_orig) { - // Don't stop CSMA-CA if request by itself or RAAL. - if (req_orig == REQ_ORIG_CSMA_CA || - req_orig == REQ_ORIG_RAAL) + bool result = false; + + // Stop CSMA-CA only if request by the core or the higher layer. + if ((req_orig != REQ_ORIG_CORE) && (req_orig != REQ_ORIG_HIGHER_LAYER)) { return true; } - // Stop CSMA-CA if termination level is high enough. + nrf_802154_log(EVENT_TRACE_ENTER, FUNCTION_CSMA_ABORT); + if (term_lvl >= NRF_802154_TERM_802154) { + // Stop CSMA-CA if termination level is high enough. nrf_802154_timer_sched_remove(&m_timer); procedure_stop(); - return true; + result = true; } - - // Return success in case procedure is already stopped. - if (!procedure_is_running()) + else if (!procedure_is_running()) { - return true; + // Return success in case procedure is already stopped. + result = true; } - return false; + nrf_802154_log(EVENT_TRACE_EXIT, FUNCTION_CSMA_ABORT); + + return result; } bool nrf_802154_csma_ca_tx_failed_hook(const uint8_t * p_frame, nrf_802154_tx_error_t error) @@ -215,7 +227,11 @@ bool nrf_802154_csma_ca_tx_failed_hook(const uint8_t * p_frame, nrf_802154_tx_er if (p_frame == mp_psdu) { + nrf_802154_log(EVENT_TRACE_ENTER, FUNCTION_CSMA_TX_FAILED); + result = channel_busy(); + + nrf_802154_log(EVENT_TRACE_EXIT, FUNCTION_CSMA_TX_FAILED); } return result; @@ -225,8 +241,12 @@ bool nrf_802154_csma_ca_tx_started_hook(const uint8_t * p_frame) { if (p_frame == mp_psdu) { + nrf_802154_log(EVENT_TRACE_ENTER, FUNCTION_CSMA_TX_STARTED); + assert(!nrf_802154_timer_sched_is_running(&m_timer)); procedure_stop(); + + nrf_802154_log(EVENT_TRACE_EXIT, FUNCTION_CSMA_TX_STARTED); } return true; diff --git a/third_party/NordicSemiconductor/drivers/radio/mac_features/nrf_802154_delayed_trx.c b/third_party/NordicSemiconductor/drivers/radio/mac_features/nrf_802154_delayed_trx.c new file mode 100644 index 000000000..fb5e34f8d --- /dev/null +++ b/third_party/NordicSemiconductor/drivers/radio/mac_features/nrf_802154_delayed_trx.c @@ -0,0 +1,167 @@ +/* Copyright (c) 2018, Nordic Semiconductor ASA + * 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 Nordic Semiconductor ASA 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 implements delayed transmission and reception features. + * + */ + +#include "nrf_802154_delayed_trx.h" + +#include +#include +#include + +#include "nrf_802154_config.h" +#include "nrf_802154_const.h" +#include "nrf_802154_notification.h" +#include "nrf_802154_pib.h" +#include "nrf_802154_procedures_duration.h" +#include "nrf_802154_request.h" +#include "nrf_802154_rsch.h" + +#define TX_SETUP_TIME 190 ///< Time [us] needed to change channel, stop rx and setup tx procedure. + +static const uint8_t * mp_tx_psdu; ///< Pointer to PHR + PSDU of the frame requested to transmit. +static bool m_tx_cca; ///< If CCA should be performed prior to transmission. +static uint8_t m_tx_channel; ///< Channel number on which transmission should be performed. + +/** + * Check if delayed transmission procedure is in progress. + * + * @retval true Delayed transmission is in progress (waiting or transmitting). + * @retval false Delayed transmission is not in progress. + */ +static bool tx_is_in_progress(void) +{ + return mp_tx_psdu != NULL; +} + +/** + * Mark that delayed transmission procedure has stopped. + */ +static void tx_stop(void) +{ + mp_tx_psdu = NULL; +} + +/** + * Notify MAC layer that requested timeslot is not granted if tx request failed. + * + * @param[in] result Result of TX request. + */ +static void notify_tx_timeslot_denied(bool result) +{ + if (!result) + { + nrf_802154_notify_transmit_failed(mp_tx_psdu, NRF_802154_TX_ERROR_TIMESLOT_DENIED); + } +} + +bool nrf_802154_delayed_trx_transmit(const uint8_t * p_data, + bool cca, + uint32_t t0, + uint32_t dt, + uint8_t channel) +{ + bool result = true; + uint16_t timeslot_length; + + if (tx_is_in_progress()) + { + result = false; + } + + if (result) + { + dt -= TX_SETUP_TIME; + dt -= TX_RAMP_UP_TIME; + + if (cca) + { + dt -= nrf_802154_cca_before_tx_duration_get(); + } + + mp_tx_psdu = p_data; + m_tx_cca = cca; + m_tx_channel = channel; + + timeslot_length = nrf_802154_tx_duration_get(p_data[0], + cca, + p_data[ACK_REQUEST_OFFSET] & ACK_REQUEST_BIT); + + result = nrf_802154_rsch_delayed_timeslot_request(t0, dt, timeslot_length); + + if (!result) + { + notify_tx_timeslot_denied(result); + tx_stop(); + } + } + + return result; +} + +void nrf_802154_rsch_delayed_timeslot_started(void) +{ + bool result; + + assert(tx_is_in_progress()); + + nrf_802154_pib_channel_set(m_tx_channel); + result = nrf_802154_request_channel_update(); + + if (result) + { + result = nrf_802154_request_transmit(NRF_802154_TERM_802154, + REQ_ORIG_DELAYED_TRX, + mp_tx_psdu, + m_tx_cca, + true, + notify_tx_timeslot_denied); + (void)result; + } + else + { + notify_tx_timeslot_denied(result); + } + + tx_stop(); +} + +void nrf_802154_rsch_delayed_timeslot_failed(void) +{ + assert(tx_is_in_progress()); + + notify_tx_timeslot_denied(false); + + tx_stop(); +} diff --git a/third_party/NordicSemiconductor/drivers/radio/mac_features/nrf_802154_delayed_trx.h b/third_party/NordicSemiconductor/drivers/radio/mac_features/nrf_802154_delayed_trx.h new file mode 100644 index 000000000..aebc5df07 --- /dev/null +++ b/third_party/NordicSemiconductor/drivers/radio/mac_features/nrf_802154_delayed_trx.h @@ -0,0 +1,77 @@ +/* Copyright (c) 2018, Nordic Semiconductor ASA + * 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 Nordic Semiconductor ASA 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. + * + */ + +#ifndef NRF_802154_DELAYED_TRX_H__ +#define NRF_802154_DELAYED_TRX_H__ + +#include +#include + +#include "nrf_802154_const.h" +#include "nrf_802154_types.h" + +/** + * @defgroup nrf_802154_delayed_trx Delayed transmission and reception window features. + * @{ + * @ingroup nrf_802154 + * @brief Delayed transmission or receive window. + * + * This module implements delayed transmission and receive window features used in CSL and TSCH + * modes. + */ + +/** + * @brief Request transmission of a frame at given time. + * + * If requested transmission is successful and the frame is transmitted the + * @ref nrf_802154_tx_started is called. If the requested frame cannot be transmitted at given time + * the @ref nrf_802154_transmit_failed function is called. + * + * @note Delayed transmission does not timeout waiting for ACK automatically. Waiting for ACK shall + * be timed out by the next higher layer or the ACK timeout module. The ACK timeout timer + * shall start when the @ref nrf_802154_tx_started function is called. + * + * @param[in] p_data Pointer to array containing data to transmit (PHR + PSDU). + * @param[in] cca If the driver should perform CCA procedure before transmission. + * @param[in] t0 Base of delay time. + * @param[in] dt Delta of delay time from @p t0. + * @param[in] channel Number of channel on which the frame should be transmitted. + */ +bool nrf_802154_delayed_trx_transmit(const uint8_t * p_data, + bool cca, + uint32_t t0, + uint32_t dt, + uint8_t channel); + +/** + *@} + **/ + +#endif // NRF_802154_DELAYED_TRX_H__ diff --git a/third_party/NordicSemiconductor/drivers/radio/mac_features/nrf_802154_filter.c b/third_party/NordicSemiconductor/drivers/radio/mac_features/nrf_802154_filter.c index 7aaf40556..fccb8bfae 100644 --- a/third_party/NordicSemiconductor/drivers/radio/mac_features/nrf_802154_filter.c +++ b/third_party/NordicSemiconductor/drivers/radio/mac_features/nrf_802154_filter.c @@ -137,55 +137,58 @@ static bool dst_addressing_may_be_present(uint8_t frame_type) * @param[out] p_num_bytes Offset of addressing fields end. * @param[in] frame_type Type of incoming frame. * - * @retval true No errors in given frame were detected - it may be further processed. - * @retval false Detected an error in given frame - it should be discarded. + * @retval NRF_802154_RX_ERROR_NONE No errors in given frame were detected - it may be + * further processed. + * @retval NRF_802154_RX_ERROR_INVALID_DEST_ADDR The frame is valid but addressed to another node. + * @retval NRF_802154_RX_ERROR_INVALID_FRAME Detected an error in given frame - it should be + * discarded. */ -static bool dst_addressing_end_offset_get_2006(const uint8_t * p_psdu, - uint8_t * p_num_bytes, - uint8_t frame_type) +static nrf_802154_rx_error_t dst_addressing_end_offset_get_2006(const uint8_t * p_psdu, + uint8_t * p_num_bytes, + uint8_t frame_type) { - bool result; + nrf_802154_rx_error_t result; switch (p_psdu[DEST_ADDR_TYPE_OFFSET] & DEST_ADDR_TYPE_MASK) { case DEST_ADDR_TYPE_SHORT: *p_num_bytes = SHORT_ADDR_CHECK_OFFSET; - result = true; + result = NRF_802154_RX_ERROR_NONE; break; case DEST_ADDR_TYPE_EXTENDED: *p_num_bytes = EXTENDED_ADDR_CHECK_OFFSET; - result = true; + result = NRF_802154_RX_ERROR_NONE; break; case DEST_ADDR_TYPE_NONE: - if (frame_type == FRAME_TYPE_BEACON) + if (nrf_802154_pib_pan_coord_get() || (frame_type == FRAME_TYPE_BEACON)) { switch (p_psdu[SRC_ADDR_TYPE_OFFSET] & SRC_ADDR_TYPE_MASK) { case SRC_ADDR_TYPE_SHORT: *p_num_bytes = SHORT_ADDR_CHECK_OFFSET; - result = true; + result = NRF_802154_RX_ERROR_NONE; break; case SRC_ADDR_TYPE_EXTENDED: *p_num_bytes = EXTENDED_ADDR_CHECK_OFFSET; - result = true; + result = NRF_802154_RX_ERROR_NONE; break; default: - result = false; + result = NRF_802154_RX_ERROR_INVALID_FRAME; } } else { - result = true; + result = NRF_802154_RX_ERROR_INVALID_DEST_ADDR; } break; default: - result = false; + result = NRF_802154_RX_ERROR_INVALID_FRAME; } return result; @@ -203,14 +206,17 @@ static bool dst_addressing_end_offset_get_2006(const uint8_t * p_psdu, * @param[out] p_num_bytes Offset of addressing fields end. * @param[in] frame_type Type of incoming frame. * - * @retval true No errors in given frame were detected - it may be further processed. - * @retval false Detected an error in given frame - it should be discarded. + * @retval NRF_802154_RX_ERROR_NONE No errors in given frame were detected - it may be + * further processed. + * @retval NRF_802154_RX_ERROR_INVALID_DEST_ADDR The frame is valid but addressed to another node. + * @retval NRF_802154_RX_ERROR_INVALID_FRAME Detected an error in given frame - it should be + * discarded. */ -static bool dst_addressing_end_offset_get_2015(const uint8_t * p_psdu, - uint8_t * p_num_bytes, - uint8_t frame_type) +static nrf_802154_rx_error_t dst_addressing_end_offset_get_2015(const uint8_t * p_psdu, + uint8_t * p_num_bytes, + uint8_t frame_type) { - bool result = false; + nrf_802154_rx_error_t result; switch (frame_type) { @@ -224,14 +230,17 @@ static bool dst_addressing_end_offset_get_2015(const uint8_t * p_psdu, case FRAME_TYPE_MULTIPURPOSE: // TODO: Implement dst addressing filtering according to 2015 spec - result = false; + result = NRF_802154_RX_ERROR_INVALID_FRAME; break; case FRAME_TYPE_FRAGMENT: case FRAME_TYPE_EXTENDED: // No addressing data - result = true; + result = NRF_802154_RX_ERROR_NONE; break; + + default: + result = NRF_802154_RX_ERROR_INVALID_FRAME; } return result; @@ -249,15 +258,18 @@ static bool dst_addressing_end_offset_get_2015(const uint8_t * p_psdu, * @param[out] p_num_bytes Offset of addressing fields end. * @param[in] frame_type Type of incoming frame. * - * @retval true No errors in given frame were detected - it may be further processed. - * @retval false Detected an error in given frame - it should be discarded. + * @retval NRF_802154_RX_ERROR_NONE No errors in given frame were detected - it may be + * further processed. + * @retval NRF_802154_RX_ERROR_INVALID_DEST_ADDR The frame is valid but addressed to another node. + * @retval NRF_802154_RX_ERROR_INVALID_FRAME Detected an error in given frame - it should be + * discarded. */ -static bool dst_addressing_end_offset_get(const uint8_t * p_psdu, - uint8_t * p_num_bytes, - uint8_t frame_type, - uint8_t frame_version) +static nrf_802154_rx_error_t dst_addressing_end_offset_get(const uint8_t * p_psdu, + uint8_t * p_num_bytes, + uint8_t frame_type, + uint8_t frame_version) { - bool result; + nrf_802154_rx_error_t result; switch (frame_version) { @@ -271,7 +283,7 @@ static bool dst_addressing_end_offset_get(const uint8_t * p_psdu, break; default: - result = false; + result = NRF_802154_RX_ERROR_INVALID_FRAME; } return result; @@ -330,6 +342,11 @@ static bool dst_short_addr_check(const uint8_t * p_psdu) { result = true; } + else if (DEST_ADDR_TYPE_NONE == (p_psdu[DEST_ADDR_TYPE_OFFSET] & DEST_ADDR_TYPE_MASK) && + nrf_802154_pib_pan_coord_get()) + { + result = true; + } else { result = false; @@ -360,6 +377,11 @@ static bool dst_extended_addr_check(const uint8_t *p_psdu) { result = true; } + else if (DEST_ADDR_TYPE_NONE == (p_psdu[DEST_ADDR_TYPE_OFFSET] & DEST_ADDR_TYPE_MASK) && + nrf_802154_pib_pan_coord_get()) + { + result = true; + } else { result = false; @@ -398,8 +420,7 @@ nrf_802154_rx_error_t nrf_802154_filter_frame_part(const uint8_t * p_psdu, uint8 break; } - result = dst_addressing_end_offset_get(p_psdu, p_num_bytes, frame_type, frame_version) ? - NRF_802154_RX_ERROR_NONE : NRF_802154_RX_ERROR_INVALID_FRAME; + result = dst_addressing_end_offset_get(p_psdu, p_num_bytes, frame_type, frame_version); break; } diff --git a/third_party/NordicSemiconductor/drivers/radio/nrf_802154.c b/third_party/NordicSemiconductor/drivers/radio/nrf_802154.c index 8c6dc38b5..4dd205b00 100644 --- a/third_party/NordicSemiconductor/drivers/radio/nrf_802154.c +++ b/third_party/NordicSemiconductor/drivers/radio/nrf_802154.c @@ -53,17 +53,19 @@ #include "nrf_802154_priority_drop.h" #include "nrf_802154_request.h" #include "nrf_802154_revision.h" +#include "nrf_802154_rsch.h" #include "nrf_802154_rssi.h" #include "nrf_802154_rx_buffer.h" +#include "nrf_802154_timer_coord.h" #include "hal/nrf_radio.h" #include "platform/clock/nrf_802154_clock.h" +#include "platform/lp_timer/nrf_802154_lp_timer.h" #include "platform/temperature/nrf_802154_temperature.h" -#include "platform/timer/nrf_802154_timer.h" -#include "raal/nrf_raal_api.h" #include "timer_scheduler/nrf_802154_timer_sched.h" -#include "mac_features/nrf_802154_csma_ca.h" #include "mac_features/nrf_802154_ack_timeout.h" +#include "mac_features/nrf_802154_csma_ca.h" +#include "mac_features/nrf_802154_delayed_trx.h" #if ENABLE_FEM #include "fem/nrf_fem_control_api.h" @@ -97,6 +99,38 @@ static void tx_buffer_fill(const uint8_t * p_data, uint8_t length) } #endif // !NRF_802154_USE_RAW_API +/** + * @brief Get timestamp of the last received frame. + * + * @note This function increments the returned value by 1 us if the timestamp is equal to the + * @ref NRF_802154_NO_TIMESTAMP value to indicate that the timestamp is available. + * + * @returns Timestamp [us] of the last received frame or @ref NRF_802154_NO_TIMESTAMP if + * the timestamp is inaccurate. + */ +static uint32_t last_rx_frame_timestamp_get(void) +{ +#if NRF_802154_FRAME_TIMESTAMP_ENABLED + uint32_t timestamp; + bool timestamp_received = nrf_802154_timer_coord_timestamp_get(×tamp); + + if (!timestamp_received) + { + timestamp = NRF_802154_NO_TIMESTAMP; + } + else + { + if (timestamp == NRF_802154_NO_TIMESTAMP) + { + timestamp++; + } + } + + return timestamp; +#else // NRF_802154_FRAME_TIMESTAMP_ENABLED + return NRF_802154_NO_TIMESTAMP; +#endif // NRF_802154_FRAME_TIMESTAMP_ENABLED +} void nrf_802154_channel_set(uint8_t channel) { @@ -171,22 +205,25 @@ void nrf_802154_init(void) nrf_802154_critical_section_init(); nrf_802154_debug_init(); nrf_802154_notification_init(); + nrf_802154_lp_timer_init(); nrf_802154_pib_init(); nrf_802154_priority_drop_init(); nrf_802154_request_init(); nrf_802154_revision_init(); + nrf_802154_rsch_init(); nrf_802154_rx_buffer_init(); nrf_802154_temperature_init(); - nrf_802154_timer_init(); + nrf_802154_timer_coord_init(); nrf_802154_timer_sched_init(); - nrf_raal_init(); } void nrf_802154_deinit(void) { nrf_802154_timer_sched_deinit(); - nrf_802154_timer_deinit(); + nrf_802154_timer_coord_uninit(); nrf_802154_temperature_deinit(); + nrf_802154_rsch_uninit(); + nrf_802154_lp_timer_deinit(); nrf_802154_clock_deinit(); nrf_802154_core_deinit(); } @@ -251,12 +288,23 @@ bool nrf_802154_sleep(void) return result; } +nrf_802154_sleep_error_t nrf_802154_sleep_if_idle(void) +{ + nrf_802154_sleep_error_t result; + nrf_802154_log(EVENT_TRACE_ENTER, FUNCTION_SLEEP); + + result = nrf_802154_request_sleep(NRF_802154_TERM_NONE) ? NRF_802154_SLEEP_ERROR_NONE : NRF_802154_SLEEP_ERROR_BUSY; + + nrf_802154_log(EVENT_TRACE_EXIT, FUNCTION_SLEEP); + return result; +} + bool nrf_802154_receive(void) { bool result; nrf_802154_log(EVENT_TRACE_ENTER, FUNCTION_RECEIVE); - result = nrf_802154_request_receive(NRF_802154_TERM_802154, REQ_ORIG_HIGHER_LAYER, NULL); + result = nrf_802154_request_receive(NRF_802154_TERM_802154, REQ_ORIG_HIGHER_LAYER, NULL, true); nrf_802154_log(EVENT_TRACE_EXIT, FUNCTION_RECEIVE); return result; @@ -272,6 +320,7 @@ bool nrf_802154_transmit_raw(const uint8_t * p_data, bool cca) REQ_ORIG_HIGHER_LAYER, p_data, cca, + false, NULL); nrf_802154_log(EVENT_TRACE_EXIT, FUNCTION_TRANSMIT); @@ -290,6 +339,7 @@ bool nrf_802154_transmit(const uint8_t * p_data, uint8_t length, bool cca) REQ_ORIG_HIGHER_LAYER, m_tx_buffer, cca, + false, NULL); nrf_802154_log(EVENT_TRACE_EXIT, FUNCTION_TRANSMIT); @@ -298,6 +348,21 @@ bool nrf_802154_transmit(const uint8_t * p_data, uint8_t length, bool cca) #endif // NRF_802154_USE_RAW_API +bool nrf_802154_transmit_raw_at(const uint8_t * p_data, + bool cca, + uint32_t t0, + uint32_t dt, + uint8_t channel) +{ + bool result; + nrf_802154_log(EVENT_TRACE_ENTER, FUNCTION_TRANSMIT_AT); + + result = nrf_802154_delayed_trx_transmit(p_data, cca, t0, dt, channel); + + nrf_802154_log(EVENT_TRACE_EXIT, FUNCTION_TRANSMIT_AT); + return result; +} + bool nrf_802154_energy_detection(uint32_t time_us) { bool result; @@ -432,6 +497,16 @@ bool nrf_802154_auto_ack_get(void) return nrf_802154_pib_auto_ack_get(); } +bool nrf_802154_pan_coord_get(void) +{ + return nrf_802154_pib_pan_coord_get(); +} + +void nrf_802154_pan_coord_set(bool enabled) +{ + nrf_802154_pib_pan_coord_set(enabled); +} + void nrf_802154_auto_pending_bit_set(bool enabled) { nrf_802154_ack_pending_bit_set(enabled); @@ -509,35 +584,9 @@ __WEAK void nrf_802154_tx_ack_started(void) #if NRF_802154_USE_RAW_API __WEAK void nrf_802154_received_raw(uint8_t * p_data, int8_t power, uint8_t lqi) { -#if NRF_802154_FRAME_TIMESTAMP_ENABLED - uint32_t timestamp = nrf_802154_timer_sched_time_get(); - - nrf_802154_received_timestamp_raw(p_data, power, lqi, timestamp); -#else // NRF_802154_FRAME_TIMESTAMP_ENABLED - nrf_802154_buffer_free_raw(p_data); -#endif // NRF_802154_FRAME_TIMESTAMP_ENABLED + nrf_802154_received_timestamp_raw(p_data, power, lqi, last_rx_frame_timestamp_get()); } -#else // NRF_802154_USE_RAW_API -__WEAK void nrf_802154_received(uint8_t * p_data, uint8_t length, int8_t power, uint8_t lqi) -{ -#if NRF_802154_FRAME_TIMESTAMP_ENABLED - uint32_t timestamp = nrf_802154_timer_sched_time_get(); - - nrf_802154_received_timestamp(p_data, length, power, lqi, timestamp); -#else // NRF_802154_FRAME_TIMESTAMP_ENABLED - (void)length; - (void)power; - (void)lqi; - - nrf_802154_buffer_free(p_data); -#endif // NRF_802154_FRAME_TIMESTAMP_ENABLED -} -#endif // !NRF_802154_USE_RAW_API - -#if NRF_802154_FRAME_TIMESTAMP_ENABLED -#if NRF_802154_USE_RAW_API - __WEAK void nrf_802154_received_timestamp_raw(uint8_t * p_data, int8_t power, uint8_t lqi, @@ -552,6 +601,11 @@ __WEAK void nrf_802154_received_timestamp_raw(uint8_t * p_data, #else // NRF_802154_USE_RAW_API +__WEAK void nrf_802154_received(uint8_t * p_data, uint8_t length, int8_t power, uint8_t lqi) +{ + nrf_802154_received_timestamp(p_data, length, power, lqi, last_rx_frame_timestamp_get()); +} + __WEAK void nrf_802154_received_timestamp(uint8_t * p_data, uint8_t length, int8_t power, @@ -565,9 +619,7 @@ __WEAK void nrf_802154_received_timestamp(uint8_t * p_data, nrf_802154_buffer_free(p_data); } - -#endif // NRF_802154_USE_RAW_API -#endif // NRF_802154_FRAME_TIMESTAMP_ENABLED +#endif // !NRF_802154_USE_RAW_API __WEAK void nrf_802154_receive_failed(nrf_802154_rx_error_t error) { @@ -585,59 +637,11 @@ __WEAK void nrf_802154_transmitted_raw(const uint8_t * p_frame, int8_t power, uint8_t lqi) { -#if NRF_802154_FRAME_TIMESTAMP_ENABLED - - uint32_t timestamp = (p_ack == NULL) ? 0 : nrf_802154_timer_sched_time_get(); + uint32_t timestamp = (p_ack == NULL) ? NRF_802154_NO_TIMESTAMP : last_rx_frame_timestamp_get(); nrf_802154_transmitted_timestamp_raw(p_frame, p_ack, power, lqi, timestamp); - -#else // NRF_802154_FRAME_TIMESTAMP_ENABLED - - (void)p_frame; - (void)power; - (void)lqi; - - if (p_ack != NULL) - { - nrf_802154_buffer_free_raw(p_ack); - } - -#endif // NRF_802154_FRAME_TIMESTAMP_ENABLED } -#else // NRF_802154_USE_RAW_API -__WEAK void nrf_802154_transmitted(const uint8_t * p_frame, - uint8_t * p_ack, - uint8_t length, - int8_t power, - uint8_t lqi) -{ -#if NRF_802154_FRAME_TIMESTAMP_ENABLED - - uint32_t timestamp = (p_ack == NULL) ? 0 : nrf_802154_timer_sched_time_get(); - - nrf_802154_transmitted_timestamp(p_frame, p_ack, length, power, lqi, timestamp); - -#else // NRF_802154_FRAME_TIMESTAMP_ENABLED - - (void)p_frame; - (void)length; - (void)power; - (void)lqi; - - if (p_ack != NULL) - { - nrf_802154_buffer_free(p_ack); - } - -#endif // NRF_802154_FRAME_TIMESTAMP_ENABLED -} -#endif // NRF_802154_USE_RAW_API - - -#if NRF_802154_FRAME_TIMESTAMP_ENABLED -#if NRF_802154_USE_RAW_API - __WEAK void nrf_802154_transmitted_timestamp_raw(const uint8_t * p_frame, uint8_t * p_ack, int8_t power, @@ -657,6 +661,17 @@ __WEAK void nrf_802154_transmitted_timestamp_raw(const uint8_t * p_frame, #else // NRF_802154_USE_RAW_API +__WEAK void nrf_802154_transmitted(const uint8_t * p_frame, + uint8_t * p_ack, + uint8_t length, + int8_t power, + uint8_t lqi) +{ + uint32_t timestamp = (p_ack == NULL) ? NRF_802154_NO_TIMESTAMP : last_rx_frame_timestamp_get(); + + nrf_802154_transmitted_timestamp(p_frame, p_ack, length, power, lqi, timestamp); +} + __WEAK void nrf_802154_transmitted_timestamp(const uint8_t * p_frame, uint8_t * p_ack, uint8_t length, @@ -675,9 +690,8 @@ __WEAK void nrf_802154_transmitted_timestamp(const uint8_t * p_frame, nrf_802154_buffer_free(p_ack); } } - #endif // NRF_802154_USE_RAW_API -#endif // NRF_802154_FRAME_TIMESTAMP_ENABLED + __WEAK void nrf_802154_transmit_failed(const uint8_t * p_frame, nrf_802154_tx_error_t error) { diff --git a/third_party/NordicSemiconductor/drivers/radio/nrf_802154.h b/third_party/NordicSemiconductor/drivers/radio/nrf_802154.h index 4cab60c27..3cc7aeec3 100644 --- a/third_party/NordicSemiconductor/drivers/radio/nrf_802154.h +++ b/third_party/NordicSemiconductor/drivers/radio/nrf_802154.h @@ -51,6 +51,11 @@ extern "C" { #endif +/** + * @brief Timestamp value indicating that the timestamp is inaccurate. + */ +#define NRF_802154_NO_TIMESTAMP 0 + /** * @brief Initialize the 802.15.4 driver. * @@ -258,6 +263,21 @@ nrf_802154_state_t nrf_802154_state_get(void); */ bool nrf_802154_sleep(void); +/** + * @brief Change radio state to sleep if radio is idle. + * + * Sleep state is the lowest power state. In this state, the radio cannot transmit or receive + * frames. It is the only state in which the driver releases the high-frequency clock and does not + * request timeslots from a radio arbiter. + * + * @note If another module requests it, the high-frequency clock may be enabled even in radio sleep + * state. + * + * @retval NRF_802154_SLEEP_ERROR_NONE If the radio changes its state to low power mode. + * @retval NRF_802154_SLEEP_ERROR_BUSY If the driver could not schedule changing state. + */ +nrf_802154_sleep_error_t nrf_802154_sleep_if_idle(void); + /** * @brief Change radio state to receive. * @@ -352,6 +372,41 @@ bool nrf_802154_transmit(const uint8_t * p_data, uint8_t length, bool cca); #endif // NRF_802154_USE_RAW_API +/** + * @brief Request transmission at specified time. + * + * @note This function is implemented in zero-copy fashion. It passes the given buffer pointer to + * the RADIO peripheral. + * + * This function works as delayed version of the @sref nrf_drv_radio802154_transmit_raw. It is not + * blocking, but queues delayed transmission using Radio Scheduler module. If delayed transmission + * cannot be performed (the @ref nrf_drv_radio802154_transmit_raw would return false) or requested + * transmission timeslot is denied, the @ref nrf_drv_radio802154_transmit_failed with the + * @ref NRF_802154_TX_ERROR_TIMESLOT_DENIED argument is called. + * + * This function is designed to transmit first symbol of SHR at given time. + * + * If the requested transmission time is in the past, the function returns false and does not + * schedule transmission. + * + * @param[in] p_data Pointer to array containing data to transmit. First byte should contain frame + * length (including PHR and FCS). Following bytes should contain data. CRC is + * computed automatically by radio hardware. Therefore, the FCS field can + * contain any bytes. + * @param[in] cca If the driver should perform a CCA procedure before transmission. + * @param[in] t0 Base of delay time - absolute time used by the Timer Scheduler [us]. + * @param[in] dt Delta of delay time from @p t0 [us]. + * @param[in] channel Radio channel on which the frame should be transmitted. + * + * @retval true If the transmission procedure was scheduled. + * @retval false If the driver could not schedule the transmission procedure. + */ +bool nrf_802154_transmit_raw_at(const uint8_t * p_data, + bool cca, + uint32_t t0, + uint32_t dt, + uint8_t channel); + /** * @brief Change radio state to energy detection. * @@ -443,6 +498,30 @@ extern void nrf_802154_tx_ack_started(void); */ extern void nrf_802154_received_raw(uint8_t * p_data, int8_t power, uint8_t lqi); +/** + * @brief Notify that a frame was received at a given time. + * + * This functions works like @ref nrf_802154_received_raw and adds a timestamp to the parameter + * list. + * + * @note The received frame usually contains a timestamp. However, due to a race condition, + * the timestamp may be invalid. This erroneous situation is indicated by + * the @ref NRF_802154_NO_TIMESTAMP value of the @p time parameter. + * + * @param[in] p_data Pointer to the buffer containing received data (PHR + PSDU). First byte in + * the buffer is length of the frame (PHR). The following bytes contain the + * frame itself (PSDU). The length byte (PHR) includes FCS. FCS is already + * verified by the hardware and may be modified by the hardware. + * @param[in] power RSSI of received frame. + * @param[in] lqi LQI of received frame. + * @param[in] time Timestamp taken when the last symbol of the frame was received (in us) + * or @ref NRF_802154_NO_TIMESTAMP if the timestamp is invalid. + */ +extern void nrf_802154_received_timestamp_raw(uint8_t * p_data, + int8_t power, + uint8_t lqi, + uint32_t time); + #else // NRF_802154_USE_RAW_API /** @@ -471,54 +550,22 @@ extern void nrf_802154_received_raw(uint8_t * p_data, int8_t power, uint8_t lqi) */ extern void nrf_802154_received(uint8_t * p_data, uint8_t length, int8_t power, uint8_t lqi); -#endif // !NRF_802154_USE_RAW_API - -#if NRF_802154_FRAME_TIMESTAMP_ENABLED -#if NRF_802154_USE_RAW_API -/** - * @brief Notify that a frame was received at a given time. - * - * This functions works like @ref nrf_802154_received_raw and adds a timestamp to the parameter - * list. - * - * @note @p timestamp may be inaccurate due to software latency (IRQ handling). - * @note @p timestamp granularity depends on the granularity of the timer driver in the - * platform/timer directory. - * @note Including the timestamp for received frames uses resources like CPU time and memory. If the - * timestamp is not required, use @ref nrf_802154_received_raw instead. - * - * @param[in] p_data Pointer to the buffer containing received data (PHR + PSDU). First byte in - * the buffer is length of the frame (PHR). The following bytes contain the - * frame itself (PSDU). The length byte (PHR) includes FCS. FCS is already - * verified by the hardware and may be modified by the hardware. - * @param[in] power RSSI of received frame. - * @param[in] lqi LQI of received frame. - * @param[in] time Timestamp taken when the last symbol of the frame was received (in us). - */ -extern void nrf_802154_received_timestamp_raw(uint8_t * p_data, - int8_t power, - uint8_t lqi, - uint32_t time); - -#else // NRF_802154_USE_RAW_API - /** * @brief Notify that a frame was received at a given time. * * This functions works like @ref nrf_802154_received and adds a timestamp to the parameter list. * - * @note @p timestamp may be inaccurate due to software latency (IRQ handling). - * @note @p timestamp granularity depends on the granularity of the timer driver in the - * platform/timer directory. - * @note Including the timestamp for received frames uses resources like CPU time and memory. If the - * timestamp is not required, use @ref nrf_802154_received instead. + * @note The received frame usually contains timestamp. However, due to a race condition, + * the timestamp may be invalid. This erroneous situation is indicated by + * the @ref NRF_802154_NO_TIMESTAMP value of the @p time parameter. * * @param[in] p_data Pointer to the buffer containing the payload of the received frame (PSDU * without FCS). * @param[in] length Length of received payload. * @param[in] power RSSI of received frame. * @param[in] lqi LQI of received frame. - * @param[in] time Timestamp taken when the last symbol of the frame was received (in us). + * @param[in] time Timestamp taken when the last symbol of the frame was received (in us) + * or @ref NRF_802154_NO_TIMESTAMP if the timestamp is invalid. */ extern void nrf_802154_received_timestamp(uint8_t * p_data, uint8_t length, @@ -526,8 +573,7 @@ extern void nrf_802154_received_timestamp(uint8_t * p_data, uint8_t lqi, uint32_t time); -#endif // NRF_802154_USE_RAW_API -#endif // NRF_802154_FRAME_TIMESTAMP_ENABLED +#endif // !NRF_802154_USE_RAW_API /** * @brief Notify that reception of a frame failed. @@ -575,41 +621,6 @@ extern void nrf_802154_transmitted_raw(const uint8_t * p_frame, int8_t power, uint8_t lqi); -#else // NRF_802154_USE_RAW_API - -/** - * @brief Notify that a frame was transmitted. - * - * @note If ACK was requested for the transmitted frame, this function is called after a proper ACK - * is received. If ACK was not requested, this function is called just after transmission has - * ended. - * @note The buffer pointed to by @p p_ack is not modified by the radio driver (and cannot - * be used to receive a frame) until @ref nrf_802154_buffer_free is - * called. - * @note The buffer pointed to by @p p_ack may be modified by the function handler (and other - * modules) until @ref nrf_802154_buffer_free is called. - * @note The next higher layer should handle either @ref nrf_802154_transmitted or - * @ref nrf_802154_transmitted_raw. It should not handle both functions. - * - * @param[in] p_frame Pointer to the buffer containing PSDU of the transmitted frame. - * @param[in] p_ack Pointer to the buffer containing the received ACK payload (PHR excluding - * FCS). - * If ACK was not requested, @p p_ack is set to NULL. - * @param[in] length Length of the received ACK payload or 0 if ACK was not requested. - * @param[in] power RSSI of received frame or 0 if ACK was not requested. - * @param[in] lqi LQI of received frame or 0 if ACK was not requested. - */ -extern void nrf_802154_transmitted(const uint8_t * p_frame, - uint8_t * p_ack, - uint8_t length, - int8_t power, - uint8_t lqi); - -#endif // !NRF_802154_USE_RAW_API - -#if NRF_802154_FRAME_TIMESTAMP_ENABLED -#if NRF_802154_USE_RAW_API - /** * @brief Notify that a frame was transmitted. * @@ -641,6 +652,34 @@ extern void nrf_802154_transmitted_timestamp_raw(const uint8_t * p_frame, #else // NRF_802154_USE_RAW_API +/** + * @brief Notify that a frame was transmitted. + * + * @note If ACK was requested for the transmitted frame, this function is called after a proper ACK + * is received. If ACK was not requested, this function is called just after transmission has + * ended. + * @note The buffer pointed to by @p p_ack is not modified by the radio driver (and cannot + * be used to receive a frame) until @ref nrf_802154_buffer_free is + * called. + * @note The buffer pointed to by @p p_ack may be modified by the function handler (and other + * modules) until @ref nrf_802154_buffer_free is called. + * @note The next higher layer should handle either @ref nrf_802154_transmitted or + * @ref nrf_802154_transmitted_raw. It should not handle both functions. + * + * @param[in] p_frame Pointer to the buffer containing PSDU of the transmitted frame. + * @param[in] p_ack Pointer to the buffer containing the received ACK payload (PHR excluding + * FCS). + * If ACK was not requested, @p p_ack is set to NULL. + * @param[in] length Length of the received ACK payload or 0 if ACK was not requested. + * @param[in] power RSSI of received frame or 0 if ACK was not requested. + * @param[in] lqi LQI of received frame or 0 if ACK was not requested. + */ +extern void nrf_802154_transmitted(const uint8_t * p_frame, + uint8_t * p_ack, + uint8_t length, + int8_t power, + uint8_t lqi); + /** * @brief Notify that a frame was transmitted. * @@ -669,8 +708,7 @@ extern void nrf_802154_transmitted_timestamp(const uint8_t * p_frame, uint8_t lqi, uint32_t time); -#endif // NRF_802154_USE_RAW_API -#endif // NRF_802154_FRAME_TIMESTAMP_ENABLED +#endif // !NRF_802154_USE_RAW_API /** * @brief Notify that a frame was not transmitted due to busy channel. @@ -871,6 +909,23 @@ void nrf_802154_auto_ack_set(bool enabled); */ bool nrf_802154_auto_ack_get(void); +/** + * @brief Notify driver that radio is configured as the PAN coordinator. + * + * @note That information is used for packet filtering. + * + * @param[in] enabled If radio is configured as the PAN coordinator. + */ +void nrf_802154_pan_coord_set(bool enabled); + +/** + * @brief Check if radio is configured as the PAN coordinator. + * + * @retval true If radio is configured as the PAN coordinator. + * @retval false If radio is not configured as the PAN coordinator. + */ +bool nrf_802154_pan_coord_get(void); + /** * @brief Enable or disable setting pending bit in automatically transmitted ACK frames. * diff --git a/third_party/NordicSemiconductor/drivers/radio/nrf_802154_ack_pending_bit.c b/third_party/NordicSemiconductor/drivers/radio/nrf_802154_ack_pending_bit.c index 44d6cadaf..b02083550 100644 --- a/third_party/NordicSemiconductor/drivers/radio/nrf_802154_ack_pending_bit.c +++ b/third_party/NordicSemiconductor/drivers/radio/nrf_802154_ack_pending_bit.c @@ -77,27 +77,26 @@ static uint8_t m_num_of_pending_extended; */ static int8_t extended_addr_compare(const uint8_t * p_first_addr, const uint8_t * p_second_addr) { - uint64_t first_addr; - uint64_t second_addr; + uint32_t first_addr; + uint32_t second_addr; - // Cast address pointer to uint64_t variables in 2 steps to prevent unaligned access errors. - first_addr = *(uint32_t *)p_first_addr; - first_addr = (first_addr << 32) | (*(uint32_t *)(p_first_addr + sizeof(uint32_t))); - second_addr = *(uint32_t *)p_second_addr; - second_addr = (second_addr << 32) | (*(uint32_t *)(p_second_addr + sizeof(uint32_t))); + // Compare extended address in two steps to prevent unaligned access error. + for (uint32_t i = 0; i < EXTENDED_ADDRESS_SIZE / sizeof(uint32_t); i++) + { + first_addr = *(uint32_t *)(p_first_addr + (i * sizeof(uint32_t))); + second_addr = *(uint32_t *)(p_second_addr + (i * sizeof(uint32_t))); - if (first_addr < second_addr) - { - return -1; - } - else if (first_addr > second_addr) - { - return 1; - } - else - { - return 0; + if (first_addr < second_addr) + { + return -1; + } + else if (first_addr > second_addr) + { + return 1; + } } + + return 0; } /** diff --git a/third_party/NordicSemiconductor/drivers/radio/nrf_802154_config.h b/third_party/NordicSemiconductor/drivers/radio/nrf_802154_config.h index bea42e28c..aec1e42f6 100644 --- a/third_party/NordicSemiconductor/drivers/radio/nrf_802154_config.h +++ b/third_party/NordicSemiconductor/drivers/radio/nrf_802154_config.h @@ -272,6 +272,16 @@ extern "C" { #define NRF_802154_FRAME_TIMESTAMP_ENABLED 1 #endif +/** + * @def NRF_802154_DELAYED_TRX_ENABLED + * + * If delayed transmission and receive window features are available. + * + */ +#ifndef NRF_802154_DELAYED_TRX_ENABLED +#define NRF_802154_DELAYED_TRX_ENABLED 1 +#endif + /** * @} * @defgroup nrf_802154_config_clock Clock driver configuration @@ -315,8 +325,8 @@ extern "C" { * * Priority of RTC interrupt used in standalone timer driver implementation. * - * @note This configuration is only applicable for the Timer Abstraction Layer implementation - * in nrf_802154_timer_nodrv.c. + * @note This configuration is only applicable for the Low Power Timer Abstraction Layer implementation + * in nrf_802154_lp_timer_nodrv.c. * */ #ifndef NRF_802154_RTC_IRQ_PRIORITY @@ -328,8 +338,8 @@ extern "C" { * * RTC instance used in standalone timer driver implementation. * - * @note This configuration is only applicable for the Timer Abstraction Layer implementation - * in nrf_802154_timer_nodrv.c. + * @note This configuration is only applicable for the Low Power Timer Abstraction Layer implementation + * in nrf_802154_lp_timer_nodrv.c. * */ #ifndef NRF_802154_RTC_INSTANCE @@ -341,8 +351,8 @@ extern "C" { * * RTC interrupt handler name used in standalone timer driver implementation. * - * @note This configuration is only applicable for Timer Abstraction Layer implementation - * in nrf_802154_timer_nodrv.c. + * @note This configuration is only applicable for Low Power Timer Abstraction Layer implementation + * in nrf_802154_lp_timer_nodrv.c. * */ #ifndef NRF_802154_RTC_IRQ_HANDLER @@ -355,8 +365,8 @@ extern "C" { * * RTC Interrupt number used in standalone timer driver implementation. * - * @note This configuration is only applicable for the Timer Abstraction Layer implementation - * in nrf_802154_timer_nodrv.c. + * @note This configuration is only applicable for the Low Power Timer Abstraction Layer implementation + * in nrf_802154_lp_timer_nodrv.c. * */ #ifndef NRF_802154_RTC_IRQN diff --git a/third_party/NordicSemiconductor/drivers/radio/nrf_802154_const.h b/third_party/NordicSemiconductor/drivers/radio/nrf_802154_const.h index bd6d28d86..fd019e7cc 100644 --- a/third_party/NordicSemiconductor/drivers/radio/nrf_802154_const.h +++ b/third_party/NordicSemiconductor/drivers/radio/nrf_802154_const.h @@ -115,13 +115,16 @@ typedef enum { REQ_ORIG_HIGHER_LAYER, REQ_ORIG_CORE, - REQ_ORIG_RAAL, + REQ_ORIG_RSCH, #if NRF_802154_CSMA_CA_ENABLED REQ_ORIG_CSMA_CA, #endif // NRF_802154_CSMA_CA_ENABLED #if NRF_802154_ACK_TIMEOUT_ENABLED REQ_ORIG_ACK_TIMEOUT, #endif // NRF_802154_ACK_TIMEOUT_ENABLED +#if NRF_802154_DELAYED_TRX_ENABLED + REQ_ORIG_DELAYED_TRX, +#endif // NRF_802154_DELAYED_TRX_ENABLED } req_originator_t; #endif // NRD_DRV_RADIO802154_CONST_H_ diff --git a/third_party/NordicSemiconductor/drivers/radio/nrf_802154_core.c b/third_party/NordicSemiconductor/drivers/radio/nrf_802154_core.c index 1ac3bb2e6..a7dac12ab 100644 --- a/third_party/NordicSemiconductor/drivers/radio/nrf_802154_core.c +++ b/third_party/NordicSemiconductor/drivers/radio/nrf_802154_core.c @@ -52,8 +52,10 @@ #include "nrf_802154_priority_drop.h" #include "nrf_802154_procedures_duration.h" #include "nrf_802154_revision.h" +#include "nrf_802154_rsch.h" #include "nrf_802154_rssi.h" #include "nrf_802154_rx_buffer.h" +#include "nrf_802154_timer_coord.h" #include "nrf_802154_types.h" #include "fem/nrf_fem_control_api.h" #include "hal/nrf_egu.h" @@ -63,7 +65,6 @@ #include "mac_features/nrf_802154_filter.h" #include "nrf_802154_core_hooks.h" -#include "raal/nrf_raal_api.h" #define EGU_EVENT NRF_EGU_EVENT_TRIGGERED15 @@ -157,6 +158,8 @@ static inline uint32_t short_phyend_disable_mask_get(void) #define TXRU_TIME 40 ///< Transmitter ramp up time [us] #define EVENT_LAT 23 ///< END event latency [us] +#define MAX_CRIT_SECT_TIME 60 ///< Maximal time that the driver spends in single critical section. + #define LQI_VALUE_FACTOR 4 ///< Factor needed to calculate LQI value based on data from RADIO peripheral #define LQI_MAX 0xff ///< Maximal LQI value @@ -179,8 +182,7 @@ static const uint8_t * mp_tx_data; ///< Pointer to data to trans static uint32_t m_ed_time_left; ///< Remaining time of current energy detection procedure [us]. static uint8_t m_ed_result; ///< Result of current energy detection procedure. -static volatile radio_state_t m_state; ///< State of the radio driver -static volatile bool m_timeslot_is_granted; ///< Indicates if RAAL reported that timeslot is granted. +static volatile radio_state_t m_state; ///< State of the radio driver typedef struct { @@ -189,9 +191,13 @@ typedef struct #if !NRF_802154_DISABLE_BCC_MATCHING bool psdu_being_received :1; ///< If PSDU is currently being received. #endif // !NRF_802154_DISABLE_BCC_MATCHING -} nrf_radio802154_flags_t; -static nrf_radio802154_flags_t m_flags; ///< Flags used to store current driver state. +#if NRF_802154_TX_STARTED_NOTIFY_ENABLED + bool tx_started :1; ///< If requested transmission has started. +#endif // NRF_802154_TX_STARTED_NOTIFY_ENABLED +} nrf_802154_flags_t; +static nrf_802154_flags_t m_flags; ///< Flags used to store current driver state. +static volatile bool m_rsch_timeslot_is_granted; ///< State of the RSCH timeslot. /*************************************************************************************************** * @section Common core operations @@ -252,19 +258,6 @@ static uint8_t lqi_get(const uint8_t * p_data) return (uint8_t)lqi; } -/** Check if RAAL reported that timeslot is granted. - * - * @note This function may return other value than @ref nrf_raal_timeslot_is_granted, because - * in critical sections RAAL reports are delayed. - * - * @retval true RAAL reported that timeslot is granted. - * @retval false RAAL reported that timeslot is not granted. - */ -static bool timeslot_is_granted(void) -{ - return m_timeslot_is_granted; -} - static void received_frame_notify(uint8_t * p_psdu) { nrf_802154_notify_received(p_psdu, // data @@ -295,9 +288,11 @@ static void receive_failed_notify(nrf_802154_rx_error_t error) /** Notify MAC layer that transmission of requested frame has started. */ static void transmit_started_notify(void) { - if (nrf_802154_core_hooks_tx_started(mp_tx_data)) + const uint8_t * p_frame = mp_tx_data; + + if (nrf_802154_core_hooks_tx_started(p_frame)) { - nrf_802154_tx_started(mp_tx_data); + nrf_802154_tx_started(p_frame); } } @@ -305,23 +300,33 @@ static void transmit_started_notify(void) /** Notify MAC layer that a frame was transmitted. */ static void transmitted_frame_notify(uint8_t * p_ack, int8_t power, int8_t lqi) { + const uint8_t * p_frame = mp_tx_data; + nrf_802154_critical_section_nesting_allow(); - nrf_802154_core_hooks_transmitted(mp_tx_data); - nrf_802154_notify_transmitted(mp_tx_data, p_ack, power, lqi); + nrf_802154_core_hooks_transmitted(p_frame); + nrf_802154_notify_transmitted(p_frame, p_ack, power, lqi); nrf_802154_critical_section_nesting_deny(); } /** Notify MAC layer that transmission procedure failed. */ -static void transmit_failed_notify(const uint8_t * p_data, nrf_802154_tx_error_t error) +static void transmit_failed_notify(nrf_802154_tx_error_t error) +{ + const uint8_t * p_frame = mp_tx_data; + + if (nrf_802154_core_hooks_tx_failed(p_frame, error)) + { + nrf_802154_notify_transmit_failed(p_frame, error); + } +} + +/** Allow nesting critical sections and notify MAC layer that transmission procedure failed. */ +static void transmit_failed_notify_and_nesting_allow(nrf_802154_tx_error_t error) { nrf_802154_critical_section_nesting_allow(); - if (nrf_802154_core_hooks_tx_failed(p_data, error)) - { - nrf_802154_notify_transmit_failed(p_data, error); - } + transmit_failed_notify(error); nrf_802154_critical_section_nesting_deny(); } @@ -377,6 +382,30 @@ static bool psdu_is_being_received(void) #endif // NRF_802154_DISABLE_BCC_MATCHING } +/** Check if requested transmission has already started. + * + * @retval true The transmission has started. + * @retval false The transmission has not started. + */ +static bool transmission_has_started(void) +{ +#if NRF_802154_TX_STARTED_NOTIFY_ENABLED + return m_flags.tx_started; +#else // NRF_802154_TX_STARTED_NOTIFY_ENABLED + return nrf_radio_event_get(NRF_RADIO_EVENT_ADDRESS); +#endif // NRF_802154_TX_STARTED_NOTIFY_ENABLED +} + +/** Check if timeslot is currently granted. + * + * @retval true The timeslot is granted. + * @retval false The timeslot is not granted. + */ +static bool timeslot_is_granted(void) +{ + return m_rsch_timeslot_is_granted; +} + /*************************************************************************************************** * @section RX buffer management **************************************************************************************************/ @@ -605,7 +634,7 @@ static uint8_t ed_result_get(void) */ static bool ed_iter_setup(uint32_t time_us) { - uint32_t us_left_in_timeslot = nrf_raal_timeslot_us_left_get(); + uint32_t us_left_in_timeslot = nrf_802154_rsch_timeslot_us_left_get(); uint32_t next_ed_iters = us_left_in_timeslot / ED_ITER_DURATION; if (next_ed_iters > ED_ITERS_OVERHEAD) @@ -629,14 +658,7 @@ static bool ed_iter_setup(uint32_t time_us) } else { - if (timeslot_is_granted()) - { - irq_deinit(); - nrf_radio_reset(); - } - - nrf_fem_control_ppi_disable(NRF_FEM_CONTROL_LNA_PIN); - nrf_fem_control_pin_clear(); + // Silently wait for a new timeslot m_ed_time_left = time_us; @@ -846,12 +868,12 @@ static void rx_restart(bool set_shorts) } // Restart TIMER. - nrf_timer_task_trigger(NRF_802154_TIMER_INSTANCE, NRF_TIMER_TASK_STOP); - nrf_timer_task_trigger(NRF_802154_TIMER_INSTANCE, NRF_TIMER_TASK_CLEAR); + // Anomaly 78: use SHUTDOWN instead of STOP and CLEAR. + nrf_timer_task_trigger(NRF_802154_TIMER_INSTANCE, NRF_TIMER_TASK_SHUTDOWN); #if NRF_802154_DISABLE_BCC_MATCHING - nrf_timer_task_trigger(NRF_802154_COUNTER_TIMER_INSTANCE, NRF_TIMER_TASK_STOP); - nrf_timer_task_trigger(NRF_802154_COUNTER_TIMER_INSTANCE, NRF_TIMER_TASK_CLEAR); + // Anomaly 78: use SHUTDOWN instead of STOP and CLEAR. + nrf_timer_task_trigger(NRF_802154_COUNTER_TIMER_INSTANCE, NRF_TIMER_TASK_SHUTDOWN); #endif // NRF_802154_DISABLE_BCC_MATCHING // Enable self-disabled PPI or PPI disabled by CRCOK @@ -867,12 +889,40 @@ static void rx_restart(bool set_shorts) nrf_egu_event_clear(NRF_802154_SWI_EGU_INSTANCE, EGU_EVENT); nrf_ppi_channel_enable(PPI_DISABLED_EGU); + // Prepare the timer coordinator to get a precise timestamp of the CRCOK event. + nrf_802154_timer_coord_timestamp_prepare( + (uint32_t)nrf_radio_event_address_get(NRF_RADIO_EVENT_CRCOK)); + if (!ppi_egu_worked()) { nrf_radio_task_trigger(NRF_RADIO_TASK_DISABLE); } } +/** Check if time remaining in the timeslot is long enough to process whole critical section. */ +static bool remaining_timeslot_time_is_enough_for_crit_sect(void) +{ + return nrf_802154_rsch_timeslot_us_left_get() >= MAX_CRIT_SECT_TIME; +} + +/** Enter critical section and verify if there is enough time to complete operations within. */ +static bool critical_section_enter(void) +{ + bool result = nrf_802154_critical_section_enter(); + + if (result) + { + if (timeslot_is_granted() && !remaining_timeslot_time_is_enough_for_crit_sect()) + { + result = false; + + nrf_802154_critical_section_exit(); + } + } + + return result; +} + /** Terminate Falling Asleep procedure. */ static void falling_asleep_terminate(void) { @@ -886,7 +936,7 @@ static void falling_asleep_terminate(void) static void sleep_terminate(void) { nrf_802154_priority_drop_timeslot_exit_terminate(); - nrf_raal_continuous_mode_enter(); + nrf_802154_rsch_continuous_mode_enter(); } /** Terminate RX procedure. */ @@ -914,13 +964,13 @@ static void rx_terminate(void) nrf_ppi_fork_endpoint_setup(PPI_EGU_RAMP_UP, 0); #endif // NRF_802154_DISABLE_BCC_MATCHING - nrf_timer_task_trigger(NRF_802154_TIMER_INSTANCE, NRF_TIMER_TASK_STOP); - nrf_timer_task_trigger(NRF_802154_TIMER_INSTANCE, NRF_TIMER_TASK_CLEAR); + // Anomaly 78: use SHUTDOWN instead of STOP and CLEAR. + nrf_timer_task_trigger(NRF_802154_TIMER_INSTANCE, NRF_TIMER_TASK_SHUTDOWN); nrf_timer_shorts_disable(NRF_802154_TIMER_INSTANCE, NRF_TIMER_SHORT_COMPARE0_STOP_MASK | NRF_TIMER_SHORT_COMPARE2_STOP_MASK); #if NRF_802154_DISABLE_BCC_MATCHING - nrf_timer_task_trigger(NRF_802154_COUNTER_TIMER_INSTANCE, NRF_TIMER_TASK_STOP); - nrf_timer_task_trigger(NRF_802154_COUNTER_TIMER_INSTANCE, NRF_TIMER_TASK_CLEAR); + // Anomaly 78: use SHUTDOWN instead of STOP and CLEAR. + nrf_timer_task_trigger(NRF_802154_COUNTER_TIMER_INSTANCE, NRF_TIMER_TASK_SHUTDOWN); nrf_timer_shorts_disable(NRF_802154_COUNTER_TIMER_INSTANCE, NRF_TIMER_SHORT_COMPARE1_STOP_MASK); #endif // NRF_802154_DISABLE_BCC_MATCHING @@ -959,13 +1009,13 @@ static void tx_ack_terminate(void) nrf_ppi_fork_endpoint_setup(PPI_EGU_RAMP_UP, 0); #endif // !NRF_802154_DISABLE_BCC_MATCHING - nrf_timer_task_trigger(NRF_802154_TIMER_INSTANCE, NRF_TIMER_TASK_STOP); - nrf_timer_task_trigger(NRF_802154_TIMER_INSTANCE, NRF_TIMER_TASK_CLEAR); + // Anomaly 78: use SHUTDOWN instead of STOP and CLEAR. + nrf_timer_task_trigger(NRF_802154_TIMER_INSTANCE, NRF_TIMER_TASK_SHUTDOWN); nrf_timer_shorts_disable(NRF_802154_TIMER_INSTANCE, NRF_TIMER_SHORT_COMPARE0_STOP_MASK | NRF_TIMER_SHORT_COMPARE2_STOP_MASK); #if NRF_802154_DISABLE_BCC_MATCHING - nrf_timer_task_trigger(NRF_802154_COUNTER_TIMER_INSTANCE, NRF_TIMER_TASK_STOP); - nrf_timer_task_trigger(NRF_802154_COUNTER_TIMER_INSTANCE, NRF_TIMER_TASK_CLEAR); + // Anomaly 78: use SHUTDOWN instead of STOP and CLEAR. + nrf_timer_task_trigger(NRF_802154_COUNTER_TIMER_INSTANCE, NRF_TIMER_TASK_SHUTDOWN); nrf_timer_shorts_disable(NRF_802154_COUNTER_TIMER_INSTANCE, NRF_TIMER_SHORT_COMPARE1_STOP_MASK); #endif // NRF_802154_DISABLE_BCC_MATCHING @@ -1007,6 +1057,7 @@ static void tx_terminate(void) nrf_radio_int_disable(ints_to_disable); nrf_radio_shorts_set(SHORTS_IDLE); + nrf_radio_task_trigger(NRF_RADIO_TASK_CCASTOP); nrf_radio_task_trigger(NRF_RADIO_TASK_DISABLE); } } @@ -1047,6 +1098,7 @@ static void ed_terminate(void) { nrf_radio_int_disable(NRF_RADIO_INT_EDEND_MASK); nrf_radio_shorts_set(SHORTS_IDLE); + nrf_radio_task_trigger(NRF_RADIO_TASK_EDSTOP); nrf_radio_task_trigger(NRF_RADIO_TASK_DISABLE); } } @@ -1066,6 +1118,7 @@ static void cca_terminate(void) { nrf_radio_int_disable(NRF_RADIO_INT_CCABUSY_MASK | NRF_RADIO_INT_CCAIDLE_MASK); nrf_radio_shorts_set(SHORTS_IDLE); + nrf_radio_task_trigger(NRF_RADIO_TASK_CCASTOP); nrf_radio_task_trigger(NRF_RADIO_TASK_DISABLE); } } @@ -1088,8 +1141,8 @@ static void continuous_carrier_terminate(void) * * This function is called when MAC layer requests transition to another operation. * - * After calling this function RADIO should enter DISABLED state and RAAL should be in continuous - * mode. + * After calling this function RADIO should enter DISABLED state and Radio Scheduler + * should be in continuous mode. * * @param[in] term_lvl Termination level of this request. Selects procedures to abort. * @param[in] req_orig Module that originates termination request. @@ -1100,7 +1153,7 @@ static void continuous_carrier_terminate(void) */ static bool current_operation_terminate(nrf_802154_term_t term_lvl, req_originator_t req_orig, - bool notify_abort) + bool notify) { bool result = nrf_802154_core_hooks_terminate(term_lvl, req_orig); @@ -1109,9 +1162,10 @@ static bool current_operation_terminate(nrf_802154_term_t term_lvl, switch (m_state) { case RADIO_STATE_SLEEP: - if (req_orig != REQ_ORIG_RAAL) + if (req_orig != REQ_ORIG_RSCH) { - // Terminate sleep state unless it is requested by RAAL during timeslot end. + // Terminate sleep state unless it is requested by Radio Scheduler + // during timeslot end. sleep_terminate(); } @@ -1128,7 +1182,7 @@ static bool current_operation_terminate(nrf_802154_term_t term_lvl, { rx_terminate(); - if (notify_abort) + if (notify) { nrf_802154_notify_receive_failed(NRF_802154_RX_ERROR_ABORTED); } @@ -1150,7 +1204,7 @@ static bool current_operation_terminate(nrf_802154_term_t term_lvl, { tx_ack_terminate(); - if (notify_abort) + if (notify) { mp_current_rx_buffer->free = false; received_frame_notify(mp_current_rx_buffer->psdu); @@ -1169,9 +1223,9 @@ static bool current_operation_terminate(nrf_802154_term_t term_lvl, { tx_terminate(); - if (notify_abort) + if (notify) { - nrf_802154_notify_transmit_failed(mp_tx_data, NRF_802154_TX_ERROR_ABORTED); + transmit_failed_notify(NRF_802154_TX_ERROR_ABORTED); } } else @@ -1186,9 +1240,9 @@ static bool current_operation_terminate(nrf_802154_term_t term_lvl, { rx_ack_terminate(); - if (notify_abort) + if (notify) { - nrf_802154_notify_transmit_failed(mp_tx_data, NRF_802154_TX_ERROR_ABORTED); + transmit_failed_notify(NRF_802154_TX_ERROR_ABORTED); } } else @@ -1203,7 +1257,7 @@ static bool current_operation_terminate(nrf_802154_term_t term_lvl, { ed_terminate(); - if (notify_abort) + if (notify) { nrf_802154_notify_energy_detection_failed(NRF_802154_ED_ERROR_ABORTED); } @@ -1220,7 +1274,7 @@ static bool current_operation_terminate(nrf_802154_term_t term_lvl, { cca_terminate(); - if (notify_abort) + if (notify) { nrf_802154_notify_cca_failed(NRF_802154_CCA_ERROR_ABORTED); } @@ -1247,7 +1301,8 @@ static bool current_operation_terminate(nrf_802154_term_t term_lvl, static void sleep_init(void) { nrf_802154_priority_drop_timeslot_exit(); - m_timeslot_is_granted = false; + m_rsch_timeslot_is_granted = false; + nrf_802154_timer_coord_stop(); } /** Initialize Falling Asleep operation. */ @@ -1361,11 +1416,12 @@ static void rx_init(bool disabled_was_triggered) (uint32_t)nrf_timer_task_address_get( NRF_802154_COUNTER_TIMER_INSTANCE, NRF_TIMER_TASK_START)); + // Anomaly 78: use SHUTDOWN instead of CLEAR. nrf_ppi_channel_endpoint_setup(PPI_CRCERROR_CLEAR, (uint32_t)nrf_radio_event_address_get(NRF_RADIO_EVENT_CRCERROR), (uint32_t)nrf_timer_task_address_get( NRF_802154_TIMER_INSTANCE, - NRF_TIMER_TASK_CLEAR)); + NRF_TIMER_TASK_SHUTDOWN)); nrf_ppi_channel_endpoint_setup(PPI_CRCOK_DIS_PPI, (uint32_t)nrf_radio_event_address_get(NRF_RADIO_EVENT_CRCOK), (uint32_t)nrf_ppi_task_address_get(PPI_CHGRP0_DIS_TASK)); @@ -1399,11 +1455,12 @@ static void rx_init(bool disabled_was_triggered) (uint32_t)nrf_timer_task_address_get( NRF_802154_COUNTER_TIMER_INSTANCE, NRF_TIMER_TASK_COUNT)); + // Anomaly 78: use SHUTDOWN instead of CLEAR. nrf_ppi_channel_endpoint_setup(PPI_CRCERROR_COUNTER_CLEAR, (uint32_t)nrf_radio_event_address_get(NRF_RADIO_EVENT_CRCERROR), (uint32_t)nrf_timer_task_address_get( NRF_802154_COUNTER_TIMER_INSTANCE, - NRF_TIMER_TASK_CLEAR)); + NRF_TIMER_TASK_SHUTDOWN)); #endif // NRF_802154_DISABLE_BCC_MATCHING nrf_ppi_channel_enable(PPI_EGU_RAMP_UP); @@ -1416,6 +1473,10 @@ static void rx_init(bool disabled_was_triggered) #endif // NRF_802154_DISABLE_BCC_MATCHING nrf_ppi_channel_enable(PPI_DISABLED_EGU); + // Configure the timer coordinator to get a timestamp of the CRCOK event. + nrf_802154_timer_coord_timestamp_prepare( + (uint32_t)nrf_radio_event_address_get(NRF_RADIO_EVENT_CRCOK)); + // Start procedure if necessary if (!disabled_was_triggered || !ppi_egu_worked()) { @@ -1445,7 +1506,7 @@ static bool tx_init(const uint8_t * p_data, bool cca, bool disabled_was_triggere { uint32_t ints_to_enable = 0; - if (!nrf_raal_timeslot_request( + if (!timeslot_is_granted() || !nrf_802154_rsch_timeslot_request( nrf_802154_tx_duration_get(p_data[0], cca, ack_is_requested(p_data)))) { return false; @@ -1475,9 +1536,10 @@ static bool tx_init(const uint8_t * p_data, bool cca, bool disabled_was_triggere ints_to_enable |= NRF_RADIO_INT_CCABUSY_MASK; } -#if NRF_802154_TX_STARTED_NOTIFY_ENABLED nrf_radio_event_clear(NRF_RADIO_EVENT_ADDRESS); +#if NRF_802154_TX_STARTED_NOTIFY_ENABLED ints_to_enable |= NRF_RADIO_INT_ADDRESS_MASK; + m_flags.tx_started = false; #endif // NRF_802154_TX_STARTED_NOTIFY_ENABLED nrf_radio_int_enable(ints_to_enable); @@ -1502,7 +1564,7 @@ static bool tx_init(const uint8_t * p_data, bool cca, bool disabled_was_triggere /** Initialize ED operation */ static void ed_init(bool disabled_was_triggered) { - if (!ed_iter_setup(m_ed_time_left)) + if (!timeslot_is_granted() || !ed_iter_setup(m_ed_time_left)) { // Just wait for next timeslot if there is not enough time in this one. return; @@ -1533,7 +1595,7 @@ static void ed_init(bool disabled_was_triggered) /** Initialize CCA operation. */ static void cca_init(bool disabled_was_triggered) { - if (!nrf_raal_timeslot_request(nrf_802154_cca_duration_get())) + if (!timeslot_is_granted() || !nrf_802154_rsch_timeslot_request(nrf_802154_cca_duration_get())) { return; } @@ -1585,94 +1647,90 @@ static void continuous_carrier_init(bool disabled_was_triggered) } + /*************************************************************************************************** - * @section RAAL notification handlers + * @section Radio Scheduler notification handlers **************************************************************************************************/ -void nrf_raal_timeslot_started(void) +void nrf_802154_critical_section_rsch_prec_approved(void) { nrf_802154_log(EVENT_TRACE_ENTER, FUNCTION_TIMESLOT_STARTED); - // Prevent interrupting of this handler by requests from higher priority code. - nrf_802154_critical_section_forcefully_enter(); - - nrf_radio_reset(); - nrf_radio_init(); - irq_init(); - - if (m_state != RADIO_STATE_SLEEP) + if (remaining_timeslot_time_is_enough_for_crit_sect()) { - m_timeslot_is_granted = true; + nrf_radio_reset(); + nrf_radio_init(); + irq_init(); + + assert(nrf_radio_shorts_get() == SHORTS_IDLE); + + if (m_state != RADIO_STATE_SLEEP) + { + m_rsch_timeslot_is_granted = true; + nrf_802154_timer_coord_start(); + } + + switch (m_state) + { + case RADIO_STATE_SLEEP: + // Intentionally empty. + // Ignore this notification if continuous mode was not requested. + break; + + case RADIO_STATE_RX: + rx_init(false); + break; + + case RADIO_STATE_CCA_TX: + (void)tx_init(mp_tx_data, true, false); + break; + + case RADIO_STATE_TX: + (void)tx_init(mp_tx_data, false, false); + break; + + case RADIO_STATE_ED: + ed_init(false); + break; + + case RADIO_STATE_CCA: + cca_init(false); + break; + + case RADIO_STATE_CONTINUOUS_CARRIER: + continuous_carrier_init(false); + break; + + default: + assert(false); + } } - assert(nrf_radio_shorts_get() == SHORTS_IDLE); - - switch (m_state) - { - case RADIO_STATE_RX: - rx_init(false); - break; - - case RADIO_STATE_CCA_TX: - (void)tx_init(mp_tx_data, true, false); - break; - - case RADIO_STATE_TX: - (void)tx_init(mp_tx_data, false, false); - break; - - case RADIO_STATE_ED: - ed_init(false); - break; - - case RADIO_STATE_CCA: - cca_init(false); - break; - - case RADIO_STATE_CONTINUOUS_CARRIER: - continuous_carrier_init(false); - break; - - case RADIO_STATE_SLEEP: - // This case may happen when sleep is requested by the next higher layer right before - // timeslot starts and the driver uses SWI for requests and notifications. In this case - // RAAL may report timeslot start event when exiting sleep request critical section. - // The driver is already in SLEEP state but did not request timeslot end yet - it will - // be requested in the next SWI handler. - break; - - default: - assert(false); - } - - nrf_802154_critical_section_exit(); - nrf_802154_log(EVENT_TRACE_EXIT, FUNCTION_TIMESLOT_STARTED); } -void nrf_raal_timeslot_ended(void) +void nrf_802154_critical_section_rsch_prec_denied(void) { bool result; nrf_802154_log(EVENT_TRACE_ENTER, FUNCTION_TIMESLOT_ENDED); - // Prevent interrupting of this handler by requests from higher priority code. - nrf_802154_critical_section_forcefully_enter(); - irq_deinit(); nrf_radio_reset(); nrf_fem_control_pin_clear(); - result = current_operation_terminate(NRF_802154_TERM_802154, REQ_ORIG_RAAL, false); + m_rsch_timeslot_is_granted = false; + nrf_802154_timer_coord_stop(); + + result = current_operation_terminate(NRF_802154_TERM_802154, REQ_ORIG_RSCH, false); assert(result); (void)result; - m_timeslot_is_granted = false; - switch (m_state) { case RADIO_STATE_SLEEP: // Intentionally empty. + // Ignore this notification if continuous mode was not requested. break; case RADIO_STATE_FALLING_ASLEEP: @@ -1698,7 +1756,7 @@ void nrf_raal_timeslot_ended(void) case RADIO_STATE_TX: case RADIO_STATE_RX_ACK: state_set(RADIO_STATE_RX); - transmit_failed_notify(mp_tx_data, NRF_802154_TX_ERROR_TIMESLOT_ENDED); + transmit_failed_notify_and_nesting_allow(NRF_802154_TX_ERROR_TIMESLOT_ENDED); break; case RADIO_STATE_ED: @@ -1711,8 +1769,6 @@ void nrf_raal_timeslot_ended(void) assert(false); } - nrf_802154_critical_section_exit(); - nrf_802154_log(EVENT_TRACE_EXIT, FUNCTION_TIMESLOT_ENDED); } @@ -1724,6 +1780,10 @@ void nrf_raal_timeslot_ended(void) static void irq_address_state_tx_frame(void) { transmit_started_notify(); + +#if NRF_802154_TX_STARTED_NOTIFY_ENABLED + m_flags.tx_started = true; +#endif // NRF_802154_TX_STARTED_NOTIFY_ENABLED } static void irq_address_state_tx_ack(void) @@ -1732,16 +1792,20 @@ static void irq_address_state_tx_ack(void) } #if !NRF_802154_DISABLE_BCC_MATCHING -/// This event is generated during frame reception to request RAAL timeslot and to filter frame +// This event is generated during frame reception to request Radio Scheduler timeslot +// and to filter frame static void irq_bcmatch_state_rx(void) { uint8_t prev_num_psdu_bytes; uint8_t num_psdu_bytes; nrf_802154_rx_error_t filter_result; + bool frame_accepted = true; num_psdu_bytes = nrf_radio_bcc_get() / 8; prev_num_psdu_bytes = num_psdu_bytes; + assert(num_psdu_bytes >= PHR_SIZE + FCF_SIZE); + // If CRCERROR event is set, it means that events are handled out of order due to software // latency. Just skip this handler in this case - frame will be dropped. if (nrf_radio_event_get(NRF_RADIO_EVENT_CRCERROR)) @@ -1749,27 +1813,6 @@ static void irq_bcmatch_state_rx(void) return; } - if (!m_flags.rx_timeslot_requested) - { - assert(num_psdu_bytes >= PHR_SIZE + FCF_SIZE); - - if (nrf_raal_timeslot_request(nrf_802154_rx_duration_get( - mp_current_rx_buffer->psdu[0], - ack_is_requested(mp_current_rx_buffer->psdu)))) - { - m_flags.rx_timeslot_requested = true; - } - else - { - irq_deinit(); - nrf_radio_reset(); - - nrf_802154_notify_receive_failed(NRF_802154_RX_ERROR_TIMESLOT_ENDED); - - return; - } - } - if (!m_flags.frame_filtered) { m_flags.psdu_being_received = true; @@ -1786,11 +1829,14 @@ static void irq_bcmatch_state_rx(void) m_flags.frame_filtered = true; } } - else if (!nrf_802154_pib_promiscuous_get()) + else if ((filter_result == NRF_802154_RX_ERROR_INVALID_FRAME) || + (!nrf_802154_pib_promiscuous_get())) { rx_terminate(); rx_init(true); + frame_accepted = false; + if ((mp_current_rx_buffer->psdu[FRAME_TYPE_OFFSET] & FRAME_TYPE_MASK) != FRAME_TYPE_ACK) { @@ -1802,6 +1848,23 @@ static void irq_bcmatch_state_rx(void) // Promiscuous mode, allow incorrect frames. Nothing to do here. } } + + if ((!m_flags.rx_timeslot_requested) && (frame_accepted)) + { + if (nrf_802154_rsch_timeslot_request(nrf_802154_rx_duration_get( + mp_current_rx_buffer->psdu[0], + ack_is_requested(mp_current_rx_buffer->psdu)))) + { + m_flags.rx_timeslot_requested = true; + } + else + { + // Disable receiver and wait for a new timeslot. + rx_terminate(); + + nrf_802154_notify_receive_failed(NRF_802154_RX_ERROR_TIMESLOT_ENDED); + } + } } #endif //!NRF_802154_DISABLE_BCC_MATCHING @@ -1851,11 +1914,11 @@ static void irq_crcok_state_rx(void) // Timeslot request if (m_flags.frame_filtered && ack_is_requested(p_received_psdu) && - !nrf_raal_timeslot_request(nrf_802154_rx_duration_get(0, true))) + !nrf_802154_rsch_timeslot_request(nrf_802154_rx_duration_get(0, true))) { - // Frame is destined to this node but there is no timeslot to transmit ACK - irq_deinit(); - nrf_radio_reset(); + // Frame is destined to this node but there is no timeslot to transmit ACK. + // Just disable receiver and wait for a new timeslot. + rx_terminate(); rx_flags_clear(); @@ -2082,22 +2145,23 @@ static void irq_phyend_state_tx_ack(void) nrf_radio_int_enable(ints_to_enable); // Restart TIMER. - nrf_timer_task_trigger(NRF_802154_TIMER_INSTANCE, NRF_TIMER_TASK_STOP); - nrf_timer_task_trigger(NRF_802154_TIMER_INSTANCE, NRF_TIMER_TASK_CLEAR); + // Anomaly 78: use SHUTDOWN instead of STOP and CLEAR. + nrf_timer_task_trigger(NRF_802154_TIMER_INSTANCE, NRF_TIMER_TASK_SHUTDOWN); #if NRF_802154_DISABLE_BCC_MATCHING - nrf_timer_task_trigger(NRF_802154_COUNTER_TIMER_INSTANCE, NRF_TIMER_TASK_STOP); - nrf_timer_task_trigger(NRF_802154_COUNTER_TIMER_INSTANCE, NRF_TIMER_TASK_CLEAR); + // Anomaly 78: use SHUTDOWN instead of STOP and CLEAR. + nrf_timer_task_trigger(NRF_802154_COUNTER_TIMER_INSTANCE, NRF_TIMER_TASK_SHUTDOWN); // Reset PPI for RX mode #if PPI_TIMER_TX_ACK != PPI_CRCERROR_CLEAR #error Invalid PPI configuration #endif + // Anomaly 78: use SHUTDOWN instead of CLEAR. nrf_ppi_channel_endpoint_setup(PPI_CRCERROR_CLEAR, (uint32_t)nrf_radio_event_address_get(NRF_RADIO_EVENT_CRCERROR), (uint32_t)nrf_timer_task_address_get( NRF_802154_TIMER_INSTANCE, - NRF_TIMER_TASK_CLEAR)); + NRF_TIMER_TASK_SHUTDOWN)); nrf_ppi_fork_endpoint_setup(PPI_EGU_TIMER_START, (uint32_t)nrf_timer_task_address_get( @@ -2118,6 +2182,10 @@ static void irq_phyend_state_tx_ack(void) nrf_egu_event_clear(NRF_802154_SWI_EGU_INSTANCE, EGU_EVENT); nrf_ppi_channel_enable(PPI_DISABLED_EGU); + // Prepare the timer coordinator to get a precise timestamp of the CRCOK event. + nrf_802154_timer_coord_timestamp_prepare( + (uint32_t)nrf_radio_event_address_get(NRF_RADIO_EVENT_CRCOK)); + if (!ppi_egu_worked()) { nrf_radio_task_trigger(NRF_RADIO_TASK_DISABLE); @@ -2149,6 +2217,13 @@ static void irq_phyend_state_tx_frame(void) { uint32_t ints_to_disable = 0; + // Ignore PHYEND event if transmission has not started. This event may be triggered by + // previously terminated transmission. + if (!transmission_has_started()) + { + return; + } + if (ack_is_requested(mp_tx_data)) { bool rx_buffer_free = rx_buffer_is_available(); @@ -2258,7 +2333,7 @@ static void irq_end_state_rx_ack(void) } else { - transmit_failed_notify(mp_tx_data, NRF_802154_TX_ERROR_INVALID_ACK); + transmit_failed_notify_and_nesting_allow(NRF_802154_TX_ERROR_INVALID_ACK); } } @@ -2285,7 +2360,7 @@ static void irq_ccabusy_state_tx_frame(void) state_set(RADIO_STATE_RX); rx_init(true); - transmit_failed_notify(mp_tx_data, NRF_802154_TX_ERROR_BUSY_CHANNEL); + transmit_failed_notify_and_nesting_allow(NRF_802154_TX_ERROR_BUSY_CHANNEL); } static void irq_ccabusy_state_cca(void) @@ -2604,17 +2679,22 @@ radio_state_t nrf_802154_core_state_get(void) bool nrf_802154_core_sleep(nrf_802154_term_t term_lvl) { - bool result = true; + bool result = critical_section_enter(); - if ((m_state != RADIO_STATE_SLEEP) && (m_state != RADIO_STATE_FALLING_ASLEEP)) + if (result) { - result = current_operation_terminate(term_lvl, REQ_ORIG_CORE, true); - - if (result) + if ((m_state != RADIO_STATE_SLEEP) && (m_state != RADIO_STATE_FALLING_ASLEEP)) { - state_set(RADIO_STATE_FALLING_ASLEEP); - falling_asleep_init(); + result = current_operation_terminate(term_lvl, REQ_ORIG_CORE, true); + + if (result) + { + state_set(RADIO_STATE_FALLING_ASLEEP); + falling_asleep_init(); + } } + + nrf_802154_critical_section_exit(); } return result; @@ -2622,23 +2702,36 @@ bool nrf_802154_core_sleep(nrf_802154_term_t term_lvl) bool nrf_802154_core_receive(nrf_802154_term_t term_lvl, req_originator_t req_orig, - nrf_802154_notification_func_t notify_function) + nrf_802154_notification_func_t notify_function, + bool notify_abort) { - bool result = true; + bool result = critical_section_enter(); - if ((m_state != RADIO_STATE_RX) && (m_state != RADIO_STATE_TX_ACK)) + if (result) { - result = current_operation_terminate(term_lvl, req_orig, notify_function == NULL); - - if (result) + if ((m_state != RADIO_STATE_RX) && (m_state != RADIO_STATE_TX_ACK)) { - state_set(RADIO_STATE_RX); - rx_init(true); + result = current_operation_terminate(term_lvl, req_orig, notify_abort); + + if (result) + { + state_set(RADIO_STATE_RX); + rx_init(true); + } + + if (notify_function != NULL) + { + notify_function(result); + } } + nrf_802154_critical_section_exit(); + } + else + { if (notify_function != NULL) { - notify_function(result); + notify_function(false); } } @@ -2649,27 +2742,47 @@ bool nrf_802154_core_transmit(nrf_802154_term_t term_lvl, req_originator_t req_orig, const uint8_t * p_data, bool cca, + bool immediate, nrf_802154_notification_func_t notify_function) { - bool result = current_operation_terminate(term_lvl, req_orig, notify_function == NULL); + bool result = critical_section_enter(); if (result) { - // Set state to RX in case sleep terminate succeeded, but transmit_begin fails. - state_set(RADIO_STATE_RX); + result = current_operation_terminate(term_lvl, req_orig, true); - mp_tx_data = p_data; - result = tx_init(p_data, cca, true); + if (result) + { + // Set state to RX in case sleep terminate succeeded, but transmit_begin fails. + state_set(RADIO_STATE_RX); + + mp_tx_data = p_data; + result = tx_init(p_data, cca, true); + + if (!immediate) + { + result = true; + } + } + + if (result) + { + state_set(cca ? RADIO_STATE_CCA_TX : RADIO_STATE_TX); + } + + if (notify_function != NULL) + { + notify_function(result); + } + + nrf_802154_critical_section_exit(); } - - if (result) + else { - state_set(cca ? RADIO_STATE_CCA_TX : RADIO_STATE_TX); - } - - if (notify_function != NULL) - { - notify_function(result); + if (notify_function != NULL) + { + notify_function(false); + } } return result; @@ -2677,14 +2790,21 @@ bool nrf_802154_core_transmit(nrf_802154_term_t term_lvl, bool nrf_802154_core_energy_detection(nrf_802154_term_t term_lvl, uint32_t time_us) { - bool result = current_operation_terminate(term_lvl, REQ_ORIG_CORE, true); + bool result = critical_section_enter(); if (result) { - state_set(RADIO_STATE_ED); - m_ed_time_left = time_us; - m_ed_result = 0; - ed_init(true); + result = current_operation_terminate(term_lvl, REQ_ORIG_CORE, true); + + if (result) + { + state_set(RADIO_STATE_ED); + m_ed_time_left = time_us; + m_ed_result = 0; + ed_init(true); + } + + nrf_802154_critical_section_exit(); } return result; @@ -2692,12 +2812,19 @@ bool nrf_802154_core_energy_detection(nrf_802154_term_t term_lvl, uint32_t time_ bool nrf_802154_core_cca(nrf_802154_term_t term_lvl) { - bool result = current_operation_terminate(term_lvl, REQ_ORIG_CORE, true); + bool result = critical_section_enter(); if (result) { - state_set(RADIO_STATE_CCA); - cca_init(true); + result = current_operation_terminate(term_lvl, REQ_ORIG_CORE, true); + + if (result) + { + state_set(RADIO_STATE_CCA); + cca_init(true); + } + + nrf_802154_critical_section_exit(); } return result; @@ -2705,12 +2832,19 @@ bool nrf_802154_core_cca(nrf_802154_term_t term_lvl) bool nrf_802154_core_continuous_carrier(nrf_802154_term_t term_lvl) { - bool result = current_operation_terminate(term_lvl, REQ_ORIG_CORE, true); + bool result = critical_section_enter(); if (result) { - state_set(RADIO_STATE_CONTINUOUS_CARRIER); - continuous_carrier_init(true); + result = current_operation_terminate(term_lvl, REQ_ORIG_CORE, true); + + if (result) + { + state_set(RADIO_STATE_CONTINUOUS_CARRIER); + continuous_carrier_init(true); + } + + nrf_802154_critical_section_exit(); } return result; @@ -2718,50 +2852,54 @@ bool nrf_802154_core_continuous_carrier(nrf_802154_term_t term_lvl) bool nrf_802154_core_notify_buffer_free(uint8_t * p_data) { - rx_buffer_t * p_buffer = (rx_buffer_t *)p_data; + rx_buffer_t * p_buffer = (rx_buffer_t *)p_data; + bool in_crit_sect = critical_section_enter(); p_buffer->free = true; - if (!timeslot_is_granted()) + if (in_crit_sect) { - return true; - } - - switch (m_state) - { - case RADIO_STATE_RX: - if (nrf_radio_state_get() == NRF_RADIO_STATE_RX_IDLE) + if (timeslot_is_granted()) + { + switch (m_state) { - assert(nrf_radio_shorts_get() == SHORTS_RX); + case RADIO_STATE_RX: + if (nrf_radio_state_get() == NRF_RADIO_STATE_RX_IDLE) + { + assert(nrf_radio_shorts_get() == SHORTS_RX); - rx_buffer_in_use_set(p_buffer); + rx_buffer_in_use_set(p_buffer); - nrf_radio_packet_ptr_set(rx_buffer_get()); - nrf_radio_shorts_set(SHORTS_RX | SHORTS_RX_FREE_BUFFER); + nrf_radio_packet_ptr_set(rx_buffer_get()); + nrf_radio_shorts_set(SHORTS_RX | SHORTS_RX_FREE_BUFFER); - nrf_radio_task_trigger(NRF_RADIO_TASK_START); + nrf_radio_task_trigger(NRF_RADIO_TASK_START); + } + + break; + + case RADIO_STATE_RX_ACK: + if (nrf_radio_state_get() == NRF_RADIO_STATE_RX_IDLE) + { + assert(nrf_radio_shorts_get() == SHORTS_RX_ACK); + + rx_buffer_in_use_set(p_buffer); + + nrf_radio_packet_ptr_set(rx_buffer_get()); + nrf_radio_shorts_set(SHORTS_RX_ACK | SHORTS_RX_FREE_BUFFER); + + nrf_radio_task_trigger(NRF_RADIO_TASK_START); + } + + break; + + default: + // Don't perform any action in any other state (receiver should not be started). + break; } + } - break; - - case RADIO_STATE_RX_ACK: - if (nrf_radio_state_get() == NRF_RADIO_STATE_RX_IDLE) - { - assert(nrf_radio_shorts_get() == SHORTS_RX_ACK); - - rx_buffer_in_use_set(p_buffer); - - nrf_radio_packet_ptr_set(rx_buffer_get()); - nrf_radio_shorts_set(SHORTS_RX_ACK | SHORTS_RX_FREE_BUFFER); - - nrf_radio_task_trigger(NRF_RADIO_TASK_START); - } - - break; - - default: - // Don't perform any action in any other state (receiver should not be started). - break; + nrf_802154_critical_section_exit(); } return true; @@ -2769,66 +2907,80 @@ bool nrf_802154_core_notify_buffer_free(uint8_t * p_data) bool nrf_802154_core_channel_update(void) { - switch (m_state) + bool result = critical_section_enter(); + + if (result) { - case RADIO_STATE_RX: + switch (m_state) { - bool result; - - if (timeslot_is_granted()) + case RADIO_STATE_RX: { - channel_set(nrf_802154_pib_channel_get()); + bool term_result; + + if (timeslot_is_granted()) + { + channel_set(nrf_802154_pib_channel_get()); + } + + term_result = current_operation_terminate(NRF_802154_TERM_NONE, REQ_ORIG_CORE, true); + + if (term_result) + { + rx_init(true); + } + + break; } - result = current_operation_terminate(NRF_802154_TERM_NONE, REQ_ORIG_CORE, true); + case RADIO_STATE_CONTINUOUS_CARRIER: + if (timeslot_is_granted()) + { + channel_set(nrf_802154_pib_channel_get()); + nrf_radio_task_trigger(NRF_RADIO_TASK_DISABLE); + } - if (result) - { - rx_init(true); - } + break; - break; + case RADIO_STATE_TX_ACK: + case RADIO_STATE_CCA_TX: + case RADIO_STATE_TX: + case RADIO_STATE_RX_ACK: + case RADIO_STATE_CCA: + if (timeslot_is_granted()) + { + channel_set(nrf_802154_pib_channel_get()); + } + + break; + + case RADIO_STATE_SLEEP: + case RADIO_STATE_FALLING_ASLEEP: + case RADIO_STATE_ED: + // Don't perform any action in these states (channel will be updated on state change). + break; } - case RADIO_STATE_CONTINUOUS_CARRIER: - if (timeslot_is_granted()) - { - channel_set(nrf_802154_pib_channel_get()); - nrf_radio_task_trigger(NRF_RADIO_TASK_DISABLE); - } - - break; - - case RADIO_STATE_TX_ACK: - case RADIO_STATE_CCA_TX: - case RADIO_STATE_TX: - case RADIO_STATE_RX_ACK: - case RADIO_STATE_CCA: - if (timeslot_is_granted()) - { - channel_set(nrf_802154_pib_channel_get()); - } - - break; - - case RADIO_STATE_SLEEP: - case RADIO_STATE_FALLING_ASLEEP: - case RADIO_STATE_ED: - // Don't perform any action in these states (channel will be updated on state change). - break; + nrf_802154_critical_section_exit(); } - return true; + return result; } bool nrf_802154_core_cca_cfg_update(void) { - if (timeslot_is_granted()) + bool result = critical_section_enter(); + + if (result) { - cca_configuration_update(); + if (timeslot_is_granted()) + { + cca_configuration_update(); + } + + nrf_802154_critical_section_exit(); } - return true; + return result; } #if NRF_802154_INTERNAL_RADIO_IRQ_HANDLING diff --git a/third_party/NordicSemiconductor/drivers/radio/nrf_802154_core.h b/third_party/NordicSemiconductor/drivers/radio/nrf_802154_core.h index 66be46176..87001e769 100644 --- a/third_party/NordicSemiconductor/drivers/radio/nrf_802154_core.h +++ b/third_party/NordicSemiconductor/drivers/radio/nrf_802154_core.h @@ -54,8 +54,8 @@ extern "C" { typedef enum { // Sleep - RADIO_STATE_SLEEP, ///< Low power (DISABLED) mode - the only state in which HF clock is released and RAAL disabled. - RADIO_STATE_FALLING_ASLEEP, ///< Prior entering SLEEP state RAAL and HF clock are active. + RADIO_STATE_SLEEP, ///< Low power (DISABLED) mode - the only state in which all radio preconditions ane not requested. + RADIO_STATE_FALLING_ASLEEP, ///< Prior entering SLEEP state all radio preconditions are requested. // Receive RADIO_STATE_RX, ///< Receiver is enabled and it is receiving frames. @@ -101,7 +101,7 @@ radio_state_t nrf_802154_core_state_get(void); * @brief Request transition to SLEEP state. * * @note This function shall be called from a critical section context. It shall not be interrupted - * by the RADIO event handler or RAAL notification. + * by the RADIO event handler or Radio Shceduler notification. * * @param[in] term_lvl Termination level of this request. Selects procedures to abort. * @@ -114,32 +114,35 @@ bool nrf_802154_core_sleep(nrf_802154_term_t term_lvl); * @brief Request transition to RECEIVE state. * * @note This function shall be called from a critical section context. It shall not be interrupted - * by the RADIO event handler or RAAL notification. + * by the RADIO event handler or Radio Scheduler notification. * * @param[in] term_lvl Termination level of this request. Selects procedures to abort. * @param[in] req_orig Module that originates this request. - * @param[in] notify_function Function called to notify status of this procedure instead of - * default notification. If NULL default notification is used. + * @param[in] notify_function Function called to notify status of this procedure. May be NULL. + * @param[in] notify_abort If abort notification should be triggered. * * @retval true Entering RECEIVE state succeeded. * @retval false Entering RECEIVE state failed (driver is performing other procedure). */ bool nrf_802154_core_receive(nrf_802154_term_t term_lvl, req_originator_t req_orig, - nrf_802154_notification_func_t notify_function); + nrf_802154_notification_func_t notify_function, + bool notify_abort); /** * @brief Request transition to TRANSMIT state. * * @note This function shall be called from a critical section context. It shall not be interrupted - * by the RADIO event handler or RAAL notification. + * by the RADIO event handler or Radio Scheduler notification. * * @param[in] term_lvl Termination level of this request. Selects procedures to abort. * @param[in] req_orig Module that originates this request. * @param[in] p_data Pointer to a frame to transmit. * @param[in] cca If the driver should perform CCA procedure before transmission. - * @param[in] notify_function Function called to notify status of this procedure instead of - * default notification. If NULL default notification is used. + * @param[in] immediate If true, the driver schedules transmission immediately or never; + * if false transmission may be postponed until tx preconditions are + * met. + * @param[in] notify_function Function called to notify status of this procedure. May be NULL. * * @retval true Entering TRANSMIT state succeeded. * @retval false Entering TRANSMIT state failed (driver is performing other procedure). @@ -148,13 +151,14 @@ bool nrf_802154_core_transmit(nrf_802154_term_t term_lvl, req_originator_t req_orig, const uint8_t * p_data, bool cca, + bool immediate, nrf_802154_notification_func_t notify_function); /** * @brief Request transition to ENERGY_DETECTION state. * * @note This function shall be called from a critical section context. It shall not be interrupted - * by the RADIO event handler or RAAL notification. + * by the RADIO event handler or Radio Scheduler notification. * * @note This function shall be called when the driver is in SLEEP or RECEIVE state. When Energy * detection procedure is finished the driver will transit to RECEIVE state. @@ -171,7 +175,7 @@ bool nrf_802154_core_energy_detection(nrf_802154_term_t term_lvl, uint32_t time_ * @brief Request transition to CCA state. * * @note This function shall be called from a critical section context. It shall not be interrupted - * by the RADIO event handler or RAAL notification. + * by the RADIO event handler or Radio Scheduler notification. * * @param[in] term_lvl Termination level of this request. Selects procedures to abort. * @@ -184,7 +188,7 @@ bool nrf_802154_core_cca(nrf_802154_term_t term_lvl); * @brief Request transition to CONTINUOUS_CARRIER state. * * @note This function shall be called from a critical section context. It shall not be interrupted - * by the RADIO event handler or RAAL notification. + * by the RADIO event handler or Radio Scheduler notification. * * @param[in] term_lvl Termination level of this request. Selects procedures to abort. * @@ -204,7 +208,7 @@ bool nrf_802154_core_continuous_carrier(nrf_802154_term_t term_lvl); * notification it changes internal state to make sure receiver is started if requested. * * @note This function shall be called from a critical section context. It shall not be interrupted - * by the RADIO event handler or RAAL notification. + * by the RADIO event handler or Radio Scheduler notification. * * @param[in] p_data Pointer to buffer that has been freed. */ diff --git a/third_party/NordicSemiconductor/drivers/radio/nrf_802154_critical_section.c b/third_party/NordicSemiconductor/drivers/radio/nrf_802154_critical_section.c index e0dc56a50..1b504fa9d 100644 --- a/third_party/NordicSemiconductor/drivers/radio/nrf_802154_critical_section.c +++ b/third_party/NordicSemiconductor/drivers/radio/nrf_802154_critical_section.c @@ -41,9 +41,9 @@ #include "nrf_802154_config.h" #include "nrf_802154_debug.h" +#include "nrf_802154_rsch.h" #include "hal/nrf_radio.h" -#include "platform/timer/nrf_802154_timer.h" -#include "raal/nrf_raal_api.h" +#include "platform/lp_timer/nrf_802154_lp_timer.h" #include @@ -51,9 +51,104 @@ #define NESTED_CRITICAL_SECTION_ALLOWED_PRIORITY_NONE (-1) +static volatile uint8_t m_critical_section_monitor; ///< Monitors each critical section enter operation static volatile uint8_t m_nested_critical_section_counter; ///< Counter of nested critical sections static volatile int8_t m_nested_critical_section_allowed_priority; ///< Indicator if nested critical sections are currently allowed +typedef enum +{ + RSCH_EVT_NONE, + RSCH_EVT_STARTED, + RSCH_EVT_ENDED, +} rsch_evt_t; + +static volatile uint8_t m_rsch_pending_evt; ///< Indicator of pending RSCH event. + + +/*************************************************************************************************** + * @section RSCH pending events management + **************************************************************************************************/ + +static void rsch_pending_evt_set(rsch_evt_t evt) +{ + rsch_evt_t curr_evt; + rsch_evt_t new_evt; + uint8_t evt_value; + + do + { + evt_value = __LDREXB(&m_rsch_pending_evt); + curr_evt = (rsch_evt_t)evt_value; + + switch (curr_evt) + { + case RSCH_EVT_NONE: + new_evt = evt; + break; + + case RSCH_EVT_ENDED: + assert(evt == RSCH_EVT_STARTED); + new_evt = RSCH_EVT_NONE; + break; + + case RSCH_EVT_STARTED: + assert(evt == RSCH_EVT_ENDED); + new_evt = RSCH_EVT_NONE; + break; + + default: + assert(false); + } + + evt_value = (uint8_t)new_evt; + } while (__STREXB(evt_value, &m_rsch_pending_evt)); +} + +static rsch_evt_t rsch_pending_evt_clear(void) +{ + rsch_evt_t evt; + uint8_t evt_value; + + do + { + evt_value = __LDREXB(&m_rsch_pending_evt); + evt = (rsch_evt_t)evt_value; + + evt_value = RSCH_EVT_NONE; + } while (__STREXB(evt_value, &m_rsch_pending_evt)); + + return evt; +} + +static bool rsch_pending_evt_is_none(void) +{ + return (rsch_evt_t)m_rsch_pending_evt == RSCH_EVT_NONE; +} + +static void rsch_evt_process(rsch_evt_t evt) +{ + switch (evt) + { + case RSCH_EVT_NONE: + break; + + case RSCH_EVT_STARTED: + nrf_802154_critical_section_rsch_prec_approved(); + break; + + case RSCH_EVT_ENDED: + nrf_802154_critical_section_rsch_prec_denied(); + break; + + default: + assert(false); + } +} + +/*************************************************************************************************** + * @section Critical sections management + **************************************************************************************************/ + /** @brief Enter critical section for RADIO peripheral * * @note RADIO peripheral registers (and NVIC) are modified only when timeslot is granted for the @@ -61,11 +156,9 @@ static volatile int8_t m_nested_critical_section_allowed_priority; ///< Indica */ static void radio_critical_section_enter(void) { - if (nrf_raal_timeslot_is_granted()) + if (nrf_802154_rsch_prec_is_approved(RSCH_PREC_RAAL)) { NVIC_DisableIRQ(RADIO_IRQn); - __DSB(); - __ISB(); } } @@ -76,7 +169,7 @@ static void radio_critical_section_enter(void) */ static void radio_critical_section_exit(void) { - if (nrf_raal_timeslot_is_granted()) + if (nrf_802154_rsch_prec_is_approved(RSCH_PREC_RAAL)) { NVIC_EnableIRQ(RADIO_IRQn); } @@ -107,33 +200,103 @@ static bool nested_critical_section_is_allowed_in_this_context(void) static bool critical_section_enter(bool forced) { - bool result = true; + bool result = false; uint8_t cnt; - nrf_802154_log(EVENT_TRACE_ENTER, FUNCTION_CRIT_SECT_ENTER); + if (forced || + (m_nested_critical_section_counter == 0) || + nested_critical_section_is_allowed_in_this_context()) + { + do + { + cnt = __LDREXB(&m_nested_critical_section_counter); + + assert(cnt < UINT8_MAX); + } + while (__STREXB(cnt + 1, &m_nested_critical_section_counter)); + + nrf_802154_lp_timer_critical_section_enter(); + radio_critical_section_enter(); + __DSB(); + __ISB(); + + m_critical_section_monitor++; + + result = true; + } + + return result; +} + +static void critical_section_exit(void) +{ + uint8_t cnt = m_nested_critical_section_counter; + rsch_evt_t rsch_evt = RSCH_EVT_NONE; + uint8_t monitor; + uint8_t atomic_cnt; + static bool exiting_crit_sect; + bool result; + + assert(cnt > 0); + + if (cnt == 1) + { + rsch_evt = rsch_pending_evt_clear(); + } do { - cnt = __LDREXB(&m_nested_critical_section_counter); + monitor = m_critical_section_monitor; - assert(cnt < UINT8_MAX); - - if (!forced && cnt > 0 && !nested_critical_section_is_allowed_in_this_context()) + // If critical section is not nested exit critical section + if (cnt == 1) { - __CLREX(); - result = false; - break; + assert(!exiting_crit_sect); + (void)exiting_crit_sect; + exiting_crit_sect = true; + + rsch_evt_process(rsch_evt); + radio_critical_section_exit(); + nrf_802154_lp_timer_critical_section_exit(); + + exiting_crit_sect = false; } - radio_critical_section_enter(); - nrf_raal_critical_section_enter(); - } - while (__STREXB(cnt + 1, &m_nested_critical_section_counter)); + do + { + atomic_cnt = __LDREXB(&m_nested_critical_section_counter); + assert(atomic_cnt == cnt); + } + while (__STREXB(atomic_cnt - 1, &m_nested_critical_section_counter)); - nrf_802154_log(EVENT_TRACE_EXIT, FUNCTION_CRIT_SECT_ENTER); - return result; + // If critical section is not nested verify if during exit procedure RSCH notified + // change of state or critical section was visited by higher priority IRQ meantime. + if (cnt == 1) + { + rsch_evt = rsch_pending_evt_clear(); + + // Check if critical section must be exited again. + if ((rsch_evt != RSCH_EVT_NONE) || (monitor != m_critical_section_monitor)) + { + result = critical_section_enter(false); + assert(result); + (void)result; + + continue; + } + else + { + break; + } + } + } + while (cnt == 1); } +/*************************************************************************************************** + * @section API functions + **************************************************************************************************/ + void nrf_802154_critical_section_init(void) { m_nested_critical_section_counter = 0; @@ -142,44 +305,35 @@ void nrf_802154_critical_section_init(void) bool nrf_802154_critical_section_enter(void) { - return critical_section_enter(false); + bool result; + + nrf_802154_log(EVENT_TRACE_ENTER, FUNCTION_CRIT_SECT_ENTER); + + result = critical_section_enter(false); + + nrf_802154_log(EVENT_TRACE_EXIT, FUNCTION_CRIT_SECT_ENTER); + + return result; } void nrf_802154_critical_section_forcefully_enter(void) { - bool critical_section_entered = critical_section_enter(true); + bool critical_section_entered; + + nrf_802154_log(EVENT_TRACE_ENTER, FUNCTION_CRIT_SECT_ENTER); + + critical_section_entered = critical_section_enter(true); assert(critical_section_entered); (void)critical_section_entered; + + nrf_802154_log(EVENT_TRACE_EXIT, FUNCTION_CRIT_SECT_ENTER); } void nrf_802154_critical_section_exit(void) { - uint8_t cnt; - static bool exiting_crit_sect; - nrf_802154_log(EVENT_TRACE_ENTER, FUNCTION_CRIT_SECT_EXIT); - do - { - cnt = __LDREXB(&m_nested_critical_section_counter); - - assert(cnt > 0); - - if (cnt == 1) - { - assert(!exiting_crit_sect); - (void)exiting_crit_sect; - exiting_crit_sect = true; - - // RAAL critical section shall be exited before RADIO IRQ handler is enabled. In other - // case RADIO IRQ handler may be called out of timeslot. - nrf_raal_critical_section_exit(); - radio_critical_section_exit(); - - exiting_crit_sect = false; - } - } - while (__STREXB(cnt - 1, &m_nested_critical_section_counter)); + critical_section_exit(); nrf_802154_log(EVENT_TRACE_EXIT, FUNCTION_CRIT_SECT_EXIT); } @@ -188,6 +342,7 @@ void nrf_802154_critical_section_nesting_allow(void) { assert(m_nested_critical_section_allowed_priority == NESTED_CRITICAL_SECTION_ALLOWED_PRIORITY_NONE); + assert(m_nested_critical_section_counter >= 1); m_nested_critical_section_allowed_priority = active_priority_convert( nrf_802154_critical_section_active_vector_priority_get()); @@ -196,10 +351,16 @@ void nrf_802154_critical_section_nesting_allow(void) void nrf_802154_critical_section_nesting_deny(void) { assert(m_nested_critical_section_allowed_priority >= 0); + assert(m_nested_critical_section_counter >= 1); m_nested_critical_section_allowed_priority = NESTED_CRITICAL_SECTION_ALLOWED_PRIORITY_NONE; } +bool nrf_802154_critical_section_is_nested(void) +{ + return m_nested_critical_section_counter > 1; +} + uint32_t nrf_802154_critical_section_active_vector_priority_get(void) { uint32_t active_vector_id = (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) >> SCB_ICSR_VECTACTIVE_Pos; @@ -220,3 +381,45 @@ uint32_t nrf_802154_critical_section_active_vector_priority_get(void) return active_priority; } +/*************************************************************************************************** + * @section RSCH callbacks + **************************************************************************************************/ + +void nrf_802154_rsch_prec_approved(void) +{ + bool crit_sect_success = critical_section_enter(false); + + if (crit_sect_success && rsch_pending_evt_is_none()) + { + nrf_802154_critical_section_rsch_prec_approved(); + } + else + { + rsch_pending_evt_set(RSCH_EVT_STARTED); + } + + if (crit_sect_success) + { + critical_section_exit(); + } +} + +void nrf_802154_rsch_prec_denied(void) +{ + bool crit_sect_success = critical_section_enter(false); + + if (crit_sect_success && rsch_pending_evt_is_none()) + { + nrf_802154_critical_section_rsch_prec_denied(); + } + else + { + rsch_pending_evt_set(RSCH_EVT_ENDED); + } + + if (crit_sect_success) + { + critical_section_exit(); + } +} + diff --git a/third_party/NordicSemiconductor/drivers/radio/nrf_802154_critical_section.h b/third_party/NordicSemiconductor/drivers/radio/nrf_802154_critical_section.h index e37c98abf..9a94eaab6 100644 --- a/third_party/NordicSemiconductor/drivers/radio/nrf_802154_critical_section.h +++ b/third_party/NordicSemiconductor/drivers/radio/nrf_802154_critical_section.h @@ -89,6 +89,14 @@ void nrf_802154_critical_section_nesting_allow(void); */ void nrf_802154_critical_section_nesting_deny(void); +/** + * @brief Check if critical section is nested. + * + * @retval true Critical section is nested. + * @retval false Critical section is not nested. + */ +bool nrf_802154_critical_section_is_nested(void); + /** * @brief Get current IRQ priority. * @@ -96,6 +104,25 @@ void nrf_802154_critical_section_nesting_deny(void); */ uint32_t nrf_802154_critical_section_active_vector_priority_get(void); + +/** + * @brief The critical section module calls this function to notify the core that all RSCH + * preconditions are met. + * + * @note This function is called from critical section context and does not preempt other critical + * sections. + */ +extern void nrf_802154_critical_section_rsch_prec_approved(void); + +/** + * @brief The critical section module calls this function to notify the core that any of RSCH + * preconditions is not met anymore. + * + * @note This function is called from critical section context and does not preempt other critical + * sections. + */ +extern void nrf_802154_critical_section_rsch_prec_denied(void); + /** *@} **/ diff --git a/third_party/NordicSemiconductor/drivers/radio/nrf_802154_debug.h b/third_party/NordicSemiconductor/drivers/radio/nrf_802154_debug.h index ebeea04ce..60e93c041 100644 --- a/third_party/NordicSemiconductor/drivers/radio/nrf_802154_debug.h +++ b/third_party/NordicSemiconductor/drivers/radio/nrf_802154_debug.h @@ -59,6 +59,7 @@ extern "C" { #define FUNCTION_CCA 0x0006UL #define FUNCTION_CONTINUOUS_CARRIER 0x0007UL #define FUNCTION_CSMACA 0x0008UL +#define FUNCTION_TRANSMIT_AT 0x0009UL #define FUNCTION_IRQ_HANDLER 0x0100UL #define FUNCTION_EVENT_FRAMESTART 0x0101UL @@ -96,6 +97,30 @@ extern "C" { #define FUNCTION_RAAL_EVT_SESSION_IDLE 0x0409UL #define FUNCTION_RAAL_EVT_HFCLK_READY 0x040AUL +#define FUNCTION_RSCH_CONTINUOUS_ENTER 0x040BUL +#define FUNCTION_RSCH_CONTINUOUS_EXIT 0x040CUL +#define FUNCTION_RSCH_CRITICAL_SECTION_ENTER 0x040DUL +#define FUNCTION_RSCH_CRITICAL_SECTION_EXIT 0x040EUL +#define FUNCTION_RSCH_TIMESLOT_STARTED 0x040FUL +#define FUNCTION_RSCH_TIMESLOT_ENDED 0x0410UL +#define FUNCTION_RSCH_PEND_GRANTED 0x0411UL +#define FUNCTION_RSCH_PEND_REVOKED 0x0412UL +#define FUNCTION_RSCH_NOTIFY_GRANTED 0x0413UL +#define FUNCTION_RSCH_NOTIFY_REVOKED 0x0414UL +#define FUNCTION_RSCH_NOTIFY_IF_PENDING 0x0415UL +#define FUNCTION_RSCH_DELAYED_TIMESLOT_REQ 0x0416UL +#define FUNCTION_RSCH_TIMER_DELAYED_PREC 0x0417UL +#define FUNCTION_RSCH_TIMER_DELAYED_START 0x0418UL + +#define FUNCTION_CSMA_ABORT 0x0500UL +#define FUNCTION_CSMA_TX_FAILED 0x0501UL +#define FUNCTION_CSMA_TX_STARTED 0x0502UL +#define FUNCTION_CSMA_CHANNEL_BUSY 0x0503UL +#define FUNCTION_CSMA_FRAME_TRANSMIT 0x0504UL + +#define FUNCTION_TSCH_ADD 0x0600UL +#define FUNCTION_TSCH_FIRED 0x0601UL + #define PIN_DBG_RADIO_EVT_END 11 #define PIN_DBG_RADIO_EVT_DISABLED 12 #define PIN_DBG_RADIO_EVT_READY 13 diff --git a/third_party/NordicSemiconductor/drivers/radio/nrf_802154_notification_direct.c b/third_party/NordicSemiconductor/drivers/radio/nrf_802154_notification_direct.c index db424abaf..1f92d6a26 100644 --- a/third_party/NordicSemiconductor/drivers/radio/nrf_802154_notification_direct.c +++ b/third_party/NordicSemiconductor/drivers/radio/nrf_802154_notification_direct.c @@ -73,7 +73,7 @@ void nrf_802154_notify_transmitted(const uint8_t * p_frame, nrf_802154_transmitted_raw(p_frame, p_ack, power, lqi); #else // NRF_802154_USE_RAW_API nrf_802154_transmitted(p_frame + RAW_PAYLOAD_OFFSET, - p_ack + RAW_PAYLOAD_OFFSET, + p_ack == NULL ? NULL : p_ack + RAW_PAYLOAD_OFFSET, p_ack[RAW_LENGTH_OFFSET], power, lqi); diff --git a/third_party/NordicSemiconductor/drivers/radio/nrf_802154_notification_swi.c b/third_party/NordicSemiconductor/drivers/radio/nrf_802154_notification_swi.c index aed226628..1719a5930 100644 --- a/third_party/NordicSemiconductor/drivers/radio/nrf_802154_notification_swi.c +++ b/third_party/NordicSemiconductor/drivers/radio/nrf_802154_notification_swi.c @@ -41,7 +41,6 @@ #include "nrf_802154.h" #include "nrf_802154_swi.h" -#include "raal/nrf_raal_api.h" void nrf_802154_notification_init(void) { diff --git a/third_party/NordicSemiconductor/drivers/radio/nrf_802154_pib.c b/third_party/NordicSemiconductor/drivers/radio/nrf_802154_pib.c index 92c19003c..5592e51e0 100644 --- a/third_party/NordicSemiconductor/drivers/radio/nrf_802154_pib.c +++ b/third_party/NordicSemiconductor/drivers/radio/nrf_802154_pib.c @@ -53,6 +53,7 @@ typedef struct nrf_802154_cca_cfg_t cca; ///< CCA mode and thresholds. bool promiscuous :1; ///< Indicating if radio is in promiscuous mode. bool auto_ack :1; ///< Indicating if auto ACK procedure is enabled. + bool pan_coord :1; ///< Indicating if radio is configured as the PAN coordinator. uint8_t channel :5; ///< Channel on which the node receives messages. } nrf_802154_pib_data_t; @@ -62,6 +63,7 @@ void nrf_802154_pib_init(void) { m_data.promiscuous = false; m_data.auto_ack = true; + m_data.pan_coord = false; m_data.channel = 11; memset(m_data.pan_id, 0xff, sizeof(m_data.pan_id)); @@ -95,6 +97,16 @@ void nrf_802154_pib_auto_ack_set(bool enabled) m_data.auto_ack = enabled; } +bool nrf_802154_pib_pan_coord_get(void) +{ + return m_data.pan_coord; +} + +void nrf_802154_pib_pan_coord_set(bool enabled) +{ + m_data.pan_coord = enabled; +} + uint8_t nrf_802154_pib_channel_get(void) { return m_data.channel; diff --git a/third_party/NordicSemiconductor/drivers/radio/nrf_802154_pib.h b/third_party/NordicSemiconductor/drivers/radio/nrf_802154_pib.h index 08ec8638c..95bdc0544 100644 --- a/third_party/NordicSemiconductor/drivers/radio/nrf_802154_pib.h +++ b/third_party/NordicSemiconductor/drivers/radio/nrf_802154_pib.h @@ -80,6 +80,21 @@ bool nrf_802154_pib_auto_ack_get(void); */ void nrf_802154_pib_auto_ack_set(bool enabled); +/** + * @brief Check if radio is configured as the PAN coordinator. + * + * @retval true If radio is configured as the PAN coordinator. + * @retval false If radio is not configured as the PAN coordinator. + */ +bool nrf_802154_pib_pan_coord_get(void); + +/** + * @brief Notify driver that radio is configured as the PAN coordinator. + * + * @param[in] enabled If radio is configured as the PAN coordinator. + */ +void nrf_802154_pib_pan_coord_set(bool enabled); + /** * @brief Get currently used channel. diff --git a/third_party/NordicSemiconductor/drivers/radio/nrf_802154_priority_drop_direct.c b/third_party/NordicSemiconductor/drivers/radio/nrf_802154_priority_drop_direct.c index 0930e39de..486312fd1 100644 --- a/third_party/NordicSemiconductor/drivers/radio/nrf_802154_priority_drop_direct.c +++ b/third_party/NordicSemiconductor/drivers/radio/nrf_802154_priority_drop_direct.c @@ -37,7 +37,7 @@ #include "nrf_802154_priority_drop.h" -#include "raal/nrf_raal_api.h" +#include "nrf_802154_rsch.h" void nrf_802154_priority_drop_init(void) { @@ -46,7 +46,7 @@ void nrf_802154_priority_drop_init(void) void nrf_802154_priority_drop_timeslot_exit(void) { - nrf_raal_continuous_mode_exit(); + nrf_802154_rsch_continuous_mode_exit(); } void nrf_802154_priority_drop_timeslot_exit_terminate(void) diff --git a/third_party/NordicSemiconductor/drivers/radio/nrf_802154_procedures_duration.h b/third_party/NordicSemiconductor/drivers/radio/nrf_802154_procedures_duration.h index 7e8889d10..7e360c5b9 100644 --- a/third_party/NordicSemiconductor/drivers/radio/nrf_802154_procedures_duration.h +++ b/third_party/NordicSemiconductor/drivers/radio/nrf_802154_procedures_duration.h @@ -46,6 +46,7 @@ #define RX_RAMP_UP_TIME 40 // us #define RX_RAMP_DOWN_TIME 0 // us #define MAX_RAMP_DOWN_TIME 6 // us +#define RX_TX_TURNAROUND_TIME 20 // us #define A_CCA_DURATION 8 // sym #define A_TURNAROUND_TIME 12 // sym @@ -59,8 +60,10 @@ (NUM_OCTETS_IN_ACK * PHY_SYMBOLS_PER_OCTET)) __STATIC_INLINE uint16_t nrf_802154_tx_duration_get(uint8_t psdu_length, - bool cca, - bool ack_requested); + bool cca, + bool ack_requested); + +__STATIC_INLINE uint16_t nrf_802154_cca_before_tx_duration_get(void); __STATIC_INLINE uint16_t nrf_802154_rx_duration_get(uint8_t psdu_length, bool ack_requested); @@ -95,6 +98,14 @@ __STATIC_INLINE uint16_t nrf_802154_tx_duration_get(uint8_t psdu_length, return result; } +__STATIC_INLINE uint16_t nrf_802154_cca_before_tx_duration_get(void) +{ + // CCA + turnaround time + uint16_t result = (A_CCA_DURATION * PHY_US_PER_SYMBOL) + RX_TX_TURNAROUND_TIME; + + return result; +} + __STATIC_INLINE uint16_t nrf_802154_rx_duration_get(uint8_t psdu_length, bool ack_requested) { // SHR + PHR + PSDU diff --git a/third_party/NordicSemiconductor/drivers/radio/nrf_802154_request.h b/third_party/NordicSemiconductor/drivers/radio/nrf_802154_request.h index 233ec6ed4..ace2b7718 100644 --- a/third_party/NordicSemiconductor/drivers/radio/nrf_802154_request.h +++ b/third_party/NordicSemiconductor/drivers/radio/nrf_802154_request.h @@ -69,15 +69,16 @@ bool nrf_802154_request_sleep(nrf_802154_term_t term_lvl); * * @param[in] term_lvl Termination level of this request. Selects procedures to abort. * @param[in] req_orig Module that originates this request. - * @param[in] notify_function Function called to notify status of this procedure instead of - * default notification. If NULL default notification is used. + * @param[in] notify_function Function called to notify status of this procedure. May be NULL. + * @param[in] notify_abort If abort notification should be triggered automatically. * * @retval true The driver will enter receive state. * @retval false The driver cannot enter receive state due to ongoing operation. */ bool nrf_802154_request_receive(nrf_802154_term_t term_lvl, req_originator_t req_orig, - nrf_802154_notification_func_t notify_function); + nrf_802154_notification_func_t notify_function, + bool notify_abort); /** * @brief Request entering transmit state. @@ -86,8 +87,10 @@ bool nrf_802154_request_receive(nrf_802154_term_t term_lvl, * @param[in] req_orig Module that originates this request. * @param[in] p_data Pointer to the frame to transmit. * @param[in] cca If the driver should perform CCA procedure before transmission. - * @param[in] notify_function Function called to notify status of this procedure instead of - * default notification. If NULL default notification is used. + * @param[in] immediate If true, the driver schedules transmission immediately or never; + * if false transmission may be postponed until tx preconditions are + * met. + * @param[in] notify_function Function called to notify status of this procedure. May be NULL. * * @retval true The driver will enter transmit state. * @retval false The driver cannot enter transmit state due to ongoing operation. @@ -96,6 +99,7 @@ bool nrf_802154_request_transmit(nrf_802154_term_t term_lvl, req_originator_t req_orig, const uint8_t * p_data, bool cca, + bool immediate, nrf_802154_notification_func_t notify_function); /** diff --git a/third_party/NordicSemiconductor/drivers/radio/nrf_802154_request_direct.c b/third_party/NordicSemiconductor/drivers/radio/nrf_802154_request_direct.c index 34a580298..4ee9e8f01 100644 --- a/third_party/NordicSemiconductor/drivers/radio/nrf_802154_request_direct.c +++ b/third_party/NordicSemiconductor/drivers/radio/nrf_802154_request_direct.c @@ -40,28 +40,15 @@ #include #include "nrf_802154_core.h" -#include "nrf_802154_critical_section.h" #include "hal/nrf_radio.h" -#define REQUEST_FUNCTION_WITH_FAIL_INSTR(func_core, fail_instr, ...) \ +#define REQUEST_FUNCTION(func_core, ...) \ bool result; \ \ - if (nrf_802154_critical_section_enter()) \ - { \ - result = func_core(__VA_ARGS__); \ - nrf_802154_critical_section_exit(); \ - } \ - else \ - { \ - fail_instr \ - result = false; \ - } \ + result = func_core(__VA_ARGS__); \ \ return result; -#define REQUEST_FUNCTION(func_core, ...) \ - REQUEST_FUNCTION_WITH_FAIL_INSTR(func_core, , __VA_ARGS__) - void nrf_802154_request_init(void) { @@ -75,28 +62,20 @@ bool nrf_802154_request_sleep(nrf_802154_term_t term_lvl) bool nrf_802154_request_receive(nrf_802154_term_t term_lvl, req_originator_t req_orig, - nrf_802154_notification_func_t notify_function) + nrf_802154_notification_func_t notify_function, + bool notify_abort) { - REQUEST_FUNCTION_WITH_FAIL_INSTR(nrf_802154_core_receive, - notify_function(false); , - term_lvl, - req_orig, - notify_function) + REQUEST_FUNCTION(nrf_802154_core_receive, term_lvl, req_orig, notify_function, notify_abort) } bool nrf_802154_request_transmit(nrf_802154_term_t term_lvl, req_originator_t req_orig, const uint8_t * p_data, bool cca, + bool immediate, nrf_802154_notification_func_t notify_function) { - REQUEST_FUNCTION_WITH_FAIL_INSTR(nrf_802154_core_transmit, - notify_function(false); , - term_lvl, - req_orig, - p_data, - cca, - notify_function) + REQUEST_FUNCTION(nrf_802154_core_transmit, term_lvl, req_orig, p_data, cca, immediate, notify_function) } bool nrf_802154_request_energy_detection(nrf_802154_term_t term_lvl, uint32_t time_us) diff --git a/third_party/NordicSemiconductor/drivers/radio/nrf_802154_request_swi.c b/third_party/NordicSemiconductor/drivers/radio/nrf_802154_request_swi.c index 0f5e352a0..f16754b68 100644 --- a/third_party/NordicSemiconductor/drivers/radio/nrf_802154_request_swi.c +++ b/third_party/NordicSemiconductor/drivers/radio/nrf_802154_request_swi.c @@ -50,21 +50,12 @@ #include -#define REQUEST_FUNCTION_WITH_FAIL_INSTR(func_core, func_swi, fail_instr, ...) \ +#define REQUEST_FUNCTION(func_core, func_swi, ...) \ bool result = false; \ \ if (active_vector_priority_is_high()) \ { \ - if (nrf_802154_critical_section_enter()) \ - { \ - result = func_core(__VA_ARGS__); \ - nrf_802154_critical_section_exit(); \ - } \ - else \ - { \ - fail_instr \ - result = false; \ - } \ + result = func_core(__VA_ARGS__); \ } \ else \ { \ @@ -73,23 +64,12 @@ \ return result; -#define REQUEST_FUNCTION(func_core, func_swi, ...) \ - REQUEST_FUNCTION_WITH_FAIL_INSTR(func_core, func_swi, , __VA_ARGS__) - #define REQUEST_FUNCTION_NO_ARGS(func_core, func_swi) \ bool result = false; \ \ if (active_vector_priority_is_high()) \ { \ - if (nrf_802154_critical_section_enter()) \ - { \ - result = func_core(); \ - nrf_802154_critical_section_exit(); \ - } \ - else \ - { \ - result = false; \ - } \ + result = func_core(); \ } \ else \ { \ @@ -121,30 +101,32 @@ bool nrf_802154_request_sleep(nrf_802154_term_t term_lvl) bool nrf_802154_request_receive(nrf_802154_term_t term_lvl, req_originator_t req_orig, - nrf_802154_notification_func_t notify_function) + nrf_802154_notification_func_t notify_function, + bool notify_abort) { - REQUEST_FUNCTION_WITH_FAIL_INSTR(nrf_802154_core_receive, - nrf_802154_swi_receive, - notify_function(false); , - term_lvl, - req_orig, - notify_function) + REQUEST_FUNCTION(nrf_802154_core_receive, + nrf_802154_swi_receive, + term_lvl, + req_orig, + notify_function, + notify_abort) } bool nrf_802154_request_transmit(nrf_802154_term_t term_lvl, req_originator_t req_orig, const uint8_t * p_data, bool cca, + bool immediate, nrf_802154_notification_func_t notify_function) { - REQUEST_FUNCTION_WITH_FAIL_INSTR(nrf_802154_core_transmit, - nrf_802154_swi_transmit, - notify_function(false); , - term_lvl, - req_orig, - p_data, - cca, - notify_function) + REQUEST_FUNCTION(nrf_802154_core_transmit, + nrf_802154_swi_transmit, + term_lvl, + req_orig, + p_data, + cca, + immediate, + notify_function) } bool nrf_802154_request_energy_detection(nrf_802154_term_t term_lvl, diff --git a/third_party/NordicSemiconductor/drivers/radio/nrf_802154_rsch.c b/third_party/NordicSemiconductor/drivers/radio/nrf_802154_rsch.c new file mode 100644 index 000000000..f65594ac8 --- /dev/null +++ b/third_party/NordicSemiconductor/drivers/radio/nrf_802154_rsch.c @@ -0,0 +1,466 @@ +#include "nrf_802154_rsch.h" + +#include +#include +#include + +#include "nrf_802154_debug.h" +#include "platform/clock/nrf_802154_clock.h" +#include "raal/nrf_raal_api.h" +#include "timer_scheduler/nrf_802154_timer_sched.h" + +#define PREC_RAMP_UP_TIME 300 ///< Ramp-up time of preconditions [us]. 300 is worst case for HFclock + +typedef enum +{ + RSCH_PREC_STATE_IDLE, + RSCH_PREC_STATE_REQUESTED, + RSCH_PREC_STATE_APPROVED, +} rsch_prec_state_t; + +static volatile uint8_t m_mutex; ///< Mutex for notyfying core. +static volatile uint8_t m_mutex_monitor; ///< Mutex monitor, incremented every failed mutex lock. +static volatile bool m_last_notified_approved; ///< Last reported state was approved. +static volatile rsch_prec_state_t m_prec_states[RSCH_PREC_CNT]; ///< State of all preconditions. +static bool m_in_cont_mode; ///< If RSCH operates in continuous mode. + +static bool m_delayed_timeslot_is_scheduled; ///< If delayed timeslot is scheduled at the moment. +static uint32_t m_delayed_timeslot_t0; ///< Time base of the delayed timeslot trigger time. +static uint32_t m_delayed_timeslot_dt; ///< Time delta of the delayed timeslot trigger time. +static nrf_802154_timer_t m_timer; ///< Timer used to trigger delayed timeslot. + +/** @brief Non-blocking mutex for notifying core. + * + * @retval true Mutex was acquired. + * @retval false Mutex could not be acquired. + */ +static inline bool mutex_trylock(void) +{ + do + { + uint8_t mutex_value = __LDREXB(&m_mutex); + + if (mutex_value) + { + __CLREX(); + + m_mutex_monitor++; + return false; + } + } while (__STREXB(1, &m_mutex)); + + __DMB(); + + return true; +} + +/** @brief Release mutex. */ +static inline void mutex_unlock(void) +{ + __DMB(); + m_mutex = 0; +} + +/** @brief Check if any precondition should be requested at the moment for delayed timeslot. + * + * To meet delayed timeslot timing requirements there is a time window in which radio + * preconditions should be requested. This function is used to prevent releasing preconditions + * in this time window. + * + * @retval true A precondition should be requested at the moment for delayed timeslot feature. + * @retval false None of preconditions should be requested at the moment for delayed timeslot. + */ +static bool any_prec_should_be_requested_for_delayed_timeslot(void) +{ + uint32_t now = nrf_802154_timer_sched_time_get(); + uint32_t t0 = m_delayed_timeslot_t0; + uint32_t dt = m_delayed_timeslot_dt - PREC_RAMP_UP_TIME - + nrf_802154_timer_sched_granularity_get(); + + return (m_delayed_timeslot_is_scheduled && + !nrf_802154_timer_sched_time_is_in_future(now, t0, dt)); +} + +/** @brief Set RSCH_PREC_STATE_APPROVED state on given precondition @p prec only if + * its current state is other than RSCH_PREC_STATE_IDLE. + * + * @param[in] prec Precondition which state will be changed. + */ +static inline void prec_approve(rsch_prec_t prec) +{ + do + { + rsch_prec_state_t old_state = (rsch_prec_state_t) __LDREXB((uint8_t*)&m_prec_states[prec]); + + assert(old_state != RSCH_PREC_STATE_APPROVED); + + if (old_state == RSCH_PREC_STATE_IDLE) + { + __CLREX(); + return; + } + } while (__STREXB((uint8_t)RSCH_PREC_STATE_APPROVED, (uint8_t*)&m_prec_states[prec])); +} + +/** @brief Set RSCH_PREC_STATE_REQUESTED state on given precondition @p prec only if + * its current state is RSCH_PREC_STATE_APPROVED. + * + * @param[in] prec Precondition which state will be changed. + */ +static inline void prec_deny(rsch_prec_t prec) +{ + do + { + rsch_prec_state_t old_state = (rsch_prec_state_t) __LDREXB((uint8_t*)&m_prec_states[prec]); + + assert(old_state != RSCH_PREC_STATE_REQUESTED); + + if (old_state != RSCH_PREC_STATE_APPROVED) + { + __CLREX(); + return; + } + } while (__STREXB((uint8_t)RSCH_PREC_STATE_REQUESTED, (uint8_t*)&m_prec_states[prec])); +} + +/** @brief Set RSCH_PREC_STATE_REQUESTED state on given precondition @p prec only if + * its current state is RSCH_PREC_STATE_IDLE. + * + * @param[in] prec Precondition which state will be changed. + * + * @retval true Precondition changed state to requested. + * @retval false Precondition cannot change state to requested due to invalid state. + */ +static inline bool prec_request(rsch_prec_t prec) +{ + do + { + rsch_prec_state_t old_state = (rsch_prec_state_t) __LDREXB((uint8_t*)&m_prec_states[prec]); + + if (old_state != RSCH_PREC_STATE_IDLE) + { + __CLREX(); + return false; + } + } while (__STREXB((uint8_t)RSCH_PREC_STATE_REQUESTED, (uint8_t*)&m_prec_states[prec])); + + return true; +} + +/** @brief Set RSCH_PREC_STATE_IDLE state on given precondition @p prec. + * + * @param[in] prec Precondition which state will be changed. + */ +static inline void prec_release(rsch_prec_t prec) +{ + assert(m_prec_states[prec] != RSCH_PREC_STATE_IDLE); + m_prec_states[prec] = RSCH_PREC_STATE_IDLE; + __CLREX(); +} + +/** @brief Request all preconditions. + */ +static inline void all_prec_request(void) +{ + if (prec_request(RSCH_PREC_HFCLK)) + { + nrf_802154_clock_hfclk_start(); + } + + if (prec_request(RSCH_PREC_RAAL)) + { + nrf_raal_continuous_mode_enter(); + } +} + +/** @brief Release all preconditions if not needed. + * + * If RSCH is not in continuous mode and delayed timeslot is not expected all preconditions are + * released. + */ +static inline void all_prec_release(void) +{ + if (!m_in_cont_mode && !any_prec_should_be_requested_for_delayed_timeslot()) + { + prec_release(RSCH_PREC_HFCLK); + nrf_802154_clock_hfclk_stop(); + + prec_release(RSCH_PREC_RAAL); + nrf_raal_continuous_mode_exit(); + } +} + +/** @brief Check if all preconditions are met. + * + * @retval true All preconditions are met. + * @retval false At least one precondition is not met. + */ +static inline bool all_prec_are_approved(void) +{ + for (uint32_t i = 0; i < RSCH_PREC_CNT; i++) + { + if (m_prec_states[i] != RSCH_PREC_STATE_APPROVED) + { + return false; + } + } + + return true; +} + +/** @brief Check if all preconditions are requested or met. + * + * @retval true All preconditions are requested or met. + * @retval false At least one precondition is idle. + */ +static inline bool all_prec_are_requested(void) +{ + for (uint32_t i = 0; i < RSCH_PREC_CNT; i++) + { + if (m_prec_states[i] == RSCH_PREC_STATE_IDLE) + { + return false; + } + } + + return true; +} + + +/** @brief Notify core if preconditions are approved or denied if current state differs from last reported. + */ +static inline void notify_core(void) +{ + bool notify_approved; + uint8_t temp_mon; + + do + { + if (!mutex_trylock()) + { + return; + } + + /* It is possible that preemption is not detected (m_mutex_monitor is read after acquiring mutex). + * It is not a problem because we will call proper handler function requested by preempting context. + * Avoiding this race would generate one additional iteration without any effect. + */ + temp_mon = m_mutex_monitor; + notify_approved = all_prec_are_approved(); + + if (m_in_cont_mode && (m_last_notified_approved != notify_approved)) + { + m_last_notified_approved = notify_approved; + + if (notify_approved) + { + nrf_802154_rsch_prec_approved(); + } + else + { + nrf_802154_rsch_prec_denied(); + } + } + + mutex_unlock(); + } while(temp_mon != m_mutex_monitor); +} + +/** Timer callback used to trigger delayed timeslot. + * + * @param[in] p_context Unused parameter. + */ +static void delayed_timeslot_start(void * p_context) +{ + (void)p_context; + + nrf_802154_log(EVENT_TRACE_ENTER, FUNCTION_RSCH_TIMER_DELAYED_START); + + m_delayed_timeslot_is_scheduled = false; + + if (all_prec_are_approved()) + { + nrf_802154_rsch_delayed_timeslot_started(); + } + else + { + nrf_802154_rsch_delayed_timeslot_failed(); + } + + nrf_802154_log(EVENT_TRACE_EXIT, FUNCTION_RSCH_TIMER_DELAYED_START); +} + +/** Timer callback used to request preconditions for delayed timeslot. + * + * @param[in] p_context Unused parameter. + */ +static void delayed_timeslot_prec_request(void * p_context) +{ + (void)p_context; + + nrf_802154_log(EVENT_TRACE_ENTER, FUNCTION_RSCH_TIMER_DELAYED_PREC); + + all_prec_request(); + + m_timer.t0 = m_delayed_timeslot_t0; + m_timer.dt = m_delayed_timeslot_dt; + m_timer.callback = delayed_timeslot_start; + m_timer.p_context = NULL; + + nrf_802154_timer_sched_add(&m_timer, true); + + nrf_802154_log(EVENT_TRACE_EXIT, FUNCTION_RSCH_TIMER_DELAYED_PREC); +} + +/*************************************************************************************************** + * Public API + **************************************************************************************************/ + +void nrf_802154_rsch_init(void) +{ + nrf_raal_init(); + + m_mutex = 0; + m_last_notified_approved = false; + m_in_cont_mode = false; + m_delayed_timeslot_is_scheduled = false; + + for (uint32_t i = 0; i < RSCH_PREC_CNT; i++) + { + m_prec_states[i] = RSCH_PREC_STATE_IDLE; + } +} + +void nrf_802154_rsch_uninit(void) +{ + nrf_802154_timer_sched_remove(&m_timer); + + nrf_raal_uninit(); +} + +void nrf_802154_rsch_continuous_mode_enter(void) +{ + nrf_802154_log(EVENT_TRACE_ENTER, FUNCTION_RSCH_CONTINUOUS_ENTER); + + m_in_cont_mode = true; + __DMB(); + + all_prec_request(); + notify_core(); + + nrf_802154_log(EVENT_TRACE_EXIT, FUNCTION_RSCH_CONTINUOUS_ENTER); +} + +void nrf_802154_rsch_continuous_mode_exit(void) +{ + nrf_802154_log(EVENT_TRACE_ENTER, FUNCTION_RSCH_CONTINUOUS_EXIT); + + __DMB(); + m_in_cont_mode = false; + + all_prec_release(); + notify_core(); + m_last_notified_approved = false; + + nrf_802154_log(EVENT_TRACE_EXIT, FUNCTION_RSCH_CONTINUOUS_EXIT); +} + +bool nrf_802154_rsch_prec_is_approved(rsch_prec_t prec) +{ + assert(prec < RSCH_PREC_CNT); + return m_prec_states[prec] == RSCH_PREC_STATE_APPROVED; +} + +bool nrf_802154_rsch_timeslot_request(uint32_t length_us) +{ + return nrf_raal_timeslot_request(length_us); +} + +bool nrf_802154_rsch_delayed_timeslot_request(uint32_t t0, uint32_t dt, uint32_t length) +{ + (void)length; + + nrf_802154_log(EVENT_TRACE_ENTER, FUNCTION_RSCH_DELAYED_TIMESLOT_REQ); + + uint32_t now = nrf_802154_timer_sched_time_get(); + uint32_t req_dt = dt - PREC_RAMP_UP_TIME; + bool result; + + assert(!nrf_802154_timer_sched_is_running(&m_timer)); + assert(!m_delayed_timeslot_is_scheduled); + + if (nrf_802154_timer_sched_time_is_in_future(now, t0, req_dt)) + { + m_delayed_timeslot_is_scheduled = true; + m_delayed_timeslot_t0 = t0; + m_delayed_timeslot_dt = dt; + + m_timer.t0 = t0; + m_timer.dt = req_dt; + m_timer.callback = delayed_timeslot_prec_request; + m_timer.p_context = NULL; + + nrf_802154_timer_sched_add(&m_timer, false); + + result = true; + } + else if (all_prec_are_requested() && nrf_802154_timer_sched_time_is_in_future(now, t0, dt)) + { + m_delayed_timeslot_is_scheduled = true; + m_delayed_timeslot_t0 = t0; + m_delayed_timeslot_dt = dt; + + m_timer.t0 = t0; + m_timer.dt = dt; + m_timer.callback = delayed_timeslot_start; + m_timer.p_context = NULL; + + nrf_802154_timer_sched_add(&m_timer, true); + + result = true; + } + else + { + result = false; + } + + nrf_802154_log(EVENT_TRACE_EXIT, FUNCTION_RSCH_DELAYED_TIMESLOT_REQ); + + return result; +} + +uint32_t nrf_802154_rsch_timeslot_us_left_get(void) +{ + return nrf_raal_timeslot_us_left_get(); +} + +// External handlers + +void nrf_raal_timeslot_started(void) +{ + nrf_802154_log(EVENT_TRACE_ENTER, FUNCTION_RSCH_TIMESLOT_STARTED); + + prec_approve(RSCH_PREC_RAAL); + + notify_core(); + + nrf_802154_log(EVENT_TRACE_EXIT, FUNCTION_RSCH_TIMESLOT_STARTED); +} + +void nrf_raal_timeslot_ended(void) +{ + nrf_802154_log(EVENT_TRACE_ENTER, FUNCTION_RSCH_TIMESLOT_ENDED); + + prec_deny(RSCH_PREC_RAAL); + + notify_core(); + + nrf_802154_log(EVENT_TRACE_EXIT, FUNCTION_RSCH_TIMESLOT_ENDED); +} + +void nrf_802154_clock_hfclk_ready(void) +{ + prec_approve(RSCH_PREC_HFCLK); + + notify_core(); +} diff --git a/third_party/NordicSemiconductor/drivers/radio/nrf_802154_rsch.h b/third_party/NordicSemiconductor/drivers/radio/nrf_802154_rsch.h new file mode 100644 index 000000000..3c9c0f109 --- /dev/null +++ b/third_party/NordicSemiconductor/drivers/radio/nrf_802154_rsch.h @@ -0,0 +1,215 @@ +/* Copyright (c) 2018, Nordic Semiconductor ASA + * 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 Nordic Semiconductor ASA 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. + * + */ + +/** + * @brief This module defines Radio Scheduler interface. + * + */ + +#ifndef NRF_802154_RSCH_H_ +#define NRF_802154_RSCH_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup nrf_rsch Radio Scheduler + * @{ + * @ingroup nrf_802154 + * @brief Radio Scheduler interface. + * + * Radio Scheduler is responsible to schedule in time radio activities and preconditions. It is + * expected that the Radio Scheduler module manages timings to meet requirements requested from the + * core module. + * + * Examples of radio activity preconditions are: High-Frequency Clock running, radio arbiter (RAAL) + * granted access to the RADIO peripheral. + */ + +/** + * @brief List of preconditions that have to be met before any radio activity. + */ +typedef enum +{ + RSCH_PREC_HFCLK, + RSCH_PREC_RAAL, + RSCH_PREC_CNT, +} rsch_prec_t; + +/** + * @brief Initialize Radio Scheduler. + * + * @note This function shall be called once, before any other function from this module. + * + * Initialize Radio Scheduler. + * + * @note Radio Scheduler starts in inactive mode after initialization. In order to start radio activity + * @ref nrf_802154_rsch_continuous_mode_enter should be called. + * + */ +void nrf_802154_rsch_init(void); + +/** + * @brief Uninitialize Radio Scheduler. + * + */ +void nrf_802154_rsch_uninit(void); + +/** + * @brief Enter continuous radio mode. + * + * In the continuous mode the radio scheduler should try to satisfy all preconditions as long as + * possible in order to give to the radio driver core as much radio time as possible while + * disturbing the other activities as little as possible. + * + * @note The start of a timeslot will be indicated by @ref nrf_802154_rsch_prec_approved call. + * + */ +void nrf_802154_rsch_continuous_mode_enter(void); + +/** + * @brief Exit continuous radio mode. + * + * In this mode the radio scheduler should not try to satisfy any of radio activity preconditions + * unless it was requested by other functionalities of this module. + * + */ +void nrf_802154_rsch_continuous_mode_exit(void); + +/** + * @brief Request timeslot for radio communication immediately. + * + * This function should be called only after @ref nrf_802154_rsch_prec_approved indicated the + * start of a timeslot. + * + * @param[in] length_us Requested radio timeslot length in microsecond. + * + * @retval true The radio driver now has exclusive access to the RADIO peripheral for the + * full length of the timeslot. + * @retval false Slot cannot be assigned due to other activities. + * + */ +bool nrf_802154_rsch_timeslot_request(uint32_t length_us); + +/** + * @brief Request timeslot in the future. + * + * Request timeslot that should be granted in the future. Function parameters provides data when + * the timeslot should start and how long should it last. When requested timeslot starts the + * @ref nrf_802154_rsch_delayed_timeslot_started is called. If requested timeslot cannot be granted + * with requested parameters, the @ref nrf_802154_rsch_delayed_timeslot_failed is called. + * + * @note Time parameters use the same units that are used in the Timer Scheduler module. + * + * @param[in] t0 Base time of the timestamp of the timeslot start [us]. + * @param[in] dt Time delta between @p t0 and the timestamp of the timeslot start [us]. + * @param[in] length Requested radio timeslot length [us]. + * + * @retval true Requested timeslot has been scheduled. + * @retval false Requested timeslot cannot be scheduled and will not be granted. + */ +bool nrf_802154_rsch_delayed_timeslot_request(uint32_t t0, uint32_t dt, uint32_t length); + +/** + * @brief Check if the RSCH precondition is satisfied. + * + * @param[in] prec RSCH precondition to be checked. + * + * @retval true Precondition @p prec is currently granted. + * @retval false Precondition @p prec is not currently granted. + */ +bool nrf_802154_rsch_prec_is_approved(rsch_prec_t prec); + +/** + * @brief Get left time of currently granted timeslot [us]. + * + * @returns Number of microseconds left in currently granted timeslot. + */ +uint32_t nrf_802154_rsch_timeslot_us_left_get(void); + +/** + * @brief The Radio Scheduler calls this function to notify the core + * about granting all preconditions. + * + * The radio driver now has exclusive access to the peripherals until + * @ref nrf_802154_rsch_prec_denied is called. + * + * @note The end of the timeslot will be indicated by @ref nrf_802154_rsch_prec_denied function. + * + */ +extern void nrf_802154_rsch_prec_approved(void); + +/** + * @brief The Radio Scheduler calls this function to notify the core + * about denial of one or all preconditions. + * + * Depending on the preconditions configuration, radio driver has NRF_RAAL_MAX_CLEAN_UP_TIME_US + * microseconds to do any clean-up actions on RADIO peripheral and stop using it completely. + * Thus the Radio Scheduler has to call this function NRF_RAAL_MAX_CLEAN_UP_TIME_US microseconds + * before the timeslot is finished. + * + * If the Radio Scheduler is in the continuous mode, the next timeslot will be indicated again by + * the @ref nrf_802154_rsch_prec_approved. + * + * @note Because the radio driver core needs to stop any operation on the RADIO peripheral within + * NRF_RAAL_MAX_CLEAN_UP_TIME_US microseconds, this function should be called with high + * interrupt priority level to avoid unwanted delays. + * + * @note This function may be called after @ref nrf_802154_rsch_continuous_mode_exit is called. + * + */ +extern void nrf_802154_rsch_prec_denied(void); + +/** + * @brief Notification that previously requested delayed timeslot has started just now. + */ +extern void nrf_802154_rsch_delayed_timeslot_started(void); + +/** + * @brief Notification that previously requested delayed timeslot cannot be started. + * + * This function may be called when any of radio activity precondition is not satisfied at the + * time when the timeslot should start. + */ +extern void nrf_802154_rsch_delayed_timeslot_failed(void); + +/** + *@} + **/ + +#ifdef __cplusplus +} +#endif + +#endif /* NRF_802154_RSCH_H_ */ diff --git a/third_party/NordicSemiconductor/drivers/radio/nrf_802154_swi.c b/third_party/NordicSemiconductor/drivers/radio/nrf_802154_swi.c index dc78f266a..fff23f07a 100644 --- a/third_party/NordicSemiconductor/drivers/radio/nrf_802154_swi.c +++ b/third_party/NordicSemiconductor/drivers/radio/nrf_802154_swi.c @@ -43,10 +43,9 @@ #include "nrf_802154.h" #include "nrf_802154_config.h" #include "nrf_802154_core.h" -#include "nrf_802154_critical_section.h" +#include "nrf_802154_rsch.h" #include "nrf_802154_rx_buffer.h" #include "hal/nrf_egu.h" -#include "raal/nrf_raal_api.h" /** Size of notification queue. @@ -169,63 +168,65 @@ typedef struct { struct { - nrf_802154_term_t term_lvl; ///< Request priority. - bool * p_result; ///< Sleep request result. - } sleep; ///< Sleep request details. + nrf_802154_term_t term_lvl; ///< Request priority. + bool * p_result; ///< Sleep request result. + } sleep; ///< Sleep request details. struct { - nrf_802154_notification_func_t notif_func; ///< Error notified in case of success. - nrf_802154_term_t term_lvl; ///< Request priority. - req_originator_t req_orig; ///< Request originator. - bool * p_result; ///< Receive request result. - } receive; ///< Receive request details. + nrf_802154_notification_func_t notif_func; ///< Error notified in case of success. + nrf_802154_term_t term_lvl; ///< Request priority. + req_originator_t req_orig; ///< Request originator. + bool notif_abort; ///< If function termination should be notified. + bool * p_result; ///< Receive request result. + } receive; ///< Receive request details. struct { - nrf_802154_notification_func_t notif_func; ///< Error notified in case of success. - nrf_802154_term_t term_lvl; ///< Request priority. - req_originator_t req_orig; ///< Request originator. - const uint8_t * p_data; ///< Pointer to PSDU to transmit. - bool cca; ///< If CCA was requested prior to transmission. - bool * p_result; ///< Transmit request result. - } transmit; ///< Transmit request details. + nrf_802154_notification_func_t notif_func; ///< Error notified in case of success. + nrf_802154_term_t term_lvl; ///< Request priority. + req_originator_t req_orig; ///< Request originator. + const uint8_t * p_data; ///< Pointer to PSDU to transmit. + bool cca; ///< If CCA was requested prior to transmission. + bool immediate; ///< If TX procedure must be performed immediately. + bool * p_result; ///< Transmit request result. + } transmit; ///< Transmit request details. struct { - nrf_802154_term_t term_lvl; ///< Request priority. - bool * p_result; ///< Energy detection request result. - uint32_t time_us; ///< Requested time of energy detection procedure. - } energy_detection; ///< Energy detection request details. + nrf_802154_term_t term_lvl; ///< Request priority. + bool * p_result; ///< Energy detection request result. + uint32_t time_us; ///< Requested time of energy detection procedure. + } energy_detection; ///< Energy detection request details. struct { - nrf_802154_term_t term_lvl; ///< Request priority. - bool * p_result; ///< CCA request result. - } cca; ///< CCA request details. + nrf_802154_term_t term_lvl; ///< Request priority. + bool * p_result; ///< CCA request result. + } cca; ///< CCA request details. struct { - nrf_802154_term_t term_lvl; ///< Request priority. - bool * p_result; ///< Continuous carrier request result. - } continuous_carrier; ///< Continuous carrier request details. + nrf_802154_term_t term_lvl; ///< Request priority. + bool * p_result; ///< Continuous carrier request result. + } continuous_carrier; ///< Continuous carrier request details. struct { - uint8_t * p_data; ///< Pointer to receive buffer to free. - bool * p_result; ///< Buffer free request result. - } buffer_free; ///< Buffer free request details. + uint8_t * p_data; ///< Pointer to receive buffer to free. + bool * p_result; ///< Buffer free request result. + } buffer_free; ///< Buffer free request details. struct { - bool * p_result; ///< Channel update request result. - } channel_update; ///< Channel update request details. + bool * p_result; ///< Channel update request result. + } channel_update; ///< Channel update request details. struct { - bool * p_result; ///< CCA config update request result. - } cca_cfg_update; ///< CCA config update request details. - } data; ///< Request data depending on it's type. + bool * p_result; ///< CCA config update request result. + } cca_cfg_update; ///< CCA config update request details. + } data; ///< Request data depending on it's type. } nrf_802154_req_data_t; static nrf_802154_ntf_data_t m_ntf_queue[NTF_QUEUE_SIZE]; ///< Notification queue. @@ -321,6 +322,41 @@ static bool ntf_queue_is_empty(void) return queue_is_empty(m_ntf_r_ptr, m_ntf_w_ptr); } +/** + * Enter notify block. + * + * This is a helper function used in all notification functions to atomically + * find an empty slot in the notification queue and allow atomic slot update. + * + * @return Pointer to an empty slot in the notification queue. + */ +static nrf_802154_ntf_data_t * ntf_enter(void) +{ + __disable_irq(); + __DSB(); + __ISB(); + + assert(!ntf_queue_is_full()); + (void)ntf_queue_is_full(); + + return &m_ntf_queue[m_ntf_w_ptr]; +} + +/** + * Exit notify block. + * + * This is a helper function used in all notification functions to end atomic slot update + * and trigger SWI to process the notification from the slot. + */ +static void ntf_exit(void) +{ + ntf_queue_ptr_increment(&m_ntf_w_ptr); + + nrf_egu_task_trigger(SWI_EGU, NTF_TASK); + + __enable_irq(); +} + /** * Increment given index associated with request queue. * @@ -404,32 +440,24 @@ void nrf_802154_swi_init(void) void nrf_802154_swi_notify_received(uint8_t * p_data, int8_t power, int8_t lqi) { - assert(!ntf_queue_is_full()); - - nrf_802154_ntf_data_t * p_slot = &m_ntf_queue[m_ntf_w_ptr]; + nrf_802154_ntf_data_t * p_slot = ntf_enter(); p_slot->type = NTF_TYPE_RECEIVED; p_slot->data.received.p_psdu = p_data; p_slot->data.received.power = power; p_slot->data.received.lqi = lqi; - ntf_queue_ptr_increment(&m_ntf_w_ptr); - - nrf_egu_task_trigger(SWI_EGU, NTF_TASK); + ntf_exit(); } void nrf_802154_swi_notify_receive_failed(nrf_802154_rx_error_t error) { - assert(!ntf_queue_is_full()); - - nrf_802154_ntf_data_t * p_slot = &m_ntf_queue[m_ntf_w_ptr]; + nrf_802154_ntf_data_t * p_slot = ntf_enter(); p_slot->type = NTF_TYPE_RECEIVE_FAILED; p_slot->data.receive_failed.error = error; - ntf_queue_ptr_increment(&m_ntf_w_ptr); - - nrf_egu_task_trigger(SWI_EGU, NTF_TASK); + ntf_exit(); } void nrf_802154_swi_notify_transmitted(const uint8_t * p_frame, @@ -437,9 +465,7 @@ void nrf_802154_swi_notify_transmitted(const uint8_t * p_frame, int8_t power, int8_t lqi) { - assert(!ntf_queue_is_full()); - - nrf_802154_ntf_data_t * p_slot = &m_ntf_queue[m_ntf_w_ptr]; + nrf_802154_ntf_data_t * p_slot = ntf_enter(); p_slot->type = NTF_TYPE_TRANSMITTED; p_slot->data.transmitted.p_frame = p_frame; @@ -447,81 +473,58 @@ void nrf_802154_swi_notify_transmitted(const uint8_t * p_frame, p_slot->data.transmitted.power = power; p_slot->data.transmitted.lqi = lqi; - ntf_queue_ptr_increment(&m_ntf_w_ptr); - - nrf_egu_task_trigger(SWI_EGU, NTF_TASK); + ntf_exit(); } void nrf_802154_swi_notify_transmit_failed(const uint8_t * p_frame, nrf_802154_tx_error_t error) { - assert(!ntf_queue_is_full()); - - nrf_802154_ntf_data_t * p_slot = &m_ntf_queue[m_ntf_w_ptr]; + nrf_802154_ntf_data_t * p_slot = ntf_enter(); p_slot->type = NTF_TYPE_TRANSMIT_FAILED; p_slot->data.transmit_failed.p_frame = p_frame; p_slot->data.transmit_failed.error = error; - ntf_queue_ptr_increment(&m_ntf_w_ptr); - - nrf_egu_task_trigger(SWI_EGU, NTF_TASK); + ntf_exit(); } void nrf_802154_swi_notify_energy_detected(uint8_t result) { - assert(!ntf_queue_is_full()); - - nrf_802154_ntf_data_t * p_slot = &m_ntf_queue[m_ntf_w_ptr]; + nrf_802154_ntf_data_t * p_slot = ntf_enter(); p_slot->type = NTF_TYPE_ENERGY_DETECTED; p_slot->data.energy_detected.result = result; - ntf_queue_ptr_increment(&m_ntf_w_ptr); - - nrf_egu_task_trigger(SWI_EGU, NTF_TASK); + ntf_exit(); } void nrf_802154_swi_notify_energy_detection_failed(nrf_802154_ed_error_t error) { - assert(!ntf_queue_is_full()); - - nrf_802154_ntf_data_t * p_slot = &m_ntf_queue[m_ntf_w_ptr]; + nrf_802154_ntf_data_t * p_slot = ntf_enter(); p_slot->type = NTF_TYPE_ENERGY_DETECTION_FAILED; p_slot->data.energy_detection_failed.error = error; - ntf_queue_ptr_increment(&m_ntf_w_ptr); - - nrf_egu_task_trigger(SWI_EGU, NTF_TASK); + ntf_exit(); } void nrf_802154_swi_notify_cca(bool channel_free) { - assert(!ntf_queue_is_full()); - - nrf_802154_ntf_data_t * p_slot = &m_ntf_queue[m_ntf_w_ptr]; + nrf_802154_ntf_data_t * p_slot = ntf_enter(); p_slot->type = NTF_TYPE_CCA; p_slot->data.cca.result = channel_free; - ntf_queue_ptr_increment(&m_ntf_w_ptr); - - nrf_egu_task_trigger(SWI_EGU, NTF_TASK); + ntf_exit(); } void nrf_802154_swi_notify_cca_failed(nrf_802154_cca_error_t error) { - assert(!ntf_queue_is_full()); - (void)ntf_queue_is_full(); - - nrf_802154_ntf_data_t * p_slot = &m_ntf_queue[m_ntf_w_ptr]; + nrf_802154_ntf_data_t * p_slot = ntf_enter(); p_slot->type = NTF_TYPE_CCA_FAILED; p_slot->data.cca_failed.error = error; - ntf_queue_ptr_increment(&m_ntf_w_ptr); - - nrf_egu_task_trigger(SWI_EGU, NTF_TASK); + ntf_exit(); } void nrf_802154_swi_timeslot_exit(void) @@ -550,15 +553,17 @@ void nrf_802154_swi_sleep(nrf_802154_term_t term_lvl, bool * p_result) void nrf_802154_swi_receive(nrf_802154_term_t term_lvl, req_originator_t req_orig, nrf_802154_notification_func_t notify_function, + bool notify_abort, bool * p_result) { nrf_802154_req_data_t * p_slot = req_enter(); - p_slot->type = REQ_TYPE_RECEIVE; - p_slot->data.receive.term_lvl = term_lvl; - p_slot->data.receive.req_orig = req_orig; - p_slot->data.receive.notif_func = notify_function; - p_slot->data.receive.p_result = p_result; + p_slot->type = REQ_TYPE_RECEIVE; + p_slot->data.receive.term_lvl = term_lvl; + p_slot->data.receive.req_orig = req_orig; + p_slot->data.receive.notif_func = notify_function; + p_slot->data.receive.notif_abort = notify_abort; + p_slot->data.receive.p_result = p_result; req_exit(); } @@ -567,6 +572,7 @@ void nrf_802154_swi_transmit(nrf_802154_term_t term_lvl, req_originator_t req_orig, const uint8_t * p_data, bool cca, + bool immediate, nrf_802154_notification_func_t notify_function, bool * p_result) { @@ -577,6 +583,7 @@ void nrf_802154_swi_transmit(nrf_802154_term_t term_lvl, p_slot->data.transmit.req_orig = req_orig; p_slot->data.transmit.p_data = p_data; p_slot->data.transmit.cca = cca; + p_slot->data.transmit.immediate = immediate; p_slot->data.transmit.notif_func = notify_function; p_slot->data.transmit.p_result = p_result; @@ -687,7 +694,8 @@ void SWI_IRQHandler(void) p_slot->data.transmitted.lqi); #else // NRF_802154_USE_RAW_API nrf_802154_transmitted(p_slot->data.transmitted.p_frame + RAW_PAYLOAD_OFFSET, - p_slot->data.transmitted.p_psdu + RAW_PAYLOAD_OFFSET, + p_slot->data.transmitted.p_psdu == NULL ? NULL : + p_slot->data.transmitted.p_psdu + RAW_PAYLOAD_OFFSET, p_slot->data.transmitted.p_psdu[RAW_LENGTH_OFFSET], p_slot->data.transmitted.power, p_slot->data.transmitted.lqi); @@ -731,7 +739,7 @@ void SWI_IRQHandler(void) if (nrf_egu_event_check(SWI_EGU, TIMESLOT_EXIT_EVENT)) { - nrf_raal_continuous_mode_exit(); + nrf_802154_rsch_continuous_mode_exit(); nrf_egu_event_clear(SWI_EGU, TIMESLOT_EXIT_EVENT); } @@ -743,97 +751,66 @@ void SWI_IRQHandler(void) while (!req_queue_is_empty()) { nrf_802154_req_data_t * p_slot = &m_req_queue[m_req_r_ptr]; - bool in_crit_sect; - - in_crit_sect = nrf_802154_critical_section_enter(); switch (p_slot->type) { case REQ_TYPE_SLEEP: - *(p_slot->data.sleep.p_result) = in_crit_sect ? - nrf_802154_core_sleep(p_slot->data.sleep.term_lvl) : - false; + *(p_slot->data.sleep.p_result) = + nrf_802154_core_sleep(p_slot->data.sleep.term_lvl); break; case REQ_TYPE_RECEIVE: - *(p_slot->data.receive.p_result) = in_crit_sect ? + *(p_slot->data.receive.p_result) = nrf_802154_core_receive(p_slot->data.receive.term_lvl, p_slot->data.receive.req_orig, - p_slot->data.receive.notif_func) : - false; - - if (!in_crit_sect) - { - p_slot->data.receive.notif_func(false); - } - + p_slot->data.receive.notif_func, + p_slot->data.receive.notif_abort); break; case REQ_TYPE_TRANSMIT: - *(p_slot->data.transmit.p_result) = in_crit_sect ? + *(p_slot->data.transmit.p_result) = nrf_802154_core_transmit(p_slot->data.transmit.term_lvl, p_slot->data.transmit.req_orig, p_slot->data.transmit.p_data, p_slot->data.transmit.cca, - p_slot->data.transmit.notif_func) : - false; - - if (!in_crit_sect) - { - p_slot->data.transmit.notif_func(false); - } - + p_slot->data.transmit.immediate, + p_slot->data.transmit.notif_func); break; case REQ_TYPE_ENERGY_DETECTION: - *(p_slot->data.energy_detection.p_result) = in_crit_sect ? + *(p_slot->data.energy_detection.p_result) = nrf_802154_core_energy_detection( p_slot->data.energy_detection.term_lvl, - p_slot->data.energy_detection.time_us) : - false; + p_slot->data.energy_detection.time_us); break; case REQ_TYPE_CCA: - *(p_slot->data.cca.p_result) = in_crit_sect ? - nrf_802154_core_cca(p_slot->data.cca.term_lvl) : - false; + *(p_slot->data.cca.p_result) = nrf_802154_core_cca(p_slot->data.cca.term_lvl); break; case REQ_TYPE_CONTINUOUS_CARRIER: - *(p_slot->data.continuous_carrier.p_result) = in_crit_sect ? + *(p_slot->data.continuous_carrier.p_result) = nrf_802154_core_continuous_carrier( - p_slot->data.continuous_carrier.term_lvl) : - false; + p_slot->data.continuous_carrier.term_lvl); break; case REQ_TYPE_BUFFER_FREE: - *(p_slot->data.buffer_free.p_result) = in_crit_sect ? - nrf_802154_core_notify_buffer_free( - p_slot->data.buffer_free.p_data): - false; + *(p_slot->data.buffer_free.p_result) = + nrf_802154_core_notify_buffer_free(p_slot->data.buffer_free.p_data); break; case REQ_TYPE_CHANNEL_UPDATE: - *(p_slot->data.channel_update.p_result) = in_crit_sect ? - nrf_802154_core_channel_update() : - false; + *(p_slot->data.channel_update.p_result) = nrf_802154_core_channel_update(); break; case REQ_TYPE_CCA_CFG_UPDATE: - *(p_slot->data.cca_cfg_update.p_result) = in_crit_sect ? - nrf_802154_core_cca_cfg_update() : - false; + *(p_slot->data.cca_cfg_update.p_result) = nrf_802154_core_cca_cfg_update(); break; default: assert(false); } - if (in_crit_sect) - { - nrf_802154_critical_section_exit(); - } - req_queue_ptr_increment(&m_req_r_ptr); } } diff --git a/third_party/NordicSemiconductor/drivers/radio/nrf_802154_swi.h b/third_party/NordicSemiconductor/drivers/radio/nrf_802154_swi.h index 809e34d56..775a90fff 100644 --- a/third_party/NordicSemiconductor/drivers/radio/nrf_802154_swi.h +++ b/third_party/NordicSemiconductor/drivers/radio/nrf_802154_swi.h @@ -147,13 +147,14 @@ void nrf_802154_swi_sleep(nrf_802154_term_t term_lvl, bool * p_result); * * @param[in] term_lvl Termination level of this request. Selects procedures to abort. * @param[in] req_orig Module that originates this request. - * @param[in] notify_function Function called to notify status of this procedure instead of - * default notification. If NULL default notification is used. + * @param[in] notify_function Function called to notify status of this procedure. May be NULL. + * @param[in] notify_abort If abort notification should be triggered automatically. * @param[out] p_result Result of entering receive state. */ void nrf_802154_swi_receive(nrf_802154_term_t term_lvl, req_originator_t req_orig, nrf_802154_notification_func_t notify_function, + bool notify_abort, bool * p_result); /** @@ -163,6 +164,9 @@ void nrf_802154_swi_receive(nrf_802154_term_t term_lvl, * @param[in] req_orig Module that originates this request. * @param[in] p_data Pointer to PSDU of the frame to transmit. * @param[in] cca If the driver should perform CCA procedure before transmission. + * @param[in] immediate If true, the driver schedules transmission immediately or never; + * if false transmission may be postponed until tx preconditions are + * met. * @param[in] notify_function Function called to notify status of this procedure instead of * default notification. If NULL default notification is used. * @param[out] p_result Result of entering transmit state. @@ -171,6 +175,7 @@ void nrf_802154_swi_transmit(nrf_802154_term_t term_lvl, req_originator_t req_orig, const uint8_t * p_data, bool cca, + bool immediate, nrf_802154_notification_func_t notify_function, bool * p_result); diff --git a/third_party/NordicSemiconductor/drivers/radio/nrf_802154_timer_coord.c b/third_party/NordicSemiconductor/drivers/radio/nrf_802154_timer_coord.c new file mode 100644 index 000000000..225b07b22 --- /dev/null +++ b/third_party/NordicSemiconductor/drivers/radio/nrf_802154_timer_coord.c @@ -0,0 +1,249 @@ +/* Copyright (c) 2018, Nordic Semiconductor ASA + * 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 Nordic Semiconductor ASA 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 implements Timer Coordinator module. + * + */ + +#include "nrf_802154_timer_coord.h" + +#include +#include +#include + +#include "nrf_802154_config.h" +#include "hal/nrf_ppi.h" +#include "platform/hp_timer/nrf_802154_hp_timer.h" +#include "platform/lp_timer/nrf_802154_lp_timer.h" + +#define DIV_ROUND_POSITIVE(n, d) (((n) + (d)/2)/(d)) +#define DIV_ROUND_NEGATIVE(n, d) (((n) - (d)/2)/(d)) +#define DIV_ROUND(n, d) ((((n) < 0) ^ ((d) < 0)) ? DIV_ROUND_NEGATIVE(n, d) : DIV_ROUND_POSITIVE(n, d)) + + +#define TIME_BASE (1UL << 22) ///< Unit used to calculate PPTB (Point per Time Base). It is not equal million to speed up computations and increase precision. +#define FIRST_RESYNC_TIME TIME_BASE ///< Delay of the first resynchronization. The first resynchronization is needed to measure timers drift. +#define RESYNC_TIME (64 * TIME_BASE) ///< Delay of following resynchronizations. +#define EWMA_COEF (8) ///< Weight used in the EWMA algorithm. + +#define PPI_CH0 NRF_PPI_CHANNEL13 +#define PPI_CH1 NRF_PPI_CHANNEL14 +#define PPI_CHGRP0 NRF_PPI_CHANNEL_GROUP1 + +#define PPI_SYNC PPI_CH0 +#define PPI_TIMESTAMP PPI_CH1 +#define PPI_TIMESTAMP_GROUP PPI_CHGRP0 + +#if NRF_802154_FRAME_TIMESTAMP_ENABLED +// Structure holding common timepoint from both timers. +typedef struct +{ + uint32_t lp_timer_time; ///< LP Timer time of common timepoint. + uint32_t hp_timer_time; ///< HP Timer time of common timepoint. +} common_timepoint_t; + +static common_timepoint_t m_last_sync; ///< Common timepoint of last synchronization event. +static volatile bool m_synchronized; ///< If timers were synchronized since last start. +static bool m_drift_known; ///< If timer drift value is known. +static int32_t m_drift; ///< Drift of the HP timer relatively to the LP timer [PPTB]. + +void nrf_802154_timer_coord_init(void) +{ + uint32_t sync_event; + uint32_t sync_task; + + m_drift = 0; + m_drift_known = 0; + + nrf_802154_hp_timer_init(); + + sync_event = nrf_802154_lp_timer_sync_event_get(); + sync_task = nrf_802154_hp_timer_sync_task_get(); + + nrf_ppi_channel_endpoint_setup(PPI_SYNC, sync_event, sync_task); + nrf_ppi_channel_enable(PPI_SYNC); + + nrf_ppi_channel_include_in_group(PPI_TIMESTAMP, PPI_TIMESTAMP_GROUP); +} + +void nrf_802154_timer_coord_uninit(void) +{ + nrf_802154_hp_timer_deinit(); + + nrf_ppi_channel_disable(PPI_SYNC); + nrf_ppi_channel_endpoint_setup(PPI_SYNC, 0, 0); + + nrf_ppi_group_disable(PPI_TIMESTAMP_GROUP); + nrf_ppi_channel_and_fork_endpoint_setup(PPI_TIMESTAMP, 0, 0, 0); +} + +void nrf_802154_timer_coord_start(void) +{ + m_synchronized = false; + nrf_802154_hp_timer_start(); + nrf_802154_hp_timer_sync_prepare(); + nrf_802154_lp_timer_sync_start_now(); +} + +void nrf_802154_timer_coord_stop(void) +{ + nrf_802154_hp_timer_stop(); + nrf_802154_lp_timer_sync_stop(); +} + +void nrf_802154_timer_coord_timestamp_prepare(uint32_t event_addr) +{ + nrf_ppi_channel_and_fork_endpoint_setup(PPI_TIMESTAMP, + event_addr, + nrf_802154_hp_timer_timestamp_task_get(), + (uint32_t)nrf_ppi_task_group_disable_address_get(PPI_TIMESTAMP_GROUP)); + + nrf_ppi_group_enable(PPI_TIMESTAMP_GROUP); +} + +bool nrf_802154_timer_coord_timestamp_get(uint32_t * p_timestamp) +{ + uint32_t hp_timestamp; + uint32_t hp_delta; + int32_t drift; + + assert(p_timestamp != NULL); + + if (!m_synchronized) + { + return false; + } + + hp_timestamp = nrf_802154_hp_timer_timestamp_get(); + hp_delta = hp_timestamp - m_last_sync.hp_timer_time; + drift = m_drift_known ? + (DIV_ROUND(((int64_t)m_drift * hp_delta), ((int64_t)TIME_BASE + m_drift))) : 0; + *p_timestamp = m_last_sync.lp_timer_time + hp_delta - drift; + + return true; +} + +void nrf_802154_lp_timer_synchronized(void) +{ + common_timepoint_t sync_time; + uint32_t lp_delta; + uint32_t hp_delta; + int32_t timers_diff; + int32_t drift; + int32_t tb_fraction_of_lp_delta; + + if (nrf_802154_hp_timer_sync_time_get(&sync_time.hp_timer_time)) + { + sync_time.lp_timer_time = nrf_802154_lp_timer_sync_time_get(); + + // Calculate timers drift + if (m_synchronized) + { + lp_delta = sync_time.lp_timer_time - m_last_sync.lp_timer_time; + hp_delta = sync_time.hp_timer_time - m_last_sync.hp_timer_time; + tb_fraction_of_lp_delta = DIV_ROUND_POSITIVE(lp_delta, TIME_BASE); + timers_diff = hp_delta - lp_delta; + drift = DIV_ROUND(timers_diff, tb_fraction_of_lp_delta); // Drift in PPTB + + if (m_drift_known) + { + m_drift = DIV_ROUND((m_drift * (EWMA_COEF - 1) + drift), EWMA_COEF); + } + else + { + m_drift = drift; + } + + m_drift_known = true; + } + + /* To avoid possible race when nrf_802154_timer_coord_timestamp_get + * is called when m_last_sync is being assigned report that we are not synchronized + * during assignment. + * This is naive solution that can be improved if needed with double buffering. + */ + m_synchronized = false; + __DMB(); + m_last_sync = sync_time; + __DMB(); + m_synchronized = true; + + nrf_802154_hp_timer_sync_prepare(); + nrf_802154_lp_timer_sync_start_at(m_last_sync.lp_timer_time, + m_drift_known ? RESYNC_TIME : FIRST_RESYNC_TIME); + } + else + { + nrf_802154_hp_timer_sync_prepare(); + nrf_802154_lp_timer_sync_start_now(); + } +} + +#else // NRF_802154_FRAME_TIMESTAMP_ENABLED + +void nrf_802154_timer_coord_init(void) +{ + // Intentionally empty +} + +void nrf_802154_timer_coord_uninit(void) +{ + // Intentionally empty +} + +void nrf_802154_timer_coord_start(void) +{ + // Intentionally empty +} + +void nrf_802154_timer_coord_stop(void) +{ + // Intentionally empty +} + +void nrf_802154_timer_coord_timestamp_prepare(uint32_t event_addr) +{ + (void)event_addr; + + // Intentionally empty +} + +bool nrf_802154_timer_coord_timestamp_get(uint32_t * p_timestamp) +{ + (void)p_timestamp; + + // Intentionally empty + + return false; +} + +#endif // NRF_802154_FRAME_TIMESTAMP_ENABLED diff --git a/third_party/NordicSemiconductor/drivers/radio/nrf_802154_timer_coord.h b/third_party/NordicSemiconductor/drivers/radio/nrf_802154_timer_coord.h new file mode 100644 index 000000000..06c2eae62 --- /dev/null +++ b/third_party/NordicSemiconductor/drivers/radio/nrf_802154_timer_coord.h @@ -0,0 +1,115 @@ +/* Copyright (c) 2018, Nordic Semiconductor ASA + * 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 Nordic Semiconductor ASA 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. + * + */ + +/** + * @brief This module defines the Timer Coordinator interface. + * + */ + +#ifndef NRF_802154_TIMER_COORD_H_ +#define NRF_802154_TIMER_COORD_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup nrf_802154_timer_coord Timer Coordinator + * @{ + * @ingroup nrf_802154 + * @brief Timer Coordinator interface. + * + * Timer Coordinator is responsible to synchronize and coordinate operations of the Low Power timer + * that counts absolute time and the High Precision timer that counts time relative to a timeslot. + */ + +/** + * @brief Initialize the Timer Coordinator module. + * + */ +void nrf_802154_timer_coord_init(void); + +/** + * @brief Uninitialize the Timer Coordinator module. + * + */ +void nrf_802154_timer_coord_uninit(void); + +/** + * @brief Start the Timer Coordinator. + * + * This function starts the HP timer and synchronizes it with the LP timer. + * + * Started Timer Coordinator resynchronizes automatically in constant interval. + */ +void nrf_802154_timer_coord_start(void); + +/** + * @brief Stop the Timer Coordinator. + * + * This function stops the HP timer. + */ +void nrf_802154_timer_coord_stop(void); + +/** + * @brief Prepare getting precise timestamp of given event. + * + * @param[in] event_addr Address of the peripheral register corresponding to the event that + * should be timestamped. + */ +void nrf_802154_timer_coord_timestamp_prepare(uint32_t event_addr); + +/** + * @brief Get timestamp of the recently prepared event. + * + * If recently prepared event occurred a few times since preparation, this function returns + * timestamp of the first occurrence. + * If the requested event did not occur since preparation or HP timer is not synchronized, this + * function returns false. + * + * @param[out] p_timestamp Precise absolute timestamp of recently prepared event [us]. + * + * @retval true Timestamp is available. + * @retval false Timestamp is unavailable. + */ +bool nrf_802154_timer_coord_timestamp_get(uint32_t * p_timestamp); + +/** + *@} + **/ + +#ifdef __cplusplus +} +#endif + +#endif /* NRF_802154_TIMER_COORD_H_ */ diff --git a/third_party/NordicSemiconductor/drivers/radio/nrf_802154_types.h b/third_party/NordicSemiconductor/drivers/radio/nrf_802154_types.h index 2f1799b7b..abe44a4c2 100644 --- a/third_party/NordicSemiconductor/drivers/radio/nrf_802154_types.h +++ b/third_party/NordicSemiconductor/drivers/radio/nrf_802154_types.h @@ -67,6 +67,7 @@ typedef uint8_t nrf_802154_tx_error_t; #define NRF_802154_TX_ERROR_TIMESLOT_ENDED 0x04 //!< Radio timeslot ended during transmission procedure. #define NRF_802154_TX_ERROR_NO_ACK 0x05 //!< ACK frame was not received during time-out period. #define NRF_802154_TX_ERROR_ABORTED 0x06 //!< Procedure was aborted by another driver operation with FORCE priority. +#define NRF_802154_TX_ERROR_TIMESLOT_DENIED 0x07 //!< Transmission did not start due to denied timeslot request. /** * @brief Possible errors during frame reception. @@ -95,6 +96,14 @@ typedef uint8_t nrf_802154_cca_error_t; #define NRF_802154_CCA_ERROR_ABORTED 0x01 //!< Procedure was aborted by another driver operation with FORCE priority. +/** + * @brief Possible errors during sleep procedure call. + */ +typedef uint8_t nrf_802154_sleep_error_t; + +#define NRF_802154_SLEEP_ERROR_NONE 0x00 //!< There is no error. +#define NRF_802154_SLEEP_ERROR_BUSY 0x01 //!< The driver cannot enter sleep state due to ongoing operation. + /** * @brief Termination level selected for a particular request. * diff --git a/third_party/NordicSemiconductor/drivers/radio/nrf_802154_utils.h b/third_party/NordicSemiconductor/drivers/radio/nrf_802154_utils.h new file mode 100644 index 000000000..7ae62d386 --- /dev/null +++ b/third_party/NordicSemiconductor/drivers/radio/nrf_802154_utils.h @@ -0,0 +1,145 @@ +/* Copyright (c) 2018, Nordic Semiconductor ASA + * 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 Nordic Semiconductor ASA 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. + * + */ + +#ifndef NRF_802154_UTILS_H__ +#define NRF_802154_UTILS_H__ + +#include +#include + +/** + * @defgroup nrf_802154_utils Utils definitions used in the 802.15.4 driver. + * @{ + * @ingroup nrf_802154 + * @brief Definitions of utils used in the 802.15.4 driver. + */ + +/**@brief RTC clock frequency. */ +#define NRF_802154_RTC_FREQUENCY 32768UL + +/**@brief Defines number of microseconds in one second. */ +#define NRF_802154_US_PER_S 1000000ULL + +/**@brief Number of microseconds in one RTC tick. (rounded up) */ +#define NRF_802154_US_PER_TICK NRF_802154_RTC_TICKS_TO_US(1) + +/**@brief Number of bits to shift RTC_FREQUENCY and US_PER_S to achieve division by greatest common divisor. */ +#define NRF_802154_FREQUENCY_US_PER_S_GCD_BITS 6 + + +/**@brief Ceil division helper */ +#define NRF_802154_DIVIDE_AND_CEIL(A, B) (((A) + (B) - 1) / (B)) + +/**@brief RTC ticks to us conversion. */ +#define NRF_802154_RTC_TICKS_TO_US(ticks) \ + NRF_802154_DIVIDE_AND_CEIL( \ + (ticks) * (NRF_802154_US_PER_S >> NRF_802154_FREQUENCY_US_PER_S_GCD_BITS), \ + (NRF_802154_RTC_FREQUENCY >> NRF_802154_FREQUENCY_US_PER_S_GCD_BITS)) + +static inline uint64_t NRF_802154_US_TO_RTC_TICKS(uint64_t time) +{ + uint64_t t1, u1; + uint64_t result; + + /* The required range for time is [0..315360000000000], and the calculation below are + verified to work within broader range [0...2^49 ~ 17 years] + + This first step in the calculation is to find out how many units + of 15625 us there are in the input_us, because 512 RTC units + corresponds _exactly_ to 15625 us. The calculation we want to do is therefore + t1 = time / 15625, but division is slow and therefore we want to calculate + t1 = time * k instead. The constant k is 1/15625 shifted up by as many bits + as we can without causing overflow during the calculation. + + 49 bits are needed to store the maximum value that time can have, and the + lowest 13 bits in that value can be shifted away because a minimum of 14 bits + are needed to store the divisor. + + This means that time can be reduced to 49 - 13 = 36 bits to make space + for k. + + The most suitable number of shift for the value 1 / 15625 = 0.000064 + (binary 0.00000000000001000011000110111101111...) is 41, because that results + in a 28 bits number that does not cause overflow in the multiplication. + + (2^41)/15625) is equal to 0x8637bd0, and is written in hexadecimal representation + to show the bit width of the number. Shifting is limited to 41 bits because: + 1 The time uses up to 49 bits, and + 2) The time can only be shifted down 13 bits to avoid shifting away + a full unit of 15625 microseconds, and + 3) The maximum value of the calculation would otherwise overflow (i.e. + (315360000000000 >> 13) * 0x8637bd0 = 0x4b300bfcd0aefde0, would no longer be less than + 0Xffffffffffffffff). + + There is a possible loss of precision so that t1 will be up to 93*15625 _smaller_ + than the accurate number. This is taken into account in the next step. + */ + + t1 = ((time >> 13) * 0x8637bd0) >> 28; // ((time >> 13) * (2^41 / 15625)) >> (41 - 13) + result = t1 * 512; + t1 = time - t1 * 15625; + + /* This second step of the calculation is to find out how many RTC units there are + still left in the remaining microseconds. + + (2^56)/15625) is equal to 0x431bde82d7b, and is written in hexadecimal representation + to show the bit width of the number. Shifting 56 bits is determined by the worst + case value of t1. The constant is selected by using the same methodology as in the + first step of the calculation above. + + The possible loss of precision in the calculation above can make t1 93*15625 lower + than it should have been here. The worst case found is that t1 can be 1453125, and + therefore there is no overflow in the calculation + 1453125 * 0x431bde82d7b = 0x5cfffffffff76627 (i.e. it is less than 0xffffffffffffffff). + + 15625 below is the binary representation of 30.51757813 (11110.100001001) + scaled up by 2^9, and the calculation below are therefore using that scale. + + Rounding up to the nearest RTC tick is done by adding the value of the least + significant bits of the fraction (i.e. adding the value of bits 1..47 of the scaled + up timer unit size (2^47)) to the calculated value before scaling the final + value down to RTC ticks.*/ + + // ceil((time * (2^56 / 15625)) >> (56 - 9)) + assert(t1 <= 1453125); + u1 = (t1 * 0x431bde82d7b); // (time * (2^56 / 15625)) + u1 += 0x7fffffffffff; // round up + u1 >>= 47; // ceil(u1 >> (56 - 9)) + + result += u1; + + return result; +} + +/** + *@} + **/ + +#endif // NRF_802154_UTILS_H__ diff --git a/third_party/NordicSemiconductor/drivers/radio/platform/clock/nrf_802154_clock.h b/third_party/NordicSemiconductor/drivers/radio/platform/clock/nrf_802154_clock.h index 8c9bf88e6..724079c2b 100644 --- a/third_party/NordicSemiconductor/drivers/radio/platform/clock/nrf_802154_clock.h +++ b/third_party/NordicSemiconductor/drivers/radio/platform/clock/nrf_802154_clock.h @@ -36,7 +36,8 @@ * It is used by Radio Arbiter clients (RAAL) to start HF clock when entering continuous mode * and stop HF clock after continuous mode exit. * - * It is used by standalone Timer Abstraction Layer implementation to start LF clock during initialization. + * It is used by standalone Low Power Timer Abstraction Layer implementation + * to start LF clock during initialization. * */ diff --git a/third_party/NordicSemiconductor/drivers/radio/platform/hp_timer/nrf_802154_hp_timer.c b/third_party/NordicSemiconductor/drivers/radio/platform/hp_timer/nrf_802154_hp_timer.c new file mode 100644 index 000000000..ca78f1ce4 --- /dev/null +++ b/third_party/NordicSemiconductor/drivers/radio/platform/hp_timer/nrf_802154_hp_timer.c @@ -0,0 +1,142 @@ +/* Copyright (c) 2018, Nordic Semiconductor ASA + * 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 Nordic Semiconductor ASA 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 contains implementation of the nRF 802.15.4 high precision timer abstraction. + * + * This implementation is built on top of the TIMER peripheral. + * If SoftDevice RAAL is in use the TIMER peripheral is shared between RAAL and this module. + * + */ + +#include "nrf_802154_hp_timer.h" + +#include +#include +#include + +#include +#include + +#include "nrf_802154_config.h" + +/**@brief Timer instance. */ +#define TIMER NRF_TIMER0 + +/**@brief Timer compare channel definitions. */ +#define TIMER_CC_CAPTURE NRF_TIMER_CC_CHANNEL1 +#define TIMER_CC_CAPTURE_TASK NRF_TIMER_TASK_CAPTURE1 + +#define TIMER_CC_SYNC NRF_TIMER_CC_CHANNEL2 +#define TIMER_CC_SYNC_TASK NRF_TIMER_TASK_CAPTURE2 +#define TIMER_CC_SYNC_EVENT NRF_TIMER_EVENT_COMPARE2 +#define TIMER_CC_SYNC_INT NRF_TIMER_INT_COMPARE2_MASK + +#define TIMER_CC_EVT NRF_TIMER_CC_CHANNEL3 +#define TIMER_CC_EVT_TASK NRF_TIMER_TASK_CAPTURE3 +#define TIMER_CC_EVT_INT NRF_TIMER_INT_COMPARE3_MASK + +/**@brief Unexpected value in the sync compare channel. */ +static uint32_t m_unexpected_sync; + +/**@brief Get current time on the Timer. */ +static inline uint32_t timer_time_get(void) +{ + nrf_timer_task_trigger(TIMER, TIMER_CC_CAPTURE_TASK); + return nrf_timer_cc_read(TIMER, TIMER_CC_CAPTURE); +} + +void nrf_802154_hp_timer_init(void) +{ + // Intentionally empty +} + +void nrf_802154_hp_timer_deinit(void) +{ + nrf_timer_task_trigger(TIMER, NRF_TIMER_TASK_SHUTDOWN); +} + +void nrf_802154_hp_timer_start(void) +{ +#if !RAAL_SOFTDEVICE && !RAAL_SIMULATOR + nrf_timer_mode_set(TIMER, NRF_TIMER_MODE_TIMER); + nrf_timer_bit_width_set(TIMER, NRF_TIMER_BIT_WIDTH_32); + nrf_timer_frequency_set(TIMER, NRF_TIMER_FREQ_1MHz); + nrf_timer_task_trigger(TIMER, NRF_TIMER_TASK_START); +#endif // !RAAL_SOFTDEVICE && !RAAL_SIMULATOR +} + +void nrf_802154_hp_timer_stop(void) +{ +#if !RAAL_SOFTDEVICE && !RAAL_SIMULATOR + nrf_timer_task_trigger(TIMER, NRF_TIMER_TASK_SHUTDOWN); +#endif // !RAAL_SOFTDEVICE && !RAAL_SIMULATOR +} + +uint32_t nrf_802154_hp_timer_sync_task_get(void) +{ + return (uint32_t)nrf_timer_task_address_get(TIMER, TIMER_CC_SYNC_TASK); +} + +void nrf_802154_hp_timer_sync_prepare(void) +{ + uint32_t past_time = timer_time_get() - 1; + + m_unexpected_sync = past_time; + nrf_timer_cc_write(TIMER, TIMER_CC_SYNC, past_time); +} + +bool nrf_802154_hp_timer_sync_time_get(uint32_t * p_timestamp) +{ + bool result = false; + uint32_t sync_time = nrf_timer_cc_read(TIMER, TIMER_CC_SYNC); + + assert(p_timestamp != NULL); + + if (sync_time != m_unexpected_sync) + { + *p_timestamp = sync_time; + result = true; + } + + return result; +} + +uint32_t nrf_802154_hp_timer_timestamp_task_get(void) +{ + return (uint32_t)nrf_timer_task_address_get(TIMER, TIMER_CC_EVT_TASK); +} + +uint32_t nrf_802154_hp_timer_timestamp_get(void) +{ + return nrf_timer_cc_read(TIMER, TIMER_CC_EVT); +} + diff --git a/third_party/NordicSemiconductor/drivers/radio/platform/hp_timer/nrf_802154_hp_timer.h b/third_party/NordicSemiconductor/drivers/radio/platform/hp_timer/nrf_802154_hp_timer.h new file mode 100644 index 000000000..ea561a682 --- /dev/null +++ b/third_party/NordicSemiconductor/drivers/radio/platform/hp_timer/nrf_802154_hp_timer.h @@ -0,0 +1,146 @@ +/* Copyright (c) 2018, Nordic Semiconductor ASA + * 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 Nordic Semiconductor ASA 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. + * + */ + +/** + * @brief This module defines API or High Precision Timer for the 802.15.4 driver. + * + */ + +#ifndef NRF_802154_HP_TIMER_H_ +#define NRF_802154_HP_TIMER_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup nrf_802154_hp_timer High Precision Timer for the 802.15.4 driver + * @{ + * @ingroup nrf_802154_hp_timer + * @brief High Precision Timer for the 802.15.4 driver. + * + * High Precision Timer is a timer that is used only when the radio is in use. This timer is not + * used when the radio is in the sleep mode or out of RAAL timeslots. This timer should provide at + * least 1us precision. It is intended to be used for precise frame timestamps or synchronous radio + * operations. + * + * @note High Precision Timer is a relative timer. To use it as absolute timer it must be + * synchronized with the Low Power Timer. + * + */ + +/** + * @brief Initialize the timer. + */ +void nrf_802154_hp_timer_init(void); + +/** + * @brief Uninitialize the timer. + */ +void nrf_802154_hp_timer_deinit(void); + +/** + * @brief Start the timer. + * + * The timer starts counting when this command is called. + */ +void nrf_802154_hp_timer_start(void); + +/** + * @brief Stop the timer. + * + * The timer stops counting and enters low power mode. + */ +void nrf_802154_hp_timer_stop(void); + +/** + * @brief Get value indicated by the timer right now. + * + * @note Returned value is relative to the @ref nrf_802154_hp_timer_start call time. It is not + * synchronized with the lp timer. + * + * @returns Current timer value [us]. + */ +uint32_t nrf_802154_hp_timer_current_time_get(void); + +/** + * @brief Get task used to synchronize this timer with the LP timer. + * + * @returns Address of the task. + */ +uint32_t nrf_802154_hp_timer_sync_task_get(void); + +/** + * @brief Configure the timer to detect if sync task was triggered. + */ +void nrf_802154_hp_timer_sync_prepare(void); + +/** + * @brief Get timestamp of the synchronization event. + * + * @param[out] p_timestamp Timestamp of the synchronization event. + * + * @retval true Synchronization was performed and @p p_timestamp is valid. + * @retval false Synchronization was not performed. @p p_timestamp was not modified. + */ +bool nrf_802154_hp_timer_sync_time_get(uint32_t * p_timestamp); + +/** + * @brief Get task used to make timestamp of an event. + * + * This function should be used to configure PPI. + * This function configures the timer in order to detect if returned task was triggered to return + * valid value by the @ref nrf_802154_hp_timer_timestamp_get. + * + * @returns Address of the task. + */ +uint32_t nrf_802154_hp_timer_timestamp_task_get(void); + +/** + * @brief Get timestamp of last event. + * + * @returns Timestamp of last event that triggered the @ref nrf_802154_hp_timer_timestamp_task_get + * task. + */ +uint32_t nrf_802154_hp_timer_timestamp_get(void); + + +/** + *@} + **/ + +#ifdef __cplusplus +} +#endif + +#endif /* NRF_802154_HP_TIMER_H_ */ diff --git a/third_party/NordicSemiconductor/drivers/radio/platform/timer/nrf_802154_timer.h b/third_party/NordicSemiconductor/drivers/radio/platform/lp_timer/nrf_802154_lp_timer.h similarity index 53% rename from third_party/NordicSemiconductor/drivers/radio/platform/timer/nrf_802154_timer.h rename to third_party/NordicSemiconductor/drivers/radio/platform/lp_timer/nrf_802154_lp_timer.h index c0ff11e98..26ef993a6 100644 --- a/third_party/NordicSemiconductor/drivers/radio/platform/timer/nrf_802154_timer.h +++ b/third_party/NordicSemiconductor/drivers/radio/platform/lp_timer/nrf_802154_lp_timer.h @@ -29,12 +29,12 @@ */ /** - * @brief This module defines Timer Abstraction Layer for the 802.15.4 driver. + * @brief This module defines Low Power Timer Abstraction Layer for the 802.15.4 driver. * */ -#ifndef NRF_802154_TIMER_API_H_ -#define NRF_802154_TIMER_API_H_ +#ifndef NRF_802154_LP_TIMER_API_H_ +#define NRF_802154_LP_TIMER_API_H_ #include #include @@ -44,12 +44,12 @@ extern "C" { #endif /** - * @defgroup nrf_802154_timer Timer Abstraction Layer for the 802.15.4 driver + * @defgroup nrf_802154_timer Low Power Timer Abstraction Layer for the 802.15.4 driver * @{ * @ingroup nrf_802154_timer - * @brief Timer Abstraction Layer interface for the 802.15.4 driver. + * @brief Low Power Timer Abstraction Layer interface for the 802.15.4 driver. * - * Timer Abstraction Layer is an abstraction layer of timer that is meant to be used by + * Low Power Timer Abstraction Layer is an abstraction layer of timer that is meant to be used by * the nRF 802.15.4 driver. This timer should provide low latency (max 100 us) in order to allow * implementation in the driver code features like: * * Timing out waiting for ACK frame @@ -58,54 +58,54 @@ extern "C" { * * CSL * * Auto polling by rx-off-when-idle devices * - * @note Most of Timer Abstraction Layer API should not be called directly by 802.15.4 driver + * @note Most of Low Power Timer Abstraction Layer API should not be called directly by 802.15.4 driver * modules. This API is used by the Timer Scheduler module included in the driver and other * modules should use Timer Scheduler API. Exception from above rule are initialization and - * deinitialization functions @sa nrf_802154_timer_init() - * @sa nrf_802154_timer_deinit() and critical section management - * @sa nrf_802154_timer_critical_section_enter() - * @sa nrf_802154_timer_critical_section_exit() as these functions are called from + * deinitialization functions @sa nrf_802154_lp_timer_init() + * @sa nrf_802154_lp_timer_deinit() and critical section management + * @sa nrf_802154_lp_timer_critical_section_enter() + * @sa nrf_802154_lp_timer_critical_section_exit() as these functions are called from * nrf_802154_critical_section module and from global initialization functions * @sa nrf_802154_init() @sa nrf_802154_deinit(). */ /** - * @brief Initialize Timer. + * @brief Initialize the Timer. */ -void nrf_802154_timer_init(void); +void nrf_802154_lp_timer_init(void); /** - * @brief Uninitialize Timer. + * @brief Uninitialize the Timer. */ -void nrf_802154_timer_deinit(void); +void nrf_802154_lp_timer_deinit(void); /** * @brief Enter critical section of the timer. * - * In critical section timer cannot execute @sa nrf_802154_timer_fired() function. + * In critical section timer cannot execute @sa nrf_802154_lp_timer_fired() function. * * @note Critical section cannot be nested. */ -void nrf_802154_timer_critical_section_enter(void); +void nrf_802154_lp_timer_critical_section_enter(void); /** * @brief Exit critical section of the timer. * - * In critical section timer cannot execute @sa nrf_802154_timer_fired() function. + * In critical section timer cannot execute @sa nrf_802154_lp_timer_fired() function. * * @note Critical section cannot be nested. */ -void nrf_802154_timer_critical_section_exit(void); +void nrf_802154_lp_timer_critical_section_exit(void); /** * @brief Get current time. * - * Prior to getting current time, Timer must be initialized @sa nrf_802154_timer_init(). + * Prior to getting current time, Timer must be initialized @sa nrf_802154_lp_timer_init(). * There are no other requirements that must be fulfilled before using this function. * * @return Current time in microseconds [us]. */ -uint32_t nrf_802154_timer_time_get(void); +uint32_t nrf_802154_lp_timer_time_get(void); /** * @brief Get granularity of currently used timer. @@ -114,7 +114,7 @@ uint32_t nrf_802154_timer_time_get(void); * * @return Timer granularity in microseconds [us]. */ -uint32_t nrf_802154_timer_granularity_get(void); +uint32_t nrf_802154_lp_timer_granularity_get(void); /** * @brief Start one-shot timer that expires at specified time. @@ -123,18 +123,18 @@ uint32_t nrf_802154_timer_granularity_get(void); * If timer is running when this function is called, previously running timer will be stopped * automatically. * - * On timer expiration @sa nrf_802154_timer_fired function will be called. + * On timer expiration @sa nrf_802154_lp_timer_fired function will be called. * Timer automatically stops after expiration. * * @param[in] t0 Number of microseconds representing timer start time. * @param[in] dt Time of timer expiration as time elapsed from @p t0 [us]. */ -void nrf_802154_timer_start(uint32_t t0, uint32_t dt); +void nrf_802154_lp_timer_start(uint32_t t0, uint32_t dt); /** * @brief Stop currently running timer. */ -void nrf_802154_timer_stop(void); +void nrf_802154_lp_timer_stop(void); /** * @brief Check if timer is currently running. @@ -142,12 +142,60 @@ void nrf_802154_timer_stop(void); * @retval true Timer is running. * @retval false Timer is not running. */ -bool nrf_802154_timer_is_running(void); +bool nrf_802154_lp_timer_is_running(void); + +/** + * @brief Start one-shot synchronization timer that expires at nearest possible timepoint. + * + * On timer expiration @ref nrf_802154_lp_timer_synchronized function will be called and + * event returned by @ref nrf_802154_lp_timer_sync_event_get will be triggered. + * + * @note @ref nrf_802154_lp_timer_synchronized may be called multiple times. + */ +void nrf_802154_lp_timer_sync_start_now(void); + +/** + * @brief Start one-shot synchronization timer that expires at specified time. + * + * Start one-shot synchronization timer that will expire @p dt microseconds after @p t0 time. + * + * On timer expiration @ref nrf_802154_lp_timer_synchronized function will be called and + * event returned by @ref nrf_802154_lp_timer_sync_event_get will be triggered. + * + * @param[in] t0 Number of microseconds representing timer start time. + * @param[in] dt Time of timer expiration as time elapsed from @p t0 [us]. + */ +void nrf_802154_lp_timer_sync_start_at(uint32_t t0, uint32_t dt); + +/** + * @brief Stop currently running synchronization timer. + */ +void nrf_802154_lp_timer_sync_stop(void); + +/** + * @brief Get event used to synchronize this timer with HP Timer + * + * @return Address of the peripheral register corresponding to the event that + * should be used for timers synchronization. + */ +uint32_t nrf_802154_lp_timer_sync_event_get(void); + +/** + * @brief Get timestamp of the synchronization event. + * + * @return Timestamp of the synchronization event. + */ +uint32_t nrf_802154_lp_timer_sync_time_get(void); /** * @brief Callback executed when timer expires. */ -extern void nrf_802154_timer_fired(void); +extern void nrf_802154_lp_timer_fired(void); + +/** + * @brief Callback executed when synchronization timer expires. + */ +extern void nrf_802154_lp_timer_synchronized(void); /** *@} @@ -157,4 +205,4 @@ extern void nrf_802154_timer_fired(void); } #endif -#endif /* NRF_802154_TIMER_API_H_ */ +#endif /* NRF_802154_LP_TIMER_API_H_ */ diff --git a/third_party/NordicSemiconductor/drivers/radio/platform/lp_timer/nrf_802154_lp_timer_nodrv.c b/third_party/NordicSemiconductor/drivers/radio/platform/lp_timer/nrf_802154_lp_timer_nodrv.c new file mode 100644 index 000000000..ca9979e2b --- /dev/null +++ b/third_party/NordicSemiconductor/drivers/radio/platform/lp_timer/nrf_802154_lp_timer_nodrv.c @@ -0,0 +1,576 @@ +/* Copyright (c) 2017 - 2018, Nordic Semiconductor ASA + * 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 Nordic Semiconductor ASA 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 contains standalone implementation of the nRF 802.15.4 timer abstraction. + * + * This implementation is built on top of the RTC peripheral. + * + */ + +#include "nrf_802154_lp_timer.h" + +#include + +#include +#include + +#include "platform/clock/nrf_802154_clock.h" +#include "nrf_802154_config.h" +#include "nrf_802154_utils.h" + +#define RTC_LP_TIMER_COMPARE_CHANNEL 0 +#define RTC_LP_TIMER_COMPARE_INT_MASK NRF_RTC_INT_COMPARE0_MASK +#define RTC_LP_TIMER_COMPARE_EVENT NRF_RTC_EVENT_COMPARE_0 +#define RTC_LP_TIMER_COMPARE_EVENT_MASK RTC_EVTEN_COMPARE0_Msk + +#define RTC_SYNC_COMPARE_CHANNEL 1 +#define RTC_SYNC_COMPARE_INT_MASK NRF_RTC_INT_COMPARE1_MASK +#define RTC_SYNC_COMPARE_EVENT NRF_RTC_EVENT_COMPARE_1 +#define RTC_SYNC_COMPARE_EVENT_MASK RTC_EVTEN_COMPARE1_Msk + +#define US_PER_OVERFLOW (512UL * NRF_802154_US_PER_S) ///< Time that has passed between overflow events. On full RTC speed, it occurs every 512 s. +#define MIN_RTC_COMPARE_EVENT_DT (2 * NRF_802154_US_PER_TICK) ///< Minimum time delta from now before RTC compare event is guaranteed to fire. + +#define EPOCH_32BIT_US (1ULL << 32) +#define EPOCH_FROM_TIME(time) ((time) & ((uint64_t)UINT32_MAX << 32)) + + +// Struct holding information about compare channel. +typedef struct +{ + uint32_t channel; ///< Channel number + uint32_t int_mask; ///< Interrupt mask + nrf_rtc_event_t event; ///< Event + uint32_t event_mask; ///< Event mask +} compare_channel_descriptor_t; + +// Enum holding all used compare channels. +typedef enum {LP_TIMER_CHANNEL, SYNC_CHANNEL, CHANNEL_CNT} compare_channel_t; + +// Descriptors of all used compare channels. +static const compare_channel_descriptor_t m_cmp_ch[CHANNEL_CNT] = {{RTC_LP_TIMER_COMPARE_CHANNEL, + RTC_LP_TIMER_COMPARE_INT_MASK, + RTC_LP_TIMER_COMPARE_EVENT, + RTC_LP_TIMER_COMPARE_EVENT_MASK}, + {RTC_SYNC_COMPARE_CHANNEL, + RTC_SYNC_COMPARE_INT_MASK, + RTC_SYNC_COMPARE_EVENT, + RTC_SYNC_COMPARE_EVENT_MASK}}; + +static uint64_t m_target_times[CHANNEL_CNT]; ///< Target time of given channel [us]. + + +static volatile uint32_t m_offset_counter; ///< Counter of RTC overflows, incremented by 2 on each OVERFLOW event. +static volatile uint8_t m_mutex; ///< Mutex for write access to @ref m_offset_counter. +static volatile bool m_clock_ready; ///< Information that LFCLK is ready. + +static uint32_t overflow_counter_get(void); + +/** @brief Non-blocking mutex for mutual write access to @ref m_offset_counter variable. + * + * @retval true Mutex was acquired. + * @retval false Mutex could not be acquired. + */ +static inline bool mutex_get(void) +{ + do + { + volatile uint8_t mutex_value = __LDREXB(&m_mutex); + + if (mutex_value) + { + __CLREX(); + return false; + } + } + while (__STREXB(1, &m_mutex)); + + // Disable OVERFLOW interrupt to prevent lock-up in interrupt context while mutex is locked from lower priority context + // and OVERFLOW event flag is stil up. + nrf_rtc_int_disable(NRF_802154_RTC_INSTANCE, NRF_RTC_INT_OVERFLOW_MASK); + + __DMB(); + + return true; +} + +/** @brief Release mutex. */ +static inline void mutex_release(void) +{ + // Re-enable OVERFLOW interrupt. + nrf_rtc_int_enable(NRF_802154_RTC_INSTANCE, NRF_RTC_INT_OVERFLOW_MASK); + + __DMB(); + m_mutex = 0; +} + +/** @brief Check if timer shall strike. + * + * @param[in] now Current time. + * + * @retval true Timer shall strike now. + * @retval false Timer shall not strike now. + */ +static inline bool shall_strike(uint64_t now) +{ + return now >= m_target_times[LP_TIMER_CHANNEL]; +} + +/** @brief Convert time in [us] to RTC ticks. + * + * @param[in] time Time to convert. + * + * @return Time value in RTC ticks. + */ +static inline uint64_t time_to_ticks(uint64_t time) +{ + return NRF_802154_US_TO_RTC_TICKS(time); +} + +/** @brief Convert RTC ticks to time in [us]. + * + * @param[in] ticks RTC ticks to convert. + * + * @return Time value in [us]. + */ +static inline uint64_t ticks_to_time(uint64_t ticks) +{ + return NRF_802154_RTC_TICKS_TO_US(ticks); +} + +/** @brief Get current value of the RTC counter. + * + * @return RTC counter value [ticks]. + */ +static uint32_t counter_get(void) +{ + return nrf_rtc_counter_get(NRF_802154_RTC_INSTANCE); +} + +/** @brief Get RTC counter value and matching offset that represent the current time. + * + * @param[out] p_offset Offset of the current time. + * @param[out] p_counter RTC value of the current time. + */ +static void offset_and_counter_get(uint32_t * p_offset, uint32_t * p_counter) +{ + uint32_t offset_1 = overflow_counter_get(); + + __DMB(); + + uint32_t rtc_value_1 = counter_get(); + + __DMB(); + + uint32_t offset_2 = overflow_counter_get(); + + *p_offset = offset_2; + *p_counter = (offset_1 == offset_2) ? rtc_value_1 : counter_get(); +} + +/** @brief Get time from given @p offset and @p counter values. + * + * @param[in] offset Offset of time to get. + * @param[in] counter RTC value representing time to get. + * + * @return Time calculated from given offset and counter [us]. + */ +static uint64_t time_get(uint32_t offset, uint32_t counter) +{ + return (uint64_t)offset * US_PER_OVERFLOW + ticks_to_time(counter); +} + +/** @brief Get current time. + * + * @return Current time in [us]. + */ +static uint64_t curr_time_get(void) +{ + uint32_t offset; + uint32_t rtc_value; + + offset_and_counter_get(&offset, &rtc_value); + + return time_get(offset, rtc_value); +} + +/** @brief Get current overflow counter and handle OVERFLOW event if present. + * + * This function returns current value of m_overflow_counter variable. If OVERFLOW event is present + * while calling this function, it is handled within it. + * + * @return Current number of OVERFLOW events since platform start. + */ +static uint32_t overflow_counter_get(void) +{ + uint32_t offset; + + // Get mutual access for writing to m_offset_counter variable. + if (mutex_get()) + { + bool increasing = false; + + // Check if interrupt was handled already. + if (nrf_rtc_event_pending(NRF_802154_RTC_INSTANCE, NRF_RTC_EVENT_OVERFLOW)) + { + m_offset_counter++; + increasing = true; + + __DMB(); + + // Mark that interrupt was handled. + nrf_rtc_event_clear(NRF_802154_RTC_INSTANCE, NRF_RTC_EVENT_OVERFLOW); + + // Result should be incremented. m_offset_counter will be incremented after mutex is released. + } + else + { + // Either overflow handling is not needed OR we acquired the mutex just after it was released. + // Overflow is handled after mutex is released, but it cannot be assured that m_offset_counter + // was incremented for the second time, so we increment the result here. + } + + offset = (m_offset_counter + 1) / 2; + + mutex_release(); + + if (increasing) + { + // It's virtually impossible that overflow event is pending again before next instruction is performed. It is an error condition. + assert(m_offset_counter & 0x01); + + // Increment the counter for the second time, to alloww instructions from other context get correct value of the counter. + m_offset_counter++; + } + } + else + { + // Failed to acquire mutex. + if (nrf_rtc_event_pending(NRF_802154_RTC_INSTANCE, NRF_RTC_EVENT_OVERFLOW) || (m_offset_counter & 0x01)) + { + // Lower priority context is currently incrementing m_offset_counter variable. + offset = (m_offset_counter + 2) / 2; + } + else + { + // Lower priority context has already incremented m_offset_counter variable or incrementing is not needed now. + offset = m_offset_counter / 2; + } + } + + return offset; +} + +/** @brief Handle COMPARE event. */ +static void handle_compare_match(bool skip_check) +{ + nrf_rtc_event_clear(NRF_802154_RTC_INSTANCE, m_cmp_ch[LP_TIMER_CHANNEL].event); + + // In case the target time was larger than single overflow, + // we should only strike the timer on final compare event. + if (skip_check || shall_strike(curr_time_get())) + { + nrf_rtc_event_disable(NRF_802154_RTC_INSTANCE, m_cmp_ch[LP_TIMER_CHANNEL].event_mask); + nrf_rtc_int_disable(NRF_802154_RTC_INSTANCE, m_cmp_ch[LP_TIMER_CHANNEL].int_mask); + + nrf_802154_lp_timer_fired(); + } +} + +/** + * @brief Convert t0 and dt to 64 bit time. + * + * @note This function takes into account possible overflow of first 32 bits in current time. + * + * @return Converted time in [us]. + */ +static uint64_t convert_to_64bit_time(uint32_t t0, uint32_t dt, const uint64_t * p_now) +{ + uint64_t now; + + now = *p_now; + + // Check if 32 LSB of `now` overflowed between getting t0 and loading `now` value. + if (((uint32_t)now < t0) && ((t0 - (uint32_t)now) > (UINT32_MAX / 2))) + { + now -= EPOCH_32BIT_US; + } + else if (((uint32_t)now > t0) && (((uint32_t)now) - t0 > (UINT32_MAX / 2))) + { + now += EPOCH_32BIT_US; + } + + return (EPOCH_FROM_TIME(now)) + t0 + dt; +} + +/** + * @brief Round time up to multiple of the timer ticks. + */ +static uint64_t round_up_to_timer_ticks_multiply(uint64_t time) +{ + uint64_t ticks = time_to_ticks(time); + uint64_t result = ticks_to_time(ticks); + return result; +} + +/** + * @brief Start one-shot timer that expires at specified time on desired channel. + * + * Start one-shot timer that will expire @p dt microseconds after @p t0 time on channel @p channel. + * + * @param[in] channel Compare channel on which timer will be started. + * @param[in] t0 Number of microseconds representing timer start time. + * @param[in] dt Time of timer expiration as time elapsed from @p t0 [us]. + * @param[in] p_now Pointer to data with the current time. + */ +static void timer_start_at(compare_channel_t channel, + uint32_t t0, + uint32_t dt, + const uint64_t * p_now) +{ + uint64_t target_counter; + uint64_t target_time; + + nrf_rtc_int_disable(NRF_802154_RTC_INSTANCE, m_cmp_ch[channel].int_mask); + nrf_rtc_event_enable(NRF_802154_RTC_INSTANCE, m_cmp_ch[channel].event_mask); + + target_time = convert_to_64bit_time(t0, dt, p_now); + target_counter = time_to_ticks(target_time); + + m_target_times[channel] = round_up_to_timer_ticks_multiply(target_time); + + nrf_rtc_cc_set(NRF_802154_RTC_INSTANCE, m_cmp_ch[channel].channel, target_counter); +} + +/** + * @brief Start synchronization timer at given time. + * + * @param[in] t0 Number of microseconds representing timer start time. + * @param[in] dt Time of timer expiration as time elapsed from @p t0 [us]. + * @param[in] p_now Pointer to data with current time. + */ +static void timer_sync_start_at(uint32_t t0, uint32_t dt, const uint64_t * p_now) +{ + timer_start_at(SYNC_CHANNEL, t0, dt, p_now); + + nrf_rtc_int_enable(NRF_802154_RTC_INSTANCE, m_cmp_ch[SYNC_CHANNEL].int_mask); +} + +void nrf_802154_lp_timer_init(void) +{ + m_offset_counter = 0; + m_target_times[LP_TIMER_CHANNEL] = 0; + m_clock_ready = false; + + // Setup low frequency clock. + nrf_802154_clock_lfclk_start(); + + while (!m_clock_ready) { } + + // Setup RTC timer. + NVIC_SetPriority(NRF_802154_RTC_IRQN, NRF_802154_RTC_IRQ_PRIORITY); + NVIC_ClearPendingIRQ(NRF_802154_RTC_IRQN); + NVIC_EnableIRQ(NRF_802154_RTC_IRQN); + + nrf_rtc_prescaler_set(NRF_802154_RTC_INSTANCE, 0); + + // Setup RTC events. + nrf_rtc_event_clear(NRF_802154_RTC_INSTANCE, NRF_RTC_EVENT_OVERFLOW); + nrf_rtc_event_enable(NRF_802154_RTC_INSTANCE, RTC_EVTEN_OVRFLW_Msk); + nrf_rtc_int_enable(NRF_802154_RTC_INSTANCE, NRF_RTC_INT_OVERFLOW_MASK); + + nrf_rtc_int_disable(NRF_802154_RTC_INSTANCE, m_cmp_ch[LP_TIMER_CHANNEL].int_mask); + nrf_rtc_event_disable(NRF_802154_RTC_INSTANCE, m_cmp_ch[LP_TIMER_CHANNEL].event_mask); + nrf_rtc_event_clear(NRF_802154_RTC_INSTANCE, m_cmp_ch[LP_TIMER_CHANNEL].event); + + // Start RTC timer. + nrf_rtc_task_trigger(NRF_802154_RTC_INSTANCE, NRF_RTC_TASK_START); +} + +void nrf_802154_lp_timer_deinit(void) +{ + nrf_rtc_task_trigger(NRF_802154_RTC_INSTANCE, NRF_RTC_TASK_STOP); + + nrf_rtc_int_disable(NRF_802154_RTC_INSTANCE, m_cmp_ch[LP_TIMER_CHANNEL].int_mask); + nrf_rtc_event_disable(NRF_802154_RTC_INSTANCE, m_cmp_ch[LP_TIMER_CHANNEL].event_mask); + nrf_rtc_event_clear(NRF_802154_RTC_INSTANCE, m_cmp_ch[LP_TIMER_CHANNEL].event); + + nrf_rtc_int_disable(NRF_802154_RTC_INSTANCE, NRF_RTC_INT_OVERFLOW_MASK); + nrf_rtc_event_disable(NRF_802154_RTC_INSTANCE, RTC_EVTEN_OVRFLW_Msk); + nrf_rtc_event_clear(NRF_802154_RTC_INSTANCE, NRF_RTC_EVENT_OVERFLOW); + + nrf_802154_lp_timer_sync_stop(); + + NVIC_DisableIRQ(NRF_802154_RTC_IRQN); + NVIC_ClearPendingIRQ(NRF_802154_RTC_IRQN); + NVIC_SetPriority(NRF_802154_RTC_IRQN, 0); + + nrf_802154_clock_lfclk_stop(); +} + +void nrf_802154_lp_timer_critical_section_enter(void) +{ + NVIC_DisableIRQ(NRF_802154_RTC_IRQN); +} + +void nrf_802154_lp_timer_critical_section_exit(void) +{ + NVIC_EnableIRQ(NRF_802154_RTC_IRQN); +} + +uint32_t nrf_802154_lp_timer_time_get(void) +{ + return (uint32_t)curr_time_get(); +} + +uint32_t nrf_802154_lp_timer_granularity_get(void) +{ + return NRF_802154_US_PER_TICK; +} + +void nrf_802154_lp_timer_start(uint32_t t0, uint32_t dt) +{ + uint32_t offset; + uint32_t rtc_value; + uint64_t now; + + offset_and_counter_get(&offset, &rtc_value); + now = time_get(offset, rtc_value); + + timer_start_at(LP_TIMER_CHANNEL, t0, dt, &now); + + if (rtc_value != counter_get()) + { + now = curr_time_get(); + } + + if (shall_strike(now + MIN_RTC_COMPARE_EVENT_DT)) + { + handle_compare_match(true); + } + else + { + nrf_rtc_int_enable(NRF_802154_RTC_INSTANCE, m_cmp_ch[LP_TIMER_CHANNEL].int_mask); + } +} + +bool nrf_802154_lp_timer_is_running(void) +{ + return nrf_rtc_int_is_enabled(NRF_802154_RTC_INSTANCE, m_cmp_ch[LP_TIMER_CHANNEL].int_mask); +} + +void nrf_802154_lp_timer_stop(void) +{ + nrf_rtc_event_disable(NRF_802154_RTC_INSTANCE, m_cmp_ch[LP_TIMER_CHANNEL].event_mask); + nrf_rtc_int_disable(NRF_802154_RTC_INSTANCE, m_cmp_ch[LP_TIMER_CHANNEL].int_mask); + nrf_rtc_event_clear(NRF_802154_RTC_INSTANCE, m_cmp_ch[LP_TIMER_CHANNEL].event); +} + +void nrf_802154_lp_timer_sync_start_now(void) +{ + uint32_t counter; + uint32_t offset; + uint64_t now; + + do + { + offset_and_counter_get(&offset, &counter); + now = time_get(offset, counter); + timer_sync_start_at((uint32_t)now, MIN_RTC_COMPARE_EVENT_DT, &now); + } while (counter_get() != counter); +} + +void nrf_802154_lp_timer_sync_start_at(uint32_t t0, uint32_t dt) +{ + uint64_t now = curr_time_get(); + + timer_sync_start_at(t0, dt, &now); +} + +void nrf_802154_lp_timer_sync_stop(void) +{ + nrf_rtc_event_disable(NRF_802154_RTC_INSTANCE, m_cmp_ch[SYNC_CHANNEL].event_mask); + nrf_rtc_int_disable(NRF_802154_RTC_INSTANCE, m_cmp_ch[SYNC_CHANNEL].int_mask); + nrf_rtc_event_clear(NRF_802154_RTC_INSTANCE, m_cmp_ch[SYNC_CHANNEL].event); +} + +uint32_t nrf_802154_lp_timer_sync_event_get(void) +{ + return (uint32_t)nrf_rtc_event_address_get(NRF_802154_RTC_INSTANCE, m_cmp_ch[SYNC_CHANNEL].event); +} + +uint32_t nrf_802154_lp_timer_sync_time_get(void) +{ + return (uint32_t)m_target_times[SYNC_CHANNEL]; +} + +void nrf_802154_clock_lfclk_ready(void) +{ + m_clock_ready = true; +} + +void NRF_802154_RTC_IRQ_HANDLER(void) +{ + // Handle overflow. + if (nrf_rtc_event_pending(NRF_802154_RTC_INSTANCE, NRF_RTC_EVENT_OVERFLOW)) + { + // Disable OVERFLOW interrupt to prevent lock-up in interrupt context while mutex is locked from lower priority context + // and OVERFLOW event flag is stil up. + // OVERFLOW interrupt will be re-enabled when mutex is released - either from this handler, or from lower priority context, + // that locked the mutex. + nrf_rtc_int_disable(NRF_802154_RTC_INSTANCE, NRF_RTC_INT_OVERFLOW_MASK); + + // Handle OVERFLOW event by reading current value of overflow counter. + (void)overflow_counter_get(); + } + + // Handle compare match. + if (nrf_rtc_int_is_enabled(NRF_802154_RTC_INSTANCE, m_cmp_ch[LP_TIMER_CHANNEL].int_mask) && + nrf_rtc_event_pending(NRF_802154_RTC_INSTANCE, m_cmp_ch[LP_TIMER_CHANNEL].event)) + { + handle_compare_match(false); + } + + if (nrf_rtc_int_is_enabled(NRF_802154_RTC_INSTANCE, m_cmp_ch[SYNC_CHANNEL].int_mask) && + nrf_rtc_event_pending(NRF_802154_RTC_INSTANCE, m_cmp_ch[SYNC_CHANNEL].event)) + { + nrf_rtc_event_clear(NRF_802154_RTC_INSTANCE, m_cmp_ch[SYNC_CHANNEL].event); + nrf_rtc_event_disable(NRF_802154_RTC_INSTANCE, m_cmp_ch[SYNC_CHANNEL].event_mask); + nrf_rtc_int_disable(NRF_802154_RTC_INSTANCE, m_cmp_ch[SYNC_CHANNEL].int_mask); + nrf_802154_lp_timer_synchronized(); + } +} + +__WEAK void nrf_802154_lp_timer_synchronized(void) +{ + // Intentionally empty +} diff --git a/third_party/NordicSemiconductor/drivers/radio/platform/timer/nrf_802154_timer_none.c b/third_party/NordicSemiconductor/drivers/radio/platform/lp_timer/nrf_802154_lp_timer_none.c similarity index 90% rename from third_party/NordicSemiconductor/drivers/radio/platform/timer/nrf_802154_timer_none.c rename to third_party/NordicSemiconductor/drivers/radio/platform/lp_timer/nrf_802154_lp_timer_none.c index dcd5508d7..7dd29376f 100644 --- a/third_party/NordicSemiconductor/drivers/radio/platform/timer/nrf_802154_timer_none.c +++ b/third_party/NordicSemiconductor/drivers/radio/platform/lp_timer/nrf_802154_lp_timer_none.c @@ -36,24 +36,24 @@ * */ -#include "nrf_802154_timer.h" +#include "nrf_802154_lp_timer.h" -void nrf_802154_timer_init(void) +void nrf_802154_lp_timer_init(void) { // Intentionally empty } -void nrf_802154_timer_deinit(void) +void nrf_802154_lp_timer_deinit(void) { // Intentionally empty } -void nrf_802154_timer_critical_section_enter(void) +void nrf_802154_lp_timer_critical_section_enter(void) { // Intentionally empty } -void nrf_802154_timer_critical_section_exit(void) +void nrf_802154_lp_timer_critical_section_exit(void) { // Intentionally empty } diff --git a/third_party/NordicSemiconductor/drivers/radio/platform/timer/nrf_802154_timer_nodrv.c b/third_party/NordicSemiconductor/drivers/radio/platform/timer/nrf_802154_timer_nodrv.c deleted file mode 100644 index d0d76f8b3..000000000 --- a/third_party/NordicSemiconductor/drivers/radio/platform/timer/nrf_802154_timer_nodrv.c +++ /dev/null @@ -1,411 +0,0 @@ -/* Copyright (c) 2017 - 2018, Nordic Semiconductor ASA - * 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 Nordic Semiconductor ASA 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 contains standalone implementation of the nRF 802.15.4 timer abstraction. - * - * This implementation is built on top of the RTC peripheral. - * - */ - -#include "nrf_802154_timer.h" - -#include - -#include -#include - -#include "platform/clock/nrf_802154_clock.h" -#include "nrf_802154_config.h" - -#define RTC_COMPARE_CHANNEL 0 -#define RTC_COMPARE_INT_MASK NRF_RTC_INT_COMPARE0_MASK -#define RTC_COMPARE_EVENT NRF_RTC_EVENT_COMPARE_0 -#define RTC_COMPARE_EVENT_MASK RTC_EVTEN_COMPARE0_Msk - -#define RTC_FREQUENCY 32768ULL - -#define US_PER_S 1000000ULL -#define US_PER_TICK CEIL_DIV(US_PER_S, RTC_FREQUENCY) -#define US_PER_OVERFLOW (512UL * US_PER_S) ///< Time that has passed between overflow events. On full RTC speed, it occurs every 512 s. - -#define FREQUENCY_US_PER_S_GDD_BITS 6 ///< Number of bits to shift RTC_FREQUENCY and US_PER_S to achieve division by greatest common divisor. - -#define CEIL_DIV(A, B) (((A) + (B) - 1) / (B)) - -static volatile uint32_t m_offset_counter; ///< Counter of RTC overflows, incremented by 2 on each OVERFLOW event. -static volatile uint8_t m_mutex; ///< Mutex for write access to @ref m_offset_counter. -static volatile bool m_clock_ready; ///< Information that LFCLK is ready. -static uint64_t m_target_time; ///< Timer fire time [us]. - -static uint32_t overflow_counter_get(void); - -/** @brief Non-blocking mutex for mutual write access to @ref m_offset_counter variable. - * - * @retval true Mutex was acquired. - * @retval false Mutex could not be acquired. - */ -static inline bool mutex_get(void) -{ - do - { - volatile uint8_t mutex_value = __LDREXB(&m_mutex); - - if (mutex_value) - { - __CLREX(); - return false; - } - } - while (__STREXB(1, &m_mutex)); - - // Disable OVERFLOW interrupt to prevent lock-up in interrupt context while mutex is locked from lower priority context - // and OVERFLOW event flag is stil up. - nrf_rtc_int_disable(NRF_802154_RTC_INSTANCE, NRF_RTC_INT_OVERFLOW_MASK); - - __DMB(); - - return true; -} - -/** @brief Release mutex. */ -static inline void mutex_release(void) -{ - // Re-enable OVERFLOW interrupt. - nrf_rtc_int_enable(NRF_802154_RTC_INSTANCE, NRF_RTC_INT_OVERFLOW_MASK); - - __DMB(); - m_mutex = 0; -} - -/** @brief Check if timer shall strike. - * - * @param[in] now Current time. - * - * @retval true Timer shall strike now. - * @retval false Timer shall not strike now. - */ -static inline bool shall_strike(uint64_t now) -{ - return now >= m_target_time; -} - -/** @brief Convert time in [us] to RTC ticks. - * - * @param[in] time Time to convert. - * - * @return Time value in RTC ticks. - */ -static inline uint32_t time_to_ticks(uint64_t time) -{ - // Divide the divider and the divident by the greatest common divisor to increase capacity of the multiplication. - return (uint32_t)CEIL_DIV((time * (RTC_FREQUENCY >> FREQUENCY_US_PER_S_GDD_BITS)), - (US_PER_S >> FREQUENCY_US_PER_S_GDD_BITS)) & RTC_CC_COMPARE_Msk; -} - -/** @brief Convert RTC ticks to time in [us]. - * - * @param[in] ticks RTC ticks to convert. - * - * @return Time value in [us]. - */ -static inline uint64_t ticks_to_time(uint32_t ticks) -{ - return CEIL_DIV((US_PER_S * (uint64_t)ticks), RTC_FREQUENCY); -} - -/** @brief Get current time. - * - * @return Current time in [us]. - */ -static uint64_t time_get(void) -{ - uint32_t offset_1 = overflow_counter_get(); - - __DMB(); - - uint32_t rtc_value_1 = nrf_rtc_counter_get(NRF_802154_RTC_INSTANCE); - - __DMB(); - - uint32_t offset_2 = overflow_counter_get(); - - __DMB(); - - uint32_t rtc_value_2 = nrf_rtc_counter_get(NRF_802154_RTC_INSTANCE); - - if (offset_1 == offset_2) - { - return (uint64_t)offset_1 * US_PER_OVERFLOW + ticks_to_time(rtc_value_1); - } - else - { - return (uint64_t)offset_2 * US_PER_OVERFLOW + ticks_to_time(rtc_value_2); - } -} - -/** @brief Get current time plus 2 RTC ticks to prevent RTC compare event miss. - * - * @return Current time with RTC protection in [us]. - */ -static inline uint64_t rtc_protected_time_get(void) -{ - return time_get() + 2 * US_PER_TICK; -} - -/** @brief Get current overflow counter and handle OVERFLOW event if present. - * - * This function returns current value of m_overflow_counter variable. If OVERFLOW event is present - * while calling this function, it is handled within it. - * - * @return Current number of OVERFLOW events since platform start. - */ -static uint32_t overflow_counter_get(void) -{ - uint32_t offset; - - // Get mutual access for writing to m_offset_counter variable. - if (mutex_get()) - { - bool increasing = false; - - // Check if interrupt was handled already. - if (nrf_rtc_event_pending(NRF_802154_RTC_INSTANCE, NRF_RTC_EVENT_OVERFLOW)) - { - m_offset_counter++; - increasing = true; - - __DMB(); - - // Mark that interrupt was handled. - nrf_rtc_event_clear(NRF_802154_RTC_INSTANCE, NRF_RTC_EVENT_OVERFLOW); - - // Result should be incremented. m_offset_counter will be incremented after mutex is released. - } - else - { - // Either overflow handling is not needed OR we acquired the mutex just after it was released. - // Overflow is handled after mutex is released, but it cannot be assured that m_offset_counter - // was incremented for the second time, so we increment the result here. - } - - offset = (m_offset_counter + 1) / 2; - - mutex_release(); - - if (increasing) - { - // It's virtually impossible that overflow event is pending again before next instruction is performed. It is an error condition. - assert(m_offset_counter & 0x01); - - // Increment the counter for the second time, to alloww instructions from other context get correct value of the counter. - m_offset_counter++; - } - } - else - { - // Failed to acquire mutex. - if (nrf_rtc_event_pending(NRF_802154_RTC_INSTANCE, NRF_RTC_EVENT_OVERFLOW) || (m_offset_counter & 0x01)) - { - // Lower priority context is currently incrementing m_offset_counter variable. - offset = (m_offset_counter + 2) / 2; - } - else - { - // Lower priority context has already incremented m_offset_counter variable or incrementing is not needed now. - offset = m_offset_counter / 2; - } - } - - return offset; -} - -/** @brief Handle COMPARE event. */ -static void handle_compare_match(bool skip_check) -{ - nrf_rtc_event_clear(NRF_802154_RTC_INSTANCE, RTC_COMPARE_EVENT); - - // In case the target time was larger than single overflow, - // we should only strike the timer on final compare event. - if (skip_check || shall_strike(time_get())) - { - nrf_rtc_event_disable(NRF_802154_RTC_INSTANCE, RTC_COMPARE_EVENT_MASK); - nrf_rtc_int_disable(NRF_802154_RTC_INSTANCE, RTC_COMPARE_INT_MASK); - - nrf_802154_timer_fired(); - } -} - -void nrf_802154_timer_init(void) -{ - m_offset_counter = 0; - m_target_time = 0; - m_clock_ready = false; - - // Setup low frequency clock. - nrf_802154_clock_lfclk_start(); - - while (!m_clock_ready) { } - - // Setup RTC timer. - NVIC_SetPriority(NRF_802154_RTC_IRQN, NRF_802154_RTC_IRQ_PRIORITY); - NVIC_ClearPendingIRQ(NRF_802154_RTC_IRQN); - NVIC_EnableIRQ(NRF_802154_RTC_IRQN); - - nrf_rtc_prescaler_set(NRF_802154_RTC_INSTANCE, 0); - - // Setup RTC events. - nrf_rtc_event_clear(NRF_802154_RTC_INSTANCE, NRF_RTC_EVENT_OVERFLOW); - nrf_rtc_event_enable(NRF_802154_RTC_INSTANCE, RTC_EVTEN_OVRFLW_Msk); - nrf_rtc_int_enable(NRF_802154_RTC_INSTANCE, NRF_RTC_INT_OVERFLOW_MASK); - - nrf_rtc_int_disable(NRF_802154_RTC_INSTANCE, RTC_COMPARE_INT_MASK); - nrf_rtc_event_disable(NRF_802154_RTC_INSTANCE, RTC_COMPARE_EVENT_MASK); - nrf_rtc_event_clear(NRF_802154_RTC_INSTANCE, RTC_COMPARE_EVENT); - - // Start RTC timer. - nrf_rtc_task_trigger(NRF_802154_RTC_INSTANCE, NRF_RTC_TASK_START); -} - -void nrf_802154_timer_deinit(void) -{ - nrf_rtc_task_trigger(NRF_802154_RTC_INSTANCE, NRF_RTC_TASK_STOP); - - nrf_rtc_int_disable(NRF_802154_RTC_INSTANCE, RTC_COMPARE_INT_MASK); - nrf_rtc_event_disable(NRF_802154_RTC_INSTANCE, RTC_COMPARE_EVENT_MASK); - nrf_rtc_event_clear(NRF_802154_RTC_INSTANCE, RTC_COMPARE_EVENT); - - nrf_rtc_int_disable(NRF_802154_RTC_INSTANCE, NRF_RTC_INT_OVERFLOW_MASK); - nrf_rtc_event_disable(NRF_802154_RTC_INSTANCE, RTC_EVTEN_OVRFLW_Msk); - nrf_rtc_event_clear(NRF_802154_RTC_INSTANCE, NRF_RTC_EVENT_OVERFLOW); - - NVIC_DisableIRQ(NRF_802154_RTC_IRQN); - NVIC_ClearPendingIRQ(NRF_802154_RTC_IRQN); - NVIC_SetPriority(NRF_802154_RTC_IRQN, 0); - - nrf_802154_clock_lfclk_stop(); -} - -void nrf_802154_timer_critical_section_enter(void) -{ - NVIC_DisableIRQ(NRF_802154_RTC_IRQN); - __DSB(); - __ISB(); -} - -void nrf_802154_timer_critical_section_exit(void) -{ - NVIC_EnableIRQ(NRF_802154_RTC_IRQN); -} - -uint32_t nrf_802154_timer_time_get(void) -{ - return (uint32_t)time_get(); -} - -uint32_t nrf_802154_timer_granularity_get(void) -{ - return US_PER_TICK; -} - -void nrf_802154_timer_start(uint32_t t0, uint32_t dt) -{ - uint64_t now; - uint32_t target_counter; - - nrf_rtc_int_disable(NRF_802154_RTC_INSTANCE, RTC_COMPARE_INT_MASK); - nrf_rtc_event_enable(NRF_802154_RTC_INSTANCE, RTC_COMPARE_EVENT_MASK); - - now = time_get(); - - // Check if 32 LSB of `now` overflowed between getting t0 and loading `now` value. - if ((uint32_t)now < t0) - { - now -= 0x0000000100000000; - } - - m_target_time = (now & 0xffffffff00000000) + t0 + dt; - - target_counter = time_to_ticks(m_target_time); - - nrf_rtc_cc_set(NRF_802154_RTC_INSTANCE, RTC_COMPARE_CHANNEL, target_counter); - - now = rtc_protected_time_get(); - - if (shall_strike(now)) - { - handle_compare_match(true); - } - else - { - nrf_rtc_int_enable(NRF_802154_RTC_INSTANCE, RTC_COMPARE_INT_MASK); - } -} - -bool nrf_802154_timer_is_running(void) -{ - return nrf_rtc_int_is_enabled(NRF_802154_RTC_INSTANCE, RTC_COMPARE_INT_MASK); -} - -void nrf_802154_timer_stop(void) -{ - nrf_rtc_event_disable(NRF_802154_RTC_INSTANCE, RTC_COMPARE_EVENT_MASK); - nrf_rtc_int_disable(NRF_802154_RTC_INSTANCE, RTC_COMPARE_INT_MASK); - nrf_rtc_event_clear(NRF_802154_RTC_INSTANCE, RTC_COMPARE_EVENT); -} - -void nrf_802154_clock_lfclk_ready(void) -{ - m_clock_ready = true; -} - -void NRF_802154_RTC_IRQ_HANDLER(void) -{ - // Handle overflow. - if (nrf_rtc_event_pending(NRF_802154_RTC_INSTANCE, NRF_RTC_EVENT_OVERFLOW)) - { - // Disable OVERFLOW interrupt to prevent lock-up in interrupt context while mutex is locked from lower priority context - // and OVERFLOW event flag is stil up. - // OVERFLOW interrupt will be re-enabled when mutex is released - either from this handler, or from lower priority context, - // that locked the mutex. - nrf_rtc_int_disable(NRF_802154_RTC_INSTANCE, NRF_RTC_INT_OVERFLOW_MASK); - - // Handle OVERFLOW event by reading current value of overflow counter. - (void)overflow_counter_get(); - } - - // Handle compare match. - if (nrf_rtc_int_is_enabled(NRF_802154_RTC_INSTANCE, RTC_COMPARE_INT_MASK) && - nrf_rtc_event_pending(NRF_802154_RTC_INSTANCE, RTC_COMPARE_EVENT)) - { - handle_compare_match(false); - } -} diff --git a/third_party/NordicSemiconductor/drivers/radio/raal/nrf_raal_api.h b/third_party/NordicSemiconductor/drivers/radio/raal/nrf_raal_api.h index 606ec3091..82573c22a 100644 --- a/third_party/NordicSemiconductor/drivers/radio/raal/nrf_raal_api.h +++ b/third_party/NordicSemiconductor/drivers/radio/raal/nrf_raal_api.h @@ -107,14 +107,6 @@ void nrf_raal_continuous_mode_exit(void); */ bool nrf_raal_timeslot_request(uint32_t length_us); -/** - * @brief Check if the 802.15.4 driver is currently in timeslot. - * - * @retval TRUE Timeslot is currently granted. - * @retval FALSE Timeslot is not currently granted. - */ -bool nrf_raal_timeslot_is_granted(void); - /** * @brief Get left time of currently granted timeslot [us]. * diff --git a/third_party/NordicSemiconductor/drivers/radio/raal/simulator/nrf_raal_simulator.c b/third_party/NordicSemiconductor/drivers/radio/raal/simulator/nrf_raal_simulator.c new file mode 100644 index 000000000..dae0438b5 --- /dev/null +++ b/third_party/NordicSemiconductor/drivers/radio/raal/simulator/nrf_raal_simulator.c @@ -0,0 +1,246 @@ +/* Copyright (c) 2017 - 2018, Nordic Semiconductor ASA + * 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 Nordic Semiconductor ASA 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 implements the nrf 802.15.4 simulated radio arbiter. + * + * This arbiter should be used for testing driver and tweaking other arbiters. + * + */ + +#include "nrf_raal_api.h" + +#include +#include +#include + +#include "nrf.h" +#include "nrf_802154_debug.h" + +static bool m_continuous_requested; +static bool m_continuous_granted; + +static uint16_t m_time_interval = 250; // ms +static uint16_t m_ble_duty = 10; // ms +static uint16_t m_pre_preemption_notification = 150; // us + +static uint32_t m_ended_timestamp; +static uint32_t m_started_timestamp; +static uint32_t m_margin_timestamp; + +static void continuous_grant(void) +{ + if (m_continuous_requested && !m_continuous_granted) + { + nrf_802154_pin_set(PIN_DBG_TIMESLOT_ACTIVE); + m_continuous_granted = true; + nrf_raal_timeslot_started(); + } +} + +static void continuous_revoke(void) +{ + if (m_continuous_requested && m_continuous_granted) + { + nrf_802154_pin_clr(PIN_DBG_TIMESLOT_ACTIVE); + m_continuous_granted = false; + nrf_raal_timeslot_ended(); + } +} + +static uint32_t time_get(void) +{ + NRF_TIMER0->TASKS_CAPTURE[1] = 1; + return NRF_TIMER0->CC[1]; +} + +void nrf_raal_init(void) +{ + m_ended_timestamp = m_time_interval * 1000UL; + m_started_timestamp = m_ble_duty * 1000UL; + m_margin_timestamp = (m_time_interval * 1000UL) - m_pre_preemption_notification; + + NRF_MWU->PREGION[0].SUBS = 0x00000002; + NRF_MWU->INTENSET = MWU_INTENSET_PREGION0WA_Msk | MWU_INTENSET_PREGION0RA_Msk; + + NVIC_SetPriority(MWU_IRQn, 0); + NVIC_ClearPendingIRQ(MWU_IRQn); + NVIC_EnableIRQ(MWU_IRQn); + + NRF_TIMER0->MODE = TIMER_MODE_MODE_Timer; + NRF_TIMER0->BITMODE = TIMER_BITMODE_BITMODE_24Bit; + NRF_TIMER0->PRESCALER = 4; + NRF_TIMER0->INTENSET = TIMER_INTENSET_COMPARE0_Msk; + NRF_TIMER0->CC[0] = m_started_timestamp; + + NVIC_SetPriority(TIMER0_IRQn, 1); + NVIC_ClearPendingIRQ(TIMER0_IRQn); + NVIC_EnableIRQ(TIMER0_IRQn); + + m_continuous_requested = false; + + NRF_TIMER0->TASKS_START = 1; +} + +void nrf_raal_uninit(void) +{ + // Intentionally empty. +} + +void nrf_raal_continuous_mode_enter(void) +{ + uint32_t time; + + nrf_802154_log(EVENT_TRACE_ENTER, FUNCTION_RAAL_CONTINUOUS_ENTER); + + assert(!m_continuous_requested); + + m_continuous_requested = true; + + NVIC_DisableIRQ(TIMER0_IRQn); + __DSB(); + __ISB(); + + time = time_get(); + + if ((time >= m_started_timestamp) && (time < m_margin_timestamp)) + { + continuous_grant(); + } + + NVIC_EnableIRQ(TIMER0_IRQn); + + nrf_802154_log(EVENT_TRACE_EXIT, FUNCTION_RAAL_CONTINUOUS_ENTER); +} + +void nrf_raal_continuous_mode_exit(void) +{ + nrf_802154_log(EVENT_TRACE_ENTER, FUNCTION_RAAL_CONTINUOUS_EXIT); + + assert(m_continuous_requested); + + m_continuous_requested = false; + m_continuous_granted = false; + + nrf_802154_pin_clr(PIN_DBG_TIMESLOT_ACTIVE); + + nrf_802154_log(EVENT_TRACE_EXIT, FUNCTION_RAAL_CONTINUOUS_EXIT); +} + +bool nrf_raal_timeslot_request(uint32_t length_us) +{ + uint32_t timer; + + assert(m_continuous_requested); + + if (!m_continuous_granted) + { + return false; + } + + timer = time_get(); + + return (timer >= m_started_timestamp) && ((timer + length_us) < m_margin_timestamp); +} + +uint32_t nrf_raal_timeslot_us_left_get(void) +{ + uint32_t timer = time_get(); + + return ((timer >= m_started_timestamp) && (timer < m_margin_timestamp)) ? + (m_margin_timestamp - timer) : 0; +} + +void TIMER0_IRQHandler(void) +{ + uint32_t ev_timestamp; + + nrf_802154_log(EVENT_TRACE_ENTER, FUNCTION_RAAL_SIG_HANDLER); + + if (NRF_TIMER0->EVENTS_COMPARE[0]) + { + while (time_get() >= NRF_TIMER0->CC[0]) + { + NRF_TIMER0->EVENTS_COMPARE[0] = 0; + + ev_timestamp = NRF_TIMER0->CC[0]; + + if (ev_timestamp == m_ended_timestamp) + { + nrf_802154_log(EVENT_TRACE_ENTER, FUNCTION_RAAL_SIG_EVENT_ENDED); + + NRF_MWU->REGIONENSET = MWU_REGIONENSET_PRGN0WA_Msk | MWU_REGIONENSET_PRGN0RA_Msk; + + NRF_TIMER0->TASKS_STOP = 1; + NRF_TIMER0->TASKS_CLEAR = 1; + + NRF_TIMER0->CC[0] = m_started_timestamp; + + NRF_TIMER0->TASKS_START = 1; + + nrf_802154_log(EVENT_TRACE_EXIT, FUNCTION_RAAL_SIG_EVENT_ENDED); + } + else if (ev_timestamp == m_started_timestamp) + { + nrf_802154_log(EVENT_TRACE_ENTER, FUNCTION_RAAL_SIG_EVENT_START); + + NRF_MWU->REGIONENCLR = MWU_REGIONENCLR_PRGN0WA_Msk | MWU_REGIONENCLR_PRGN0RA_Msk; + + NRF_TIMER0->CC[0] = m_margin_timestamp; + + continuous_grant(); + + nrf_802154_log(EVENT_TRACE_EXIT, FUNCTION_RAAL_SIG_EVENT_START); + } + else if (ev_timestamp == m_margin_timestamp) + { + nrf_802154_log(EVENT_TRACE_ENTER, FUNCTION_RAAL_SIG_EVENT_MARGIN); + + NRF_TIMER0->CC[0] = m_ended_timestamp; + + continuous_revoke(); + + nrf_802154_log(EVENT_TRACE_EXIT, FUNCTION_RAAL_SIG_EVENT_MARGIN); + } + else + { + assert(false); + } + } + } + + nrf_802154_log(EVENT_TRACE_EXIT, FUNCTION_RAAL_SIG_HANDLER); +} + +void MWU_IRQHandler(void) +{ + assert(false); +} diff --git a/third_party/NordicSemiconductor/drivers/radio/raal/single_phy/single_phy.c b/third_party/NordicSemiconductor/drivers/radio/raal/single_phy/single_phy.c index 6e9937429..2067586b2 100644 --- a/third_party/NordicSemiconductor/drivers/radio/raal/single_phy/single_phy.c +++ b/third_party/NordicSemiconductor/drivers/radio/raal/single_phy/single_phy.c @@ -42,11 +42,7 @@ #include #include -#include "platform/clock/nrf_802154_clock.h" - -static bool m_continuous; -static volatile bool m_critical_section; -static volatile bool m_started_pending; +static bool m_continuous; void nrf_raal_init(void) { @@ -62,8 +58,8 @@ void nrf_raal_continuous_mode_enter(void) { assert(!m_continuous); - nrf_802154_clock_hfclk_start(); m_continuous = true; + nrf_raal_timeslot_started(); } void nrf_raal_continuous_mode_exit(void) @@ -71,7 +67,6 @@ void nrf_raal_continuous_mode_exit(void) assert(m_continuous); m_continuous = false; - nrf_802154_clock_hfclk_stop(); } bool nrf_raal_timeslot_request(uint32_t length_us) @@ -83,40 +78,8 @@ bool nrf_raal_timeslot_request(uint32_t length_us) return true; } -bool nrf_raal_timeslot_is_granted(void) -{ - return true; -} - uint32_t nrf_raal_timeslot_us_left_get(void) { return UINT32_MAX; } -void nrf_raal_critical_section_enter(void) -{ - m_critical_section = true; -} - -void nrf_raal_critical_section_exit(void) -{ - m_critical_section = false; - - if (m_started_pending) - { - nrf_raal_timeslot_started(); - m_started_pending = false; - } -} - -void nrf_802154_clock_hfclk_ready(void) -{ - if (m_critical_section) - { - m_started_pending = true; - } - else - { - nrf_raal_timeslot_started(); - } -} diff --git a/third_party/NordicSemiconductor/drivers/radio/raal/softdevice/nrf_raal_softdevice.c b/third_party/NordicSemiconductor/drivers/radio/raal/softdevice/nrf_raal_softdevice.c index 07854641c..bbdc1aae2 100644 --- a/third_party/NordicSemiconductor/drivers/radio/raal/softdevice/nrf_raal_softdevice.c +++ b/third_party/NordicSemiconductor/drivers/radio/raal/softdevice/nrf_raal_softdevice.c @@ -46,7 +46,6 @@ #include #include #include -#include #if defined(__GNUC__) _Pragma("GCC diagnostic push") @@ -78,30 +77,14 @@ #define TIMER_TO_SIGNAL_JITTER_US NRF_RADIO_START_JITTER_US + 6 /**@brief Timer compare channel definitions. */ -#define TIMER_CC_EXTEND NRF_TIMER_CC_CHANNEL0 -#define TIMER_CC_EXTEND_EVENT NRF_TIMER_EVENT_COMPARE0 -#define TIMER_CC_EXTEND_INT NRF_TIMER_INT_COMPARE0_MASK +#define TIMER_CC_ACTION NRF_TIMER_CC_CHANNEL0 +#define TIMER_CC_ACTION_EVENT NRF_TIMER_EVENT_COMPARE0 +#define TIMER_CC_ACTION_INT NRF_TIMER_INT_COMPARE0_MASK -#define TIMER_CC_MARGIN NRF_TIMER_CC_CHANNEL1 -#define TIMER_CC_MARGIN_EVENT NRF_TIMER_EVENT_COMPARE1 -#define TIMER_CC_MARGIN_INT NRF_TIMER_INT_COMPARE1_MASK +#define TIMER_CC_CAPTURE NRF_TIMER_CC_CHANNEL1 +#define TIMER_CC_CAPTURE_TASK NRF_TIMER_TASK_CAPTURE1 -#define TIMER_CC_CAPTURE NRF_TIMER_CC_CHANNEL2 -#define TIMER_CC_CAPTURE_TASK NRF_TIMER_TASK_CAPTURE2 - -/**@brief Defines number of microseconds in one second. */ -#define US_PER_S 1000000 - -/**@brief Ceil division helper */ -#define DIVIDE_AND_CEIL(A, B) (((A) + (B) - 1) / (B)) - -/**@brief Defines pending events. */ -typedef enum -{ - PENDING_EVENT_NONE = 0, - PENDING_EVENT_STARTED, - PENDING_EVENT_ENDED -} pending_events_t; +#define NRF_RADIO_MINIMUM_TIMESLOT_LENGTH_EXTENSION_TIME_TICKS NRF_802154_US_TO_RTC_TICKS(NRF_RADIO_MINIMUM_TIMESLOT_LENGTH_EXTENSION_TIME_US) /**@brief Defines states of timeslot. */ typedef enum @@ -111,6 +94,13 @@ typedef enum TIMESLOT_STATE_GRANTED } timeslot_state_t; +/**@brief Define timer actions. */ +typedef enum +{ + TIMER_ACTION_EXTEND, + TIMER_ACTION_MARGIN, +} timer_action_t; + /*************************************************************************************************** * @section Static variables. **************************************************************************************************/ @@ -133,21 +123,15 @@ static volatile bool m_continuous = false; /**@brief Defines if RAAL is currently in a timeslot. */ static volatile timeslot_state_t m_timeslot_state; +/**@brief Current action of the timer. */ +static timer_action_t m_timer_action; + /**@brief Current timeslot length. */ static uint16_t m_timeslot_length; /**@brief Number of already performed extentions tries on failed event. */ static volatile uint16_t m_timeslot_extend_tries; -/**@breif Defines if Radio Driver entered critical section. */ -static volatile bool m_in_critical_section = false; - -/**@brief Defines current pending event. */ -static volatile pending_events_t m_pending_event = PENDING_EVENT_NONE; - -/**@brief Defines RTC0 counter value on timeslot begin. */ -static uint32_t m_start_rtc_ticks = 0; - /*************************************************************************************************** * @section Operations on RAAL TIMER. **************************************************************************************************/ @@ -155,12 +139,11 @@ static uint32_t m_start_rtc_ticks = 0; /**@brief Set timer on timeslot started. */ static void timer_start(void) { + m_timer_action = TIMER_ACTION_EXTEND; nrf_timer_task_trigger(RAAL_TIMER, NRF_TIMER_TASK_STOP); nrf_timer_task_trigger(RAAL_TIMER, NRF_TIMER_TASK_CLEAR); nrf_timer_bit_width_set(RAAL_TIMER, NRF_TIMER_BIT_WIDTH_32); - nrf_timer_int_enable(RAAL_TIMER, TIMER_CC_MARGIN_INT); - nrf_timer_cc_write(RAAL_TIMER, TIMER_CC_EXTEND, 0); - nrf_timer_cc_write(RAAL_TIMER, TIMER_CC_MARGIN, m_timeslot_length - m_config.timeslot_safe_margin); + nrf_timer_cc_write(RAAL_TIMER, TIMER_CC_ACTION, 0); nrf_timer_task_trigger(RAAL_TIMER, NRF_TIMER_TASK_START); NVIC_EnableIRQ(RAAL_TIMER_IRQn); @@ -170,8 +153,7 @@ static void timer_start(void) static void timer_reset(void) { nrf_timer_task_trigger(RAAL_TIMER, NRF_TIMER_TASK_STOP); - nrf_timer_event_clear(RAAL_TIMER, TIMER_CC_EXTEND_EVENT); - nrf_timer_event_clear(RAAL_TIMER, TIMER_CC_MARGIN_EVENT); + nrf_timer_event_clear(RAAL_TIMER, TIMER_CC_ACTION_EVENT); NVIC_ClearPendingIRQ(RAAL_TIMER_IRQn); } @@ -182,16 +164,62 @@ static inline uint32_t timer_time_get(void) return nrf_timer_cc_read(RAAL_TIMER, TIMER_CC_CAPTURE); } +/**@brief Check if timer is set to margin. + * + * @retval true Timer action CC is set to the margin action. + * @retval false Timer action CC is set to the extend action. + */ +static inline bool timer_is_set_to_margin(void) +{ + return m_timer_action == TIMER_ACTION_MARGIN; +} + +static inline uint32_t ticks_to_timeslot_end_get(void) +{ + uint32_t cc = NRF_RTC0->CC[1]; + uint32_t counter = NRF_RTC0->COUNTER; + + // We add one tick as RTC might be just about to increment COUNTER value. + return (cc - (counter + 1)) & RTC_COUNTER_COUNTER_Msk; +} + +static inline uint32_t safe_time_to_timeslot_end_get(void) +{ + uint32_t margin = m_config.timeslot_safe_margin + TIMER_TO_SIGNAL_JITTER_US; + uint32_t timeslot_end = NRF_802154_RTC_TICKS_TO_US(ticks_to_timeslot_end_get()); + + if (timeslot_end > margin) + { + return timeslot_end - margin; + } + else + { + return 0; + } +} + +/**@brief Get timeslot margin. */ +static uint32_t timer_get_cc_margin(void) +{ + return timer_time_get() + safe_time_to_timeslot_end_get(); +} + +/**@brief Set timer action to the timeslot margin. */ +static inline void timer_to_margin_set(void) +{ + uint32_t margin_cc = timer_get_cc_margin(); + + m_timer_action = TIMER_ACTION_MARGIN; + + nrf_timer_event_clear(RAAL_TIMER, TIMER_CC_ACTION_EVENT); + nrf_timer_cc_write(RAAL_TIMER, TIMER_CC_ACTION, margin_cc); + nrf_timer_int_enable(RAAL_TIMER, TIMER_CC_ACTION_INT); +} + /**@brief Check if margin is already reached. */ static inline bool timer_is_margin_reached(void) { - return nrf_timer_event_check(RAAL_TIMER, TIMER_CC_MARGIN_EVENT); -} - -/**@brief Calculate maximal crystal drift. */ -static inline uint32_t timer_rtc_drift_calculate(uint32_t timeslot_length) -{ - return DIVIDE_AND_CEIL(((uint64_t)timeslot_length * m_config.lf_clk_accuracy_ppm), US_PER_S); + return timer_is_set_to_margin() && nrf_timer_event_check(RAAL_TIMER, TIMER_CC_ACTION_EVENT); } /**@brief Set timer on extend event. */ @@ -199,61 +227,18 @@ static void timer_on_extend_update(void) { NVIC_ClearPendingIRQ(RAAL_TIMER_IRQn); - nrf_timer_cc_write(RAAL_TIMER, TIMER_CC_MARGIN, - nrf_timer_cc_read(RAAL_TIMER, TIMER_CC_MARGIN) + m_timeslot_length); - - if (m_timeslot_extend_tries == 0) + if (timer_is_set_to_margin()) { - nrf_timer_cc_write(RAAL_TIMER, TIMER_CC_EXTEND, - nrf_timer_cc_read(RAAL_TIMER, TIMER_CC_EXTEND) + m_timeslot_length); - nrf_timer_int_enable(RAAL_TIMER, TIMER_CC_EXTEND_INT); - } -} - -/**@brief Eliminate timers jitters. */ -static void timer_jitter_adjust(void) -{ - // Adjust TIMER0 and RTC0 clocks drifts. - uint32_t timer_ticks = timer_time_get(); - uint64_t rtc_ticks = NRF_RTC0->COUNTER; - - if (rtc_ticks > m_start_rtc_ticks) - { - rtc_ticks -= m_start_rtc_ticks; + uint32_t margin_cc = nrf_timer_cc_read(RAAL_TIMER, TIMER_CC_ACTION); + margin_cc += m_timeslot_length; + nrf_timer_cc_write(RAAL_TIMER, TIMER_CC_ACTION, margin_cc); } else { - // Overflow detected. - rtc_ticks = RTC_COUNTER_COUNTER_Msk - m_start_rtc_ticks + rtc_ticks; + nrf_timer_cc_write(RAAL_TIMER, TIMER_CC_ACTION, + nrf_timer_cc_read(RAAL_TIMER, TIMER_CC_ACTION) + m_timeslot_length); + nrf_timer_int_enable(RAAL_TIMER, TIMER_CC_ACTION_INT); } - - // Adjust RTC0 ticks to TIMER0 resolution. RTC0 works with 32768kHz so first - // multiply with 10^6 (microseconds) and divide by 32768Hz (2^15) to get microseconds. - rtc_ticks = DIVIDE_AND_CEIL((rtc_ticks * US_PER_S), 32768); - - // Check if we are still in time. - uint32_t cc_margin = nrf_timer_cc_read(RAAL_TIMER, TIMER_CC_MARGIN); - assert(cc_margin > rtc_ticks + timer_rtc_drift_calculate(cc_margin)); - - if (rtc_ticks > timer_ticks) - { - nrf_timer_cc_write(RAAL_TIMER, - TIMER_CC_MARGIN, - cc_margin - (rtc_ticks - timer_ticks)); - } - else - { - nrf_timer_cc_write(RAAL_TIMER, - TIMER_CC_MARGIN, - cc_margin + (rtc_ticks - timer_ticks)); - } - - // Add safety drift time. - uint32_t safety_drift_time = timer_rtc_drift_calculate(2 * m_config.timeslot_length) + - TIMER_TO_SIGNAL_JITTER_US; - nrf_timer_cc_write(RAAL_TIMER, - TIMER_CC_MARGIN, - nrf_timer_cc_read(RAAL_TIMER, TIMER_CC_MARGIN) - safety_drift_time); } /*************************************************************************************************** @@ -279,20 +264,6 @@ static inline bool timeslot_is_granted(void) return (m_timeslot_state == TIMESLOT_STATE_GRANTED); } -/**@brief Enter timeslot critical section. */ -static inline void timeslot_critical_section_enter(void) -{ - NVIC_DisableIRQ(RAAL_TIMER_IRQn); - __DSB(); - __ISB(); -} - -/**@brief Exit timeslot critical section. */ -static inline void timeslot_critical_section_exit(void) -{ - NVIC_EnableIRQ(RAAL_TIMER_IRQn); -} - /**@brief Notify driver that timeslot has been started. */ static inline void timeslot_started_notify(void) { @@ -370,11 +341,6 @@ static void timeslot_next_extend(void) // Try to extend right after start. timeslot_extend(m_timeslot_length); } - else - { - // Adjust possible clock jitters and wait for safety margin. - timer_jitter_adjust(); - } } /*************************************************************************************************** @@ -384,72 +350,58 @@ static void timeslot_next_extend(void) /**@brief Handle timer interrupts. */ static void timer_irq_handle(void) { - // Safe margin exceeded. - if (nrf_timer_event_check(RAAL_TIMER, TIMER_CC_MARGIN_EVENT)) + // Margin or extend event triggered. + if (nrf_timer_event_check(RAAL_TIMER, TIMER_CC_ACTION_EVENT)) { - nrf_802154_pin_clr(PIN_DBG_TIMESLOT_ACTIVE); - nrf_802154_log(EVENT_TRACE_ENTER, FUNCTION_RAAL_SIG_EVENT_MARGIN); - - m_timeslot_state = TIMESLOT_STATE_IDLE; - - if (m_in_critical_section) + if (timer_is_set_to_margin()) { - assert(m_pending_event != PENDING_EVENT_ENDED); + // Safe margin exceeded. + nrf_802154_pin_clr(PIN_DBG_TIMESLOT_ACTIVE); + nrf_802154_log(EVENT_TRACE_ENTER, FUNCTION_RAAL_SIG_EVENT_MARGIN); - if (m_pending_event == PENDING_EVENT_STARTED) + m_timeslot_state = TIMESLOT_STATE_IDLE; + timeslot_ended_notify(); + + // Ignore any other events. + timer_reset(); + + #if (ENABLE_REQUEST_AND_END_ON_TIMESLOT_END == 1) + timeslot_data_init(); + timeslot_request_prepare(); + m_ret_param.callback_action = NRF_RADIO_SIGNAL_CALLBACK_ACTION_REQUEST_AND_END; + m_ret_param.params.request.p_next = &m_request; + #else + // Return and wait for NRF_EVT_RADIO_SESSION_IDLE event. + m_ret_param.callback_action = NRF_RADIO_SIGNAL_CALLBACK_ACTION_NONE; + #endif + + nrf_802154_log(EVENT_TRACE_EXIT, FUNCTION_RAAL_SIG_EVENT_MARGIN); + } + else + { + // Extension margin exceeded. + nrf_802154_log(EVENT_TRACE_ENTER, FUNCTION_RAAL_SIG_EVENT_EXTEND); + + nrf_timer_int_disable(RAAL_TIMER, TIMER_CC_ACTION_INT); + nrf_timer_event_clear(RAAL_TIMER, TIMER_CC_ACTION_EVENT); + + if (m_continuous && + (nrf_timer_cc_read(RAAL_TIMER, TIMER_CC_ACTION) + + m_config.timeslot_length < m_config.timeslot_max_length)) { - m_pending_event = PENDING_EVENT_NONE; + // Try to extend timeslot. + timeslot_extend(m_config.timeslot_length); } else { - m_pending_event = PENDING_EVENT_ENDED; + // We have reached maximum timeslot length. + timer_to_margin_set(); + + m_ret_param.callback_action = NRF_RADIO_SIGNAL_CALLBACK_ACTION_NONE; } + + nrf_802154_log(EVENT_TRACE_EXIT, FUNCTION_RAAL_SIG_EVENT_EXTEND); } - else - { - timeslot_ended_notify(); - } - - // Ignore any other events. - timer_reset(); - -#if (ENABLE_REQUEST_AND_END_ON_TIMESLOT_END == 1) - timeslot_data_init(); - timeslot_request_prepare(); - m_ret_param.callback_action = NRF_RADIO_SIGNAL_CALLBACK_ACTION_REQUEST_AND_END; - m_ret_param.params.request.p_next = &m_request; -#else - // Return and wait for NRF_EVT_RADIO_SESSION_IDLE event. - m_ret_param.callback_action = NRF_RADIO_SIGNAL_CALLBACK_ACTION_NONE; -#endif - - nrf_802154_log(EVENT_TRACE_EXIT, FUNCTION_RAAL_SIG_EVENT_MARGIN); - } - - // Extension margin exceeded. - else if (nrf_timer_event_check(RAAL_TIMER, TIMER_CC_EXTEND_EVENT)) - { - nrf_802154_log(EVENT_TRACE_ENTER, FUNCTION_RAAL_SIG_EVENT_EXTEND); - - nrf_timer_int_disable(RAAL_TIMER, TIMER_CC_EXTEND_INT); - nrf_timer_event_clear(RAAL_TIMER, TIMER_CC_EXTEND_EVENT); - - if (m_continuous && - (nrf_timer_cc_read(RAAL_TIMER, TIMER_CC_EXTEND) + - m_config.timeslot_length < m_config.timeslot_max_length)) - { - // Try to extend timeslot. - timeslot_extend(m_config.timeslot_length); - } - else - { - // We have reached maximum timeslot length. - timer_jitter_adjust(); - - m_ret_param.callback_action = NRF_RADIO_SIGNAL_CALLBACK_ACTION_NONE; - } - - nrf_802154_log(EVENT_TRACE_EXIT, FUNCTION_RAAL_SIG_EVENT_EXTEND); } else { @@ -475,8 +427,7 @@ static nrf_radio_signal_callback_return_param_t *signal_handler(uint8_t signal_t nrf_802154_pin_clr(PIN_DBG_TIMESLOT_ACTIVE); nrf_802154_log(EVENT_TRACE_ENTER, FUNCTION_RAAL_SIG_EVENT_ENDED); - m_pending_event = PENDING_EVENT_NONE; - m_timeslot_state = TIMESLOT_STATE_IDLE; + m_timeslot_state = TIMESLOT_STATE_IDLE; // TODO: Change to NRF_RADIO_SIGNAL_CALLBACK_ACTION_END (KRKNWK-937) m_ret_param.callback_action = NRF_RADIO_SIGNAL_CALLBACK_ACTION_NONE; @@ -494,12 +445,9 @@ static nrf_radio_signal_callback_return_param_t *signal_handler(uint8_t signal_t nrf_802154_pin_set(PIN_DBG_TIMESLOT_ACTIVE); nrf_802154_log(EVENT_TRACE_ENTER, FUNCTION_RAAL_SIG_EVENT_START); - // Ensure HFCLK is running before start is issued. - assert(NRF_CLOCK->HFCLKSTAT == (CLOCK_HFCLKSTAT_SRC_Msk | CLOCK_HFCLKSTAT_STATE_Msk)); assert(m_timeslot_state == TIMESLOT_STATE_REQUESTED); - m_start_rtc_ticks = NRF_RTC0->COUNTER; - m_timeslot_state = TIMESLOT_STATE_GRANTED; + m_timeslot_state = TIMESLOT_STATE_GRANTED; // Set up timer first with requested timeslot length. timer_start(); @@ -507,23 +455,7 @@ static nrf_radio_signal_callback_return_param_t *signal_handler(uint8_t signal_t // Re-initialize timeslot data for future extensions. timeslot_data_init(); - if (m_in_critical_section) - { - assert(m_pending_event != PENDING_EVENT_STARTED); - - if (m_pending_event == PENDING_EVENT_ENDED) - { - m_pending_event = PENDING_EVENT_NONE; - } - else - { - m_pending_event = PENDING_EVENT_STARTED; - } - } - else - { - timeslot_started_notify(); - } + timeslot_started_notify(); // Try to extend right after start. timeslot_extend(m_timeslot_length); @@ -548,10 +480,14 @@ static nrf_radio_signal_callback_return_param_t *signal_handler(uint8_t signal_t } else { - // Handle margin exceeded event. + // Handle margin exceeded event. timer_irq_handle(); } } + else + { + NVIC_DisableIRQ(RADIO_IRQn); + } nrf_802154_log(EVENT_TRACE_EXIT, FUNCTION_RAAL_SIG_EVENT_RADIO); nrf_802154_pin_clr(PIN_DBG_TIMESLOT_RADIO_IRQ); @@ -561,6 +497,11 @@ static nrf_radio_signal_callback_return_param_t *signal_handler(uint8_t signal_t nrf_802154_pin_tgl(PIN_DBG_TIMESLOT_FAILED); nrf_802154_log(EVENT_TRACE_ENTER, FUNCTION_RAAL_SIG_EVENT_EXTEND_FAIL); + if (!timer_is_set_to_margin()) + { + timer_to_margin_set(); + } + timeslot_next_extend(); nrf_802154_log(EVENT_TRACE_EXIT, FUNCTION_RAAL_SIG_EVENT_EXTEND_FAIL); @@ -569,6 +510,14 @@ static nrf_radio_signal_callback_return_param_t *signal_handler(uint8_t signal_t case NRF_RADIO_CALLBACK_SIGNAL_TYPE_EXTEND_SUCCEEDED: /**< This signal indicates extend action succeeded. */ nrf_802154_log(EVENT_TRACE_ENTER, FUNCTION_RAAL_SIG_EVENT_EXTEND_SUCCESS); + if ((!timer_is_set_to_margin()) && (ticks_to_timeslot_end_get() < NRF_RADIO_MINIMUM_TIMESLOT_LENGTH_EXTENSION_TIME_TICKS)) + { + timer_to_margin_set(); + + m_ret_param.callback_action = NRF_RADIO_SIGNAL_CALLBACK_ACTION_NONE; + break; + } + timer_on_extend_update(); // Request futher extension only if any of previous one failed. @@ -645,23 +594,6 @@ void nrf_raal_softdevice_soc_evt_handler(uint32_t evt_id) } } -/*************************************************************************************************** - * @section HFCLK management. - **************************************************************************************************/ - -void nrf_802154_clock_hfclk_ready(void) -{ - nrf_802154_log(EVENT_TRACE_ENTER, FUNCTION_RAAL_EVT_HFCLK_READY); - - if (m_continuous && timeslot_is_idle()) - { - timeslot_data_init(); - timeslot_request(); - } - - nrf_802154_log(EVENT_TRACE_EXIT, FUNCTION_RAAL_EVT_HFCLK_READY); -} - /*************************************************************************************************** * @section RAAL API. **************************************************************************************************/ @@ -679,10 +611,9 @@ void nrf_raal_init(void) { assert(!m_initialized); - m_continuous = false; - m_timeslot_state = TIMESLOT_STATE_IDLE; + m_continuous = false; + m_timeslot_state = TIMESLOT_STATE_IDLE; - m_config.lf_clk_accuracy_ppm = NRF_RAAL_DEFAULT_LF_CLK_ACCURACY_PPM; m_config.timeslot_length = NRF_RAAL_TIMESLOT_DEFAULT_LENGTH; m_config.timeslot_alloc_iters = NRF_RAAL_TIMESLOT_DEFAULT_ALLOC_ITERS; m_config.timeslot_safe_margin = NRF_RAAL_TIMESLOT_DEFAULT_SAFE_MARGIN; @@ -704,8 +635,8 @@ void nrf_raal_uninit(void) assert(err_code == NRF_SUCCESS); (void)err_code; - m_continuous = false; - m_timeslot_state = TIMESLOT_STATE_IDLE; + m_continuous = false; + m_timeslot_state = TIMESLOT_STATE_IDLE; nrf_802154_pin_clr(PIN_DBG_TIMESLOT_ACTIVE); } @@ -719,7 +650,11 @@ void nrf_raal_continuous_mode_enter(void) m_continuous = true; - nrf_802154_clock_hfclk_start(); + if (timeslot_is_idle()) + { + timeslot_data_init(); + timeslot_request(); + } nrf_802154_log(EVENT_TRACE_EXIT, FUNCTION_RAAL_CONTINUOUS_ENTER); } @@ -739,8 +674,6 @@ void nrf_raal_continuous_mode_exit(void) NVIC_SetPendingIRQ(RAAL_TIMER_IRQn); } - nrf_802154_clock_hfclk_stop(); - nrf_802154_log(EVENT_TRACE_EXIT, FUNCTION_RAAL_CONTINUOUS_EXIT); } @@ -751,58 +684,10 @@ bool nrf_raal_timeslot_request(uint32_t length_us) return false; } - return timer_time_get() + length_us < nrf_timer_cc_read(RAAL_TIMER, TIMER_CC_MARGIN); -} - -bool nrf_raal_timeslot_is_granted(void) -{ - return (m_continuous && timeslot_is_granted()); + return length_us < nrf_raal_timeslot_us_left_get(); } uint32_t nrf_raal_timeslot_us_left_get(void) { - if (!m_continuous || !timeslot_is_granted()) - { - return 0; - } - - return nrf_timer_cc_read(RAAL_TIMER, TIMER_CC_MARGIN) - timer_time_get(); -} - -void nrf_raal_critical_section_enter(void) -{ - nrf_802154_log(EVENT_TRACE_ENTER, FUNCTION_RAAL_CRIT_SECT_ENTER); - - m_in_critical_section = true; - - nrf_802154_log(EVENT_TRACE_EXIT, FUNCTION_RAAL_CRIT_SECT_ENTER); -} - -void nrf_raal_critical_section_exit(void) -{ - nrf_802154_log(EVENT_TRACE_ENTER, FUNCTION_RAAL_CRIT_SECT_EXIT); - - timeslot_critical_section_enter(); - - m_in_critical_section = false; - - switch (m_pending_event) - { - case PENDING_EVENT_STARTED: - timeslot_started_notify(); - break; - - case PENDING_EVENT_ENDED: - timeslot_ended_notify(); - break; - - default: - break; - } - - m_pending_event = PENDING_EVENT_NONE; - - timeslot_critical_section_exit(); - - nrf_802154_log(EVENT_TRACE_EXIT, FUNCTION_RAAL_CRIT_SECT_EXIT); + return safe_time_to_timeslot_end_get(); } diff --git a/third_party/NordicSemiconductor/drivers/radio/raal/softdevice/nrf_raal_softdevice.h b/third_party/NordicSemiconductor/drivers/radio/raal/softdevice/nrf_raal_softdevice.h index a118a2e88..8095ffbee 100644 --- a/third_party/NordicSemiconductor/drivers/radio/raal/softdevice/nrf_raal_softdevice.h +++ b/third_party/NordicSemiconductor/drivers/radio/raal/softdevice/nrf_raal_softdevice.h @@ -39,6 +39,8 @@ #include #include +#include + #ifdef __cplusplus extern "C" { #endif @@ -46,20 +48,65 @@ extern "C" { /** @brief RAAL Softdevice default parameters. */ #define NRF_RAAL_TIMESLOT_DEFAULT_LENGTH 6400 #define NRF_RAAL_TIMESLOT_DEFAULT_ALLOC_ITERS 5 -#define NRF_RAAL_TIMESLOT_DEFAULT_SAFE_MARGIN 91 +#define NRF_RAAL_TIMESLOT_DEFAULT_SAFE_MARGIN nrf_raal_softdevice_safe_margin_calc(NRF_RAAL_DEFAULT_LF_CLK_ACCURACY_PPM) #define NRF_RAAL_TIMESLOT_DEFAULT_TIMEOUT 6400 #define NRF_RAAL_TIMESLOT_DEFAULT_MAX_LENGTH 120000000 -#define NRF_RAAL_DEFAULT_LF_CLK_ACCURACY_PPM 25 +#define NRF_RAAL_DEFAULT_LF_CLK_ACCURACY_PPM 500 + +#define NRF_RAAL_TIMESLOT_DEFAULT_SAFE_MARGIN_LFRC_TICKS 4 +#define NRF_RAAL_TIMESLOT_DEFAULT_SAFE_MARGIN_CRYSTAL_TICKS 3 +#define NRF_RAAL_TIMESLOT_DEFAULT_SAFE_MARGIN_US 3 + +#define NRF_RAAL_PPM_THRESHOLD 500 + +#define NRF_RAAL_TIMESLOT_SAFE_MARGIN_TICKS(ppm) ((ppm >= NRF_RAAL_PPM_THRESHOLD) ? \ + NRF_RAAL_TIMESLOT_DEFAULT_SAFE_MARGIN_LFRC_TICKS : \ + NRF_RAAL_TIMESLOT_DEFAULT_SAFE_MARGIN_CRYSTAL_TICKS) + +/** + * @brief Function used to calculate safe margin from LF clock accuracy in ppm unit. + * + * @param[in] ppm LF clock accuracy in ppm unit. + */ +#define nrf_raal_softdevice_safe_margin_calc(ppm) (NRF_802154_RTC_TICKS_TO_US(NRF_RAAL_TIMESLOT_SAFE_MARGIN_TICKS(ppm)) \ + + NRF_RAAL_TIMESLOT_DEFAULT_SAFE_MARGIN_US) /** @brief RAAL Softdevice configuration parameters. */ typedef struct { - uint32_t timeslot_length; /**< Timeslot length requested by the module in microseconds. */ - uint32_t timeslot_timeout; /**< Longest acceptable delay until the start of the requested timeslot in microseconds. */ - uint32_t timeslot_max_length; /**< Maximum single timeslot length created by extension processing in microseconds. */ - uint16_t timeslot_alloc_iters; /**< Maximum number of iteration of dividing timeslot_length by factor of 2 performed by arbiter. */ - uint16_t timeslot_safe_margin; /**< Safe margin before timeslot is finished and nrf_raal_timeslot_ended should be called in microseconds. */ - uint16_t lf_clk_accuracy_ppm; /**< Clock accuracy in ppm unit. */ + /** + * @brief Timeslot length requested by the module in microseconds. + */ + uint32_t timeslot_length; + + /** + * @brief Longest acceptable delay until the start of the requested timeslot in microseconds. + */ + uint32_t timeslot_timeout; + + /** + * @brief Maximum single timeslot length created by extension processing in microseconds. + */ + uint32_t timeslot_max_length; + + /** + * @brief Maximum number of iteration of dividing timeslot_length by factor of 2 performed by arbiter. + */ + uint16_t timeslot_alloc_iters; + + /** + * @brief Safe margin before timeslot is finished and nrf_raal_timeslot_ended should be called in microseconds. + * @ref nrf_raal_softdevice_safe_margin_calc can be used to calculate proper value based on clock accuracy. + * This value can also be selected experimentally. + */ + uint16_t timeslot_safe_margin; + + /** + * @brief @deprecated Clock accuracy in ppm unit. + * This value is not used anymore. + * Clock accuracy is embedded into timeslot_safe_margin. + */ + uint16_t lf_clk_accuracy_ppm; } nrf_raal_softdevice_cfg_t; /** diff --git a/third_party/NordicSemiconductor/drivers/radio/timer_scheduler/nrf_802154_timer_sched.c b/third_party/NordicSemiconductor/drivers/radio/timer_scheduler/nrf_802154_timer_sched.c index 67f353b6d..1f23d774d 100644 --- a/third_party/NordicSemiconductor/drivers/radio/timer_scheduler/nrf_802154_timer_sched.c +++ b/third_party/NordicSemiconductor/drivers/radio/timer_scheduler/nrf_802154_timer_sched.c @@ -47,13 +47,15 @@ #include #include -#include "platform/timer/nrf_802154_timer.h" +#include "nrf_802154_debug.h" +#include "platform/lp_timer/nrf_802154_lp_timer.h" #if defined(__ICCARM__) _Pragma("diag_suppress=Pe167") #endif -static volatile uint8_t m_mutex; ///< Mutex for starting the timer. +static volatile uint8_t m_timer_mutex; ///< Mutex for starting the timer. +static volatile uint8_t m_fired_mutex; ///< Mutex for the timer firing procedure. static volatile uint8_t m_queue_changed_cntr; ///< Information that scheduler queue was modified. static volatile nrf_802154_timer_t * mp_head; ///< Head of the running timers list. @@ -62,18 +64,18 @@ static volatile nrf_802154_timer_t * mp_head; ///< Head of the runn * @retval true Mutex was acquired. * @retval false Mutex could not be acquired. */ -static inline bool mutex_trylock(void) +static inline bool mutex_trylock(volatile uint8_t * p_mutex) { do { - volatile uint8_t mutex_value = __LDREXB(&m_mutex); + volatile uint8_t mutex_value = __LDREXB(p_mutex); if (mutex_value) { __CLREX(); return false; } - } while (__STREXB(1, &m_mutex)); + } while (__STREXB(1, p_mutex)); __DMB(); @@ -81,10 +83,10 @@ static inline bool mutex_trylock(void) } /** @brief Release mutex. */ -static inline void mutex_unlock(void) +static inline void mutex_unlock(volatile uint8_t * p_mutex) { __DMB(); - m_mutex = 0; + *p_mutex = 0; } /** @brief Increment queue counter value to detect changes in the queue. */ @@ -142,11 +144,11 @@ static inline void handle_timer(void) queue_cntr = m_queue_changed_cntr; p_head = mp_head; - if (mutex_trylock()) + if (mutex_trylock(&m_timer_mutex)) { if (p_head == NULL) { - nrf_802154_timer_stop(); + nrf_802154_lp_timer_stop(); } else { @@ -157,11 +159,11 @@ static inline void handle_timer(void) // between reading t0 and dt and not be a valid combination. if (p_head == mp_head) { - nrf_802154_timer_start(t0, dt); + nrf_802154_lp_timer_start(t0, dt); } } - mutex_unlock(); + mutex_unlock(&m_timer_mutex); } } while (queue_cntr != m_queue_changed_cntr); } @@ -250,11 +252,14 @@ static bool timer_remove(nrf_802154_timer_t * p_timer) // lower pritority context in case it was going to be used. if (p_cur != NULL) { + uint32_t temp; + do { // This assignment is used to prevent compiler from removing exclusive load during optimization (IAR). - p_next = (nrf_802154_timer_t *)__LDREXW((uint32_t *)&p_cur->p_next); - } while (__STREXW((uint32_t)NULL, (uint32_t *)&p_cur->p_next)); + temp = __LDREXW((uint32_t *)&p_cur->p_next); + assert((void *)temp != p_cur); + } while (__STREXW(temp, (uint32_t *)&p_cur->p_next)); } return (timer_start || timer_stop); @@ -263,20 +268,26 @@ static bool timer_remove(nrf_802154_timer_t * p_timer) void nrf_802154_timer_sched_init(void) { mp_head = NULL; - m_mutex = 0; + m_timer_mutex = 0; + m_fired_mutex = 0; m_queue_changed_cntr = 0; } void nrf_802154_timer_sched_deinit(void) { - nrf_802154_timer_stop(); + nrf_802154_lp_timer_stop(); mp_head = NULL; } uint32_t nrf_802154_timer_sched_time_get(void) { - return nrf_802154_timer_time_get(); + return nrf_802154_lp_timer_time_get(); +} + +uint32_t nrf_802154_timer_sched_granularity_get(void) +{ + return nrf_802154_lp_timer_granularity_get(); } bool nrf_802154_timer_sched_time_is_in_future(uint32_t now, uint32_t t0, uint32_t dt) @@ -289,12 +300,14 @@ bool nrf_802154_timer_sched_time_is_in_future(uint32_t now, uint32_t t0, uint32_ void nrf_802154_timer_sched_add(nrf_802154_timer_t * p_timer, bool round_up) { + nrf_802154_log(EVENT_TRACE_ENTER, FUNCTION_TSCH_ADD); + assert(p_timer != NULL); assert(p_timer->callback != NULL); if (round_up) { - p_timer->dt += nrf_802154_timer_granularity_get() - 1; + p_timer->dt += nrf_802154_lp_timer_granularity_get() - 1; } if (timer_remove(p_timer)) @@ -317,6 +330,8 @@ void nrf_802154_timer_sched_add(nrf_802154_timer_t * p_timer, bool round_up) { nrf_802154_timer_t * p_cur = (nrf_802154_timer_t *)__LDREXW((uint32_t *)pp_item); + assert(p_cur != p_timer); + if (p_cur == NULL) { // No HEAD or insert at the end. @@ -340,6 +355,7 @@ void nrf_802154_timer_sched_add(nrf_802154_timer_t * p_timer, bool round_up) continue; } + assert(p_next != p_timer); p_timer->p_next = p_next; if (!__STREXW((uint32_t)p_timer, (uint32_t *)pp_item)) @@ -354,6 +370,8 @@ void nrf_802154_timer_sched_add(nrf_802154_timer_t * p_timer, bool round_up) { handle_timer(); } + + nrf_802154_log(EVENT_TRACE_EXIT, FUNCTION_TSCH_ADD); } void nrf_802154_timer_sched_remove(nrf_802154_timer_t * p_timer) @@ -389,21 +407,31 @@ bool nrf_802154_timer_sched_is_running(nrf_802154_timer_t * p_timer) return result; } -void nrf_802154_timer_fired(void) +void nrf_802154_lp_timer_fired(void) { - nrf_802154_timer_t * p_timer = (nrf_802154_timer_t *) mp_head; - nrf_802154_timer_callback_t callback = p_timer->callback; - void * p_context = p_timer->p_context; + nrf_802154_log(EVENT_TRACE_ENTER, FUNCTION_TSCH_FIRED); - if ((p_timer != NULL) && (callback != NULL)) + if (mutex_trylock(&m_fired_mutex)) { - bool timer_shall_be_handled = timer_remove(p_timer); + nrf_802154_timer_t * p_timer = (nrf_802154_timer_t *) mp_head; - callback(p_context); - - if (timer_shall_be_handled) + if (p_timer != NULL) { - handle_timer(); + nrf_802154_timer_callback_t callback = p_timer->callback; + void * p_context = p_timer->p_context; + + (void)timer_remove(p_timer); + + if (callback != NULL) + { + callback(p_context); + } } + + mutex_unlock(&m_fired_mutex); } + + handle_timer(); + + nrf_802154_log(EVENT_TRACE_EXIT, FUNCTION_TSCH_FIRED); } diff --git a/third_party/NordicSemiconductor/drivers/radio/timer_scheduler/nrf_802154_timer_sched.h b/third_party/NordicSemiconductor/drivers/radio/timer_scheduler/nrf_802154_timer_sched.h index 944b9d3d4..dc2d9bae5 100644 --- a/third_party/NordicSemiconductor/drivers/radio/timer_scheduler/nrf_802154_timer_sched.h +++ b/third_party/NordicSemiconductor/drivers/radio/timer_scheduler/nrf_802154_timer_sched.h @@ -76,11 +76,11 @@ typedef struct nrf_802154_timer_s nrf_802154_timer_t; */ struct nrf_802154_timer_s { - uint32_t t0; ///< Base time of the timer [us] - uint32_t dt; ///< Timer expiration delta from @p t0 [us] - nrf_802154_timer_callback_t callback; ///< Callback function called when timer expires - void * p_context; ///< User-defined context passed to callback function - nrf_802154_timer_t * p_next; ///< A pointer to the next running timer + uint32_t t0; ///< Base time of the timer [us] + uint32_t dt; ///< Timer expiration delta from @p t0 [us] + nrf_802154_timer_callback_t callback; ///< Callback function called when timer expires + void * p_context; ///< User-defined context passed to callback function + nrf_802154_timer_t * p_next; ///< A pointer to the next running timer }; /** @@ -102,6 +102,13 @@ void nrf_802154_timer_sched_deinit(void); */ uint32_t nrf_802154_timer_sched_time_get(void); +/** + * @brief Get granularity of the timer that runs the timer scheduler. + * + * @return Granularity of the timer in microseconds [us]. + */ +uint32_t nrf_802154_timer_sched_granularity_get(void); + /** * @brief Check if given time is in future. *