This is an automated email from the ASF dual-hosted git repository. xiaoxiang781216 pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit c54acde4f2f513ab1f1f0aaf6aeceb7a967525ca Author: raiden00pl <[email protected]> AuthorDate: Thu Oct 22 16:36:21 2026 +0200 arch/arm/nrf54l: add the IEEE 802.15.4 superframe support Port the beacon transmission, the superframe timing and the slotted CSMA-CA of nrf52 to nrf54l. The superframe timing runs from the GRTC SYSCOUNTER on interrupt group 1 and compare channel 1, so it coexists with the GRTC system timer. Assisted-by: Claude Code Signed-off-by: raiden00pl <[email protected]> --- arch/arm/src/nrf54l/CMakeLists.txt | 3 + arch/arm/src/nrf54l/Kconfig | 14 + arch/arm/src/nrf54l/Make.defs | 3 + arch/arm/src/nrf54l/nrf54l_ieee802154.c | 268 ++++++++-- arch/arm/src/nrf54l/nrf54l_ieee802154_priv.h | 28 ++ arch/arm/src/nrf54l/nrf54l_ieee802154_radio.c | 677 +++++++++++++++++++++++--- arch/arm/src/nrf54l/nrf54l_ieee802154_radio.h | 52 +- arch/arm/src/nrf54l/nrf54l_ieee802154_rtc.c | 564 +++++++++++++++++++++ arch/arm/src/nrf54l/nrf54l_ieee802154_rtc.h | 139 ++++++ arch/arm/src/nrf54l/nrf54l_ieee802154_trace.c | 6 +- arch/arm/src/nrf54l/nrf54l_ieee802154_trace.h | 6 +- 11 files changed, 1653 insertions(+), 107 deletions(-) diff --git a/arch/arm/src/nrf54l/CMakeLists.txt b/arch/arm/src/nrf54l/CMakeLists.txt index 72cabf76fcf..2c7d0d29e1d 100644 --- a/arch/arm/src/nrf54l/CMakeLists.txt +++ b/arch/arm/src/nrf54l/CMakeLists.txt @@ -67,6 +67,9 @@ if(CONFIG_NRF54L_RADIO) list(APPEND SRCS nrf54l_ieee802154_radio.c) list(APPEND SRCS nrf54l_ieee802154_tim.c) list(APPEND SRCS nrf54l_ieee802154.c) + if(CONFIG_NRF54L_RADIO_IEEE802154_SUPERFRAME) + list(APPEND SRCS nrf54l_ieee802154_rtc.c) + endif() if(CONFIG_NRF54L_RADIO_IEEE802154_TRACE) list(APPEND SRCS nrf54l_ieee802154_trace.c) endif() diff --git a/arch/arm/src/nrf54l/Kconfig b/arch/arm/src/nrf54l/Kconfig index c308ee2bead..803e9e81292 100644 --- a/arch/arm/src/nrf54l/Kconfig +++ b/arch/arm/src/nrf54l/Kconfig @@ -565,6 +565,20 @@ config NRF54L_RADIO_IEEE802154 if NRF54L_RADIO_IEEE802154 +config NRF54L_RADIO_IEEE802154_SUPERFRAME + bool "RADIO IEEE802.15.4 superframe support" + default n + select NRF54L_GRTC + select NRF54L_USE_LFCLK + ---help--- + The superframe timing uses GRTC interrupt group 1 and compare + channel 1, the system timer keeps group 0 and channel 0. + +config NRF54L_RADIO_IEEE802154_GTS_SLOTS + int "RADIO IEEE802.15.4 GTS slots" + default 2 + depends on NRF54L_RADIO_IEEE802154_SUPERFRAME + config NRF54L_RADIO_IEEE802154_TRACE bool "RADIO IEEE802.15.4 trace support" default n diff --git a/arch/arm/src/nrf54l/Make.defs b/arch/arm/src/nrf54l/Make.defs index 44fa4b3f2e2..3bacfee821f 100644 --- a/arch/arm/src/nrf54l/Make.defs +++ b/arch/arm/src/nrf54l/Make.defs @@ -72,6 +72,9 @@ ifeq ($(CONFIG_NRF54L_RADIO_IEEE802154),y) CHIP_CSRCS += nrf54l_ieee802154_radio.c CHIP_CSRCS += nrf54l_ieee802154_tim.c CHIP_CSRCS += nrf54l_ieee802154.c +ifeq ($(CONFIG_NRF54L_RADIO_IEEE802154_SUPERFRAME),y) +CHIP_CSRCS += nrf54l_ieee802154_rtc.c +endif ifeq ($(CONFIG_NRF54L_RADIO_IEEE802154_TRACE),y) CHIP_CSRCS += nrf54l_ieee802154_trace.c endif diff --git a/arch/arm/src/nrf54l/nrf54l_ieee802154.c b/arch/arm/src/nrf54l/nrf54l_ieee802154.c index 58a88d1ffde..08180ca3de2 100644 --- a/arch/arm/src/nrf54l/nrf54l_ieee802154.c +++ b/arch/arm/src/nrf54l/nrf54l_ieee802154.c @@ -27,6 +27,7 @@ #include <nuttx/config.h> #include <nuttx/debug.h> +#include <stdlib.h> #include <string.h> #include <nuttx/kmalloc.h> @@ -69,54 +70,54 @@ static struct nrf54l_radioi8_dev_s g_nrf54l_radioi8; /* Set/get radio attributes */ static int nrf54l_radioi8_setrxmode(struct nrf54l_radioi8_dev_s *dev, - int mode); + int mode); static int nrf54l_radioi8_setpanid(struct nrf54l_radioi8_dev_s *dev, - const uint8_t *panid); + const uint8_t *panid); static int nrf54l_radioi8_setsaddr(struct nrf54l_radioi8_dev_s *dev, - const uint8_t *saddr); + const uint8_t *saddr); static int nrf54l_radioi8_seteaddr(struct nrf54l_radioi8_dev_s *dev, - const uint8_t *eaddr); + const uint8_t *eaddr); static int nrf54l_radioi8_setcoordsaddr(struct nrf54l_radioi8_dev_s *dev, const uint8_t *saddr); static int nrf54l_radioi8_setdevmode(struct nrf54l_radioi8_dev_s *dev, - uint8_t mode); + uint8_t mode); /* Radio ops */ static int nrf54l_radioi8_bind(struct ieee802154_radio_s *radio, - struct ieee802154_radiocb_s *radiocb); + struct ieee802154_radiocb_s *radiocb); static int nrf54l_radioi8_reset(struct ieee802154_radio_s *radio); static int nrf54l_radioi8_getattr(struct ieee802154_radio_s *radio, - enum ieee802154_attr_e attr, - union ieee802154_attr_u *attrval); + enum ieee802154_attr_e attr, + union ieee802154_attr_u *attrval); static int nrf54l_radioi8_setattr(struct ieee802154_radio_s *radio, - enum ieee802154_attr_e attr, - const union ieee802154_attr_u *attrval); + enum ieee802154_attr_e attr, + const union ieee802154_attr_u *attrval); static int nrf54l_radioi8_txnotify(struct ieee802154_radio_s *radio, - bool gts); + bool gts); static int nrf54l_radioi8_txdelayed(struct ieee802154_radio_s *radio, - struct ieee802154_txdesc_s *txdesc, - uint32_t symboldelay); + struct ieee802154_txdesc_s *txdesc, + uint32_t symboldelay); static int nrf54l_radioi8_rxenable(struct ieee802154_radio_s *radio, - bool enable); + bool enable); static int nrf54l_radioi8_setchannel(struct nrf54l_radioi8_dev_s *dev, - uint8_t chan); + uint8_t chan); static int nrf54l_radioi8_setcca(struct nrf54l_radioi8_dev_s *dev, - struct ieee802154_cca_s *cca); + struct ieee802154_cca_s *cca); static int nrf54l_radioi8_energydetect(struct ieee802154_radio_s *radio, - uint32_t nsymbols); + uint32_t nsymbols); static int nrf54l_radioi8_beaconstart(struct ieee802154_radio_s *radio, - const struct ieee802154_superframespec_s *sfspec, - struct ieee802154_beaconframe_s *beacon); + const struct ieee802154_superframespec_s *sfspec, + struct ieee802154_beaconframe_s *beacon); static int nrf54l_radioi8_beaconupdate(struct ieee802154_radio_s *radio, - struct ieee802154_beaconframe_s *beacon); + struct ieee802154_beaconframe_s *beacon); static int nrf54l_radioi8_beaconstop(struct ieee802154_radio_s *radio); static int nrf54l_radioi8_sfupdate(struct ieee802154_radio_s *radio, - const struct ieee802154_superframespec_s *sfspec); + const struct ieee802154_superframespec_s *sfspec); /**************************************************************************** * Private Functions @@ -131,7 +132,7 @@ nrf54l_radioi8_sfupdate(struct ieee802154_radio_s *radio, ****************************************************************************/ static int nrf54l_radioi8_setrxmode(struct nrf54l_radioi8_dev_s *dev, - int mode) + int mode) { wlinfo("setrxmode %d\n", mode); @@ -154,7 +155,7 @@ static int nrf54l_radioi8_setrxmode(struct nrf54l_radioi8_dev_s *dev, ****************************************************************************/ static int nrf54l_radioi8_setpanid(struct nrf54l_radioi8_dev_s *dev, - const uint8_t *panid) + const uint8_t *panid) { wlinfo("setpanid: %02X:%02X\n", panid[0], panid[1]); IEEE802154_PANIDCOPY(dev->state.addr.panid, panid); @@ -173,7 +174,7 @@ static int nrf54l_radioi8_setpanid(struct nrf54l_radioi8_dev_s *dev, ****************************************************************************/ static int nrf54l_radioi8_setsaddr(struct nrf54l_radioi8_dev_s *dev, - const uint8_t *saddr) + const uint8_t *saddr) { wlinfo("setsaddr: %02X:%02X\n", saddr[0], saddr[1]); IEEE802154_SADDRCOPY(dev->state.addr.saddr, saddr); @@ -191,7 +192,7 @@ static int nrf54l_radioi8_setsaddr(struct nrf54l_radioi8_dev_s *dev, ****************************************************************************/ static int nrf54l_radioi8_seteaddr(struct nrf54l_radioi8_dev_s *dev, - const uint8_t *eaddr) + const uint8_t *eaddr) { int i = 0; @@ -265,7 +266,7 @@ static int nrf54l_radioi8_setcoordeaddr(struct nrf54l_radioi8_dev_s *dev, ****************************************************************************/ static int nrf54l_radioi8_setdevmode(struct nrf54l_radioi8_dev_s *dev, - uint8_t mode) + uint8_t mode) { wlinfo("setdevmode %d\n", mode); @@ -288,7 +289,7 @@ static int nrf54l_radioi8_setdevmode(struct nrf54l_radioi8_dev_s *dev, ****************************************************************************/ static int nrf54l_radioi8_bind(struct ieee802154_radio_s *radio, - struct ieee802154_radiocb_s *radiocb) + struct ieee802154_radiocb_s *radiocb) { struct nrf54l_radioi8_dev_s *dev = (struct nrf54l_radioi8_dev_s *)radio; @@ -337,6 +338,12 @@ static int nrf54l_radioi8_reset(struct ieee802154_radio_s *radio) dev->tim->ops->reset(dev); +#ifdef CONFIG_NRF54L_RADIO_IEEE802154_SUPERFRAME + /* Reset RTC */ + + dev->rtc->ops->reset(dev); +#endif + /* Set channel */ nrf54l_radioi8_setchannel(dev, 11); @@ -370,8 +377,8 @@ errout: ****************************************************************************/ static int nrf54l_radioi8_getattr(struct ieee802154_radio_s *radio, - enum ieee802154_attr_e attr, - union ieee802154_attr_u *attrval) + enum ieee802154_attr_e attr, + union ieee802154_attr_u *attrval) { struct nrf54l_radioi8_dev_s *dev = (struct nrf54l_radioi8_dev_s *)radio; int ret = OK; @@ -447,8 +454,8 @@ static int nrf54l_radioi8_getattr(struct ieee802154_radio_s *radio, ****************************************************************************/ static int nrf54l_radioi8_setattr(struct ieee802154_radio_s *radio, - enum ieee802154_attr_e attr, - const union ieee802154_attr_u *attrval) + enum ieee802154_attr_e attr, + const union ieee802154_attr_u *attrval) { struct nrf54l_radioi8_dev_s *dev = (struct nrf54l_radioi8_dev_s *)radio; int ret = IEEE802154_STATUS_SUCCESS; @@ -531,7 +538,7 @@ static int nrf54l_radioi8_setattr(struct ieee802154_radio_s *radio, ****************************************************************************/ static int nrf54l_radioi8_txnotify(struct ieee802154_radio_s *radio, - bool gts) + bool gts) { struct nrf54l_radioi8_dev_s *dev = (struct nrf54l_radioi8_dev_s *)radio; int ret = OK; @@ -560,8 +567,8 @@ static int nrf54l_radioi8_txnotify(struct ieee802154_radio_s *radio, ****************************************************************************/ static int nrf54l_radioi8_txdelayed(struct ieee802154_radio_s *radio, - struct ieee802154_txdesc_s *txdesc, - uint32_t symboldelay) + struct ieee802154_txdesc_s *txdesc, + uint32_t symboldelay) { struct nrf54l_radioi8_dev_s *dev = (struct nrf54l_radioi8_dev_s *)radio; int ret = OK; @@ -600,7 +607,7 @@ static int nrf54l_radioi8_txdelayed(struct ieee802154_radio_s *radio, ****************************************************************************/ static int nrf54l_radioi8_rxenable(struct ieee802154_radio_s *radio, - bool enable) + bool enable) { struct nrf54l_radioi8_dev_s *dev = (struct nrf54l_radioi8_dev_s *)radio; @@ -617,7 +624,7 @@ static int nrf54l_radioi8_rxenable(struct ieee802154_radio_s *radio, ****************************************************************************/ static int nrf54l_radioi8_energydetect(struct ieee802154_radio_s *radio, - uint32_t nsymbols) + uint32_t nsymbols) { struct nrf54l_radioi8_dev_s *dev = (struct nrf54l_radioi8_dev_s *)radio; @@ -640,7 +647,7 @@ static int nrf54l_radioi8_energydetect(struct ieee802154_radio_s *radio, ****************************************************************************/ static int nrf54l_radioi8_setchannel(struct nrf54l_radioi8_dev_s *dev, - uint8_t chan) + uint8_t chan) { int ret = OK; @@ -678,7 +685,7 @@ static int nrf54l_radioi8_setchannel(struct nrf54l_radioi8_dev_s *dev, ****************************************************************************/ static int nrf54l_radioi8_setcca(struct nrf54l_radioi8_dev_s *dev, - struct ieee802154_cca_s *cca) + struct ieee802154_cca_s *cca) { int ret = OK; @@ -716,10 +723,58 @@ static int nrf54l_radioi8_setcca(struct nrf54l_radioi8_dev_s *dev, static int nrf54l_radioi8_beaconstart(struct ieee802154_radio_s *radio, - const struct ieee802154_superframespec_s *sfspec, - struct ieee802154_beaconframe_s *beacon) + const struct ieee802154_superframespec_s *sfspec, + struct ieee802154_beaconframe_s *beacon) { +#ifdef CONFIG_NRF54L_RADIO_IEEE802154_SUPERFRAME + struct nrf54l_radioi8_dev_s *dev = (struct nrf54l_radioi8_dev_s *)radio; + irqstate_t flags; + int ret; + + if (sfspec->pancoord) + { + flags = enter_critical_section(); + + /* Local copy */ + + memcpy(&dev->state.sf, (void *)sfspec, + sizeof(struct ieee802154_superframespec_s)); + + /* Configure the superframe timings */ + + ret = dev->rtc->ops->setup(dev, &dev->state.sf); + if (ret == OK) + { + /* Setup beacon transmission, macBSN starts from a random value */ + + dev->radio->ops->beacon_setup(dev, beacon->bf_data, + beacon->bf_len); + dev->radio->state.bsn = rand(); + + /* Slotted CSMA-CA in the CAP */ + + dev->radio->state.slotted = true; + dev->radio->state.ble = sfspec->ble; + + /* The PAN coordinator defines the superframe */ + + dev->radio->state.sftrack = false; + dev->rtc->ops->start(dev); + } + + leave_critical_section(flags); + } + else + { + /* TODO: missing logic for non-PAN coord */ + + ret = -ENOTTY; + } + + return ret; +#else return -ENOTSUP; +#endif } /**************************************************************************** @@ -732,11 +787,58 @@ nrf54l_radioi8_beaconstart(struct ieee802154_radio_s *radio, static int nrf54l_radioi8_beaconupdate(struct ieee802154_radio_s *radio, - struct ieee802154_beaconframe_s *beacon) + struct ieee802154_beaconframe_s *beacon) { +#ifdef CONFIG_NRF54L_RADIO_IEEE802154_SUPERFRAME + struct nrf54l_radioi8_dev_s *dev = (struct nrf54l_radioi8_dev_s *)radio; + irqstate_t flags; + + flags = enter_critical_section(); + + /* The beacon on the air is replaced when it is sent */ + + if (dev->radio->state.state == NRF54L_RADIO_STATE_BEACONTX) + { + dev->state.beacon_next = beacon; + } + else + { + dev->radio->ops->beacon_setup(dev, beacon->bf_data, beacon->bf_len); + } + + leave_critical_section(flags); + + return OK; +#else return -ENOTSUP; +#endif } +#ifdef CONFIG_NRF54L_RADIO_IEEE802154_SUPERFRAME +/**************************************************************************** + * Name: nrf54l_radioi8_unslotted + * + * Description: + * Switch to un-slotted CSMA-CA, a transaction paused for the next CAP + * starts again un-slotted. Must be called with the radio locked. + * + ****************************************************************************/ + +static void nrf54l_radioi8_unslotted(struct nrf54l_radioi8_dev_s *dev) +{ + dev->radio->state.slotted = false; + dev->radio->state.sftrack = false; + + if (dev->radio->state.csma_paused) + { + dev->radio->state.csma_paused = false; + dev->radio->state.csma_busy = false; + dev->state.csma_pending = true; + dev->radio->ops->txnext(dev, 0); + } +} +#endif + /**************************************************************************** * Name: nrf54l_radioi8_beaconstop * @@ -747,7 +849,23 @@ nrf54l_radioi8_beaconupdate(struct ieee802154_radio_s *radio, static int nrf54l_radioi8_beaconstop(struct ieee802154_radio_s *radio) { +#ifdef CONFIG_NRF54L_RADIO_IEEE802154_SUPERFRAME + struct nrf54l_radioi8_dev_s *dev = (struct nrf54l_radioi8_dev_s *)radio; + irqstate_t flags; + + flags = enter_critical_section(); + + /* Stop RTC */ + + dev->rtc->ops->stop(dev); + nrf54l_radioi8_unslotted(dev); + + leave_critical_section(flags); + + return OK; +#else return -ENOTSUP; +#endif } /**************************************************************************** @@ -760,9 +878,53 @@ static int nrf54l_radioi8_beaconstop(struct ieee802154_radio_s *radio) static int nrf54l_radioi8_sfupdate(struct ieee802154_radio_s *radio, - const struct ieee802154_superframespec_s *sfspec) + const struct ieee802154_superframespec_s *sfspec) { +#ifdef CONFIG_NRF54L_RADIO_IEEE802154_SUPERFRAME + struct nrf54l_radioi8_dev_s *dev = (struct nrf54l_radioi8_dev_s *)radio; + irqstate_t flags; + int ret = OK; + + flags = enter_critical_section(); + + /* Local copy */ + + memcpy(&dev->state.sf, (void *)sfspec, + sizeof(struct ieee802154_superframespec_s)); + + /* If we are operating on a beacon-enabled network, use slotted CSMA */ + + if (sfspec->beaconorder < 15) + { + /* Configure the superframe timings */ + + ret = dev->rtc->ops->setup(dev, &dev->state.sf); + if (ret == OK) + { + /* Need slotted CSMA-CA */ + + dev->radio->state.slotted = true; + dev->radio->state.ble = sfspec->ble; + + /* Sync with the beacons */ + + dev->radio->state.sftrack = true; + } + } + else + { + /* Need un-slotted CSMA-CA */ + + dev->rtc->ops->stop(dev); + nrf54l_radioi8_unslotted(dev); + } + + leave_critical_section(flags); + + return ret; +#else return -ENOTSUP; +#endif } /**************************************************************************** @@ -808,6 +970,10 @@ nrf54l_radioi8_register(struct nrf54l_radio_board_s *board) DEBUGASSERT(dev->radio->ops->setcca); DEBUGASSERT(dev->radio->ops->norm_setup); DEBUGASSERT(dev->radio->ops->norm_trigger); +#ifdef CONFIG_NRF54L_RADIO_IEEE802154_SUPERFRAME + DEBUGASSERT(dev->radio->ops->beacon_setup); + DEBUGASSERT(dev->radio->ops->beacon_tx); +#endif DEBUGASSERT(dev->radio->ops->reset); DEBUGASSERT(dev->radio->ops->csma_poll); DEBUGASSERT(dev->radio->ops->gts_poll); @@ -825,6 +991,24 @@ nrf54l_radioi8_register(struct nrf54l_radio_board_s *board) DEBUGASSERT(dev->tim->ops->stop); DEBUGASSERT(dev->tim->ops->reset); +#ifdef CONFIG_NRF54L_RADIO_IEEE802154_SUPERFRAME + /* Initialize RTC */ + + dev->rtc = nrf54l_radioi8_rtc_init(dev); + if (dev->rtc == NULL) + { + wlerr("nrf54l_radioi8_rtc_init failed %d\n", -errno); + return NULL; + } + + DEBUGASSERT(dev->rtc->ops->setup); + DEBUGASSERT(dev->rtc->ops->start); + DEBUGASSERT(dev->rtc->ops->sync); + DEBUGASSERT(dev->rtc->ops->stop); + DEBUGASSERT(dev->rtc->ops->reset); + DEBUGASSERT(dev->rtc->ops->elapsed); +#endif + /* Connect MAC ops */ dev->macops.bind = nrf54l_radioi8_bind; diff --git a/arch/arm/src/nrf54l/nrf54l_ieee802154_priv.h b/arch/arm/src/nrf54l/nrf54l_ieee802154_priv.h index 8e46ec99dab..cc056c837e8 100644 --- a/arch/arm/src/nrf54l/nrf54l_ieee802154_priv.h +++ b/arch/arm/src/nrf54l/nrf54l_ieee802154_priv.h @@ -35,12 +35,21 @@ #include <nuttx/wireless/ieee802154/ieee802154_radio.h> #include "nrf54l_ieee802154_radio.h" +#include "nrf54l_ieee802154_rtc.h" #include "nrf54l_ieee802154_tim.h" /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ +/* GTS slots */ + +#ifndef CONFIG_NRF54L_RADIO_IEEE802154_GTS_SLOTS +# define NRF54L_GTS_SLOTS 0 +#else +# define NRF54L_GTS_SLOTS CONFIG_NRF54L_RADIO_IEEE802154_GTS_SLOTS +#endif + /**************************************************************************** * Public Types ****************************************************************************/ @@ -90,6 +99,21 @@ struct nrf54l_radioi8_state_s bool txdelayed_pending; /* No CSMA transfer not started yet */ bool csma_pending; /* CSMA transfer not started yet */ uint32_t txdelayed_delay; /* Delay of the pending no CSMA transfer */ + +#ifdef CONFIG_NRF54L_RADIO_IEEE802154_SUPERFRAME + /* Superframe data */ + + struct ieee802154_superframespec_s sf; + + /* Beacon updated during its TX */ + + struct ieee802154_beaconframe_s *beacon_next; + + /* TX GTS */ + + struct ieee802154_txdesc_s *gts_desc[NRF54L_GTS_SLOTS]; + bool gts_busy[NRF54L_GTS_SLOTS]; +#endif }; /* IEEE 802.15.4 radio device */ @@ -114,6 +138,10 @@ struct nrf54l_radioi8_dev_s /* Low resolution, low power timer */ +#ifdef CONFIG_NRF54L_RADIO_IEEE802154_SUPERFRAME + struct nrf54l_radioi8_rtc_s *rtc; +#endif + /* Radio state */ struct nrf54l_radioi8_state_s state; diff --git a/arch/arm/src/nrf54l/nrf54l_ieee802154_radio.c b/arch/arm/src/nrf54l/nrf54l_ieee802154_radio.c index 8bef3542f82..bce002efad2 100644 --- a/arch/arm/src/nrf54l/nrf54l_ieee802154_radio.c +++ b/arch/arm/src/nrf54l/nrf54l_ieee802154_radio.c @@ -158,14 +158,33 @@ #define IEEE802154_ACKTX_INT (RADIO_INT_PHYEND) #define IEEE802154_ACKTX_SHORTS (RADIO_SHORTS_PHYEND_DISABLE) +/* aUnitBackoffPeriod in us */ + +#define NRF54L_BACKOFF_US (IEEE802154_UNIT_BACKOFF_PERIOD * \ + IEEE802154_SYMBOL_US) + +/* Time to set up a slotted CSMA-CA step before its boundary: the ramp-up + * and two timer ticks + */ + +#define NRF54L_SLOTTED_GUARD_US (NRF54L_RADIOI8_RAMPUP_US + \ + 2 * IEEE802154_SYMBOL_US) + /**************************************************************************** * Private Function Prototypes ****************************************************************************/ static void nrf54l_radioi8_rx_parse(struct nrf54l_radioi8_dev_s *dev, - uint8_t *ftype, uint8_t *cmdtype); + uint8_t *ftype, uint8_t *cmdtype); static void nrf54l_radioi8_ack_transmit(struct nrf54l_radioi8_dev_s *dev); static bool nrf54l_radioi8_filter(struct nrf54l_radioi8_dev_s *dev); +#ifdef CONFIG_NRF54L_RADIO_IEEE802154_SUPERFRAME +static bool nrf54l_radioi8_coordbeacon(struct nrf54l_radioi8_dev_s *dev); +#endif +#if NRF54L_GTS_SLOTS > 0 +static void nrf54l_radioi8_gts_setup(struct nrf54l_radioi8_dev_s *dev, + uint8_t fifo, struct iob_s *frame); +#endif /* Ops */ @@ -173,18 +192,29 @@ static void nrf54l_radioi8_txstart(struct nrf54l_radioi8_dev_s *dev); static void nrf54l_radioi8_ccastart(struct nrf54l_radioi8_dev_s *dev); static void nrf54l_radioi8_notify_noack(struct nrf54l_radioi8_dev_s *dev); static int nrf54l_radioi8_rxenable(struct nrf54l_radioi8_dev_s *dev, - bool enable); + bool enable); static int nrf54l_radioi8_energydetect(struct nrf54l_radioi8_dev_s *dev, - uint32_t nsymbols); + uint32_t nsymbols); static int nrf54l_radioi8_setchannel(struct nrf54l_radioi8_dev_s *dev, - uint8_t chan); + uint8_t chan); static int nrf54l_radioi8_setcca(struct nrf54l_radioi8_dev_s *dev, - struct ieee802154_cca_s *cca); + struct ieee802154_cca_s *cca); static void nrf54l_radioi8_norm_setup_buf(struct nrf54l_radioi8_dev_s *dev, - uint8_t *buf, bool csma); + uint8_t *buf, bool csma); static void nrf54l_radioi8_norm_setup(struct nrf54l_radioi8_dev_s *dev, - struct iob_s *frame, bool csma); + struct iob_s *frame, bool csma); static void nrf54l_radioi8_norm_trigger(struct nrf54l_radioi8_dev_s *dev); +#ifdef CONFIG_NRF54L_RADIO_IEEE802154_SUPERFRAME +static void nrf54l_radioi8_slotted_at(struct nrf54l_radioi8_dev_s *dev, + uint8_t chan); +static void nrf54l_radioi8_slotted_pause(struct nrf54l_radioi8_dev_s *dev); +static void nrf54l_radioi8_slotted_backoff(struct nrf54l_radioi8_dev_s *dev, + uint32_t guard); +static void nrf54l_radioi8_slotted_resume(struct nrf54l_radioi8_dev_s *dev); +static void nrf54l_radioi8_beacon_setup(struct nrf54l_radioi8_dev_s *dev, + uint8_t *data, uint8_t len); +static void nrf54l_radioi8_beacon_tx(struct nrf54l_radioi8_dev_s *dev); +#endif static int nrf54l_radioi8_reset(struct nrf54l_radioi8_radio_s *dev); static int nrf54l_radioi8_csmapoll(struct nrf54l_radioi8_dev_s *dev); static int nrf54l_radioi8_gtspoll(struct nrf54l_radioi8_dev_s *dev); @@ -192,7 +222,7 @@ static int nrf54l_radioi8_gtspoll(struct nrf54l_radioi8_dev_s *dev); /* Interrupts logic */ static void nrf54l_radioi8_txnext(struct nrf54l_radioi8_dev_s *dev, - uint32_t mindelay); + uint32_t mindelay); static void nrf54l_radioi8_work_noack(void *arg); static void nrf54l_radioi8_work_rx(void *arg); static void nrf54l_radioi8_work_tx(void *arg); @@ -201,6 +231,9 @@ static void nrf54l_radioi8_work_ed(void *arg); static void nrf54l_radioi8_state_rx(struct nrf54l_radioi8_dev_s *dev); static void nrf54l_radioi8_state_tx(struct nrf54l_radioi8_dev_s *dev); static void nrf54l_radioi8_state_acktx(struct nrf54l_radioi8_dev_s *dev); +#ifdef CONFIG_NRF54L_RADIO_IEEE802154_SUPERFRAME +static void nrf54l_radioi8_state_beacontx(struct nrf54l_radioi8_dev_s *dev); +#endif static void nrf54l_radioi8_state_ed(struct nrf54l_radioi8_dev_s *dev); static int nrf54l_radioi8_isr_radio(int irq, void *context, void *arg); @@ -222,6 +255,11 @@ static uint8_t aligned_data(8) static uint8_t aligned_data(8) g_nrf54l_radioi8_ackbuf[IEEE802154_ACK_FRAME_SIZE + 1]; +#ifdef CONFIG_NRF54L_RADIO_IEEE802154_SUPERFRAME +static uint8_t aligned_data(8) + g_nrf54l_radioi8_beaconbuf[IEEE802154_MAX_PHY_PACKET_SIZE + 2]; +#endif + /* Radio ops */ static struct nrf54l_radioi8_radio_ops_s g_radioi8_radio_ops = @@ -236,6 +274,10 @@ static struct nrf54l_radioi8_radio_ops_s g_radioi8_radio_ops = .norm_setup = nrf54l_radioi8_norm_setup, .norm_trigger = nrf54l_radioi8_norm_trigger, .txnext = nrf54l_radioi8_txnext, +#ifdef CONFIG_NRF54L_RADIO_IEEE802154_SUPERFRAME + .beacon_setup = nrf54l_radioi8_beacon_setup, + .beacon_tx = nrf54l_radioi8_beacon_tx, +#endif .reset = nrf54l_radioi8_reset, .csma_poll = nrf54l_radioi8_csmapoll, .gts_poll = nrf54l_radioi8_gtspoll @@ -258,7 +300,7 @@ static struct nrf54l_radioi8_radio_s g_radioi8_radio; ****************************************************************************/ static void nrf54l_radioi8_rx_parse(struct nrf54l_radioi8_dev_s *dev, - uint8_t *ftype, uint8_t *cmdtype) + uint8_t *ftype, uint8_t *cmdtype) { uint16_t *frame_ctrl = NULL; uint8_t smode = 0; @@ -536,6 +578,114 @@ static bool nrf54l_radioi8_filter(struct nrf54l_radioi8_dev_s *dev) return false; } +#ifdef CONFIG_NRF54L_RADIO_IEEE802154_SUPERFRAME +/**************************************************************************** + * Name: nrf54l_radioi8_coordbeacon + * + * Description: + * The received frame is a beacon of our coordinator (IEEE 802.15.4-2003 + * 7.5.4.1). + * + ****************************************************************************/ + +static bool nrf54l_radioi8_coordbeacon(struct nrf54l_radioi8_dev_s *dev) +{ + uint8_t *rx = &dev->radio->rxbuf[1]; + uint8_t eaddr[IEEE802154_EADDRSIZE]; + uint8_t mode; + int i; + + if ((rx[0] & IEEE802154_FRAMECTRL_FTYPE) != IEEE802154_FRAME_BEACON || + !IEEE802154_PANIDCMP(&rx[3], dev->state.addr.panid)) + { + return false; + } + + mode = ((rx[1] << 8) & IEEE802154_FRAMECTRL_SADDR) >> + IEEE802154_FRAMECTRL_SHIFT_SADDR; + + if (mode == IEEE802154_ADDRMODE_SHORT) + { + return IEEE802154_SADDRCMP(&rx[5], dev->state.coordaddr.saddr); + } + else if (mode == IEEE802154_ADDRMODE_EXTENDED) + { + /* Sent least significant octet first */ + + for (i = 0; i < IEEE802154_EADDRSIZE; i++) + { + eaddr[i] = rx[5 + IEEE802154_EADDRSIZE - 1 - i]; + } + + return IEEE802154_EADDRCMP(eaddr, dev->state.coordaddr.eaddr); + } + + return false; +} +#endif + +#if NRF54L_GTS_SLOTS > 0 +/**************************************************************************** + * Name: nrf54l_radioi8_gts_setup + * + * Description: + * Setup a GTS transaction. + * + ****************************************************************************/ + +static void nrf54l_radioi8_gts_setup(struct nrf54l_radioi8_dev_s *dev, + uint8_t fifo, struct iob_s *frame) +{ + /* Missing logic */ + + ASSERT(0); +} + +/**************************************************************************** + * Name: nrf54l_radioi8_dopoll_gts + * + * Description: + * This function is called in order to perform an out-of-sequence TX poll. + * + ****************************************************************************/ + +static void nrf54l_radioi8_dopoll_gts(void *arg) +{ + struct nrf54l_radioi8_dev_s *dev = (struct nrf54l_radioi8_dev_s *)arg; + int gts = 0; + int len = 0; + + /* Get exclusive access to the driver */ + + while (nxmutex_lock(&dev->lock) < 0) + { + } + + for (gts = 0; gts < NRF54L_GTS_SLOTS; gts++) + { + if (!dev->state.gts_busy[gts]) + { + len = dev->radiocb->poll(dev->radiocb, true, + &dev->state.gts_desc[gts]); + + if (len > 0) + { + /* Now the txdesc is in use */ + + dev->state.gts_busy[gts] = 1; + + /* Setup the GTS transaction */ + + nrf54l_radioi8_gts_setup(dev, gts, + dev->state.gts_desc[gts]->frame); + } + } + } + + nxmutex_unlock(&dev->lock); +} +#endif /* NRF54L_GTS_SLOTS > 0 */ + /**************************************************************************** * Name: nrf54l_radioi8_txstart * @@ -569,6 +719,16 @@ static void nrf54l_radioi8_ccastart(struct nrf54l_radioi8_dev_s *dev) DEBUGASSERT(dev); lower = dev->radio->lower; +#ifdef CONFIG_NRF54L_RADIO_IEEE802154_SUPERFRAME + /* Slotted CCA starts with the RXREADY-CCASTART short */ + + if (dev->radio->state.slotted) + { + NRF54L_RADIO_PUTREG(lower, NRF54L_RADIO_TASKS_RXEN_OFFSET, 1); + return; + } +#endif + NRF54L_RADIO_PUTREG(lower, NRF54L_RADIO_TASKS_CCASTART_OFFSET, 1); } @@ -607,7 +767,7 @@ static void nrf54l_radioi8_notify_noack(struct nrf54l_radioi8_dev_s *dev) ****************************************************************************/ static int nrf54l_radioi8_rxenable(struct nrf54l_radioi8_dev_s *dev, - bool enable) + bool enable) { struct nrf54l_radio_dev_s *lower = NULL; uint32_t regval = 0; @@ -677,7 +837,7 @@ static int nrf54l_radioi8_rxenable(struct nrf54l_radioi8_dev_s *dev, ****************************************************************************/ static int nrf54l_radioi8_energydetect(struct nrf54l_radioi8_dev_s *dev, - uint32_t nsymbols) + uint32_t nsymbols) { struct nrf54l_radio_dev_s *lower = NULL; uint32_t edcnt = 0; @@ -831,7 +991,7 @@ static int nrf54l_radioi8_setcca(struct nrf54l_radioi8_dev_s *dev, ****************************************************************************/ static void nrf54l_radioi8_norm_setup_buf(struct nrf54l_radioi8_dev_s *dev, - uint8_t *buf, bool csma) + uint8_t *buf, bool csma) { struct nrf54l_radio_dev_s *lower = NULL; uint32_t regval = 0; @@ -872,7 +1032,12 @@ static void nrf54l_radioi8_norm_setup_buf(struct nrf54l_radioi8_dev_s *dev, if (dev->radio->state.slotted) { +#ifdef CONFIG_NRF54L_RADIO_IEEE802154_SUPERFRAME + NRF54L_RADIO_SHRTSET(lower, IEEE802154_TXCCASLT_SHORTS); + NRF54L_RADIO_INTEN(lower, IEEE802154_TXCCASLT_INT); +#else ASSERT(0); +#endif } else { @@ -907,7 +1072,7 @@ static void nrf54l_radioi8_norm_setup_buf(struct nrf54l_radioi8_dev_s *dev, ****************************************************************************/ static void nrf54l_radioi8_norm_setup(struct nrf54l_radioi8_dev_s *dev, - struct iob_s *frame, bool csma) + struct iob_s *frame, bool csma) { /* The first byte in TX buffer is PHR */ @@ -926,6 +1091,177 @@ static void nrf54l_radioi8_norm_setup(struct nrf54l_radioi8_dev_s *dev, nrf54l_radioi8_norm_setup_buf(dev, dev->radio->txbuf, csma); } +#ifdef CONFIG_NRF54L_RADIO_IEEE802154_SUPERFRAME +/**************************************************************************** + * Name: nrf54l_radioi8_slotted_at + * + * Description: + * Run the next slotted CSMA-CA step on its backoff period boundary, the + * radio ramps up before it. + * + ****************************************************************************/ + +static void nrf54l_radioi8_slotted_at(struct nrf54l_radioi8_dev_s *dev, + uint8_t chan) +{ + uint32_t elapsed = 0; + uint32_t at; + + at = dev->radio->state.boundary * NRF54L_BACKOFF_US - + NRF54L_RADIOI8_RAMPUP_US; + + dev->rtc->ops->elapsed(dev, &elapsed); + + dev->tim->ops->setup(dev, chan, at > elapsed ? + (at - elapsed) / IEEE802154_SYMBOL_US : 0); +} + +/**************************************************************************** + * Name: nrf54l_radioi8_slotted_pause + * + * Description: + * Wait for the next CAP, receive in the meantime. + * + ****************************************************************************/ + +static void nrf54l_radioi8_slotted_pause(struct nrf54l_radioi8_dev_s *dev) +{ + struct nrf54l_radio_dev_s *lower = dev->radio->lower; + + nrf54l_radioi8_trace_put(RADIO_TRACE_CSMAPAUSE, + dev->radio->state.backoffs); + + dev->radio->state.csma_paused = true; + dev->radio->state.waitack = false; + dev->radio->state.state = NRF54L_RADIO_STATE_DISABLED; + + NRF54L_RADIO_INTCLR(lower, 0xffffffff); + NRF54L_RADIO_SHRTSET(lower, 0); + + if (dev->radio->state.rxrestore) + { + dev->radio->state.rxrestore = false; + + while (NRF54L_RADIO_GETREG(lower, NRF54L_RADIO_STATE_OFFSET) != + RADIO_STATE_DISABLED); + + dev->macops.rxenable((struct ieee802154_radio_s *)dev, true); + } +} + +/**************************************************************************** + * Name: nrf54l_radioi8_slotted_backoff + * + * Description: + * Count down the backoff periods in the CAP and schedule the first CCA + * (IEEE 802.15.4-2003 7.5.1.3). + * + ****************************************************************************/ + +static void nrf54l_radioi8_slotted_backoff(struct nrf54l_radioi8_dev_s *dev, + uint32_t guard) +{ + struct nrf54l_radioi8_radio_data_s *state = &dev->radio->state; + struct nrf54l_radioi8_rtc_s *rtc = dev->rtc; + uint32_t elapsed = 0; + uint32_t end; + uint32_t b; + uint32_t need; + + /* Not synchronized or the CAP is over */ + + if (!rtc->ops->elapsed(dev, &elapsed) || elapsed >= rtc->cap) + { + nrf54l_radioi8_slotted_pause(dev); + return; + } + + /* The first boundary after the beacon IFS that can still be set up */ + + b = (MAX(elapsed + guard, rtc->capstart) + NRF54L_BACKOFF_US - 1) / + NRF54L_BACKOFF_US; + + /* The last boundary of the countdown and of the TX start: the CAP end, + * with battery life extension the sixth full backoff period after the + * beacon IFS + */ + + end = rtc->cap / NRF54L_BACKOFF_US; + if (state->ble) + { + end = MIN(end, (rtc->capstart + NRF54L_BACKOFF_US - 1) / + NRF54L_BACKOFF_US + 5); + } + + /* Pause the countdown at the end of the CAP */ + + if (b + state->backoffs > end) + { + if (end > b) + { + state->backoffs -= end - b; + } + + nrf54l_radioi8_slotted_pause(dev); + return; + } + + b += state->backoffs; + state->backoffs = 0; + + /* The CCAs, the frame, its ACK and the IFS must end before the end of + * the CAP (7.5.1.1.1), otherwise evaluate again in the next CAP + */ + + need = state->CW * NRF54L_BACKOFF_US + + NRF54L_RADIOI8_FRAME_US(dev->radio->txbuf[0]) + + NRF54L_RADIOI8_IFS_US(dev->radio->txbuf[0]); + if (dev->radio->txbuf[1] & IEEE802154_FRAMECTRL_ACKREQ) + { + need += IEEE802154_ACK_WAIT * IEEE802154_SYMBOL_US; + } + + if (b * NRF54L_BACKOFF_US + need > rtc->cap || b + state->CW > end) + { + nrf54l_radioi8_slotted_pause(dev); + return; + } + + /* CCA on the boundary */ + + state->boundary = b; + nrf54l_radioi8_slotted_at(dev, NRF54L_TIMER_CHAN_CSMADELAY); +} + +/**************************************************************************** + * Name: nrf54l_radioi8_slotted_resume + * + * Description: + * Continue the slotted CSMA-CA paused for this CAP. Must be called with + * the radio locked. + * + ****************************************************************************/ + +static void nrf54l_radioi8_slotted_resume(struct nrf54l_radioi8_dev_s *dev) +{ + uint32_t elapsed = 0; + + /* Not in a CAP, stay paused */ + + if (!dev->rtc->ops->elapsed(dev, &elapsed) || elapsed >= dev->rtc->cap) + { + return; + } + + dev->radio->state.csma_paused = false; + + nrf54l_radioi8_norm_setup_buf(dev, dev->radio->txbuf, true); + dev->radio->state.state = NRF54L_RADIO_STATE_TX; + + nrf54l_radioi8_slotted_backoff(dev, NRF54L_SLOTTED_GUARD_US); +} +#endif + /**************************************************************************** * Name: nrf54l_radioi8_trg_csma * @@ -946,7 +1282,35 @@ static void nrf54l_radioi8_trg_csma(struct nrf54l_radioi8_dev_s *dev) if (dev->radio->state.slotted) { +#ifdef CONFIG_NRF54L_RADIO_IEEE802154_SUPERFRAME + if (!dev->radio->state.csma_busy) + { + /* Initial backoff state, battery life extension limits BE + * (IEEE 802.15.4-2003 7.5.1.3) + */ + + dev->radio->state.NB = 0; + dev->radio->state.BE = dev->radio->state.ble ? + MIN(2, dev->radio->state.min_be) : + dev->radio->state.min_be; + } + + /* CW is reset for each attempt */ + + dev->radio->state.CW = IEEE802154_CW0; + dev->radio->state.backoffs = rand() % (1 << dev->radio->state.BE); + + /* CSMA is in use */ + + dev->radio->state.csma_busy = true; + nrf54l_radioi8_trace_put(RADIO_TRACE_CSMATRIGGER, + dev->radio->state.backoffs); + + nrf54l_radioi8_slotted_backoff(dev, NRF54L_SLOTTED_GUARD_US); + return; +#else ASSERT(0); +#endif } /* Need un-slotted CSMA-CA */ @@ -997,7 +1361,7 @@ static void nrf54l_radioi8_trg_nocsma(struct nrf54l_radioi8_dev_s *dev) /* Start TX - we transmit immediately */ nrf54l_radioi8_trace_put(RADIO_TRACE_NOCSMATRIGGER, - dev->radio->state.state); + dev->radio->state.state); NRF54L_RADIO_PUTREG(lower, NRF54L_RADIO_TASKS_TXEN_OFFSET, 1); } @@ -1011,6 +1375,19 @@ static void nrf54l_radioi8_trg_nocsma(struct nrf54l_radioi8_dev_s *dev) static void nrf54l_radioi8_norm_trigger(struct nrf54l_radioi8_dev_s *dev) { +#ifdef CONFIG_NRF54L_RADIO_IEEE802154_SUPERFRAME + /* The beacon is not acknowledged and keeps its state */ + + if (dev->radio->state.state == NRF54L_RADIO_STATE_BEACONTX) + { + nrf54l_radioi8_trace_put(RADIO_TRACE_BEACONTX, + dev->radio->beaconbuf[3]); + NRF54L_RADIO_PUTREG(dev->radio->lower, NRF54L_RADIO_TASKS_TXEN_OFFSET, + 1); + return; + } +#endif + /* Wait for ACK */ if (dev->radio->txbuf[1] & IEEE802154_FRAMECTRL_ACKREQ) @@ -1042,6 +1419,46 @@ static void nrf54l_radioi8_norm_trigger(struct nrf54l_radioi8_dev_s *dev) } } +#ifdef CONFIG_NRF54L_RADIO_IEEE802154_SUPERFRAME +/**************************************************************************** + * Name: nrf54l_radioi8_beacon_setup + * + * Description: + * Setup a beacon frame transfer + * + ****************************************************************************/ + +static void nrf54l_radioi8_beacon_setup(struct nrf54l_radioi8_dev_s *dev, + uint8_t *data, uint8_t len) +{ + /* Set beacon TX buffer */ + + memcpy(&dev->radio->beaconbuf[1], data, len); + + /* Length = Frame data + CRC */ + + dev->radio->beaconbuf[0] = len + 2; +} + +/**************************************************************************** + * Name: nrf54l_radioi8_beacon_tx + * + * Description: + * Transmit a beacon frame (non CSMA-CA transfer). + * + ****************************************************************************/ + +static void nrf54l_radioi8_beacon_tx(struct nrf54l_radioi8_dev_s *dev) +{ + /* The beacon has priority over all other operations (IEEE 802.15.4-2003 + * 7.5.2.4), only a frame on the air completes first + */ + + dev->radio->state.beacon_pending = true; + nrf54l_radioi8_txnext(dev, 0); +} +#endif + /**************************************************************************** * Name: nrf54l_radioi8_reset * @@ -1153,28 +1570,63 @@ static void nrf54l_radioi8_rxrestore(struct nrf54l_radioi8_dev_s *dev) * Name: nrf54l_radioi8_txnext * * Description: - * Start the next frame if the radio is free: the delayed TX that waits, - * else the CSMA TX that waits, else restore RX. mindelay is the IFS a - * delayed TX keeps after the ACK TX that deferred it. + * Start the next frame if the radio is free: the beacon that waits, else + * the delayed TX that waits, else the CSMA TX that waits, else restore + * RX. mindelay is the IFS a frame keeps after the TX that deferred it. * ****************************************************************************/ static void nrf54l_radioi8_txnext(struct nrf54l_radioi8_dev_s *dev, - uint32_t mindelay) + uint32_t mindelay) { irqstate_t flags; uint32_t delay; flags = enter_critical_section(); - /* A frame on the air, an armed frame, an ACK TX, an energy detection - * or a CSMA transaction between its backoffs owns the radio. + /* A frame on the air, an ACK TX, an energy detection or a timed step + * owns the radio + */ + + if ((dev->radio->state.state != NRF54L_RADIO_STATE_DISABLED && + dev->radio->state.state != NRF54L_RADIO_STATE_RX) || + dev->tim->tim_pending) + { + leave_critical_section(flags); + return; + } + +#ifdef CONFIG_NRF54L_RADIO_IEEE802154_SUPERFRAME + /* The beacon first, it preempts the receiver */ + + if (dev->radio->state.beacon_pending) + { + dev->radio->state.beacon_pending = false; + dev->radio->beaconbuf[3] = dev->radio->state.bsn++; + + nrf54l_radioi8_norm_setup_buf(dev, dev->radio->beaconbuf, false); + dev->radio->state.state = NRF54L_RADIO_STATE_BEACONTX; + + if (mindelay == 0) + { + nrf54l_radioi8_norm_trigger(dev); + } + else + { + dev->tim->ops->setup(dev, NRF54L_TIMER_CHAN_TXDELAY, mindelay); + } + + leave_critical_section(flags); + return; + } +#endif + + /* An armed frame or a CSMA transaction between its backoffs owns the + * radio, a slotted one paused for the next CAP does not. */ - if (dev->radio->state.csma_busy || - (dev->state.txdelayed_busy && !dev->state.txdelayed_pending) || - (dev->radio->state.state != NRF54L_RADIO_STATE_DISABLED && - dev->radio->state.state != NRF54L_RADIO_STATE_RX)) + if ((dev->radio->state.csma_busy && !dev->radio->state.csma_paused) || + (dev->state.txdelayed_busy && !dev->state.txdelayed_pending)) { leave_critical_section(flags); return; @@ -1186,7 +1638,7 @@ static void nrf54l_radioi8_txnext(struct nrf54l_radioi8_dev_s *dev, { dev->state.txdelayed_pending = false; nrf54l_radioi8_norm_setup(dev, dev->state.txdelayed_desc->frame, - false); + false); delay = MAX(dev->state.txdelayed_delay, mindelay); if (delay == 0) @@ -1208,6 +1660,15 @@ static void nrf54l_radioi8_txnext(struct nrf54l_radioi8_dev_s *dev, nrf54l_radioi8_norm_trigger(dev); } +#ifdef CONFIG_NRF54L_RADIO_IEEE802154_SUPERFRAME + /* Then the slotted CSMA TX paused for this CAP */ + + else if (dev->radio->state.csma_paused) + { + nrf54l_radioi8_slotted_resume(dev); + } +#endif + /* Nothing to send, restore RX */ else if (dev->radio->state.rxrestore) @@ -1295,9 +1756,26 @@ static int nrf54l_radioi8_csmapoll(struct nrf54l_radioi8_dev_s *dev) static int nrf54l_radioi8_gtspoll(struct nrf54l_radioi8_dev_s *dev) { +#if NRF54L_GTS_SLOTS > 0 + /* Is our single work structure available? It may not be if there are + * pending interrupt actions and we will have to ignore the Tx + * availability action. + */ + + if (work_available(&dev->radio->gts_pollwork)) + { + /* Schedule to serialize the poll on the worker thread. */ + + work_queue(HPWORK, &dev->radio->gts_pollwork, + nrf54l_radioi8_dopoll_gts, dev, 0); + } + + return OK; +#else /* GTS not supported */ return -ENOTSUP; +#endif } /**************************************************************************** @@ -1664,6 +2142,20 @@ static void nrf54l_radioi8_state_rx(struct nrf54l_radioi8_dev_s *dev) } } +#ifdef CONFIG_NRF54L_RADIO_IEEE802154_SUPERFRAME + /* The superframe started with the SHR of the coordinator beacon */ + + if (dev->radio->state.sftrack && dev->radio->state.waitack == false && + nrf54l_radioi8_coordbeacon(dev)) + { + dev->rtc->ops->sync(dev, rxbuf[0]); + + /* The CAP starts, continue the TX that waited for it */ + + nrf54l_radioi8_txnext(dev, 0); + } +#endif + /* Start ACK handling now - this must be done in isr. Frames are not * filtered while we wait for our ACK, so they are not acknowledged. */ @@ -1702,7 +2194,7 @@ static void nrf54l_radioi8_state_rx(struct nrf54l_radioi8_dev_s *dev) else if (rxbuf[0] != IEEE802154_ACK_FRAME_SIZE || (rxbuf[1] & IEEE802154_FRAMECTRL_FTYPE) != IEEE802154_FRAME_ACK || !NRF54L_RADIO_GETREG(dev->radio->lower, - NRF54L_RADIO_CRCSTATUS_OFFSET)) + NRF54L_RADIO_CRCSTATUS_OFFSET)) { dev->macops.rxenable((struct ieee802154_radio_s *)dev, true); } @@ -1778,48 +2270,36 @@ static void nrf54l_radioi8_state_tx(struct nrf54l_radioi8_dev_s *dev) NRF54L_RADIO_PUTREG(lower, NRF54L_RADIO_EVENTS_CCABUSY_OFFSET, 0); - /* Slotted CSMA-CA */ - - if (dev->radio->state.slotted == true) - { - ASSERT(0); - } + /* Update backoff */ - /* Un-slotted CSMA-CA */ + dev->radio->state.NB += 1; + dev->radio->state.BE = MIN(dev->radio->state.BE + 1, + dev->radio->state.max_be); - else + if (dev->radio->state.NB > dev->radio->state.max_csma_backoffs) { - /* Update backoff */ + /* Return failure to MAC, no frame was sent */ - dev->radio->state.NB += 1; - dev->radio->state.BE = MIN(dev->radio->state.BE + 1, - dev->radio->state.max_be); + dev->radio->state.waitack = false; - if (dev->radio->state.NB > dev->radio->state.max_csma_backoffs) + if (work_available(&dev->radio->irqwork)) { - /* Return failure to MAC, no frame was sent */ - - dev->radio->state.waitack = false; - - if (work_available(&dev->radio->irqwork)) - { - work_queue(HPWORK, &dev->radio->irqwork, - nrf54l_radioi8_work_busy, (void *)dev, 0); - } - - goto out; + work_queue(HPWORK, &dev->radio->irqwork, + nrf54l_radioi8_work_busy, (void *)dev, 0); } - else - { - /* Try again, shorts and interrupts were reset in ISR */ - nrf54l_radioi8_norm_setup_buf(dev, dev->radio->txbuf, true); - nrf54l_radioi8_norm_trigger(dev); + goto out; + } + else + { + /* Try again, shorts and interrupts were reset in ISR */ - /* Do not restore RX */ + nrf54l_radioi8_norm_setup_buf(dev, dev->radio->txbuf, true); + nrf54l_radioi8_norm_trigger(dev); - return; - } + /* Do not restore RX */ + + return; } } @@ -1835,7 +2315,34 @@ static void nrf54l_radioi8_state_tx(struct nrf54l_radioi8_dev_s *dev) if (dev->radio->state.slotted == true) { +#ifdef CONFIG_NRF54L_RADIO_IEEE802154_SUPERFRAME + /* The next step starts on the next boundary */ + + dev->radio->state.CW -= 1; + dev->radio->state.boundary += 1; + + if (dev->radio->state.CW == 0) + { + /* Transmit, the ACK wait starts with the TX */ + + nrf54l_radioi8_norm_setup_buf(dev, dev->radio->txbuf, false); + nrf54l_radioi8_slotted_at(dev, NRF54L_TIMER_CHAN_TXDELAY); + } + else + { + /* Next CCA */ + + nrf54l_radioi8_norm_setup_buf(dev, dev->radio->txbuf, true); + dev->radio->state.state = NRF54L_RADIO_STATE_TX; + nrf54l_radioi8_slotted_at(dev, NRF54L_TIMER_CHAN_CSMADELAY); + } + + /* Do not restore RX */ + + return; +#else ASSERT(0); +#endif } /* Un-slotted CSMA-CA */ @@ -1900,6 +2407,37 @@ static void nrf54l_radioi8_state_acktx(struct nrf54l_radioi8_dev_s *dev) IEEE802154_SIFS_SYMBOLS); } +#ifdef CONFIG_NRF54L_RADIO_IEEE802154_SUPERFRAME +/**************************************************************************** + * Name: nrf54l_radioi8_state_beacontx + * + * Description: + * Handle radio interrupt for BEACONTX state. + * + ****************************************************************************/ + +static void nrf54l_radioi8_state_beacontx(struct nrf54l_radioi8_dev_s *dev) +{ + nrf54l_radioi8_trace_put(RADIO_TRACE_IRQ_BEACONTX, 0); + + /* Arm the beacon updated during the TX */ + + if (dev->state.beacon_next != NULL) + { + nrf54l_radioi8_beacon_setup(dev, dev->state.beacon_next->bf_data, + dev->state.beacon_next->bf_len); + dev->state.beacon_next = NULL; + } + + /* Start the TX that waited for the beacon, after the IFS */ + + nrf54l_radioi8_txnext(dev, dev->radio->beaconbuf[0] > + IEEE802154_MAX_SIFS_FRAME_SIZE ? + IEEE802154_LIFS_SYMBOLS : + IEEE802154_SIFS_SYMBOLS); +} +#endif + /**************************************************************************** * Name: nrf54l_radioi8_state_ed * @@ -1994,6 +2532,15 @@ static int nrf54l_radioi8_isr_radio(int irq, void *context, void *arg) break; } +#ifdef CONFIG_NRF54L_RADIO_IEEE802154_SUPERFRAME + case NRF54L_RADIO_STATE_BEACONTX: + { + nrf54l_radioi8_state_beacontx(dev); + + break; + } +#endif + case NRF54L_RADIO_STATE_ED: { /* Clear event */ @@ -2012,6 +2559,15 @@ static int nrf54l_radioi8_isr_radio(int irq, void *context, void *arg) } } +#ifdef CONFIG_NRF54L_RADIO_IEEE802154_SUPERFRAME + /* The beacon that waited for the frame on the air */ + + if (dev->radio->state.beacon_pending) + { + nrf54l_radioi8_txnext(dev, 0); + } +#endif + leave_critical_section(flags); return OK; @@ -2031,7 +2587,7 @@ static int nrf54l_radioi8_isr_radio(int irq, void *context, void *arg) struct nrf54l_radioi8_radio_s * nrf54l_radioi8_radio_init(struct nrf54l_radioi8_dev_s *dev, - struct nrf54l_radio_board_s *board) + struct nrf54l_radio_board_s *board) { struct nrf54l_radio_dev_s *radio = NULL; @@ -2060,6 +2616,9 @@ nrf54l_radioi8_radio_init(struct nrf54l_radioi8_dev_s *dev, g_radioi8_radio.rxbuf = g_nrf54l_radioi8_rxbuf; g_radioi8_radio.txbuf = g_nrf54l_radioi8_txbuf; g_radioi8_radio.ackbuf = g_nrf54l_radioi8_ackbuf; +#ifdef CONFIG_NRF54L_RADIO_IEEE802154_SUPERFRAME + g_radioi8_radio.beaconbuf = g_nrf54l_radioi8_beaconbuf; +#endif return &g_radioi8_radio; } diff --git a/arch/arm/src/nrf54l/nrf54l_ieee802154_radio.h b/arch/arm/src/nrf54l/nrf54l_ieee802154_radio.h index 9dddfcabf76..c248fc0061d 100644 --- a/arch/arm/src/nrf54l/nrf54l_ieee802154_radio.h +++ b/arch/arm/src/nrf54l/nrf54l_ieee802154_radio.h @@ -56,6 +56,21 @@ IEEE802154_TURN_AROUND_TIME + \ 10 + 12) +/* RADIO ramp-up time in fast ramp-up mode */ + +#define NRF54L_RADIOI8_RAMPUP_US (40) + +/* Air time of a frame with the given PHR: SHR, PHR and PSDU, 32 us/octet */ + +#define NRF54L_RADIOI8_FRAME_US(phr) (((phr) + 6) * 2 * IEEE802154_SYMBOL_US) + +/* IFS after a frame with the given PHR */ + +#define NRF54L_RADIOI8_IFS_US(phr) \ + (((phr) > IEEE802154_MAX_SIFS_FRAME_SIZE ? \ + IEEE802154_LIFS_SYMBOLS : IEEE802154_SIFS_SYMBOLS) * \ + IEEE802154_SYMBOL_US) + /**************************************************************************** * Public Types ****************************************************************************/ @@ -71,6 +86,7 @@ enum nrf54l_radioi8_state_e NRF54L_RADIO_STATE_RX, /* RX active */ NRF54L_RADIO_STATE_ACKTX, /* Transmitting ACK now */ NRF54L_RADIO_STATE_ED, /* Energy detection now */ + NRF54L_RADIO_STATE_BEACONTX, /* Transmitting beacon now */ }; /* Radio data */ @@ -98,7 +114,8 @@ struct nrf54l_radioi8_radio_data_s bool waitack; /* TX needs ACK */ bool framepending; /* Frame pending from the last TX ACK */ - bool csma_busy; /* Un-slotted CSMA busy */ + bool csma_busy; /* CSMA-CA transaction in progress */ + bool csma_paused; /* Slotted CSMA-CA waits for the next CAP */ bool slotted; /* Slotted CSMA-CA */ /* Radio configuration */ @@ -107,6 +124,17 @@ struct nrf54l_radioi8_radio_data_s uint8_t max_csma_backoffs; /* Max CSMA backoffs */ uint8_t min_be; /* Min backoff exponent (BE) */ uint8_t max_be; /* Max backoff exponent (BE) */ + +#ifdef CONFIG_NRF54L_RADIO_IEEE802154_SUPERFRAME + /* Superframe data */ + + bool sftrack; /* Device tracks the beacons */ + bool ble; /* Battery life extension */ + bool beacon_pending; /* A beacon waits for the radio */ + uint8_t bsn; /* macBSN, the beacon sequence number */ + uint8_t backoffs; /* Backoff periods before the next CCA */ + uint32_t boundary; /* Backoff period boundary of the next step */ +#endif }; /* Forward reference */ @@ -162,6 +190,17 @@ struct nrf54l_radioi8_radio_ops_s void (*txnext)(struct nrf54l_radioi8_dev_s *dev, uint32_t mindelay); +#ifdef CONFIG_NRF54L_RADIO_IEEE802154_SUPERFRAME + /* Setup a beacon frame transfe */ + + void (*beacon_setup)(struct nrf54l_radioi8_dev_s *dev, + uint8_t *data, uint8_t len); + + /* Transmit a beacon frame (non CSMA-CA transfer) */ + + void (*beacon_tx)(struct nrf54l_radioi8_dev_s *dev); +#endif + /* Reset radio state to work with IEEE 802.15.4 */ int (*reset)(struct nrf54l_radioi8_radio_s *dev); @@ -192,6 +231,9 @@ struct nrf54l_radioi8_radio_s uint8_t *rxbuf; uint8_t *txbuf; uint8_t *ackbuf; +#ifdef CONFIG_NRF54L_RADIO_IEEE802154_SUPERFRAME + uint8_t *beaconbuf; +#endif /* For deferring interrupts work */ @@ -205,6 +247,12 @@ struct nrf54l_radioi8_radio_s struct work_s csma_pollwork; +#ifdef CONFIG_NRF54L_RADIO_IEEE802154_SUPERFRAME + /* For deferring poll work to the work queue */ + + struct work_s gts_pollwork; +#endif + /* Radio data */ struct nrf54l_radioi8_radio_data_s state; @@ -224,6 +272,6 @@ struct nrf54l_radioi8_radio_s struct nrf54l_radioi8_radio_s * nrf54l_radioi8_radio_init(struct nrf54l_radioi8_dev_s *dev, - struct nrf54l_radio_board_s *board); + struct nrf54l_radio_board_s *board); #endif /* __ARCH_ARM_SRC_NRF54L_NRF54L_IEEE802154_RADIO_H */ diff --git a/arch/arm/src/nrf54l/nrf54l_ieee802154_rtc.c b/arch/arm/src/nrf54l/nrf54l_ieee802154_rtc.c new file mode 100644 index 00000000000..3d92cc9671f --- /dev/null +++ b/arch/arm/src/nrf54l/nrf54l_ieee802154_rtc.c @@ -0,0 +1,564 @@ +/**************************************************************************** + * arch/arm/src/nrf54l/nrf54l_ieee802154_rtc.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <nuttx/debug.h> +#include <errno.h> + +#include <nuttx/arch.h> +#include <nuttx/irq.h> +#include <nuttx/wqueue.h> + +#include <nuttx/wireless/ieee802154/ieee802154_mac.h> + +#include "nrf54l_ieee802154_rtc.h" +#include "nrf54l_ieee802154_trace.h" + +#include "nrf54l_ieee802154_priv.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifndef CONFIG_NRF54L_GRTC +# error CONFIG_NRF54L_GRTC is needed to handle radio timings +#endif + +/* GRTC interrupt group of the radio, the system timer uses group 0 */ + +#define NRF54L_IEEE802154_GRTC (1) + +/* The SYSCOUNTER counts us, a compare value closer than this to the + * counter may not match + */ + +#define NRF54L_GRTC_CC_MARGIN (10) + +/* Superframe events compare channel, the system timer uses CC0 */ + +#define NRF54L_GRTC_CC (NRF54L_GRTC_CC1) +#define NRF54L_GRTC_EVT (NRF54L_GRTC_EVT_COMPARE1) + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* RTC ops */ + +static int nrf54l_radioi8_rtc(struct nrf54l_radioi8_dev_s *dev, + struct ieee802154_superframespec_s *sfspec); +static int nrf54l_radioi8_rtc_start(struct nrf54l_radioi8_dev_s *dev); +static int nrf54l_radioi8_rtc_sync(struct nrf54l_radioi8_dev_s *dev, + uint8_t phr); +static int nrf54l_radioi8_rtc_stop(struct nrf54l_radioi8_dev_s *dev); +static void nrf54l_radioi8_rtc_reset(struct nrf54l_radioi8_dev_s *dev); +static bool nrf54l_radioi8_rtc_elapsed(struct nrf54l_radioi8_dev_s *dev, + uint32_t *us); + +/* Interrupts logic */ + +static void nrf54l_radioi8_work_inactive(void *arg); +static int nrf54l_radioi8_isr_rtc(int irq, void *context, void *arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* RTC ops */ + +static struct nrf54l_radioi8_rtc_ops_s g_radioi8_rtc_ops = +{ + .setup = nrf54l_radioi8_rtc, + .start = nrf54l_radioi8_rtc_start, + .sync = nrf54l_radioi8_rtc_sync, + .stop = nrf54l_radioi8_rtc_stop, + .reset = nrf54l_radioi8_rtc_reset, + .elapsed = nrf54l_radioi8_rtc_elapsed +}; + +/* RTC instance */ + +static struct nrf54l_radioi8_rtc_s g_radioi8_rtc; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nrf54l_radioi8_rtc_now + * + * Description: + * Get the current time in us. + * + ****************************************************************************/ + +static uint64_t nrf54l_radioi8_rtc_now(struct nrf54l_radioi8_rtc_s *rtc) +{ + uint64_t now = 0; + + NRF54L_GRTC_GETCOUNTER(rtc->rtc, &now); + + return now; +} + +/**************************************************************************** + * Name: nrf54l_radioi8_rtc_alarm + * + * Description: + * Schedule a superframe event. Must be called with the radio locked. + * + ****************************************************************************/ + +static void nrf54l_radioi8_rtc_alarm(struct nrf54l_radioi8_rtc_s *rtc, + uint8_t evt, uint64_t time) +{ + uint64_t target = time; + uint64_t now; + + rtc->evt = evt; + rtc->evt_time = time; + + NRF54L_GRTC_ACKINT(rtc->rtc, NRF54L_GRTC_EVT); + + /* Move a due compare value ahead of the counter, repeat until it is + * ahead or the event fired + */ + + for (; ; ) + { + now = nrf54l_radioi8_rtc_now(rtc); + if (target <= now + NRF54L_GRTC_CC_MARGIN) + { + target = now + NRF54L_GRTC_CC_MARGIN; + } + + NRF54L_GRTC_SETCC(rtc->rtc, NRF54L_GRTC_CC, target); + + if (target > nrf54l_radioi8_rtc_now(rtc) || + NRF54L_GRTC_CHECKINT(rtc->rtc, NRF54L_GRTC_EVT)) + { + break; + } + } + + NRF54L_GRTC_ENABLEINT(rtc->rtc, NRF54L_GRTC_EVT); +} + +/**************************************************************************** + * Name: nrf54l_radioi8_rtc_cancel + * + * Description: + * Cancel the scheduled superframe event. Must be called with the radio + * locked. + * + ****************************************************************************/ + +static void nrf54l_radioi8_rtc_cancel(struct nrf54l_radioi8_rtc_s *rtc) +{ + NRF54L_GRTC_DISABLEINT(rtc->rtc, NRF54L_GRTC_EVT); + NRF54L_GRTC_DISABLECC(rtc->rtc, NRF54L_GRTC_CC); + NRF54L_GRTC_ACKINT(rtc->rtc, NRF54L_GRTC_EVT); + + rtc->evt = NRF54L_SFEVT_NONE; +} + +/**************************************************************************** + * Name: nrf54l_radioi8_rtc_next + * + * Description: + * Schedule the next superframe event after the superframe start. Must + * be called with the radio locked. + * + ****************************************************************************/ + +static void nrf54l_radioi8_rtc_next(struct nrf54l_radioi8_dev_s *dev) +{ + struct nrf54l_radioi8_rtc_s *rtc = dev->rtc; + + if (rtc->sd < rtc->bi) + { + nrf54l_radioi8_rtc_alarm(rtc, NRF54L_SFEVT_SDEND, + rtc->sf_start + rtc->sd); + } + else + { + /* No inactive portion, the MAC updates the beacon from now */ + + if (work_available(&dev->rtc->inactive_work)) + { + work_queue(HPWORK, &dev->rtc->inactive_work, + nrf54l_radioi8_work_inactive, dev, 0); + } + + nrf54l_radioi8_rtc_alarm(rtc, NRF54L_SFEVT_START, + rtc->sf_start + rtc->bi - + NRF54L_RADIOI8_RAMPUP_US); + } +} + +/**************************************************************************** + * Name: nrf54l_radioi8_rtc + * + * Description: + * Configure the superframe timings according to superframe spec. + * + ****************************************************************************/ + +static int nrf54l_radioi8_rtc(struct nrf54l_radioi8_dev_s *dev, + struct ieee802154_superframespec_s *sfspec) +{ + struct nrf54l_radioi8_rtc_s *rtc = dev->rtc; + uint32_t slot; + irqstate_t flags; + + if (sfspec->beaconorder > 14 || + (sfspec->sforder > sfspec->beaconorder && sfspec->sforder != 15)) + { + return -EINVAL; + } + + flags = enter_critical_section(); + + /* BI = aBaseSuperframeDuration * 2^BO, SD = aBaseSuperframeDuration * + * 2^SO, the CAP ends with the final CAP slot (IEEE 802.15.4-2003 7.5.1.1) + */ + + rtc->bi = (IEEE802154_BASE_SUPERFRAME_DURATION * IEEE802154_SYMBOL_US) << + sfspec->beaconorder; + + if (sfspec->sforder == 15) + { + /* The superframe is not active after the beacon */ + + rtc->sd = 0; + rtc->cap = 0; + } + else + { + slot = (IEEE802154_BASE_SLOT_DURATION * IEEE802154_SYMBOL_US) << + sfspec->sforder; + rtc->sd = slot * IEEE802154_NUM_SUPERFRAME_SLOTS; + rtc->cap = slot * (sfspec->final_capslot + 1); + } + + leave_critical_section(flags); + + return OK; +} + +/**************************************************************************** + * Name: nrf54l_radioi8_rtc_start + * + * Description: + * Start transmitting beacons. + * + ****************************************************************************/ + +static int nrf54l_radioi8_rtc_start(struct nrf54l_radioi8_dev_s *dev) +{ + struct nrf54l_radioi8_rtc_s *rtc = dev->rtc; + irqstate_t flags; + + flags = enter_critical_section(); + + rtc->sf_start = nrf54l_radioi8_rtc_now(rtc) + NRF54L_GRTC_CC_MARGIN + + NRF54L_RADIOI8_RAMPUP_US; + rtc->running = false; + + nrf54l_radioi8_rtc_alarm(rtc, NRF54L_SFEVT_START, + rtc->sf_start - NRF54L_RADIOI8_RAMPUP_US); + + leave_critical_section(flags); + + return OK; +} + +/**************************************************************************** + * Name: nrf54l_radioi8_rtc_sync + * + * Description: + * Synchronize with the beacon just received, the superframe started + * with its SHR. + * + ****************************************************************************/ + +static int nrf54l_radioi8_rtc_sync(struct nrf54l_radioi8_dev_s *dev, + uint8_t phr) +{ + struct nrf54l_radioi8_rtc_s *rtc = dev->rtc; + irqstate_t flags; + + flags = enter_critical_section(); + + nrf54l_radioi8_trace_put(RADIO_TRACE_SFSYNC, phr); + + rtc->sf_start = nrf54l_radioi8_rtc_now(rtc) - + NRF54L_RADIOI8_FRAME_US(phr); + rtc->capstart = NRF54L_RADIOI8_FRAME_US(phr) + + NRF54L_RADIOI8_IFS_US(phr); + rtc->running = true; + + leave_critical_section(flags); + + return OK; +} + +/**************************************************************************** + * Name: nrf54l_radioi8_rtc_stop + * + * Description: + * Stop the superframe. + * + ****************************************************************************/ + +static int nrf54l_radioi8_rtc_stop(struct nrf54l_radioi8_dev_s *dev) +{ + irqstate_t flags; + + flags = enter_critical_section(); + + nrf54l_radioi8_rtc_cancel(dev->rtc); + dev->rtc->running = false; + + leave_critical_section(flags); + + return OK; +} + +/**************************************************************************** + * Name: nrf54l_radioi8_rtc_reset + * + * Description: + * Reset the superframe, the SYSCOUNTER is shared and keeps running. + * + ****************************************************************************/ + +static void nrf54l_radioi8_rtc_reset(struct nrf54l_radioi8_dev_s *dev) +{ + struct nrf54l_radioi8_rtc_s *rtc = dev->rtc; + irqstate_t flags; + + flags = enter_critical_section(); + + nrf54l_radioi8_rtc_cancel(rtc); + + /* Reset data */ + + rtc->sf_start = 0; + rtc->running = false; + + leave_critical_section(flags); +} + +/**************************************************************************** + * Name: nrf54l_radioi8_rtc_elapsed + * + * Description: + * Time in us since the superframe start. + * + ****************************************************************************/ + +static bool nrf54l_radioi8_rtc_elapsed(struct nrf54l_radioi8_dev_s *dev, + uint32_t *us) +{ + struct nrf54l_radioi8_rtc_s *rtc = dev->rtc; + uint64_t now; + irqstate_t flags; + bool ret = false; + + flags = enter_critical_section(); + + if (rtc->running) + { + now = nrf54l_radioi8_rtc_now(rtc); + if (now < rtc->sf_start) + { + *us = 0; + } + else if (now - rtc->sf_start > UINT32_MAX) + { + *us = UINT32_MAX; + } + else + { + *us = now - rtc->sf_start; + } + + ret = true; + } + + leave_critical_section(flags); + + return ret; +} + +/**************************************************************************** + * Name: nrf54l_radioi8_work_inactive + * + * Description: + * Work when we enter inactive state. + * + ****************************************************************************/ + +static void nrf54l_radioi8_work_inactive(void *arg) +{ + struct nrf54l_radioi8_dev_s *dev = (struct nrf54l_radioi8_dev_s *)arg; + enum ieee802154_sfevent_e sfevent; + + /* Notify MAC */ + + sfevent = IEEE802154_SFEVENT_ENDOFACTIVE; + dev->radiocb->sfevent(dev->radiocb, sfevent); +} + +/**************************************************************************** + * Name: nrf54l_radioi8_isr_rtc + * + * Description: + * Helper GRTC interrupt handler. + * + ****************************************************************************/ + +static int nrf54l_radioi8_isr_rtc(int irq, void *context, void *arg) +{ + struct nrf54l_radioi8_dev_s *dev = (struct nrf54l_radioi8_dev_s *)arg; + struct nrf54l_radioi8_rtc_s *rtc = NULL; + irqstate_t flags; + uint8_t evt; + + DEBUGASSERT(dev != NULL); + rtc = dev->rtc; + + flags = enter_critical_section(); + + if (NRF54L_GRTC_CHECKINT(rtc->rtc, NRF54L_GRTC_EVT)) + { + NRF54L_GRTC_ACKINT(rtc->rtc, NRF54L_GRTC_EVT); + + evt = rtc->evt; + if (evt == NRF54L_SFEVT_NONE) + { + goto out; + } + + rtc->evt = NRF54L_SFEVT_NONE; + + /* Superframe start */ + + if (evt == NRF54L_SFEVT_START) + { + nrf54l_radioi8_trace_put(RADIO_TRACE_IRQ_RTCBI, 0); + + rtc->sf_start = rtc->evt_time + NRF54L_RADIOI8_RAMPUP_US; + rtc->capstart = NRF54L_RADIOI8_FRAME_US(dev->radio->beaconbuf[0]) + + NRF54L_RADIOI8_IFS_US(dev->radio->beaconbuf[0]); + rtc->running = true; + + /* Transmit data from beaconbuf */ + + dev->radio->ops->beacon_tx(dev); + + nrf54l_radioi8_rtc_next(dev); + } + + /* End of active portion */ + + else if (evt == NRF54L_SFEVT_SDEND) + { + nrf54l_radioi8_trace_put(RADIO_TRACE_IRQ_RTCSD, 0); + + if (work_available(&dev->rtc->inactive_work)) + { + work_queue(HPWORK, &dev->rtc->inactive_work, + nrf54l_radioi8_work_inactive, dev, 0); + } + + nrf54l_radioi8_rtc_alarm(rtc, NRF54L_SFEVT_START, + rtc->sf_start + rtc->bi - + NRF54L_RADIOI8_RAMPUP_US); + } + } + +out: + leave_critical_section(flags); + + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nrf54l_radioi8_rtc_init + * + * Description: + * Initialize the GRTC for IEEE802154 operations. + * Used to handle superframe timings. + * + ****************************************************************************/ + +struct nrf54l_radioi8_rtc_s * +nrf54l_radioi8_rtc_init(struct nrf54l_radioi8_dev_s *dev) +{ + struct nrf54l_grtc_dev_s *grtc = NULL; + + /* Reserve the GRTC interrupt group */ + + grtc = nrf54l_grtc_init(NRF54L_IEEE802154_GRTC); + if (grtc == NULL) + { + wlerr("nrf54l_grtc_init(%d) failed\n", NRF54L_IEEE802154_GRTC); + return NULL; + } + + /* The GRTC is not reset by a system reset, stop the superframe event + * left by the previous firmware before the interrupt is enabled. + */ + + NRF54L_GRTC_DISABLEINT(grtc, NRF54L_GRTC_EVT); + NRF54L_GRTC_DISABLECC(grtc, NRF54L_GRTC_CC); + NRF54L_GRTC_ACKINT(grtc, NRF54L_GRTC_EVT); + + /* Attach GRTC interrupt */ + + NRF54L_GRTC_SETISR(grtc, nrf54l_radioi8_isr_rtc, dev); + + /* The SYSCOUNTER runs from now on, it is not cleared as the system + * timer may use it + */ + + NRF54L_GRTC_START(grtc); + + /* Connect RTC */ + + memset(&g_radioi8_rtc, 0, sizeof(struct nrf54l_radioi8_rtc_s)); + g_radioi8_rtc.ops = &g_radioi8_rtc_ops; + g_radioi8_rtc.rtc = grtc; + + return &g_radioi8_rtc; +} diff --git a/arch/arm/src/nrf54l/nrf54l_ieee802154_rtc.h b/arch/arm/src/nrf54l/nrf54l_ieee802154_rtc.h new file mode 100644 index 00000000000..b2801553c7b --- /dev/null +++ b/arch/arm/src/nrf54l/nrf54l_ieee802154_rtc.h @@ -0,0 +1,139 @@ +/**************************************************************************** + * arch/arm/src/nrf54l/nrf54l_ieee802154_rtc.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_NRF54L_NRF54L_IEEE802154_RTC_H +#define __ARCH_ARM_SRC_NRF54L_NRF54L_IEEE802154_RTC_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <stdbool.h> +#include <stdint.h> + +#include <nuttx/wqueue.h> + +#include "nrf54l_grtc.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Superframe events */ + +enum nrf54l_ieee802154_sfevt_e +{ + NRF54L_SFEVT_NONE = 0, /* No event scheduled */ + NRF54L_SFEVT_START = 1, /* Superframe start, beacon TX */ + NRF54L_SFEVT_SDEND = 2 /* End of the active portion */ +}; + +/* Forward reference */ + +struct ieee802154_superframespec_s; +struct nrf54l_radioi8_rtc_s; +struct nrf54l_radioi8_dev_s; + +/* RTC ops */ + +struct nrf54l_radioi8_rtc_ops_s +{ + /* Configure the superframe timings according to superframe spec */ + + int (*setup)(struct nrf54l_radioi8_dev_s *dev, + struct ieee802154_superframespec_s *sfspec); + + /* Start transmitting beacons */ + + int (*start)(struct nrf54l_radioi8_dev_s *dev); + + /* A beacon with the given PHR was just received */ + + int (*sync)(struct nrf54l_radioi8_dev_s *dev, uint8_t phr); + + /* Stop the superframe */ + + int (*stop)(struct nrf54l_radioi8_dev_s *dev); + + /* Reset RTC */ + + void (*reset)(struct nrf54l_radioi8_dev_s *dev); + + /* Time in us since the superframe start, false if not synchronized */ + + bool (*elapsed)(struct nrf54l_radioi8_dev_s *dev, uint32_t *us); +}; + +/* RTC interface */ + +struct nrf54l_radioi8_rtc_s +{ + /* GRTC lower-half */ + + struct nrf54l_grtc_dev_s *rtc; + + /* IEEE 802.15.4 RTC operations */ + + struct nrf54l_radioi8_rtc_ops_s *ops; + + /* For deferring inactive state work to the work queue */ + + struct work_s inactive_work; + + /* Superframe timings in us */ + + uint64_t sf_start; /* Start of the current superframe */ + uint64_t evt_time; /* Time of the scheduled event */ + uint32_t bi; /* Beacon interval */ + uint32_t sd; /* Superframe duration */ + uint32_t cap; /* End of the CAP */ + uint32_t capstart; /* Start of the CAP, after the beacon IFS */ + + /* RTC state */ + + uint8_t evt; /* Scheduled event */ + bool running; /* Superframe timing valid */ +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: nrf54l_radioi8_rtc_init + * + * Description: + * Initialize low resolution, low power timer for IEEE802154 operations. + * Used to handle superframe timings. + * + ****************************************************************************/ + +struct nrf54l_radioi8_rtc_s * +nrf54l_radioi8_rtc_init(struct nrf54l_radioi8_dev_s *dev); + +#endif /* __ARCH_ARM_SRC_NRF54L_NRF54L_IEEE802154_RTC_H */ diff --git a/arch/arm/src/nrf54l/nrf54l_ieee802154_trace.c b/arch/arm/src/nrf54l/nrf54l_ieee802154_trace.c index 0ff39658598..0503f2d847d 100644 --- a/arch/arm/src/nrf54l/nrf54l_ieee802154_trace.c +++ b/arch/arm/src/nrf54l/nrf54l_ieee802154_trace.c @@ -50,14 +50,13 @@ static const char *g_radio_trace_str[] = "RADIO IRQ: WAIT ACK", "RADIO IRQ: RX ACK DONE", "RADIO IRQ: TX CCA BUSY", + "RADIO IRQ: BEACON TX", "TIM IRQ: TIM ACK TX", "TIM IRQ; TIM TX DELAY", "TIM IRQ: TIM WAIT ACK", "TIM IRQ: TIM CSMA DELAY", "RTC IRQ: RTC SD", - "RTC IRQ: RTC CAP", - "RTC IRQ: RTC TIMESLOT", "RTC IRQ: RTC BI", "WORK: RX", @@ -73,9 +72,12 @@ static const char *g_radio_trace_str[] = "CSMASETUP", "CSMATRIGGER", "NOCSMATRIGGER", + "BEACONTX", "NOACK", "DROPFRAME", "TXTRYAGAIN", + "SFSYNC", + "CSMAPAUSE", }; /* Trace data */ diff --git a/arch/arm/src/nrf54l/nrf54l_ieee802154_trace.h b/arch/arm/src/nrf54l/nrf54l_ieee802154_trace.h index 015b7a8a636..190ad6a2762 100644 --- a/arch/arm/src/nrf54l/nrf54l_ieee802154_trace.h +++ b/arch/arm/src/nrf54l/nrf54l_ieee802154_trace.h @@ -59,6 +59,7 @@ enum radio_trace_type_e RADIO_TRACE_IRQ_WAITACK, RADIO_TRACE_IRQ_RXACKDONE, RADIO_TRACE_IRQ_TXCCABUSY, + RADIO_TRACE_IRQ_BEACONTX, /* Timer interrupts */ @@ -67,8 +68,6 @@ enum radio_trace_type_e RADIO_TRACE_IRQ_TIMWAITACK, RADIO_TRACE_IRQ_TIMCSMADELAY, RADIO_TRACE_IRQ_RTCSD, - RADIO_TRACE_IRQ_RTCCAP, - RADIO_TRACE_IRQ_RTCTIMESLOT, RADIO_TRACE_IRQ_RTCBI, /* Works */ @@ -88,9 +87,12 @@ enum radio_trace_type_e RADIO_TRACE_CSMASETUP, RADIO_TRACE_CSMATRIGGER, RADIO_TRACE_NOCSMATRIGGER, + RADIO_TRACE_BEACONTX, RADIO_TRACE_NOACK, RADIO_TRACE_DROPFRAME, RADIO_TRACE_TXRETRY, + RADIO_TRACE_SFSYNC, + RADIO_TRACE_CSMAPAUSE, }; /****************************************************************************
