PRU timing with __delay_cycles()

I seem to have discovered an anomaly with the __delay_cycles command trying to get precise timing on the PRU’s. Jason asked that I post my results for all to see.

My desire was to create a steady clock pulse on P8.11 and then set P8.12 low for just a single low cycle of P8.11. Then P8.12 was to go back high.

In using the __delay_cycles command, the timing on P8.12 was extremely random and would bounce back up to 3.3 volts immediately instead of staying low for a clock cycle. This can be seen in the following images.image.png
image.png

image.png

However, replacing the __delay_cycles command with a “for” loop created output seen in the following image which is exactly what I needed.
image.png

I have no idea why the __delay_cycles is working this way. The really strange thing is if I cause P8.12 to follow P8.11, i.e. to rise when P8.11 rises and fall when P8.11 falls, then the output is exactly as expected. In fact, I can even make P8.12 to mirror P8.11 by causing it to fall when P8.11 rises and rise when P8.11 falls but not to stay down for just one low cycle of P8.11. Another interesting note is that the assembly code generated by the C compiler shows the delays exactly as one would expect but the output is still random. At any rate, the output pulses are as desired by using the “for” loops.

Here is the pertinent code. It contains both the working and non-working code. The non-working code is commented out and replaced with the “for” loops that do work.

The variables that set __R30 are hex values pertaining to the appropriate bits for pins P8.11 and P8.12. For example, gpioClockHighStartLow = 0x8000, gpioClockLowStartLow = 0x0000, gpioClockHighStartHigh = 0xC0000 and gpioClockLowStartHigh = 0x4000.

while(1){
for (resetCount = 0; resetCount < 100; resetCount++){
if (sensorState == STARTUP){ // Sensor is in startup mode
__R30 = gpioClockHighStartLow; //Set Clock High and Start Low
for (delayCount = 0; delayCount < 1000; delayCount++){
x++;
}
// __delay_cycles(1000);
__R30 = gpioClockLowStartLow; //Set Clock Low and Start Low
// __delay_cycles(1000);
for (delayCount = 0; delayCount < 1000; delayCount++){
x++;
}
sensorState = RUNNING; // Indicate sensor is in running state
}

__R30 = gpioClockHighStartHigh; //Set Clock High and Start High
// __delay_cycles(1000);
for (delayCount = 0; delayCount < 1000; delayCount++){
x++;
}
__R30 = gpioClockLowStartHigh; //Set Clock Low and Start High
// __delay_cycles(1000);
for (delayCount = 0; delayCount < 1000; delayCount++){
x++;
}
}

resetCount = 0;
sensorState = STARTUP;

}

If anyone can shed any light on what I’m seeing with the __delay_cycles command I would be interested in hearing about it. Thanks.

Mike Pitman

So it’s been a while since I’ve used __delay_cycles() in any code for any hardware. But the general reason why I’ve not used it is that the implementation from one piece of hardware to another is, or can be different, and it’s a “software way” to time things. Which is to say, it can sometimes be wildly inaccurate. From memory it’s also a blocking wait. Which is a way for me to say I’m pretty sure that’s how it is, but my memory is not always the best.

So in the case of bare metal hardware, I almost always look to using a hardware timer that “throws” an interrupt when the timer rolls over.

In other cases, I use usleep() when I need something to wait a bit for the hardware to catch up with the code. But from this gist of your situation here, that probably wont work for you.

I did see someone post a bit of PRU code not too long ago that used hardware timers in their code, for timing, Which I thought was interesting. So I can not tell you specifically why __delay_cycles() is not working in your case, or why it’s behaving the way it is. But I can say that it’s generally best to avoid using it, and there should be another option as far as what I saw posted not long ago on these groups.

As to specifics, sorry, I can not help, I’m not good enough with the PRU and some of the other hardware on this platform. But yo ucould start by reading the implementation code for __delay_cycles() and maybe get a hint as to why it’s acting the way it is.

image.png

image.png

image.png

image.png

Program the PRUs in assembly, especially if you want single cycle
accuracy.

...or at least provide us the assembly listing for the C code, as well
as the entire C program.  There are lots of things that can be
happening behind the scenes since you're using C.

On the MPS-430, for example, the __delay_cycles() routine actually
turns off most of the processor (to save power) and wakes it back up
after the delay expires, which could obviously cause unexpected
behavior if you didn't realize that's what it was doing.  I don't use
C compiler so I'm not sure how this routine is implemented for the 

PRU, but it probably isn’t a simple noop loop like you might expect.


1 cycle pulse is 5ns. What instrument are you using? Just a guess, but won’t you need something on the order of 1GHz bandwidth to capture that accurately?

I used the delay cycle intrinsic successfully to implement a SPI bus. However, I never attempted a 1 cycle pulse.
Other than that, the function worked perfectly.

Regards,
Greg

On Thu, 25 May 2017 11:59:24 -0700, William Hermans
<yyrkoon@gmail.com> declaimed the following:

So it's been a while since I've used __delay_cycles() in any code for any
hardware. But the general reason why I've not used it is that the
implementation from one piece of hardware to another is, or can be
different, and it's a "software way" to time things. Which is to say, it
can sometimes be wildly inaccurate. From memory it's also a blocking wait.

"intrinsic" of CCS which is supposed to insert an instruction sequence
which takes the specified delay to execute. A 1-cycle delay is probably
whatever a NOP instruction is... Longer delays may be a loop of NOPs.

Program the PRUs in assembly, especially if you want single cycle
accuracy.

...or at least provide us the assembly listing for the C code, as well
as the entire C program. There are lots of things that can be
happening behind the scenes since you're using C.

On the MPS-430, for example, the __delay_cycles() routine actually
turns off most of the processor (to save power) and wakes it back up
after the delay expires, which could obviously cause unexpected
behavior if you didn't realize that's what it was doing. I don't use
C compiler so I'm not sure how this routine is implemented for the
PRU, but it probably isn't a simple noop loop like you might expect.