Shop OBEX P1 Docs P2 Docs Learn Events
Propeller Tricks & Traps (Last update 21 June 2007) - Page 6 — Parallax Forums

Propeller Tricks & Traps (Last update 21 June 2007)

12346»

Comments

  • XlogicXXlogicX Posts: 18
    edited 2009-09-12 15:40
    This is my first post. I got trapped (for 2 days) and finally got out of it, so I figured I would share. I hope this hasn’t been posted about somewhere else (because I could have used it so much sooner)
    I was trying to pass information from one cog to another, not a new concept. I was pretty sure I didn’t need semephores (though I experimented with them) since I was only passing one long at a time (using to send status flags back and forth between cogs).
    Here’s what I was doing (wrong):
    One cog would be reading a variable from main memory (to see if there was any pending tasks in the form of a flag in the variable). I passed the variable using par and indexed appropriately.
    Something like:
    :start    Mov    variable, par        ‘get address of variable
    Rdlong    local_var, variable    ‘put the value of that address in local_var
    …..(program)
    Jmp    #:start            ‘do it all over again (check flags again)
    Local_var    long    0
    
    


    My other program would toggle the data on that same memory location (just as a test, a test that failed). The code would do some waitcnt stuff, then toggle the data on that memory, then loop. The command I used for writing was in the form of:
    Wrlong        cogvar, mem_loc   ‘where the mem_loc is the same as variable in previous cog
    
    


    This actually worked fine for a while of execution (of which I watched in GEAR). The symptom that this cuased baffled me. I practically watched what every relevant thing was doing in GEAR to try and pinpoint what was going wrong. At around clock tick 25000 or so, the main memory would just clear itself, almost all of it, including the cog0 program itself. In the end, I really couldn’t find out what exactly was causing the issue. But I did do something to erradicate it.
    Instead of having my shared value in main memory declared in the VAR section. I declared it in a DAT section. I’m sure someone else could have told me this, but the forums overwhelmed me, it felt like it would have taken longer to search for it then just start hacking it myself.
    Anyone know why this happened exactly (or if I may still be doing something wrong and am just lucky for now)?

    The moral of the story (so far), declare shared memory between cogs using DAT, not VAR
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2009-09-12 15:52
    There's nothing wrong with using VAR variables in the way you describe. It's done all the time. The only catch is that you have to make sure that both cogs are working with the same variable, since VARs are replicated among instances of the same object. But if both cogs are started with a pointer to the VARiable from the same object instance that delares the VARiable, that's not a problem. I suspect there's something else going on in your program that caused the problem you saw and, as you say, you just got lucky when switching to a DAT variable. I would suggest starting a new thread and posting your entire program thre so others can help you debug it.

    -Phil
  • Clock LoopClock Loop Posts: 2,069
    edited 2009-09-12 15:54
    XlogicX said...
    The moral of the story (so far), declare shared memory between cogs using DAT, not VAR


    Thats how I did it in my projects program.

    http://forums.parallax.com/forums/default.aspx?f=21&m=376422&p=1&ord=a

    Using DAT for variable space is a topic that DOES NOT get enough attention in the manual or education kit.
  • AlsowolfmanAlsowolfman Posts: 65
    edited 2009-09-18 17:08
    Here is a trick that comes with plenty of its own traps.

    There have been a number of posts where people are using djnz to decrement a pointer, and i thought i would try taking this a little further(sorry to anyone who may have come up with this before me). One can use the line:

    djnz :loop, #:loop

    to jump and post decrement the source register. one can use the following two instructions to find whether an array in cog memory contains a value (a)

    :loop cmp a, 0-0 wc, wz
    if_b djnz :loop, #:loop

    this first executes the compare between a and whatever address is loaded into 0-0, then it decrements the address( after, due to pipelineing), and then executes the compare between a and the register with the next lowest index.

    This does not have that many uses where it would be beneficial, but i am using it in a a cog that has 4 nested loops that each run up to 80 times, so a single instruction can add seconds to the completion time.

    some things that make this harder to use are:
    - the array must be loaded in with the first data point having the highest address
    -once it has completed its operations one must use the movs instruction, and add one to find the address that contained the correct data.

    Here is some working demo code for it that turns pin 5 high to demonstrate that it works:
    CON
      _clkmode      = xtal1 + pll16x
      _xinfreq      = 5_000_000
    pub main
    cognew (@work,0)
    repeat
    dat
    
    org 0
    work          movs :loop, #:d   'this gives the address after the main array
                  nop
                  djnz :loop, #:loop    ' this jumps past the data, it is necessary to start with a djnz so the first cycle is not repeated twice
                  long 0           ' this fulfills the condition in :loop, and prevents it from doing strange things
                  long 1,2,3,4,5,6,7,8
    :d            long 9
    :loop         cmp a, 0-0 wc, wz   
         if_b    djnz :loop, #:loop   ' this works as a jump and post decrement the source register operation
                  movs b, :loop
                  add b, #1   ' this corrects for the post decrement of :loop
                  cmp b, #3 wz, wc    'these two lines verify that it has not stopped due to the zero at the end
            if_e  jmp #noend
                  movs :loop2, b
                  nop
    :loop2        mov c, 0-0   ' this retrieves the number from the array
                  mov b, #1
                  shl b,c
                  mov dira, b
                  mov outa, b
    noend         jmp #noend   ' prevents execution of further data
    
    a long 5
    b long 0
    c long 0
    
    




    I hope this helps someone, or is at least interesting.
  • kuronekokuroneko Posts: 3,623
    edited 2009-12-13 02:20
    • hub ops take 8..23 cycles (hub_window.8.23.spin)
    • waitpxx, waitcnt take 6+ cycles
    • waitvid takes 4+ cycles ([thread=121521]waitvid minimum timing[/thread]) but has a [thread=126874]blind spot of 3 cycles[/thread], i.e. a non-blocking waitvid isn't useful
    • ina/phsx/cnt are sampled during IdSDeR ([post=862241]Call for Clarity from Chip or Beau[/post])
    • the 1st hub slot starts 4 cycles into cog execution
    • the minimal waitcnt adjustment is 9 (or 5 if you re-order the instructions)
      mov     cnt, cnt        ' current time
      add     cnt, #9{14}     ' minimal advance (to avoid full range delay)
      waitcnt cnt, increment  ' run-through
      
      mov     cnt, #5{14}     ' minimal advance (to avoid full range delay)
      add     cnt, cnt        ' current time
      waitcnt cnt, increment  ' run-through
      
    • an instruction placed at $1FF (and then jumped to) isn't executed (probably due to rollover to $000)
      - Update: execution is possible with a [thread=118374]phase jump[/thread]
    • never enable a counter with frqx != 0, you might get more than you asked for ([thread=103032]counter issue[/thread])
    • jmp phsx (counter enabled, frqx != 0) ([thread=118159]jumping counter[/thread])
      - base = phsx at 2nd cycle of jmp (IdSDeR)
      - jump target: base+2*frqx, next instruction at base+3*frqx+1
    • jmp cnt (like jmp phsx, frqx = 1)
    • jmpret dst, phsx|cnt will store $+1 as return address
    • phase jumping to a normal jmpret will cause base+3*frqx+1 being stored as return address
    • phase jumping to any instruction where base+3*frqx+1 == 0 will abort the instruction (nop)
    • waitvid colors, pixels wr will perform colors += pixels
    • waitpeq target, mask wr will perform target += mask
    • waitpne target, mask wr will perform target += (mask + 1)
    • waitpxx/waitcnt structure is IdSDwm.R
      - w is the stage that is propagating the condition
      - m is the earliest match point that the WAIT circuit can test
      - afterwards (after the match), the ALU and the rest of the chip wakes up and performs, followed by R
    • starting cogs N+1..N+3 misses their hub window and is therefore slower than N+4..N+7
      - based on measuring cnt difference just before sync'd coginit and first instruction in cog
      - core overhead 8K+4 (8+511*16+8+4), extra +0..+7: $08 $0A $0C $0E $00 $02 $04 $06
      - additional cycles: ((T - L + 4) & %111) * 2; (T)arget, (L)auncher
    • it may be impossible to catch DUTY pulses generated by a counter within the same cog using waitpeq (e.g. Demoboard, pin16/cog4)
    • same issue applies to DUTY coupled counters (pulse width may be seen ranging from 0..2), so also [post=914151]this comment[/post] regarding pulse sampling
    • wc effect for mov[dis] and jmp[ret] is unsigned borrow
    • wc effect for rdxxxx/wrxxxx, clkset, cogid and cogstop is the same as for coginit (no cog free)
    • PLL output is sync'd to the rising edge of the feeder NCO (starting with the low half of the cycle). Note that /32, /64 and /128 can lock to any rising edge (relative to NCO start), e.g. /128 can lock to 8n+0 .. 8n+7 meaning that starting a PLL in two different cogs will - despite NCOs being in sync - not necessarily sync the PLL output.

    Useful code fragments
    ' phsx read-modify-write issue
    
    mov     temp, phsx     ' temp := counter[phsx]
    shr     temp, #1       ' temp >>= 1
    mov     phsx, temp     ' update shadow and counter
    
    ' is equivalent to
    
    mov     phsx, phsx     ' shadow[phsx] := counter[phsx]
    shr     phsx, #1       ' r: operate on shadow[phsx]
                           ' m: shadow[phsx] >>= 1
                           ' w: update shadow and counter
    
  • LukeHLukeH Posts: 22
    edited 2010-09-12 16:21
    Zombie thread revive...

    Want to communicate immediately between cogs without using hub ram or waiting for the hub cycle? Use the I/O pins. Even if a cog sets a pin to an output, you can still read it as an input. All the cogs are wired to all the pins 100% of the time, like a bus kinda.

    example
    PRI pincounter
      dira[3..0]~~
      repeat
        waitcnt(clkfreq+cnt)
        outa[3..0]++
    
    

    now run pincounter in cog 1, and some other routine in cog2. Cog1 will be counting in binary on pins 3..0. When our other routine in cog2 wants to know how far cog1 has counted, all it has to do is
    value:=ina[3..0]
    
    and it should not have to wait for the hub timer.

    I don't know, am I full of BS? This appears to work when I build/code it, the question is is it really immediate, or even practical.

    Of course now you have to be careful with latency, collisions etc but you could potentially use a single pin as a lock bit, just like the regular locks in the hub. Obviously you're limited to a maximum of 32 bits in parallel transmitted in this way, but you could have one cog serially piping data to another cog using just a few pins and neither of them have to wait for anything.

    That said, this is probably only useful for byte size values or smaller. If you're pumping data serially, it will take at least as many clocks to receive all 16 bits as it would to just wait for the hub timer to come back around, unless you use 16 or all 32 i/o pins. But using 1-8 pins, it appears you can beat the hub timer and communicate these values immediately.

    Why anybody would need to do something like this, I don't know, but it is an interesting feature.

    Gonna have to try this out with ASM and pay close attention to the timings.
  • Alex.StanfieldAlex.Stanfield Posts: 198
    edited 2011-04-15 14:22
    Phipi, here's another trick I submit for checking limits at compile time.

    If you have some calculations/settings in a CON section you can use a DAT section to verify limits or several conditions in the following way:

    DAT
    ORG 1
    FIT logical_condition '(whatever logical condition you wan to be TRUE)
    FIT logical_condition_2 'yet another condition to be met
    FIT logical_condition_3 'as many as you need as long as it evaluates to TRUE/FALSE

    That way if your logical condition(s) evaluates to FALSE (== 0) you get a compile error stating that you exceeded the allowed size but what really means is that you missed the condition.

    This let's the programmer state the conditions that must be met for a correct configuration of constants in the object and rely on the compiler (not the user) to catch them.

    I don't know if this has been discussed before but I found this feature handy for my needs.

    Best regards
    Alex
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2011-04-15 14:52
    'Neat trick, Alex! Thank you for posting it!

    -Phil
  • RaymanRayman Posts: 13,797
    edited 2011-04-15 15:19
    That could be useful actually. Thanks, Alex.
    For example, I have notes on how people should select pins for my FlashPoint, but having it give an error if they do it wrong is nice.

    Does the Prop tool automatically scroll to the line that gives the error?
  • Alex.StanfieldAlex.Stanfield Posts: 198
    edited 2011-04-17 19:37
    Rayman, yes it does. In case of error you will be looking at the line where the logical condition failed; just after the condition and will have the rest of the line highlighted.

    Regards, Alex
  • D.PD.P Posts: 790
    edited 2011-04-18 17:45
    Hi Alex,

    Could you post an example snippet. I'm having a hard time picturing this format but use allot of configuration parameter in the CON section.


    dp
  • Alex.StanfieldAlex.Stanfield Posts: 198
    edited 2011-04-19 11:02
    D.P wrote: »
    Hi Alex,

    Could you post an example snippet. I'm having a hard time picturing this format but use allot of configuration parameter in the CON section.


    dp

    Here's a snippet of a stepper driver I'm working on
     
    CON
        _xinfreq = 5_000_000                  
        _clkmode = xtal1 + pll16x
    ' 
      #0,   _No, _Yes                                  
    ' 
    'Enable lines & features ----------------------------------------------------------
    ' 
    _Enable_Break    = _Yes       'Are Break lines connected?
    _Enable_Right    = _Yes       'Is there a right motor installed?
    ' 
    'Motor characteristics -------------------------------------------------------------
      _Irated   = 2100   '(mA)   The rated max current for your stepper motor
      _RS       = 2800   '(Ohms) Value for the current sensing resistor
    ' 
      _dutyrt = 256 * _RS / _RatedTorqueValue * _Irated / 3300    
    ' 
    ' 
    DAT
      org 1 'Dummy start address to force checks below
     
    '┌─────────────────────────────────────────────────────────────────────────────────────────────────────────┐
      FIT _dutyrt < 65538 'See below                                                                           &#9474;
    '&#9474;^^^ ERROR - if you get a compile error in the above line it's because you missed the calculation &#9474;
    '&#9474;    for _dutyrt and it got bigger than 65537. Adjust either Irate or change your RS value in the circuit &#9474;
    '&#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;
     
    '&#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;
     
      FIT (_Enable_Break==_Yes)|(_Enable_Right==_No) 'See below                                             &#9474;
    '&#9474;^^^ ERROR - if you get a compile error in the above line it's because you enabled the right&#9474;
    '&#9474;    motor AND you didn't enable the break lines                                                          &#9474;
    '&#9474;    (they are needed to block one motor when you drive the other)                                       &#9474;
    '&#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;
     
    

    I posted two different and unrelated conditions to check at compile time

    1- Given some user settable parameters (_RS & _Irate) I'm checking value limits on a calculated constant (_dutyrt)

    2- Given some flags (_Enable...) by design if I have a right motor installed I MUST have the break lines manged since some other control lines are shared. This must be checked at compile time to avoid hard to find bugs.

    In case of missconfiguration it won't compile and the PropellerTool will scroll to the offending line. A brief explanation is sorrounding the check to make it easier for the user to understand and correct the error. You DAT section can be empty of code or variables as you see; and it saves me a lot of trouble :smile:

    Regards, Alex
  • Heater.Heater. Posts: 21,230
    edited 2011-04-19 12:48
    Alex.Stanfield,

    That's brilliant. Makes me think it's time for an obfuscated Spin competition:)
  • Alex.StanfieldAlex.Stanfield Posts: 198
    edited 2011-04-19 14:09
    Heater. wrote: »
    Alex.Stanfield,

    That's brilliant. Makes me think it's time for an obfuscated Spin competition:)

    Heater, I'm honored by your comment, thanks a lot!

    Regarding the spin competition I believe it's a great way to learn. I've learned a lot by browsing the forum. It's an amazing resource and an excellent source of "crowd sourcing".

    Regards Alex
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2014-06-02 16:15
    I didn't think there were any left! But I've been working on a new product whose development requires frequent enabling and disabling blocks of Spin/PASM code as I fumble my way through the labyrinth. Typically, commenting out a block of code is done by enclosing it in braces: {disabled code}. To reenable a block, you delete both braces.

    By accident, I discovered that if the closing brace is commented with an apostrophe '}, you don't need to delete it. This is because Spin comments have a pecking order. Apostrophes inside braces just become part of the block comment without further significance, so the closing brace after the apostrophe is significant. But without the opening brace, an apostrophe-commented closing brace has no special meaning; it's just part of the comment. So all you have to do to disable or enable a block of code ending in '} is to insert or delete the opening brace.

    Okay, okay, it's a small thing, but it made my day. :)

    -Phil
  • PublisonPublison Posts: 12,366
    edited 2014-06-02 16:29
    Neat find Phil!

    Does it go into Tricks and Traps?

    Jim
  • jazzedjazzed Posts: 11,803
    edited 2014-06-02 16:36
    Ya.

    Helps to read other's code sometimes :)

    I've been using that trick since 2009.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2014-06-02 16:42
    jazzed wrote:
    I've been using that trick since 2009.
    Heck, I might have seen it back then, too, but my attention span isn't that long! :)

    -Phil
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2014-06-02 16:47
    Publison wrote:
    Does it go into Tricks and Traps?
    'Moved it here. Thanks for the reminder!

    -Phil
  • TorTor Posts: 2,010
    edited 2014-06-03 02:02
    It is a good trick, and as bst does the rigth thing (as it should) and switches the colouring to and from comment-colour correctly by just changing the first brace -- very useful. :)

    -Tor
  • ErNaErNa Posts: 1,738
    edited 2014-06-03 03:49
    '{
    '}
    

    If you like to remember, where the opening bracket was, don't delete but comment it
  • Wuerfel_21Wuerfel_21 Posts: 4,370
    edited 2019-12-30 19:47
    Time to revive this ol' thread yet again with an addition of mine.

    Given that I don't think I've read any of these before, I'd like to think I came up with them myself, but maybe it's just so obvious that no one bothered to write it down before. Or maybe I looked past it.

    There exists many a situation in which integers have to be compared. Luckily, the propeller has a bunch of CMP* instructions to do this. Here's some interesting ways to use them:

    (Note that the range 1...5 includes 1,2,3 and 4, but not 5!)

    Is signed integer x in range 0...y ?
    This one is obvious, but let's include it nonetheless: Negative values, when treated as unsigned, are very large.
         cmp x,y wc
    if_c jmp #condition_met
    

    Is signed integer x in range (-y)...0 ?
    This one is perhaps less obvious, with it (ab)using the ADD instruction,
    but everything starts to make sense when you realize that CMP is actually just SUB NR in disguise.
         add x,y wc,nr
    if_c jmp #condition_met
    
    This of course also works with ADDABS if you need your range to be y...0 (assuming y < 0)


    Logical AND of multiple compares
    Also farily obvious. All compares but the first are executed only if the previous compare had the desired result. As soon as one fails, all subsequent compares are skipped over.
    This will jump if x1 < y1 AND x2 < y2 AND x3 < y3:
         cmp x1,y1 wc
    if_c cmp x2,y2 wc
    if_c cmp x3,y3 wc
    if_c jmp #condition_met
    
    This concept can also be applied to other instructions that produce a carry/zero output, not just CMP/CMPS and not just IF_C.
    However, all instructions must produce the same "desired" flag results!

    Speaking of which...

    Is unsigned integer x in range a...b ?
    It turns out that CMPSUB's carry out is inverted which makes it perfect for range checking.
         cmpsub x,a wc,nr
    if_c cmp    x,b wc,nr
    if_c jmp #condition_met
    
    Of course, you can AND two of these together as decribed above for a 2D "Is point inside box?" check.
         cmpsub x,box_left   wc,nr
    if_c cmp    x,box_right  wc,nr
    if_c cmpsub y,box_top    wc,nr
    if_c cmp    y,box_bottom wc,nr
    if_c jmp #condition_met
    
    I love this little idiom, which is the whole reason I posted this, lol.
Sign In or Register to comment.