This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Hardware Timer interrupt delayed significantly by SoftDevice (Or even missed)

Hello,

I am having an issue where it appears that my hardware timer interrupts can occasionally be delayed by upwards of 20 ms or even be missed entirely. My code is based on Martin BL's example as seen below.

Original thread: https://devzone.nordicsemi.com/f/nordic-q-a/25074/why-is-not-my-timer-triggered-when-configured-through-ppi-and-spi

Direct link to MartinBL's code in this thread: https://devzone.nordicsemi.com/cfs-file/__key/communityserver-discussions-components-files/4/5037.main.c

Hardware: nRF52840 SoC

SDK: 15.0.0

Connected to PC with nRF52840 USB Dongle and nRF Connect

In short, I have two hardware timers as observed in the example source above (Timer CC values are different however):

TIMER_TIMER (Timer mode): Fires every 32 usec that triggers the SPI_START_TASK using PPI. This iterates through an ArrayList. This timer's interrupt callback id disabled.

TIMER_COUNTER (Counter mode): Counts the SPI_END_EVENT occurrences. This timer interrupt is enabled.

I have a SPI TX buffer of size 625 bytes. So I expect the TIMER_COUNTER interrupt to fire once for every 625 bytes sent.

It is imperative that my SPI writes occur every 32 usec since I am writing to a DAC. So to achieve this, I have a total buffer of length 1250 bytes that makes up 40 msec worth of DAC values where the last 20 msec worth of buffer space is deemed an overflow region. So the first 625 bytes are the intended transmit buffer region, and the remaining 625 bytes are the overflow region. This is done since I expect the SoftDevice to delay my TIMER_COUNTER interrupt callback to some degree. When the TIMER_COUNTER interrupt fires, I read the spi3.p_reg->TXD.PTR to see how far into the overflow region of the buffer it has gone, and then move the pointer back to the start of the buffer (plus an offset of however many bytes the TX.PTR went over 625) and update the TIMER_COUNTER CC value accordingly. This way I am not changing the interval of my 32 usec SPI writes.

Here is an example log snippet of everything working properly:

(10:58:57.251) bufferOver 1
(10:58:57.251) TXD.PTR = 0x20017828 and COUNTER CC0 = 624
(10:59:00.431) bufferOver 3
(10:59:00.431) TXD.PTR = 0x2001782c and COUNTER CC0 = 622
(10:59:06.450) bufferOver 3
(10:59:06.450) TXD.PTR = 0x2001782c and COUNTER CC0 = 622
(10:59:12.469) bufferOver 4
(10:59:12.469) TXD.PTR = 0x2001782c and COUNTER CC0 = 621
(10:59:17.931) bufferOver 3
(10:59:17.931) TXD.PTR = 0x2001782c and COUNTER CC0 = 622

The log statements above are printed from the TIMER_COUNTER interrupt handler every ~6 msec as to not flood the UART. The code will run fine continuously for 10 min to an hour (varies). The failure occurs when the following happens:

(11:54:45.606) TXD.PTR = 0x20017828 and COUNTER CC0 = 624
(11:54:51.588) bufferOver 1
(11:54:51.588) TXD.PTR = 0x20017828 and COUNTER CC0 = 624
(11:54:51.656) bufferOver 625
(11:54:51.656) TXD.PTR = 0x20017d10 and COUNTER CC0 = 0

In the log above, it appears hat for some reason the TIMER_COUNTER interrupt never fired when it counted to 624. It instead waited an additional 625 counts + 1 for interrupt latency, hence the bufferOver value of 625. This is causing the CC register to be set to a value of 0 effectively disabling the TIMER_COUNTER.

For context: 

bufferOver = (spi3.p_reg->TXD.PTR) - (uint32_t) p_waveformData_End;    //p_waveformData_End points to the halfway point of the TX buffer (625 bytes). It never changes

timer_counter.p_reg->CC[0] = number_of_transfers - bufferOver;    // number_of_transfers == 625 (Always)

So essentially, TXD.PTR made it all the way to the end of the entire buffer before the interrupt fired, indicating that either the SoftDevice blocked for ~20 msec (625 bytes of SPI data) or that the timer interrupt never fired once it counted to the CC value of 624.

So, is it possible for the SoftDevice (or some other driver) to disable interrupts at any point that could cause a missed timer interrupt? The only operations that are running during this time are the two hardware timers mentioned above, the two associated PPI channels, BLE with no traffic (0 PDU size), and an app_timer() that is blinking a LED.

Thanks for any help or insight! I have been trying to resolve this issue for days.

Parents
  • Hi,

    Higher priority interrupts will block lower priority interrupts and if you have an interrupt that runs for 20 ms or longer you will miss a timer interrupt (if it has lower priority). However, 20 ms is a relatively long time, so unless you are doing some serious processing or printing a lot of data on a slow UART line inside a high priority interrupt context I find it difficult to believe that this is what happens. 

    The Softdevice alone should never block your application for 20 ms straight. You can see what latency to expect from the Softdevice here

    But I don't understand why you need this whole mechanism? With your 32 us timer and PPI you can transfer your buffer to the DAC autonomously. The Softdevice will never block your timer from starting the SPI. 

    Have you looked through the Errata list?

    Best regards,
    Martin

  • Hey Martin!

    My timer interrupts are set to priority 2 (Highest possible) for this reason. Normally, I am not printing any information to a debug UART during this time, but have added a few debug statements as highlighted in my original post in an attempt to debug why this is happening. Ie, it was happening prior to adding any debug statements.

    Based on the BLE peripheral timing diagram, I agree in that the SoftDevice shouldn't block for more than a few hundred usec at a time, hence why I am stumped as to why this is happening.

    In regards to the buffer "mechanism" that I am using, the purposes of this is because the DAC writes need to occur indefinitely until the application tells it to stop. Given my understanding of ArrayLists, PPI, timers, and the SPI driver, the TXD.PTR must be reset by the CPU once all data in the ArrayList has been transmitted. In other words, the SPI driver cannot loop through the ArrayList without CPU intervention. This was confirmed by another FAE in my post here: https://devzone.nordicsemi.com/f/nordic-q-a/37731/nrf52840-spi-easydma-double-buffering (Read the last 3 replies). If there is a way to indefinitely loop through an ArrayList without CPU intervention, that would be great! (As this is what I had hoped could be done originally). Or perhaps there is an easier way to achieve my desired behavior?

    I will look into the Errata list. I am using a Rigado branded nRF52840, so I need to figure out which revision Nordic chipset it is.

    Thanks!

Reply
  • Hey Martin!

    My timer interrupts are set to priority 2 (Highest possible) for this reason. Normally, I am not printing any information to a debug UART during this time, but have added a few debug statements as highlighted in my original post in an attempt to debug why this is happening. Ie, it was happening prior to adding any debug statements.

    Based on the BLE peripheral timing diagram, I agree in that the SoftDevice shouldn't block for more than a few hundred usec at a time, hence why I am stumped as to why this is happening.

    In regards to the buffer "mechanism" that I am using, the purposes of this is because the DAC writes need to occur indefinitely until the application tells it to stop. Given my understanding of ArrayLists, PPI, timers, and the SPI driver, the TXD.PTR must be reset by the CPU once all data in the ArrayList has been transmitted. In other words, the SPI driver cannot loop through the ArrayList without CPU intervention. This was confirmed by another FAE in my post here: https://devzone.nordicsemi.com/f/nordic-q-a/37731/nrf52840-spi-easydma-double-buffering (Read the last 3 replies). If there is a way to indefinitely loop through an ArrayList without CPU intervention, that would be great! (As this is what I had hoped could be done originally). Or perhaps there is an easier way to achieve my desired behavior?

    I will look into the Errata list. I am using a Rigado branded nRF52840, so I need to figure out which revision Nordic chipset it is.

    Thanks!

Children
No Data
Related