Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Command: Start the next available cog to run Spin code or Propeller Assembly code.

...

  • SpinMethod is the PUB or PRI Spin method that the new cog should run. Optionally, it can be followed by a parameter list enclosed in parentheses.
  • ParameterList is an optional, comma-delimited list of one or more parameters for SpinMethod. It must be included only if SpinMethod requires parameters.
  • StackPointer is a pointer to memory, such as a long array, reserved for stack space for the new cog. The new cog uses this space to store temporary data during further calls and expression evaluations. If insufficient space is allocated, either the application will fail to run or it will run with strange results.
  • AsmAddress is the address of a Propeller Assembly routine, usually from a DAT block.
  • Parameter is used to optionally pass a value to the new cog. This value ends up in the new cog's read-only Cog Boot Parameter (PAR) register. Parameter can be used to pass a either a single 14-bit value or the address of a block of memory to be used by the assembly routine. Parameter is required by COGNEW, but if not needed for your routine, simply set it to an innocuous value like zero (0).

Explanation

COGNEW starts a new cog and runs either a Spin method or a Propeller Assembly routine within it. For Spin methods, the new cog's RAM is loaded with the Spin Interpreter from the Main ROM, which then fetches and executes the Spin code tokens from the application image in Main RAM. For Propeller Assembly, the targeted assembly code itself is loaded from the application image in Main RAM into the cog's RAM for execution.

...

  1. It launches code into the next available cog
  2. It returns the ID of the cog that it started, if any.

Spin Code (Syntax 1)

To run a Spin method in another cog, the COGNEW command needs the method name, its parameters, and a pointer to some stack space. For example:

Code Block
VAR
  long SqStack[6]                'Stack space for Square cog

PUB Main | X
  X := 2                         'Initialize X
  cognew(Square(@X), @SqStack)   'Launch square cog
  <check X here>                 'Loop here and check X

PUB Square(XAddr)
  'Square the value at XAddr
  repeat                         'Repeat the following endlessly
    long[XAddr] *= long[XAddr]   'Square value, store back
    waitcnt(2_000_000 + cnt)     'Wait 2 million cycles

This example shows two methods, Main and Square. Main starts another cog that runs Square endlessly, then Main can monitor the results in the X variable. Square, being run by another cog, takes the value of XAddr, squares it and stores the result back into XAddr, then waits for 2 million cycles before it does it again. More explanation follows, but the result is that X starts out as 2, and the second cog, running Square, iteratively sets X to 4, 16, 256, 65536 and then finally to 0 (it overflowed 32 bits), all independent of the first cog which may be checking the value of X or performing some other task.

The Main method declares a local variable, X, that is set to 2 in its first line. Then Main starts a new cog, with COGNEW, to run the Square method in a separate cog. COGNEW's first parameter, Square(@X), is the Spin method to run and its required parameter; in this case we pass it the address of the X variable. The second parameter of COGNEW, @SqStack, is the address of stack space reserved for the new cog. When a cog is started to run Spin code, it needs some stack space where it can store temporary data. This example only requires 6 longs of stack space for proper operation (see The Need for Stack Space, below, for more information).

After the COGNEW command is executed, two cogs are running; the first is still running the Main method and the second is starting to run the Square method. Despite the fact that they are using code from the same Spin object, they are running independently. The "<check X here>" line can be replaced with code that uses the value of X in some way.

The Need for Stack Space

A cog executing Spin code, unlike one executing Propeller Assembly code, needs some temporary workspace, called "stack space," to hold operational data such as call stacks, parameters and intermediate expression results. Without this, sophisticated features such as method calls, result values, and complex expressions would not be possible without severe limitations.

The Spin compiler automatically provides stack space for the Propeller Application's initial code, the top-level Spin code of the application. The "free space" following the application's memory image is used for this purpose. However, the compiler is not able to provide distinct blocks of stack space for Spin code that the application may launch on its own, therefore, the application must provide that stack space itself.

Typically, this stack space is provided in the form of a declared global variable meant only for that use, such as the SqStack variable in the example above. Unfortunately, it's difficult to determine just how much stack space should be provided, so when developing an object, it is suggested to initially provide a large amount of longs of memory (like 128 longs or more) and once the object is deemed complete, use an object like the Stack Length object in the Propeller Library to determine the optimal length. See the Stack Length object for further explanation.

Spin Code Can Only be Launched by its Containing Object

In the Spin language, by design, objects must intelligently manage their own data, the methods that operate on that data, the cogs that execute those methods, and the interface that other objects use to affect it. These are all aspects that serve to maintain the integrity of the object and increase its useful and reliable nature.

For these reasons, the object and its designer are notably the best equipped to provide the proper stack space that is required for Spin code being launched into another cog.

To enforce this principle, the COGNEW and COGINIT commands cannot launch Spin code outside of its containing object. This means that a statement like the following will not work as expected.

Code Block
cognew(SomeObject.Method, @StackSpace)

Instead of launching SomeObject.Method into another cog, the Propeller will instead execute SomeObject.Method within the current cog and if that method returns a value, the Propeller will take that value and use it as the address of code to launch with the COGNEW command. This will not result in the code writer's intended effect.

If Method is determined to be code that is truly important to run in another cog, rather than write code like the example above, SomeObject should instead be rewritten similar to the example below.

Code Block
VAR
  long  StackSpace[8]                   'Stack space for new cog
  byte  CogID                           'Stores the ID of new cog 

PUB Start
  Stop                                  'Prevent multiple starts
  CogID := cognew(Method, @StackSpace)  'Launch method in another cog 

PUB Stop
  if CogID > -1
    cogstop(CogID)                      'Stop previously launched cog 

PRI Method
  <some code here> 

The sample above includes two public interface methods, Start and Stop, which an outside object can use to properly launch the object's code into another cog. The important principle is that the object itself is providing this capability, and in doing so, is managing the stack memory required for proper operation. Also note that Method was changed to a private (PRI) method to discourage direct calling from the outside.

Propeller Assembly Code (Syntax 2) )

To run Propeller Assembly code in another cog, the COGNEW command needs the address of the assembly routine and a value that can optionally be used by the assembly routine. For example:

Code Block
PUB Main
  cognew(@Toggle, 0)             'Launch Toggle code

DAT
          org     0              'Reset assembly pointer

Toggle    rdlong  Delay, #0      'Get clock frequency
          shr     Delay, #2      'Divide by 4
          mov     Time, cnt      'Get current time
          add     Time, Delay    'Adjust by 1/4 second
          mov     dira, #1       'set pin 0 to output

Loop      waitcnt Time, Delay    'Wait for 1/4 second
          xor     outa, #1       'toggle pin
          jmp     #Loop          'loop back 

Delay     res     1
Time      res     1 

...

The Parameter Field

...

  1. .

Excerpt
hiddentrue

Start the next available cog and return the new cog's ID.