How to delay exact CPU cycles?

yuwenpeng
Posts: 30
Joined: Tue Apr 12, 2022 6:25 am

How to delay exact CPU cycles?

Postby yuwenpeng » Tue Jun 28, 2022 9:19 pm

In the high-priority interrupt I tried to delay the command by exact CPU cycles. But it seems the best accuracy of CPU delay I can get is 7 CPU cycles.
I have some code that requires low interrupt jitter. Even with level-5 interrupt jitter still exists, and the jitter can be 23 CPU cycles caused by some uncontrollable WiFi task. To reduce the jitter, I set the timer 100 CPU cycles earlier than the objective time, then delay the commands in the handler until the real objective time arrives. (The same concept can be found in some STM32 chips to reduce/avoid jitter by introducing some delay)
In the interrupt handler, I compare the ccount with the objective time in a loop until the objective time is current. However, each iteration of the loop takes 7 CPU cycles, which limits the accuracy of the delay. I'm wondering if there is any other command or workaround to further improve the accuracy.
An example of the logic is shown below. command 'rsr' takes 1 cpu cycle, and branch command 'blt' takes 6 CPU cycles. When the aforementioned uncontrollable WiFi task run, one iteration of the loop could take up to 12 CPU cycles. Is that normal?

Code: Select all

//level-5 interrupt handler

    movi   a6,	   100					//The objective time
LoopDelay:    
    rsr     a5,         ccount		
    blt     a5,         a6,         LoopDelay         //if current time < timer setting, loop back to wait.

    //other commands that require low jitter.
    //......


Best,
Wayne

ESP_Sprite
Posts: 9711
Joined: Thu Nov 26, 2015 4:08 am

Re: How to delay exact CPU cycles?

Postby ESP_Sprite » Wed Jun 29, 2022 2:16 am

There's something called 'zero-overhead loop' on Xtensa, and the ESP32 supports it. Possibly using a single NOP in a loop like that can help you. (As in: read ccount, figure out how long to delay, set zero-overhead loop iterations to that, loop over one NOP)

yuwenpeng
Posts: 30
Joined: Tue Apr 12, 2022 6:25 am

Re: How to delay exact CPU cycles?

Postby yuwenpeng » Wed Jun 29, 2022 5:02 am

I believe you are referring instruction loop, loopgtz or loopnez. They are zero-overhead loop instructions. I tried these 3 instructions this afternoon, but I can't get them working. One of the example code is attached below, I counted how many loops are executed, but it seems only executed 1 time. I also tried loopgtz and loopnez many times with all the ways I can think of, all of them only executed 1 time, as if the loop instruction didn't exist at all. I also tried to manually write the LCount, it didn't work either. So I have 2 questions want to confirm:
1. Does ESP32 implement these 3 loop instructions?
2. Is the loop instruction disabled in interrupt handler? I don't fully understand section 4.3.2.3 of the xtensa instruction set manual, as attached below. Is it saying the loop instructions are disabled in interrupt handler? Or can we manually set PS.EXCM to zero to enable it? I tried but didn't work, it stuck after wsr 1 to PS.EXCM...
Can't find many examples of using the xtensa loop instructions in interrupt handler... Did I make anything wrong?

Code: Select all

    movi    a5,     100
    loop    a5,     EndDelay
        nop
        //or some other instruction to count how many loops are executed.
EndDelay:
    //other instructions...

or

Code: Select all

    movi        a6,     200             //objective time
    rsr         a5,     ccount          //read current time
    sub         a5,     a6,     a5
    loopgtz     a5,     EndDelay        //if a5>0 loop; else jump to EndDelay.
        rsr     a5,     ccount          
        sub     a5,     a6,     a5      //update a5 = objective time - ccount
EndDelay:
    //other instructions...
1.png
1.png (9.49 KiB) Viewed 4616 times
2.PNG
2.PNG (9.39 KiB) Viewed 4616 times
3.PNG
3.PNG (6.95 KiB) Viewed 4616 times

ESP_Sprite
Posts: 9711
Joined: Thu Nov 26, 2015 4:08 am

Re: How to delay exact CPU cycles?

Postby ESP_Sprite » Thu Jun 30, 2022 3:23 am

Sorry, I never practically did something with loop instructions...

Another thing I thought of: perhaps a computed jump can help you here? Pseudo-code because my Xtensa assembly is rusty:

Code: Select all

	mov reg, 32 ;amount of nops in table
	sub reg, cycles_to_delay
	mul reg, sizeof(nop_instruction) ;iirc 2 or 3 bytes
	add reg, nop_start
	jmp [reg]
nop_start:
	nop
	nop
	[.. total of 32x nop]
	nop
	nop
end: //we should be precisely on time here
To make the nop table small enough, you can 'burn' cycles by doing a normal loop before that.

yuwenpeng
Posts: 30
Joined: Tue Apr 12, 2022 6:25 am

Re: How to delay exact CPU cycles?

Postby yuwenpeng » Thu Jun 30, 2022 4:43 am

Thank you Sprite, that's a brilliant idea! I'll try and let you know if it works

Best,
Wenpeng

yuwenpeng
Posts: 30
Joined: Tue Apr 12, 2022 6:25 am

Re: How to delay exact CPU cycles?

Postby yuwenpeng » Thu Jun 30, 2022 6:04 am

The method works as expected, but didn't resolve the problem.
It does execute a various number of nop. The problem is jitter happens when it executes the series of nop instructions(less than 32), making the final time not accurate. The time error of the method is attached below. Every 1.33 seconds there is a 0.3s window when the jitter is very large. A series of nop instructions is a kind of open-loop control without any feedback. I also attached the branch loop, which is a control with feedback, and the result is a little bit better. Similarly, if loopgtz worked in the interrupt handler, I believe it will be better than loop.
Do you have any idea what could cause these 1.33s periodic windows? Is it possible to temporarily disable all other higher level interrupts or tasks in the interrupt handler, like the taskENTER_CRITICAL of FreeRTOS?

Result of the method of a series of NOP instructions:
jump .PNG
jump .PNG (76.56 KiB) Viewed 4480 times
Result of conditional branch:
branch.PNG
branch.PNG (86.14 KiB) Viewed 4480 times

Who is online

Users browsing this forum: Bing [Bot] and 73 guests