Shop Learn P1 Docs P2 Docs
Timing blocks of code with waitms in SPIN2 — Parallax Forums

Timing blocks of code with waitms in SPIN2

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

instead, I'm seeing

` set command mode.started: 1 microseconds

Code taken from the P2 Spin manual


_clkfreq = 250_000_000

_txpin = 16 + 6

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


  • ersmithersmith Posts: 5,515

    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.

  • JonnyMacJonnyMac Posts: 8,314
    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.

    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()
      t := getct()-t-40

    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
  • ersmithersmith Posts: 5,515

    @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.

  • JonnyMacJonnyMac Posts: 8,314

    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
                    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
                    add     x,w                     'add ct
                    jmp     #pwct                   'do WAITCT
  • 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...

      time_low, time_high := sw.elapsed()

    The stop watch object

            long _low
            long _high
    PUB reset()| low, high
    ' Reset current start time
        getct high wc
        getct low
      _low, _high := low, high
    PUB elapsed() : low, high | c_low, c_high
    ' Get elapsed time
      c_low, c_high := _low, _high
        getct high wc
        getct low
        sub   low, c_low wcz
        subx  high, c_high
    PUB elapsedms() : low, high | c_low, c_high
    ' Get elapsed time in milliseconds (ms)
      c_low, c_high := _low, _high
        getct high wc
        getct low
        sub low, c_low wcz
        subx high, c_high
        ' TODO: convert to millisecond (ms)
    PUB elapsedmu() : low, high | c_low, c_high
    ' Get elapsed time in microseconds (mu)
      c_low, c_high := _low, _high
        getct high wc
        getct low
        sub low, c_low wcz
        subx high, c_high
        ' TODO: convert to microseconds (mu)
  • JonnyMacJonnyMac Posts: 8,314

    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.

Sign In or Register to comment.