mirror of
https://github.com/espressif/openthread.git
synced 2026-08-28 12:59:54 +00:00
[nrf52840] update nRF52840 radio driver to version 1.2.0 (#2968)
This commit is contained in:
@@ -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 \
|
||||
|
||||
+218
-126
@@ -52,26 +52,30 @@
|
||||
#include "cmsis/core_cmFunc.h"
|
||||
|
||||
#include <drivers/clock/nrf_drv_clock.h>
|
||||
#include <drivers/radio/platform/timer/nrf_802154_timer.h>
|
||||
#include <drivers/radio/nrf_802154_utils.h>
|
||||
#include <drivers/radio/platform/lp_timer/nrf_802154_lp_timer.h>
|
||||
|
||||
#include <hal/nrf_rtc.h>
|
||||
|
||||
#include <openthread/config.h>
|
||||
|
||||
// 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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
+34
-12
@@ -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)
|
||||
|
||||
+40
-20
@@ -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;
|
||||
|
||||
+167
@@ -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 <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#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();
|
||||
}
|
||||
+77
@@ -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 <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#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__
|
||||
+53
-32
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+102
-88
@@ -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)
|
||||
{
|
||||
|
||||
+131
-76
@@ -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.
|
||||
*
|
||||
|
||||
+17
-18
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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_
|
||||
|
||||
+434
-282
File diff suppressed because it is too large
Load Diff
+18
-14
@@ -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.
|
||||
*/
|
||||
|
||||
+250
-47
@@ -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 <nrf.h>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
/**
|
||||
*@}
|
||||
**/
|
||||
|
||||
@@ -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
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
+2
-2
@@ -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)
|
||||
|
||||
+13
-2
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
/**
|
||||
|
||||
@@ -40,28 +40,15 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#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)
|
||||
|
||||
@@ -50,21 +50,12 @@
|
||||
|
||||
#include <nrf.h>
|
||||
|
||||
#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,
|
||||
|
||||
@@ -0,0 +1,466 @@
|
||||
#include "nrf_802154_rsch.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <nrf.h>
|
||||
|
||||
#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();
|
||||
}
|
||||
@@ -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 <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#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_ */
|
||||
+116
-139
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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 <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#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
|
||||
@@ -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 <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#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_ */
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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 <assert.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @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__
|
||||
+2
-1
@@ -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.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
+142
@@ -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 <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <hal/nrf_timer.h>
|
||||
#include <nrf.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
|
||||
+146
@@ -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 <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#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_ */
|
||||
+76
-28
@@ -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 <stdbool.h>
|
||||
#include <stdint.h>
|
||||
@@ -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_ */
|
||||
Vendored
+576
@@ -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 <assert.h>
|
||||
|
||||
#include <hal/nrf_rtc.h>
|
||||
#include <nrf.h>
|
||||
|
||||
#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
|
||||
}
|
||||
+5
-5
@@ -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
|
||||
}
|
||||
-411
@@ -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 <assert.h>
|
||||
|
||||
#include <hal/nrf_rtc.h>
|
||||
#include <nrf.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
}
|
||||
@@ -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].
|
||||
*
|
||||
|
||||
+246
@@ -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 <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
+2
-39
@@ -42,11 +42,7 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#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();
|
||||
}
|
||||
}
|
||||
|
||||
+154
-269
@@ -46,7 +46,6 @@
|
||||
#include <nrf_raal_api.h>
|
||||
#include <nrf_802154.h>
|
||||
#include <nrf_802154_debug.h>
|
||||
#include <platform/clock/nrf_802154_clock.h>
|
||||
|
||||
#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();
|
||||
}
|
||||
|
||||
+55
-8
@@ -39,6 +39,8 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <nrf_802154_utils.h>
|
||||
|
||||
#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;
|
||||
|
||||
/**
|
||||
|
||||
+55
-27
@@ -47,13 +47,15 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include <nrf.h>
|
||||
#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);
|
||||
}
|
||||
|
||||
+12
-5
@@ -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.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user