[cc2538] interrupt driven radio driver (#4137)

- Add a configuration flag that enables the reception of 802.15.4
radio frames by way of a hardware interrupt so that frames are not
missed whilst the CPU is busy processing something else.

- Move otPlatRadioGetPromiscuous into separate function. The only
place `readFrame` uses `aInstance` is when calling
`otPlatRadioGetPromiscuous`, which then ignores `aInstance` anyway.
So make a private function that reads the hardware register which
`readFrame` and `otPlatRadioGetPromiscuous` can call.

- Use cc2538RadioGetPromiscuous in readFrame. This allows us to drop
the problematic `aInstance` pointer. Whilst we're here, make the
function `static` since nothing else outside of `radio.c` calls it.

- Call readFrame on incoming data. If the interrupt fires, call
`readFrame` to pull that data in. When polling, also check (with the
interrupts disabled) just in case.

- Enable correct NVIC interrupt. `startup-gcc.c` enables alternate
interrupt mappings, which places the radio receive interrupt at number
26; which is in the very first register.

- Avoid debug logs in receiveFrame. If we call this method from an
interrupt handler, we want to avoid calling `snprintf` and similar
functions as these are generally not interrupt-safe. Instead, use an
extra byte of RAM to store the number of bytes dropped so we can log
it from a safe context. This is only needed when both interrupts _and_
platform logging are enabled.
This commit is contained in:
Stuart Longland
2019-09-17 17:10:01 -07:00
committed by Jonathan Hui
parent 992ca4fdda
commit ebf665eb6a
2 changed files with 82 additions and 10 deletions
@@ -108,6 +108,20 @@
*/
#define OPENTHREAD_CONFIG_NCP_UART_ENABLE 1
/**
* @def OPENTHREAD_CONFIG_CC2538_USE_RADIO_RX_INTERRUPT
*
* Enable support for using interrupt-driven radio reception. This allows
* for a single frame to be received whilst the CPU is busy processing some
* other code.
*
* To disable interrupts and just rely on polling, set this to 0.
*
*/
#ifndef OPENTHREAD_CONFIG_CC2538_USE_RADIO_RX_INTERRUPT
#define OPENTHREAD_CONFIG_CC2538_USE_RADIO_RX_INTERRUPT 1
#endif
/**
* @def OPENTHREAD_CONFIG_CC2538_WITH_CC2592
*
+68 -10
View File
@@ -41,8 +41,6 @@
#include "common/logging.hpp"
#include "utils/code_utils.h"
#define RFCORE_RXTX_INT (141)
#define RFCORE_XREG_RFIRQM0 0x4008868C // RF interrupt masks
#define RFCORE_XREG_RFIRQM1 0x40088690 // RF interrupt masks
#define RFCORE_XREG_RFERRM 0x40088694 // RF error interrupt mask
@@ -173,6 +171,13 @@ static int8_t sTxPower = 0;
static otRadioState sState = OT_RADIO_STATE_DISABLED;
static bool sIsReceiverEnabled = false;
#if OPENTHREAD_CONFIG_LOG_PLATFORM && OPENTHREAD_CONFIG_CC2538_USE_RADIO_RX_INTERRUPT
// Debugging _and_ logging are enabled, so if there's a dropped frame
// we'll need to store the length here as using snprintf from an interrupt
// handler is not a good idea.
static uint8_t sDroppedFrameLength = 0;
#endif
void enableReceiver(void)
{
if (!sIsReceiverEnabled)
@@ -327,10 +332,12 @@ void cc2538RadioInit(void)
sReceiveFrame.mLength = 0;
sReceiveFrame.mPsdu = sReceivePsdu;
// Enable interrupts for RX/TX, interrupt 141.
// That's NVIC index 5 bit 13.
HWREG(NVIC_EN0 + (5 * 4)) = (1 << 13);
#if OPENTHREAD_CONFIG_CC2538_USE_RADIO_RX_INTERRUPT
// Enable interrupts for RX/TX, interrupt 26.
// That's NVIC index 0 (26 >> 5) bit 26 (26 & 0x1f).
HWREG(NVIC_EN0 + (0 * 4)) = (1 << 26);
HWREG(RFCORE_XREG_RFIRQM0) |= RFCORE_XREG_RFIRQM0_RXPKTDONE;
#endif
// enable clock
HWREG(SYS_CTRL_RCGCRFC) = SYS_CTRL_RCGCRFC_RFC0;
@@ -610,11 +617,16 @@ otRadioCaps otPlatRadioGetCaps(otInstance *aInstance)
return OT_RADIO_CAPS_NONE;
}
static bool cc2538RadioGetPromiscuous(void)
{
return (HWREG(RFCORE_XREG_FRMFILT0) & RFCORE_XREG_FRMFILT0_FRAME_FILTER_EN) == 0;
}
bool otPlatRadioGetPromiscuous(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
return (HWREG(RFCORE_XREG_FRMFILT0) & RFCORE_XREG_FRMFILT0_FRAME_FILTER_EN) == 0;
return cc2538RadioGetPromiscuous();
}
static int8_t cc2538RadioGetRssiOffset(void)
@@ -649,12 +661,18 @@ void otPlatRadioSetPromiscuous(otInstance *aInstance, bool aEnable)
}
}
void readFrame(otInstance *aInstance)
static void readFrame(void)
{
uint8_t length;
uint8_t crcCorr;
int i;
/*
* There is already a frame present in the buffer, return early so
* we do not overwrite it (hopefully we'll catch it on the next run).
*/
otEXPECT(sReceiveFrame.mLength == 0);
otEXPECT(sState == OT_RADIO_STATE_RECEIVE || sState == OT_RADIO_STATE_TRANSMIT);
otEXPECT((HWREG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_FIFOP) != 0);
@@ -666,7 +684,7 @@ void readFrame(otInstance *aInstance)
#error Time sync requires the timestamp of SFD rather than that of rx done!
#else
// Timestamp
if (otPlatRadioGetPromiscuous(aInstance))
if (cc2538RadioGetPromiscuous())
#endif
{
// The current driver only supports milliseconds resolution.
@@ -692,8 +710,14 @@ void readFrame(otInstance *aInstance)
// resets rxfifo
HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX;
HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX;
#if OPENTHREAD_CONFIG_LOG_PLATFORM && OPENTHREAD_CONFIG_CC2538_USE_RADIO_RX_INTERRUPT
// Debugging _and_ logging are enabled, it may not be safe to do
// logging if we're in the interrupt context, so just stash the
// length and do the logging later.
sDroppedFrameLength = length;
#else
otLogDebgPlat("Dropping %d received bytes (Invalid CRC)", length);
#endif
}
// check for rxfifo overflow
@@ -710,7 +734,21 @@ exit:
void cc2538RadioProcess(otInstance *aInstance)
{
readFrame(aInstance);
#if OPENTHREAD_CONFIG_CC2538_USE_RADIO_RX_INTERRUPT
// Disable the receive interrupt so that sReceiveFrame doesn't get
// blatted by the interrupt handler while we're polling.
HWREG(RFCORE_XREG_RFIRQM0) &= ~RFCORE_XREG_RFIRQM0_RXPKTDONE;
#endif
readFrame();
#if OPENTHREAD_CONFIG_LOG_PLATFORM && OPENTHREAD_CONFIG_CC2538_USE_RADIO_RX_INTERRUPT
if (sDroppedFrameLength != 0)
{
otLogDebgPlat("Dropping %d received bytes (Invalid CRC)", sDroppedFrameLength);
sDroppedFrameLength = 0;
}
#endif
if ((sState == OT_RADIO_STATE_RECEIVE && sReceiveFrame.mLength > 0) ||
(sState == OT_RADIO_STATE_TRANSMIT && sReceiveFrame.mLength > IEEE802154_ACK_LENGTH))
@@ -772,10 +810,30 @@ void cc2538RadioProcess(otInstance *aInstance)
}
sReceiveFrame.mLength = 0;
#if OPENTHREAD_CONFIG_CC2538_USE_RADIO_RX_INTERRUPT
// Turn the receive interrupt handler back on now the buffer is clear.
HWREG(RFCORE_XREG_RFIRQM0) |= RFCORE_XREG_RFIRQM0_RXPKTDONE;
#endif
}
void RFCoreRxTxIntHandler(void)
{
#if OPENTHREAD_CONFIG_CC2538_USE_RADIO_RX_INTERRUPT
if (HWREG(RFCORE_SFR_RFIRQF0) & RFCORE_SFR_RFIRQF0_RXPKTDONE)
{
readFrame();
if (sReceiveFrame.mLength > 0)
{
// A frame has been received, disable the interrupt handler
// until the main loop has dealt with this previous frame,
// otherwise we might overwrite it whilst it is being read.
HWREG(RFCORE_XREG_RFIRQM0) &= ~RFCORE_XREG_RFIRQM0_RXPKTDONE;
}
}
#endif
HWREG(RFCORE_SFR_RFIRQF0) = 0;
}