Shop OBEX P1 Docs P2 Docs Learn Events
Stack Space and Program Flow Questions — Parallax Forums

Stack Space and Program Flow Questions

laser-vectorlaser-vector Posts: 118
edited 2010-08-18 21:56 in Propeller 1
I've got a few questions about how stack space, loading new cogs and calling objects differ from each other and how that effects the way the propeller runs the code.

assume this program only as an example:

VARIATION 1:
{VARIATION #1}

CON

  { ==[ CLOCK SET ]== }
  _CLKMODE      = XTAL1 + PLL16X
  _XINFREQ      = 6_250_000

  RX_pin = 12
  TX_pin = 11

OBJ

  DEBUG  : "FullDuplexSerial"    
  PCRX   : "PROP_COMM_RX"
  PCTX   : "PROP_COMM_TX" 

VAR

  LONG tx_stack[30]             ' probably be safe at 22 longs

PUB Main | rx_buff, i, seed, v

  DEBUG.start(31, 30, 0, 57600)
  waitcnt(clkfreq + cnt)  
  DEBUG.tx($D)
  DEBUG.str(string("If 'Warning' shows up, input does not match expected value",$D))

  cognew(tx, @tx_stack)         ' start transmit cog

'' -----------------------------------------------------
'' setup recieve cog

  rx_buff := PCRX.recieve(RX_pin)                       ' start RX cog
  seed := $9876_5432                                    ' make sure it is the same as tx cog

  REPEAT
    PCRX.waitrx_wd(100)                                 ' wait up to 100ms for information to be recieved.
    REPEAT i FROM 0 TO constant(PCRX#BUFFER_SIZE - 1)
      IF (?seed <> long[rx_buff][i])                    ' if information differs from what it is supposed to be
        DEBUG.str(string("Warning",$D)) 
    DEBUG.str(string("Buffer Good",$D))

PUB tx | tx_buff, i, seed
'' setup transmit cog
                        
  tx_buff := PCTX.send(TX_pin)                          ' start TX cog
  seed := $9876_5432                                    ' make sure it is the same as rx cog    
    
  REPEAT
    REPEAT i FROM 0 TO constant(PCTX#BUFFER_SIZE - 1)
      long[tx_buff][i] := ?seed                         ' fill buffer
    PCTX.transmitwait_wd(100)                           ' send buffer, then wait up to 100ms for it to complete
    waitcnt(120000 + cnt)                               ' give enough time for RX cog to "analize" data

I see that the MAIN method launches the TX method in a new cog (via cognew) with a runtime stack space 30 elements wide. however MAIN does not launch the RX method in a new cog and doesnt define any stack space.
where is the RX method running (in cog 0??) and what is the stack size and address??


Ok so im intrested in knowing what the difference is between the program above and just launching two cogs to do RX and TX. for example

VARIATION 2:
{VARIATION #2}
CON

  { ==[ CLOCK SET ]== }
  _CLKMODE      = XTAL1 + PLL16X
  _XINFREQ      = 6_250_000

  RX_pin = 12
  TX_pin = 11

OBJ

  DEBUG  : "FullDuplexSerial"    
  PCRX   : "PROP_COMM_RX"
  PCTX   : "PROP_COMM_TX" 

VAR

  LONG tx_stack[30]             ' probably be safe at 22 longs
  LONG rx_stack[30]
  
PUB Main

  DEBUG.start(31, 30, 0, 57600)
  waitcnt(clkfreq + cnt)  
  DEBUG.tx($D)
  DEBUG.str(string("If 'Warning' shows up, input does not match expected value",$D))

  cognew(tx, @tx_stack)         ' start transmit cog
  cognew(rx, @rx_stack)         ' start receive cog 

PUB rx | rx_buff, i, seed, v 

'' -----------------------------------------------------
'' setup recieve cog

  rx_buff := PCRX.recieve(RX_pin)                       ' start RX cog
  seed := $9876_5432                                    ' make sure it is the same as tx cog

  REPEAT
    PCRX.waitrx_wd(100)                                 ' wait up to 100ms for information to be recieved.
    REPEAT i FROM 0 TO constant(PCRX#BUFFER_SIZE - 1)
      IF (?seed <> long[rx_buff][i])                    ' if information differs from what it is supposed to be
        DEBUG.str(string("Warning",$D)) 
    DEBUG.str(string("Buffer Good",$D))

PUB tx | tx_buff, i, seed
'' setup transmit cog
                        
  tx_buff := PCTX.send(TX_pin)                          ' start TX cog
  seed := $9876_5432                                    ' make sure it is the same as rx cog    
    
  REPEAT
    REPEAT i FROM 0 TO constant(PCTX#BUFFER_SIZE - 1)
      long[tx_buff][i] := ?seed                         ' fill buffer
    PCTX.transmitwait_wd(100)                           ' send buffer, then wait up to 100ms for it to complete
    waitcnt(120000 + cnt)                               ' give enough time for RX cog to "analize" data
  

so which cogs are being used now?? and what happens to the main method, does it stay dormant in cog 0 just taking up space??


lastly what if this were done with an object call??

VARIATION 3:
{VARIATION #3}
CON

  { ==[ CLOCK SET ]== }
  _CLKMODE      = XTAL1 + PLL16X
  _XINFREQ      = 6_250_000

  RX_pin = 12
  TX_pin = 11

OBJ

  DEBUG  : "FullDuplexSerial"    
  PCRX   : "PROP_COMM_RX"
  PCTX   : "PROP_COMM_TX"
  
  RX     : "RXfile"                'RXfile's main method contains the same instructions as the previous rx method
  TX     : "TXfile"                'TXfile's main method contains the same instructions as the previous tx method
PUB Main

  DEBUG.start(31, 30, 0, 57600)
  waitcnt(clkfreq + cnt)  
  DEBUG.tx($D)
  DEBUG.str(string("If 'Warning' shows up, input does not match expected value",$D))

  RX.main(RX_pin)
  TX.main(TX_pin)

ok so what makes this different?
do RX and TX launch in different cogs??
how do i know their stack size and address (if those even matter here)??
and what happens to the original Main method?????


This is something thats been baffling me for some time. i'm really trying to visualize the flow of things and where they currently are running. plus pros and cons to launching cogs and calling other objects.

And id really like to give thanks to everyone, youve all been awesome support!!

Comments

  • Mike GreenMike Green Posts: 23,101
    edited 2010-08-18 21:56
    FullDuplexSerial launches a combined receive / transmit routine in its own cog and provides methods (like .tx and .rx) to communicate via buffers declared within FullDuplexSerial. This is used for debugging I/O.

    Without the source for PROP_COMM_RX and PROP_COMM_TX, it's impossible to tell what they're doing internally, but I suspect they're starting their own cogs or cog. If their cog code is in assembly, there's no stack needed. Only Spin code uses an implicit stack.

    The most important thing to remember is that objects and cogs are two separate (orthogonal) concepts. Objects are a way to encapsulate some function. There's nothing about objects that requires the use of a cog. If part of your program calls an object's method, that method is executed by the same cog that executes the code that calls it.

    Similarly, you can have a program completely contained in the main object that uses all the cogs. Each cog running a Spin method has to have its own stack area.

    Most commonly, objects are used to implement I/O device drivers and frequently these will use a cog to implement the low level I/O functionality. Whether these objects use a cog or not is mostly hidden from the caller. The driver initialization routine usually does the COGNEW and the object has the declaration for the stack area, any buffers, and any other shared variables.
Sign In or Register to comment.