sdbbs
Posts: 43
Joined: Mon Apr 04, 2022 7:20 am

Nanosecond delay for RP2040/Pico?

Tue May 03, 2022 10:03 am

Occasionally, I need to "hold" GPIO outputs for a certain time.

If this "hold" time is in the range of microseconds, I've found that using the Pico SDK like this works great for me:

Code: Select all

...
// need to hold MY_PIN high for 200us here:

absolute_time_t dir_pin_timeout_time = make_timeout_time_us(200); // to hold for 200 us
gpio_put(MY_PIN, 1);
sleep_until(dir_pin_timeout_time);

gpio_put(MY_PIN, 0);
...
The problem is now, that I need to "hold" a digital out for about 400 ns. Unfortunately, when I look in https://raspberrypi.github.io/pico-sdk-doxygen/group__timestamp.html, I can only see microsecond or millisecond resolution, no nanosecond resolution.

Note that I'm running the Pico at 133 MHz clock frequency, so a clock period would be (ideally) 1/133e6 = 7.5188 ns; which means a duration of 400 ns takes about 400/7.5188 = 53.2 clock ticks.

After some reading, I've seen:
My guess is, I could use systick, in the same spirit as in my starting example, somewhat like this:

Code: Select all

...
// need to hold MY_PIN high for 200us here:

systick_hw->rvr = 53; // start value? or "Value to load into the SysTick Current Value Register when the counter reaches 0"
systick_hw->csr = 0x5; // set SysTick clock source, and Enable (start timer)
gpio_put(MY_PIN, 1);
while(systick_hw->cvr > 0) { ; } // delay loop

gpio_put(MY_PIN, 0);
...
... the problem is:
  • I'm not sure if systick_hw->rvr explicitly sets the "start" value of the timer, once it is enabled - or is it just the "wrap" value, to which the (downcounting) counter wraps when it reaches 0 (and the timer, once started, starts from a random value)
  • I'm not sure if systick_hw can be set up in "one-shot" mode (that is, it stops once it reaches 0) -- if it cannot, and it always wraps, then the condition while(systick_hw->cvr > 0) is definitely not enough to check when to exit the delay loop
  • I'm not sure if I must have the function, performing the above "hold" delay, defined as __not_in_flash_func so it runs from RAM - or can it run from flash, even for these types of short delays?

So, my question is - what are my options, to achieve a hold time (delay) at nanosecond precision with Pico/RP2040 (preferably, not using the PIO)?

Lobo-T
Posts: 132
Joined: Fri Jan 22, 2021 10:52 am

Re: Nanosecond delay for RP2040/Pico?

Tue May 03, 2022 10:36 am

Something like this? (not tested):

Code: Select all

gpio_put(MY_PIN, 1);

//53 CPU cycles
asm volatile(
	"mov  r0, #17\n"    		// 1 cycle
	"loop1: subs  r0, r0, #1\n"	// 1 cycle
	"bne   loop1\n"          	// 2 cycles if loop taken, 1 if not
);

gpio_put(MY_PIN, 0);

PiGraham
Posts: 5400
Joined: Fri Jun 07, 2013 12:37 pm
Location: Waterlooville

Re: Nanosecond delay for RP2040/Pico?

Tue May 03, 2022 10:43 am

Can you just busy wait in a for loop ?

slimhazard
Posts: 307
Joined: Sat Apr 03, 2021 8:47 pm

Re: Nanosecond delay for RP2040/Pico?

Tue May 03, 2022 11:11 am

The systick_hw->csr register has a COUNTFLAG bit that returns 1 if systick has counted down to 0 since the last time it was read, 0 otherwise. I've never tried to use it, but maybe you could attempt something like this:

Code: Select all

#include "hardware/regs/m0plus.h"

gpio_put(MY_PIN, 1);
while(!(systick_hw->csr & M0PLUS_SYST_CSR_COUNTFLAG_BITS)) {  }

lurk101
Posts: 2597
Joined: Mon Jan 27, 2020 2:35 pm
Location: Cumming, GA (US)

Re: Nanosecond delay for RP2040/Pico?

Tue May 03, 2022 12:11 pm

slimhazard wrote:
Tue May 03, 2022 11:11 am
The systick_hw->csr register has a COUNTFLAG bit that returns 1 if systick has counted down to 0 since the last time it was read, 0 otherwise. I've never tried to use it, but maybe you could attempt something like this:

Code: Select all

#include "hardware/regs/m0plus.h"

gpio_put(MY_PIN, 1);
while(!(systick_hw->csr & M0PLUS_SYST_CSR_COUNTFLAG_BITS)) {  }
It seems SYSTICK runs at 1MHz.

Better resolution could be achieved with a PWM counter which can be configured to run at 125MHz.

slimhazard
Posts: 307
Joined: Sat Apr 03, 2021 8:47 pm

Re: Nanosecond delay for RP2040/Pico?

Tue May 03, 2022 1:45 pm

lurk101 wrote:
Tue May 03, 2022 12:11 pm
It seems SYSTICK runs at 1MHz.
If you set the CLKSOURCE bit in systick_hw->csr (as the OP did in the code snippet), SYSTICK runs at processor speed -- same as clk_sys, so it's counting down processor cycles.

alastairpatrick
Posts: 753
Joined: Fri Apr 22, 2022 1:39 am
Location: USA

Re: Nanosecond delay for RP2040/Pico?

Tue May 03, 2022 2:30 pm

You could use one of the PWMs. They don't have a built-in single shot mode but you could get the same effect by switching to 0% duty cycle immediately after starting the PWM. Because of the double buffering feature, the 0% duty cycle won't take effect until TOP wraps around.

lurk101
Posts: 2597
Joined: Mon Jan 27, 2020 2:35 pm
Location: Cumming, GA (US)

Re: Nanosecond delay for RP2040/Pico?

Tue May 03, 2022 4:50 pm

slimhazard wrote:
Tue May 03, 2022 1:45 pm
lurk101 wrote:
Tue May 03, 2022 12:11 pm
It seems SYSTICK runs at 1MHz.
If you set the CLKSOURCE bit in systick_hw->csr (as the OP did in the code snippet), SYSTICK runs at processor speed -- same as clk_sys, so it's counting down processor cycles.
Hadn't noticed. But that's good to know.

dthacher
Posts: 1025
Joined: Sun Jun 06, 2021 12:07 am

Re: Nanosecond delay for RP2040/Pico?

Tue May 03, 2022 7:31 pm

I would avoid any sys_tick, timers, interrupts, etc here. Too much jitter. You are probably looking at handwritten assembly here. Couple ways to do this. You need to take this from the compiler and the hardware as much as possible. You also need to use the SDK default clock of 125MHz, which is 8nS per cycle. This will mean you need to do something for 50 cycles.

This is kind of bad practice. However, many processors are not very good at nS resolution. This deadline can be realized by the RP2040. This code will need to be updated by hand and this will represent a dependency. This is the nature of the beast here. There are other options, but these are likely too expensive.

WestfW
Posts: 271
Joined: Tue Nov 01, 2011 9:56 pm

Re: Nanosecond delay for RP2040/Pico?

Tue May 03, 2022 9:36 pm

I have a couple of cycle-counting delays that use SysTick here: https://github.com/WestfW/Duino-hacks/b ... ay.ino#L53
There are a couple of notes:
  • SysTick must be running at the CPU Clock rate.
  • The delay must be shorter than the systick reload value. (reload is frequently set to get a 1ms interrupt.)
  • I "guess" that the delay is probably accurate to something like +/- 20 cycles. That's swell for the "5 to 500us" area the code was originally aimed at, and is probably OK for 400ns, but it may take some tuning.
The motivation was that previous implementations that did counted instruction loops were wildly inaccurate due to (apparently) caching issues (that was on a SAMD51, with internal flash.) You can solve that using functions in RAM (at least, until that starts being cached as well - does the rp2040 "striped" RAM have an effect?) but ... you're adding more complexity. (and probably, a cache miss from the QSPI flash can probably create a pretty large error even on a call to RAM, on the 400ns scale. Although that could affect the systick-based function as well.)

I'd bet that you could use the PIO to generate accurate pulses. Write to a FIFO, get a pulse on some pin. ought to be trivial for constant pulse width and constant pin. A bit of a waste of capability, perhaps, but if you're not using them for anything else, anyway...

dthacher
Posts: 1025
Joined: Sun Jun 06, 2021 12:07 am

Re: Nanosecond delay for RP2040/Pico?

Tue May 03, 2022 10:03 pm

@WestfW fair point and this is kind of my point also. The RAM is stripped on the main blocks. The smaller blocks are reserved for the CPU. These will not have arbitration most of the time. If you put the code there, the CPU should always hit. PIO can be a bit of a hassle and still have jitter. In this case you would want the whole program in PIO not just a delay. There is not enough clearance for the jitter.

Use the RP2040's SIO and reserved RAM bank. This should allow very low jitter from memory and system bus. The logic will have to be handwritten and tightly coupled to the RP2040. Unless you can move the whole problem to hardware there is a chance of error. The Cortex-M0+ in the RP2040 is designed to be a self-contained module like PIO for IO operations. If used correctly. This may require linker voodoo.

WestfW
Posts: 271
Joined: Tue Nov 01, 2011 9:56 pm

Re: Nanosecond delay for RP2040/Pico?

Tue May 03, 2022 10:46 pm

In this case you would want the whole program in PIO not just a delay.
Maybe, maybe not... There's a lot of desirability to only optimizing the bits of your code that actually need optimizing.
Implementing a highly accurate "delay for n Nanoseconds" is relatively difficult. Implementing "set this pin state and delay for at least 400ns" is comparatively simple. (oh! You could probably implement "set pin state with hold time" and have it not actually delay if the hold time had already expired...)

Original post:
The problem is now, that I need to "hold" a digital out for about 400 ns.
"Hold" times are specified as minimums. So a 1us delay probably works fine for a 400ns requirement, unless you really need the speed. Unfortunately, they're also usually specified WRT actual "signal level changes", which may not be happening externally exactly when you think they are, depending on drive strengths, wiring, pin capacitance, and who knows what else.

OP did also say:
(preferably, not using the PIO)
but they didn't say why... Usually, I assume people are just worried about the complexity of implementing it. If someone else implements it, then it becomes easy to use (the same can be said about delay() functions in general!)

mschnell
Posts: 428
Joined: Wed Jul 28, 2021 10:33 am
Location: Krefeld, Germany

Re: Nanosecond delay for RP2040/Pico?

Thu May 05, 2022 10:09 am

If you want to be a "Pico Guy" definitively do a PIO program.
Same needs to include as well the timing as all I/O actions that are associated with that timing.
This porvided there will be no jitter.
IMHO, a main reason for many "critical" PICO / RP 2020 applications is that it provides the PIOs / State Machines.
-Michael

TomDD
Posts: 13
Joined: Sun Aug 28, 2022 8:05 pm

Re: Nanosecond delay for RP2040/Pico?

Sun Aug 28, 2022 8:49 pm

Been looking for something similar as porting some Arduino code over which uses the _delay_us function. From experimenting I've found the busy_wait_at_least_cycles function works well. At default clock (125MHz) this should give 8ns per cycle resolution although in reality there is some overhead. I've wrote a _delay_us equivalent which you can pass in a microsecond number with nanoseconds as the decimal e.g 1.25, and it will wait for very close to that time.

Code: Select all

void _delay_us(double __us)
{
    uint32_t __count=(uint32_t)(__us/0.008)-3; // 8ns per cycle for 125MHz, from experimentation remove 3cycles for overhead 
    busy_wait_at_least_cycles(__count);
}

kilograham
Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator
Posts: 1540
Joined: Fri Apr 12, 2019 11:00 am
Location: austin tx

Re: Nanosecond delay for RP2040/Pico?

Mon Aug 29, 2022 1:32 am

note that all bets are off once you start using double! each double operation is gonna take you ~60-100+ns!

matherp
Posts: 309
Joined: Tue May 02, 2017 10:54 am

Re: Nanosecond delay for RP2040/Pico?

Mon Aug 29, 2022 9:51 am

Set up systick to count full 24-bits

Code: Select all

    systick_hw->csr = 0x5;
    systick_hw->rvr = 0x00FFFFFF;
Then to do a small delay calculate how many ticks you need and subtract this from 2^24 but add a setup time fudge factor

Code: Select all

    		a=16777215 + setuptime- calculated_ticks ;
then

Code: Select all

#define shortpause(a){systick_hw->cvr=0;while(systick_hw->cvr>a){};}
works perfectly for me.

setuptime can be tuned by toggling a pin but 12 ticks is a good starting point

WestfW
Posts: 271
Joined: Tue Nov 01, 2011 9:56 pm

Re: Nanosecond delay for RP2040/Pico?

Tue Aug 30, 2022 12:07 am

Set up systick to count full 24-bits
I have some functions for using systick for "N cycle" delays that work even if you're using sysTick for some other purpose. As long as the reload value you're using is greater than the number of cycles you want to delay,
https://github.com/WestfW/Duino-hacks/b ... ay.ino#L54

Return to “SDK”