diff --git a/cpu/cc2538/include/cc2538_rf.h b/cpu/cc2538/include/cc2538_rf.h index d7c10bfac7..82d307ede6 100644 --- a/cpu/cc2538/include/cc2538_rf.h +++ b/cpu/cc2538/include/cc2538_rf.h @@ -298,6 +298,24 @@ typedef struct { */ void cc2538_rf_hal_setup(ieee802154_dev_t *hal); +/** + * @brief Enable CC2538 RF IRQs. + */ +static inline void cc2538_rf_enable_irq(void) +{ + RFCORE_XREG_RFIRQM1 = TXDONE | CSP_STOP | TXACKDONE; + RFCORE_XREG_RFIRQM0 = RXPKTDONE | SFD; +} + +/** + * @brief Disable CC2538 RF IRQs. + */ +static inline void cc2538_rf_disable_irq(void) +{ + RFCORE_XREG_RFIRQM1 = 0; + RFCORE_XREG_RFIRQM0 = 0; +} + /** * @brief IRQ handler for RF events * diff --git a/cpu/cc2538/radio/cc2538_rf.c b/cpu/cc2538/radio/cc2538_rf.c index 01822823bd..4755ad3fe7 100644 --- a/cpu/cc2538/radio/cc2538_rf.c +++ b/cpu/cc2538/radio/cc2538_rf.c @@ -134,8 +134,7 @@ void cc2538_init(void) RFCORE_XREG_FIFOPCTRL = CC2538_RF_MAX_DATA_LEN; /* Set default IRQ */ - RFCORE_XREG_RFIRQM1 = TXDONE | CSP_STOP | TXACKDONE; - RFCORE_XREG_RFIRQM0 = RXPKTDONE | FIFOP | SFD; + cc2538_rf_enable_irq(); /* Enable all RF CORE error interrupts */ RFCORE_XREG_RFERRM = STROBE_ERR | TXUNDERF | TXOVERF | \ diff --git a/cpu/cc2538/radio/cc2538_rf_radio_ops.c b/cpu/cc2538/radio/cc2538_rf_radio_ops.c index b62b21ef08..c3184bec4f 100644 --- a/cpu/cc2538/radio/cc2538_rf_radio_ops.c +++ b/cpu/cc2538/radio/cc2538_rf_radio_ops.c @@ -30,21 +30,39 @@ #include "net/ieee802154/radio.h" +#define ENABLE_DEBUG 0 +#include "debug.h" + static const ieee802154_radio_ops_t cc2538_rf_ops; static ieee802154_dev_t *cc2538_rf_hal; -static bool cc2538_tx_busy; /**< used to indicate TX chain is busy */ -static bool cc2538_rx_busy; /**< used to indicate RX chain is busy */ +typedef enum { + CC2538_STATE_READY, /**< The radio is ready to receive requests */ + CC2538_STATE_TRX_TRANSITION, /**< There's a pending TRX state transition */ + CC2538_STATE_CONFIRM_TX, /**< Transmission finished and waiting for confirm */ + CC2538_STATE_TX_BUSY, /**< The radio is busy transmitting */ + CC2538_STATE_TX_ACK, /**< The radio is currently transmitting an ACK frame */ + CC2538_STATE_CCA, /**< The radio is doing CCA */ + CC2538_STATE_CONFIRM_CCA, /**< CCA finished and waiting for confirm */ +} cc2538_state_t; +static cc2538_state_t cc2538_state; static uint8_t cc2538_min_be = CONFIG_IEEE802154_DEFAULT_CSMA_CA_MIN_BE; static uint8_t cc2538_max_be = CONFIG_IEEE802154_DEFAULT_CSMA_CA_MAX_BE; static int cc2538_csma_ca_retries = CONFIG_IEEE802154_DEFAULT_CSMA_CA_RETRIES; static bool cc2538_cca_status; /**< status of the last CCA request */ -static bool cc2538_cca; /**< used to check whether the last CCA result - corresponds to a CCA request or send with - CSMA-CA */ + +static void _enable_rx(void) +{ + RFCORE_XREG_FRMCTRL0 &= ~CC2538_FRMCTRL0_RX_MODE_DIS; +} + +static void _disable_rx(void) +{ + RFCORE_XREG_FRMCTRL0 |= CC2538_FRMCTRL0_RX_MODE_DIS; +} static int _write(ieee802154_dev_t *dev, const iolist_t *iolist) { @@ -69,8 +87,10 @@ static int _confirm_transmit(ieee802154_dev_t *dev, ieee802154_tx_info_t *info) { (void) dev; - if (cc2538_tx_busy) { - return -EAGAIN; + cc2538_rf_disable_irq(); + int res = -EAGAIN; + if (cc2538_state != CC2538_STATE_CONFIRM_TX) { + goto end; } if (info) { @@ -82,14 +102,22 @@ static int _confirm_transmit(ieee802154_dev_t *dev, ieee802154_tx_info_t *info) } } - return 0; + cc2538_state = CC2538_STATE_READY; + res = 0; + +end: + cc2538_rf_enable_irq(); + return res; } static int _request_transmit(ieee802154_dev_t *dev) { (void) dev; - cc2538_tx_busy = true; + cc2538_rf_disable_irq(); + assert(cc2538_state != CC2538_STATE_TX_BUSY); + cc2538_state = CC2538_STATE_TX_BUSY; + if (cc2538_csma_ca_retries < 0) { RFCORE_SFR_RFST = ISTXON; /* The CPU Ctrl mask is used here to indicate whether the radio is being @@ -101,10 +129,8 @@ static int _request_transmit(ieee802154_dev_t *dev) RFCORE_XREG_CSPCTRL |= CC2538_CSP_MCU_CTRL_MASK; } else { - cc2538_cca = false; - /* Disable RX Chain for CCA (see CC2538 RM, Section 29.9.5.3) */ - RFCORE_XREG_FRMCTRL0 |= CC2538_FRMCTRL0_RX_MODE_DIS; + _disable_rx(); RFCORE_SFR_RFST = ISRXON; /* Clear last program */ RFCORE_SFR_RFST = ISCLEAR; @@ -162,6 +188,7 @@ static int _request_transmit(ieee802154_dev_t *dev) /* Execute the program */ RFCORE_SFR_RFST = ISSTART; } + cc2538_rf_enable_irq(); return 0; } @@ -226,8 +253,12 @@ static int _read(ieee802154_dev_t *dev, void *buf, size_t size, ieee802154_rx_in } end: - /* Enable RX Chain */ - RFCORE_XREG_FRMCTRL0 &= ~CC2538_FRMCTRL0_RX_MODE_DIS; + /* Don't enable RX chain if the radio is currently doing CCA or during + * transmission. */ + if (cc2538_state != CC2538_STATE_CCA + && cc2538_state != CC2538_STATE_TX_BUSY) { + _enable_rx(); + } RFCORE_SFR_RFST = ISFLUSHRX; return res; } @@ -236,22 +267,38 @@ static int _confirm_cca(ieee802154_dev_t *dev) { (void) dev; - RFCORE_XREG_RFIRQM0 |= RXPKTDONE; + int res = -EAGAIN; + cc2538_rf_disable_irq(); + assert(cc2538_state == CC2538_STATE_CCA || cc2538_state == CC2538_STATE_CONFIRM_CCA); - return cc2538_cca_status; + if (cc2538_state != CC2538_STATE_CONFIRM_CCA) { + goto end; + } + + cc2538_state = CC2538_STATE_READY; + _enable_rx(); + res = cc2538_cca_status; + +end: + cc2538_rf_enable_irq(); + + return res; } static int _request_cca(ieee802154_dev_t *dev) { (void) dev; + int res = -EINVAL; + cc2538_rf_disable_irq(); if (!RFCORE->XREG_FSMSTAT1bits.RX_ACTIVE) { - return -EINVAL; + goto end; } - /* Ignore baseband processing */ - RFCORE_XREG_RFIRQM0 &= ~RXPKTDONE; + cc2538_state = CC2538_STATE_CCA; + + /* Ignore baseband processing */ + _disable_rx(); - cc2538_cca = true; RFCORE_SFR_RFST = ISCLEAR; RFCORE_SFR_RFST = STOP; RFCORE_XREG_CSPCTRL &= ~CC2538_CSP_MCU_CTRL_MASK; @@ -259,7 +306,9 @@ static int _request_cca(ieee802154_dev_t *dev) /* Execute the last program */ RFCORE_SFR_RFST = ISSTART; - return 0; +end: + cc2538_rf_enable_irq(); + return res; } static int _set_cca_threshold(ieee802154_dev_t *dev, int8_t threshold) @@ -295,9 +344,9 @@ static int _config_phy(ieee802154_dev_t *dev, const ieee802154_phy_conf_t *conf) static int _confirm_set_trx_state(ieee802154_dev_t *dev) { (void) dev; - if (RFCORE->XREG_FSMSTAT0bits.FSM_FFCTRL_STATE == FSM_STATE_RX_CALIBRATION) { - return -EAGAIN; - } + assert(cc2538_state == CC2538_STATE_TRX_TRANSITION); + cc2538_state = CC2538_STATE_READY; + cc2538_rf_enable_irq(); return 0; } @@ -305,33 +354,38 @@ static int _request_set_trx_state(ieee802154_dev_t *dev, ieee802154_trx_state_t { (void) dev; - int irq = irq_disable(); - if (cc2538_tx_busy || cc2538_rx_busy) { - irq_restore(irq); - return -EBUSY; + cc2538_rf_disable_irq(); + int res = -EBUSY; + if (cc2538_state != CC2538_STATE_READY) { + goto end; } + res = 0; + cc2538_state = CC2538_STATE_TRX_TRANSITION; + switch (state) { case IEEE802154_TRX_STATE_TRX_OFF: case IEEE802154_TRX_STATE_TX_ON: if (RFCORE->XREG_FSMSTAT0bits.FSM_FFCTRL_STATE != FSM_STATE_IDLE) { RFCORE_SFR_RFST = ISRFOFF; } - cc2538_rx_busy = false; break; case IEEE802154_TRX_STATE_RX_ON: - RFCORE_XREG_RFIRQM0 |= RXPKTDONE; /* Enable RX Chain */ - RFCORE_XREG_FRMCTRL0 &= ~CC2538_FRMCTRL0_RX_MODE_DIS; + _enable_rx(); RFCORE_SFR_RFST = ISRXON; break; } - RFCORE_SFR_RFIRQF0 = 0; - RFCORE_SFR_RFIRQF1 = 0; + RFCORE_SFR_RFIRQF0 &= ~RXPKTDONE; + RFCORE_SFR_RFIRQF0 &= ~SFD; - irq_restore(irq); - return 0; +end: + if (res < 0) { + cc2538_rf_enable_irq(); + } + + return res; } void cc2538_irq_handler(void) @@ -339,79 +393,108 @@ void cc2538_irq_handler(void) uint_fast8_t flags_f0 = RFCORE_SFR_RFIRQF0; uint_fast8_t flags_f1 = RFCORE_SFR_RFIRQF1; - RFCORE_SFR_RFIRQF0 = 0; - RFCORE_SFR_RFIRQF1 = 0; + uint8_t handled_f0 = 0; + uint8_t handled_f1 = 0; - if ((flags_f0 & SFD)) { - /* If the radio already transmitted, this SFD is the TX_START event */ - if (cc2538_tx_busy) { - cc2538_rf_hal->cb(cc2538_rf_hal, IEEE802154_RADIO_INDICATION_TX_START); - } - /* If the RX chain was not busy, the detected SFD corresponds to a new - * incoming frame. Note the automatic ACK frame also triggers this event. - * Therefore, we use this variable to distinguish them. */ - else if (!cc2538_rx_busy){ - cc2538_rx_busy = true; - cc2538_rf_hal->cb(cc2538_rf_hal, IEEE802154_RADIO_INDICATION_RX_START); - } - } + assert(cc2538_state != CC2538_STATE_TRX_TRANSITION); if (flags_f1 & TXDONE) { + handled_f1 |= TXDONE; /* TXDONE marks the end of the TX chain. The radio is not busy anymore */ - cc2538_tx_busy = false; + assert(cc2538_state == CC2538_STATE_TX_BUSY); + cc2538_state = CC2538_STATE_CONFIRM_TX; cc2538_rf_hal->cb(cc2538_rf_hal, IEEE802154_RADIO_CONFIRM_TX_DONE); } + /* The RX chain is not busy anymore on TXACKDONE event */ + if (flags_f1 & TXACKDONE) { + handled_f1 |= TXACKDONE; + assert(cc2538_state == CC2538_STATE_TX_ACK); + cc2538_state = CC2538_STATE_READY; + } + + if ((flags_f0 & SFD)) { + handled_f0 |= SFD; + switch(cc2538_state) { + case CC2538_STATE_READY: + cc2538_rf_hal->cb(cc2538_rf_hal, IEEE802154_RADIO_INDICATION_RX_START); + break; + case CC2538_STATE_TX_BUSY: + /* If the radio already transmitted, this SFD is the TX_START event */ + cc2538_rf_hal->cb(cc2538_rf_hal, IEEE802154_RADIO_INDICATION_TX_START); + break; + case CC2538_STATE_TX_ACK: + /* The detected SFD comes from the transmitted ACK frame. Simply + * ignore it */ + break; + default: + /* This should never happen */ + DEBUG("ERROR: cc2538_state: %i\n", cc2538_state); + assert(false); + } + } + if (flags_f0 & RXPKTDONE) { + handled_f0 |= RXPKTDONE; /* CRC check */ uint8_t pkt_len = rfcore_peek_rx_fifo(0); if (rfcore_peek_rx_fifo(pkt_len) & CC2538_CRC_BIT_MASK) { /* Disable RX while the frame has not been processed */ - RFCORE_XREG_FRMCTRL0 |= CC2538_FRMCTRL0_RX_MODE_DIS; + _disable_rx(); /* If AUTOACK is disabled or the ACK request bit is not set */ if (IS_ACTIVE(CONFIG_IEEE802154_AUTO_ACK_DISABLE) || (!(rfcore_peek_rx_fifo(1) & IEEE802154_FCF_ACK_REQ))) { /* The radio won't send an ACK. Therefore the RX chain is not * busy anymore */ - cc2538_rx_busy = false; + cc2538_state = CC2538_STATE_READY; + } + else { + cc2538_state = CC2538_STATE_TX_ACK; } cc2538_rf_hal->cb(cc2538_rf_hal, IEEE802154_RADIO_INDICATION_RX_DONE); } else { /* Disable RX while the frame has not been processed */ /* CRC failed; discard packet. The RX chain is not busy anymore */ - cc2538_rx_busy = false; + cc2538_state = CC2538_STATE_READY; cc2538_rf_hal->cb(cc2538_rf_hal, IEEE802154_RADIO_INDICATION_CRC_ERROR); } } - /* The RX chain is not busy anymore on TXACKDONE event */ - if (flags_f1 & TXACKDONE) { - cc2538_rx_busy = false; - } - /* Check if the interrupt was triggered because the CSP finished its routine * (CSMA-CA or CCA request) */ if (flags_f1 & CSP_STOP) { + handled_f1 |= CSP_STOP; RFCORE_XREG_CSPCTRL |= CC2538_CSP_MCU_CTRL_MASK; - if (!cc2538_cca) { + switch (cc2538_state) { + case CC2538_STATE_TX_BUSY: if (RFCORE_XREG_CSPZ > 0) { RFCORE_SFR_RFST = ISTXON; } else { /* In case of CCA failure the TX chain is not busy anymore */ - cc2538_tx_busy = false; + cc2538_state = CC2538_STATE_CONFIRM_TX; cc2538_rf_hal->cb(cc2538_rf_hal, IEEE802154_RADIO_CONFIRM_TX_DONE); } - } - else { + break; + case CC2538_STATE_CCA: cc2538_cca_status = BOOLEAN(RFCORE->XREG_FSMSTAT1bits.CCA) && RFCORE->XREG_RSSISTATbits.RSSI_VALID; + cc2538_state = CC2538_STATE_CONFIRM_CCA; cc2538_rf_hal->cb(cc2538_rf_hal, IEEE802154_RADIO_CONFIRM_CCA); + break; + default: + /* This should never happen */ + DEBUG("ERROR: cc2538_state: %i\n", cc2538_state); + assert(false); + break; } } + + RFCORE_SFR_RFIRQF0 &= ~handled_f0; + RFCORE_SFR_RFIRQF1 &= ~handled_f1; } static int _off(ieee802154_dev_t *dev)