Looking to translate spin to PASM
Hi,
I have a method (actually two) that are written in spin, they works well but they are too slow for what I need. Unfortunately they involve a lot of math and I'd like to add even more math, and from what I've read in the manual, PASM math/comparisons are pretty difficult for a PASM beginner.
Also I'm about to start trying to translate them to PASM and I'm used to troubleshooting with the PST, how do you debug PASM?
I have a method (actually two) that are written in spin, they works well but they are too slow for what I need. Unfortunately they involve a lot of math and I'd like to add even more math, and from what I've read in the manual, PASM math/comparisons are pretty difficult for a PASM beginner.
Also I'm about to start trying to translate them to PASM and I'm used to troubleshooting with the PST, how do you debug PASM?
PUB calculateCrankRpm(localCrankPeriod) | elapsed
elapsed := -cnt
l_previousNum8 := l_previousNum7
l_previousNum7 := l_previousNum6
l_previousNum6 := l_previousNum5
l_previousNum5 := l_previousNum4
l_previousNum4 := l_previousNum3
l_previousNum3 := l_previousNum2
l_previousNum2 := l_current
l_current := localCrankPeriod
if (l_timeStampCrank == 0)
l_timeStampCrank := (cnt + clkfreq)
if (l_previousNum8 <> 0 and l_previousNum7 <> 0 and l_previousNum6 <> 0 and l_previousNum5 <> 0 and l_previousNum4 <> 0 and l_previousNum3 <> 0 and l_previousNum2 <> 0)
l_previous7average := ((l_previousNum8 + l_previousNum7 + l_previousNum6 + l_previousNum5 + l_previousNum4 + l_previousNum3 + l_previousNum2)/7)
if (((l_current/10)*100) > ((l_previous7average/10)* 110)) or (((l_current/10)*100) < ((l_previous7average/10)*90))
' GAP exists if current greater than 110% of average or current less than 90% of average
l_gapTimePrevious := l_gapTime
l_gapTime := cnt
l_current := l_previous7average
else
if (((clkfreq/l_current)*60)/36) < 200
pst.str(string("crap2"))
pst.char(13)
else
l_toothRpm := (((clkfreq/l_previous7average)*60)/36)
elapsed += cnt - 544
PUB calculateCamRpm(localCamPeriod)
if(localCamPeriod) <> 0 and l_pstReadCamWheel == 1
l_previousCam6 := l_previousCam5
l_previousCam5 := l_previousCam4
l_previousCam4 := l_previousCam3
l_previousCam3 := l_previousCam2
l_previousCam2 := l_currentCam
l_currentCam := localCamPeriod
{pst.dec(l_previousCam6) ' 2324128 ' 2323968 ' 4546304 ' 2324128
pst.char(13)
pst.dec(l_previousCam5) ' 2323968 ' 4546304 ' 2324128 ' 2323968
pst.char(13)
pst.dec(l_previousCam4) ' 4546304 ' 2324128 ' 2323968 ' 4546304
pst.char(13)
pst.dec(l_previousCam3) ' 2324128 ' 2323968 ' 4546304 ' 2324128
pst.char(13)
pst.dec(l_previousCam2) ' 2323968 ' 4546304 ' 2324128 ' 2323968
pst.char(13)
pst.dec(l_currentCam) ' 4546304 ' 2324128 ' 2323968 ' 4546304
pst.char(13)
pst.char(13)} '(average) ' 2768499 ' 3212934 ' 3212966
' 4546304 is 1.65% of 2768499 and 2323968 is 83% of 2768499
' 4546304 is 1.42% of 3212934 and 2323968 is 72% of 3212934
' 4546304 is 1.42% of 3212966 and 2323968 is 72% of 3212966
if l_previousCam6 <> 0 and l_previousCam5 <> 0 and l_previousCam4 <> 0 and l_previousCam3 <> 0 and l_previousCam2 <> 0
l_CamToothAverage := ((l_previousCam6+l_previousCam5+l_previousCam4+l_previousCam3+l_previousCam2/5))
'if (((l_currentCam/10)*100) =< ((l_CamToothAverage/10)*105)) and (((l_currentCam/10)*100) => ((l_CamToothAverage/10)*95))
if (((l_currentCam/10)*100) =< ((l_CamToothAverage/10)*170)) or (((l_currentCam/10)*100) => ((l_CamToothAverage/10)*70))
'(768/10)*100 [7600] =< (3812614/10)*170 [40032405] or (768/10)*100 [7600] => (3812614/10)*95 [36219795]
' less than 105% of average or greater than 70% of average
' trigger1/(cba)
' c > b*1.1 and c > a*1.1
if((l_currentCam*100) > (l_previousCam2*110)) and ((l_currentCam*100) > (l_previousCam3*110)) and (l_trigger1 <> true)
if((l_previousCam2*100) < (l_previousCam3*105)) and ((l_previousCam2*100) > (l_previousCam3*95)) ' b < a*1.1 and b > a*.95
l_trigger1 := true
l_trigger2 := false
l_trigger3 := false
' trigger2/(acb)
' a*1.1 < c
if((l_currentCam*110) < (l_previousCam2*100)) and (l_trigger2 <> true)
if(((l_currentCam*100) < (l_previousCam3*105)) and ((l_currentCam*100) > (l_previousCam3*95))) ' a < b*1.05 and a > b*.95
if((l_previousCam2*100) > (l_previousCam3*110)) 'c > b*1.1
l_trigger2 := true
l_trigger3 := false
l_trigger1 := false
' trigger3/(bac)
' b < a*1.05 and b > a*.95 b*1.1 < c
if(((l_currentCam*100) < (l_previousCam2*105)) and ((l_currentCam*100) > (l_previousCam2*95))) and ((l_currentCam*110) < (l_previousCam3*100)) and (l_trigger3 <> true)
if((l_previousCam2*110) < (l_previousCam3*100)) ' a*1.1 < c
l_trigger3 := true
l_trigger1 := false
l_trigger2 := false
if(l_trigger3 == true)and(l_trigger1 == false)and(l_trigger2 == false)
l_calculatedCamRpm := (((clkfreq/((l_currentCam+l_previousCam2+l_previousCam3)/10))*120)/10) ' or ((((clkfreq/1+2+3)*10)*120)/10)
l_trigger3 := false
else
l_currentCam := l_CamToothAverage

Comments
I'm not sure how to do the math, so my code does not compile because the compiler tells me my compare math is exceeding the registers 9 bits (511) storage location . Are you able to use operators in PASM like you can in spin?
I also do not see a way to have clkfreq in PASM, although I'm sure there is a way, I used 80000000 temporarily instead.
How bad does this PASM code look? *hides behind rock*
DAT org 0 frcntr mov tmp1, par ' * start of structure, passes an address to the value par represents and copies that address to tmp1 rdlong tmp2, tmp1 ' * get beginning variable address location, copy into tmp2 start mov previousNum8, previousNum7 ' l_previousNum8 := l_previousNum7 mov previousNum7, previousNum6 ' l_previousNum7 := l_previousNum6 mov previousNum6, previousNum5 ' l_previousNum6 := l_previousNum5 mov previousNum5, previousNum4 ' l_previousNum5 := l_previousNum4 mov previousNum4, previousNum3 ' l_previousNum4 := l_previousNum3 mov previousNum3, previousNum2 ' l_previousNum3 := l_previousNum2 mov previousNum2, current ' l_previousNum2 := l_current mov current, jmfreqperiod ' l_current := localCrankPeriod cmp previousNum8, #0 WZ, WC ' z=0, c=0 l_previousNum8 <> 0 IF_NC_AND_NZ jmp #seven IF_C_OR_Z jmp #start seven cmp previousNum7, #0 WZ, WC ' z=0, c=0 l_previousNum7 <> 0 IF_NC_AND_NZ jmp #six IF_C_OR_Z jmp #start six cmp previousNum6, #0 WZ, WC ' z=0, c=0 l_previousNum6 <> 0 IF_NC_AND_NZ jmp #five IF_C_OR_Z jmp #start five cmp previousNum5, #0 WZ, WC ' z=0, c=0 l_previousNum5 <> 0 IF_NC_AND_NZ jmp #four IF_C_OR_Z jmp #start four cmp previousNum4, #0 WZ, WC ' z=0, c=0 l_previousNum4 <> 0 IF_NC_AND_NZ jmp #three IF_C_OR_Z jmp #start three cmp previousNum3, #0 WZ, WC ' z=0, c=0 l_previousNum3 <> 0 IF_NC_AND_NZ jmp #two IF_C_OR_Z jmp #start two cmp previousNum2, #0 WZ, WC ' z=0, c=0 l_previousNum2 <> 0 IF_NC_AND_NZ jmp #sevenAvg IF_C_OR_Z jmp #start sevenAvg add previous7Avg, previousNum8 ' l_previous7average + l_previousNum8 add previous7Avg, previousNum7 ' l_previous7average + l_previousNum7 add previous7Avg, previousNum6 ' l_previous7average + l_previousNum6 add previous7Avg, previousNum5 ' l_previous7average + l_previousNum5 add previous7Avg, previousNum4 ' l_previous7average + l_previousNum4 add previous7Avg, previousNum3 ' l_previous7average + l_previousNum3 add previous7Avg, previousNum2 ' l_previous7average + l_previousNum2 mov previous7Avg, (previous7Avg/7) ' l_previous7average := (l_previous7average/7) 'cmp ((current/10)*100), ((previous7Avg/10)*110) WZ, WC ' check for gap/to see if current is greater than 110% of average IF_BE jmp #greaterThan90 ' jump to see if it is greater than 90% of average IF_A jmp #gap ' gap detected, jump to gap greaterThan90 cmp ((current/10)*100), ((previous7Avg/10)*90) ' check for gap/to see if current is greater than 110% of average IF_AE jmp #notGap ' it is smaller than 110% of avg and greater than 90% of avg, not a gap, go to notGap IF_B jmp #gap ' gap detected, jump to gap gap mov gapPreviousTime, gapTime ' this is to keep track of time mov gapTime, cnt ' this is to keep track of time notGap 'cmp (((80000000/current)*60)/36), #200'if (((clkfreq/l_current)*60)/36) < 200 IF_BE jmp #start ' if below 200, restart 'IF_A mov toothRpm, (((80000000/previous7Avg)*60)/36)'l_toothRpm := (((clkfreq/l_previous7average)*60)/36) ' -------------------------------------------------------------------------------------------------- current long 0 previousNum2 long 0 previousNum3 long 0 previousNum4 long 0 previousNum5 long 0 previousNum6 long 0 previousNum7 long 0 previousNum8 long 0 previous7Avg long 0 jmfreqperiod long 0 ' jmfreqperiod is a place holder for a value from another cog, I don't know how to pass that yet gapPreviousTime long 0 gapTime long 0 toothRpm long 0 tmp1 res 1 tmp2 res 1 mask res 1 ' mask for frequency input pin fit 492Although you are probably right, I only left that PST code in there because it had table data that I needed to reference, I am not using that to send debug data to the PST. I am actually using this method below (in its own cog) for that, which I believe is similar to the way you are describing that you debug.
PUB com | timeStamp pst.Start(115_200) pst.Clear pst.str(string("Start")) pst.Str(String(pst#NL)) initScreen l_updateRateDenominator := 2000 timeStamp := cnt repeat if((cnt-timeStamp) > (clkfreq/l_updateRateDenominator)) timeStamp := cnt pst.Dec(l_toothGapRpm) pst.char(13) pst.str(string("crank: ")) pst.Dec(l_toothRpm) pst.char(13) pst.str(string("cam: ")) pst.Dec(l_calculatedCamRpm) pst.char(13) if (l_CntPeakRead == false) pst.dec(l_CntPeak) pst.char(13) pst.dec(cnt) pst.char(13) l_CntPeakRead := true l_CntPeak := 0 else pst.str(string("read")) pst.char(13) pst.str(@b_waitingToBeParsed) pst.char(13) pst.char(13) if (l_sendAllValuesOnce == 1) if(cnt > (timeStamp + (clkfreq/l_updateRateDenominator))) sendValues l_sendAllValuesOnce := 0 if (l_sendAllValues == 1) sendValues l_myReceivedByte := pst.RxCheck if l_myReceivedByte <> -1 b_waitingToBeParsed[b_byteMoveIndex++] := l_myReceivedByte pst.str(@b_waitingToBeParsed) pst.str(string(pst#NL)) if l_myReceivedByte == 13 DelimiterFinder(@b_waitingToBeParsed) ByteFill(@b_waitingToBeParsed, 0, strsize(@b_waitingToBeParsed)) b_byteMoveIndex := 0l_previousNum8 := l_previousNum7 l_previousNum7 := l_previousNum6 l_previousNum6 := l_previousNum5 l_previousNum5 := l_previousNum4 l_previousNum4 := l_previousNum3 l_previousNum3 := l_previousNum2 l_previousNum2 := l_current l_current := localCrankPeriodcollapses neatly into
assuming those variables are longs, are declared thus, and in this order: The Spin compiler makes sure that the move is done in the correct order to avoid toe-stepping when the ranges overlap.
Also, the "<> 0" in this if statement is redundant:
The ands take care of that for you.
Also, doing 32-bit multiplies and divides in PASM won't save you that much time, since the Spin byte-code interpreter does them in assembly anyway. However, you could save time is by hard-coding the multiplications by constants using predetermined shifts and adds.
-Phil
cognew(simulateCamWheel(2, 15000, 2, 72, 72, 159, 19), @simulateCamWheelstack) (pin, rpm, cam rotations:crank rotations ratio, degrees from 0 degrees, degrees from first trigger, degrees from second trigger, actual metal trigger width in degrees)
cognew(simulateCrankWheel(4, 36, 2, 15000), @simulateCrankWheelStack) (pin, total teeth, missing teeth, rpm)
PUB simulateCamWheel(simCamWheelPinNum, engineRPM, crankRotsToCamRots, degreeOfTrigger1, degreeOfTrigger2, degreeOfTrigger3, camTriggerDegreeWidth) | localIndex1, crankRpmPerSecond, camRpmPerSecond, localCnt, trigger1Wait, trigger2Wait, trigger3Wait, emtpySpace1Wait, emtpySpace2Wait, emtpySpace3Wait, cyclesPerDegOfRot dira[simCamWheelPinNum]~~ outa[simCamWheelPinNum]~ localIndex1 := 0 crankRpmPerSecond := (engineRPM/60) ' 166.667 crank rots per second at 10k rpm camRpmPerSecond := (((engineRPM*10)/60)/2) ' 83.333 cam rots per second at 10k rpm cyclesPerDegOfRot := (((clkfreq/camRpmPerSecond)/360)*10) ' 960000/360 at 10k or 2660 trigger1Wait := (camTriggerDegreeWidth*cyclesPerDegOfRot) ' 13300 / 5 degrees trigger2Wait := (camTriggerDegreeWidth*cyclesPerDegOfRot) ' 13300 / 5 degrees trigger3Wait := (camTriggerDegreeWidth*cyclesPerDegOfRot) ' 13300 / 5 degrees emtpySpace1Wait := (degreeOfTrigger1*cyclesPerDegOfRot) ' 266000 / 100 degrees emtpySpace2Wait := (degreeOfTrigger2*cyclesPerDegOfRot) ' 266000 / 100 degrees emtpySpace3Wait := (degreeOfTrigger3*cyclesPerDegOfRot) ' 385700 / 145 degrees ' + = 957600 (((clkfreq/957600) * 60sec/min) * 2rots campercrank) = 10krpm localCnt := cnt repeat outa[simCamWheelPinNum]~~ waitcnt(localCnt += trigger1Wait) ' 6665 outa[simCamWheelPinNum]~ 'waitcnt(localCnt += (clkfreq/521)) ' 160000-6665 waitcnt(localCnt += emtpySpace1Wait) outa[simCamWheelPinNum]~~ waitcnt(localCnt += trigger2Wait) ' 6665 outa[simCamWheelPinNum]~ 'waitcnt(localCnt += (clkfreq/521)) ' 160000-6665 waitcnt(localCnt += emtpySpace2Wait) outa[simCamWheelPinNum]~~ waitcnt(localCnt += trigger3Wait) ' 6665 outa[simCamWheelPinNum]~ 'waitcnt(localCnt += (clkfreq/521)) ' 160000-6665 waitcnt(localCnt += emtpySpace3Wait)PUB simulateCrankWheel(simCrankWheelPinNum, crankTeethSimToothCnt, crankTeethSimMissingToothCnt, crankTeethSimRPM) | localIndex, waitTime, logicHighWaitTime, logicLowWaitTime, logicHighGapWaitTime, logicLowGapWaitTime, startCnt, existingTeeth ' Initialize variables here dira[simCrankWheelPinNum]~~ outa[simCrankWheelPinNum]~ localIndex := 0 waitTime := (clkfreq/((crankTeethSimRPM*(crankTeethSimToothCnt*2))/60)) ' for 1500rpm = 44444 logicHighWaitTime := ((waitTime/50)*45) logicLowWaitTime := ((waitTime/50)*55) logicHighGapWaitTime := ((((waitTime*6)/100)*43)-logicHighWaitTime) ' this percentage can be measured by a prop scope and a signal capture logicLowGapWaitTime := ((((waitTime*6)/100)*57)-logicLowWaitTime) existingTeeth := (crankTeethSimToothCnt - crankTeethSimMissingToothCnt) startCnt := cnt {ctra := (%00100 << 26) | simCrankWheelPinNum ' setup counter for pin frqa := 1 ' this makes the cnt screw up every 26 seconds phsa := 0} { This is for a Hall effect signal/sensor simulation For 1000rpms on a 36-2 crank trigger wheel, it'd be (1000(36*2)) = 72000, then 72000/60 = 1200 teeth on/teeth off per clkfreq. 80000000(clkfreq)/1200 = a tooth on or tooth off every 133333.3 clk cycles. It would be on for 66666.67 clk cycles and then was off for every 66666.67 clk cycles So you'd loop on for 66666.67 cycles and then off for 66666.67 cycles and after 34 times, you'd wait for 66666.67 cycles * 4, to simulate the 2 missing teeth on and accompanying 2 "missing" teeth off (or gaps in between each tooth) . It should account for 2 missing teeth and 3 missing toothgaps An example to start the method would be simulateCamWheel(7, 3, 105, 100) } repeat repeat localIndex from 1 to (existingTeeth) ' loop number of physical teeth on and off outa[simCrankWheelPinNum]~~ 'l_crankTeethSimOn := startCnt waitcnt(startCnt += logicHighWaitTime) ' on for 1 tooth outa[simCrankWheelPinNum]~ 'l_crankTeethSimOff := startCnt waitcnt(startCnt += logicLowWaitTime) ' off for 1 tooth repeat localIndex from 1 to 1'(crankTeethSimMissingToothCnt) ' loop number of times for each missing tooth outa[simCrankWheelPinNum]~ 'l_crankTeethSimOff := startCnt waitcnt(startCnt += logicLowGapWaitTime) ' off for 57% of 2 teeth outa[simCrankWheelPinNum]~~ 'l_crankTeethSimOff := startCnt waitcnt(startCnt += logicHighGapWaitTime) ' on for 43% of 2 teeth@Phil
Can you explain why the below saves time, and if there is a way to estimate how much time it does save? I will incorporate your time savings into my code now.
Also, if I remove the <>, couldn't it run through the code if just one value wasn't equal to 0, because then the sum would not then equal 0? I need to check and make sure they are not equal to 0 individually, to make sure I have cycled through 7 times and there are 7 different values.
No. The logical and operator does not operate bitwise like the "&" operator does. Each of and's operands is deemed to be "true" if it's non-zero.
-Phil
I have the loop taking 27888 to 33216 cycles from start to end (more 33216 then 25056) and I need it to happen (this is worst case of course) in less than 8888 or even 1/2 that if I use cog stopping commands like waitpeq. I'm looking for a second and third opinion though ... What do you all think?
In case you were curious, when I was using the := way of transferring variable values, method time was either 25056 or 30384, so your long move saves me 2832 cycles
The <> part of the code saves me an additional 1920 cycles at 23136 or 28464.
I removed one of the if statements just to time it [if (((clkfreq/l_current)*60)/36) < 200] ... should that take 3696 cycles?
longmove(@l_previousNum2, @l_current, 7) l_current := localCrankPeriod if (l_timeStampCrank == 0) l_timeStampCrank := (cnt + clkfreq) if (l_previousNum8 <> 0 and l_previousNum7 <> 0 and l_previousNum6 <> 0 and l_previousNum5 <> 0 and l_previousNum4 <> 0 and l_previousNum3 <> 0 and l_previousNum2 <> 0) l_previous7average := ((l_previousNum8 + l_previousNum7 + l_previousNum6 + l_previousNum5 + l_previousNum4 + l_previousNum3 + l_previousNum2)/7)to
or, better yet,
clkfreq may not be evenly-divisible by 120, but it may not matter.
-Phil
if (((clkfreq/l_current)*60)/36) < 200 is the same as 15000 < 200
if (clkfreq < 120 * l_current) is the same as 80000000 < 1066560
Maybe I'm missing something?
I guess more importantly though, is it normal for that operation to take 3600 cycles? And is my loop time math correct from the post quoted below?
But, by working though the algebra, that's the very same thing as saying
-Phil
Ok, so if you did 180rpm, which it should be true
180rpm = 3 revolutions per second or 1 revolution every 26666666 clk cycles
if (clkfreq < 120 * l_current) or 80,000,000 < 3,199,999,920
But if you went to 240rpm, which should be false (but is true)
240rpm = 4 revolutions per second or 1 revolution every 20000000 clk cycles
if (clkfreq < 120 * l_current) or 80,000,000 < 2,400,000,000
I think it will evaluate to true until it is greater than 7200rpm or 1 revolution every 666666 clk cycles, when it should be evaluating to false at anything above 200rpm?
I am probably missing something still, so don't be insulted by the additional questions please, I'm asking again because I want to learn/understand.
80_000_000 / 20_000_000 * 60 / 36 == 6 (integer), which is less than 200
-Phil
240rpm = 1 tooth every 555555 cycles, and I originally did 4 revolutions and 26666666 which was wrong, sorry about that.
180rpm = 1 tooth every 740740 cycles
(clkfreq < 120 * l_current)
80000000 < 120 * 740740 = 80,000,000 < 88,888,800 - TRUE for 180rpm
80000000 < 120 * 555555 = 80,000,000 < 66,666,600 - FALSE for 240rpm
I appreciate your patience, thanks for sticking with me on this
Does spin have the speed to do calculations within 8888 clk cycles or possibly 1/2 that?