Author Topic: interrupt latency on ESP32  (Read 1017 times)

0 Members and 1 Guest are viewing this topic.

Offline apraxophobiaTopic starter

  • Contributor
  • Posts: 12
  • Country: us
interrupt latency on ESP32
« on: July 04, 2024, 03:56:04 pm »
I need to output SPI messages from my ESP32 with well-defined regular intervals (100 kHz). Effectively, I need to make a DAC using an external DAC SPI IC. But I only need to make square waves and constant DC.

In my experience the only way to get a solid, jitter-free 70 kHz trigger is with ledcWrite. Timers, delays etc. always introduce jitter.

So the current approach is to make a 70 kHz square wave on one pin, where a change on that pin triggers sending an SPI message with an interrupt routine.

Via some simplifying (e.g. just toggling a pin instead of using SPI), there's clearly an issue with the timing jitter of the execution of the interrupt routine. The 70 kHz wave is solid, and via debugging I know that the actual execution of the interrupt routine (toggling a pin) always takes the same duration of time. But there's jitter in the delay between when the interrupt is triggered (the solid 70 kHz square wave) and when the function within the interrupt is executed. This isn't terrible, but results in a jittery cycle about one out of every thousand or so cycles.

I've tried using higher priorities, disabling wifi, etc., and the interrupt function itself is dead simple, but I always have this issue. Am I trying to do something impossible? I have had near-zero luck with finding anything online about this (seemingly straightforward) issue.

TLDR:why is it so hard to generate a 100 kHz square wave with esp32 using interrupts and an external triggering 100 kHz square wave?
« Last Edit: July 04, 2024, 04:05:58 pm by apraxophobia »
 

Offline selcuk

  • Regular Contributor
  • *
  • Posts: 181
  • Country: tr
Re: interrupt latency on ESP32
« Reply #1 on: July 04, 2024, 04:06:46 pm »
I used ledc in the past with a 20 MHz output and I didn't remember issues. There is a glitch filter. You may want to disable that. And I recommend you to use ESP-IDF for such a project. You may have better control over hardware access.
 

Offline apraxophobiaTopic starter

  • Contributor
  • Posts: 12
  • Country: us
Re: interrupt latency on ESP32
« Reply #2 on: July 04, 2024, 04:14:43 pm »
The 100 kHz wave from ledc is solid, it's the ISR triggered off a change on the ledc pin that is giving issues.

The jitter appears somewhat regular, which makes me think it's a delay caused by some background routine, but I don't know.

I'll try ESP-IDF, though, thanks.
 

Offline globoy

  • Regular Contributor
  • *
  • Posts: 211
  • Country: us
Re: interrupt latency on ESP32
« Reply #3 on: July 04, 2024, 04:22:23 pm »
One out of 1000 cycles seems pretty good (but I understand, not good enough).  The ESP32 memory system is slightly complex and you could be seeing the result of a contention or a cache miss somewhere (maybe contention more likely).  Probably there are synchronization domains as well which can introduce jitter.  Is this something you could do with the ULP?
 

Offline nctnico

  • Super Contributor
  • ***
  • Posts: 27346
  • Country: nl
    • NCT Developments
Re: interrupt latency on ESP32
« Reply #4 on: July 04, 2024, 04:40:52 pm »
My approach would be to trigger the SPI transfer from a timer to update the DAC and use a timer-driven pin to update the DAC output. That way the jitter on the SPI transfer has zero effect on the DAC update.

The routines and variables to update the DAC need to be in internal RAM to avoid flash read/write latencies.
« Last Edit: July 04, 2024, 06:36:47 pm by nctnico »
There are small lies, big lies and then there is what is on the screen of your oscilloscope.
 

Offline westfw

  • Super Contributor
  • ***
  • Posts: 4238
  • Country: us
Re: interrupt latency on ESP32
« Reply #5 on: July 05, 2024, 12:28:02 am »
Do you have your ISR in tightly coupled memory (I guess internal RAM), rather than QSPI flash/ram?
On most of these "fast" CPUs, caching interferes with determinancy, and having to reset QSPI addresses to new locations can make that significantly worse.
 

Offline mtwieg

  • Regular Contributor
  • *
  • Posts: 184
  • Country: us
Re: interrupt latency on ESP32
« Reply #6 on: July 05, 2024, 12:38:37 pm »
In the past I once drove the CS pin of the slave (an ADC in this case) with the output of a PWM peripheral instead of letting the SPI peripheral control it. Just have to make sure the serial transfer doesn't jitter so much that it overlaps with the CS edges. Of course this approach probably won't work if there are other slaves on the same bus. In that case you should get a slave with a dedicated pin for controlling when the output updates.
 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3199
  • Country: ca
Re: interrupt latency on ESP32
« Reply #7 on: July 05, 2024, 12:43:27 pm »
Does your DAC get triggered by a signal edge?

If so, it's very easy - trigger it with the external pulse that you already have. Also wire this same external pulse to your MCU and produce an interrupt on the same edge. The interrupt will happen right after the DAC has been triggered, so you have full 10 us to transfer a new value to DAC through SPI. 10 us is plenty, and the exact timing doesn't matter.
« Last Edit: July 05, 2024, 01:25:16 pm by NorthGuy »
 

Offline apraxophobiaTopic starter

  • Contributor
  • Posts: 12
  • Country: us
Re: interrupt latency on ESP32
« Reply #8 on: July 05, 2024, 01:07:47 pm »
Does your DAC gets triggered by a signal edge?

If so, it's very easy - trigger it with the external pulse that you already have. Also wire this same external pulse to your MCU and produce an interrupt on the same edge. The interrupt will happen right after the DAC has been triggered, so you have full 10 us to transfer a new value to DAC through SPI. 10 us is plenty, and the exact timing doesn't matter.

Triggering the DAC isn't the issue. The simplest version of the problem is: given an external 100 kHz square wave, can I make an identical (but maybe slightly phase-shifted) square wave with the esp32 using an interrupt function, just following/copying the external one. I am using IRAM and a very simple interrupt function, which always executes in the same period of time, but there's jitter in the delay after which it is executed so the copied wave generated with the interrupt is jittery.
 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3199
  • Country: ca
Re: interrupt latency on ESP32
« Reply #9 on: July 05, 2024, 01:30:12 pm »
Does your DAC get triggered by a signal edge?

If so, it's very easy - trigger it with the external pulse that you already have. Also wire this same external pulse to your MCU and produce an interrupt on the same edge. The interrupt will happen right after the DAC has been triggered, so you have full 10 us to transfer a new value to DAC through SPI. 10 us is plenty, and the exact timing doesn't matter.

Triggering the DAC isn't the issue. The simplest version of the problem is: given an external 100 kHz square wave, can I make an identical (but maybe slightly phase-shifted) square wave with the esp32 using an interrupt function, just following/copying the external one. I am using IRAM and a very simple interrupt function, which always executes in the same period of time, but there's jitter in the delay after which it is executed so the copied wave generated with the interrupt is jittery.

My point was that you don't need to create a copy of your square wave and can use the square wave you already have. All you need to do is to synchronize the loading of the DAC with the existing square wave, which does not require any strict timing. Does this make sense?
 

Offline apraxophobiaTopic starter

  • Contributor
  • Posts: 12
  • Country: us
Re: interrupt latency on ESP32
« Reply #10 on: July 05, 2024, 01:42:36 pm »
Does your DAC get triggered by a signal edge?

If so, it's very easy - trigger it with the external pulse that you already have. Also wire this same external pulse to your MCU and produce an interrupt on the same edge. The interrupt will happen right after the DAC has been triggered, so you have full 10 us to transfer a new value to DAC through SPI. 10 us is plenty, and the exact timing doesn't matter.

Triggering the DAC isn't the issue. The simplest version of the problem is: given an external 100 kHz square wave, can I make an identical (but maybe slightly phase-shifted) square wave with the esp32 using an interrupt function, just following/copying the external one. I am using IRAM and a very simple interrupt function, which always executes in the same period of time, but there's jitter in the delay after which it is executed so the copied wave generated with the interrupt is jittery.

My point was that you don't need to create a copy of your square wave and can use the square wave you already have. All you need to do is to synchronize the loading of the DAC with the existing square wave, which does not require any strict timing. Does this make sense?


I don't quite see what you mean. Even if I can trigger the DAC with a solid square wave, if there's jitter on the SPI messages themselves, the DAC output won't be a solid square wave, right?
 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3199
  • Country: ca
Re: interrupt latency on ESP32
« Reply #11 on: July 05, 2024, 01:56:29 pm »
I don't quite see what you mean. Even if I can trigger the DAC with a solid square wave, if there's jitter on the SPI messages themselves, the DAC output won't be a solid square wave, right?

The SPI message doesn't happen all at once, it includes several clock edges which are spread over time. SPI message is not a single event you can time or synchronize.

There must be some event which causes the DAC to change output. What you need to do is to precisely time this event. Timing of everything else doesn't matter. What is this exact event which causes your DAC to change output?
 

Offline apraxophobiaTopic starter

  • Contributor
  • Posts: 12
  • Country: us
Re: interrupt latency on ESP32
« Reply #12 on: July 05, 2024, 02:31:26 pm »
I don't quite see what you mean. Even if I can trigger the DAC with a solid square wave, if there's jitter on the SPI messages themselves, the DAC output won't be a solid square wave, right?

The SPI message doesn't happen all at once, it includes several clock edges which are spread over time. SPI message is not a single event you can time or synchronize.

There must be some event which causes the DAC to change output. What you need to do is to precisely time this event. Timing of everything else doesn't matter. What is this exact event which causes your DAC to change output?


Right- the event triggering the DAC change is the SPI SYNC pin, but this is simultaneous with SPI clock and MOSI. So the problem is the same- I can't get that sync pin (or the other SPI controls) to synchronize with the fast square wave.
 

Offline nctnico

  • Super Contributor
  • ***
  • Posts: 27346
  • Country: nl
    • NCT Developments
Re: interrupt latency on ESP32
« Reply #13 on: July 05, 2024, 03:18:14 pm »
Typically a DAC has a seperate pin to update the outputs. If your DAC doesn't have such a pin, change to a DAC which has it. It will make your life much easier. Alternatively, you could try to make the sync pin low for the majority of the time (using a PWM output) and send the SPI message during this low time. I have not seen SPI devices which have a sync-active to clock active maximum time limit.
There are small lies, big lies and then there is what is on the screen of your oscilloscope.
 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3199
  • Country: ca
Re: interrupt latency on ESP32
« Reply #14 on: July 05, 2024, 03:32:30 pm »
Right- the event triggering the DAC change is the SPI SYNC pin, but this is simultaneous with SPI clock and MOSI. So the problem is the same- I can't get that sync pin (or the other SPI controls) to synchronize with the fast square wave.

Ok. So, your "SPI SYNC" signal goes down, then you transfer data through SPI, then "SPI SYNC" goes up which triggers DAC reload. Is that correct?

Then, instead of "SPI SYNC" from the MCU, connect your 100 kHz input signal (assuming it's 50% duty). Also wire the same signal to MCU. Create an interrupt on the falling edge of the 100 kHz signal. When processing this interrupt, transmit SPI. You have 5 us for this, which is still plenty. And the SPI timing doesn't matter.

The whole thing will work as follows:

On the falling edge of 100 kHz signal the DAC sees the SYNC going down. At the same time, the interrupt happens in your MCU, you start transmitting SPI and quit the interrupt. Some time later, the SPI transmission completes, the DAC receives the data, but the DAC does not update the output and waits until it gets a rising edge of SYNC.

On the rising edge of 100 kHz signal, DAC sees is as SYNC going up and updates the output. The change of the DAC output is thereby synchronized to your 100 kHz signal directly. Hence you don't need to create a copy of it.
« Last Edit: July 05, 2024, 03:34:06 pm by NorthGuy »
 

Offline mianos

  • Contributor
  • Posts: 48
  • Country: au
Re: interrupt latency on ESP32
« Reply #15 on: July 06, 2024, 11:52:07 am »
With the esp idf RTOS running you can't do something jitter free at 100khz in an interrupt or software. Kinda end of story. I tried. I thought the new "Event Task Matrix" in the C6 would help but it only allows GPIOs to be task targets.
The hardware timers and counters do work perfectly, never missing an edge to 10s of Mhz but interrupts have heaps of unavoidable jitter due to higher priority system interrupts.
You could try disabling the wifi. That helps a lot.

There are quite a few github tickets asking about this.

What might be worth a try is using a RP2040 PIO peripheral to do this in real time, outside the CPU core. I got a few boards but have not had time to try.
 

Offline xvr

  • Frequent Contributor
  • **
  • Posts: 371
  • Country: ie
    • LinkedIn
Re: interrupt latency on ESP32
« Reply #16 on: July 06, 2024, 12:10:33 pm »
You could try to use high priority interrupt to reduce jitter. But you have to write it in assembler - C handlers for them not supported.
 

Offline apraxophobiaTopic starter

  • Contributor
  • Posts: 12
  • Country: us
Re: interrupt latency on ESP32
« Reply #17 on: July 08, 2024, 05:15:52 pm »
For anyone still following this:

-Couldn't quite figure out how to get high-level interrupts to work in assembly with the esp32.

-Implemented the same functionality with a teensy 4.0 very, very easily. Don't need wifi/BT for my application, so sticking with this one.

Thanks for all your suggestions!
 

Offline xvr

  • Frequent Contributor
  • **
  • Posts: 371
  • Country: ie
    • LinkedIn
Re: interrupt latency on ESP32
« Reply #18 on: July 08, 2024, 07:19:16 pm »
Quote
-Couldn't quite figure out how to get high-level interrupts to work in assembly with the esp32.

Quote from file esp-idf\components\xtensa\xtensa_vectors.S

Code: [Select]
/*******************************************************************************

HIGH PRIORITY (LEVEL > XCHAL_EXCM_LEVEL) INTERRUPT VECTORS AND HANDLERS

High priority interrupts are by definition those with priorities greater
than XCHAL_EXCM_LEVEL. This includes non-maskable (NMI). High priority
interrupts cannot interact with the RTOS, that is they must save all regs
they use and not call any RTOS function.

A further restriction imposed by the Xtensa windowed architecture is that
high priority interrupts must not modify the stack area even logically
"above" the top of the interrupted stack (they need to provide their
own stack or static save area).

Cadence Design Systems recommends high priority interrupt handlers be coded in assembly
and used for purposes requiring very short service times.

Here are templates for high priority (level 2+) interrupt vectors.
They assume only one interrupt per level to avoid the burden of identifying
which interrupts at this level are pending and enabled. This allows for
minimum latency and avoids having to save/restore a2 in addition to a0.
If more than one interrupt per high priority level is configured, this burden
is on the handler which in any case must provide a way to save and restore
registers it uses without touching the interrupted stack.

Each vector goes at a predetermined location according to the Xtensa
hardware configuration, which is ensured by its placement in a special
section known to the Xtensa linker support package (LSP). It performs
the minimum necessary before jumping to the handler in the .text section.

*******************************************************************************/

/*
Currently only shells for high priority interrupt handlers are provided
here. However a template and example can be found in the Cadence Design Systems tools
documentation: "Microprocessor Programmer's Guide".
*/

#if XCHAL_NUM_INTLEVELS >=2 && XCHAL_EXCM_LEVEL <2 && XCHAL_DEBUGLEVEL !=2

    .begin      literal_prefix .Level2InterruptVector
    .section    .Level2InterruptVector.text, "ax"
    .global     _Level2Vector
    .type       _Level2Vector,[member=46715]function[/member]
    .global     xt_highint2
    .align      4
_Level2Vector:
    wsr     a0, EXCSAVE_2                   /* preserve a0 */
    call0   xt_highint2                     /* load interrupt handler */

    .end        literal_prefix

    .global     xt_highint2
    .weak       xt_highint2
    .set        xt_highint2, _xt_highint2
    .section    .iram1, "ax"
    .type       _xt_highint2,[member=46715]function[/member]
    .align      4
_xt_highint2:

    #ifdef XT_INTEXC_HOOKS
    /* Call interrupt hook if present to (pre)handle interrupts. */
    movi    a0, _xt_intexc_hooks
    l32i    a0, a0, 2<<2
    beqz    a0, 1f
.Ln_xt_highint2_call_hook:
    callx0  a0                              /* must NOT disturb stack! */
1:
    #endif

    /* USER_EDIT:
    ADD HIGH PRIORITY LEVEL 2 INTERRUPT HANDLER CODE HERE.
    */

    .align  4
.L_xt_highint2_exit:
    rsr     a0, EXCSAVE_2                   /* restore a0 */
    rfi     2
And the same for levels up to 6
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf