Non-deterministic PASM loops ?
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
Post Edited (Paul Rowntree) : 4/11/2008 2:46:06 AM GMT
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
spin
11K

Comments
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)
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!
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.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
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)
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!