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,

Reply via email to