EFR32#

While RAIL attempts to be chip-agnostic, certain hardware specifics can't be overlooked. Where possible, missing features will be simulated in software, but performance characteristics may vary. This section covers EFR32-specific content including hardware-specific configurations or calibrations.

Clocks#

High-Frequency Clocks#

The EFR32 has a configurable clock tree with a number of different prescalars. One thing to keep in mind, however, is that the radio must always run off of an HFXO at 38.4 MHz. This means that you must switch your HFCLK source to the HFXO before calling RAIL_Init() and make sure to leave the CMU->HFPRESC set to 1 to prevent dividing down the radio clock. You are free to scale the peripheral (CMU->HFPERPRESC) or Cortex (CMU->HFCOREPRESC) clocks using their prescalars, which are further down the clock tree. Keep in mind that this may introduce extra wait states when interacting with the radio and slow down some operations. On EFR32xG22, we limit going to EM1P sleep mode when an 80MHz HRFRCO PLL system clock is selected. Going to EM1P sleep may cause a noticeable clock drift which would impact radio timing and tuning.

Low-Frequency Clocks#

RAIL can use a low-frequency clock on the EFR32 to synchronize its time base across EM2 sleep. This only happens if you call RAIL_ConfigSleep with RAIL_SleepConfig_t::RAIL_SLEEP_CONFIG_TIMERSYNC_ENABLED. On the EFR32xG1 and EFR32xG12 this uses the RTCC's clock which is the LFE clock with a possible additional prescalar. The slower the clock source, the longer it will take to synchronize because the process requires several ticks of the RTCC clock. EFR32xG13 and EFR32xG14 have a separate timer to help with the synchronization, but either the LFXO or LFRCO are still used as a clock. Note that to use this internal timer the cmuClock_HFLE clock must always be enabled. When calling RAIL_ConfigSleep(), the code will first try to set up the timer with the LFXO if it's started, then fall back to the LFRCO, and finally assert if no LF clocks are running.

Receive and Transmit FIFO Buffers#

The RAIL library for the EFR32 optionally allocates an internal buffer to store receive data contiguously. Its required alignment is platform-dependent (EFR32 Series 1 and EFR32xG21 require byte alignment while EFR32xG22 and later platforms require 4-byte alignment; applications can use RAIL_FIFO_ALIGNMENT or the RAIL_FIFO_ALIGNMENT_TYPE to ensure conformance).

The buffer is set to 512 bytes at build time by default, but can be changed by calling RAIL_SetRxFifo, for example in an implementation of RAILCb_SetupRxFifo. The receive FIFO's size must be a power of 2 between 64 and 4096 bytes for hardware compatibility.

The chosen buffer size limits the maximum size of receive packets in packet mode and determines the size of the receive FIFO in FIFO mode. Because each receive packet has several bytes of overhead, you can only receive up to one (buffer size - overhead) byte packet without switching to FIFO mode. In FIFO mode, you must read out packet data as you approach this limit and store it off to construct the full packet later. This overhead is currently 8 bytes on all EFR32 platforms except EFR32xG1 where it is 6 bytes. Note that this overhead may increase or decrease in future releases as the functionality is changed though large jumps are not expected in either direction.

Supplementing the receive FIFO used for packet data, the RAIL library also allocates an internal packet metadata FIFO capable of holding up to 16 packets' worth of metadata. An entry in this FIFO is made on every packet completion (successful or not) signaled to the application. If the application calls RAIL_HoldRxPacket() that entry is held along with any of its packet data in the receive FIFO. When either the receive FIFO or internal packet metadata FIFO fills or overflows, RAIL_EVENT_RX_FIFO_FULL or RAIL_EVENT_RX_FIFO_OVERFLOW occur at packet completion time, respectively.

The transmit FIFO must be set at runtime with RAIL_SetTxFifo(). Like the receive FIFO the transmit FIFO's required alignment is platform-dependent (EFR32 Series 1 and EFR32xG21 require byte alignment while EFR32xG22 and later platforms require 4-byte alignment; applications can use RAIL_FIFO_ALIGNMENT or the RAIL_FIFO_ALIGNMENT_TYPE to ensure conformance).

The transmit FIFO's size has the same restrictions as the receive FIFO. The size must be a power of 2 from 64 to 4096 for hardware compatibility. Note that there is no difference between packet and FIFO mode on the transmit side. A packet may either be loaded all at once or in pieces using the RAIL_WriteTxFifo() function. You may also use the RAIL_SetTxFifoThreshold() function and the RAIL_EVENT_TX_FIFO_ALMOST_EMPTY event to load data as there is space available in the FIFO during transmission.

RAIL also provides an internal transmit AutoAck FIFO whose size is fixed at RAIL_AUTOACK_MAX_LENGTH bytes. Applications can write to it via RAIL_WriteAutoAckFifo(). Since RAIL does not permit the queuing of multiple transmits, there is no need for any internal transmit metadata FIFO like the one which exists on the receive side.

Data Reception Sources#

When receiving data, you can configure hardware to provide data from three different hardware sources.

First, you can configure the hardware to provide a packet of information. This configuration uses the built-in demodulator and frame controller. Use RAIL_RxDataSource_t::RX_PACKET_DATA to enable this receive source.

Second, you can configure hardware to provide data directly from the demodulator. In this mode, hardware demodulation is used, but the user is responsible for implementing frame controller functionality. In other words, preamble detection, sync word detection, CRC validation, and so on must be performed in software by the application. All data returned is represented as 8-bit 2's-compliment values. Use RAIL_RxDataSource_t::RX_DEMOD_DATA to enable the receive source.

Third, you can configure hardware to provide data directly from the I and Q ADCs. In this mode, the user is responsible for implementing demodulator and frame controller functionality. The receive signal hardware has a 19-bit dynamic range. The user can select whether to return the upper 16 bits of the 19-bit value (RAIL_RxDataSource_t::RX_IQDATA_FILTMSB) or the lower 16 bits (RAIL_RxDataSource_t::RX_IQDATA_FILTLSB). All data returned is represented as 16-bit 2s-complement values. The I and Q values are put into the buffer in an alternating fashion where the first 16-bit value is the first I ADC value and the second 16-bit value is the first Q ADC value, and so on.

Interrupt Vectors#

The RAIL library for EFR32 implements all of the interrupt vectors for radio peripherals, which is required for the RAIL library to function correctly. RAIL does not, however, set the priorities of these interrupt vectors except for FRC_PRI_IRQHandler on EFR32xG12 which runs at the highest priority. The others must be handled by your application using the CMSIS NVIC_SetPriority() API or direct manipulation of the NVIC. Below is the full list of interrupts used by the radio. You must run them all except FRC_PRI_IRQHandler at the same priority which is what all Silicon Labs RAIL applications do by default. You are free to choose what that priority is based on the requirements of your application (e.g., putting them below FreeRTOS OS atomic for access to OS functions in RAIL callbacks). Keep in mind that putting the radio interrupts at too low a priority can cause missed packets and other radio performance issues. A restriction on EFR32xG12 and newer is that you must not disable radio interrupts for longer than a quarter of the RAIL timebase (2^32/4 microseconds or 18 minutes) to ensure proper timekeeping. See EFR32xG1x_Interrupts or EFR32xG2x_Interrupts for more information.

Chip-Specific Initialization#

EFR32 Hardware Initialization#

EFR32 includes EMLIB and EMDRV to create a basic HAL layer. A lot of this initialization code is completely up to you, but there are a couple of requirements when building a RAIL app. Specifically, the radio will only work if you are running off a high-precision crystal oscillator. Since some APIs will assume this is running, make sure to initialize and switch to the crystal before calling any radio APIs.

For the Wireless Starter Kit (WSTK), you can use the crystal configuration in the HAL configuration header file for your specific kit. Example code for this is shown below. If you have a custom hardware layout, you may want to create your own HFXOInit structure to account for things such as your specific CTUNE value.

#include "bsp.h" // Contains WSTK versions of the HFXO init structure

void efrStartup(void)
{
  CMU_HFXOInit_TypeDef hfxoInit = CMU_HFXOINIT_WSTK_DEFAULT;

  // Initialize the HFXO using the settings from the WSTK bspconfig.h
  // Note: This configures things like the capacitive tuning CTUNE variable
  //   which can vary based on your hardware design.
  CMU_HFXOInit(&hfxoInit);

  // Switch HFCLK to HFXO and disable HFRCO
  CMU_ClockSelectSet(cmuClock_HF, cmuSelect_HFXO);
  CMU_OscillatorEnable(cmuOsc_HFRCO, false, false);
}

Radio-Specific Initialization#

Currently, RAIL_ConfigTxPower() and RAIL_ConfigPti() initialization functions depend on the board configuration and must be manually called at startup. You MUST initialize the power amplifier (PA) to transmit. You may initialize the packet trace interface (PTI) for use in debugging.

Power Amplifier (PA) Initialization#

High Power 2.4 GHz PA, Low Power 2.4 GHz PA, and Sub GHz PA PAs are available for the EFR32xG1 family of chips. The specific set of PAs you have available and the supported power levels for those PAs are determined by your part number. See the appropriate data sheet for more details.

Each PA supports a raw power level value of type RAIL_TxPowerLevel_t where a numerically smaller number causes less power to be output from the chip than a higher number. Keep in mind that these values may be capped at the upper and lower ends and do not necessarily map linearly to output power in dBm. To map these values to and from dBm values, the RAIL library uses the RAIL_ConvertRawToDbm() and RAIL_ConvertDbmToRaw() APIs. By default, these use whatever piecewise linear power curves were passed into RAIL_InitTxPowerCurves() to do the conversion. On Silicon Labs boards, these curves are included with the board header files. For custom boards, this must be measured using test equipment to take into account the differences in the RF path that may exist. In addition, for even more custom use cases, the conversion functions can be strongly defined in the customer application and provide whatever type of mapping is found to be most effective. Below is an example of using the standard APIs to initialize all PAs on a dual band chip and switch to the 2.4 GHz high-power PA.

#include "rail.h"
#include "rail_chip_specific.h"
#include "plugin/pa-conversion/pa_conversion_efr32.h"

RAIL_DECLARE_TX_POWER_VBAT_CURVES(piecewiseSegments, curvesSg, curves24Hp, curves24Lp);

// Must be called with a valid RAIL_Handle_t returned by RAIL_Init()
void initPa(RAIL_Handle_t myRailHandle)
{
  // Initialize the RAIL Tx power curves for all PAs on this chip
  RAIL_TxPowerCurvesConfig_t txPowerCurvesConfig = {
    curves24Hp,
    curvesSg,
    curves24Lp,
    piecewiseSegments
  };
  if (RAIL_InitTxPowerCurves(&txPowerCurvesConfig) != RAIL_STATUS_NO_ERROR) {
    // Could not initialize transmit power curves so something is configured
    // wrong. Please fix and rebuild.
    while(1);
  }

  // Switch to the 2.4GHz HP PA powered off the 1.8V DCDC connection
  RAIL_TxPowerConfig_t railTxPowerConfig = {
    RAIL_TX_POWER_MODE_2P4_HP, // 2.4GHz HP Power Amplifier mode
    1800,                      // 1.8V vPA voltage for DCDC connection
    10                         // Desired ramp time in us
  };
  if (RAIL_ConfigTxPower(myRailHandle, &railTxPowerConfig)
      != RAIL_STATUS_NO_ERROR) {
    // Error: The requested PA could not be selected. Fix your configuration
    // and try again.
    while(1);
  }

  // Set the output power to the maximum supported by this chip
  RAIL_SetTxPower(myRailHandle, 255);
}

Packet Trace Interface (PTI) Initialization#

Packet trace on the EFR32 provides a mechanism for viewing transmitted and received radio packets for network sniffing or debugging. It can also be captured by a WSTK and sent to Simplicity Studio for viewing data in its Network Analyzer tool.

To use this functionality, configure the pins to use for this output and, optionally, how to format data. Note that the WSTK requires the following output format:

Mode: 8 bit UART mode
Baudrate: 1.6 Mbps
Framing Signal: Enabled

Introducing changes is currently unsupported by the WSTK. Other output formats are currently unsupported.

To choose the pins, look in the applicable data sheet and select a valid route location for the FRC_DOUT and FRC_DFRAME signals. These map to PTI.DATA and PTI.FRAME on the WSTK respectively. Once you've found pins that work for your hardware, configure the RouteLocation, port, and pin fields in the PTI initialization structure. When using the WSTK for example, initialize as follows:

#include "rail.h"
#include "rail_chip_specific.h"

void initPti(void)
{
  RAIL_PtiConfig_t ptiConfig =   {
    RAIL_PTI_MODE_UART,     // Only supported output mode for the WSTK
    1600000,                // Choose 1.6 Mbps for the WSTK
    6,                      // WSTK uses location 6 for DOUT
    gpioPortB,              // FRC_DOUT#6 is PB12
    12,                     // FRC_DOUT#6 is PB12
    6,                      // UNUSED IN UART MODE
    gpioPortB,              // UNUSED IN UART MODE
    11,                     // UNUSED IN UART MODE
    6,                      // WSTK uses location 6 for DFRAME
    gpioPortB,              // FRC_DOUT#6 is PB13
    13,                     // FRC_DOUT#6 is PB13
  };

  // Initialize the Packet Trace Interface (PTI) for the EFR32
  RAIL_ConfigPti(RAIL_EFR32_HANDLE, &ptiConfig);

  // Enable Packet Trace (PTI)
  RAIL_EnablePti(RAIL_EFR32_HANDLE, true);
}

Other Radio GPIO Functions#

Various useful signals related to the radio can be output on a GPIO using the Peripheral Reflex System (PRS). PRS is an advanced system where you can route signals to channels and then output those channels to a number of configurable locations. For more information, see the PRS chapter in the reference manual.

A list of some of the most interesting PRS signals related to the radio is shown below along with how to enable them in the PRS. The definition for these signals for a given chip can be found in the release at platform/Device/SiliconLabs/<chipFamily>/Include/<chipFamily>_prs_signals.h where <chipFamily> would be the beginning of the part number (EFR32MG12P for example).

Signal

Summary

PRS_RAC_ACTIVE

Radio enabled

PRS_RAC_TX

Transmit mode enabled

PRS_RAC_RX

Receive mode enabled

PRS_RAC_LNAEN

LNA enabled for RX

PRS_RAC_PAEN

PA enabled for TX

PRS_MODEM_FRAMEDET

Frame detected

PRS_MODEM_PREDET

Preamble detected

PRS_MODEM_TIMDET

Timing detected

PRS_MODEM_FRAMESENT

Frame sent

PRS_MODEM_SYNCSENT

Sync word sent

PRS_MODEM_PRESENT

Preamble sent

The example below shows how to configure a PRS channel to output RAC_RX on a GPIO. In this example, the WSTK is used with the BRD4153A radio board. The code below will put RAC_RX on PRS Channel 0 and output PRS Channel 0 on pin PC10 which is wired to WSTK_P12 and EXP_HEADER15 on the WSTK.

#include "em_cmu.h"
#include "em_prs.h"
#include "em_gpio.h"
#include "em_device.h"

void enableDebugGpios(void)
{
  // Turn on the PRS and GPIO clocks to access their registers.
  CMU_ClockEnable(cmuClock_PRS, true);
  CMU_ClockEnable(cmuClock_GPIO, true);

  // Configure PC10 as an output.
  GPIO_PinModeSet(gpioPortC, 10, gpioModePushPull, 0);

  // Configure PRS Channel 0 to output RAC_RX.
  PRS_SourceSignalSet(0,
                      ((PRS_RAC_RX & _PRS_CH_CTRL_SOURCESEL_MASK)
                       >> _PRS_CH_CTRL_SOURCESEL_SHIFT),
                      ((PRS_RAC_RX & _PRS_CH_CTRL_SIGSEL_MASK)
                       >> _PRS_CH_CTRL_SIGSEL_SHIFT),
                      prsEdgeOff);

  // Configure PRS Channel 0 to use output location 12 (PC10 - see data sheet).
  PRS->ROUTELOC0 &= ~_PRS_ROUTELOC0_CH0LOC_MASK;
  PRS->ROUTELOC0 |= PRS_ROUTELOC0_CH0LOC_LOC12;

  // Enable PRS Channel 0.
  PRS->ROUTEPEN |= PRS_ROUTEPEN_CH0PEN;
}

Required Dependencies#

Most of the RAIL library is self-contained, however, there are some dependencies on external functions, such as functions from the C standard library, EMLIB, and CMSIS. Below is a complete list of these dependencies. Note that changing the implementation of any of these functions while maintaining their functionality could still impact RAIL operation.

Group

Functions

CMSIS

SystemHFXOClockGet()SystemHFClockGet()SystemLFRCOClockGet()SystemLFXOClockGet()SystemULFRCOClockGet()

EMLIB (em_cmu)

CMU_ClockEnableCMU_ClockSelectGetCMU_ClockSelectSetCMU_OscillatorEnable

EMLIB (em_gpio)

GPIO_PinModeSet()

EMLIB (em_system)

SYSTEM_ChipRevisionGet()

EMLIB (em_core)

CORE_EnterCritical()CORE_ExitCritical()CORE_EnterAtomic()CORE_ExitAtomic()

EMLIB (em_emu)

EMU_DCDCLnRcoBandSet()

stdlib

memcpy()memset()

Peripherals Consumed by RAIL#

Certain functionality in RAIL requires some of the chip's peripherals. The specifics of these requirements are enumerated below.

RAIL Timer Synchronization#

If you call RAIL_ConfigSleep with RAIL_SleepConfig_t::RAIL_SLEEP_CONFIG_TIMERSYNC_ENABLED to keep the clock synchronized across sleep RAIL must use the PRS and RTCC.

  • On all platforms, PRS channel 7 is used to perform the synchronization. This channel is only initialized one time when calling the RAIL_ConfigSleep function so you must not change it after that or the synchronization may fail.

  • On the EFR32xG1, EFR32xG12, and EFR32xG21 platforms, the timer synchronization also uses RTCC channel 0. This channel should not be used by the application after a call to RAIL_Sleep and before the corresponding RAIL_Wake call. The application is also responsible for enabling this top level interrupt if they want it to serve as a wake source and implementing an interrupt handler that clears the RTCC_IFC_CC0 flag in the RTCC->IF register any time it is pended to prevent the chip from getting stuck in the handler. RAIL does not need to be tied into the interrupt handler in any other way.

  • On the EFR32xG13 and EFR32xG14 platforms you must also ensure that the CMU's cmuClock_HFLE is enabled or the internal timer used for sleep will not be able to power up. This happens by default on the other platforms when configuring the RTCC but since this is not required on these chips it must be done separately.

DCDC#

If you enable or disable the DCDC peripheral, you must follow it by calling RAIL_ChangedDcdc. This API indicates that the DCDC peripheral bus clock has changed allowing RAIL to react accordingly.

Entropy Generation#

EFR32 supports true entropy collection using the radio. It is able to collect 1 bit per radio clock cycle while in receive mode. This means that the receiver must be enabled when collecting entropy. If you attempt to transmit or otherwise delay entry into receive, data collection will take longer. You can still receive packets while collecting entropy. This means that requesting random numbers longer than the length of your preamble and sync word may trigger a packet reception. This may be fixed in a future revision but for now it ensures a proper packet reception during a random data collection.

Due to the nature of random data collection, either the number of bytes or zero is returned after the full amount requested. Zero is returned if the radio is uninitialized and cannot be enabled for collection.

RAIL Timebase#

EFR32 uses a dedicated radio timer to create the RAIL timebase and perform scheduled transmit and receive operations. This timer ticks at roughly every 2 us on the EFR32xG1 platform and 0.5 us on the EFR32xG12 and newer. The exact tick value depends on the high-frequency crystal in use so there may be a small rounding error. All internal APIs account for this error, so over long periods of time it is generally unnoticeable. Note that RAIL uses the hardware tick rate to create a 1 us time base that is exposed to the user. This is all that should matter for most users and the information here is included to explain the underlying implementation.

Radio Calibration#

EFR32 supports image rejection (IR) calibrations, VCO temperature calibration, and HFXO compensation. You may choose to enable some or all depending on your use case. Using IRCAL and VCO_TEMPCAL is recommended for optimal performance of the radio.

Image Rejection Calibration (IRCAL)#

This calibration should be run each time your PHY configuration changes. It is based on the modulation scheme, frequency band, and other radio settings. RAIL will request this by calling RAIL_Config_t::eventsCallback with the RAIL_EVENT_CAL_NEEDED bit set and the RAIL_CAL_ONETIME_IRCAL bit set in RAIL_GetPendingCal().

Using a proper value for this will improve sensitivity by several dBm, so it's highly recommended.

It can take on the order of 700 ms to complete this calibration. You may want to save off a known good value for this calibration and load it each time you switch PHYs to save time.

This calibration should be initialized before calling RAIL_ConfigChannels() to ensure that it is properly configured by the time the first channel is set up. The initialization involves enabling the algorithm and passing in parameters to configure the algorithm for the specific PHY generated by the EFR32 Radio Configurator.

You cannot use the radio while this calibration is being performed or you may generate an incorrect calibration. Application code should ensure that the radio remains in the idle state during this calibration.

This calibration is only meaningful for Zigbee, BLE, and sub GHz radio configurations. For all others you should still enable the calibration but the algorithm will apply a safe, default, calibration value.

Image Rejection Calibration for Transmit (TXIRCAL)#

This calibration is only meaningful for sub GHz radio configurations using SUN-OFDM PHYs and it should be run only once per multi-PHY configuration. For all other configurations this calibration won't run. RAIL will request this by calling RAIL_Config_t::eventsCallback with the RAIL_EVENT_CAL_NEEDED bit set and the RAIL_CAL_ONETIME_IRCAL bit set in RAIL_GetPendingCal().

This feature is automatically run after IRCAL, when a SUN-OFDM PHY is configured. If a SUN-OFDM PHY exists in the multi-PHYs radio configuration, it is advised to first load this OFDM PHY and then call RAIL_Calibrate(), so that both the IRCAL and TXIRCAL are run immediately. Otherwise the calibration will be requested the first time an OFDM PHY will be loaded.

Like IRCAL, using a proper value for this will improve sensitivity by several dBm, so it's highly recommended.

It can take on the order of 150 ms to complete this calibration. You may want to save off a known good value for this calibration and load it each time you switch multi-PHY configuration to save time.

You cannot use the radio while this calibration is being performed or you may generate an incorrect calibration. Application code should ensure that the radio remains in the idle state during this calibration.

VCO Temperature Calibration (VCO_TEMPCAL)#

When staying in receive for a very long time and the temperature changes causing the radio to drift off-frequency, RAIL will request this calibration via RAIL_Config_t::eventsCallback with the RAIL_EVENT_CAL_NEEDED bit set and the RAIL_CAL_TEMP_VCO bit set in RAIL_CalPendingGet().

This calibration is automatically run every time receive is entered. If the application, by its nature, frequently re-enters receive mode, this calibration may not need to be enabled.

On EFR, the application will get this event when the absolute temperature crosses 0C degrees as well as when the temperature delta from the last calibration increases or decreases by 70C.

It is always recommended to enable and handle this calibration since it doesn't add much overhead and is much safer.

HFXO Temperature Compensation (HFXO_TEMPCOMP)#

This optional feature requires plugin Thermistor Utility. More details about the compensation are available in the plugin page.

When the themperature varies too much, HFXO frequency drifts, resulting in signal degradation. The compensation corrects the effects of this deviation in the radio, and is done in 2 steps (2 events).

The first event is RAIL_EVENT_CAL_NEEDED with the RAIL_CAL_TEMP_HFXO bit set in RAIL_GetPendingCal(). This signals the need to call RAIL_StartThermistorMeasurement().

The second event occurs at the end of this measurement triggering RAIL_EVENT_THERMISTOR_DONE with the RAIL_CAL_COMPENSATE_HFXO bit set in RAIL_GetPendingCal(). This signals the need to idle the radio and call RAIL_CalibrateHFXO().

When enabled, RAIL_EVENT_CAL_NEEDED event is automatically set when the number of degrees set in either RAIL_HFXOCompensationConfig_t::deltaNominal or RAIL_HFXOCompensationConfig_t::deltaCritical are exceeded.

State Transition Timing#

EFR32 allows automatic transitions from receive to transmit to precisely time transmitted packets after packet reception. During this process, internal calculations are performed which require the received packet's duration, from sync word to end of CRC, to be less than 32 ms. If received packets in a given protocol have an on-air time greater than 32 ms, the automatic transition from receive to transmit may not be used with that protocol because of incorrect timing of that transition.

Radio State Verification#

RAIL includes a radio state verification feature capable of verifying radio register contents. There are multiple ways of configuring and running the verification process. All radio state verification should occur when the radio is idle. If not idle, the number of radio registers different from their reference values varies at any given time.

Default vs. Custom Radio Configuration#

When verification occurs, a reference value is compared with a radio register's contents. The reference value is provided through one of two ways.

The radio configuration (originally passed into RAIL_ConfigChannels()) is used as a reference with which to compare radio register values. When the default radio configuration is used for verification, only those addresses flagged as being verifiable by the radio configurator will be checked. Bit 27 of an encoded address indicates whether that address is verifiable or not. For example, encoded address 0x08020004 is flagged as being verifiable and will be verified when RAIL_Verify() is called, but the encoded address 0x00020008 will not be verified.

A custom radio configuration can be provided to the verification API (passed into RAIL_ConfigVerification()). When a custom radio configuration is provided, all addresses in the provided radio configuration will be checked, regardless of whether or not their encoded addresses are flagged as being verifiable.

Approval Callback#

When verification occurs, no register value differences are allowed unless an application-level approval callback is defined.

Without an approval callback, any difference will be flagged as data corruption, with RAIL_Verify() returning a status of RAIL_STATUS_INVALID_STATE.

With an approval callback, the application can scrutinize all differences and deem them acceptable or not. By providing an approval callback, individual register fields, instead of the entire register contents, can be compared against reference values at the application level.

Restarting#

Verification can be restarted if necessary.

When running verification from the beginning to completion (by specifying a sufficiently long test duration or by specifying a duration of RAIL_VERIFY_DURATION_MAX), the verification process is indifferent to the restart parameter.

When running verification for a duration that does not permit verification to run to completion, the RAIL_Verify() function will return with a status of RAIL_STATUS_SUSPENDED. This indicates test success for those values already verified, but sufficient time was not permitted to verify all values. In this scenario, the initial run of RAIL_Verify() is indifferent to the value of the restart input, but subsequent runs of RAIL_Verify() should set the restart input to false so that verification will resume where it left off on the previous call. To record where a previous verification left off, the RAIL_VerifyConfig_t structure must be declared by the application and provided to RAIL. These structure contents should only be altered by RAIL.

Configuration and Running Options#

With the ability to specify default radio configuration vs. custom radio configuration, approval callback vs. no approval callback, and run to completion vs. run for a specific time, there are multiple ways in which to run RAIL's radio state verification feature. The example below shows running verification in each of these modes.

#include "rail.h"
#include "rail_types.h"
#include "rail_config.h"

RAIL_VerifyConfig_t configVerify;

const uint32_t customRadioConfig[] = {
  0x00010048UL, 0x00000000UL,
  0x000400A0UL, 0x00004CFFUL,
     /* 00A4 */ 0x00000000UL,
     /* 00A8 */ 0x00004DFFUL,
     /* 00AC */ 0x00000000UL,
  0x00012000UL, 0x00000744UL,
  ...
  0xFFFFFFFFUL,
};

bool RAILCb_VerificationApproval(uint32_t address,
                                 uint32_t expectedValue,
                                 uint32_t actualValue)
{
  responsePrint("verifyRadioCb",
                "address:0x%08x,expectedValue:0x%08x,actualValue:0x%08x",
                address, expectedValue, actualValue);
  return true OR false; // true = change approved; false = change unapproved
}

main ()
{
  RAIL_Status_t result;
  ...
  // Initialize the radio.
  railHandle = RAIL_Init(..., ...);
  ...
  // Associate a radio config with this RAIL handle.
  RAIL_ConfigChannels(railHandle, channelConfigs[0], ...);
  ...
  // Idle the radio before verifying registers.
  RAIL_Idle(railHandle, RAIL_IDLE, true);

  uint8_t verifyConfigOption = 1; // 1,2,3,4
  uint8_t verifyRunOption    = 1; // 1,2

  // Configure the radio state verification in one of 4 ways:
  if (1 == verifyConfigOption) {
    // Config Option 1: Use default radio config with no approval callback.
    RAIL_ConfigVerification(railHandle,
                            &configVerify,
                            NULL,   // use channelConfigs[0] for verification
                            NULL);
  } else if (2 == verifyConfigOption) {
    // Config Option 2: Use default radio config with approval callback.
    RAIL_ConfigVerification(railHandle,
                            &configVerify,
                            NULL,   // use channelConfigs[0] for verification
                            RAILCb_VerificationApproval);
  } else if (3 == verifyConfigOption) {
    // Config Option 3: Use custom radio config with no approval callback.
    RAIL_ConfigVerification(railHandle,
                            &configVerify,
                            &customRadioConfig[0],
                            NULL);
  } else {
    // Config Option 4: Use custom radio config with approval callback.
    RAIL_ConfigVerification(railHandle,
                            &configVerify,
                            &customRadioConfig[0],
                            RAILCb_VerificationApproval);
  }

  // Run the radio state verification in one of 2 ways:
  if (1 == verifyRunOption) {
    // Run Option 1: Run verification once to completion.
    result = RAIL_Verify(railHandle,
                         &configVerify,
                         RAIL_VERIFY_DURATION_MAX,
                         true); // run from the beginning of verification
  } else {
    // Run Option 2: Run verification for a specific time before returning.
    result = RAIL_Verify(railHandle,
                         &configVerify,
                         100,   // run for 100 microseconds before returning
                         true); // run from the beginning of verification

    while (RAIL_STATUS_SUSPENDED == result) {
      responsePrint("verifyRadio",
                    "verification suspended - run again to continue");
      result = RAIL_Verify(railHandle,
                           &configVerify,
                           100,    // run for 100 us before returning
                           false); // run from where verification left off
    }
  }

  switch (result) {
    case RAIL_STATUS_NO_ERROR:
    {
      responsePrint("verifyRadio", "success, done");
      break;
    }
    case RAIL_STATUS_INVALID_PARAMETER:
    {
      responsePrint("verifyRadio", "invalid input parameter");
      break;
    }
    case RAIL_STATUS_INVALID_STATE:
    default:
    {
      responsePrint("verifyRadio", "data corruption");
    }
  }
  ...
}

RFSENSE#

The RAIL library for EFR32 supports RF Sensing for EFR32 Series 1 devices and EFR32xG22. On EFR32xG22, enabling RFSENSE will enable the ULFRCO clock, if not already enabled.

Energy Detection Mode#

This mode is supported by all EFR32 Series 1 devices and EFR32xG22, where the chip senses the presence of RF Energy and triggers an event if that energy is continuously present for certain duration of time. On Series 1 devices, rounding is done to the nearest choice for requested period (in microseconds). RAIL_StartRfSense() will return the actual senseTime used, which may be different than the requested time due to hardware limitations. Also, on Series 1, RFSENSE functionality is only guaranteed from 0 to 85 degrees Celsius and should be disabled outside of this temperature range. On EFR32xG22 device, the RFSENSE Energy Duration ranges from 1 ms - 128 ms as a power of 2. Based on the user's RFSENSE duration (senseTime) input, the duration is checked against the actual period and the next higher period. If the senseTime is greater than or equal to ((actualPeriod + nextHigherPeriod) / 2), the next higher duration is chosen. Both Series 1 and EFR32xG22 support setting the energy detection threshold for high sensitivity (low noise environment) and low sensitivity (high noise environment) by selecting appropriate RF Band via RAIL_RfSenseBand_t.

Selective Mode#

Only supported on EFR32xG22, the Selective mode uses 0.5 kbps Manchester encoded OOK detection which allows the chip to wake up upon detecting particular preamble and sync word pattern sent using OOK. The transmitting node must be configured to use the OOK based RFSENSE PHY for waking up EFR32xG22, via RAIL_ConfigRfSenseSelectiveOokWakeupPhy, followed by setting the transmit FIFO to include the Preamble Byte and the sync word (1 byte - 4 bytes), using RAIL_SetRfSenseSelectiveOokWakeupPayload. Note that the RAIL_ConfigRfSenseSelectiveOokWakeupPhy() API is only supported on 2.4GHz chips and does not work on EFR32xG21 devices.