WAITPEQ with timeout
ManAtWork
Posts: 2,178
Hi,
I'm sure someone has found out before but I didn't find anything about this in the manuals so I think this could be useful to anybody with the same problem...
The WAITPEQ or WAITPNE commands can wait for a pin change but there's no way to cancel them. If the pin doesn't change the program will hang forever. To avoid this we could use a polling loop instead. If we have a fixed timeout then a relatively tight loop will do:
However, if we want to signal the abort condition from a different cog we have to use a flag in hub memory or a lock. This adds additional delay and jitter because of the hub synchronization.
For some applications this might be unacceptable. There's no way to make it faster (at least I don't know how, if you do please tell us). But theres a way to avoid the jitter. The following code results in cycle-exact timing.
Cheers
I'm sure someone has found out before but I didn't find anything about this in the manuals so I think this could be useful to anybody with the same problem...
The WAITPEQ or WAITPNE commands can wait for a pin change but there's no way to cancel them. If the pin doesn't change the program will hang forever. To avoid this we could use a polling loop instead. If we have a fixed timeout then a relatively tight loop will do:
mov timeout,#noOfCycles/8 :loop test bitmask,INA wz if_z djnz timeout,#:loop
However, if we want to signal the abort condition from a different cog we have to use a flag in hub memory or a lock. This adds additional delay and jitter because of the hub synchronization.
:wait rdlong flag,adrHubFlag wz test bitmask,INA wc if_z_and_nc djnz timeout,#:wait if_nz jmp #abortThis tests for both timeout and an abort flag. Loop execution time is now 16 cycles plus a unknown delay of up to 7 cycles for the first execution due to hub synchronization.
For some applications this might be unacceptable. There's no way to make it faster (at least I don't know how, if you do please tell us). But theres a way to avoid the jitter. The following code results in cycle-exact timing.
mov CTRA,modePosCnt neg FRQA,#1 ... mov PHSA,#24 ' start value, decrement when pin high :wait rdlong flag,adrHubFlag wz test bitmask,INA wc if_z_and_nc djnz timeout,#:wait if_nz jmp #abort mov delay,PHSA wc ' if negative set C add delay,CNT add delay,#9 ' min delay if_nc waitcnt delay,#0 ... modePosCnt long %01000<<26 + PinNoThe counter measures the time that the pin was already high before the PHSA is read. The later the pin went high the more delay is added at the end balancing out the jitter. The check for negative values is necessary because short glitches might trigger the counter but can be missed by the TEST instruction because it only polls every 16th cycle.
Cheers
Comments
yes, I've known the trick with the sacrificed pin. But sometimes you have none left over. How do you change the WAITPxx mask from another cog? You mean driving the input pin as output temporarily?
That would be indeed faster and more elegant than my code. However, what if you have multiple different WAITPxx following each other in a sort of state machine and the cog wishing to abort the wait has now knowledge in which of the WAITs the cog is currently in? Then you need a dedicated abort pin to reset the state machine.
Regards
1) Sacrifice an I/O pin to use internally to signal an abort using a cog counter.
2) Sacrifice a cog to keep a timeout and use COGSTOP to abort the cog or cogs hanging on the WAITPxx
3) Simply don't use a WAITPxx and use a TEST instruction as well as one of the counters in a wait loop with a timeout.
Here's another:
-Phil
Let's assume perfect hub-window timing (worst case delay/miss is actually 15 cycles). Let's also assume that ctra starts counting with the first cycle of the rdlong and the pulse is long enough to be caught by the test (i.e. no looping). In this case you'll add 2 extra cycles for the waitcnt. Total running time 40 cycles. Now introduce the other 15 cases:
The first 3 delays get smoothed out by the waitcnt, delay 3 is the first one where phsa holds a negative value and therefore simply leaves the loop with 36 + delay cycles (as will 4..15).
So what am I missing here? Are we talking about different (jittery) things?
In case it's not about hub-sync adjustment, there is still an inconsistency in your code. Effectively #24 isn't big enough. The initial value needs to be at least #26 (live registers are sampled during the execution phase, i.e. two cycles later than you'd expect).
thanks for the suggestions. Unfortunatelly, I need two cogs waiting with timeout and I only have one spare pin, so the sacrificed pin method doesn't work for me. I also tried out the cogstop method but it takes too much time to restart the cog. I even tried to always launch one spare cog in background to avoid the deadtime of coginit. It worked but I had too little confidence in it being reliable in all cases. The controller is designed to be used in industrial environment and has to withstand glitches and all sorts of timing violations.
Phil's second solution is very similar to mine. It takes several instructions less but if I understood correctly it is not glitch proof. It doesn't matter much if the timing is wrong when a glitch occures but it is absolutely important that my program doesn't hang and safely recovers as soon as the disturbance is gone.
Hello Kuroneko,
good point, I think you are right. I haven't found any precise information about the propeller's internal instruction timing. So I simply used the scope and incremented the start value for PHSA until the jitter disappeared. However, I didn't take into account that my code was probably already synchronised to the hub by preceding rdlong instructions.
So what start value do you suggest so that it works in all cases?
Best regards
I'm using a 1-wire DS2324 on my motor speed gauge. It allowed me to totally isolate my code from waiting and it handles long periods of time without a value quite well. The assembler does take up a cog but if you are just checking the number of switch closures, it made the RPM calculation a snap. I just check it every 5 seconds, multiply the counter by 12 and be done. But the propeller is so much faster than the physical, I could have looped much quicker. At 3600/rpm, thats just not that fast.
If you are interested, I'll send you the 1-wire code for that device. It's a work in progress but it goes live in 2 weeks so it's not that far off. I can isolate it in a smaller program.
Thanks
thanks for your offer. But this time my application is totally different. I don't wait for a pin to calculate motor speed. Instead it's a stepper motor controller that has to control current through the two motor windings. There are two state machine each running in one cog. They must handle current of both polarities and must be re-synchronized to a master clock on demand to avoid beat frequencies. To make it even harder it has to automatically switch between slow decay (recirculating) and fast decay mode.
Last time I implemented this in an FPGA which is originally better suited to state machines. But the propeller is better for other algorithms (waveform shaping, programmable resolution...) and I don't want to spend money and board space for both.
BTW, could it be you misspelled DS2324? I can't find it's datasheet with google.
Regards
Thanks
Define all cases, i.e. can it ignore hub-sync or should that be handled? While the latter is certainly possible it's usually better to arrange for a known entry condition (ideally perfect match). Also, relative to phsa setup, when do you expect the first change?