Skip to content

nrfx: drivers: nrfx_grtc: Improvements to speed up nrf_grtc_timer #288

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 59 additions & 1 deletion nrfx/drivers/include/nrfx_grtc.h
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,19 @@ nrfx_err_t nrfx_grtc_sleep_configuration_get(nrfx_grtc_sleep_config_t * p_sleep_
*/
nrfx_err_t nrfx_grtc_channel_alloc(uint8_t * p_channel);

/**
* @brief Function for setting a callback to a channel.
*
* Function enabled the interrupt for that channel.
*
* @param[in] channel Pointer to the capture/compare channel.
* @param[in] handler User handler called when channel expires.
* @param[in] p_context Context passed to the callback.
*/
void nrfx_grtc_channel_callback_set(uint8_t channel,
nrfx_grtc_cc_handler_t handler,
void * p_context);

/**
* @brief Function for freeing the GRTC capture/compare channel.
*
Expand Down Expand Up @@ -296,7 +309,6 @@ void nrfx_grtc_syscountervalid_int_disable(void);
*/

nrfx_err_t nrfx_grtc_syscounter_start(bool busy_wait, uint8_t * p_main_cc_channel);

/**
* @brief Function for performing an action for the GRTC.
*
Expand Down Expand Up @@ -359,6 +371,22 @@ nrfx_err_t nrfx_grtc_syscounter_cc_absolute_set(nrfx_grtc_channel_t * p_chan_dat
uint64_t val,
bool enable_irq);

/**
* @brief Function for setting the absolute compare value for the SYSCOUNTER.
*
* Function must be called with interrupts locked. If %p safe_setting is true then
* it means that previous CC for that channel did not yet expired and it
* was set to a value earlier than %p val so there is a chance that it will
* expire during setting the new value. In that case compare event may be misinterpreted.
* Slower but safe procedure is used in that case. If %p safe_setting is false then
* function just sets new CC value.
*
* @param[in] channel Channel.
* @param[in] val Absolute value to be set in the compare register.
* @param[in] safe_setting Use safe procedure if true or just set CC to a new value if false.
*/
void nrfx_grtc_syscounter_cc_abs_set(uint8_t channel, uint64_t val, bool safe_setting);

/**
* @brief Function for setting the relative compare value for the SYSCOUNTER.
*
Expand All @@ -379,6 +407,22 @@ nrfx_err_t nrfx_grtc_syscounter_cc_relative_set(nrfx_grtc_channel_t *
bool enable_irq,
nrfx_grtc_cc_relative_reference_t reference);

/**
* @brief Function for setting the relative compare value for the SYSCOUNTER.
*
* Function just sets CCADD value and does not attempt to enable or disable the interrupt.
* It assumes that expected channel configuration is done prior to that call.
* Function assumes that previously used CC value has already expired so new value
* can be safely set without a risk of spurious CC expiration.
*
* @param[in] channel Channel.
* @param[in] val Relative value to be set in the CCADD register.
* @param[in] reference Reference. Current counter value or current CC value.
*/
void nrfx_grtc_syscounter_cc_rel_set(uint8_t channel,
uint32_t val,
nrfx_grtc_cc_relative_reference_t reference);

/**
* @brief Function for disabling the SYSCOUNTER compare interrupt.
*
Expand Down Expand Up @@ -539,6 +583,15 @@ NRFX_STATIC_INLINE bool nrfx_grtc_sys_counter_cc_enable_check(uint8_t channel);
*/
NRFX_STATIC_INLINE bool nrfx_grtc_syscounter_compare_event_check(uint8_t channel);

/**
* @brief Function for retrieving CC value.
*
* @param[in] channel Compare channel.
*
* @return Value read from CC register.
*/
NRFX_STATIC_INLINE uint64_t nrfx_grtc_sys_counter_cc_get(uint8_t channel);

#if NRF_GRTC_HAS_RTCOUNTER || defined(__NRFX_DOXYGEN__)
/**
* @brief Function for reading the GRTC RTCOUNTER value.
Expand Down Expand Up @@ -590,6 +643,11 @@ NRFX_STATIC_INLINE bool nrfx_grtc_syscounter_compare_event_check(uint8_t channel
return nrfy_grtc_sys_counter_compare_event_check(NRF_GRTC, channel);
}

NRFX_STATIC_INLINE uint64_t nrfx_grtc_sys_counter_cc_get(uint8_t channel)
{
return nrfy_grtc_sys_counter_cc_get(NRF_GRTC, channel);
}

#if NRF_GRTC_HAS_RTCOUNTER
NRFX_STATIC_INLINE uint64_t nrfx_grtc_rtcounter_get(void)
{
Expand Down
176 changes: 128 additions & 48 deletions nrfx/drivers/src/nrfx_grtc.c
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@ typedef struct
nrfx_atomic_t available_channels; /**< Bitmask of available channels. */
uint32_t used_channels; /**< Bitmask of channels used by the driver. */
nrfx_grtc_channel_t channel_data[NRFX_GRTC_CONFIG_NUM_OF_CC_CHANNELS + 1]; /**< Channel specific data. */
uint8_t ch_to_data[GRTC_NCC_SIZE]; /**< Mapping of channel indext to channel_data index. */
uint64_t cc_value[NRFX_GRTC_CONFIG_NUM_OF_CC_CHANNELS]; /**< Last CC value. */
nrfx_atomic_t read_cc_mask; /**< Indicating if CC value must be passed to the handler. */
#if NRF_GRTC_HAS_RTCOUNTER
nrfx_grtc_rtcomparesync_handler_t rtcomparesync_handler; /**< User handler corresponding to rtcomparesync event.*/
void * rtcomparesync_context; /**< User context for rtcomparesync event handler. */
Expand Down Expand Up @@ -306,6 +309,18 @@ nrfx_err_t nrfx_grtc_syscounter_get(uint64_t * p_counter)
return err_code;
}

void nrfx_grtc_channel_callback_set(uint8_t channel,
nrfx_grtc_cc_handler_t handler,
void * p_context)
{
uint8_t ch_data_idx = get_ch_data_index_for_channel(channel);

m_cb.channel_data[ch_data_idx].handler = handler;
m_cb.channel_data[ch_data_idx].p_context = p_context;
m_cb.channel_data[ch_data_idx].channel = channel;
nrfy_grtc_int_enable(NRF_GRTC, GRTC_CHANNEL_TO_BITMASK(channel));
}

nrfx_err_t nrfx_grtc_channel_alloc(uint8_t * p_channel)
{
NRFX_ASSERT(p_channel);
Expand Down Expand Up @@ -394,7 +409,10 @@ nrfx_err_t nrfx_grtc_init(uint8_t interrupt_priority)

for (uint8_t i = 0; i < NRFX_GRTC_CONFIG_NUM_OF_CC_CHANNELS; i++)
{
m_cb.channel_data[i].channel = get_channel_for_ch_data_idx(i);
uint8_t ch = get_channel_for_ch_data_idx(i);

m_cb.channel_data[i].channel = ch;
m_cb.ch_to_data[ch] = i;
}

nrfy_grtc_int_init(NRF_GRTC, GRTC_ALL_INT_MASK, interrupt_priority, false);
Expand Down Expand Up @@ -732,6 +750,29 @@ nrfx_err_t nrfx_grtc_syscounter_cc_disable(uint8_t channel)
return err_code;
}

void nrfx_grtc_syscounter_cc_abs_set(uint8_t channel, uint64_t val, bool safe_setting)
{
m_cb.cc_value[m_cb.ch_to_data[channel]] = val;
if (safe_setting)
{
nrfy_grtc_sys_counter_cc_set(NRF_GRTC, channel, val);
if (nrfy_grtc_sys_counter_compare_event_check(NRF_GRTC, channel))
{
uint64_t now;

nrfx_grtc_syscounter_get(&now);
if (val > now)
{
nrfy_grtc_sys_counter_compare_event_clear(NRF_GRTC, channel);
}
}
}
else
{
nrfy_grtc_sys_counter_cc_set(NRF_GRTC, channel, val);
}
}

nrfx_err_t nrfx_grtc_syscounter_cc_absolute_set(nrfx_grtc_channel_t * p_chan_data,
uint64_t val,
bool enable_irq)
Expand All @@ -755,6 +796,7 @@ nrfx_err_t nrfx_grtc_syscounter_cc_absolute_set(nrfx_grtc_channel_t * p_chan_dat

if (enable_irq)
{
NRFX_ATOMIC_FETCH_OR(&m_cb.read_cc_mask, NRFX_BIT(p_chan_data->channel));
nrfy_grtc_int_enable(NRF_GRTC, GRTC_CHANNEL_TO_BITMASK(p_chan_data->channel));
}

Expand All @@ -764,6 +806,17 @@ nrfx_err_t nrfx_grtc_syscounter_cc_absolute_set(nrfx_grtc_channel_t * p_chan_dat
return err_code;
}

void nrfx_grtc_syscounter_cc_rel_set(uint8_t channel,
uint32_t val,
nrfx_grtc_cc_relative_reference_t reference)
{
m_cb.cc_value[m_cb.ch_to_data[channel]] += val;
nrfy_grtc_sys_counter_cc_add_set(NRF_GRTC,
channel,
val,
(nrf_grtc_cc_add_reference_t)reference);
}

nrfx_err_t nrfx_grtc_syscounter_cc_relative_set(nrfx_grtc_channel_t * p_chan_data,
uint32_t val,
bool enable_irq,
Expand Down Expand Up @@ -793,6 +846,7 @@ nrfx_err_t nrfx_grtc_syscounter_cc_relative_set(nrfx_grtc_channel_t *

if (enable_irq)
{
NRFX_ATOMIC_FETCH_OR(&m_cb.read_cc_mask, NRFX_BIT(p_chan_data->channel));
nrfy_grtc_int_enable(NRF_GRTC, GRTC_CHANNEL_TO_BITMASK(p_chan_data->channel));
}

Expand Down Expand Up @@ -896,69 +950,95 @@ nrfx_err_t nrfx_grtc_syscounter_cc_value_read(uint8_t channel, uint64_t * p_val)
return err_code;
}

#if NRF_GRTC_HAS_RTCOUNTER || (NRFY_GRTC_HAS_EXTENDED && NRFY_GRTC_HAS_SYSCOUNTERVALID)
#define GRTC_EXT 1
#endif

static void grtc_irq_handler(void)
{
uint32_t evt_to_process = GRTC_CHANNEL_MASK_TO_INT_MASK(allocated_channels_mask_get() &
used_channels_mask_get());
#if NRF_GRTC_HAS_RTCOUNTER
evt_to_process |= (GRTC_NON_SYSCOMPARE_INT_MASK & ~NRF_GRTC_INT_SYSCOUNTERVALID_MASK);
#endif
uint32_t event_mask = nrfy_grtc_events_process(NRF_GRTC, evt_to_process);
uint32_t active_int_mask = nrfy_grtc_int_enable_check(NRF_GRTC, event_mask);
nrf_grtc_event_t event;
uint32_t intpend = nrf_grtc_int_pending_get(NRF_GRTC);

for (uint32_t i = 0; i < NRFX_GRTC_CONFIG_NUM_OF_CC_CHANNELS; i++)
{
uint8_t channel = m_cb.channel_data[i].channel;
while (intpend) {
uint8_t idx = (uint8_t)(31 - NRFX_CLZ(intpend));

intpend &= ~NRFX_BIT(idx);

event = nrfy_grtc_sys_counter_compare_event_get(channel);
if (active_int_mask & NRFY_EVENT_TO_INT_BITMASK(event))
if (!NRFX_IS_ENABLED(GRTC_EXT) || idx < GRTC_NCC_SIZE)
{
NRFX_LOG_INFO("Event: NRF_GRTC_EVENT_COMPARE_%d.", channel);
uint32_t i = m_cb.ch_to_data[idx];

NRFX_ASSERT(m_cb.channel_data[i].channel == idx);

if (m_cb.channel_data[i].handler)
{
m_cb.channel_data[i].handler((int32_t)channel,
nrfy_grtc_sys_counter_cc_get(NRF_GRTC, channel),
m_cb.channel_data[i].p_context);
uint64_t cc_value;

if (NRFX_ATOMIC_FETCH_AND(&m_cb.read_cc_mask, ~NRFX_BIT(idx)) & NRFX_BIT(idx))
{
/* Read CC value only if channel was set using legacy functions. It is done
* for API backward compatibility. Reading 64 bit value from GRTC is costly
* and it is avoided if possible.
*/
cc_value = nrfy_grtc_sys_counter_cc_get(NRF_GRTC, idx);
}
else
{
/* If CC was set using optimized API then CC is stored in RAM (much faster
* access).
*/
cc_value = m_cb.cc_value[i];
}

/* Check event again (initially checked via INTPEND). It is possible that
* CC is reconfigured from higher priority context. In that case event
* might be cleared.
*/
if (!nrf_grtc_event_check(NRF_GRTC, NRFY_INT_BITPOS_TO_EVENT(idx)))
{
break;
}

nrf_grtc_event_clear(NRF_GRTC, NRFY_INT_BITPOS_TO_EVENT(idx));

m_cb.channel_data[i].handler(idx, cc_value, m_cb.channel_data[i].p_context);
break;
}

/* Return early as this is the most likely scenario (single CC expiring). */
if (NRFX_IS_ENABLED(GRTC_EXT) && (intpend == 0))
{
break;
}
}
}
#if NRF_GRTC_HAS_RTCOUNTER
if (active_int_mask & NRF_GRTC_INT_RTCOMPARE_MASK)
{
NRFX_LOG_INFO("Event: NRF_GRTC_EVENT_RTCOMPARE.");
nrfx_grtc_channel_t const * p_channel = &m_cb.channel_data[GRTC_RTCOUNTER_CC_HANDLER_IDX];
if (p_channel->handler)
if (idx == NRFY_EVENT_TO_INT_BITPOS(NRF_GRTC_EVENT_RTCOMPARE))
{
p_channel->handler((int32_t)GRTC_RTCOUNTER_COMPARE_CHANNEL,
nrfy_grtc_rt_counter_cc_get(NRF_GRTC),
p_channel->p_context);
}
}

if (active_int_mask & NRF_GRTC_INT_RTCOMPARESYNC_MASK)
{
NRFX_LOG_INFO("Event: NRF_GRTC_EVENT_RTCOMPARESYNC.");
if (m_cb.rtcomparesync_handler)
{
m_cb.rtcomparesync_handler(m_cb.rtcomparesync_context);
}
}
nrf_grtc_event_clear(NRF_GRTC, NRFY_INT_BITPOS_TO_EVENT(idx));
nrfx_grtc_channel_t const * p_channel =
&m_cb.channel_data[GRTC_RTCOUNTER_CC_HANDLER_IDX];
if (p_channel->handler)
{
p_channel->handler((int32_t)GRTC_RTCOUNTER_COMPARE_CHANNEL,
nrfy_grtc_rt_counter_cc_get(NRF_GRTC),
p_channel->p_context);
}
}
#endif // NRF_GRTC_HAS_RTCOUNTER
#if NRFY_GRTC_HAS_EXTENDED && NRFY_GRTC_HAS_SYSCOUNTERVALID
/* The SYSCOUNTERVALID bit is automatically cleared when GRTC goes into sleep state and set
* when returning from this state. It can't be cleared inside the ISR procedure because we rely
* on it during SYSCOUNTER value reading procedure. */
if (nrfy_grtc_event_check(NRF_GRTC, NRF_GRTC_EVENT_SYSCOUNTERVALID) &&
nrfy_grtc_int_enable_check(NRF_GRTC, NRF_GRTC_INT_SYSCOUNTERVALID_MASK))
{
NRFX_LOG_INFO("Event: NRF_GRTC_EVENT_SYSCOUNTERVALID.");
if (m_cb.syscountervalid_handler)
if (idx == NRFY_EVENT_TO_INT_BITPOS(NRF_GRTC_EVENT_SYSCOUNTERVALID))
{
m_cb.syscountervalid_handler(m_cb.syscountervalid_context);
/* The SYSCOUNTERVALID bit is automatically cleared when GRTC goes into sleep state
* and set when returning from this state. It can't be cleared inside the ISR
* procedure because we rely on it during SYSCOUNTER value reading procedure. */
NRFX_LOG_INFO("Event: NRF_GRTC_EVENT_SYSCOUNTERVALID.");
nrf_grtc_event_clear(NRF_GRTC, NRFY_INT_BITPOS_TO_EVENT(idx));
if (m_cb.syscountervalid_handler)
{
m_cb.syscountervalid_handler(m_cb.syscountervalid_context);
}
}
}
#endif // NRFY_GRTC_HAS_EXTENDED && NRFY_GRTC_HAS_SYSCOUNTERVALID
}
}

void nrfx_grtc_irq_handler(void)
Expand Down
1 change: 0 additions & 1 deletion nrfx/hal/nrf_grtc.h
Original file line number Diff line number Diff line change
Expand Up @@ -1559,7 +1559,6 @@ NRF_STATIC_INLINE void nrf_grtc_event_clear(NRF_GRTC_Type * p_reg, nrf_grtc_even
#endif

*((volatile uint32_t *)((uint8_t *)p_reg + (uint32_t)event)) = 0x0UL;
nrf_event_readback((uint8_t *)p_reg + (uint32_t)event);
}

#if NRF_GRTC_HAS_RTCOUNTER
Expand Down