This is an automated email from the ASF dual-hosted git repository. acassis pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit ce550c4fe67e691a288de065c39d0366699a73dc Author: raiden00pl <[email protected]> AuthorDate: Wed Nov 4 15:46:47 2026 +0100 arch/arm/nrf52: Start the IEEE 802.15.4 ACK at aTurnaroundTime. The ACK TX was started from two interrupts and went on air about 80 us late, which a strict receiver like the STM32WB MAC rejects. RADIO PHYEND starts the idle 802.15.4 timer over PPI and its ACK compare enables TX. Assisted-by: Claude Code:claude-fable-5-1 Signed-off-by: raiden00pl <[email protected]> --- arch/arm/src/nrf52/Kconfig | 1 + arch/arm/src/nrf52/hardware/nrf52_tim.h | 4 + arch/arm/src/nrf52/nrf52_ieee802154.c | 1 - arch/arm/src/nrf52/nrf52_ieee802154_radio.c | 37 ++---- arch/arm/src/nrf52/nrf52_ieee802154_radio.h | 8 +- arch/arm/src/nrf52/nrf52_ieee802154_tim.c | 186 ++++++++++++++++++++++++---- arch/arm/src/nrf52/nrf52_ieee802154_tim.h | 8 ++ arch/arm/src/nrf52/nrf52_ieee802154_trace.c | 1 - arch/arm/src/nrf52/nrf52_ieee802154_trace.h | 1 - 9 files changed, 193 insertions(+), 54 deletions(-) diff --git a/arch/arm/src/nrf52/Kconfig b/arch/arm/src/nrf52/Kconfig index 55952674312..add102671a7 100644 --- a/arch/arm/src/nrf52/Kconfig +++ b/arch/arm/src/nrf52/Kconfig @@ -645,6 +645,7 @@ config NRF52_RADIO_IEEE802154 select ARCH_IRQPRIO select NRF52_RADIO_CUSTOM select NRF52_TIMER0 + select NRF52_PPI if NRF52_RADIO_IEEE802154 diff --git a/arch/arm/src/nrf52/hardware/nrf52_tim.h b/arch/arm/src/nrf52/hardware/nrf52_tim.h index 417ff1adf3a..44d1fa12df1 100644 --- a/arch/arm/src/nrf52/hardware/nrf52_tim.h +++ b/arch/arm/src/nrf52/hardware/nrf52_tim.h @@ -73,6 +73,10 @@ #define TIM_TASKS_CLEAR (1 << 0) /* Bit 0: Clear Timer */ +/* TASKS_CAPTURE[x] Register */ + +#define TIM_TASKS_CAPTURE (1 << 0) /* Bit 0: Capture Timer value */ + /* SHORTS Register */ #define TIM_SHORTS_COMPARE_CLEAR(x) (1 << (x)) /* Bits 0-5: */ diff --git a/arch/arm/src/nrf52/nrf52_ieee802154.c b/arch/arm/src/nrf52/nrf52_ieee802154.c index de8eec5a0ba..a755b96e770 100644 --- a/arch/arm/src/nrf52/nrf52_ieee802154.c +++ b/arch/arm/src/nrf52/nrf52_ieee802154.c @@ -964,7 +964,6 @@ nrf52_radioi8_register(struct nrf52_radio_board_s *board) return NULL; } - DEBUGASSERT(dev->radio->ops->txstart); DEBUGASSERT(dev->radio->ops->notify_noack); DEBUGASSERT(dev->radio->ops->ccastart); DEBUGASSERT(dev->radio->ops->rxenable); diff --git a/arch/arm/src/nrf52/nrf52_ieee802154_radio.c b/arch/arm/src/nrf52/nrf52_ieee802154_radio.c index 001911f4bb8..4238bd3f96d 100644 --- a/arch/arm/src/nrf52/nrf52_ieee802154_radio.c +++ b/arch/arm/src/nrf52/nrf52_ieee802154_radio.c @@ -158,7 +158,8 @@ */ #define IEEE802154_ACKTX_INT (RADIO_INT_PHYEND) -#define IEEE802154_ACKTX_SHORTS (RADIO_SHORTS_PHYEND_DISABLE) +#define IEEE802154_ACKTX_SHORTS (RADIO_SHORTS_TXREADY_START | \ + RADIO_SHORTS_PHYEND_DISABLE) /* aUnitBackoffPeriod in us */ @@ -190,7 +191,6 @@ static void nrf52_radioi8_gts_setup(struct nrf52_radioi8_dev_s *dev, /* Ops */ -static void nrf52_radioi8_txstart(struct nrf52_radioi8_dev_s *dev); static void nrf52_radioi8_ccastart(struct nrf52_radioi8_dev_s *dev); static void nrf52_radioi8_notify_noack(struct nrf52_radioi8_dev_s *dev); static int nrf52_radioi8_rxenable(struct nrf52_radioi8_dev_s *dev, @@ -266,7 +266,6 @@ static uint8_t aligned_data(8) static struct nrf52_radioi8_radio_ops_s g_radioi8_radio_ops = { - .txstart = nrf52_radioi8_txstart, .ccastart = nrf52_radioi8_ccastart, .notify_noack = nrf52_radioi8_notify_noack, .rxenable = nrf52_radioi8_rxenable, @@ -456,14 +455,16 @@ static void nrf52_radioi8_ack_transmit(struct nrf52_radioi8_dev_s *dev) dev->radio->state.state = NRF52_RADIO_STATE_ACKTX; - /* Enable TX - start TX is called from timer interrupt */ - - NRF52_RADIO_PUTREG(lower, NRF52_RADIO_TASKS_TXEN_OFFSET, 1); + /* The timer started at PHYEND enables TX on time, enable it now when the + * timer was busy or is already behind. + */ - /* Run TIMER - TX is handled in timer isr */ + if (!dev->tim->ops->ackarm(dev)) + { + NRF52_RADIO_PUTREG(lower, NRF52_RADIO_TASKS_TXEN_OFFSET, 1); + } nrf52_radioi8_trace_put(RADIO_TRACE_ACKTX, pending); - dev->tim->ops->setup(dev, NRF52_TIMER_CHAN_ACK, IEEE802154_ACKIFS_SYMBOLS); } /**************************************************************************** @@ -688,24 +689,6 @@ static void nrf52_radioi8_dopoll_gts(void *arg) } #endif /* NRF52_GTS_SLOTS > 0 */ -/**************************************************************************** - * Name: nrf52_radioi8_txstart - * - * Description: - * Start transmission, TX must be armed (TXEN must be set). - * - ****************************************************************************/ - -static void nrf52_radioi8_txstart(struct nrf52_radioi8_dev_s *dev) -{ - struct nrf52_radio_dev_s *lower = NULL; - - DEBUGASSERT(dev); - lower = dev->radio->lower; - - NRF52_RADIO_PUTREG(lower, NRF52_RADIO_TASKS_START_OFFSET, 1); -} - /**************************************************************************** * Name: nrf52_radioi8_ccastart * @@ -2413,6 +2396,8 @@ static void nrf52_radioi8_state_acktx(struct nrf52_radioi8_dev_s *dev) { nrf52_radioi8_trace_put(RADIO_TRACE_IRQ_ACKTX, 0); + dev->tim->ops->ackdone(dev); + /* Start the TX that waited for the ACK TX, after the IFS */ nrf52_radioi8_txnext(dev, dev->radio->state.rxlong ? diff --git a/arch/arm/src/nrf52/nrf52_ieee802154_radio.h b/arch/arm/src/nrf52/nrf52_ieee802154_radio.h index 0d6a1f948b9..fa68add1fdd 100644 --- a/arch/arm/src/nrf52/nrf52_ieee802154_radio.h +++ b/arch/arm/src/nrf52/nrf52_ieee802154_radio.h @@ -60,6 +60,10 @@ #define NRF52_RADIOI8_RAMPUP_US (40) +/* Latency from the last bit on air to the RADIO PHYEND event */ + +#define NRF52_RADIOI8_PHYEND_US (23) + /* Air time of a frame with the given PHR: SHR, PHR and PSDU, 32 us/octet */ #define NRF52_RADIOI8_FRAME_US(phr) (((phr) + 6) * 2 * IEEE802154_SYMBOL_US) @@ -148,10 +152,6 @@ struct ieee802154_cca_s; struct nrf52_radioi8_radio_ops_s { - /* Start transmission - TX must be armed (TXEN set) */ - - void (*txstart)(struct nrf52_radioi8_dev_s *dev); - /* Start CCA - RX must be armed (RXEN set) */ void (*ccastart)(struct nrf52_radioi8_dev_s *dev); diff --git a/arch/arm/src/nrf52/nrf52_ieee802154_tim.c b/arch/arm/src/nrf52/nrf52_ieee802154_tim.c index 4ef0fbd231f..6536fba1331 100644 --- a/arch/arm/src/nrf52/nrf52_ieee802154_tim.c +++ b/arch/arm/src/nrf52/nrf52_ieee802154_tim.c @@ -33,6 +33,9 @@ #include <nuttx/irq.h> #include <nuttx/wqueue.h> +#include "arm_internal.h" +#include "nrf52_ppi.h" +#include "hardware/nrf52_tim.h" #include "nrf52_ieee802154_tim.h" #include "nrf52_ieee802154_trace.h" @@ -50,13 +53,23 @@ #define NRF52_IEEE802154_TIMER0 (0) -/* Timer period set to 16us (symbol duration) */ +/* Timer period set to 1 us, 16 MHz / 16 = 1 MHz */ -#define NRF52_TIMER_FREQUENCY (1000000 / 16) +#define NRF52_TIMER_PRESCALER NRF52_TIM_PRE_1000000 -/* 16MHz / (2 ** 8) = 62500 */ +/* The ACK of a received frame starts aTurnaroundTime after its last symbol + * (IEEE 802.15.4-2006 7.5.6.4.2). While the timer is idle, RADIO PHYEND + * clears and starts it and it stops at the ACK compare. For an ACK the + * compare enables TX through RADIO TXEN, TXREADY starts the TX. + */ -#define NRF52_TIMER_PRESCALER (8) +#define NRF52_TIMER_BASE NRF52_TIMER0_BASE +#define NRF52_TIMER_ACKTXEN_US (IEEE802154_TURN_AROUND_TIME * \ + IEEE802154_SYMBOL_US - \ + NRF52_RADIOI8_RAMPUP_US - \ + NRF52_RADIOI8_PHYEND_US) +#define NRF52_TIMER_LINK_PHYEND (14) +#define NRF52_TIMER_LINK_ACKTXEN (15) /**************************************************************************** * Private Function Prototypes @@ -68,6 +81,8 @@ static int nrf52_radioi8_tim(struct nrf52_radioi8_dev_s *dev, uint8_t chan, uint32_t val); static void nrf52_radioi8_tim_stop(struct nrf52_radioi8_dev_s *dev); static void nrf52_radioi8_tim_reset(struct nrf52_radioi8_dev_s *dev); +static bool nrf52_radioi8_tim_ackarm(struct nrf52_radioi8_dev_s *dev); +static void nrf52_radioi8_tim_ackdone(struct nrf52_radioi8_dev_s *dev); /* Interrupts logic */ @@ -82,9 +97,11 @@ static int nrf52_radioi8_isr_tim(int irq, void *context, void *arg); static struct nrf52_radioi8_tim_ops_s g_radioi8_tim_ops = { - .setup = nrf52_radioi8_tim, - .stop = nrf52_radioi8_tim_stop, - .reset = nrf52_radioi8_tim_reset + .setup = nrf52_radioi8_tim, + .stop = nrf52_radioi8_tim_stop, + .reset = nrf52_radioi8_tim_reset, + .ackarm = nrf52_radioi8_tim_ackarm, + .ackdone = nrf52_radioi8_tim_ackdone }; /* Timer instance */ @@ -95,6 +112,52 @@ static struct nrf52_radioi8_tim_s g_radioi8_tim; * Private Function ****************************************************************************/ +/**************************************************************************** + * Name: nrf52_radioi8_tim_count + * + * Description: + * Capture the timer, the TX delay channel is free when the timer is idle. + * + ****************************************************************************/ + +static uint32_t nrf52_radioi8_tim_count(void) +{ + putreg32(TIM_TASKS_CAPTURE, NRF52_TIMER_BASE + + NRF52_TIM_TASKS_CAPTURE_OFFSET(NRF52_TIMER_CHAN_TXDELAY)); + + return getreg32(NRF52_TIMER_BASE + + NRF52_TIM_CC_OFFSET(NRF52_TIMER_CHAN_TXDELAY)); +} + +/**************************************************************************** + * Name: nrf52_radioi8_tim_idle + * + * Description: + * Let RADIO PHYEND start the idle timer, or keep it for a timer event. + * + ****************************************************************************/ + +static void nrf52_radioi8_tim_idle(struct nrf52_radioi8_dev_s *dev, + bool idle) +{ + struct nrf52_radioi8_tim_s *tim = dev->tim; + + if (idle) + { + NRF52_TIM_STOP(tim->tim); + NRF52_TIM_CLEAR(tim->tim); + NRF52_TIM_SHORTS(tim->tim, NRF52_TIM_SHORT_COMPARE_STOP, + NRF52_TIMER_CHAN_ACK, true); + nrf52_ppi_channel_enable(NRF52_TIMER_LINK_PHYEND, true); + } + else + { + nrf52_ppi_channel_enable(NRF52_TIMER_LINK_PHYEND, false); + NRF52_TIM_SHORTS(tim->tim, NRF52_TIM_SHORT_COMPARE_STOP, + NRF52_TIMER_CHAN_ACK, false); + } +} + /**************************************************************************** * Name: nrf52_radioi8_tim * @@ -123,6 +186,7 @@ static int nrf52_radioi8_tim(struct nrf52_radioi8_dev_s *dev, uint8_t chan, /* Stop timer and clear the counter */ + nrf52_radioi8_tim_idle(dev, false); NRF52_TIM_STOP(tim->tim); NRF52_TIM_CLEAR(tim->tim); @@ -132,7 +196,7 @@ static int nrf52_radioi8_tim(struct nrf52_radioi8_dev_s *dev, uint8_t chan, /* Set compare register */ - NRF52_TIM_SETCC(tim->tim, chan, val); + NRF52_TIM_SETCC(tim->tim, chan, val * IEEE802154_SYMBOL_US); /* Configure interrupt */ @@ -187,6 +251,8 @@ static void nrf52_radioi8_tim_stop(struct nrf52_radioi8_dev_s *dev) dev->tim->tim_pending = false; dev->tim->tim_now = -1; + + nrf52_radioi8_tim_idle(dev, true); } /**************************************************************************** @@ -199,49 +265,125 @@ static void nrf52_radioi8_tim_stop(struct nrf52_radioi8_dev_s *dev) static void nrf52_radioi8_tim_reset(struct nrf52_radioi8_dev_s *dev) { - /* Configure TIMER - freq = 62500 */ + /* Configure TIMER - freq = 1 MHz */ NRF52_TIM_STOP(dev->tim->tim); NRF52_TIM_CONFIGURE(dev->tim->tim, NRF52_TIM_MODE_TIMER, NRF52_TIM_WIDTH_32B); NRF52_TIM_SETPRE(dev->tim->tim, NRF52_TIMER_PRESCALER); + NRF52_TIM_SETCC(dev->tim->tim, NRF52_TIMER_CHAN_ACK, + NRF52_TIMER_ACKTXEN_US); + + /* RADIO PHYEND starts the timer, the ACK compare enables TX */ + + nrf52_ppi_set_event_ep(NRF52_TIMER_LINK_PHYEND, + NRF52_RADIO_EVENTS_PHYEND); + nrf52_ppi_set_task_ep(NRF52_TIMER_LINK_PHYEND, + NRF52_TIMER_BASE + NRF52_TIM_TASKS_CLEAR_OFFSET); + nrf52_ppi_set_task2_ep(NRF52_TIMER_LINK_PHYEND, + NRF52_TIMER_BASE + NRF52_TIM_TASKS_START_OFFSET); + nrf52_ppi_set_event_ep(NRF52_TIMER_LINK_ACKTXEN, + NRF52_TIMER_BASE + + NRF52_TIM_EVENTS_COMPARE_OFFSET( + NRF52_TIMER_CHAN_ACK)); + nrf52_ppi_set_task_ep(NRF52_TIMER_LINK_ACKTXEN, NRF52_RADIO_TASKS_TXEN); + + nrf52_ppi_channel_enable(NRF52_TIMER_LINK_ACKTXEN, false); /* Reset state */ dev->tim->tim_pending = false; dev->tim->tim_now = -1; + + nrf52_radioi8_tim_idle(dev, true); } /**************************************************************************** - * Name: nrf52_radioi8_tim_expire + * Name: nrf52_radioi8_tim_ackarm * * Description: - * Handle the expired timer channel. + * Let the timer enable the ACK TX. It runs since PHYEND only when it was + * idle, and it must not have reached the ACK compare yet. * ****************************************************************************/ -static void nrf52_radioi8_tim_expire(struct nrf52_radioi8_dev_s *dev) +static bool nrf52_radioi8_tim_ackarm(struct nrf52_radioi8_dev_s *dev) { struct nrf52_radioi8_tim_s *tim = dev->tim; - irqstate_t flags; + bool armed = false; + uint32_t count; + irqstate_t flags; flags = enter_critical_section(); - switch (tim->tim_now) + count = nrf52_radioi8_tim_count(); + if (!tim->tim_pending && count > 0 && count < NRF52_TIMER_ACKTXEN_US) { - /* RX ACK handler */ + nrf52_ppi_channel_enable(NRF52_TIMER_LINK_ACKTXEN, true); + + /* The compare may pass while the channel is enabled */ - case NRF52_TIMER_CHAN_ACK: + if (nrf52_radioi8_tim_count() < NRF52_TIMER_ACKTXEN_US || + (NRF52_RADIO_GETREG(dev->radio->lower, NRF52_RADIO_STATE_OFFSET) & + RADIO_STATE_MASK) != RADIO_STATE_DISABLED) + { + tim->tim_pending = true; + tim->tim_now = NRF52_TIMER_CHAN_ACK; + armed = true; + } + else { - nrf52_radioi8_trace_put(RADIO_TRACE_IRQ_TIMACKTX, 0); + nrf52_ppi_channel_enable(NRF52_TIMER_LINK_ACKTXEN, false); + } + } - /* Start TX */ + leave_critical_section(flags); + return armed; +} - dev->radio->ops->txstart(dev); +/**************************************************************************** + * Name: nrf52_radioi8_tim_ackdone + * + * Description: + * The ACK TX is done, the timer is idle again. + * + ****************************************************************************/ - break; - } +static void nrf52_radioi8_tim_ackdone(struct nrf52_radioi8_dev_s *dev) +{ + irqstate_t flags; + + flags = enter_critical_section(); + + nrf52_ppi_channel_enable(NRF52_TIMER_LINK_ACKTXEN, false); + if (dev->tim->tim_now == NRF52_TIMER_CHAN_ACK) + { + dev->tim->tim_pending = false; + dev->tim->tim_now = -1; + nrf52_radioi8_tim_idle(dev, true); + } + + leave_critical_section(flags); +} + +/**************************************************************************** + * Name: nrf52_radioi8_tim_expire + * + * Description: + * Handle the expired timer channel. + * + ****************************************************************************/ + +static void nrf52_radioi8_tim_expire(struct nrf52_radioi8_dev_s *dev) +{ + struct nrf52_radioi8_tim_s *tim = dev->tim; + irqstate_t flags; + + flags = enter_critical_section(); + + switch (tim->tim_now) + { /* Delayed TX handler */ case NRF52_TIMER_CHAN_TXDELAY: @@ -302,6 +444,8 @@ static void nrf52_radioi8_tim_expire(struct nrf52_radioi8_dev_s *dev) tim->tim_pending = false; tim->tim_now = -1; + nrf52_radioi8_tim_idle(dev, true); + leave_critical_section(flags); } diff --git a/arch/arm/src/nrf52/nrf52_ieee802154_tim.h b/arch/arm/src/nrf52/nrf52_ieee802154_tim.h index 3257a9eeadd..b8a25428d1d 100644 --- a/arch/arm/src/nrf52/nrf52_ieee802154_tim.h +++ b/arch/arm/src/nrf52/nrf52_ieee802154_tim.h @@ -67,6 +67,14 @@ struct nrf52_radioi8_tim_ops_s /* Reset timer */ void (*reset)(struct nrf52_radioi8_dev_s *dev); + + /* Let the timer started at PHYEND enable the ACK TX */ + + bool (*ackarm)(struct nrf52_radioi8_dev_s *dev); + + /* ACK TX done */ + + void (*ackdone)(struct nrf52_radioi8_dev_s *dev); }; /* Timer interface */ diff --git a/arch/arm/src/nrf52/nrf52_ieee802154_trace.c b/arch/arm/src/nrf52/nrf52_ieee802154_trace.c index f9fe8384731..6194b31127a 100644 --- a/arch/arm/src/nrf52/nrf52_ieee802154_trace.c +++ b/arch/arm/src/nrf52/nrf52_ieee802154_trace.c @@ -52,7 +52,6 @@ static const char *g_radio_trace_str[] = "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", diff --git a/arch/arm/src/nrf52/nrf52_ieee802154_trace.h b/arch/arm/src/nrf52/nrf52_ieee802154_trace.h index 1e43c1e3e46..7bc74ea6cda 100644 --- a/arch/arm/src/nrf52/nrf52_ieee802154_trace.h +++ b/arch/arm/src/nrf52/nrf52_ieee802154_trace.h @@ -63,7 +63,6 @@ enum radio_trace_type_e /* Timer interrupts */ - RADIO_TRACE_IRQ_TIMACKTX, RADIO_TRACE_IRQ_TIMTXDELAY, RADIO_TRACE_IRQ_TIMWAITACK, RADIO_TRACE_IRQ_TIMCSMADELAY,
