hal_nordic/drivers/nrf_802154/driver/src/nrf_802154_trx.c

2622 lines
80 KiB
C

/*
* Copyright (c) 2019 - 2022, Nordic Semiconductor ASA
* All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
* 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.
*
*/
#define NRF_802154_MODULE_ID NRF_802154_DRV_MODULE_ID_TRX
#include "nrf_802154_trx.h"
#include <assert.h>
#include <string.h>
#include "nrf_802154_config.h"
#include "nrf_802154_const.h"
#include "nrf_802154_peripherals.h"
#include "nrf_802154_pib.h"
#include "nrf_802154_rssi.h"
#include "nrf_802154_swi.h"
#include "nrf_802154_trx_ppi_api.h"
#include "nrf_802154_utils.h"
#include "hal/nrf_egu.h"
#include "hal/nrf_radio.h"
#include "hal/nrf_timer.h"
#if defined(NRF53_SERIES)
#include "hal/nrf_vreqctrl.h"
#endif
#include "nrf_802154_procedures_duration.h"
#include "nrf_802154_critical_section.h"
#include "mpsl_fem_config_common.h"
#include "platform/nrf_802154_irq.h"
#include "protocol/mpsl_fem_protocol_api.h"
#include "nrf_802154_sl_ant_div.h"
#define EGU_SYNC_EVENT NRF_EGU_EVENT_TRIGGERED3
#define EGU_SYNC_TASK NRF_EGU_TASK_TRIGGER3
#define EGU_SYNC_INTMASK NRF_EGU_INT_TRIGGERED3
#if defined(NRF52840_XXAA) || \
defined(NRF52833_XXAA)
#define PPI_CCAIDLE_FEM NRF_802154_PPI_RADIO_CCAIDLE_TO_FEM_GPIOTE ///< PPI that connects RADIO CCAIDLE event with GPIOTE tasks used by FEM
#define PPI_CHGRP_ABORT NRF_802154_PPI_ABORT_GROUP ///< PPI group used to disable PPIs when async event aborting radio operation is propagated through the system
#define RADIO_BASE NRF_RADIO_BASE
#elif defined(NRF5340_XXAA)
#define PPI_CCAIDLE_FEM 0
#define RADIO_BASE NRF_RADIO_NS_BASE
#define FICR_BASE NRF_FICR_NS_BASE
#endif
#if NRF_802154_DISABLE_BCC_MATCHING
#define SHORT_ADDRESS_BCSTART 0UL
#else // NRF_802154_DISABLE_BCC_MATCHING
#define SHORT_ADDRESS_BCSTART NRF_RADIO_SHORT_ADDRESS_BCSTART_MASK
#endif // NRF_802154_DISABLE_BCC_MATCHING
/// Value set to SHORTS register when no shorts should be enabled.
#define SHORTS_IDLE 0
/// Value set to SHORTS register for RX operation.
#define SHORTS_RX (NRF_RADIO_SHORT_ADDRESS_RSSISTART_MASK | \
NRF_RADIO_SHORT_END_DISABLE_MASK | \
SHORT_ADDRESS_BCSTART)
#define SHORTS_RX_FREE_BUFFER (NRF_RADIO_SHORT_RXREADY_START_MASK)
#define SHORTS_TX_ACK (NRF_RADIO_SHORT_TXREADY_START_MASK | \
NRF_RADIO_SHORT_PHYEND_DISABLE_MASK)
#define SHORTS_CCA_TX (NRF_RADIO_SHORT_RXREADY_CCASTART_MASK | \
NRF_RADIO_SHORT_CCABUSY_DISABLE_MASK | \
NRF_RADIO_SHORT_CCAIDLE_TXEN_MASK | \
NRF_RADIO_SHORT_TXREADY_START_MASK | \
NRF_RADIO_SHORT_PHYEND_DISABLE_MASK)
#define SHORTS_TX (NRF_RADIO_SHORT_TXREADY_START_MASK | \
NRF_RADIO_SHORT_PHYEND_DISABLE_MASK)
#define SHORTS_RX_ACK (NRF_RADIO_SHORT_ADDRESS_RSSISTART_MASK | \
NRF_RADIO_SHORT_END_DISABLE_MASK)
#define SHORTS_MOD_CARRIER (NRF_RADIO_SHORT_TXREADY_START_MASK | \
NRF_RADIO_SHORT_PHYEND_START_MASK)
#define SHORTS_ED (NRF_RADIO_SHORT_READY_EDSTART_MASK)
#define SHORTS_CCA (NRF_RADIO_SHORT_RXREADY_CCASTART_MASK | \
NRF_RADIO_SHORT_CCABUSY_DISABLE_MASK)
#define CRC_LENGTH 2 ///< Length of CRC in 802.15.4 frames [bytes]
#define CRC_POLYNOMIAL 0x011021 ///< Polynomial used for CRC calculation in 802.15.4 frames
#define TXRU_TIME 40 ///< Transmitter ramp up time [us]
#define EVENT_LAT 23 ///< END event latency [us]
#define MAX_RXRAMPDOWN_CYCLES 32 ///< Maximum number of cycles that RX ramp-down might take
#define RSSI_SETTLE_TIME_US 15 ///< Time required for RSSI measurements to become valid after signal level change.
#if NRF_802154_INTERNAL_RADIO_IRQ_HANDLING
void nrf_802154_radio_irq_handler(void); ///< Prototype required by internal RADIO IRQ handler
#endif // NRF_802154_INTERNAL_RADIO_IRQ_HANDLING
/// Common parameters for the FEM handling.
static const mpsl_fem_event_t m_activate_rx_cc0 =
{
.type = MPSL_FEM_EVENT_TYPE_TIMER,
.event.timer =
{
.p_timer_instance = NRF_802154_TIMER_INSTANCE,
.compare_channel_mask = ((1 << NRF_TIMER_CC_CHANNEL0) | (1 << NRF_TIMER_CC_CHANNEL2)),
.counter_period = {
.end = RX_RAMP_UP_TIME
},
},
};
static const mpsl_fem_event_t m_activate_tx_cc0 =
{
.type = MPSL_FEM_EVENT_TYPE_TIMER,
.event.timer =
{
.p_timer_instance = NRF_802154_TIMER_INSTANCE,
.compare_channel_mask = ((1 << NRF_TIMER_CC_CHANNEL0) | (1 << NRF_TIMER_CC_CHANNEL2)),
.counter_period = {
.end = TX_RAMP_UP_TIME
},
},
};
static const mpsl_fem_event_t m_ccaidle =
{
.type = MPSL_FEM_EVENT_TYPE_GENERIC,
#if defined(NRF52_SERIES)
.override_ppi = true,
.ppi_ch_id = PPI_CCAIDLE_FEM,
.event.generic.event = ((uint32_t)RADIO_BASE + (uint32_t)NRF_RADIO_EVENT_CCAIDLE)
#elif defined(NRF53_SERIES)
.event.generic.event = NRF_802154_DPPI_RADIO_CCAIDLE
#endif
};
/**@brief Fal event used by @ref nrf_802154_trx_transmit_ack and @ref txack_finish */
static mpsl_fem_event_t m_activate_tx_cc0_timeshifted;
static volatile trx_state_t m_trx_state;
typedef struct
{
#if !NRF_802154_DISABLE_BCC_MATCHING
bool psdu_being_received; ///< If PSDU is currently being received.
#endif
bool missing_receive_buffer; ///!< If trx entered receive state without receive buffer
#if NRF_802154_TX_STARTED_NOTIFY_ENABLED
bool tx_started; ///< If the requested transmission has started.
#endif // NRF_802154_TX_STARTED_NOTIFY_ENABLED
bool rssi_started;
volatile bool rssi_settled;
} nrf_802154_flags_t;
static nrf_802154_flags_t m_flags; ///< Flags used to store the current driver state.
/** @brief Value of TIMER internal counter from which the counting is resumed on RADIO.EVENTS_END event. */
static volatile uint32_t m_timer_value_on_radio_end_event;
static volatile bool m_transmit_with_cca;
static void rxframe_finish_disable_ppis(void);
static void rxack_finish_disable_ppis(void);
static void txframe_finish_disable_ppis(bool cca);
static void go_idle_abort(void);
static void receive_frame_abort(void);
static void receive_ack_abort(void);
static void transmit_frame_abort(void);
static void transmit_ack_abort(void);
static void standalone_cca_abort(void);
#if NRF_802154_CARRIER_FUNCTIONS_ENABLED
static void continuous_carrier_abort(void);
static void modulated_carrier_abort(void);
#endif // NRF_802154_CARRIER_FUNCTIONS_ENABLED
static void energy_detection_abort(void);
/** Clear flags describing frame being received. */
void rx_flags_clear(void)
{
m_flags.missing_receive_buffer = false;
#if !NRF_802154_DISABLE_BCC_MATCHING
m_flags.psdu_being_received = false;
#endif // !NRF_802154_DISABLE_BCC_MATCHING
}
static void * volatile mp_receive_buffer;
static void txpower_set(nrf_radio_txpower_t txpower)
{
#ifdef NRF53_SERIES
bool radio_high_voltage_enable = false;
if ((int8_t)txpower > 0)
{
/* To get higher than 0dBm raise operating voltage of the radio, giving 3dBm power boost */
radio_high_voltage_enable = true;
txpower -= 3;
}
nrf_vreqctrl_radio_high_voltage_set(NRF_VREQCTRL_NS, radio_high_voltage_enable);
#endif
nrf_radio_txpower_set(NRF_RADIO, txpower);
}
/** Initialize TIMER peripheral used by the driver. */
void nrf_timer_init(void)
{
nrf_timer_task_trigger(NRF_802154_TIMER_INSTANCE, NRF_TIMER_TASK_SHUTDOWN);
nrf_timer_mode_set(NRF_802154_TIMER_INSTANCE, NRF_TIMER_MODE_TIMER);
nrf_timer_bit_width_set(NRF_802154_TIMER_INSTANCE, NRF_TIMER_BIT_WIDTH_32);
nrf_timer_prescaler_set(NRF_802154_TIMER_INSTANCE, NRF_TIMER_FREQ_1MHz);
#if NRF_802154_DISABLE_BCC_MATCHING
// Setup timer for detecting PSDU reception.
nrf_timer_mode_set(NRF_802154_COUNTER_TIMER_INSTANCE, NRF_TIMER_MODE_COUNTER);
nrf_timer_bit_width_set(NRF_802154_COUNTER_TIMER_INSTANCE, NRF_TIMER_BIT_WIDTH_8);
#endif
}
/** Reset radio peripheral. */
static void nrf_radio_reset(void)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
nrf_radio_power_set(NRF_RADIO, false);
nrf_radio_power_set(NRF_RADIO, true);
nrf_802154_log_global_event(NRF_802154_LOG_VERBOSITY_LOW,
NRF_802154_LOG_GLOBAL_EVENT_ID_RADIO_RESET,
0U);
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}
static void channel_set(uint8_t channel)
{
assert(channel >= 11U && channel <= 26U);
nrf_radio_frequency_set(NRF_RADIO, 2405U + 5U * (channel - 11U));
}
static void cca_configuration_update(void)
{
nrf_802154_cca_cfg_t cca_cfg;
nrf_802154_pib_cca_cfg_get(&cca_cfg);
nrf_radio_cca_configure(NRF_RADIO,
cca_cfg.mode,
nrf_802154_rssi_cca_ed_threshold_corrected_get(cca_cfg.ed_threshold),
cca_cfg.corr_threshold,
cca_cfg.corr_limit);
}
/** Initialize interrupts for radio peripheral. */
static void irq_init(void)
{
#if NRF_802154_INTERNAL_RADIO_IRQ_HANDLING
nrf_802154_irq_init(RADIO_IRQn, NRF_802154_IRQ_PRIORITY, nrf_802154_radio_irq_handler);
#endif
nrf_802154_irq_enable(RADIO_IRQn);
}
static void trigger_disable_to_start_rampup(void)
{
if (!nrf_802154_trx_ppi_for_ramp_up_was_triggered())
{
nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_DISABLE);
}
}
/** Configure FEM to set LNA at appropriate time. */
static void fem_for_lna_set(void)
{
if (mpsl_fem_lna_configuration_set(&m_activate_rx_cc0, NULL) == 0)
{
nrf_timer_shorts_enable(m_activate_rx_cc0.event.timer.p_timer_instance,
NRF_TIMER_SHORT_COMPARE0_STOP_MASK);
nrf_802154_trx_ppi_for_fem_set();
}
}
/** Reset FEM configuration for LNA. */
static void fem_for_lna_reset(void)
{
mpsl_fem_lna_configuration_clear();
nrf_timer_task_trigger(NRF_802154_TIMER_INSTANCE, NRF_TIMER_TASK_SHUTDOWN);
nrf_timer_shorts_disable(NRF_802154_TIMER_INSTANCE, NRF_TIMER_SHORT_COMPARE0_STOP_MASK);
nrf_802154_trx_ppi_for_fem_clear();
/* There is no need to explicitly deactivate LNA pin during reset as mpsl_fem_abort_set is used
* to provide a deactivation mechanism on DISABLED event.
*/
}
/** Configure FEM to set PA at appropriate time.
*
* @note This function must be called before ramp up PPIs are configured.
*/
static void fem_for_pa_set(const mpsl_fem_gain_t * p_fem_gain_data)
{
(void)mpsl_fem_pa_gain_set(p_fem_gain_data);
if (mpsl_fem_pa_configuration_set(&m_activate_tx_cc0, NULL) == 0)
{
nrf_timer_shorts_enable(m_activate_tx_cc0.event.timer.p_timer_instance,
NRF_TIMER_SHORT_COMPARE0_STOP_MASK);
nrf_802154_trx_ppi_for_fem_set();
}
}
/** Reset FEM configuration for PA.
*
* @note This function must be called before ramp up PPIs are configured.
*/
static void fem_for_pa_reset(void)
{
mpsl_fem_pa_configuration_clear();
nrf_timer_task_trigger(NRF_802154_TIMER_INSTANCE, NRF_TIMER_TASK_SHUTDOWN);
nrf_802154_trx_ppi_for_fem_clear();
mpsl_fem_deactivate_now(MPSL_FEM_PA);
}
/** Configure FEM for TX procedure.
*
* @note This function must be called before ramp up PPIs are configured.
*/
static void fem_for_tx_set(bool cca, const mpsl_fem_gain_t * p_fem_gain_data)
{
bool success;
(void)mpsl_fem_pa_gain_set(p_fem_gain_data);
if (cca)
{
bool pa_set = false;
bool lna_set = false;
if (mpsl_fem_lna_configuration_set(&m_activate_rx_cc0, &m_ccaidle) == 0)
{
lna_set = true;
}
if (mpsl_fem_pa_configuration_set(&m_ccaidle, NULL) == 0)
{
pa_set = true;
}
success = pa_set || lna_set;
}
else
{
success = (mpsl_fem_pa_configuration_set(&m_activate_tx_cc0, NULL) == 0);
}
if (success)
{
nrf_timer_shorts_enable(NRF_802154_TIMER_INSTANCE, NRF_TIMER_SHORT_COMPARE0_STOP_MASK);
nrf_802154_trx_ppi_for_fem_set();
}
}
static void fem_for_tx_reset(bool cca)
{
nrf_timer_shorts_disable(NRF_802154_TIMER_INSTANCE, NRF_TIMER_SHORT_COMPARE0_STOP_MASK);
if (cca)
{
mpsl_fem_lna_configuration_clear();
mpsl_fem_pa_configuration_clear();
}
else
{
mpsl_fem_pa_configuration_clear();
}
nrf_802154_trx_ppi_for_fem_clear();
nrf_802154_trx_ppi_for_ramp_up_propagation_delay_wait();
nrf_timer_task_trigger(NRF_802154_TIMER_INSTANCE, NRF_TIMER_TASK_SHUTDOWN);
}
#if defined(NRF52840_XXAA) || \
defined(NRF52833_XXAA)
/** @brief Applies DEVICE-CONFIG-254.
*
* Shall be called after every RADIO peripheral reset.
*/
static void device_config_254_apply_tx(void)
{
uint32_t ficr_reg1 = *(volatile uint32_t *)0x10000330UL;
uint32_t ficr_reg2 = *(volatile uint32_t *)0x10000334UL;
uint32_t ficr_reg3 = *(volatile uint32_t *)0x10000338UL;
/* Check if the device is fixed by testing every FICR register's value separately. */
if (ficr_reg1 != 0xffffffffUL)
{
volatile uint32_t * p_radio_reg1 = (volatile uint32_t *)0x4000174cUL;
*p_radio_reg1 = ficr_reg1;
}
if (ficr_reg2 != 0xffffffffUL)
{
volatile uint32_t * p_radio_reg2 = (volatile uint32_t *)0x40001584UL;
*p_radio_reg2 = ficr_reg2;
}
if (ficr_reg3 != 0xffffffffUL)
{
volatile uint32_t * p_radio_reg3 = (volatile uint32_t *)0x40001588UL;
*p_radio_reg3 = ficr_reg3;
}
}
#endif
/** @brief Applies ERRATA-117
*
* Shall be called after setting RADIO mode to NRF_RADIO_MODE_IEEE802154_250KBIT.
*/
#if defined(NRF5340_XXAA)
static void errata_117_apply(void)
{
/* Register at 0x01FF0084. */
uint32_t ficr_reg = *(volatile uint32_t *)(FICR_BASE + 0x84UL);
/* Register at 0x41008588. */
volatile uint32_t * p_radio_reg = (volatile uint32_t *)(RADIO_BASE + 0x588UL);
*p_radio_reg = ficr_reg;
}
#endif
void nrf_802154_trx_module_reset(void)
{
m_trx_state = TRX_STATE_DISABLED;
m_timer_value_on_radio_end_event = 0;
m_transmit_with_cca = false;
mp_receive_buffer = NULL;
memset(&m_flags, 0, sizeof(m_flags));
}
void nrf_802154_trx_init(void)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
nrf_802154_trx_module_reset();
#if defined(RADIO_INTENSET_SYNC_Msk)
nrf_802154_swi_init();
#endif
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}
void nrf_802154_trx_enable(void)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
assert(m_trx_state == TRX_STATE_DISABLED);
nrf_timer_init();
nrf_radio_reset();
#if defined(NRF52840_XXAA) || \
defined(NRF52833_XXAA)
// Apply DEVICE-CONFIG-254 if needed.
if (mpsl_fem_device_config_254_apply_get())
{
device_config_254_apply_tx();
}
#endif
nrf_radio_packet_conf_t packet_conf;
nrf_radio_mode_set(NRF_RADIO, NRF_RADIO_MODE_IEEE802154_250KBIT);
#if defined(NRF5340_XXAA)
// Apply ERRATA-117 after setting RADIO mode to NRF_RADIO_MODE_IEEE802154_250KBIT.
errata_117_apply();
#endif
memset(&packet_conf, 0, sizeof(packet_conf));
packet_conf.lflen = 8;
packet_conf.plen = NRF_RADIO_PREAMBLE_LENGTH_32BIT_ZERO;
packet_conf.crcinc = true;
packet_conf.maxlen = MAX_PACKET_SIZE;
nrf_radio_packet_configure(NRF_RADIO, &packet_conf);
nrf_radio_modecnf0_set(NRF_RADIO, true, 0);
// Configure CRC
nrf_radio_crc_configure(NRF_RADIO, CRC_LENGTH, NRF_RADIO_CRC_ADDR_IEEE802154, CRC_POLYNOMIAL);
nrf_802154_trx_ppi_for_enable();
// Configure CCA
cca_configuration_update();
// Set channel
channel_set(nrf_802154_pib_channel_get());
// Custom initialization operations
nrf_802154_custom_part_of_radio_init();
irq_init();
assert(nrf_radio_shorts_get(NRF_RADIO) == SHORTS_IDLE);
#if defined(NRF52840_XXAA) || \
defined(NRF52833_XXAA)
mpsl_fem_abort_set(nrf_radio_event_address_get(NRF_RADIO, NRF_RADIO_EVENT_DISABLED),
PPI_CHGRP_ABORT);
#elif defined(NRF53_SERIES)
mpsl_fem_abort_set(NRF_802154_DPPI_RADIO_DISABLED,
0U); /* The group parameter is ignored by FEM for nRF53 */
#endif
mpsl_fem_deactivate_now(MPSL_FEM_ALL);
m_trx_state = TRX_STATE_IDLE;
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}
static void ppi_all_clear(void)
{
switch (m_trx_state)
{
case TRX_STATE_IDLE:
case TRX_STATE_GOING_IDLE:
case TRX_STATE_RXFRAME_FINISHED:
case TRX_STATE_FINISHED:
// Intentionally empty. PPIs are not configured in this state.
break;
case TRX_STATE_RXFRAME:
rxframe_finish_disable_ppis();
break;
case TRX_STATE_RXACK:
rxack_finish_disable_ppis();
nrf_802154_trx_ppi_for_fem_clear();
break;
case TRX_STATE_TXFRAME:
txframe_finish_disable_ppis(m_transmit_with_cca);
nrf_802154_trx_ppi_for_fem_clear();
break;
case TRX_STATE_TXACK:
nrf_802154_trx_ppi_for_ack_tx_clear();
// FEM PPIs are not configured for this state. TIMER was started in TRX_STATE_RXFRAME
// and PPIs starting timer were cleared when exiting TRX_STATE_RXFRAME.
break;
case TRX_STATE_STANDALONE_CCA:
nrf_802154_trx_ppi_for_ramp_up_clear(NRF_RADIO_TASK_RXEN, false);
nrf_802154_trx_ppi_for_fem_clear();
break;
#if NRF_802154_CARRIER_FUNCTIONS_ENABLED
case TRX_STATE_CONTINUOUS_CARRIER:
nrf_802154_trx_ppi_for_ramp_up_clear(NRF_RADIO_TASK_TXEN, false);
nrf_802154_trx_ppi_for_fem_clear();
break;
case TRX_STATE_MODULATED_CARRIER:
nrf_802154_trx_ppi_for_ramp_up_clear(NRF_RADIO_TASK_TXEN, false);
nrf_802154_trx_ppi_for_fem_clear();
break;
#endif // NRF_802154_CARRIER_FUNCTIONS_ENABLED
case TRX_STATE_ENERGY_DETECTION:
nrf_802154_trx_ppi_for_ramp_up_clear(NRF_RADIO_TASK_RXEN, false);
nrf_802154_trx_ppi_for_fem_clear();
break;
default:
assert(false);
}
nrf_802154_trx_ppi_for_disable();
}
static void fem_power_down_now(void)
{
mpsl_fem_deactivate_now(MPSL_FEM_ALL);
mpsl_fem_disable();
}
void nrf_802154_trx_disable(void)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
if (m_trx_state != TRX_STATE_DISABLED)
{
nrf_radio_power_set(NRF_RADIO, false);
nrf_802154_irq_clear_pending(RADIO_IRQn);
/* While the RADIO is powered off deconfigure any PPIs used directly by trx module */
ppi_all_clear();
#if !NRF_802154_DISABLE_BCC_MATCHING && defined(RADIO_INTENSET_SYNC_Msk)
nrf_egu_int_disable(NRF_802154_EGU_INSTANCE, EGU_SYNC_INTMASK);
nrf_egu_event_clear(NRF_802154_EGU_INSTANCE, EGU_SYNC_EVENT);
#endif
/* Stop & deconfigure timer */
nrf_timer_shorts_disable(NRF_802154_TIMER_INSTANCE,
NRF_TIMER_SHORT_COMPARE0_STOP_MASK |
NRF_TIMER_SHORT_COMPARE1_STOP_MASK);
nrf_timer_task_trigger(NRF_802154_TIMER_INSTANCE, NRF_TIMER_TASK_SHUTDOWN);
nrf_radio_power_set(NRF_RADIO, true);
mpsl_fem_lna_configuration_clear();
mpsl_fem_pa_configuration_clear();
mpsl_fem_abort_clear();
if (m_trx_state != TRX_STATE_IDLE)
{
fem_power_down_now();
}
#if NRF_802154_DISABLE_BCC_MATCHING
// Anomaly 78: use SHUTDOWN instead of STOP and CLEAR.
nrf_timer_task_trigger(NRF_802154_COUNTER_TIMER_INSTANCE, NRF_TIMER_TASK_SHUTDOWN);
nrf_timer_shorts_disable(NRF_802154_COUNTER_TIMER_INSTANCE,
NRF_TIMER_SHORT_COMPARE1_STOP_MASK);
#else
m_flags.psdu_being_received = false;
#endif
m_flags.missing_receive_buffer = false;
m_flags.rssi_started = false;
#if NRF_802154_TX_STARTED_NOTIFY_ENABLED
m_flags.tx_started = false;
#endif
m_trx_state = TRX_STATE_DISABLED;
nrf_802154_log_global_event(NRF_802154_LOG_VERBOSITY_LOW,
NRF_802154_LOG_GLOBAL_EVENT_ID_RADIO_RESET,
0U);
}
else
{
// Intentionally empty
}
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}
static void rx_automatic_antenna_handle(void)
{
switch (m_trx_state)
{
case TRX_STATE_RXFRAME:
case TRX_STATE_RXFRAME_FINISHED:
nrf_802154_sl_ant_div_rx_started_notify();
break;
case TRX_STATE_ENERGY_DETECTION:
// Intentionally empty - notification is called from core when requesting energy detection.
// This is done due to possibility of nrf_802154_trx_energy_detection being called multiple times
// during one energy detection from the point of view of application, if the entire procedure does not
// fit in single timeslot.
break;
case TRX_STATE_TXACK:
nrf_802154_sl_ant_div_txack_notify();
break;
default:
assert(false);
break;
}
}
/**
* Updates the antenna for reception, according to antenna diversity configuration.
*/
static void rx_antenna_update(void)
{
bool result = true;
nrf_802154_sl_ant_div_mode_t mode = nrf_802154_sl_ant_div_cfg_mode_get(
NRF_802154_SL_ANT_DIV_OP_RX);
switch (mode)
{
case NRF_802154_SL_ANT_DIV_MODE_DISABLED:
break;
case NRF_802154_SL_ANT_DIV_MODE_MANUAL:
result = nrf_802154_sl_ant_div_antenna_set(
nrf_802154_sl_ant_div_cfg_antenna_get(NRF_802154_SL_ANT_DIV_OP_RX));
break;
case NRF_802154_SL_ANT_DIV_MODE_AUTO:
rx_automatic_antenna_handle();
break;
default:
assert(false);
break;
}
assert(result);
(void)result;
}
/**
* Updates the antenna for transmission, according to antenna diversity configuration.
*
* Antenna diversity for tx is not currently supported. If antenna diversity is not
* in disabled state, default antenna will always be used for transmission.
*/
static void tx_antenna_update(void)
{
bool result = true;
nrf_802154_sl_ant_div_mode_t mode = nrf_802154_sl_ant_div_cfg_mode_get(
NRF_802154_SL_ANT_DIV_OP_TX);
switch (mode)
{
case NRF_802154_SL_ANT_DIV_MODE_DISABLED:
/* Intentionally empty. */
break;
case NRF_802154_SL_ANT_DIV_MODE_MANUAL:
result = nrf_802154_sl_ant_div_antenna_set(
nrf_802154_sl_ant_div_cfg_antenna_get(NRF_802154_SL_ANT_DIV_OP_TX));
break;
case NRF_802154_SL_ANT_DIV_MODE_AUTO:
default:
assert(false);
break;
}
if (!result)
{
assert(false);
}
}
void nrf_802154_trx_antenna_update(void)
{
assert(m_trx_state != TRX_STATE_DISABLED);
switch (m_trx_state)
{
case TRX_STATE_RXFRAME:
case TRX_STATE_RXFRAME_FINISHED:
case TRX_STATE_ENERGY_DETECTION:
case TRX_STATE_TXACK:
rx_antenna_update();
break;
case TRX_STATE_STANDALONE_CCA:
case TRX_STATE_RXACK:
case TRX_STATE_TXFRAME:
#if NRF_802154_CARRIER_FUNCTIONS_ENABLED
case TRX_STATE_CONTINUOUS_CARRIER:
case TRX_STATE_MODULATED_CARRIER:
tx_antenna_update();
break;
#endif // NRF_802154_CARRIER_FUNCTIONS_ENABLED
default:
/* Intentionally empty */
break;
}
}
void nrf_802154_trx_channel_set(uint8_t channel)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
channel_set(channel);
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}
void nrf_802154_trx_cca_configuration_update(void)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
cca_configuration_update();
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}
/** Check if PSDU is currently being received.
*
* @returns True if radio is receiving PSDU, false otherwise.
*/
bool nrf_802154_trx_psdu_is_being_received(void)
{
#if NRF_802154_DISABLE_BCC_MATCHING
nrf_timer_task_trigger(NRF_802154_COUNTER_TIMER_INSTANCE,
nrf_timer_capture_task_get(NRF_TIMER_CC_CHANNEL0));
uint32_t counter = nrf_timer_cc_get(NRF_802154_COUNTER_TIMER_INSTANCE, NRF_TIMER_CC_CHANNEL0);
assert(counter <= 1);
return counter > 0;
#else // NRF_802154_DISABLE_BCC_MATCHING
return m_flags.psdu_being_received;
#endif // NRF_802154_DISABLE_BCC_MATCHING
}
bool nrf_802154_trx_receive_is_buffer_missing(void)
{
switch (m_trx_state)
{
case TRX_STATE_RXFRAME:
/* no break */
case TRX_STATE_RXACK:
return m_flags.missing_receive_buffer;
default:
assert(!m_flags.missing_receive_buffer);
return false;
}
}
static void receive_buffer_missing_buffer_set(void * p_receive_buffer)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
uint32_t shorts = SHORTS_IDLE;
switch (m_trx_state)
{
case TRX_STATE_RXFRAME:
shorts = SHORTS_RX | SHORTS_RX_FREE_BUFFER;
break;
case TRX_STATE_RXACK:
shorts = SHORTS_RX_ACK | SHORTS_RX_FREE_BUFFER;
break;
default:
assert(false);
}
m_flags.missing_receive_buffer = false;
nrf_radio_packetptr_set(NRF_RADIO, p_receive_buffer);
nrf_radio_shorts_set(NRF_RADIO, shorts);
if (nrf_radio_state_get(NRF_RADIO) == NRF_RADIO_STATE_RXIDLE)
{
nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_START);
}
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
}
bool nrf_802154_trx_receive_buffer_set(void * p_receive_buffer)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);
bool result = false;
mp_receive_buffer = p_receive_buffer;
if ((p_receive_buffer != NULL) && m_flags.missing_receive_buffer)
{
receive_buffer_missing_buffer_set(p_receive_buffer);
result = true;
}
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
return result;
}
void nrf_802154_trx_receive_frame(uint8_t bcc,
nrf_802154_trx_receive_notifications_t notifications_mask,
const nrf_802154_fal_tx_power_split_t * p_ack_tx_power)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
uint32_t ints_to_enable = 0U;
uint32_t shorts = SHORTS_RX;
// Force the TIMER to be stopped and count from 0.
nrf_timer_task_trigger(NRF_802154_TIMER_INSTANCE, NRF_TIMER_TASK_SHUTDOWN);
m_trx_state = TRX_STATE_RXFRAME;
// Clear filtering flag
rx_flags_clear();
// Clear the RSSI measurement flag.
m_flags.rssi_started = false;
m_flags.rssi_settled = false;
txpower_set(p_ack_tx_power->radio_tx_power);
if (mp_receive_buffer != NULL)
{
m_flags.missing_receive_buffer = false;
nrf_radio_packetptr_set(NRF_RADIO, mp_receive_buffer);
shorts |= SHORTS_RX_FREE_BUFFER;
}
else
{
m_flags.missing_receive_buffer = true;
}
// Set shorts
nrf_radio_shorts_set(NRF_RADIO, shorts);
// Set BCC
#if !NRF_802154_DISABLE_BCC_MATCHING
assert(bcc != 0U);
nrf_radio_bcc_set(NRF_RADIO, bcc * 8U);
#else
assert(bcc == 0U);
(void)bcc;
#endif // !NRF_802154_DISABLE_BCC_MATCHING
// Enable IRQs
#if !NRF_802154_DISABLE_BCC_MATCHING || NRF_802154_NOTIFY_CRCERROR
nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_CRCERROR);
ints_to_enable |= NRF_RADIO_INT_CRCERROR_MASK;
#endif // !NRF_802154_DISABLE_BCC_MATCHING ||NRF_802154_NOTIFY_CRCERROR
#if !NRF_802154_DISABLE_BCC_MATCHING
nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_BCMATCH);
ints_to_enable |= NRF_RADIO_INT_BCMATCH_MASK;
#endif // !NRF_802154_DISABLE_BCC_MATCHING
nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_CRCOK);
ints_to_enable |= NRF_RADIO_INT_CRCOK_MASK;
if ((notifications_mask & TRX_RECEIVE_NOTIFICATION_STARTED) != 0U)
{
nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_ADDRESS);
ints_to_enable |= NRF_RADIO_INT_ADDRESS_MASK;
}
bool allow_sync_swi = false;
#ifdef RADIO_INTENSET_SYNC_Msk
if (((notifications_mask & TRX_RECEIVE_NOTIFICATION_PRESTARTED) != 0U) ||
(NRF_802154_SL_ANT_DIV_MODE_DISABLED !=
nrf_802154_sl_ant_div_cfg_mode_get(NRF_802154_SL_ANT_DIV_OP_RX)))
{
allow_sync_swi = true;
}
#endif
if (allow_sync_swi)
{
#if (NRF_802154_DISABLE_BCC_MATCHING || !defined(RADIO_INTENSET_SYNC_Msk))
assert(false);
#else
// The RADIO can't generate interrupt on EVENT_SYNC. Path to generate interrupt:
// RADIO.EVENT_SYNC -> PPI_RADIO_SYNC_EGU_SYNC -> EGU.TASK_SYNC -> EGU.EVENT_SYNC ->
// SWI_IRQHandler (in nrf_802154_swi.c), calls nrf_802154_trx_swi_irq_handler
nrf_802154_trx_ppi_for_radio_sync_set(EGU_SYNC_TASK);
nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_SYNC);
nrf_egu_event_clear(NRF_802154_EGU_INSTANCE, EGU_SYNC_EVENT);
nrf_egu_int_enable(NRF_802154_EGU_INSTANCE, EGU_SYNC_INTMASK);
#endif
}
nrf_radio_int_enable(NRF_RADIO, ints_to_enable);
// Set FEM
uint32_t delta_time;
if (mpsl_fem_lna_configuration_set(&m_activate_rx_cc0, NULL) == 0)
{
delta_time = nrf_timer_cc_get(NRF_802154_TIMER_INSTANCE,
NRF_TIMER_CC_CHANNEL0);
}
else
{
delta_time = 1;
nrf_timer_cc_set(NRF_802154_TIMER_INSTANCE, NRF_TIMER_CC_CHANNEL0, delta_time);
}
// Set FEM PA gain for ACK transmission
mpsl_fem_pa_gain_set(&p_ack_tx_power->fem);
m_timer_value_on_radio_end_event = delta_time;
// Select antenna
nrf_802154_trx_antenna_update();
// Let the TIMER stop on last event required by a FEM
nrf_timer_shorts_enable(NRF_802154_TIMER_INSTANCE,
NRF_TIMER_SHORT_COMPARE0_STOP_MASK);
#if NRF_802154_DISABLE_BCC_MATCHING
nrf_timer_shorts_enable(NRF_802154_COUNTER_TIMER_INSTANCE, NRF_TIMER_SHORT_COMPARE1_STOP_MASK);
nrf_timer_cc_set(NRF_802154_COUNTER_TIMER_INSTANCE, NRF_TIMER_CC_CHANNEL1, 1);
#endif // NRF_802154_DISABLE_BCC_MATCHING
// Set PPIs
#if NRF_802154_DISABLE_BCC_MATCHING
#error Configuration unsupported anymore
#endif // NRF_802154_DISABLE_BCC_MATCHING
nrf_802154_trx_ppi_for_ramp_up_set(NRF_RADIO_TASK_RXEN, true);
trigger_disable_to_start_rampup();
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}
void nrf_802154_trx_receive_ack(void)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
uint32_t shorts = SHORTS_RX_ACK;
uint32_t ints_to_enable = 0U;
m_trx_state = TRX_STATE_RXACK;
if (mp_receive_buffer != NULL)
{
m_flags.missing_receive_buffer = false;
nrf_radio_packetptr_set(NRF_RADIO, mp_receive_buffer);
shorts |= SHORTS_RX_FREE_BUFFER;
}
else
{
m_flags.missing_receive_buffer = true;
}
nrf_radio_shorts_set(NRF_RADIO, shorts);
nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_ADDRESS);
ints_to_enable |= NRF_RADIO_INT_ADDRESS_MASK;
nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_CRCOK);
ints_to_enable |= NRF_RADIO_INT_CRCOK_MASK;
nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_CRCERROR);
ints_to_enable |= NRF_RADIO_INT_CRCERROR_MASK;
nrf_radio_int_enable(NRF_RADIO, ints_to_enable);
fem_for_lna_set();
nrf_802154_trx_antenna_update();
nrf_802154_trx_ppi_for_ramp_up_set(NRF_RADIO_TASK_RXEN, false);
trigger_disable_to_start_rampup();
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}
bool nrf_802154_trx_rssi_measure(void)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
bool result = false;
if (m_trx_state == TRX_STATE_RXFRAME)
{
// When TRX is in RXFRAME the RADIO may be also ramping down after previous operation or still ramping up
nrf_radio_state_t radio_state = nrf_radio_state_get(NRF_RADIO);
if ((radio_state == RADIO_STATE_STATE_RxIdle) || (radio_state == RADIO_STATE_STATE_Rx))
{
if (!m_flags.rssi_settled)
{
// This operation is requested first time after nrf_802154_trx_receive_frame has been called
// Radio is ramped up, but we need to wait RSSISETTLE time.
// Precisely, we need to wait NRF_RADIO_EVENT_RSSIEND between READY->START short worked
// and RSSI_START task is triggered. Due to limited resources we assume worst case and
// wait whole time when rssi_measure is requested first time after nrf_802154_trx_receive_frame.
nrf_802154_delay_us(RSSI_SETTLE_TIME_US);
m_flags.rssi_settled = true;
}
nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_RSSIEND);
nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_RSSISTART);
m_flags.rssi_started = true;
result = true;
}
else
{
// The RADIO may be:
// - still ramping down after transmit
// - ramping up for receive
// - ramping down after frame is received (shorts)
}
}
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
return result;
}
bool nrf_802154_trx_rssi_measure_is_started(void)
{
return m_flags.rssi_started;
}
uint8_t nrf_802154_trx_rssi_last_sample_get(void)
{
return nrf_radio_rssi_sample_get(NRF_RADIO);
}
bool nrf_802154_trx_rssi_sample_is_available(void)
{
return nrf_radio_event_check(NRF_RADIO, NRF_RADIO_EVENT_RSSIEND);
}
void nrf_802154_trx_transmit_frame(const void * p_transmit_buffer,
bool cca,
const nrf_802154_fal_tx_power_split_t * p_tx_power,
nrf_802154_trx_transmit_notifications_t notifications_mask)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
uint32_t ints_to_enable = 0U;
// Force the TIMER to be stopped and count from 0.
nrf_timer_task_trigger(NRF_802154_TIMER_INSTANCE, NRF_TIMER_TASK_SHUTDOWN);
m_trx_state = TRX_STATE_TXFRAME;
m_transmit_with_cca = cca;
txpower_set(p_tx_power->radio_tx_power);
nrf_radio_packetptr_set(NRF_RADIO, p_transmit_buffer);
// Set shorts
if (cca)
{
nrf_radio_shorts_set(NRF_RADIO, SHORTS_CCA_TX);
}
else
{
nrf_radio_shorts_set(NRF_RADIO, SHORTS_TX);
}
// Enable IRQs
nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_PHYEND);
ints_to_enable |= NRF_RADIO_INT_PHYEND_MASK;
if (cca)
{
nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_CCABUSY);
ints_to_enable |= NRF_RADIO_INT_CCABUSY_MASK;
if ((notifications_mask & TRX_TRANSMIT_NOTIFICATION_CCAIDLE) != 0U)
{
nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_CCAIDLE);
ints_to_enable |= NRF_RADIO_INT_CCAIDLE_MASK;
}
if ((notifications_mask & TRX_TRANSMIT_NOTIFICATION_CCASTARTED) != 0U)
{
nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_READY);
ints_to_enable |= NRF_RADIO_INT_READY_MASK;
}
}
nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_ADDRESS);
#if NRF_802154_TX_STARTED_NOTIFY_ENABLED
ints_to_enable |= NRF_RADIO_INT_ADDRESS_MASK;
m_flags.tx_started = false;
#endif // NRF_802154_TX_STARTED_NOTIFY_ENABLED
nrf_radio_int_enable(NRF_RADIO, ints_to_enable);
fem_for_tx_set(cca, &p_tx_power->fem);
nrf_802154_trx_antenna_update();
nrf_802154_trx_ppi_for_ramp_up_set(cca ? NRF_RADIO_TASK_RXEN : NRF_RADIO_TASK_TXEN, false);
trigger_disable_to_start_rampup();
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}
bool nrf_802154_trx_transmit_ack(const void * p_transmit_buffer, uint32_t delay_us)
{
/* Assumptions on peripherals
* TIMER is running, is counting from value saved in m_timer_value_on_radio_end_event,
* which trigered on END event, which happened EVENT_LAT us after frame on air receive was finished.
* RADIO is DISABLED
* PPIs are DISABLED
*/
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
bool result = false;
assert(m_trx_state == TRX_STATE_RXFRAME_FINISHED);
assert(p_transmit_buffer != NULL);
m_trx_state = TRX_STATE_TXACK;
// Set TIMER's CC to the moment when ramp-up should occur.
if (delay_us <= TXRU_TIME + EVENT_LAT)
{
nrf_timer_task_trigger(NRF_802154_TIMER_INSTANCE, NRF_TIMER_TASK_SHUTDOWN);
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
return result;
}
uint32_t timer_cc_ramp_up_start = m_timer_value_on_radio_end_event + delay_us - TXRU_TIME -
EVENT_LAT;
nrf_timer_cc_set(NRF_802154_TIMER_INSTANCE,
NRF_TIMER_CC_CHANNEL1,
timer_cc_ramp_up_start);
nrf_radio_packetptr_set(NRF_RADIO, p_transmit_buffer);
// Set shorts
nrf_radio_shorts_set(NRF_RADIO, SHORTS_TX_ACK);
// Clear TXREADY event to detect if PPI worked
nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_TXREADY);
// Set FEM
// Note: the TIMER is running, ramp up will start in timer_cc_ramp_up_start tick
// Assumption here is that FEM activation takes no more than TXRU_TIME.
m_activate_tx_cc0_timeshifted = m_activate_tx_cc0;
// Set the moment for FEM at which real transmission starts.
m_activate_tx_cc0_timeshifted.event.timer.counter_period.end = timer_cc_ramp_up_start +
TXRU_TIME;
if (mpsl_fem_pa_configuration_set(&m_activate_tx_cc0_timeshifted, NULL) == 0)
{
// FEM scheduled its operations on timer, so the timer must be running until last
// operation scheduled by the FEM (TIMER's CC0), which is later than radio ramp up
nrf_timer_shorts_enable(NRF_802154_TIMER_INSTANCE, NRF_TIMER_SHORT_COMPARE0_STOP_MASK);
}
else
{
// FEM didn't schedule anything on timer, so the timer may be stopped when radio ramp-up
// is triggered
nrf_timer_shorts_enable(NRF_802154_TIMER_INSTANCE, NRF_TIMER_SHORT_COMPARE1_STOP_MASK);
}
// Select antenna
nrf_802154_trx_antenna_update();
nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_PHYEND);
#if NRF_802154_TX_STARTED_NOTIFY_ENABLED
nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_ADDRESS);
#endif
// Set PPIs
nrf_802154_trx_ppi_for_ack_tx_set();
// Since this point the transmission is armed on TIMER's CC1
// Detect if PPI will work in future or has just worked.
nrf_timer_task_trigger(NRF_802154_TIMER_INSTANCE, NRF_TIMER_TASK_CAPTURE3);
uint32_t timer_cc_now = nrf_timer_cc_get(NRF_802154_TIMER_INSTANCE,
NRF_TIMER_CC_CHANNEL3);
uint32_t timer_cc_fem_start = nrf_timer_cc_get(NRF_802154_TIMER_INSTANCE,
NRF_TIMER_CC_CHANNEL0);
// When external PA uses a timer, it should be configured to a time later than ramp up time. In
// such case, the timer stops with shorts on PA timer. But if external PA does not use a timer,
// FEM time is set to a value in the past that was used by LNA. After the timer overflows,
// the timer stops with a short on the past value used by LNA. We have to detect if the current
// timer value is after the overflow.
if ((timer_cc_now < timer_cc_ramp_up_start) &&
((timer_cc_fem_start >= timer_cc_ramp_up_start) || (timer_cc_now > timer_cc_fem_start)))
{
result = true;
}
else
{
nrf_802154_trx_ppi_for_ramp_up_propagation_delay_wait();
if (nrf_radio_state_get(NRF_RADIO) == NRF_RADIO_STATE_TXRU)
{
result = true;
}
else if (nrf_radio_event_check(NRF_RADIO, NRF_RADIO_EVENT_TXREADY))
{
result = true;
}
else
{
result = false;
}
}
if (result)
{
uint32_t ints_to_enable = NRF_RADIO_INT_PHYEND_MASK;
#if NRF_802154_TX_STARTED_NOTIFY_ENABLED
ints_to_enable |= NRF_RADIO_INT_ADDRESS_MASK;
#endif // NRF_802154_TX_STARTED_NOTIFY_ENABLED
nrf_radio_int_enable(NRF_RADIO, ints_to_enable);
}
else
{
/* We were to late with setting up PPI_TIMER_ACK, ack transmission was not triggered and
* will not be triggered in future.
*/
nrf_802154_trx_ppi_for_ack_tx_clear();
/* As the timer was running during operation, it is possible we were able to configure
* FEM thus it may trigger in future or may started PA activation.
*/
mpsl_fem_pa_configuration_clear();
mpsl_fem_deactivate_now(MPSL_FEM_PA);
nrf_timer_task_trigger(NRF_802154_TIMER_INSTANCE, NRF_TIMER_TASK_SHUTDOWN);
/* No callbacks will be called */
}
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
return result;
}
static inline void wait_until_radio_is_disabled(void)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);
bool radio_is_disabled = false;
// RADIO should enter DISABLED state after no longer than RX ramp-down time, which is equal
// approximately 0.5us. Taking a bold assumption that a single iteration of the loop takes
// one cycle to complete, 32 iterations would amount to exactly 0.5 us of execution time.
// Please note that this approach ignores software latency completely, i.e. RADIO should
// have changed state already before entering this function due to ISR processing delays.
for (uint32_t i = 0; i < MAX_RXRAMPDOWN_CYCLES; i++)
{
if (nrf_radio_state_get(NRF_RADIO) == NRF_RADIO_STATE_DISABLED)
{
radio_is_disabled = true;
break;
}
}
assert(radio_is_disabled);
(void)radio_is_disabled;
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}
static void rxframe_finish_disable_ppis(void)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);
nrf_802154_trx_ppi_for_ramp_up_clear(NRF_RADIO_TASK_RXEN, true);
#if defined(RADIO_INTENSET_SYNC_Msk)
nrf_802154_trx_ppi_for_radio_sync_clear(EGU_SYNC_TASK);
#endif
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}
static void rxframe_finish_disable_fem_activation(void)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);
// Disable LNA activation
mpsl_fem_lna_configuration_clear();
// Disable short used by LNA activation
nrf_timer_shorts_disable(NRF_802154_TIMER_INSTANCE,
NRF_TIMER_SHORT_COMPARE0_STOP_MASK);
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}
static void rxframe_finish_disable_ints(void)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);
uint32_t ints_to_disable = NRF_RADIO_INT_CRCOK_MASK | NRF_RADIO_INT_ADDRESS_MASK;
#if !NRF_802154_DISABLE_BCC_MATCHING || NRF_802154_NOTIFY_CRCERROR
ints_to_disable |= NRF_RADIO_INT_CRCERROR_MASK;
#endif // !NRF_802154_DISABLE_BCC_MATCHING || NRF_802154_NOTIFY_CRCERROR
#if !NRF_802154_DISABLE_BCC_MATCHING
ints_to_disable |= NRF_RADIO_INT_BCMATCH_MASK;
#endif // !NRF_802154_DISABLE_BCC_MATCHING
nrf_radio_int_disable(NRF_RADIO, ints_to_disable);
#if !NRF_802154_DISABLE_BCC_MATCHING && defined(RADIO_INTENSET_SYNC_Msk)
nrf_egu_int_disable(NRF_802154_EGU_INSTANCE, EGU_SYNC_INTMASK);
#endif
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}
static void rxframe_finish_psdu_is_not_being_received(void)
{
#if NRF_802154_DISABLE_BCC_MATCHING
// Anomaly 78: use SHUTDOWN instead of STOP and CLEAR.
nrf_timer_task_trigger(NRF_802154_COUNTER_TIMER_INSTANCE, NRF_TIMER_TASK_SHUTDOWN);
nrf_timer_shorts_disable(NRF_802154_COUNTER_TIMER_INSTANCE, NRF_TIMER_SHORT_COMPARE1_STOP_MASK);
#else
m_flags.psdu_being_received = false;
#endif // NRF_802154_DISABLE_BCC_MATCHING
}
static void rxframe_finish(void)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);
/* Note that CRCOK/CRCERROR event is generated several cycles before END event.
*
* Below shown what is happening in the hardware
*
* TIMER is started by path:
* RADIO.SHORT_END_DISABLE -> RADIO.TASKS_DISABLE -> RADIO.EVENTS_DISABLED ->
* PPI_DISABLED_EGU -> EGU.TASKS_TRIGGER -> EGU.EVENTS_TRIGGERED -> PPI_EGU_TIMER_START -> TIMER.TASKS_START
*
* FEM's LNA mode is disabled by path:
* RADIO.SHORT_END_DISABLE -> RADIO.TASKS_DISABLE -> RADIO.EVENTS_DISABLED -> (FEM's PPI triggering disable LNA operation,
* see mpsl_fem_abort_set() )
*
* RADIO will not ramp up, as PPI_EGU_RAMP_UP channel is self-disabling, and
* it was disabled when receive ramp-up was started.
*/
wait_until_radio_is_disabled(); // This includes waiting since CRCOK/CRCERROR (several cycles) event until END
// and then during RXDISABLE state (0.5us)
nrf_802154_trx_ppi_for_ramp_up_propagation_delay_wait();
/* Now it is guaranteed, that:
* - FEM operation to disable LNA mode is triggered through FEM's PPIs
* - TIMER is started again allowing operation of nrf_802154_trx_transmit_ack
*/
rxframe_finish_disable_ppis();
rxframe_finish_disable_fem_activation();
rxframe_finish_psdu_is_not_being_received();
rxframe_finish_disable_ints();
nrf_radio_shorts_set(NRF_RADIO, SHORTS_IDLE);
m_flags.missing_receive_buffer = false;
/* Current state of peripherals:
* RADIO is DISABLED
* FEM is powered but LNA mode has just been turned off
* TIMER is running, counting from the value stored in m_timer_value_on_radio_end_event
* All PPIs used by receive operation are disabled, forks are cleared, PPI groups that were used are cleared
* RADIO.SHORTS are cleared
*/
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}
void nrf_802154_trx_abort(void)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
switch (m_trx_state)
{
case TRX_STATE_DISABLED:
case TRX_STATE_IDLE:
case TRX_STATE_FINISHED:
/* Nothing to do, intentionally empty */
break;
case TRX_STATE_GOING_IDLE:
go_idle_abort();
break;
case TRX_STATE_RXFRAME:
receive_frame_abort();
break;
case TRX_STATE_RXFRAME_FINISHED:
nrf_timer_task_trigger(NRF_802154_TIMER_INSTANCE, NRF_TIMER_TASK_SHUTDOWN);
m_trx_state = TRX_STATE_FINISHED;
break;
case TRX_STATE_RXACK:
receive_ack_abort();
break;
case TRX_STATE_TXFRAME:
transmit_frame_abort();
break;
case TRX_STATE_TXACK:
transmit_ack_abort();
break;
case TRX_STATE_STANDALONE_CCA:
standalone_cca_abort();
break;
#if NRF_802154_CARRIER_FUNCTIONS_ENABLED
case TRX_STATE_CONTINUOUS_CARRIER:
continuous_carrier_abort();
break;
case TRX_STATE_MODULATED_CARRIER:
modulated_carrier_abort();
break;
#endif // NRF_802154_CARRIER_FUNCTIONS_ENABLED
case TRX_STATE_ENERGY_DETECTION:
energy_detection_abort();
break;
default:
assert(false);
}
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}
trx_state_t nrf_802154_trx_state_get(void)
{
return m_trx_state;
}
static void go_idle_from_state_finished(void)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);
m_trx_state = TRX_STATE_GOING_IDLE;
nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_DISABLED);
nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_DISABLE);
nrf_radio_int_enable(NRF_RADIO, NRF_RADIO_INT_DISABLED_MASK);
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}
bool nrf_802154_trx_go_idle(void)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
bool result = false;
switch (m_trx_state)
{
case TRX_STATE_DISABLED:
assert(false);
break;
case TRX_STATE_IDLE:
/* There will be no callout */
break;
case TRX_STATE_GOING_IDLE:
/* There will be callout */
result = true;
break;
case TRX_STATE_RXFRAME_FINISHED:
nrf_timer_task_trigger(NRF_802154_TIMER_INSTANCE, NRF_TIMER_TASK_SHUTDOWN);
/* Fallthrough */
case TRX_STATE_FINISHED:
go_idle_from_state_finished();
result = true;
break;
default:
assert(false);
}
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
return result;
}
static void go_idle_abort(void)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);
nrf_radio_int_disable(NRF_RADIO, NRF_RADIO_INT_DISABLED_MASK);
m_trx_state = TRX_STATE_FINISHED;
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}
static void receive_frame_abort(void)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);
rxframe_finish_disable_ppis();
rxframe_finish_disable_fem_activation();
rxframe_finish_psdu_is_not_being_received();
rxframe_finish_disable_ints();
nrf_radio_shorts_set(NRF_RADIO, SHORTS_IDLE);
m_flags.missing_receive_buffer = false;
nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_DISABLE);
nrf_timer_task_trigger(NRF_802154_TIMER_INSTANCE, NRF_TIMER_TASK_SHUTDOWN);
m_trx_state = TRX_STATE_FINISHED;
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}
static void rxack_finish_disable_ppis(void)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);
nrf_802154_trx_ppi_for_ramp_up_clear(NRF_RADIO_TASK_RXEN, false);
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}
static void rxack_finish_disable_ints(void)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);
nrf_radio_int_disable(NRF_RADIO,
NRF_RADIO_INT_ADDRESS_MASK | NRF_RADIO_INT_CRCERROR_MASK |
NRF_RADIO_INT_CRCOK_MASK);
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}
static void rxack_finish_disable_fem_activation(void)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);
// Disable LNA activation
mpsl_fem_lna_configuration_clear();
// Clear LNA PPIs
nrf_802154_trx_ppi_for_fem_clear();
// Disable short used by LNA activation
nrf_timer_shorts_disable(NRF_802154_TIMER_INSTANCE,
NRF_TIMER_SHORT_COMPARE0_STOP_MASK);
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}
static void rxack_finish(void)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);
rxack_finish_disable_ppis();
rxack_finish_disable_ints();
rxack_finish_disable_fem_activation();
nrf_timer_task_trigger(NRF_802154_TIMER_INSTANCE, NRF_TIMER_TASK_SHUTDOWN);
nrf_radio_shorts_set(NRF_RADIO, SHORTS_IDLE);
m_flags.missing_receive_buffer = false;
/* Current state of peripherals
* RADIO is DISABLED
* FEM is powered but LNA mode has just been turned off
* TIMER is shutdown
* PPIs used by receive operation are disabled, forks are cleared, PPI groups used are cleared
* RADIO.SHORTS are cleared
*/
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}
static void receive_ack_abort(void)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);
rxack_finish_disable_ppis();
rxack_finish_disable_ints();
rxack_finish_disable_fem_activation();
nrf_radio_shorts_set(NRF_RADIO, SHORTS_IDLE);
m_flags.missing_receive_buffer = false;
nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_DISABLE);
nrf_timer_task_trigger(NRF_802154_TIMER_INSTANCE, NRF_TIMER_TASK_SHUTDOWN);
m_trx_state = TRX_STATE_FINISHED;
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}
void nrf_802154_trx_standalone_cca(void)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
assert((m_trx_state == TRX_STATE_IDLE) || (m_trx_state == TRX_STATE_FINISHED));
m_trx_state = TRX_STATE_STANDALONE_CCA;
// Set shorts
nrf_radio_shorts_set(NRF_RADIO, SHORTS_CCA);
// Enable IRQs
nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_CCABUSY);
nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_CCAIDLE);
nrf_radio_int_enable(NRF_RADIO, NRF_RADIO_INT_CCABUSY_MASK | NRF_RADIO_INT_CCAIDLE_MASK);
// Set FEM
fem_for_lna_set();
// Select antenna
nrf_802154_trx_antenna_update();
// Set PPIs
nrf_802154_trx_ppi_for_ramp_up_set(NRF_RADIO_TASK_RXEN, false);
trigger_disable_to_start_rampup();
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}
static void standalone_cca_finish(void)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);
nrf_802154_trx_ppi_for_ramp_up_clear(NRF_RADIO_TASK_RXEN, false);
nrf_radio_shorts_set(NRF_RADIO, SHORTS_IDLE);
fem_for_lna_reset();
nrf_radio_int_disable(NRF_RADIO, NRF_RADIO_INT_CCABUSY_MASK | NRF_RADIO_INT_CCAIDLE_MASK);
nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_CCASTOP);
nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_DISABLE);
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}
static void standalone_cca_abort(void)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);
standalone_cca_finish();
m_trx_state = TRX_STATE_FINISHED;
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}
#if NRF_802154_CARRIER_FUNCTIONS_ENABLED
void nrf_802154_trx_continuous_carrier(const nrf_802154_fal_tx_power_split_t * p_tx_power)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
assert((m_trx_state == TRX_STATE_IDLE) || (m_trx_state == TRX_STATE_FINISHED));
m_trx_state = TRX_STATE_CONTINUOUS_CARRIER;
// Set Tx Power
txpower_set(p_tx_power->radio_tx_power);
// Set FEM
fem_for_pa_set(&p_tx_power->fem);
// Select antenna
nrf_802154_trx_antenna_update();
// Set PPIs
nrf_802154_trx_ppi_for_ramp_up_set(NRF_RADIO_TASK_TXEN, false);
trigger_disable_to_start_rampup();
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}
void nrf_802154_trx_continuous_carrier_restart(void)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
assert(m_trx_state == TRX_STATE_CONTINUOUS_CARRIER);
// Continuous carrier PPIs are configured without self-disabling
// Triggering RADIO.TASK_DISABLE causes ramp-down -> RADIO.EVENTS_DISABLED -> EGU.TASK -> EGU.EVENT ->
// RADIO.TASK_TXEN -> ramp_up -> new continous carrier
nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_DISABLE);
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}
static void continuous_carrier_abort(void)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);
nrf_802154_trx_ppi_for_ramp_up_clear(NRF_RADIO_TASK_TXEN, false);
fem_for_pa_reset();
nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_DISABLE);
m_trx_state = TRX_STATE_FINISHED;
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}
void nrf_802154_trx_modulated_carrier(const void * p_transmit_buffer,
const nrf_802154_fal_tx_power_split_t * p_tx_power)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
assert((m_trx_state == TRX_STATE_IDLE) || (m_trx_state == TRX_STATE_FINISHED));
assert(p_transmit_buffer != NULL);
m_trx_state = TRX_STATE_MODULATED_CARRIER;
// Set Tx Power
txpower_set(p_tx_power->radio_tx_power);
// Set Tx buffer
nrf_radio_packetptr_set(NRF_RADIO, p_transmit_buffer);
// Set shorts
nrf_radio_shorts_set(NRF_RADIO, SHORTS_MOD_CARRIER);
// Set FEM
fem_for_pa_set(&p_tx_power->fem);
// Select antenna
nrf_802154_trx_antenna_update();
// Set PPIs
nrf_802154_trx_ppi_for_ramp_up_set(NRF_RADIO_TASK_TXEN, false);
trigger_disable_to_start_rampup();
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}
void nrf_802154_trx_modulated_carrier_restart(void)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
assert(m_trx_state == TRX_STATE_MODULATED_CARRIER);
// Modulated carrier PPIs are configured without self-disabling
// Triggering RADIO.TASK_DISABLE causes ramp-down -> RADIO.EVENTS_DISABLED -> EGU.TASK -> EGU.EVENT ->
// RADIO.TASK_TXEN -> ramp_up -> new modulated carrier
nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_DISABLE);
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}
static void modulated_carrier_abort()
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);
nrf_802154_trx_ppi_for_ramp_up_clear(NRF_RADIO_TASK_TXEN, false);
nrf_radio_shorts_set(NRF_RADIO, SHORTS_IDLE);
fem_for_pa_reset();
nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_DISABLE);
m_trx_state = TRX_STATE_FINISHED;
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}
#endif // NRF_802154_CARRIER_FUNCTIONS_ENABLED
void nrf_802154_trx_energy_detection(uint32_t ed_count)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
assert((m_trx_state == TRX_STATE_FINISHED) || (m_trx_state == TRX_STATE_IDLE));
m_trx_state = TRX_STATE_ENERGY_DETECTION;
ed_count--;
/* Check that vd_count will fit into defined bits of register */
assert( (ed_count & (~RADIO_EDCNT_EDCNT_Msk)) == 0U);
nrf_radio_ed_loop_count_set(NRF_RADIO, ed_count);
// Set shorts
nrf_radio_shorts_set(NRF_RADIO, SHORTS_ED);
// Enable IRQs
nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_EDEND);
nrf_radio_int_enable(NRF_RADIO, NRF_RADIO_INT_EDEND_MASK);
// Set FEM
fem_for_lna_set();
// Select antenna
nrf_802154_trx_antenna_update();
// Set PPIs
nrf_802154_trx_ppi_for_ramp_up_set(NRF_RADIO_TASK_RXEN, false);
trigger_disable_to_start_rampup();
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}
static void energy_detection_finish(void)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);
nrf_802154_trx_ppi_for_ramp_up_clear(NRF_RADIO_TASK_RXEN, false);
fem_for_lna_reset();
nrf_radio_int_disable(NRF_RADIO, NRF_RADIO_INT_EDEND_MASK);
nrf_radio_shorts_set(NRF_RADIO, SHORTS_IDLE);
nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_EDSTOP);
nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_DISABLE);
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}
static void energy_detection_abort(void)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);
energy_detection_finish();
m_trx_state = TRX_STATE_FINISHED;
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}
static void irq_handler_ready(void)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
assert(m_trx_state == TRX_STATE_TXFRAME);
nrf_radio_int_disable(NRF_RADIO, NRF_RADIO_INT_READY_MASK);
nrf_802154_trx_transmit_frame_ccastarted();
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}
static void irq_handler_address(void)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
switch (m_trx_state)
{
case TRX_STATE_RXFRAME:
nrf_802154_trx_receive_frame_started();
break;
case TRX_STATE_RXACK:
m_flags.rssi_started = true;
nrf_802154_trx_receive_ack_started();
break;
#if NRF_802154_TX_STARTED_NOTIFY_ENABLED
case TRX_STATE_TXFRAME:
nrf_radio_int_disable(NRF_RADIO, NRF_RADIO_INT_ADDRESS_MASK);
m_flags.tx_started = true;
nrf_802154_trx_transmit_frame_started();
break;
#endif // NRF_802154_TX_STARTED_NOTIFY_ENABLED
#if NRF_802154_TX_STARTED_NOTIFY_ENABLED
case TRX_STATE_TXACK:
nrf_radio_int_disable(NRF_RADIO, NRF_RADIO_INT_ADDRESS_MASK);
nrf_802154_trx_transmit_ack_started();
break;
#endif
default:
assert(false);
}
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}
#if !NRF_802154_DISABLE_BCC_MATCHING
static void irq_handler_bcmatch(void)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
uint8_t current_bcc;
uint8_t next_bcc;
assert(m_trx_state == TRX_STATE_RXFRAME);
m_flags.psdu_being_received = true;
// If CRCERROR event is set, it means that events are handled out of order due to software
// latency. Just skip this handler in this case - frame will be dropped.
if (nrf_radio_event_check(NRF_RADIO, NRF_RADIO_EVENT_CRCERROR))
{
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
return;
}
current_bcc = nrf_radio_bcc_get(NRF_RADIO) / 8U;
next_bcc = nrf_802154_trx_receive_frame_bcmatched(current_bcc);
if (next_bcc > current_bcc)
{
/* Note: If we don't make it before given octet is received by RADIO bcmatch will not be triggered.
* The fact that filtering may be not completed at the call to nrf_802154_trx_receive_received handler
* should be handled by the handler.
*/
nrf_radio_bcc_set(NRF_RADIO, next_bcc * 8);
}
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}
#endif
#if !NRF_802154_DISABLE_BCC_MATCHING || NRF_802154_NOTIFY_CRCERROR
static void irq_handler_crcerror(void)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
switch (m_trx_state)
{
case TRX_STATE_RXFRAME:
#if NRF_802154_DISABLE_BCC_MATCHING
/* The hardware restarts receive at the moment.
* The TIMER is being shutdown and restarted by the hardware see
* PPI_CRCERROR_CLEAR in nrf_802154_trx_receive_frame
*/
#else
rxframe_finish();
/* On crc error TIMER is not needed, no ACK may be sent */
nrf_timer_task_trigger(NRF_802154_TIMER_INSTANCE, NRF_TIMER_TASK_SHUTDOWN);
m_trx_state = TRX_STATE_FINISHED;
#endif
nrf_802154_trx_receive_frame_crcerror();
break;
case TRX_STATE_RXACK:
rxack_finish();
m_trx_state = TRX_STATE_FINISHED;
nrf_802154_trx_receive_ack_crcerror();
break;
default:
assert(false);
}
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}
#endif
static void irq_handler_crcok(void)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
switch (m_trx_state)
{
case TRX_STATE_RXFRAME:
m_flags.rssi_started = true;
rxframe_finish();
m_trx_state = TRX_STATE_RXFRAME_FINISHED;
nrf_802154_trx_receive_frame_received();
break;
case TRX_STATE_RXACK:
rxack_finish();
m_trx_state = TRX_STATE_FINISHED;
nrf_802154_trx_receive_ack_received();
break;
default:
assert(false);
}
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}
static void txframe_finish_disable_ppis(bool cca)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);
nrf_802154_trx_ppi_for_ramp_up_clear(cca ? NRF_RADIO_TASK_RXEN : NRF_RADIO_TASK_TXEN, false);
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}
static void txframe_finish_disable_ints(void)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);
nrf_radio_int_disable(NRF_RADIO,
NRF_RADIO_INT_PHYEND_MASK |
NRF_RADIO_INT_CCAIDLE_MASK |
NRF_RADIO_INT_CCABUSY_MASK |
NRF_RADIO_INT_ADDRESS_MASK |
NRF_RADIO_INT_READY_MASK);
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}
static void txframe_finish(void)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);
/* Below shown what is happening in the hardware
*
* Due to short RADIO.SHORT_PHYEND_DISABLE the RADIO is in TXDISABLE (currently ramping down for 21us) state or
* has already reached DISABLED state (if we entered into ISR with greater latency)
*
* Even if RADIO reaches state DISABLED (event DISABLED was triggered by short), no transmission will be triggered
* as PPI_EGU_RAMP_UP is self-disabling PPI channel.
*
* If FEM is in use the PPI_EGU_TIMER_START might be triggered if radio reached DISABLED state,
* so the TIMER may start counting from the value on which FEM activation finished. The TIMER's CC registers
* are set in the past so even if TIMER started no spurious FEM PA activation will occur.
* We need to disable PPI_EGU_TIMER_START and then shutdown TIMER as it is not used.
*/
txframe_finish_disable_ppis(m_transmit_with_cca);
fem_for_tx_reset(m_transmit_with_cca);
txframe_finish_disable_ints();
nrf_radio_shorts_set(NRF_RADIO, SHORTS_IDLE);
#if NRF_802154_TX_STARTED_NOTIFY_ENABLED
m_flags.tx_started = false;
#endif
m_flags.missing_receive_buffer = false;
/* Current state of peripherals
* RADIO is either in TXDISABLE or DISABLED
* FEM is powered but PA mode will be turned off on entry into DISABLED state or is already turned off
* TIMER is shutdown
* All PPIs that were used are disabled (forks are cleared if used)
* RADIO.SHORTS are cleared
*/
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}
static void transmit_frame_abort(void)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);
txframe_finish_disable_ppis(m_transmit_with_cca);
nrf_radio_shorts_set(NRF_RADIO, SHORTS_IDLE);
fem_for_tx_reset(m_transmit_with_cca);
txframe_finish_disable_ints();
#if NRF_802154_TX_STARTED_NOTIFY_ENABLED
m_flags.tx_started = false;
#endif
m_flags.missing_receive_buffer = false;
nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_CCASTOP);
nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_DISABLE);
m_trx_state = TRX_STATE_FINISHED;
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}
static void txack_finish(void)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);
/* Below shown what is happening in the hardware
*
* Due to short RADIO.SHORT_PHYEND_DISABLE the RADIO is in TXDISABLE (currently ramping down for 21us) state or
* has already reached DISABLED state (if we entered into ISR with greater latency)
*
* Even if RADIO reaches state DISABLED (event DISABLED was triggered by short), no transmission will be triggered
* as only PPI_TIMER_TX_ACK was enabled.
*
* FEM will disable PA mode on RADIO.DISABLED event
*
* The TIMER was counting to trigger RADIO ramp up and FEM (if required). The TIMER is configured
* to trigger STOP task on one of these events (whichever is later). As we finished the TIMER is
* stopped now, and there is no PPIs starting it automatically by the hardware.
*/
nrf_802154_trx_ppi_for_ack_tx_clear();
nrf_radio_shorts_set(NRF_RADIO, SHORTS_IDLE);
mpsl_fem_pa_configuration_clear();
nrf_timer_shorts_disable(NRF_802154_TIMER_INSTANCE,
NRF_TIMER_SHORT_COMPARE0_STOP_MASK |
NRF_TIMER_SHORT_COMPARE1_STOP_MASK);
// Anomaly 78: use SHUTDOWN instead of STOP and CLEAR.
nrf_timer_task_trigger(NRF_802154_TIMER_INSTANCE, NRF_TIMER_TASK_SHUTDOWN);
nrf_radio_int_disable(NRF_RADIO, NRF_RADIO_INT_PHYEND_MASK | NRF_RADIO_INT_ADDRESS_MASK);
/* Current state of peripherals
* RADIO is either in TXDISABLE or DISABLED
* FEM is powered but PA mode will be turned off on entry into DISABLED state or is already turned off
* TIMER is shutdown
* All PPIs that were used are disabled (forks are cleared if used)
* RADIO.SHORTS are cleared
*/
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}
static void transmit_ack_abort(void)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);
nrf_802154_trx_ppi_for_ack_tx_clear();
nrf_radio_shorts_set(NRF_RADIO, SHORTS_IDLE);
mpsl_fem_pa_configuration_clear();
nrf_timer_shorts_disable(NRF_802154_TIMER_INSTANCE,
NRF_TIMER_SHORT_COMPARE0_STOP_MASK |
NRF_TIMER_SHORT_COMPARE1_STOP_MASK);
// Anomaly 78: use SHUTDOWN instead of STOP and CLEAR.
nrf_timer_task_trigger(NRF_802154_TIMER_INSTANCE, NRF_TIMER_TASK_SHUTDOWN);
nrf_radio_int_disable(NRF_RADIO, NRF_RADIO_INT_PHYEND_MASK | NRF_RADIO_INT_ADDRESS_MASK);
nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_DISABLE);
m_trx_state = TRX_STATE_FINISHED;
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}
static void irq_handler_phyend(void)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
switch (m_trx_state)
{
case TRX_STATE_TXFRAME:
txframe_finish();
m_trx_state = TRX_STATE_FINISHED;
nrf_802154_trx_transmit_frame_transmitted();
break;
case TRX_STATE_TXACK:
txack_finish();
m_trx_state = TRX_STATE_FINISHED;
nrf_802154_trx_transmit_ack_transmitted();
break;
default:
assert(false);
}
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}
static void go_idle_finish(void)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);
nrf_radio_int_disable(NRF_RADIO, NRF_RADIO_INT_DISABLED_MASK);
fem_power_down_now();
m_trx_state = TRX_STATE_IDLE;
nrf_802154_trx_go_idle_finished();
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
}
static void irq_handler_disabled(void)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
switch (m_trx_state)
{
case TRX_STATE_GOING_IDLE:
go_idle_finish();
break;
default:
assert(false);
}
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}
static void irq_handler_ccaidle(void)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
switch (m_trx_state)
{
case TRX_STATE_STANDALONE_CCA:
standalone_cca_finish();
m_trx_state = TRX_STATE_FINISHED;
nrf_802154_trx_standalone_cca_finished(true);
break;
case TRX_STATE_TXFRAME:
nrf_802154_trx_transmit_frame_ccaidle();
break;
default:
assert(false);
}
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}
static void irq_handler_ccabusy(void)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
switch (m_trx_state)
{
case TRX_STATE_TXFRAME:
assert(m_transmit_with_cca);
txframe_finish();
m_trx_state = TRX_STATE_FINISHED;
nrf_802154_trx_transmit_frame_ccabusy();
break;
case TRX_STATE_STANDALONE_CCA:
standalone_cca_finish();
m_trx_state = TRX_STATE_FINISHED;
nrf_802154_trx_standalone_cca_finished(false);
break;
default:
assert(false);
}
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}
static void irq_handler_edend(void)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
assert(m_trx_state == TRX_STATE_ENERGY_DETECTION);
uint8_t ed_sample = nrf_radio_ed_sample_get(NRF_RADIO);
energy_detection_finish();
m_trx_state = TRX_STATE_FINISHED;
nrf_802154_trx_energy_detection_finished(ed_sample);
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}
#if defined(RADIO_INTENSET_SYNC_Msk)
static void irq_handler_sync(void)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
assert(m_trx_state == TRX_STATE_RXFRAME);
nrf_802154_trx_receive_frame_prestarted();
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}
#endif
void nrf_802154_radio_irq_handler(void)
{
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
// Prevent interrupting of this handler by requests from higher priority code.
nrf_802154_critical_section_forcefully_enter();
#if defined(RADIO_INTENSET_SYNC_Msk)
// Note: For NRF_RADIO_EVENT_SYNC we enable interrupt through EGU.
// That's why we check here EGU's EGU_SYNC_INTMASK.
// The RADIO does not have interrupt from SYNC event.
if (nrf_egu_int_enable_check(NRF_802154_EGU_INSTANCE, EGU_SYNC_INTMASK) &&
nrf_radio_event_check(NRF_RADIO, NRF_RADIO_EVENT_SYNC))
{
nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_SYNC);
nrf_egu_event_clear(NRF_802154_EGU_INSTANCE, EGU_SYNC_EVENT);
irq_handler_sync();
}
#endif
if (nrf_radio_int_enable_check(NRF_RADIO, NRF_RADIO_INT_READY_MASK) &&
nrf_radio_event_check(NRF_RADIO, NRF_RADIO_EVENT_READY))
{
nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_READY);
irq_handler_ready();
}
if (nrf_radio_int_enable_check(NRF_RADIO, NRF_RADIO_INT_ADDRESS_MASK) &&
nrf_radio_event_check(NRF_RADIO, NRF_RADIO_EVENT_ADDRESS))
{
nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_ADDRESS);
irq_handler_address();
}
#if !NRF_802154_DISABLE_BCC_MATCHING
// Check MAC frame header.
if (nrf_radio_int_enable_check(NRF_RADIO, NRF_RADIO_INT_BCMATCH_MASK) &&
nrf_radio_event_check(NRF_RADIO, NRF_RADIO_EVENT_BCMATCH))
{
nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_BCMATCH);
irq_handler_bcmatch();
}
#endif // !NRF_802154_DISABLE_BCC_MATCHING
#if !NRF_802154_DISABLE_BCC_MATCHING || NRF_802154_NOTIFY_CRCERROR
if (nrf_radio_int_enable_check(NRF_RADIO, NRF_RADIO_INT_CRCERROR_MASK) &&
nrf_radio_event_check(NRF_RADIO, NRF_RADIO_EVENT_CRCERROR))
{
nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_CRCERROR);
irq_handler_crcerror();
}
#endif // !NRF_802154_DISABLE_BCC_MATCHING || NRF_802154_NOTIFY_CRCERROR
if (nrf_radio_int_enable_check(NRF_RADIO, NRF_RADIO_INT_CRCOK_MASK) &&
nrf_radio_event_check(NRF_RADIO, NRF_RADIO_EVENT_CRCOK))
{
nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_CRCOK);
irq_handler_crcok();
}
if (nrf_radio_int_enable_check(NRF_RADIO, NRF_RADIO_INT_PHYEND_MASK) &&
nrf_radio_event_check(NRF_RADIO, NRF_RADIO_EVENT_PHYEND))
{
nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_PHYEND);
irq_handler_phyend();
}
if (nrf_radio_int_enable_check(NRF_RADIO, NRF_RADIO_INT_DISABLED_MASK) &&
nrf_radio_event_check(NRF_RADIO, NRF_RADIO_EVENT_DISABLED))
{
nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_DISABLED);
irq_handler_disabled();
}
if (nrf_radio_int_enable_check(NRF_RADIO, NRF_RADIO_INT_CCAIDLE_MASK) &&
nrf_radio_event_check(NRF_RADIO, NRF_RADIO_EVENT_CCAIDLE))
{
nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_CCAIDLE);
irq_handler_ccaidle();
}
if (nrf_radio_int_enable_check(NRF_RADIO, NRF_RADIO_INT_CCABUSY_MASK) &&
nrf_radio_event_check(NRF_RADIO, NRF_RADIO_EVENT_CCABUSY))
{
nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_CCABUSY);
irq_handler_ccabusy();
}
if (nrf_radio_int_enable_check(NRF_RADIO, NRF_RADIO_INT_EDEND_MASK) &&
nrf_radio_event_check(NRF_RADIO, NRF_RADIO_EVENT_EDEND))
{
nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_EDEND);
irq_handler_edend();
}
nrf_802154_critical_section_exit();
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
}
#if NRF_802154_INTERNAL_RADIO_IRQ_HANDLING
void RADIO_IRQHandler(void)
{
nrf_802154_radio_irq_handler();
}
#endif // NRF_802154_INTERNAL_RADIO_IRQ_HANDLING
#if defined(RADIO_INTENSET_SYNC_Msk)
void nrf_802154_trx_swi_irq_handler(void)
{
// If this handler is preempted by MARGIN, RADIO IRQ might be set to pending
// and executed after MARGIN processing is finished, i.e. after the end of a timeslot.
// To prevent that from happening, the handler is executed with disabled interrupts.
nrf_802154_mcu_critical_state_t mcu_crit_state;
nrf_802154_mcu_critical_enter(mcu_crit_state);
if (nrf_egu_int_enable_check(NRF_802154_EGU_INSTANCE, EGU_SYNC_INTMASK) &&
nrf_egu_event_check(NRF_802154_EGU_INSTANCE, EGU_SYNC_EVENT))
{
nrf_egu_event_clear(NRF_802154_EGU_INSTANCE, EGU_SYNC_EVENT);
// We are in SWI_IRQHandler, which priority is usually lower than RADIO_IRQHandler.
// To avoid problems with critical sections, trigger RADIO_IRQ manually.
// - If we are not in critical section, RADIO_IRQ will start shortly (calling
// nrf_802154_radio_irq_handler) preempting current SWI_IRQHandler. From
// nrf_802154_radio_irq_handler we acquire critical section and
// process sync event.
// If we are in critical section, the RADIO_IRQ is disabled on NVIC.
// Following will make it pending, and processing of RADIO_IRQ will start
// when critical section is left.
nrf_802154_irq_set_pending(RADIO_IRQn);
}
nrf_802154_mcu_critical_exit(mcu_crit_state);
}
#endif
const nrf_802154_sl_event_handle_t * nrf_802154_trx_radio_end_event_handle_get(void)
{
static const nrf_802154_sl_event_handle_t r = {
#if defined(DPPI_PRESENT)
.event_addr = NRF_802154_DPPI_RADIO_END,
.shared = true
#else
.event_addr = (uint32_t)&NRF_RADIO->EVENTS_END
#endif
};
return &r;
}
const nrf_802154_sl_event_handle_t * nrf_802154_trx_radio_ready_event_handle_get(void)
{
static const nrf_802154_sl_event_handle_t r = {
#if defined(DPPI_PRESENT)
.event_addr = NRF_802154_DPPI_RADIO_READY,
.shared = true
#else
.event_addr = (uint32_t)&NRF_RADIO->EVENTS_READY
#endif
};
return &r;
}
const nrf_802154_sl_event_handle_t * nrf_802154_trx_radio_crcok_event_handle_get(void)
{
static const nrf_802154_sl_event_handle_t r = {
.event_addr = (uint32_t)&NRF_RADIO->EVENTS_CRCOK,
#if defined(DPPI_PRESENT)
.shared = false
#endif
};
return &r;
}
const nrf_802154_sl_event_handle_t * nrf_802154_trx_radio_phyend_event_handle_get(void)
{
static const nrf_802154_sl_event_handle_t r = {
#if defined(DPPI_PRESENT)
.event_addr = NRF_802154_DPPI_RADIO_PHYEND,
.shared = true
#else
.event_addr = (uint32_t)&NRF_RADIO->EVENTS_PHYEND
#endif
};
return &r;
}