Proprietary Knowledge Base

    Publish
     
      • Driving FEMs (and PAs and LNAs) in RAIL

        andrasbiro | 12/348/2017 | 09:19 AM

        Driving FEMs usually require some logic signals, which can be driven from software, but generally that's not fast enough (e.g. for CSMA/CA): It needs some hardware based signals.

        Since EFR32 is an integrated radio MCU, this is not handled in the radio module, but a more general purpose module: Peripheral Reflex System, or PRS for short.

        EFR32s has 12 PRS channels, each of these channels can be driven by numerous signals from various sources, and even some logic operations can be done between them. These channels can be connected to PRS consumers, so it can trigger a timer start for example.

        You can also wire PRS channels to some GPIOs, and this is the feature we'll use. For more information of PRS, see the reference manual.

        Hardware configurator does provide an "External LNA" module, but it's not supported in RAIL (as of Flex SDK 2.0). Also, it only supports one TX/RX and one sleep pin, which is probably not enough for more complicated FEMs.

        This article is stack independent, although some stacks might provide an integrated way, like the External LNA module.

        The prs signals

        Generally a FEM requires the following:

        • rx/tx switch
        • sleep
        • rx/tx bypass

        The bypass signals are usually either compile time or application time settings, so they can be driven like any other GPIO from software, or exactly the same as sleep and rx/tx, therefore it's not part of this article.

        The following PRS signals are useful for driving a FEM:

        • RAC_ACTIVE active high when radio is on (either RX or TX)
        • RAC_LNAEN active high when LNA is needed
        • RAC_PAEN active high when PA is needed

        Note that all signals can be inverted

        RAC_ACTIVE can be used to drive the sleep pin, but using (RAC_LNAEN or RAC_PAEN) is more energy efficient.

        GPIOs usable for PRS

        See the Alternate Functionality Pinout chapter or device pinout table in the datasheet. PRS channel pins has PRS_CHx functionality on it.

        The hardware configurator can be also used as a dynamic pinout diagram in Simplicity Studio (in all Flex SDK examples)

        Keep in mind that you need independent PRS channels for each signal, you can't configure a PRS channel to two locations.
        Also, logical operations limited to channels next to each other:

        • OR operation is only supported between a channel and a previous one  (i.e. channel1 = channel0 OR channel1).
        • AND operation is only supported between a channel and the next one  (i.e. channel0 = channel0 AND channel1).

        This limits the usable pins, e.g. it's recommended to drive a sleep pin as (RAC_LNAEN or RAC_PAEN), which means whatever channel you plan to use for sleep, the previous one MUST be set up to either RAC_LNAEN or RAC_PAEN. However, you can set up the same signal to multiple channels, and you don't have to drive a gpio with a channel.

        Configuring GPIOs in RAILTest

        Generally, RAILTest is recommended to test the radio's setup and performance, so energy efficiency is not a goal here. Therefor, it would be enough to just drive pins low and high, although it's a bit simpler to use the tx/rx switch from PRS.

        RAILTest only supports a few GPIOs:
        PC10, PC11, PF2, PF3, PF4, PF5, PC9, PD9, PD10, PD11, PD12

        A number of PRS signals are supported, unfortunately drive low/high and RAC_ACTIVE is not amongst them.

        The following commands can be used

        • to drive pin high (when radio is on; 32/0 81/0 is the PRS source/signal of RAC_ACTIVE):
          • On EFR32xG1: setupDebugSignal CUSTOM_PRS 32 0
          • On everything else: setupDebugSignal CUSTOM_PRS 81 0
        • to drive pin low (CUSTOM_LIB is used internally for RAIL debugging, but on the public versions of the SDK it just drives the pin low):
          • setDebugSignal CUSTOM_LIB 0
        • to drive rx/tx pin, high on rx:
          • setDebugSignal LNAEN
        • to drive rx/tx pin, high on tx:
          • setDebugSignal PAEN
        • to turn off a pin
          • setDebugSignal OFF

        E.g. to drive the LNA on the MGM12P module (sleep pin on PD10, tx/rx on PD11)

        setDebugSignal PD10 LNAEN
        setDebugSignal PD11 CUSTOM_PRS 81 0

        Configuring GPIOs in application source

        To use PRS channels, you first need to enable GPIO and PRS clock:

        CMU_ClockEnable(cmuClock_PRS, true);
        CMU_ClockEnable(cmuClock_GPIO, true);

        Set the GPIO we use to pushpull mode:

        GPIO_PinModeSet(port, pin, gpioModePushPullAlternate, 0);

        Enable PRS signal:

        PRS->CH[x].CTRL = PRS_signal | PRS_CH_CTRL_ASYNC;

        It's recommended to use async mode, since this is needed to have a working signal even in sleep states.

        Set location of the PRS signal pin:

        PRS_ROUTELOCy |= PRS_ROUTELOCy_CHxLOC_LOCz

        (the 12 PRS channels are divided into 3 ROUTELOC registers, so channel 0-3 is in ROUTELOC0, etc)

        Enable the pin:

        BUS_RegBitWrite(&PRS->ROUTEPEN, _PRS_ROUTEPEN_CHxPEN_SHIFT, 1);

        E.g. to drive the LNA on the MGM12P module (sleep pin on PD10, tx/rx on PD11):

        CMU_ClockEnable(cmuClock_GPIO, true);
        CMU_ClockEnable(cmuClock_PRS, true);
        GPIO_PinModeSet(gpioPortD, 10, gpioModePushPullAlternate, 0);
        GPIO_PinModeSet(gpioPortD, 11, gpioModePushPullAlternate, 0);
        PRS->CH[6].CTRL = PRS_RAC_ACTIVE | PRS_CH_CTRL_ASYNC;
        PRS->CH[5].CTRL = PRS_RAC_LNAEN | PRS_CH_CTRL_ASYNC;
        PRS->ROUTELOC1 |= PRS_ROUTELOC1_CH5LOC_LOC0 | PRS_ROUTELOC1_CH6LOC_LOC13;
        BUS_RegBitWrite(&PRS->ROUTEPEN, _PRS_ROUTEPEN_CH6PEN_SHIFT, 1);
        BUS_RegBitWrite(&PRS->ROUTEPEN, _PRS_ROUTEPEN_CH5PEN_SHIFT, 1);

        Invert a signal

        Inverting a signal is very simple. To create an inverted sleep signal for example (high in sleep):

        PRS->CH[x].CTRL = PRS_RAC_ACTIVE | PRS_CH_CTRL_ASYNC | PRS_CH_CTRL_INV;

        Logical operation between signals

        As above mentioned:

        • PRS channels can be OR'ed with the previous channel (i.e. channel1 = channel0 OR channel1).
        • PRS channels can be AND'ed with the next channel (i.e. channel0 = channel0 AND channel1).

        If you meet this limitation, the setup is pretty simple, just use PRS_CH_CTRL_ORPREV and PRS_CH_CTRL_ANDNEXT.

        For example the energy efficient (RAC_LNAEN | RAC_PAEN) way to drive the LNA on MGM12P:
         

        PRS->CH[5].CTRL = PRS_RAC_LNAEN | PRS_CH_CTRL_ASYNC;
        PRS->CH[6].CTRL = PRS_RAC_PAEN | PRS_CH_CTRL_ASYNC | PRS_CH_CTRL_ORPREV;

         

      • RAIL_SetStateTiming and scheduled TX

        tanagy | 07/195/2017 | 12:15 PM

        Here is an example about relation between RAIL_SetStateTiming setting and scheduled TX:

        If you scheduled a transmit to occur on absolute time 1000µs and have an idleToTx time of 100µs and have a ramp time of 30µs:

                   1. Scheduled transmit should start at absolute time 1000µs

                   2. IdleToTx is then in effect. The PA ramp will start at absolute time 1100µs.

                   3. PA ramps. Modulation starts when ramp ends (at 1130µs in this case).

      • RAIL_RX_CONFIG_TIMEOUT

        tanagy | 07/195/2017 | 11:58 AM

        In RAIL_SchedulRx() you can configure a ‘receive window’. If you do not receive a sync word within this window, the RAIL_RX_CONFIG_TIMEOUT callback will be asserted.

      • Do I have to use RAIL to access and configure the transceiver for my proprietary protocol on EFR32 devices?

        Siliconlabs | 05/124/2017 | 11:05 AM

        Question

        Do I have to use RAIL to access and configure the transceiver for my proprietary protocol on EFR32 devices?

        Answer

        Yes, RAIL and Radio Configurator tool in Simplicity Studio are the currently available means to configure and access the radio on EFR32 devices. Over the past decades we have seen an evolution in software programming from assembly code to C programming and nowadays to object oriented programming like Java, Python or C++. This was done to facilitate development of bigger and more complex software and also simplify the overall programming experience by allowing engineers to focus on their designs. On the wireless connectivity side, the integrated transceivers are also getting more complex due to multiband-multiprotocol operation in resource constrained environments. Over the last many generation of our products we have also seen an exponential growth in the MCU and transceiver complexity that has increased the customer’s time to market. The multiple optimization vectors make it difficult for an individual customer to optimally configure the transceiver within the SOC especially for proprietary wireless protocols. Furthermore each setting and configuration must be tested to ensure proper performance as part of the SOC. To make the product easier to use we abstract the radio through an interface layer called the Radio Abstraction Interface Layer (RAIL). This improves our customer’s time to market, ensures code portability and guarantees the best possible performance for given configurations. Periodically there is a configuration that hasn’t been created for the device, the configuration list is constantly evolving. In that case you can contact your local FAE or customer apps to request a specific configuration be added.

      • IEEE 802.15.4 and RAIL/Flex SDK

        andrasbiro | 02/55/2017 | 05:08 AM

        Preface

         

        802.15.4 PHY support by the hardware

         

        • All bands are supported, except 1427, 2380, 770, 4965 and 5800MHz
        • OQPSK (not SUN O-QPSK or MR-OQPSK) and FSK/GFSK modulations are supported
        • MSK modulation will be supported soon

         

        The protocol specific API

         

        The RAIL library includes protocol specific API for 15.4. It's strongly recommend to use at least version 1.4.x, which is supplied with Flex SDK 1.0.x.

         

        Setting up the PHY

         

        Frequency and modulation

         

        2.4GHz OQPSK PHY

        This protocol specific API includes PHY configuration for the 802.15.4 2.4GHz OQPSK PHY (not to be confused with the SUN O-QPSK PHY), which also sets up the variable length settings for the 1Byte (7 bit length) PHR. To use it, call  RAIL_IEEE802154_2p4GHzRadioConfig instead of loading the configuration generated by AppBuilder.


        Setting up all other PHY

        • Set up modulation and frequencies as per the requirements of the standard
        • You'll probably need to modify the channel map by hand in rail_config.c See the API doc of  RAIL_ChannelConfigEntry_t and  RAIL_ChannelConfig.

        Frame format

         

        Preamble and Sync word (SFD) depends on the PHY format, set it up accordingly.

         

        Endianness

        Use LSB first unless stated otherwise.

        SUN-FSK defines the length field MSB first. The length can be handled by the hardware, but you have to keep in mind that you have to reverse the endianness of these bytes for both RX and TX in application. I've used the following code to set and get the length of the packet:

         

        //length helper functions for WiSUN 15.4 frame formats
        void setLength(uint8_t *payload, uint16_t length){ payload[1] = __RBIT(length & 0xFF) >> 24; payload[0] = __RBIT(length >> 8) >> 24; } uint16_t getLength(uint8_t *payload){ uint16_t length = __RBIT(payload[1]) >> 24; length |= (__RBIT(payload[0]) >> 16) & 0x0700; return length; }

         

         

        Variable length setup for 1Byte PHR with 7bit length

        On 2.4GHz OQPSK, this is covered by the embedded config. Otherwise:

         2017-02-23_16h08_13.png

        Variable length setup for 2Byte PHR and 11 bit length (SUN FSK)

        2017-02-23_16h33_51.png

        Note: With Flex 1.0.x, frames are supported with the maximum of 238B length (240B total with PHR). The next release will support longer frames

         

        CRC

        CCIT-16 (16 bit ITU-T CRC)

        2017-02-23_16h05_21.png

        32 bit ANSI X3.66-1979

        The same as before, but polynomial is ANSIX366_1979


        Using MAC acceleration


        The hardware does support address filtering and Auto-ACK, which can be turned on in RAIL only for the 1Byte PHR PHY. To enable it, use the RAIL_IEEE802154_Init API call, and the address related calls.

         

        AutoACK and Address filtering module

        It might seem reasonable to use the AutoAck and address filtering module with 2B PHR. Unfortunately, this is not possible:

        • The address locations are not fixed in the header
        • The AutoAck module will Ack all packages, even the filtered ones
      • How to use calibration in RAIL

        andrasbiro | 10/293/2016 | 12:28 PM

        RAIL provides two self calibrations: Temperature (TEMP or TEMP_VCO) and Image rejection (ONETIME or ONETIME_IRCAL)

         

        Temperature calibration

         

        The calibration needs to be taken place when the temperature crosses 0 degC, or when it increases by more than ~70 degC relative to the temperature of the last calibration, while sitting in receive state. It automatically runs every time it enters receive, so if you are frequently forcing a re-enter into receive mode you may not need to enable calibration.

         

        Basically, you don't need this calibration, unless you use the chip in RX mode for a long time.

         

        Image rejection calibration

         

        What does one-time mean?

         

        It means once per chip, and once per PHY config. You can do it during the manufacturing process, and you don't need to do it ever again (if you store it in non-volatile memory). If your product uses multiple configurations, you just store multiple calibration consts.

         

        Does this work on every possible PHY?

         

        Unfortunately not, it only works on sub-GHz configs. On 2.4GHz RAIL_CalStart will load a factory calibrated value from the device information (DI) page of the chip, which is an OK value for 2.4G, but the actual calibration on the sub-GHz band is much better. Two values are stored, one for OQPSK modulation, and one for everything else. So, you don't have to do the "calibration" if you change between two FSK based 2.4GHz configuration.

         

        When and how does this calibration happen?

         

        If you call RAIL_ChannelConfig, RAIL will call the RAILCb_CalNeeded callback. If you didn't implement this callback, it will ask for a calibration with RAIL_CalStart, but it will not store the results.

         

        Should I store the calibration const?

         

        You should, in two cases:
        -Chip initialization time is important: Otherwise, RAIL will calibrate itself on each bootup
        -You use multiple PHYs: Each time you change the PHY, you trigger a calibration, which extends the time of the PHY change (the exception is 2.4GHz, where you would only load the factory stored value again)

         

        Should I do anything during initialization?

         

        Yes: Every PHY configuration comes with an irCalConfig array. It should be uploaded with RAIL_CalInit. If you have multiple PHY-s you should upload the irCalConfig array each time you change the PHY, if you want to actually calibrate, not just loading stored values.

         

        How should I call RAIL_CalStart to store and to reuse the calibration const?

         

        If you call RAIL_CalStart, it works differently, depending on the first argument

        • If it's a NULL pointer, it will do the one time calibration, but won't save it
        • If it's a pointer with the value of RAIL_CALVALUES_UNINIT, it will do the calibration, than overwrite the pointer with the calibration value
        • If it's anything else, it will just write the calibration value to the appropriate registers

         

        So, you should do something like this to store the const:

        RAIL_CalValues_t calValues = RAIL_CALVALUES_UNINIT;
        RAIL_CalStart(&calValues, RAIL_CAL_ALL_PENDING, true);
        <store calibration values>

         

         

        To restore it:

        RAIL_CalValues_t calValues = <get calibration values>;
        RAIL_CalStart(&calValues, RAIL_CAL_ALL_PENDING, false);

         

         

        And you probably want to do this from the RAILCb_CalNeeded callback.

      • Minimal RAIL application

        andrasbiro | 08/237/2016 | 08:04 AM

        This article is mostly outdated with the release of Flex 1.1, but I keep it here for legacy usage.

        For Flex 1.1, the minimal RAIL application is the "Simple Sample Application without HAL", while the "Simple Sample Application" is a good starting point for generic application development (it uses HAL, but it's usually not an issue if you're not targeting minimal code space or power consumption, and it's easy to modify it later not to use HAL).

        Turning off printf float for GCC does still work and saves code space.

         

        Most of our demos use a lot of peripherals, and in a lot of projects, you barely need any peripherals. So the question arises: How an absolute minimum RAIL application would look like?

        Let's say, just initialize the radio, send a really small message, than do nothing.

         

        Generating the project

         

        Use the empty_rail example, since it's basically just the configurator, nothing else. We will use our own c code. Set up the RadioConfig to whatever you want, and only enable the RAIL Library plugin:

        2016-08-24_14h00_40.png

        We will use the RAILCb_RfReady callback, so enable that, but only that:

        2016-08-24_13h15_52.png

        On the other tab, add some include paths (directories) needed to use the kit. The last directory should represent the board you use.

        2016-08-24_13h17_17.png

        (The first one is in there by default, but it's not shown until you manually add some more)

         

        Adding drivers

         

        RAIL (or the peripherals needed by RAIL) needs a few emlib c files to work. Namely: em_cmu.c, em_emu.c em_int.c and em_system.c.

        So let's add them to the project. The simplest way to do it is to create a new folder in the project, than drag end drop the c files there from <RAILFolder>\submodules\emlib\src\. Studio will pop up a window there, it's a good idea to create a link, relative to STUDIO_SDK_LOC: since that points to the RAIL folder, the project should work, even if you move it to an other computer.

         2016-08-24_10h59_18.png

        In the end, the project should look something like this:

         

         2016-08-24_13h26_31.png

         

        Creating the main.c file

         

        Add a main.c file to the project, and for now, just create the simplest file that should compile:

         

        int main(void){
        while(1)
        ; return 0; } void RAILCb_RfReady(void) { }

         

        Initializing RAIL

         

        In our examples, RAIL initialization starts with halInit(). We won't use that call, since it starts up PTI, some timers, etc - a lot of things we want to avoid.

        Note: Most WirelessGecko boards have an onboard serial flash chip, which, at power on reset consumes quite a lot of power. We won't start that, so be aware that the current consumption will be significantly higher. See the boardDisableSpiFlash() function in hal_efr.c to see how to turn it off.

         

        RAIL actually only needs the following:

        • DCDC
        • HFXO
        • PA

        Note: Unless you want to use RFSense in EM2. That would need some LF oscillator

        So let's modify our very simple main.c which will start up those, than starts and configures RAIL:

         

        #include "rail.h"
        #include "rail_types.h"
        #include "rail_config.h"
        
        #include "em_chip.h"
        #include "em_emu.h"
        #include "em_cmu.h"
        #include "bsp.h"
        #include "pa.h"
        
        #define MAX_PACKET_SIZE  1
        
        RAIL_Init_t railInitParams = {
        		MAX_PACKET_SIZE,
        		RADIO_CONFIG_XTAL_FREQUENCY,
        		RAIL_CAL_ALL,
        };
        
        volatile bool initialized = false;
        
        int main(void){
        	CHIP_Init();
        	EMU_DCDCInit_TypeDef dcdcInit = EMU_DCDCINIT_WSTK_DEFAULT;
        	CMU_HFXOInit_TypeDef hfxoInit = CMU_HFXOINIT_WSTK_DEFAULT;
        	RADIO_PAInit_t paInit;
        
        	EMU_DCDCInit(&dcdcInit);
        	CMU_HFXOInit(&hfxoInit);
        
        	/* Switch HFCLK to HFXO and disable HFRCO */
        	CMU_ClockSelectSet(cmuClock_HF, cmuSelect_HFXO);
        	CMU_OscillatorEnable(cmuOsc_HFRCO, false, false);
        
        	// Initialize the PA now that the HFXO is up and the timing is correct
        	#if (RADIO_CONFIG_BASE_FREQUENCY < 1000000000UL)
        	paInit = (RADIO_PAInit_t) RADIO_PA_SUBGIG_INIT;
        	#else
        	paInit = (RADIO_PAInit_t) RADIO_PA_2P4_INIT;
        	#endif
        
        	RADIO_PA_Init(&paInit);
        	RAIL_RfInit(&railInitParams);
        	RAIL_RfIdle();
        
        	RAIL_PacketLengthConfigFrameType(frameTypeConfigList[0]);
        	if (RAIL_RadioConfig((void*)configList[0])) { while(1); }
        
        	RAIL_ChannelConfig(channelConfigs[0]);
        
        	while( !initialized )
        		;

        while(1)
        ; return 0; } void RAILCb_RfReady(void) { initialized = true; }

         

        Sending a packet

         

        To send a packet, we need to define its payload

         

        uint8_t txData[1];
        
        RAIL_TxData_t transmitPayload = {
        		(uint8_t*)(&txData),
        		sizeof(txData),
        };

        Then, after initialization, we set up the payload, load it to RAIL, then send it:

         

         

        txData[0] = 0x12;
        RAIL_RfIdle();
        RAIL_TxDataLoad(&transmitPayload);
        RAIL_TxStart(0, NULL, NULL);

        And that's it. You should have something similar to the attached code. This is a very minimal RAIL example which work.

         

         

        Reducing code size (GCC only)

         

         By defualt, GCC compiles in float support for printf, which uses a lot of code space.  You can turn that off in the project properties:

        2016-08-24_13h57_07.png

      • Wireless Gecko (EFR32) Variable packet length

        mabuthi | 07/186/2016 | 10:32 AM

        Choosing the variable frame length algorithm is an option whereby the frame length information is available in the frame itself. This feature is especially valuable at the Rx side as frames with dynamically changing lengths can be received without any software intervention.

         

        When variable frame length algorithm is selected various parameters must be configured on the packet tab.

        1. First of all the header need to be enabled, because the length of the payload is placed in the header.fig1.png
        2. The variable length bit size, which sets the variable length word’s size in bits, and the maximum length of the payload need to be set on the “Frame Variable Length” tab. Please note that in case you set the variable length bit size to 10 bits, the maximum length of the payload is 1023 bytes. fig2.png
        3. On the graphical representation of the selected packet structure, click on the Header element and set the header size. The “variable length bit size” and the “variable length bit location” needs to be taken into account, while choosing the parameter of the header size. (For example: In case the variable length bit size is set to 8 and the variable length bit location is set to 2, the header needs to be set to 2 bytes, because it is not possible to shift 8 bit in one byte.)fig3.png

        In the IEEE 802.15.4 profile, the length byte is the first byte of the payload and it also covers the CRC length of the packet, which is 2 bytes.

        In the Bluetooth LE Profile the length byte is the second byte of the payload.

      • Versions of IAR EWARM and GCC Compiler Software for RAIL SDK

        andrasbiro | 06/162/2016 | 07:40 AM

        Question

        What compilers are supported with RAIL?

        Answer

        Silabs Wireless software is compiled using the IAR Integrated development environment and GCC optimizing C/C++ compiler for ARM Cortex-M.

         

        In general, RAIL has no special requirements from the compiler, but we developed and tested it with the following. Also, it should always work with the GCC version shipped with Simplicity Studio.

         

          

        Stack version Compiler version Compiler version
        RAIL SDK v1.0.0 EWARM 7.30.1 GCC 4.9.3
      • Wireless Gecko (EFR32) Rx direct mode

        zopapp | 06/158/2016 | 09:08 AM

        In a direct Rx configuration the received data is directly output to an IC pin that in turn directly feeds another MCU pin for further processing. Typically in such applications all the signal detection (in form of preamble and / or sync word detection algorithms) are done by the processing MCU as opposed to the receiver’s own frame controller (FRC) block.

        In the RAIL library there exist a function that places the receiver into this mode: RAIL_DirectModeConfig. When enabled the RX_DATA will be output to EFR32_PC11. This output data has the following properties:

         

        • It is changing at the demodulator’s clock rate, which is typically 5-10x higher than the DR itself.
        • It only works in 2FSK mode by simply outputting the sign of the frequency with regards to the nominal frequency, i.e., if the frequency is higher than the nominal it will output logical ones, if it is lower that the nominal frequency it will output zeroes.
        • There is no frequency error compensation on it.
        • It is not synchronized to any clock.

        Note that at the writing of this article it is stated in RAIL documentation that parallel to RX_DATA an RX_CLK signal is also output. This is not correct, not the least because there is no RX_CLK signal RX_DATA is synchronized to.

         

        Above direct mode operation can also be referred to as asynchronous direct mode operation. What about synchronous direct mode operation, you may quite rightly ask. Synchronous direct mode operation also provides an RX_CLK signal RX_DATA is synchronized to. Such a mode does not exist on the Wireless Gecko (EFR32).

         

        The reason it does not exist is that a successful sync word detection gates the availability of the RX_CLK signal. So without sync word detection there is no RX_CLK, if you have sync word detection, however you may as well carry on and use packet mode. If for some reason RX_CLK and RX_DATA (placed on pins) still come useful in your packet mode application keep an eye on the radio related PRS signals (see below the current offering) in the RAIL documentation, sooner or later RX_DATA and RX_CLK will show up.

         

        PRS_offering.png