Shop OBEX P1 Docs P2 Docs Learn Events
WAITPEQ with timeout — Parallax Forums

WAITPEQ with timeout

ManAtWorkManAtWork Posts: 2,178
edited 2010-10-11 17:29 in Propeller 1
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:
              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    #abort
This 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 + PinNo
The 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

  • T ChapT Chap Posts: 4,223
    edited 2010-10-09 13:44
    There was a thread recently about changing the state of a pin in waitpxx mask, so that the change would trip the wait. Other ideas include sacrificing a pin and adding the pin to the watched pins, and altering that pin as needed to get past the waitpxx.
  • ManAtWorkManAtWork Posts: 2,178
    edited 2010-10-09 13:55
    Hello T Chap,

    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
  • Mike GreenMike Green Posts: 23,101
    edited 2010-10-09 14:12
    With the kind of complex wait situation you're describing, there's no way to implement it using WAITPxx instructions because of the lack of a timeout. As you've suggested, you can:

    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.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2010-10-09 14:33
  • kuronekokuroneko Posts: 3,623
    edited 2010-10-09 21:44
    ManAtWork wrote: »
    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 + PinNo
    

    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:
    '                     [COLOR="Blue"]0  1  2[/COLOR]  [COLOR="Red"]3[/COLOR]  4  5  6  7  8  9 10 11 12 13 14 15
    ' rdlong.s       +0  [COLOR="Blue"]40 40 40[/COLOR] [COLOR="Red"]39[/COLOR] 40 41 42 43 44 45 46 47 48 49 50 51
    
    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).
  • ManAtWorkManAtWork Posts: 2,178
    edited 2010-10-11 02:01
    Hello Mike and Phil,

    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
  • bdickensbdickens Posts: 110
    edited 2010-10-11 07:05
    Not that you need more suggestions.

    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
  • ManAtWorkManAtWork Posts: 2,178
    edited 2010-10-11 07:37
    Hello bdickens,

    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
  • bdickensbdickens Posts: 110
    edited 2010-10-11 15:11
    Sure did. DS2423. I can at least blame old age.

    Thanks
  • kuronekokuroneko Posts: 3,623
    edited 2010-10-11 17:29
    ManAtWork wrote: »
    So what start value do you suggest so that it works in all cases?

    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?
Sign In or Register to comment.