cancel
Showing results for 
Search instead for 
Did you mean: 

How to implement a few cycles delay macro or function

CTabo.1
Senior

Hello,
I need to implement delay macro, lasting just a few cycles.

As the delay is so small I would not like to use TIMs o DWT.

On Texas Instruments DSPs, I was used to implement this nanoseconds delays, using NOP() instructions.
Unfortunately, as also written in STM32 Programming Manual this is not a good pattern on Cortex processors.

On a previous post, related to another issue, I was suggested to use Data Memory Barrier (DMB), Data Synchronization Barrier (DSB), and Instruction Synchronization Barrier (ISB) instructions, instead of NOPs.

How can I use this instructions, to implement a few cycles delay macro?

Regards,
Carlo

1 ACCEPTED SOLUTION

Accepted Solutions

@CTabo.1 wrote:

I need to read the MISO signal to verify if the device access has been succesful.


Maybe just read the status twice - ignore the 1st reading ... ?

:thinking_face:

View solution in original post

12 REPLIES 12
TDK
Guru

There's not really a way to do this on Cortex-M33. The processor is more complicated than X operation always takes Y cycles. If you can accept maybe 5-10 cycles of jitter, you can use a loop around DWT->CYCCNT.

Not that just because an operation complete (e.g. writing 1 to an GPIO register controlling an LED) doesn't mean the pin for that LED turns on at exactly that time. There are pipeline delays that are not within your control. Small delays, but significant in the context of "a few cycles".

More discussion here:

https://community.st.com/t5/stm32-mcus-products/cortex-m33-instruction-cycle-counts/td-p/123198

 

If you feel a post has answered your question, please click "Accept as Solution".

@CTabo.1 wrote:

I need to implement delay macro, lasting just a few cycles.


It might help if you explain why you feel the need to do that?

There may be other approaches ...

Hello,
this is the situation.

I am developing a driver to handle a device connencted via SPI to the MCU (master).
At the end of each SPI cycle, after CS# deassertion, I need to read the MISO signal to verify if the device access has been succesful.

The device datasheet indicates a minimum time of 15 ns to wait, from CS# deassertion, before reading the MISO signal.
In the past, on TI DSPs, I was used to wait this little time, simply adding some NOP() instructions.

Regards,
Carlo

 

You can call __NOP(), N times in a sequence or in a for-loop (but the for-loop itself is already some __NOPs).

BTW: 15ns between CS# and start "reading MISO" - is "nothing" 15ns is 66.6 MHz. Any C-code instruction line might be result in 4 assembly code instructions. Assume a core clock of 200 MHz. So, 4 instructions before the MISO "reading" will really start: it is already 20 ns!

You code, I am very sure, is never so fast, that after toggling a GPIO for CS# and starting to read MISO would be faster as what you chip "requests" to do.
BTW: "reading MISO" is done by the SPI HW, not via FW/SW.
Imagine, if you use the HAL drivers: the overhead is so huge, so many instructions. And even with SPI HW CS# generated - I have never seen that the SPI peripheral is so fast to generate CS# shorter as 15ns before MISO "is read" (actually sampled with SCLK signal).

BTW: what do you mean by "reading MISO signal"?
The MISO is sampled with the Master provided SCLK signal. If this signal is slow, e.g. 1 MHz, and you use a SPI mode where the second edge of SCLK is used - you have plenty of time for "sampling" (reading) the MISO signal (done anyway by the MCU HW): 1 MHz is 1 micro-second.
The FW will just read the Data Receiver (DR) register, a FIFO, but not the MISO signal itself.

So, if you think, you would "violate" the chip spec., with this 15ns "delay" needed: just slow down the clock for SPI.
(15ns would be a SPI SCLK speed of 66.6 MHz - do you want to run so fast?).

I think, your 15ns "issue" is not really an issue, you can forget/skip this requirement: your FW and the MCU HW on SPI peripheral will never be so fast that you could hit this issue really. Even no __NOP()'s needed.

If you do not believe it: use a scope and see that the duration between CS# (asserted) and the start of SCLK (the first clock edge, which clock edge is used depends on the SPI mode) is "so huge". No worries about this "timing requirement" (MCU is not so fast in order to create a violation here, even any C-code FW is not so fast...).

 


@tjaekel wrote:

BTW: "reading MISO" is done by the SPI HW, not via FW/SW.


As far as the actual SPI transfer itself is concerned, yes - but that's not what the OP is talking about.

The OP is talking about an separate requirement of the particular Slave - outside the SPI transfer - so that does have to be done in FW, apart from the SPI hardware.

Here's another example:

https://community.st.com/t5/stm32-mcus-products/stm32-spi-with-cc1125-ti/td-p/585088

 

LCE
Principal

I would simply use something like:

 

uint32_t u32WaitCnt = 0;
while( (MISO_pin == x) && (u32WaitCnt++ < y) );
...

 

Set x to the pin level you would like to see,

set y depending on CPU clock.

I agree to most what Tjaekel said, so a small value < 10 for y is probably good enough. 

Or even the incrementing of the variable...

CTabo.1
Senior

Hello and thank you all.

As @Andrew Neil  said, the delay must be done in FW, because it is outside the SPI transfer.

Also, as @TDK  said, I got that I cannot implement a "few cycle" accurate delay function, because of processor complexity.

So, in the end, I will use an approximative delay function, that surely guarantees a delay much longer of 15 ns, like this:

static inline delay(uint8_t n)
{
volatile uint8_t i;
while (i++ < n);
}

 

Regards,
Carlo


@CTabo.1 wrote:

I need to read the MISO signal to verify if the device access has been succesful.


Maybe just read the status twice - ignore the 1st reading ... ?

:thinking_face:

Yes, it is a better and more accurate solution.
However it may be necessary to encapsulate the two consecutive readings instructions between #pragma directives to disable any optimizations.

 

#pragma GCC push_options
#pragma GCC optimize ("-O0")
sts = LL_GPIO_IsInputPinSet(PORT, PIN);
sts = LL_GPIO_IsInputPinSet(PORT, PIN);
#pragma GCC pop_options

 

Thank you,
Carlo