Execution states and status codes

After delivering to the RF core, a radio operation passes several states. They are illustrated as a UML state chart in Figure 13. and explained in Table 1.. Each state performs several activities which are explained during the next sections.

 @startuml
 scale 0.8

 state PENDING {
     state Parsing
     state WaitingForStartTrigger
     [*] -> Parsing
     Parsing --> WaitingForStartTrigger : ok
     Parsing --> [*] : failed
     Parsing --> [*] : skipped
     WaitingForStartTrigger -> [*] : triggered
 }
 state ACTIVE {
     state Executing
     note right
         command-specific
     end note
     state Evaluating : evaluate(result);
     note left
         might set skipping = true
         for chained commands
     end note

     [*] -> Executing
     Executing --> Evaluating : finished / result
     Evaluating -> [*]

 }
 state DONE as "DONE*"
 state ERROR as "ERROR*"

 [*] -> IDLE
 IDLE -> PENDING : posted
 PENDING --> ACTIVE : triggered
 PENDING -> SKIPPED :  skipped
 PENDING --> ERROR : parsing failed
 ACTIVE --> DONE : done [success]
 ACTIVE --> PENDING : done\n [skipping == true,\ncmd.condition.nSkip == 0]
 ACTIVE -> ERROR : done [fail]
 DONE -[hidden]-> ERROR
 DONE --> [*]
 SKIPPED --> [*]
 ERROR -> [*]

 @enduml

Figure 13. The life-cycle of a radio operation. The state is equivalent to the status code despite of states marked with a * .

Table 1. Command status codes as defined in <ti/devices/${DEVICE_FAMILY}/driverlib/rf_mailbox.h>. Status codes marked with a (*) do not exist, but represent multiple command-specific variants.
Status Description
IDLE The command has not been posted to the RF core, yet. This is the default status.
PENDING The command is being parsed and the RF core waits for the start trigger to fire.
ACTIVE This state is very command-specific. The command executes until it terminates. Some commands provide also end triggers for alternative termination. Command execution produces a result, either of TRUE, FALSE or ABORT. After the execution phase, both, the result value and the command condition rule get evaluated. Several command-specific callback events may be generated during the ACTIVE state.
DONE* A command-specific status code is written to status and a callback event is generated for single commands or for the last command in a chain.
ERROR* Either parsing failed or command execution ended with an error. A command-specific error code is written to status and a callback event is generated.
SKIPPED The command was skipped due to a skipping rule in a previous command. command.status is set to SKIPPED. This applies only to commands in chains. A callback is not performed.

States marked with (*) do not exist as one explicit status code, but as various command-specific variants. The code PROP_DONE_OK, for instance, is specific to proprietary commands.

Status codes are defined in <ti/devices/${DEVICE_FAMILY}/driverlib/rf_mailbox.h> for common radio operations. Additional codes for proprietary commands are are defined in <ti/devices/${DEVICE_FAMILY}/driverlib/rf_prop_mailbox.h>.

The status is queried via the command’s status field that is available for every radio operation:

struct RF_Op {
   // ...
   uint16_t status;                     // An integer telling the status of the command. This value is
                                        // updated by the RF core during operation and may be read by the
                                        // system CPU at any time.
    // ...
};

It is recommended to read the status only after command completion:

// Assuming that 'command' points to a valid radio operation and
// execution has completed. Volatile is needed because 'status'
// is written on another processor.
volatile RF_Op* command;
if (((volatile RF_Op*)command)->status == PROP_DONE_RXTIMEOUT) {
    // ...
}

It is tempting to use the status member to synchronize to command execution, for instance, to wait until the command has actually started:

// Dangerous, don't do that!
while (((volatile RF_Op*)command)->status != ACTIVE) {}

However, this is strongly discouraged because the calling thread might get interrupted while the command has already finished in the meantime. Hence, the ACTIVE state is never seen and the while loop runs forever. Instead, use RF driver events to synchronize to command execution, either via callbacks or RF_pendCmd().

Whenever executing an RF operation, it is good practise to check the status code and test for possible error case. Depending whether it is a proprietary or a PHY-independent command, several various status codes apply. It is up to the application to take appropriate measures.

The following code snippet shows an example how to execute a TX operation and how to check the status:

#include <ti/devices/${DEVICE_FAMILY}/driverlib/rf_prop_mailbox.h>

// Execute the command
RF_EventMask result = RF_runCmd(rfHandle, (RF_Op*)&RF_cmdPropTx, RF_PriorityNormal, NULL, 0);

if (result & RF_EventLastCmdDone)
{
    // Ok. Expected case
}
else if (result & RF_EventCmdCancelled)
{
    // Optional. Somebody cancelled the command with RF_cancelCmd().
}

// Now evaluate the status code of CMD_PROP_TX
// to see how the command finished
switch (((volatile RF_Op*)&RF_cmdPropTx)->status)
{
case PROP_DONE_OK:
    // Success. Nothing to do
    break;
case PROP_ERROR_NO_FS:
    // Error case. Due to CMD_FS failing during power-up, CMD_PROP_TX
    // could not succeed. The RF core is powered up and the synth is not running.
    // One possible work-around is: Ignore it and power the RF core down
    // manually. CMD_FS will be re-executed on the next RF core power-up.
    RF_yield(rfHandle);
case PROP_DONE_ABORT:
case PROP_DONE_STOPPED:
    // Command was aborted/stopped by RF_cancelCmd().
    break;
case PROP_ERROR_PAR:
    // Command was started with an illegal parameter.
    break;
default:
    // Unexpected status code observed.
    // ...
    break;
}