Shop OBEX P1 Docs P2 Docs Learn Events
assembly language question — Parallax Forums

assembly language question

mynet43mynet43 Posts: 644
edited 2011-07-19 06:49 in Propeller 1
Will the following code work, regardless of whether cnt or tr_act_cnt have wrapped around?

My goal is to set a future time count, then see when cnt catches up to it. Either one could wrap before the comparison. The time difference will never be more than a few seconds. I'm timing the on-time of a signal, before I turn it off.
             
                        mov     tr_act_cnt,cnt          ' read uP count clock
                        add     tr_act_cnt,delta        ' set future time...

. . . other code

                        mov     temp,cnt                ' read uP count clock
                        cmp     temp,tr_act_cnt wc      ' see if cnt < tr_act_cnt
              if_c      jmp     :exit                   ' if so, leave it on

Thank you for your help.

Jim

Comments

  • Mike GreenMike Green Posts: 23,101
    edited 2011-07-17 14:18
    Nope. The problem is that CNT is a 32 bit unsigned quantity that wraps around. When you add a delta to it, the result can be greater or less than what you start with depending on whether it wraps around or not. You're usually better off calculating time differences and limiting delta to 31 bits so you'd calculate (CNT - (delta+oldCNT)). If the result is negative, you haven't reached the time yet. If the result is positive, you've reached or passed the time. If more than 25 seconds have passed since you started (at 80MHz), all bets are off.
  • RockyDRockyD Posts: 17
    edited 2011-07-17 14:29
    Adding to what Mike said, below is a bit of code from FullDuplexSerial that does what Mike mentioned. In this case rxcnt already has the CNT and the offset added together. Also note that in this code the subtraction is just reversed.

    :wait                   jmpret  rxcode,txcode         'run a chuck of transmit code, then return
    
                            mov     t1,rxcnt              'check if bit receive period done
                            sub     t1,cnt
                            cmps    t1,#0           wc
            if_nc           jmp     #:wait
    
  • mynet43mynet43 Posts: 644
    edited 2011-07-17 16:38
    Hi again,

    Just a sanity check. Is this what it should look like?
                 
                            mov     tr_act_cnt,cnt          ' read uP count clock
                            add     tr_act_cnt,delta        ' set future time...
    
    . . . other code
    
                            mov     temp,tr_act_cnt         ' move into test var
                            sub     temp,cnt wc             ' see if cnt =< tr_act_cnt
                  if_nc     jmp     :exit                   ' if so, leave it on
                  
    
    

    Thanks again for your help.

    Jim
  • AribaAriba Posts: 2,690
    edited 2011-07-17 17:21
    mynet43 wrote: »
    Hi again,

    Just a sanity check. Is this what it should look like?
                 
                            mov     tr_act_cnt,cnt          ' read uP count clock
                            add     tr_act_cnt,delta        ' set future time...
    
    . . . other code
    
                            mov     temp,tr_act_cnt         ' move into test var
                            sub     temp,cnt wc             ' see if cnt =< tr_act_cnt
                  if_nc     jmp     :exit                   ' if so, leave it on
                  
    
    

    Thanks again for your help.

    Jim

    This will not work, it's the same as compare. The trick is to ignore the carry at building the difference, and check then if the result is positive or negative:
    ...
               mov     temp,tr_act_cnt      ' move into test var
               sub     temp,cnt             ' see if cnt =< tr_act_cnt
               cmps    temp,#0  wc
       if_nc   jmp     :exit                ' if so, leave it on
    

    Andy
  • RockyDRockyD Posts: 17
    edited 2011-07-17 17:22
    Jim,

    I think your use of subtract and your comment "see if cnt =<" is not what you are looking for.

    Lets say that temp was max unsigned int. Then there does not exist a cnt vaule that would set the wc flag with the code you have.

    What I think you really want is to detect in the max int case of temp, values of cnt equal to max int + 1 (or 0) to be seen as past the temp value. But, also cnt values of max int + 1000 (999). But, not max int - 1000. Nor, max int + 4,000,000,000.

    So, back to what Mike was saying in his first post, is you really have to give up one bit and convert to unsigned. Then, if cnt is more than 1/2 max unsigned int past a value, it is really less than.


    Rocky
  • mynet43mynet43 Posts: 644
    edited 2011-07-17 17:28
    Hi Andy,

    I think I got it now. I missed that when I looked at the example in FullDuplexSerial.

    I'm glad I did the sanity check :)

    Thanks everyone.

    Jim
  • AribaAriba Posts: 2,690
    edited 2011-07-17 17:33
    This should clarify why we need to ignore the carry:
    temp   $6100_0000     $0100_0000
    cnt    $5F00_0000     $FF00_0000
    
    diff:  $0200_0000     $0200_0000
           carry=0        carry=1
    
    Andy
  • kuronekokuroneko Posts: 3,623
    edited 2011-07-17 18:48
    @mynet43: To throw something else in the mix, depending on your intended interval you can also use a LOGIC always counter (0..MAXUINT) and forget about the wrapping issue.
  • mynet43mynet43 Posts: 644
    edited 2011-07-17 20:58
    Hi Kuroneko,

    The other is now working.

    But this is interesting :) How do you do a LOGIC always counter?

    Jim
  • RockyDRockyD Posts: 17
    edited 2011-07-17 23:19
    The Logic always is from Table 2-7: Counter Modes page 98 of the Propeller manual. This suggestion would use one of the counter registers that each cog has. You would be able to start it at zero each time. It would count up and you would not need to worry about the rollover.
  • mynet43mynet43 Posts: 644
    edited 2011-07-18 06:54
    OK, I guess it's time for me to do two things:
    1. Start using the new 1.2 manual, and throw out the 1.0 manual I printed out years ago :)

    2. Learn more about using the counters.

    So here's my first cut at it:
    CON
      ctra_mode = %11111<<26        ' set CTRA to LOGIC always mode
    
    DAT
    ' so instead of this...
                            mov     temp,tr_act_cnt         ' move into test var
                            sub     temp,cnt                ' see if cnt =< tr_act_cnt
                            cmps    temp,#0 wc              ' now write the carry
                  if_nc     jmp     #:chk_int               ' if so, leave it on
    
    ' use this...
                            mov     ctra,ctra_mode          ' set counter mode to logic always
                                                            ' start counter at zero when delay starts
    ' other code . . . . . .
    
    ' then use this to see if the required delay has passed
                            cmp     ctra,delta wc           ' see if ctra has caught up with delay
                  if_c      jmp     #:chk_int               ' if not, leave it on                                                                 
                  
    
    

    Please let me know if I'm on the right track. If so, I really like it!

    Thanks again for all your help.

    Jim
  • RockyDRockyD Posts: 17
    edited 2011-07-18 09:48
    mynet43 wrote: »
    OK, I guess it's time for me to do two things:
    1. Start using the new 1.2 manual, and throw out the 1.0 manual I printed out years ago :)

    2. Learn more about using the counters.

    So here's my first cut at it:
    CON
      ctra_mode = %11111<<26        ' set CTRA to LOGIC always mode
    
    DAT
    ' so instead of this...
                            mov     temp,tr_act_cnt         ' move into test var
                            sub     temp,cnt                ' see if cnt =< tr_act_cnt
                            cmps    temp,#0 wc              ' now write the carry
                  if_nc     jmp     #:chk_int               ' if so, leave it on
    
    ' use this...
                            mov     ctra,ctra_mode          ' set counter mode to logic always
                                                            ' start counter at zero when delay starts
    ' other code . . . . . .
    
    ' then use this to see if the required delay has passed
                            cmp     ctra,delta wc           ' see if ctra has caught up with delay
                  if_c      jmp     #:chk_int               ' if not, leave it on                                                                 
                  
    
    

    Please let me know if I'm on the right track. If so, I really like it!

    Thanks again for all your help.

    Jim


    I think for the instruction "mov ctra,ctra_mode" to work ctra_mode would need to be in DAT instead of CON.

    Also, ctra will have the settings, so "cmp ctra,delta wc" will not have the count. I think you are looking for phsa. Also, you might have to init phsa to 0. And, frqa might need to be 1, I do not know for sure.

    There is some examples in some of the tutorials that use these counters for things like timing capacitor drain times. Also, these counters work in spin as well as pasm. I would suggest getting the counter to work the way you want in spin first then porting it to pasm.
  • mynet43mynet43 Posts: 644
    edited 2011-07-18 10:20
    Rocky,

    You're absolutely right about the CON vs DAT issue. I fixed that right after I sent the note.

    It's obvious I didn't get it quite right this time. It looked so simple :(

    If someone would spend 5 minutes and write me an example, it would really help.

    I need to do it in assembly code because it's a time critical app tracking an encoder on an AC motor at 5000 steps per rev, and doing other processing at the same time. So every instruction counts. It would be so nice to have the counter doing some of my work.

    I'll take a look at the counter document to see if I can figure it out.

    Thank you for your help and support.

    Jim
  • AribaAriba Posts: 2,690
    edited 2011-07-18 14:29
    mynet43 wrote: »
    If someone would spend 5 minutes and write me an example, it would really help.

    Not a full example but took only 2 minutes:
    'init
          movi ctra,#%11111_000     'count always mode
          mov  frqa,#1             'increment by 1 every clock cycle
          ...
    'loop
          mov  phsa,#0             'reset counter
          ...
          ...
          cmp  phsa,delta  wc      'test if delta time reached
     if_c 'not reached
    
    You can set the mode/pin fields of the CTRx register with movi, movd and movs.
    If the counter is enabled FRQx is added to PHSx every cycle while the mode-depending condition is true. In always mode it's always true.

    Andy
  • mynet43mynet43 Posts: 644
    edited 2011-07-18 14:38
    Hi Andy,

    Thanks so much for your 2-minute reply. It all makes sense now.

    Good example of how to do it with movi.

    Now I see how the three registers work together.

    Thanks again.

    Jim
  • kuronekokuroneko Posts: 3,623
    edited 2011-07-18 22:14
    'init
          [COLOR="red"]cmp  phsa,delta  wc[/COLOR]      'test if delta time reached
     if_c 'not reached
    
    Sorry for the late reply, had a couple of (relaxing) days off. That compare has to be the other way around, phsx in the dst slot will always evaluate to 0 in this case (shadow[phsx]). And you want to reverse the condition to !C.
  • mynet43mynet43 Posts: 644
    edited 2011-07-19 06:49
    Wow, my simple question a couple of days ago has turned into a great tutorial of the little known facts about the Propeller.

    It's amazing how well this forum works.

    Thanks everyone for a great learning experience.

    Jim
Sign In or Register to comment.