Shop OBEX P1 Docs P2 Docs Learn Events
Passing Parameters from Spin to PASM — Parallax Forums

Passing Parameters from Spin to PASM

doggiedocdoggiedoc Posts: 2,245
edited 2009-10-24 19:05 in Propeller 1
Hi all - need a little help on this one. I've read all the examples and watched the webinar clips but I just can't get my code to work.

I'm trying to pass a variable to an assembly routine that toggles a pin (LED). I started with the example in the manual and trying to expand on it.

{{ AssemblyToggle.spin }}

CON
  _clkmode = xtal1 + pll16x
  _xinfreq = 5_000_000

VAR
  long thePin 

  
PUB Main
  {Launch cog to toggle pin in a loop cycle}
  thePin := 16
  cognew(@Toggle, @thePin) 'Launch new cog

DAT
        {Toggle thePin}

              org 0                             'Begin at Cog RAM addr 0
Toggle       
              mov VAddr, par
              rdlong pin, VAddr
              mov dira, Pin                     'Set Pin to output                              
              mov Time, cnt                     'Calculate delay time
              add Time, #9                      'Set minimum delay here
             
              
Increment    waitcnt Time, Delay               'Wait   
              add Delay, IncValue               'Increment Delay value
              max Delay, MaxDelay wc            'Set Maximum Delay 
              xor outa, Pin                     'Toggle Pin
        if_nc jmp #Decrement                    'jump to Decrement loop
              jmp #Increment                       

Decrement    waitcnt Time, Delay               'Wait    
              sub Delay, IncValue               'Increment Delay value
              min Delay, IncValue wc            'Set minimum Delay 
              xor outa, Pin                     'Toggle Pin
        if_c  jmp #Increment                    'jump to increment loop
              jmp #Decrement

                             
Pin           long      0                       'Pin number
Delay         long       1_000_000               'Clock cycles to delay
IncValue      long          50_000               'increment value
MaxDelay      long      10_000_000               'maximum delay
VAddr         long      0
Temp          long      0
Time          res       1                        'System Counter Workspace




Ultimately I plan to have the SPIN code pass different pin values to new cogs. But I can't seem to get this one working.

Help in understanding is greatly appreciated.

Doc

▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
There are 10 types of people in the world, those that understand binary and those that don't.

Comments

  • kuronekokuroneko Posts: 3,623
    edited 2009-10-22 02:53
    When you use 16 do you mean pin 16 or pin 4 (1 << 4 = 16)? If it's the latter then fine, otherwise you'll have to add some code to create the dira/outa mask, e.g.

    mov temp, #1
    shl temp, pin
    
  • Mike GreenMike Green Posts: 23,101
    edited 2009-10-22 03:03
    Your COGNEW is correct. The first parameter is the address of the start of the code to be loaded into the cog and the second parameter is passed in PAR. It should be the address of a long since it's only 16 bits and the low order 2 bits are forced to zero.

    kuroneko is correct. You need the mov and shl instructions, then you need to change "pin" to "temp" in all the instructions that follow.
  • potatoheadpotatohead Posts: 10,261
    edited 2009-10-22 03:05
    My post removed because I didn't understand where doggiedoc was wanting to go with this.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Propeller Wiki: Share the coolness!
    Chat in real time with other Propellerheads on IRC #propeller @ freenode.net
    Safety Tip: Life is as good as YOU think it is!
  • doggiedocdoggiedoc Posts: 2,245
    edited 2009-10-22 03:37
    @all - thank you for the fast responses -

    So I am passing the address of the "thePin" from spin to "VAddr" in asm and reading the value into "pin" in asm correctly, but need to set a mask on the direction ports. Is that correct?

    PUB Main
      {Launch cog to toggle pin in loop cycle}
      thePin := 18
      cognew(@Toggle, @thePin) 'Launch new cog
    
    DAT
            {Toggle thePin}
    
                  org 0                             'Begin at Cog RAM addr 0
    Toggle       
                  mov VAddr, par
                  rdlong temp, VAddr
                  mov Pin, #1
                  shl Pin, temp
                  mov dira, Pin                     'Set Pin to output                              
                  mov Time, cnt                     'Calculate delay time
                  add Time, #9                      'Set minimum delay here
                 
                  
    Increment    waitcnt Time, Delay               'Wait   
                  add Delay, IncValue               'Increment Delay value
                  max Delay, MaxDelay wc            'Set Maximum Delay 
                  xor outa, Pin                     'Toggle Pin
    
    



    I think I'm missing a concept. Sorry to be so dense but the pin mask seems to work. When I assign a different value to "thePin" in spin the correct LED eventully comes on but it takes a minute or more before it does and then it isn't flashing as it did when the value has hard coded in ASM. Is it a delay value issue I don't understand? I think I am missing something (conceptually) here.

    Thank you all for your patience with my ignorance.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    There are 10 types of people in the world, those that understand binary and those that don't.
  • doggiedocdoggiedoc Posts: 2,245
    edited 2009-10-22 03:49
    Here is the code before I tried to pass the pin value from Spin. It works as expected.

    {{ AssemblyToggle.spin }}
    
    CON
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000
    
    VAR
      long thePin 
    
      
    PUB Main
      {Launch cog to toggle P16 endlessly}
      cognew(@Toggle, 0) 'Launch new cog
    
    DAT
            {Toggle P16}
    
                  org 0                             'Begin at Cog RAM addr 0
    Toggle       
                  mov dira, Pin                     'Set Pin to output                              
                  mov Time, cnt                     'Calculate delay time
                  add Time, #9                      'Set minimum delay here
                 
                  
    Increment    waitcnt Time, Delay               'Wait   
                  add Delay, IncValue               'Increment Delay value
                  max Delay, MaxDelay wc            'Set Maximum Delay 
                  xor outa, Pin                     'Toggle Pin
            if_nc jmp #Decrement                   'jump to Decrement loop
                  jmp #Increment                       
    
    Decrement    waitcnt Time, Delay               'Wait    
                  sub Delay, IncValue               'Increment Delay value
                  min Delay, IncValue wc            'Set minimum Delay 
                  xor outa, Pin                     'Toggle Pin
            if_c  jmp #Increment                    'jump to increment loop
                  jmp #Decrement
    
                                 
    Pin           long      |<16                       'Pin number
    Delay         long       1_000_000               'Clock cycles to delay
    IncValue      long          50_000               'increment value
    MaxDelay      long      10_000_000               'maximum delay
    Time          res       1                             'System Counter Workspace
    
    

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    There are 10 types of people in the world, those that understand binary and those that don't.
  • kuronekokuroneko Posts: 3,623
    edited 2009-10-22 04:15
    FWIW, I took your hard-wired example, passed the address of thePin as the cognew parameter and added 3 lines of code at the beginning:

    rdlong  Time, par
    mov     Pin, #1
    shl     Pin, Time
    
    mov     dira, Pin    ' existing code
    


    I don't see any different behaviour except that I can change the pin index easily. No delay, nothing. If you still have it, post the exact code which shows the 1min delay.
  • Mike GreenMike Green Posts: 23,101
    edited 2009-10-22 04:25
    As is usual with any unexpected delay on the order of 50 seconds, you've set the minimum delay too short and the WAITCNT has to wait for the system clock to roll over. In your case, 9 is too short. That's only a little more than 2 instructions and the WAITCNT probably just misses that by 1 before it starts to compare to the system clock. Try 10 or 12.
  • kuronekokuroneko Posts: 3,623
    edited 2009-10-22 04:31
    Actually, 9 is the minimum but that means the sequence has to be mov/add/waitcnt or you miss it.
  • pgbpsupgbpsu Posts: 460
    edited 2009-10-22 14:25
    doggiedoc-

    There's another way to do this, but it may not be the education you are looking for. You can declare variables in the DAT section only and populate them from spin before launching a cog and they will have the value assigned to them in spin. I'm sure Mike and kuroneko know why this works and maybe they will explain it to both of us.

    Here's a modified version of your code which shows how that works.

    {{ AssemblyToggle.spin }}
    
    CON
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000
    
      Pin = 16
    
    PUB Main
      {Launch cog to toggle pin in a loop cycle}
      PinMask := 1 << Pin   ' set the appropriate pin high for our mask
      cognew(@Toggle, 0) 'Launch new cog
    
    DAT
            {Toggle thePin}
    
                  org 0                             'Begin at Cog RAM addr 0
    Toggle
                  mov  Time, #1 wz    ' use this to set z flag; Z=0
                  muxz outa, PinMask  ' Preset OUTA to LOW         "0"
                  muxnz dira, PinMask ' Set    DIRA to HIGH=OUTPUT "1"
                  mov Time, cnt       ' move cnt into Time
                  add Time, #9        ' Set time when waitcnt should end
    
    
    Increment     waitcnt Time, Delay               'Wait
                  add Delay, IncValue               'Increment Delay value
                  max Delay, MaxDelay wc            'Set Maximum Delay
                  xor outa, PinMask                 'Toggle Pin
            if_nc jmp #Decrement                    'jump to Decrement loop
                  jmp #Increment
    
    Decrement     waitcnt Time, Delay               'Wait
                  sub Delay, IncValue               'Increment Delay value
                  min Delay, IncValue wc            'Set minimum Delay
                  xor outa, PinMask                 'Toggle Pin
            if_c  jmp #Increment                    'jump to increment loop
                  jmp #Decrement
    
    
    PinMask       long      0                        ' mask for the pin we want to toggle
    Delay         long       1_000_000               'Clock cycles to delay
    IncValue      long          50_000               'increment value
    MaxDelay      long      10_000_000               'maximum delay
    Time          res       1                        'System Counter Workspace
    
    



    I've found this useful on many occasions. Maybe someone can explain why this works.

    Regards,
    Peter
  • Mike GreenMike Green Posts: 23,101
    edited 2009-10-22 14:59
    @Peter - A 2K hunk of hub memory is copied to the cog's memory before it's started. That includes instructions and data. Your example sets the value pf PinMask to some value computed in Spin just before the COGNEW is executed. Instead of copying a long of zero as you've written in the source code (and compiled by the Spin compiler,) the COGNEW copies the long value that your routine computes and stores in place of the zero.

    By the way, it takes about 100us to copy the 512 longs and the COGNEW doesn't wait. The new cog is still copying stuff when your main program continues. That's important if you're starting two cogs with the same assembly program and you use this technique to configure each cog's program. You have to wait for the 1st cog to finish loading before you can change the data needed for the 2nd cog.
  • pgbpsupgbpsu Posts: 460
    edited 2009-10-22 15:21
    Mike-

    Thanks for the prompt response and explanation. Especially about the time required to spin up a new cog and the fact that the parent cog doesn't wait. That could produce very strange results if not considered.

    Regards,
    Peter
  • doggiedocdoggiedoc Posts: 2,245
    edited 2009-10-23 01:16
    kuroneko said...
    FWIW, I took your hard-wired example, passed the address of thePin as the cognew parameter and added 3 lines of code at the beginning:

    rdlong  Time, par
    mov     Pin, #1
    shl     Pin, Time
    
    mov     dira, Pin    ' existing code
    


    I don't see any different behaviour except that I can change the pin index easily. No delay, nothing. If you still have it, post the exact code which shows the 1min delay.

    kuroneko - Thank you. It works now, But I'm not quite clear on why? Can you help me understand? Is it the use of the reserved long "Time" instead of local asm variables? If so, can you (or somebody) help me understand why?

    Thanks in advance!
    Doc

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    There are 10 types of people in the world, those that understand binary and those that don't.
  • kuronekokuroneko Posts: 3,623
    edited 2009-10-23 01:33
    doggiedoc said...
    It works now, But I'm not quite clear on why? Can you help me understand? Is it the use of the reserved long "Time" instead of local asm variables? If so, can you (or somebody) help me understand why?
    I don't know what your bad code looked like (as Mike suggested, a 1min delay at 80MHz is usually a missed waitcnt). So I can't say for sure. Maybe you had some debug code left or something restructured?

    As for using Time [noparse][[/noparse] 1], at this point it is unused (it gets filled later with cnt). So I used it as a temporary register. I could have used (nearly) anything else.

    [noparse][[/noparse] 1] The only difference between res and long is that the former isn't initialised.
  • doggiedocdoggiedoc Posts: 2,245
    edited 2009-10-23 02:40
    kuroneko - your patience with me is much appreciated!

    Here's the old code that has the delay issues.
    {{ AssemblyToggle.spin }}
    
    CON
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000
    
    VAR
      long thePin 
    
      
    PUB Main
      {Launch cog to toggle pin in loop cycle}
      thePin := 18
      cognew(@Toggle, @thePin) 'Launch new cog
    
    DAT
            {Toggle thePin}
    
                  org 0                             'Begin at Cog RAM addr 0
    Toggle       
                  mov VAddr, par
                  rdlong temp, Par
                  mov Pin, #1
                  shl Pin, temp
                  mov dira, Pin                     'Set Pin to output                              
                  mov Time, cnt                     'Calculate delay time
                  add Time, #9                      'Set minimum delay here
                 
                  
    Increment    waitcnt Time, Delay               'Wait   
                  add Delay, IncValue               'Increment Delay value
                  max Delay, MaxDelay wc            'Set Maximum Delay 
                  xor outa, Pin                     'Toggle Pin
            if_nc jmp #Decrement                    'jump to Decrement loop
                  jmp #Increment                       
    
    Decrement    waitcnt Time, Delay               'Wait    
                  sub Delay, IncValue               'Increment Delay value
                  min Delay, IncValue wc            'Set minimum Delay 
                  xor outa, Pin                     'Toggle Pin
            if_c  jmp #Increment                    'jump to increment loop
                  jmp #Decrement
    
                                 
    VAddr         long      0
    Pin           long      0                        'Pin number
    Time          res       1                        'System Counter Workspace
    Delay         long       1_000_000               'Clock cycles to delay
    IncValue      long          50_000               'increment value
    MaxDelay      long      10_000_000               'maximum delay
    temp          long      0
    

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    There are 10 types of people in the world, those that understand binary and those that don't.
  • kuronekokuroneko Posts: 3,623
    edited 2009-10-23 02:55
    That looks like an easy one, res must always come last (i.e. after all longs). In this case Time and Delay will get the same address (you can check in the IDE by placing the cursor over the labels after you compiled the program) which isn't really what you want.
  • doggiedocdoggiedoc Posts: 2,245
    edited 2009-10-23 03:06
    You are awesome!! -- I moved "Time res 1" to the last line and it works as expected.

    Thanks! I have learned something. (my goal all along!)

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    There are 10 types of people in the world, those that understand binary and those that don't.
  • SailerManSailerMan Posts: 337
    edited 2009-10-23 11:33
    @Mike You Said "Your COGNEW is correct. The first parameter is the address of the start of the code to be loaded into the cog and the second parameter is passed in PAR. It should be the address of a long since it's only 16 bits and the low order 2 bits are forced to zero."

    I'm still trying to figure out this PAR stuff.. Why would the 2 bits be forced to Zero.. wouldn't that affect what you are passing to ASM. This PAR stuff really confuses me. If you have time can you explain. Also what are the other ways to pass information from spin to ASM.
  • kuronekokuroneko Posts: 3,623
    edited 2009-10-23 11:48
    SailerMan said...
    I'm still trying to figure out this PAR stuff.. Why would the 2 bits be forced to Zero.. wouldn't that affect what you are passing to ASM. This PAR stuff really confuses me.
    It's simple math. You have 32bits available for the coginit parameter (PASM). 4 bits are gone for the cog ID and the cognew/coginit indicator. That leaves 28 bits which have to be shared between actual code entry (14bit) and parameter pointer (14bit). Expressing a 16bit address (64K hub) in 14bits forces you to a 4n alignment. In other words, the lower 2bits are set to zero or it's a long address were a value of e.g. %1001 actually means %100100. So you can't pass a byte or word address which isn't also a long address.

    Have a look in the Propeller Manual (PASM coginit, around page 366). It explains this more detailed.
  • Mike GreenMike Green Posts: 23,101
    edited 2009-10-23 14:05
    @SailerMan - Since PAR is very limited in what can be passed, there are two ways commonly used to pass information from Spin to assembly.

    1) Pass the address in PAR of a small parameter table. This table contains longs (because of the limitation in PAR values), but can contain other data sizes just aligned to a long boundary.

    2) Store the parameters directly into the assembly code before it's started by COGNEW as is shown in this thread with thePin.

    Of course you can use a combination of these two methods as well. You can also pass a small amount of data (up to 14 bits) directly in PAR.
  • doggiedocdoggiedoc Posts: 2,245
    edited 2009-10-24 02:17
    Sorry to drag this on still but it is not immediately obvious to me when shl instruction is called, what actually happens.

    ...
    PUB Main  (loop)
      {Launch cog to toggle LEDs endlessly}
    
      repeat loop from 16 to 22
        thePin :=  loop
        cognew(@Toggle, @thePin) 'Launch new cog
        waitcnt(clkfreq/2 + cnt) ' Pause
      thePin := 23
      coginit (0,@Toggle, @thePin)
      
    ...
    


    So assuming on a particular pass through the above spin loop thePin is assigned a value of 16 (%00000000_00010000). Then in the cognew call the address of thePin is passed into the new cog's par register. So now thePin and par both point to the same address.


    ...
                  org 0                             'Begin at Cog RAM addr 0
    Toggle     rdlong  Time, par
                  mov     Pin, #1
                  shl     Pin, Time
                  mov dira, Pin                     'Set Pin to output                              
                  mov Time, cnt                     'Calculate delay time
                  add Time, #9                      'Set minimum delay here
                 
                  
    Increment    waitcnt Time, Delay               'Wait   
    
    ...
    
    



    Now rdlong copies the value from par to Time but because par is only 14 bits the value of Time is %000000_00000100 - correct? Then we shift the bits left by 1? but we still only have %0000000_00001000 shouldn't we shift left by 2?

    I'm confused.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    There are 10 types of people in the world, those that understand binary and those that don't.
  • doggiedocdoggiedoc Posts: 2,245
    edited 2009-10-24 02:22
    Wait! I see my error now. Pin is the destination and Time is the source. So we are shifting %00000000_00000001 by 4 bits to the left to get %00000000_00010000.

    I think I understand now.

    Actually - I don't think that's it now. My explanation falls apart when I try it with a value of 23 %010111 is %00101 is 5 so shifting %00000001 5 bits is not it. DOH!! I'm confused again.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    There are 10 types of people in the world, those that understand binary and those that don't.

    Post Edited (doggiedoc) : 10/24/2009 2:56:47 AM GMT
  • James LongJames Long Posts: 1,181
    edited 2009-10-24 04:26
    doggiedoc said...
    Wait! I see my error now. Pin is the destination and Time is the source. So we are shifting %00000000_00000001 by 4 bits to the left to get %00000000_00010000.

    I think I understand now.

    Actually - I don't think that's it now. My explanation falls apart when I try it with a value of 23 %010111 is %00101 is 5 so shifting %00000001 5 bits is not it. DOH!! I'm confused again.

    By my understanding.....which please remember I am limited. SHL does exactly that. It shifts the digits "Pin" to the left by the amount of "Time". If time is 1 the starting number (Pin) %0000_0000_0000_0001 will end up %0000_0000_0000_0010

    James L

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    James L
    Partner/Designer
    Lil Brother SMT Assembly Services

    Are you addicted to technology or Micro-controllers..... then checkout the forums at Savage Circuits. Learn to build your own Gizmos!
  • kuronekokuroneko Posts: 3,623
    edited 2009-10-24 06:25
    doggiedoc said...
    Wait! I see my error now. Pin is the destination and Time is the source. So we are shifting %00000000_00000001 by 4 bits to the left to get %00000000_00010000.

    I think I understand now.

    Actually - I don't think that's it now. My explanation falls apart when I try it with a value of 23 %010111 is %00101 is 5 so shifting %00000001 5 bits is not it. DOH!! I'm confused again.
    You pass the hub address of thePin which lets say is 0x490C (4n long-aligned). The address is different from the value of thePin. The upper 14bit of this address get used by cognew/coginit. Once the cog starts and gives control to the user program, par has been loaded with those upper 14bits shifted left by 2 giving you again a 4n aligned long address. Now comes the rdlong from hub address 0x490C which reads the value 23 from that address and places it into Time. Which is then subseqently used to shift #1 left, i.e. 1<<23.

    %0100_1001_0000_11xx    ' [b]address[/b] of thePin (value is 23)
    %--01_0010_0100_0011    ' value used in the PAR field for coginit (upper 14bit)
    %0100_1001_0000_11[b]00[/b]    ' restored address of thePin seen when reading PAR (note the inserted %00)
    
    ' Now a rdlong can obtain the value 23 from the restored address 0x490C.
    


    I'm only guessing as to what your problem here is. So I hope that clears it up [noparse]:)[/noparse]

    Post Edited (kuroneko) : 10/24/2009 6:38:38 AM GMT
  • StefanL38StefanL38 Posts: 2,292
    edited 2009-10-24 07:30
    Hello doggiedoc,

    did you ever use PASD the Propeller Assembler-Debugger

    to watch what is going on ?

    best regards

    Stefan
  • doggiedocdoggiedoc Posts: 2,245
    edited 2009-10-24 11:52
    kuroneko said...

    I'm only guessing as to what your problem here is. So I hope that clears it up [noparse]:)[/noparse]

    @kuroneko - thanks for the explanation
    The code is performing as expected so no problem there. I just am trying to clarify what exactly is happening so I understand the code better.

    Thanks again!

    @Stefan - I have not tried PASD. I'll search for it. I have tried GEAR but can't get it to perform as expected.

    @James- apparently I need someone to draw me some pictures! LOL

    Thank you all,
    Doc

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    There are 10 types of people in the world, those that understand binary and those that don't.
  • StefanL38StefanL38 Posts: 2,292
    edited 2009-10-24 19:05
    PASD is NOT a simulator. It is a REAL debugger

    You download your code with the added debug-kernel and start the code
    if you let it run your PASMcode is executed like always. The debug-kernel
    uses the feature that you can write to every single RAM-adress (self-modifying code)

    best regards

    Stefan
Sign In or Register to comment.