Propeller Tricks & Traps (Last update 21 June 2007)

2456

Comments

  • SteveWSteveW Posts: 246
    edited 2006-04-10 - 18:59:01
    Another phase suggestion - I don't know if the spiffy output shift registers / PLLs can do what you need?

    Steve
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 22,578
    edited 2006-06-03 - 23:01:07
    Hi all,

    PT&T has been updated with two new tricks and one new trap.

    -Phil
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 22,578
    edited 2006-07-11 - 20:19:31
    Here's a subroutine you can use to see how many free cogs remain. It saves having to chase down the accounting by hand when objects are deeply nested.

    PUB free_cogs : free | i, cog[noparse][[/noparse]8], jmp_0
    
      jmp_0 := %010111_0001_1111_000000000_000000000
      repeat while (cog[noparse][[/noparse]free] := cognew(@jmp_0, 0)) => 0 
        free++
      if free
        repeat i from 0 to free - 1
          cogstop(cog&#091i])
      return free
         
    
    


    It works by starting as many new cogs as it can, keeping a count, and then stopping them. The cogs that it starts are all assembly cogs with a single instruction: a jump to location zero (i.e. an endless loop).

    -Phil
  • parskoparsko Posts: 501
    edited 2006-07-30 - 12:28:30
    Okay, I have been quite active on the forum today, so this is it before I go for a bikeride with the lady...

    I have created an object for Phils Cog_Counter subroutine. BTW, it is one clever little subroutine. I have also included an example program showing it in use. Both are attached...

    -Parsko
  • Beau SchwabeBeau Schwabe Posts: 6,422
    edited 2006-08-22 - 19:48:15
    Phil,

    Here is link to a possible Assembly "trap" that could potentially hang someone up.

    Assembly Trap

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    [url=mailto:bschwabe@parallax.com]Beau Schwabe[/url]

    IC Layout Engineer
    Parallax, Inc.
  • John AbshierJohn Abshier Posts: 1,103
    edited 2006-09-13 - 02:00:11
    This one had me scratching my head for a while
    currSpeed -= 20 #> cmdSpeed
    With currSpeed := 1750 and cmdSpeed := 1500 the result of the line above is to set currSpeed to 250, not 1730 as expected
    The line looks so natural, but assignment is last in precedence so it really is
    currSpeed -= (20 #> cmdSpeed)
    the answer is
    currSpeed := currSpeed - 20 #> cmdSpeed
  • Beau SchwabeBeau Schwabe Posts: 6,422
    edited 2006-09-22 - 19:45:29
    Here is a link that touches on timing latency that can be an issue from COG to COG...

    http://forums.parallax.com/showthread.php?p=606446



    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    [url=mailto:bschwabe@parallax.com]Beau Schwabe[/url]

    IC Layout Engineer
    Parallax, Inc.
  • Joel RosenzweigJoel Rosenzweig Posts: 52
    edited 2006-10-11 - 03:02:59
    Perhaps this should have been obvious, but, this problem had me thinking carefully for several evenings! I have a program running using all 8 cogs, and I was performing floating point math in several of them. I had sporadic math failures. It finally occured to me that there was nothing that was saving the context of one cog while servicing the other. So, simultaneous access to the floating point library was causing intermittent problems due to these resource conflicts. When it finally dawned on me tonight, I was able to bracket my transactions with LockSet/LockCLR, and this resolved my problem.

    The general issue to be aware of is that you might have a COG that provides a service to your other cogs (such as the float lib) and you have to make sure that your cogs access these functions using safe and discreet transactions that serialize access to the shared resource. It's a classic problem. I'm surprised I didn't think of it sooner. But, that's how it goes!

    I hope this saves someone a little debugging time in the future.

    Joel-
  • parskoparsko Posts: 501
    edited 2006-11-29 - 08:12:16
    This took me three days to figure out. It's always the little stuff...

    When declaring a variable array, for instance:

    long array

    remember that it is an array of three variables, not 4, as I kept thinking... see below;

    
    [b]long[/b] array[noparse][[/noparse]3]
    .
    .
    .
    text.out(array[noparse][[/noparse]0])
    text.out(array[noparse][[/noparse]1])
    text.out(array[noparse][[/noparse]2])
    text.out(array[noparse][[/noparse]3&#093;)  <- This one doesn't exist in this array!!!
    
    
    



    Three days... I'm not sure if this could be remedied in any way, other than not being a block-head! Anyway, I fixed it and got 178kHz sampling from my MCP3001 at 3.3V (read the specs... I didn't think it possible). I'll post those results elsewhere.

    -Parsko

    EDIT:
    PS- Phil, your code editor ROCKS!!!! I'll be using that again sometime! Thanks for the help!


    Post Edited (parsko) : 11/29/2006 9:10:34 AM GMT

  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 22,578
    edited 2006-11-29 - 08:46:03
    Parsko,

    Try adding a space inside the subscript. Without it, the forum software thinks you want to change the font size. You can also use [ instead of the [noparse][[/noparse]. Or, you can try pasting it into the edit box here, clicking Format, and pasting the result into the forum.

    -Phil

    Post Edited (Phil Pilgrim (PhiPi)) : 11/29/2006 5:50:32 PM GMT
  • Beau SchwabeBeau Schwabe Posts: 6,422
    edited 2006-11-29 - 17:32:58
    parsko,

    You can confirm this by...

    [b]long[/b] array[noparse][[/noparse]0]
    

    ...and noting the number of longs used after pressing F8.
    You can also use this method to 'alias' variables.· For example:

    [b]long[/b] array1[noparse][[/noparse]0], array2[noparse][[/noparse]3]
    

    This causes Array1 to have the same pointer value as Array2, so making
    changes to Array1 would be the same as making any changes to Array2 and vise versa.

    Also, in your original example...

    [b]long[/b] array[noparse][[/noparse]3]
    

    ...If you place another variable on the end like this...

    [b]long[/b] array[noparse][[/noparse]3], testvar
    

    ...Then assigning a value to 'testvar' will fill the position of array[noparse][[/noparse]3] in your example.


    The reason, is when you declare a variable, the number within the [noparse][[/noparse]] represents the number of
    addresses you want to reserve, so Array[noparse][[/noparse]0] is address #1, Array[noparse][[/noparse]1] is address #2, and Array[noparse][[/noparse]2] is address #3.

    When you use a variable that has been declared, the number within the [noparse][[/noparse]] represents the index
    added to the variable address name.· So Array[noparse][[/noparse]3] would represent the 4th address which overflows
    the space or number of addresses that you have reserved for Array.· As a result, the next variable
    'testvar' is picked up as if it were reserved within Array.

    Another thing you can do based on the same principle mentioned above...

    [b]long[/b] Index[noparse][[/noparse]0], Varaible1, Variable2, Variable3 
    . 
    . 
    . 
    text.out(Index[noparse][[/noparse]0]) ' Will display Variable1 
    text.out(Index[noparse][[/noparse]1]) ' Will display Variable2 
    text.out(Index[noparse][[/noparse]2]) ' Will display Variable3
    

    ·...Likewise, assigning values to Varaible1, Variable2, Variable3 will effectively·place them in the 'Index' array even though the Index array has a size of Zero.



    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    [url=mailto:bschwabe@parallax.com]Beau Schwabe[/url]

    IC Layout Engineer
    Parallax, Inc.
  • parskoparsko Posts: 501
    edited 2006-11-29 - 21:13:02
    Beau,

    Much better said than I, thank you. It was one of those AHA moments. Over a few days... Method 1 can write to the variable, method 2 can write to the variable, I can write sequentially to the variable, but I can't increment to the next variable. Every time I try the TV driver goes crazy !AHA! Turns out the next item in memory was the first variable of the TV driver......which is col. But, it didn't seem to have any effect on the color, just black garbage. I could be wrong about that though.

    Keep learnin' every day.

    -Luke

    BTW- Ik houve F8 and F10!!!
  • rokickirokicki Posts: 1,000
    edited 2006-12-29 - 18:07:16
    Which actually brings out a minor defect.

    If you do

    pub start(a) | b[noparse][[/noparse]8000]

    This works (well, it compiles; I can't guarantee it *works*)

    but you do

    pub start(a) | b[noparse][[/noparse]8193]

    you get an error:

    Limit of 4096 local variables exceeded.

    So I'm not sure if the "8000" will fail at runtime in some odd way (that is, the limit is 4096 but it's not enforced there), or if
    the error message is wrong (the real limit is all of memory), or what is going on.
  • rokickirokicki Posts: 1,000
    edited 2006-12-29 - 18:29:06
    Here's another trick. It's something that took me a while to find out, but is obvious in retrospect.

    The immediate value in assembly is a 9-bit value that is not sign extended. So you can initialize
    a register with a value from 0..511 using

    MOV dst,#value

    But what if you want to initialize a register with -1 or some other negative value? Up until now I've
    been using

    MOV dst,negone
    ...
    negone long -1

    But you don't need to do this because NEG has separate source and destinations.
    You can use

    NEG dst,#1

    for instance, to initialize the destination register to -1. With this trick, you can
    initialize any register to an immediate value from -511 through 511.
  • kuismakuisma Posts: 134
    edited 2007-01-01 - 19:52:40
    Potential trap;

    The assembler instruction min returns the largest value.
    The assembler instruction max returns the smallest value.

    Guys, what did you smoke before you thought this out? wink.gif

    Else I have to say I really love the machine architecture and the orthogonality of the instruction set. I have not seen anything as beautiful as this since the PDP-10.
    Thank you guys! Well done!

    If I could wish, I'd like two things. The first is assembler macros, and the second is to separate the machine instructions from the assembler directives in the documentation. It's confusing.

    -- Mikael Kuisma
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 22,578
    edited 2007-01-01 - 20:15:57
    Mikael,

    I've often wondered the same thing about min and max. It's the same way in pBASIC. Yet, there is some logic to it if you think of these operators as limiters rather than selectors: i.e. max sets an upper limit, and min sets a lower limit.

    -Phil
  • kuismakuisma Posts: 134
    edited 2007-01-01 - 20:34:25
    Phil,

    Yes, I do understand the semantics. It is just that it breaks the conventions for min and max. If reversing the well accepted meaning, why not instead use some other mnemonics, like floor and ceil or similar? Well, it is like it is and it is really not a big issue, but still it can be worth mention.

    - Mikael
  • Tracy AllenTracy Allen Posts: 6,444
    edited 2007-01-05 - 22:35:35
    Don't fall into the trap of using the differential modes of the counters when you really want only a single ended output. If both apin and bpin point to the same pin, they will cancel out. The Propeller OR arbitration logic will make the pin high continuously, because in differential mode either apin or bpin is high. I have seen several cases where a method allows for both an apin and a bpin as parameters, and where the method itself uses differential mode. A user might be tempted to make both pins the same in order to get single ended output. Methods should default to single ended mode when apin=bpin.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Tracy Allen
    www.emesystems.com
  • rokickirokicki Posts: 1,000
    edited 2007-01-07 - 19:59:01
    Here's one that cost me many hours to debug.

    addx reg,reg wz

    only sets the z flag if the z flag was already set *and* the new result is zero.

    That is, it does not set the z flag if the z flag was clear.

    This makes sense in its use for extended arithmetic, but I think this should be
    boldfaced in the manual or something, as I really expected it to always set the
    z flag based only on its result.

    I believe this also applies to SUBX and SUBSX, ADDSX, and CMPX and CMPSX.

    Looking at the instruction summary table, I see that it is noted there in the Z
    column.
  • rokickirokicki Posts: 1,000
    edited 2007-01-12 - 22:55:42
    Another IDE/manual errata.

    The manual states on page 159 that symbols must be 32 characters or less.

    But if you try to use symbols that are 31 or 32 characters long, the IDE
    complains that symbols must be 30 characters or less.
  • glentechglentech Posts: 35
    edited 2007-02-07 - 17:50:25
    Why does
    wrlong phsa,variablepointer

    return nothing and

    mov variable,phsa
    wrlong variable,variablepointer

    returns phsa?? (not sure if I got address pointer right for wrlong this I always get them mixed up
  • Paul BakerPaul Baker Posts: 6,351
    edited 2007-02-07 - 18:33:19
    This is something that tripped me up at one point too. PHSA/B is one of those special registers, in that it has a shadow register associated with it. This ordinarily is not apparent since it is a READ/WRITE register and when writing to it's location (via it being specified in the destination field of an instruction) it works correctly and when reading from it's location (via it being specified in the source field of an instruction) also works correctly.

    Where the shadow register comes into play is when you read from the register when it's specified as the destination. This happens when you use WR(LONG/WORD/BYTE). What gets read in this situation is the last value you wrote to the register (in your case you likely didn't write anything to PHSA so it's value is 0). So any modification done by the counters does not affect this shadow register's value.

    The way around this is to copy the contents of PHSA to a temporary register as you have shown in your second example before using WR(LONG/WORD/BYTE).

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    [url=mailto:pbaker@parallax.com]Paul Baker[/url]
    Propeller Applications Engineer

    Parallax, Inc.
  • PaulPaul Posts: 263
    edited 2007-03-15 - 12:56:52
    A Spin CASE Trap:

    I've found that CASE sometimes requires more stack space that the equivelent list of IF..ELSE statements.· I actually had a CASE statement reset the Propellor and/or lock it up after hitting ctrl-F10. Going from stack[noparse][[/noparse]10] to stack[noparse][[/noparse]20] fixed it right away. I suppose if I figured out the stack space required it would help instead of just throwing numbers at it.

    Paul
  • rjo_rjo_ Posts: 1,825
    edited 2007-03-30 - 02:36:28
    Paul,

    I missed your post. (I wish that the sticky threads would rotate the most recent posts to the top of the list like the unsticky ones do.)

    Isn't your result exactly the opposite of what is described (somewhere... I can't find it now) in the User Manual?

    Could you post a complete example?

    Thanks,

    Rich
  • PaulPaul Posts: 263
    edited 2007-04-09 - 13:35:57
    Hi Rich,
    First of all: Whoo Hoo! 100 posts.
    Back to the issue: The following code should be equivalent but apparently I was just on the edge of using all the space in stack[noparse][[/noparse]10] and the CASE just wouldn't work for me but the IF..THEN would. Adding extra stack space fixed it right up.
      
    PRI check_PW1(pw)
    {had to add more stack space to get rid of 'resets'}
      dira[noparse][[/noparse]17]~~              'Phase lock LED output
      case pw
        0..50 :    !outa[noparse][[/noparse]17]  'too low : led blink
        51..300 :  outa[noparse][[/noparse]17]~~ 'ok : led on
        301..600 : !outa[noparse][[/noparse]17]  'off sync: led blink
        other :    outa[noparse][[/noparse]17]~  'too wide : led off 
     
    PRI check_PW2(pw)
    { Takes less stack than 'check_PW1' }
      dira[noparse][[/noparse]17]~~       'Phase lock LED output
      if pw =< 50
        !outa[noparse][[/noparse]17]      'too low : led blink
      if pw > 51 and pw =< 300
        outa[noparse][[/noparse]17]~~     'ok : led on
      if pw > 300 and pw =< 600
        !outa[noparse][[/noparse]17]      'off sync: led blink
      if pw =>600
        outa[noparse][[/noparse]17]~      'too wide : led off
     
    
    

    I think the manual states that the CASE is the prefered method. This is probably·because of readability, not that it takes up less stack space.
  • PyrotomPyrotom Posts: 84
    edited 2007-05-04 - 20:49:41
    I do most of my Propeller programming in assembler, because I need the speed. My application is a controller for a pipe organ, and it is using 7 of the 8 cogs, and I have to move data between the cogs quite a bit via hub memory. The trap which has bit me several times, and which I don't recall seeing mentioned much is that addresses in cog memory are in WORDs, while addresses in hub memory are in BYTEs. This means that an assembler loop which is transferring an array of longs to/from hub memory must increment the hub memory address by 4 and the cog memory address by 1. As an old mainframe guy, where all addresses are in bytes, no matter where they are, this was very counter intuitive. I realize that the 9 bit address fields make this necessary, but I do have to remind myself every time....
  • ericballericball Posts: 774
    edited 2007-05-27 - 23:17:05
    As previously mentioned, RES doesn't actually reserve space (if it's followed by additional code and/or data).· But I also discovered that ORG doesn't reserve space either.· So

    · ORG $000
    Start JMP #init
    ' code and data here
    · ORG $100
    Init·COGID count

    doesn't actually put Init OBJ Start+$100.· And if the code & data after Start runs over $100 longs there's no warning either!


    Post Edited (ericball) : 5/28/2007 4:53:08 PM GMT
  • ericballericball Posts: 774
    edited 2007-05-28 - 16:53:26
    One minor trap (for those of us who spend far too much time programming to the bare metal) is the sense of the Carry condition code for SUB/CMP (at least) is backwards from the 6502 (and probably other processors). So C=1 indicates an unsigned underflow occurred.· (And C=0 if the values are equal.)
    ·
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 22,578
    edited 2007-05-28 - 17:24:36
    I think it's the PIC and, by extension, the SX that got it backwards. Every other processor I've programmed (which set does not include the 6502, BTW) does it like the Propeller does it: the flag is a carry-borrow, instead of a carry-/borrow. The PIC-family micros must implement subtraction as a negate-and-add, instead of a true subtract. At least it helps to think about it that way. This difference cost me days of head-scratching awhile back when I had to modify an old PIC program, since I'd become reconditioned to think of the carry flag as an exception for both addition and subtraction. I still believe that to be the good and natural order of things: when the carry flag is set, some additional attention may have to be paid to the result.

    -Phil
  • mparkmpark Posts: 1,175
    edited 2007-06-19 - 23:24:11
    On page 5 of the pdf there's·"Trick: Indirect JMP addressing" with this line:
    StateMachine JMP 0-0

    I'm having a hard time seeing why it shouldn't be
    StateMachine JMP #0-0



    The code ends with
    JMP #StateMachine

    but wouldn't it work just as well and be faster to say
    JMP StateMachine



    ·
Sign In or Register to comment.