[nrf52840] update nRF52840 radio driver to version 1.2.0 (#2968)

This commit is contained in:
Hubert Miś
2018-08-17 09:03:55 -07:00
committed by Jonathan Hui
parent c116441212
commit 8d2184a4e7
50 changed files with 4502 additions and 1750 deletions
+8 -1
View File
@@ -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
View File
@@ -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 */
@@ -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)
@@ -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;
@@ -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();
}
@@ -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__
@@ -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
View File
@@ -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(&timestamp);
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
View File
@@ -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.
*
@@ -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_
File diff suppressed because it is too large Load Diff
@@ -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.
*/
@@ -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
@@ -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.
@@ -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)
@@ -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
View File
@@ -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__
@@ -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.
*
*/
@@ -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);
}
@@ -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_ */
@@ -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_ */
@@ -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
}
@@ -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
}
@@ -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].
*
@@ -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);
}
@@ -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();
}
}
@@ -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();
}
@@ -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;
/**
@@ -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);
}
@@ -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.
*