openthread: [nrf52] What could cause `NRF_802154_TX_ERROR_TIMESLOT_DENIED` in `nrf_802154_transmit_raw_at`?

I’m doing some experiment with CSL on nrf52840 dk. Now I get OT_ERROR_CHANNEL_ACCESS_FAILURE in otPlatRadioTxDone after using nrf_802154_transmit_raw_at to transmit a frame. I used some trick to get the detailed nrf error code. The error in nrf_802154_transmit_failed is NRF_802154_RX_ERROR_DELAYED_TIMESLOT_DENIED. What could be the possible reason for this?

@hubertmis @LuDuda @edmont

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 32 (32 by maintainers)

Most upvoted comments

@Irving-cl I would like to make sure I understood correctly. When you use nrf_802154_transmit_raw_at function, you receive nrf_802154_transmit_failed error with error code 7 that translates to NRF_802154_TX_ERROR_TIMESLOT_DENIED, not NRF_802154_RX_ERROR_DELAYED_TIMESLOT_DENIED. Am I correct?

NRF_802154_TX_ERROR_TIMESLOT_DENIED can be raised in following conditions:

  1. Channel cannot be updated before expected transmission. It may happen only when transmission is scheduled too short before BLE activity.
  2. At scheduled transmission time (when timer IRQ is executed), radio driver critical section is taken by lower-priority execution context. It is possible that radio.c calls nrf_802154_receive() and during such call timer IRQ fires. Critical section is already taken by nrf_802154_receive() and scheduled transmission cannot happen. Such race condition should happen very rarely, unless your code calls 802.15.4 radio driver API in a loop. You can verify if critical section is taken checking m_nested_critical_section_counter : https://github.com/openthread/openthread/blob/f7e16fec240439f54998463f6d8ab2945edbe5b3/third_party/NordicSemiconductor/drivers/radio/nrf_802154_critical_section.c#L55 . If its value is greater than 0, critical section is taken.
  3. High Frequency clock is not ready when TX should happen. If you transit from RX to TX mode, there should be no problem with hfclk. However, if you transit from sleep to TX (could be through RX, but time from sleep to TX is shorter than crystal ramp up), then crystal might not be ready when the transmission should start. You can tweak crystal ramp up time with this constant: https://github.com/openthread/openthread/blob/f7e16fec240439f54998463f6d8ab2945edbe5b3/third_party/NordicSemiconductor/drivers/radio/rsch/nrf_802154_rsch.c#L25 . You can verify HFclk readiness in m_approvied_prios variable captured when the problem occurs (you must stop execution with a debugger on the problem, because content of this variable would change in dozens of microseconds): https://github.com/openthread/openthread/blob/f7e16fec240439f54998463f6d8ab2945edbe5b3/third_party/NordicSemiconductor/drivers/radio/rsch/nrf_802154_rsch.c#L55
  4. Radio arbiter prevents radio access for 802.15.4. In dynamic multi-protocol applications access to radio is granted by radio arbiter. It is possible that sometimes arbiter prevents radio access when scheduled transmission should start. This also can be verified in m_approved_prios.

I hope these details are good hints to move debugging process forward.

How should we choose the proper value for it?

Datasheet of the crystal you use should define its startup time. You can use this value, try to add some margin and verify if it works correctly.

Is there any side effect if we set a value that’s too big?

The side effect is increased power consumption because the crystal is powered longer than necessary.

Or we should just increase the value until it can work successfully?

That’s one way to go, but be aware that crystal properties vary between each particular component, it changes with temperature and time. Because of that I would recommend to add some safety margin. The cost would be a little higher power consumption, but the risk that the device would stop working correctly after some time or when the temperature changes would be lowered.

Follow up:

The reason seems to be transition from sleep to TX. I disabled the capability of OT_RADIO_CAPS_SLEEP_TO_TX on nrf so that in SubMac::BeginTransmit so that it is enforced to do receiving before transmission, then the transmission was successful:

    if ((mRadioCaps & OT_RADIO_CAPS_SLEEP_TO_TX) == 0)                                                                                                                                                                                                                       
    {                                                                                                                                                                                                                                                                                
        error = Get<Radio>().Receive(mTransmitFrame.GetChannel());                                                                                                                                                                                                           
        OT_ASSERT(error == OT_ERROR_NONE);                                                                                                                                                                                                                                   
    }