Shop OBEX P1 Docs P2 Docs Learn Events
@ Simple Concept — Parallax Forums

@ Simple Concept

SteveWoodroughSteveWoodrough Posts: 190
edited 2012-03-29 20:41 in Propeller 1
Attached is a pair of fairly simple objects that I'm using to get the concept of address passing through my thick gear head brain. These objects will run on an unmodified DEMO board.

So far I'm fairly confident retrieving data from cogs that are started from the main method, but I'm having trouble with retrieving data from cogs launched from "lower" methods.

Ultimately what I want to be able to do is bring in the data from the lower object method running in a separate cog, into the main object and display through the PST. I can do that just fine if the method, cog, and PST are launched from the main object. Currently when I try to display the data from the cog launched by the lower object I get 9.

Coupled with all this is an attempt to better understand the use of the @ symbol. In a few cases I’ve used the @ but mostly in the trivial cases where it is equivalent to the variable value it self.

Anyway, first things first, what am I doing wrong where I get a result of 9 when the value should be incrementing by 1 each half second?

Thank You
Steve

{**************************************************
*                 Main01                          *
*                                                 *
***************************************************
The intent of this program is to demonstrate the passing of parameter data using both local and global variables
Addtionally is an attempt at using the @ symbol to pass data between objects.
Runs very easily on a Prop Demo board   
}
CON
 _clkmode        = xtal1 + pll16x
 _xinfreq        = 5_000_000
     
OBJ

LO      :  "LowerObject"
PST     :  "Parallax Serial Terminal"

VAR
long a,b,c
long countstack[15]
byte cog
  
Pub Main  
'   pst.start(115200)

{
   LO.StartBlink(22,5)          'Starts a new cog in a lower object using pin 22 at a rate of 5 hz
   waitcnt(clkfreq+cnt)         
   LO.StartBlinker(18,3)         'Starts a new cog in same lower object using pin 18 at a rate of 3 hz 
   waitcnt(clkfreq+cnt)
 }

'  StartCountUp       'CountUp run in seprate cog from this method

 LO.StartCountUp

{  
 repeat 
   'a:= Long[@b]      'both a:= expressions seem to have the same effect
  ' a:= b
   pst.dec(a)
   pst.char(13)
   waitcnt(clkfreq/2+cnt)
 }  

Pub StartCountUp  :success     
 StopCountup
 success:= (cog := cognew(CountUp, @countstack) + 1)

Pub StopCountup
  if Cog
    cogstop(Cog~ - 1)

Pub CountUp 
      repeat
        b++        
        waitcnt(clkfreq/2+cnt)


{
This is a simple object that can run in a another cog(s) 

}

OBJ


PST     :  "Parallax Serial Terminal"

 
VAR
  long  stack1[10], stack2[10]              'Cog stack space
  long countstack[15]

  long Z
  byte  cog[2]                              'Cog ID's
 
  
Pub StartBlink(mom,dad) :success      'The parameter names can be pretty much anything as long as they match                  
 StopBlink
 success:= (cog[0] := cognew(Blink(mom,dad), @stack1) + 1) 'The cog name, however, must be unique 
Pub StopBlink
  if Cog[0]
    cogstop(Cog[0]~ - 1)
    
Pub Blink(pin,rate)     
      dira[pin]~~   
    repeat
       !outa[pin]
       waitcnt(clkfreq/(rate*2) + cnt) 

Pub StartBlinker(apple,pie) :success    'The parameter names can be pretty much anything as long as they match 
 StopBlinker
 success:= (cog[1] := cognew(Blink(apple,pie), @stack2) + 1)

Pub StopBlinker
  if Cog[1]
    cogstop(Cog[1]~ - 1)

Pub Blinker(pinadd, rateadd) |pin, rate

    pin       :=   long[@pinAdd]      'Each of these expression pairs seems to be the same 
    rate      :=   long[@rateAdd]     '  what is the difference?

'    pin       :=   long[pinAdd]      'Each of these expression pairs seems to be the same 
'    rate      :=   long[rateAdd]     '  what is the difference? 

'    pin       :=   pinadd             'Each of these expression pairs seems to be the same
'    rate      :=   rateadd            '  what is the difference? 

    dira[pin]~~    
    repeat
       !outa[pin]
       waitcnt(clkfreq/(rate*2) + cnt)

Pub StartCountUp  :success     
 StopCountup
 success:= (Cog[2] := cognew(CountUp, @countstack) + 1)

Pub StopCountup
  if Cog[2]
    cogstop(Cog[2]~ - 1)

Pub CountUp 

 pst.start(115200)
 
   dira[16]~~                     'indicates method and cog are in fact operating
    repeat
        z++
        !outa[16]                 'indicates method and cog are in fact operating
      pst.dec(z)
      pst.char(13)
        waitcnt(clkfreq/2+cnt)

Comments

  • turbosupraturbosupra Posts: 1,088
    edited 2012-03-25 21:06
    Hello fellow gear head ... your stack is too small. Replace your vars in your child object with the following and she'll count up like you want her to. I didn't try increasing them individually, but the culprit is going to probably be just one of them, you can figure it out through process of elimination if you'd like.

    As a general rule of thumb, as I've had obnoxious things like this happen to me before, I make all of my stacks 100 longs, and if I ever get to a point in one of my programs where I'm almost out of space, I'll start rationing the stack space out, but that pretty much never happens to me and I've never had an issue with "long space" when I've set them to 100.
    VAR
      long  stack1[100], stack2[100]              'Cog stack space
      long countstack[100]
    
      long Z
      byte  cog[2]                              'Cog ID's
     
      
    
    

    Attached is a pair of fairly simple objects that I'm using to get the concept of address passing through my thick gear head brain. These objects will run on an unmodified DEMO board.

    So far I'm fairly confident retrieving data from cogs that are started from the main method, but I'm having trouble with retrieving data from cogs launched from "lower" methods.

    Ultimately what I want to be able to do is bring in the data from the lower object method running in a separate cog, into the main object and display through the PST. I can do that just fine if the method, cog, and PST are launched from the main object. Currently when I try to display the data from the cog launched by the lower object I get 9.

    Coupled with all this is an attempt to better understand the use of the @ symbol. In a few cases I’ve used the @ but mostly in the trivial cases where it is equivalent to the variable value it self.

    Anyway, first things first, what am I doing wrong where I get a result of 9 when the value should be incrementing by 1 each half second?

    Thank You
    Steve
    
    {**************************************************
    *                 Main01                          *
    *                                                 *
    ***************************************************
    The intent of this program is to demonstrate the passing of parameter data using both local and global variables
    Addtionally is an attempt at using the @ symbol to pass data between objects.
    Runs very easily on a Prop Demo board   
    }
    CON
     _clkmode        = xtal1 + pll16x
     _xinfreq        = 5_000_000
         
    OBJ
    
    LO      :  "LowerObject"
    PST     :  "Parallax Serial Terminal"
    
    VAR
    long a,b,c
    long countstack[15]
    byte cog
      
    Pub Main  
    '   pst.start(115200)
    
    {
       LO.StartBlink(22,5)          'Starts a new cog in a lower object using pin 22 at a rate of 5 hz
       waitcnt(clkfreq+cnt)         
       LO.StartBlinker(18,3)         'Starts a new cog in same lower object using pin 18 at a rate of 3 hz 
       waitcnt(clkfreq+cnt)
     }
    
    '  StartCountUp       'CountUp run in seprate cog from this method
    
     LO.StartCountUp
    
    {  
     repeat 
       'a:= Long[@b]      'both a:= expressions seem to have the same effect
      ' a:= b
       pst.dec(a)
       pst.char(13)
       waitcnt(clkfreq/2+cnt)
     }  
    
    Pub StartCountUp  :success     
     StopCountup
     success:= (cog := cognew(CountUp, @countstack) + 1)
    
    Pub StopCountup
      if Cog
        cogstop(Cog~ - 1)
    
    Pub CountUp 
          repeat
            b++        
            waitcnt(clkfreq/2+cnt)
    

    
    {
    This is a simple object that can run in a another cog(s) 
    
    }
    
    OBJ
    
    
    PST     :  "Parallax Serial Terminal"
    
     
    VAR
      long  stack1[10], stack2[10]              'Cog stack space
      long countstack[15]
    
      long Z
      byte  cog[2]                              'Cog ID's
     
      
    Pub StartBlink(mom,dad) :success      'The parameter names can be pretty much anything as long as they match                  
     StopBlink
     success:= (cog[0] := cognew(Blink(mom,dad), @stack1) + 1) 'The cog name, however, must be unique 
    Pub StopBlink
      if Cog[0]
        cogstop(Cog[0]~ - 1)
        
    Pub Blink(pin,rate)     
          dira[pin]~~   
        repeat
           !outa[pin]
           waitcnt(clkfreq/(rate*2) + cnt) 
    
    Pub StartBlinker(apple,pie) :success    'The parameter names can be pretty much anything as long as they match 
     StopBlinker
     success:= (cog[1] := cognew(Blink(apple,pie), @stack2) + 1)
    
    Pub StopBlinker
      if Cog[1]
        cogstop(Cog[1]~ - 1)
    
    Pub Blinker(pinadd, rateadd) |pin, rate
    
        pin       :=   long[@pinAdd]      'Each of these expression pairs seems to be the same 
        rate      :=   long[@rateAdd]     '  what is the difference?
    
    '    pin       :=   long[pinAdd]      'Each of these expression pairs seems to be the same 
    '    rate      :=   long[rateAdd]     '  what is the difference? 
    
    '    pin       :=   pinadd             'Each of these expression pairs seems to be the same
    '    rate      :=   rateadd            '  what is the difference? 
    
        dira[pin]~~    
        repeat
           !outa[pin]
           waitcnt(clkfreq/(rate*2) + cnt)
    
    Pub StartCountUp  :success     
     StopCountup
     success:= (Cog[2] := cognew(CountUp, @countstack) + 1)
    
    Pub StopCountup
      if Cog[2]
        cogstop(Cog[2]~ - 1)
    
    Pub CountUp 
    
     pst.start(115200)
     
       dira[16]~~                     'indicates method and cog are in fact operating
        repeat
            z++
            !outa[16]                 'indicates method and cog are in fact operating
          pst.dec(z)
          pst.char(13)
            waitcnt(clkfreq/2+cnt)
    
    
  • Duane DegnDuane Degn Posts: 10,588
    edited 2012-03-25 21:45
    Steve,

    One problem I noticed is this line.
    success:= (Cog[[COLOR=#ff0000]2[/COLOR]] := cognew(CountUp, @countstack) + 1)
    

    Since your array "Cog" has two elements, you only want to access elements 0 and 1.
    byte  cog[2]                              'Cog ID's
    

    Element "2" is not part of the array so it will overwrite some other memory position.
  • MagIO2MagIO2 Posts: 2,243
    edited 2012-03-25 23:35
    @Steve:
    You have some open questions in your code comments, so here is my answer:

    Let's assume we do know nothin! What is a variable?
    A variable is a concept that has been introduced with the first interpreters and compilers. Before that we only had memory addresses, then came labels. And it's only purpose is to make programs readable!
    $1f30 := $500
    is for sure worse to read and later on to remember the purpose than this
    CON
       DEFAULT_RPM = $500
    VAR
      long current_RPM
    PUB main
      current_RPM := DEFAULT_RPM
    
    Please note that with the "new" programming style using variables there is no need to know the address at all - well ... in most cases. The compiler takes care of placing the variable somewhere in memory and you can easily use it in your code.

    Nevertheless, with long[], word[] and byte[] we still have the chance to work with addresses and with @ we can ask the compiler where it placed a variable in RAM.
    One of the most important points you have to understand here is:
    long[ @aVariable ] := 10 is the same as
    aVariable := 10
    (in case aVariable is a long)

    So, if both do the same, why do we need both ways?
    Well .. there are many answers for that:
    1. Object-orientation
    2. Function calls where the parameters are called 'by value'
    3. cognew
    but below the line the reason is the same for all cases: you want direct access to memory locations!

    Why:
    1. In objects you don't know anything about the programs that will use this object. So, there is no chance to use variables defined outside of the object. If the object shall work directly on variables defined outside, the only way is to tell the object where to find the variable in memory - so, passing the address.
    2. If you call a function which has parameters, the parameters are copied into local variables on the stack:
      var1 := 10
      var2 := 20
      func_test( var1, var2 )
      var2 := 44
      print( var1, var2 )
      ...
    PUB func_test( par1, par2 )
         print( par1, par2 )
         par1 := 22
         print( par1, par2 )
    
    Output would be in func_test [10, 20] then again in func_test [22, 20] and then in the main [10,44]. The local variables par1 and par2 are initialized with the values of var1 and var2, but are completely independend variables. If you want to have a link, you'd use addresses instead:
      var1 := 10
      var2 := 20
      func_test( @var1, @var2 )
      var2 := 44
      print( var1, var2 )
      ...
    PUB func_test( par1_adr, par2_adr )
         print( long[ par1_adr ], long[ par2_adr ] )
         long[ par1_adr ] := 22                                  ' remember ... it's the same as var1 := 22
         print( long[ par1_adr ], long[ par2_adr ] )
    
    Output would be in func_test [10, 20] then again in func_test [22, 20] and then in the main [22,44].

    3. For cognew of a SPIN function you need to tell the COG which memory can be used as stack - variables defined in the same SPIN-file are accessible by all COGs started here.
    Cognew for PASM-code most times needs to agree on some memory locations which can be used to communicate with the PASM - like when it's waiting for instructions. Variables are unknown by a COG especially as most instructions of a COG operate in COG-RAM whereas the variables are placed in HUB-RAM. Here we have the concept of labels again.

    Now ... coming back to your code:
    Pub Blinker(pinadd, rateadd) |pin, rate
    
        pin       :=   long[@pinAdd]      'Each of these expression pairs seems to be the same 
        rate      :=   long[@rateAdd]     '  what is the difference?
    
    '    pin       :=   long[pinAdd]      'Each of these expression pairs seems to be the same 
    '    rate      :=   long[rateAdd]     '  what is the difference? 
    
    '    pin       :=   pinadd             'Each of these expression pairs seems to be the same
    '    rate      :=   rateadd            '  what is the difference? 
    
    under some circumstances all 3 versions will run, but differently:
    For 1.) You have to call Blinker( apple,pie ) -> value of apple and pie are copied into local variables pinAdd and rateAdd and then you say pin:=long[@pinAdd] which is the same as pin:=pinAdd where pinAdd contains the value of apple
    For 2.) You have to call Blinker( @apple, @pie ) -> the addresses of apple and pie are copied into pinAdd and rateAdd and pin:=long[pinAdd] is the same as pin:=long[@apple] which is pin:=apple
    For 3.) You have to call Blinker( apple, pie ) -> easy pin := pinAdr := apple

    1. and 3. are totally equal, but 2. gives you an addidional possibility. If you change apple or pin while Blinker is running, you can change the ... well ... at least the blink-rate. Changing the pin is not that easy and would need some additional code which also updates DIRA.

    Hope that helped!
    ;o)
  • SteveWoodroughSteveWoodrough Posts: 190
    edited 2012-03-28 18:06
    Thank you all for the thoughtful replies. It turns out the stack was too small. My aplogies for not responding sooneer, I was away on business and would you believe, no internet in the hotel! Now that we have that little matter solved it's on to a better understanding of the concepts explained by MagIO2. I'm sure I will get stumped again soon!
    Best Regards
    Steve
    PS, I can't figure how to change this post from Unsolved to Solved. I thought there was a drop dowm menu some where, but I'm not seeing it.
  • SteveWoodroughSteveWoodrough Posts: 190
    edited 2012-03-28 19:28
    Ok, now that we got the stack size issue taken care of we can get to the root of my quest. Attached are simplified versions of the corrected code mentioned above. The called method, LO.StartCountUp is running in a separate object, separate cog, so therefore the local and global variables associated with the method, and object are unknown to the Main method making the call. The challenge is how to bring that data from the called method and object into the Main object.

    Thank You
    Steve

    {**************************************************
    *                 Main01                          *
    *                                                 *
    ***************************************************
    The intent of this program is to demonstrate the passing of parameter data using both local and global variables
    Addtionally is an attempt at using the @ symbol to pass data between objects.
    Runs very easily on a Prop Demo board   
    }
    CON
     _clkmode        = xtal1 + pll16x
     _xinfreq        = 5_000_000
         
    OBJ
    
    LO      :  "LowerObject"
    PST     :  "Parallax Serial Terminal"
    
    VAR
    long a,b,c            'A few global variables 
    
    Pub Main  
       pst.start(115200)  'Starting and running the PST
    
       a:= lo.StartCountUp          'Start CountUp Method in another Object, in another Cog    
                                    'Count Up represents some other process running in a separate object, separate cog
                                    'whose information is needed in Main method.  Could be a Compass, speed sensor,
                                    'whatever.  In this case it just simulates data changing in another cog
    
                                    'HOW DO I BEST GET THAT DATA BACK INTO THE MAIN OBJECT?
      repeat 
       pst.dec(a)
       pst.char(13)
       waitcnt(clkfreq/2+cnt)
    
    
    {
    This is a simple object that can run in a another cog(s) 
    
    }
     
    VAR
      long  stack1[100], stack2[100]              'Cog stack space
      long countstack[100]
    
      byte  cog[3]                              ' 3 Cog ID's
     
    Pub StartCountUp  :success     
     StopCountup
     success:= (Cog[2] := cognew(CountUp, @countstack) + 1)
    
    Pub StopCountup
      if Cog[2]
        cogstop(Cog[2]~ - 1)
    
    Pub CountUp :  Z
    
         Z:=0
         dira[16]~~                     'indicates method and cog are in fact operating
        repeat
            Z++
            !outa[16]                 'indicates method and cog are in fact operating
            waitcnt(clkfreq/2+cnt)
    
    
    
  • MagIO2MagIO2 Posts: 2,243
    edited 2012-03-28 23:08
    First of all a question for the object:
    Why do you have 3 stacks and 3 bytes for storing the COG ID?
    Your start and stop functions don't support starting 3 COGs!

    Does it make sense to have 3 COGs started by the object?

    Well ... it depends! If all 3 COGs would do different things that work hand in hand it makes sense! For example ... hmmm ... let's say you have a device which has a very complex interface protocol which does not fit into one COG. You could start a COG for IN and another for OUT and the object provides functions for IN and OUT making use of both COGs.
    If on the other hand all 3 COGs do the same thing, only with different Pins, it's better to let the main create as many objects as needed and not code something which is only true for your current project. For example: Full Duplex Serial.

    In your case I guess that starting 3 COGs is only needed in this special project and for the object it would be better not to have this project specific setting hardcoded.
    {
    This is a simple object that can run in a another cog(s) 
    
    }
     
    VAR
      long  stack[100]
      byte  cogID
     
    Pub StartCountUp  :success     
     StopCountup
     success:= (CogID := cognew(CountUp, @stack) + 1)
    
    Pub StopCountup
      if CogID
        cogstop(CogID~ - 1)
    
    Pub CountUp :  Z
    
         Z:=0
         dira[16]~~                     'indicates method and cog are in fact operating
        repeat
            Z++
            !outa[16]                 'indicates method and cog are in fact operating
            waitcnt(clkfreq/2+cnt)
    

    If your main needs 3 of these running it can simply use:
    OBJ
      Lo[3]: "LowerObject"
    
    PUB main
      lo[0].StartCountUp
      lo[1].StartCountUp
      lo[2].StartCountUp
    


    If you need data from the COG to be showing up in the main you have several options. First of all you can use getter and setter functions (assuming that Z is what you are interested in):
    {
    This is a simple object that can run in a another cog(s) 
    
    }
     
    VAR
      long  stack[100]
    [color=green]  long  Z[/color]
      byte  cogID
     
    Pub StartCountUp  :success     
     StopCountup
     success:= (CogID := cognew(CountUp, @stack) + 1)
    
    Pub StopCountup
      if CogID
        cogstop(CogID~ - 1)
    
    [color=green]
    Pub getZ
      return Z
    [/color]
    Pub CountUp
    
         Z:=0
         dira[16]~~                     'indicates method and cog are in fact operating
        repeat
            Z++
            !outa[16]                 'indicates method and cog are in fact operating
            waitcnt(clkfreq/2+cnt)
    
    You have to move Z from the local variable space to object-global space which is the VAR-section. If it's local only CountUp knows where to find Z! Moving it to VAR, the variable is also accessible by the getter-function.

    In your main you can call lo.getZ whenever you need to know tha actual value of Z.

    Or you create the object in a way that it directly changes the value of a variable defined by the main:
    OBJ
      lo : "LowerObject"
    VAR
      myOwnZ
    PUB Main
      lo.StartCountUp( @myOwnZ )
      ...
    

    LowerObject:
    {
    This is a simple object that can run in a another cog(s) 
    
    }
     
    VAR
      long  stack[100]
      word  zPointer
      byte  cogID
     
    Pub StartCountUp( whereIsZ )  :success
     StopCountup
     zPointer := whereIsZ
     success:= (CogID := cognew(CountUp, @stack) + 1)
    
    Pub StopCountup
      if CogID
        cogstop(CogID~ - 1)
    
    Pub CountUp
    
         long[ zPointer ]:=0
         dira[16]~~                     'indicates method and cog are in fact operating
        repeat
            long[ zPointer ]++
            !outa[16]                 'indicates method and cog are in fact operating
            waitcnt(clkfreq/2+cnt)
    
    This way myOwnZ, from main's point of view, is updated by magic ;o) Remember, each instance of an object has it's own VAR-section, so each one has it's own stack, it's own cogID and it's own zPointer.

    One thing is left: If you have 3 instances of this object all 3 would still operate in the same indicator-pin (PIN 16). Maybe you want to fix that by yourself?
  • SteveWoodroughSteveWoodrough Posts: 190
    edited 2012-03-29 18:51
    Oh my gosh, this is starting to make sense! THANK YOU!! for taking the time to explain it so well and with two different approaches. Sorry about the Cog [3], that was a bit of code that did not get trimmed down.
    Steve

    How do I change this to SOLVED?
  • SapiehaSapieha Posts: 2,964
    edited 2012-03-29 20:41
    Hi Steve.Reedit

    Yours first post in Advanced mode

    Oh my gosh, this is starting to make sense! THANK YOU!! for taking the time to explain it so well and with two different approaches. Sorry about the Cog [3], that was a bit of code that did not get trimmed down.
    Steve

    How do I change this to SOLVED?
Sign In or Register to comment.