Shop OBEX P1 Docs P2 Docs Learn Events
Non-deterministic PASM loops ? — Parallax Forums

Non-deterministic PASM loops ?

Paul RowntreePaul Rowntree Posts: 49
edited 2008-04-11 09:31 in Propeller 1
Hmmmm. I read Hippy's posting on waitcnt, and I was wondering if this is the same issue that I am up against. The symptoms seem different though.

I have a PASM code that counts edges on a pin, and after a set period of time it reports that number to hub memory and moves on to the next time bin. The hub memory is a array of sequential time bins. Commercial units are called Multichannel Scalers, and cost upwards of $7K (albeit capable of 5 nS bin times). I want to use it to measure the flight time of atoms across a vacuum chamber.

I have coded a version for the Protoboard, but when I look at the clock pulses that mark the boundaries between these bins, I find that there is considerable 'jitter', despite what seems to be a simple waitcnt controlled loop. In most cases it looks like every other bin is longer then shorter, such that the second bin clock signal is exactly on cue. This goes against what I presumed was a 100% deterministic, repeatable timing system.

In the following code, when I request a bin time of an integer multiple of 1.0 microseconds (80n as the waitcnt interval), everything is fine. Any non-multiple of 80 ticks gives jitter in the bin timings, of ~100 nanoseconds. If I remove the hub read/writes, there is never any bin jitter, no matter what parameters I use.

I would be most appreciative if anyone could shed any light on this one.

Cheers !
Paul Rowntree / Guelph Ontario

DAT                                     ' Prop ASM code to operate as a multichannel scaler

'       MCS_A
'             - internally generated cycle start signal, output on Pin 2
'             - internally generated channel advance timing, output on Pin 1
'             - data accumulated to hub memory
'             - good to microsecond channel widths on a Prop1 (chanCnt=80)
'
' this code can drive a series of LEDs to report status of system.  For symplicity they are
' all assumed to be connected to pins 0-3
'
'  Pin 0 : When high the MCS is armed; checked at start of every cycle
'  Pin 1 : Goes high for 25 nS at the beginning of each channel
'  Pin 2 : Is high when the MCS is actively accumulating information.  Goes off during deadtime delays
'  Pin 3 : Is high during the accumulation of the specified cycles.
'
        org

mcs_A   mov     cycle, ncycles
        mov     dira, #%00001110
        mov     ctra, ctra_             ' establish mode and start counter
        mov     frqa, #1                ' increment for each edge seen

:newCycle  mov thisChan, par
           add thisChan, #4          ' point to buffer for the start of data.  Buffer[noparse][[/noparse]0] holds the cycles counter
           mov chan, nChans          ' load the index that will count how many Channels are acquired
           
           waitpne null_, #%1        ' wait here until pin 0 is HIGH indicating system is Armed   
           mov cnt_, cnt             ' get the current clock
           mov old, phsa             ' get the pulse count before the bin countdown starts
           add cnt_, chanCnt         ' Program does not accumulate for many seconds if this line is removed !
'
:newChan           or      outa, #%1110            ' turn on bits 1,2,3
                   xor     outa, #%0010            ' turn off bit 1   
                   waitcnt cnt_, chanCnt           ' wait for next Channel time to expire
                   mov     new, phsa               ' record new count
                   mov     temp, new               ' make second copy
                   sub     new, old                ' get difference between this reading and last one

                   rdlong hubcount, thisChan       ' this command is exactly 2 in front of wrlong to optimize hub access
                    
                   mov     old, temp               ' set next delta's base
                   add     hubcount, new           ' add these new counts to those of previous cycles
                   
                   wrlong  hubcount, thisChan      ' store the new total back into the hub
                   add     thisChan, #4            ' point to the next long in hub memory
                   
                   djnz    chan, #:newChan         ' if this was not the last bin, jump up to the top of the bin loop

           xor outa, #%0100               ' toggle bit 2 off during deadtime              

           rdlong chan, par               ' use chan for local math just because it is available.  Update cycle counter in hub
           add chan, #1
           sub cnt_, chanCnt              ' cnt_ was incremented anticipating a channel delay          
           wrlong chan, par
           add cnt_, deadcnt
            
           waitcnt cnt_, chanCnt          ' let time slip by while we wait for next cycle to start 

           djnz    cycle, #:newCycle      ' if this was not the last cycle, then jump back up to the start of the cycle loop
        xor outa, #%1000               ' turn bit 3 off
           
:die    waitcnt cnt_, 0
        jmp #:die             
'
' The following estimates assume a net clock frequecy of 80 MHz
'
' a note on timing.  the accumulation time per cycle = nbins x bincnt (in clock ticks) or nbins x bincnt/80_000_000 (in seconds)
' calculate the deadtime (in seconds) by subtracting the accumulation time (in seconds) from the desired cycle time (in seconds) 
' calculate the deadcnt by multiplying the deadtime (in seconds) by 80_000_000
'
ctra_      long    %01010 << 26 + 7        'mode + APIN (pin 7)
null_      long    0                      ' conditional to arm MCS  
'
' the following variables can be assigned in Spin PRIOR to the cognew call that launches the MCS
'
chanCnt    long    120                   ' ticks to let pass before advancing to next channel. 80 is 0.001 milliseconds 
nCycles    long    100000                ' how many cycles to accumulate        
nChans     long    5                     ' number of channels to accumulate per cycle
deadCnt    long    160000                ' ticks to let pass from the end of the accumulation to the start of next cycle, 159_920 is 1.999 msec

cnt_       res     1
new        res     1
old        res     1
temp       res     1
cycle      res     1
chan       res     1
thisChan   res     1
hubcount   res     1                       ' this is the number of counts in this Channel accumulated thus far.       


Post Edited (Paul Rowntree) : 4/11/2008 2:46:06 AM GMT

Comments

  • jazzedjazzed Posts: 11,803
    edited 2008-04-11 03:02
    If you use self-modifying code to load/store values instead of rdlong/wrlong, your jitter problems will mostly disappear.
    Unfortunately, the memory range will be limited to cog ram with this method. Good luck.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    jazzed·... about·living in·http://en.wikipedia.org/wiki/Silicon_Valley

    Traffic is slow at times, but Parallax orders·always get here fast 8)
  • Paul RowntreePaul Rowntree Posts: 49
    edited 2008-04-11 03:15
    Yes, I have a version of the MCS code in the works that uses cog memory, but it is limited to ~430 channels instead of 6000. It also rules out monitoring the data acquisition on-the-fly.

    Can you explain why the hub access causes the jitter? I thought that the hub/cog configuration was intrinsically deterministic (past the first iteration perhaps when the synch has to be established). Perhaps I have missed or over-interpreted something ... Is there a documented way to add another waitcnt inside the loop to restore order and ensure timing regularity?

    Cheers!
  • mirrormirror Posts: 322
    edited 2008-04-11 03:24
    Yes, you are seeing exactly what hippy saw. Now to explain it again -

    Your cog will get 1 hub access per every 16 clock cycles. It is totally deterministic.
    BUT, if your waicnt is a multiple of something other than 16 clocks, then when you get to
    RdLong and WrLong you'll need to wait for your next access slot. Which will be anything
    from 0 to 14 clock cycles of waiting - in addition to the instruction processing. The average of
    which is 8 cycles (x 12.5ns = 100ns of jitter, but anything from 0 to 187.5ns of jitter will be seen).

    What you need to do is store the data for the bin in cog memory at your lock-step time period,
    and then copy that snapshot of the value to the hub when you get the chance.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
  • jazzedjazzed Posts: 11,803
    edited 2008-04-11 04:17
    rd*/wr* accesses are 7 to 22 cycles. If you line them up so that they don't miss their window
    the likelyhood of jitter in that sequence is low. However, just one miss will cause jitter. Most
    loops will have too many instructions somewhere at begin or end that will break the window.
    Seeing the results on a real oscilloscope is quite convincing.

    I haven't tested multiple of 16 waitcnt, but it makes sense. Mirror's advice sounds reasonable.
    Now if you could ping-pong the cogs like in double-buffering maybe, you might have enough time
    to save data to hub using rd*/wr* while the other is collecting it with SMC, vs-a-vs. Of course
    a cog would need to poll for it's turn to save and it is very likely the active collector will finish
    before the active saver. It is unlikely the two cogs would be able to sample exactly back to back.
    Good luck.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    jazzed·... about·living in·http://en.wikipedia.org/wiki/Silicon_Valley

    Traffic is slow at times, but Parallax orders·always get here fast 8)
  • Paul RowntreePaul Rowntree Posts: 49
    edited 2008-04-11 04:46
    Mirror, that is very clear now. And indeed, just as 80 ticks is a multiple of 16 (as are all other multiples of 1 microsecond), when I give it a waitcnt of 128, the bin-width jitter goes to zero. So the trick is to require all timings to be multiples of 0.2 microseconds. My tests had always been multiples of 0.5 microseconds !

    Now the only jitter is in the first bin width, even if it is a multiple of 0.2 microseconds. At least I know where the issue is now.

    Thank you!
  • AleAle Posts: 2,363
    edited 2008-04-11 09:31
    You can try also to synchronize also using waitcnt or a dummy rd/wrbyte, but having already a wait instruction may not be the very best. I was having this same issue but in another level: I have 2 props that communicate (using Beau's routines) that got out of sync due to this jitter so the comm broke. I loosened the timing in the sender and tightened it up in the receiver to not lose some transmitted longs and interrupt the comm.
Sign In or Register to comment.