Shop OBEX P1 Docs P2 Docs Learn Events
Never returning from waitcnt — Parallax Forums

Never returning from waitcnt

RogerInHawaiiRogerInHawaii Posts: 87
edited 2009-04-25 05:27 in Propeller 1
I've got a problem with calling the waitcnt method.

I'm calling it like this:

· waitcnt(cnt+ClockDelay)

where the ClockDelay variable happens to have the value 381. In this case the amount of time
that waitcnt should wait is extremely short.

But it never returns from waitcnt. Well, I don't know if it's "never". I waited for a minute and it didn't return so I stopped the program.

Is it possible that waitcnt has some minimum wait time and cnt+381 is just too short?

Comments

  • QuattroRS4QuattroRS4 Posts: 916
    edited 2009-04-24 21:53
    try - waitcnt(clockdelay+cnt)

    regards,
    John

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    'Necessity is the mother of invention'

    Those who can, do.Those who can’t, teach.
  • virtuPICvirtuPIC Posts: 193
    edited 2009-04-24 21:56
    Yes, waitcnt has a minimum wait time. Your code reads the system counter, reads variable ClockDelay, adds the values and finally issues a waitcnt PASM instuction in the interpreter. If the SPIN interpreter is slow enough that all this stuff needs more than 381 clock cycles then you need a wrap around of the system counter which needs some 50 seconds at 80 MHz clock frequency. 381 cycles are less than 100 instructions. Interpreters are not really fast.

    Try the following:

    waitcnt(ClockDelay + cnt)

    I assume that cnt is read later than in your code and maybe late enough. And try to increase Clock Delay to find the minimum value.

    But these proposals are only for experimenting and analyzing! Take care that delays are significant longer than the minimum delay.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Airspace V - international hangar flying!
    www.airspace-v.com/ggadgets for tools & toys
  • RogerInHawaiiRogerInHawaii Posts: 87
    edited 2009-04-24 22:08
    Thanks for the suggestions. I've done a bunch of testing and there does indeed seem to be a mimimum value that waitcnt can handle, no doubt, as you suggest, due to the time it takes for the interpreter to calculate the cnt + ClockDelay and then to actually invoke the waitcnt method. The minimum seems to be somewhere between 500 and 1000. Swapping the values so that the cnt comes second helps a bit but doesn't completely solve the problem.

    I think the issue here is that watcnt waits until the system clock is EXACTLY equal to the specified value. Obviously if you specify a value that's less than the "current" clock value (current as far as when the waitcnt actually performs the comparison) then it never encounters a clock value that's EQUAL to the specified value. Well, at least not for a very, very long time.

    I think the correct solution to this is for the waitcnt method to be changed so that it returns as soon as the current clock value is GREATER THAN the specified target value. That would avod problems like I encountered, where the value added to cnt is a time value that's shorter than the amount of time taken by the interpreter to process it. Plus, it would accommodate situations where the specified value is (inadvertantly) prior to the actual current clock value, which would clearly be a mistake but waitcnt should do better than waiting days to return.
  • QuattroRS4QuattroRS4 Posts: 916
    edited 2009-04-24 22:13
    Roger,
    I think you answered the question in your question 'Is it possible that waitcnt has some minimum wait time and cnt+381 is just too short?' - but good practice is waitcnt(delay+cnt) as you found in your testing ..
    Are you enjoying the learning curve ?

    Regards,
    John

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    'Necessity is the mother of invention'

    Those who can, do.Those who can’t, teach.
  • lonesocklonesock Posts: 917
    edited 2009-04-24 22:18
    RogerInHawaii said...
    ...I think the correct solution to this is for the waitcnt method to be changed so that it returns as soon as the current clock value is GREATER THAN the specified target value.

    This works great until your counter rolls over. For example, cnt is at max - 1000, and you ask it to wait for 10000 clocks...the value it would compare to be >= would wrap around to be 9000, so waitcnt would immediately return.

    Jonathan

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    lonesock
    Piranha are people too.
  • RogerInHawaiiRogerInHawaii Posts: 87
    edited 2009-04-24 22:18
    QuattroRS4 said...

    Are you enjoying the learning curve ?
    Not at all. I've been a software engineer for 35 years and it's little undocumented details like this that consume way too much of my development time and as a result causes way too much frustration.
  • lonesocklonesock Posts: 917
    edited 2009-04-24 22:22
    RogerInHawaii said...
    ...it's little undocumented details like this...

    "undocumented" is a bit harsh. The waitcnt documentation on page 323 of the propeller manual has this text:

    "IMPORTANT: Since WAITCNT pauses the cog until the System Counter matches the given
    value, care must be taken to ensure that the given value was not already surpassed by the
    System Counter. If the System Counter already passed the given value before the wait
    hardware activated then the cog will appear to have halted permanently when, in fact, it is
    waiting for the counter to exceed 32 bits and wrap around to the given value. Even at 80
    MHz, it takes over 53 seconds for the 32-bit System Counter to wrap around!
    Related to this, when using WAITCNT in Spin code as shown above, make sure to write the
    Value expression the same way we did: in the form “offset + cnt” as opposed to
    “cnt + offset.” This is because the Spin interpreter will evaluate this expression from left
    to right, and each intermediate evaluation within an expression takes time to perform. If cnt
    were at the start of the expression, the System Counter would be read first then the rest of the
    expression would be evaluated, taking an unknown amount of cycles and making our cnt
    value quite old by the time the final result is calculated. However, having cnt as the last
    value in the WAITCNT expression ensures a fixed amount of overhead (cycles) between reading
    the System Counter and activating the wait hardware. In fact, the interpreter takes 381 cycles
    of final overhead when the command is written in the form waitcnt(offset + cnt). This
    means the value of offset must always be at least 381 to avoid unexpectedly long delays."

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    lonesock
    Piranha are people too.
  • RogerInHawaiiRogerInHawaii Posts: 87
    edited 2009-04-24 22:27
    lonesock said...
    RogerInHawaii said...
    ...I think the correct solution to this is for the waitcnt method to be changed so that it returns as soon as the current clock value is GREATER THAN the specified target value.

    This works great until your counter rolls over. For example, cnt is at max - 1000, and you ask it to wait for 10000 clocks...the value it would compare to be >= would wrap around to be 9000, so waitcnt would immediately return.

    Jonathan

    Well, how about changing the parameter for waitcnt to be "number of cycles AFTER the current cycle count", rather than the target cycle count.
    Thus, instead of doing:

    ·· waitcnt(100 + cnt)

    You'd just d:

    ·· waitcnt(100)

    and waitcnt itself would determine what the current cnt is, add the specified value to it, and wait for the cnt to hit that value. Waitcnt would also be able to determine, by itself, whether the specified value is too short a cycle count to handle and return immediately.

    Is there ANY situation where you'd call waitcnt and NOT add cnt to the value you want wait? Then why not have waitcntdo it by itself?

    Maybe it's unreasonable to expect a change to waitcnt, but perhaps another method could be added that does the +cnt itself and can therefore detect by itself when the specified "number of cycles to wait" is too small to handle and therefore return immediately.
  • RogerInHawaiiRogerInHawaii Posts: 87
    edited 2009-04-24 22:34
    lonesock said...


    "undocumented" is a bit harsh.
    I apologize for that.
  • OwenSOwenS Posts: 173
    edited 2009-04-24 22:52
    Quite often one does something like
    time = cnt
    ..some code..
    time += delay
    waitcnt(time)
    ..mode code..
    time += delay
    
    


    and so on when more precise timing is required (though, for obvious reasons, this is often done in assembly)
  • lonesocklonesock Posts: 917
    edited 2009-04-24 23:00
    RogerInHawaii said...
    lonesock said...
    RogerInHawaii said...

    ...I think the correct solution to this is for the waitcnt method to be changed so that it returns as soon as the current clock value is GREATER THAN the specified target value.

    This works great until your counter rolls over. For example, cnt is at max - 1000, and you ask it to wait for 10000 clocks...the value it would compare to be >= would wrap around to be 9000, so waitcnt would immediately return.

    Jonathan

    Well, how about changing the parameter for waitcnt to be "number of cycles AFTER the current cycle count", rather than the target cycle count.
    Thus, instead of doing:

    waitcnt(100 + cnt)

    You'd just d:

    waitcnt(100)

    and waitcnt itself would determine what the current cnt is, add the specified value to it, and wait for the cnt to hit that value. Waitcnt would also be able to determine, by itself, whether the specified value is too short a cycle count to handle and return immediately.

    Is there ANY situation where you'd call waitcnt and NOT add cnt to the value you want wait? Then why not have waitcnt do it by itself?

    Maybe it's unreasonable to expect a change to waitcnt, but perhaps another method could be added that does the +cnt itself and can therefore detect by itself when the specified "number of cycles to wait" is too small to handle and therefore return immediately.

    I agree that a "delay" type function is more often what people want than a waitcnt. You could wrap your own, of course, and use the #> operator to make sure the delay value is always >= 381. (Note that the #> operator treats values as signed.)

    As to using waitcnt without adding cnt to it, I use waitcnt in this mode most frequently. For example, if I want to sample an ADC at regular intervals, I will kickstart my wait_until_cnt variable using the cnt register, but from then on out I would just say

    repeat 
      waitcnt( wait_until_cnt )
      wait_until_cnt += interval
      <do a bunch of processing>
    



    This lets me have a deterministic (and very precise) sampling rate, even though I'm using SPIN code which may vary in execution time. I just need to make sure that my "<do a bunch of processing>" code takes less time than interval.
    RogerInHawaii said...
    I apologize for that.
    Not a problem...this particular issue has bitten me before too! [noparse][[/noparse]8^)

    Jonathan

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    lonesock
    Piranha are people too.
  • whickerwhicker Posts: 749
    edited 2009-04-24 23:02
    Roger, I know your pain. In my dayjob I'm used to waitTime(T#260ms) or whatever.

    But seriously, 32 XOR gates (equality check) was a lot cheaper to implement than having another counter register start from zero and count up to the desired delay ticks. Plus in Chip's mind, he wanted every core to be able to synchronize to and see the same number.
  • MagIO2MagIO2 Posts: 2,243
    edited 2009-04-24 23:31
    RogerInHawaii·said...
    Is there ANY situation where you'd call waitcnt and NOT add cnt to the value you want wait? Then why not have waitcntdo it by itself?
    No, but there are situations where you want several subsequent points in time·to be·related exactly to that time you started something. If you have a magic wait which does add cnt itself you never·know exactly·how long waitcnt is going to·wait. waitcnt itself is getting parameters from HUB-RAM. So·at least for the very first·fetch of data from HUB-RAM you don't know how long the HUB-access took. rdlong can take·7 to 22 cycles. So, in the way that waitcnt is implemented it is as accurate as the clock. And it can keep the accuracy even over the long term.
    For an additional wait which·would add cnt by magic and recognize a waittime which is to low, ther might be different reasons: Not enough space left in the Interpreter, no more bytecode available. No need for that.

    If you want waitcnt to work as you described it, then take the SPIN-interpreter sourcecode and change it.

    Post Edited (MagIO2) : 4/24/2009 11:39:20 PM GMT
  • jazzedjazzed Posts: 11,803
    edited 2009-04-24 23:36
    OwenS example is a way in which you would not add cnt to subsequent waitcnt calls. This is done all the time in assembly for critical timing.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    --Steve


    Propalyzer: Propeller PC Logic Analyzer
    http://forums.parallax.com/showthread.php?p=788230
  • RogerInHawaiiRogerInHawaii Posts: 87
    edited 2009-04-25 00:12
    jazzed said...
    OwenS example is a way in which you would not add cnt to subsequent waitcnt calls. This is done all the time in assembly for critical timing.

    Well, you're not explicitly adding cnt each time, except for the first time you call waitcnt, but since you started out with a variable to which you added cnt, and subsequently add additional wait "times" (number of clock cycles) to that value and pass it to waitcnt, the value that waitcnt gets still does indeed contain a cnt value.

    I understand the need to have things happen at specific time intervals, but that approach has the same danger of passing in a value to waitcnt that is already greater than the current cycle count as in my example where I'm adding a value to cnt that's too small. The problem with the time interval approach is that it's always possible that the processing that occurs between one event and the next event takes longer than expected so that when you add your constant time interval value to the running total it ends up being less than the current cnt value, so that the call to waitcnt does not in fact return when you expect it to, since the next "event time" has already passed.

    I suspect that the only way to really assure performing operations at specific time intervals·is to use a timed interrupt. But I don't think that's available with the Propeller.
  • jazzedjazzed Posts: 11,803
    edited 2009-04-25 00:37
    RogerInHawaii said...
    jazzed said...
    OwenS example is a way in which you would not add cnt to subsequent waitcnt calls. This is done all the time in assembly for critical timing.
    Well, you're not explicitly adding cnt each time, except for the first time you call waitcnt, but since you started out with a variable to which you added cnt, and subsequently add additional wait "times" (number of clock cycles) to that value and pass it to waitcnt, the value that waitcnt gets still does indeed contain a cnt value.
    Of course it implicitly contains cnt on subsequent waitcnt calls. How could you use it to "assure performing operations at specific time intervals" without it?· As far as the dangers, we've all run into this ... you are by no means alone. "This too will pass."

    Actually OwenS example should set time then add cnt afterwards, but considering how geologically slow spin is, I figure it didn't really matter·because it would be silly to do for very short periods anyway.·

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    --Steve


    Propalyzer: Propeller PC Logic Analyzer
    http://forums.parallax.com/showthread.php?p=788230
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2009-04-25 01:37
    RogerInHawaii said...
    The problem with the time interval approach is that it's always possible that the processing that occurs between one event and the next event takes longer than expected...
    Yeah, isn't that always the case? This situation is not unique to the Propeller, certainly. The same applies to interrupt routines that take too long to process and end up "skipping a beat". At some point the programmer has to take responsibility for his program's timing and not expect miracles from the hardware.

    -Phil
  • StefanL38StefanL38 Posts: 2,292
    edited 2009-04-25 05:27
    Hello Roger,

    Chip designed the propeller to be as deterministic as it can be.
    That's why waitcnt waits for an exact match.

    In the manual is a variant described that gives a rocksolid time-delay which will be still very exact
    even if execution-time of the loop will be variant. (Of course within the limit of 381-cycles plus what come
    from execution other commands in the loop.

    If you want to have it REALLY fast you have to go to PASM. Here the minimum-delay is reduced to 9 cycles.

    I haven't read each post of this thread. So maybe it was mentioned somewhere.
    But what are your minimum-requirements for the wait-delay ?

    In the obex there is a timer-object with a millisecond-timer. If you use this object

    Depending on your requirements you can use this timer to compare to what ever you want.
    Of course the resoltion is reduced to a millisecond. But maybe this would still fit your requirements.

    Another way could be to measure the elapsed_time and compare with "equal or greater than"

    some time ago I wrote a method for this

    with this you get a upper limit of around 26 seconds at 80 MHz

    CON
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000
    
      
    VAR
      long timer1
      long eTime1
    
    
    OBJ
      debug : "Extended_FDSerial"
    
    'Use F11 to store it the EEPROM. If you open the COM-Port
    'with a terminalsoftware may cause a reset and then
    'the content of RAM will be overwritten by the content of the EEPROM
    
    PUB Main
      'the FIRST PUB-Method inside a spinfile is ALWAYS the startpoint where the program starts to run    
      debug.start(31, 30, 0, 9600)
    
      timer1 := cnt 'make inital snapshot of systemcounter 
    
      repeat
        'do whatever you like inside this loop
        '
        'the method elapsed_time_msec calculates the time that has gone since
        'variable timer1 has set to the (former) value of systemcounter
        eTime1 := elapsed_time_msec(timer1) 
    
        if eTime1 => 100 '<== this is the amount of millisecs that are "waited"
          debug.str(string("snapshot of systemcounter: timer1="))
          debug.dec(timer1)
          debug.str(string("  elapsed time="))
          debug.dec(eTime1)
          debug.tx(13)
          debug.tx(13)
          'make new snapshot of systemcouner to set new startvalue
          timer1 := cnt  
    
          
    PUB elapsed_time_msec(p_TimeVar) | RightNow, ClockTicks
    
      RightNow := cnt
    
      if RightNow < p_TimeVar                          
        ClockTicks := ||($FFFFFFFF - RightNow + p_TimeVar)  
      else
        ClockTicks := ||(RightNow - p_TimeVar)
    
      result := ClockTicks / (clkfreq / 1_000) 'calculate milliseconds
      ' SPIN treats longs as SIGNED longs meaning bit 32 represents the sign "+-"
      ' the systemcounter thinks of the 32bits as UNsigned meaning bit 32 is
      ' just another bit of the number and NOT a sign "+-" 
      ' if one or both values are negative it could happen
      ' that the result is negative too
      ' the command "||" calculates the absolute value of the SIGNED longs
    
      'if the systemcounter has wrapped around since snapshot of
      'systemcounter (value of parameter p_TimeVar),
      'this method calculates the timedifference in the right way
      
      'wrap-around example with easy numbers: counter maxvalue 1000
      'p_TimeVar containing value 980
      'time goes on counter wraps around from 1000 to 0
      'time goes on 
      'RightNow containing 300. This means 20 ticks until maximum 1000 and 300 ticks
      'since wrapping from 1000 to 0 in summary 320 ticks of time has gone
      'this is calculated by MaxCounter - p_TimeVar + RightNow
      'in numbers              1000     -   980     +   300     = 320
      'the real systemcounter has 32bits max value 2^32 -1 = 4294967295
      'hexadecimal $FFFFFFFF
     
    'end of PUB elapsed_time_msec(p_TimeVar) | RightNow, ClockTicks
    
    



    best regards

    Stefan
Sign In or Register to comment.