Timing blocks of code with waitms in SPIN2 — Parallax Forums

# Timing blocks of code with waitms in SPIN2

Posts: 43

I'm using a P2 Eval board, revision C,

I'm expecting to see:

` set command mode.started: 30_000_000 microseconds

something just over 30 seconds

` set command mode.started: 1 microseconds

Code taken from the P2 Spin manual

{Object_Title_and_Purpose}

CON
_clkfreq = 250_000_000

_txpin = 16 + 6

PUB main() | time
time := getct()
pinlow(_txpin)
waitms(30_000)
pinfloat(_txpin)
time := getct() - time
time := muldiv64(time, 1_000_000, clkfreq)
debug("set command mode.started: ", sdec(time), " microseconds")

• Posts: 5,958

At 250 MHz, the number of ticks elapsed will exceed 32 bits in a bit over 16 seconds. So getct() alone will not be useful for timing intervals longer than that.

• Posts: 8,964
edited 2022-11-24 19:11

In addition to Eric's point about the getct() delta, the waitms() instruction is limited by the clock speed. This is from an article I wrote for Nuts & Volts magazine.
(https://www.nutsvolts.com/magazine/article/an-introduction-to-the-parallax-propeller-2)

The formula for determining the maximum delay is: 2^31/clock frequency. If you’re running a 200 MHz clock as I do, the maximum delay
is about 10.74 seconds; that’s 10,737 milliseconds, or 10,737,418 microseconds. Longer delays can be created with loops or through the
use of delta timing via the system counter. The P2 has a 64-bit system counter. To give you an idea of the magnitude of 64 bits, running that
counter at 200 MHz would take 2,924.7 years to roll over! Like the P1, the P2 is a 32-bit machine, so it’s not easy to deal with the system
counter directly. The good news is we don’t have to.

For a 250MHz clock, the maximum delay will be about 8.589 seconds, or 8_589 milliseconds, or 8_589_934 microseconds (yes, I tested)

```  t := getct()
waitus(8_589_934)
t := getct()-t-40
term.dec(t/US_001)
```

The -40 accounts for the getct() instruction time. I have timing constants at the top of my programs that look like this:

```con { timing }

CLK_FREQ = 250_000_000                                        ' system freq as a constant
MS_001   = CLK_FREQ / 1_000                                   ' ticks in 1ms
US_001   = CLK_FREQ / 1_000_000                               ' ticks in 1us

_clkfreq = CLK_FREQ                                           ' set system clock
```
• Posts: 5,958

@JonnyMac @cgracey : Does the standard PNut waitms() not handle long waits? It's not hard to make it work right up to 2 billion milliseconds (~23 days) by just waiting 1 second at a time, with the last one being a fractional second. With a bit of care about signed/unsigned arithmetic that can be doubled.

• Posts: 8,964

The current interpreter gets the clock frequency, divides by 1000 (waitms) or 1000000 (waitus) and then multiplies that by the user parameter for use by waitct. it's not ideal, but it's what we have.

```' WAITUS(us)
' WAITMS(ms)
'
waitus_         getct   w                       'get ct now to minimize error

rdlong  y,#@clkfreq_hub         'get clock frequency

rep     @.stall,#1              'use REP to stall interrupts to protect cordic operation
qmul    y,x                     'multiply clock frequency by us/ms
getqx   x
getqy   y
.stall
cmp     pa,#bc_waitus   wz      'us or ms time unit?
mov     z,##1000
if_z    mul     z,z

rep     @.stall2,#1             'use REP to stall interrupts to protect cordic operation
setq    y                       'divide by time unit
qdiv    x,z
getqx   x
.stall2

jmp     #pwct                   'do WAITCT
```
• Posts: 43

What do you think?

sample code of my stopwatch object. call sw.reset at the beginning of the code you want tested; use sw.elapsed at the end of the code you want tested...

```  sw.reset()
pinlow(Txpin)
waitms(500)
pinfloat(Txpin)
time_low, time_high := sw.elapsed()
```

The stop watch object

```VAR
long _low
long _high

PUB reset()| low, high
' Reset current start time
org
getct high wc
getct low
end
_low, _high := low, high

PUB elapsed() : low, high | c_low, c_high
' Get elapsed time
c_low, c_high := _low, _high
org
getct high wc
getct low
sub   low, c_low wcz
subx  high, c_high
end

PUB elapsedms() : low, high | c_low, c_high
' Get elapsed time in milliseconds (ms)
c_low, c_high := _low, _high
org
getct high wc
getct low
sub low, c_low wcz
subx high, c_high
' TODO: convert to millisecond (ms)
end

PUB elapsedmu() : low, high | c_low, c_high
' Get elapsed time in microseconds (mu)
c_low, c_high := _low, _high
org
getct high wc
getct low
sub low, c_low wcz
subx high, c_high
' TODO: convert to microseconds (mu)
end
```
• Posts: 8,964

I wrote similar code a couple years ago -- before getms() was available in Spin2. Seeing your code I got mine back out and did a clean-up, especially as I have a new project coming that needs long-duration timing. What you call reset(), I call start(), and I allow an offset. In the P1 version of this code I have found it convenient to set a negative time duration and then while checking look for the timer to be 0 or higher. This object has delay routines that will break the ~10 second (at 200MHz) limit of the waitms() and waitus() instructions.

I run my P2 projects at 200MHz, hence the constants in the program that make calculating duration easy.

You might find some of this useful.