Multicore Example

The previous lesson showed you how to make functions share global variables.  Now,  let's try making two functions run in different Propeller cores, also called cogs, and exchange information through those global variables. 

This next example program launches the adder function into another cog.  The adder function has been modified so that it keeps on adding 1 to the global n variable every 20th of a second. 

The main function monitors the adder activity in that other cog by repeatedly displaying the value of the global n variable.  The adder function changes n 20 times-per-second, so the main function will display a new value each time through its loop even though it doesn't actually do anything to n.

  • Click the Open Project button.
  • Select Open Multicore Example from ...\SimpleIDE\Learn\Examples\C Intro\Functions.
  • Read the code. Note that the main function only sets the value of n once, but that the adder function updates it each time through its loop. 
  • Also note that the main function has a while(1) loop that displays the n variable over and over again.
  • Click the Run with Terminal button and verify that the value of n keeps changing. 
/*
  Multicore Example.c
 
  Launch a function into another cog (processor) and display what it does
  to the global n variable over time.
*/

#include "simpletools.h"                      // Include simpletools

void adder(void *par);                        // Forward declaration

static volatile int t, n;                     // Global vars for cogs to share
unsigned int stack[40 + 25];                  // Stack vars for other cog

int main()                                    // main function
{
  t = 50;                                     // Set values of t & n
  n = 5000;

  // Launch adder function into another cog (processor).
  cogstart(adder, NULL, stack, sizeof(stack));

  // Watch what the other cog is doing to the value of n.
  while(1)
  {
    print("n = %d\n", n);                     // Display result
    pause(100);                               // Wait 1/10 of a second    
  }    
}

// Function that can continue on its
// own if launched into another cog.
void adder(void *par)                         // adder keeps going on its own
{
  while(1)                                    // Endless loop
  {
    n = n + 1;                                // n + 1 each time loop repeats
    pause(t);                                 // Wait for t ms between updates
  }                            
}

 

How it Works

Cog 0 starts at the beginning of main, setting the values of t and n

Next, cog 0 passes the memory address of the adder function along with some other details, to a function called cogstart.  (It's part of the propeller library, which simpletools includes.)  cogstart uses that information to make the next available cog (cog 1) start executing code in the adder function. 

At this point, two cogs are executing different parts of the program.  Cog 0 moves on to start repeating the while(1) loop in the main function and cog 1 starts executing the while loop in the adder function.  Cog 1 updates the value of n every 50 ms because t = 50 and adder's while loop has pause(t).  Meanwhile, cog 0 checks the value of n every 100 ms and displays the value with print.  Since cog 0 is checking more slowly than cog 1 is updating, the value of n it displays increases by about 2 every time.

After the cogstart call, the adder function starts running in another cog.  Meanwhile, the code in the first cog continues onward into the main function's while(1) loop.

 

General Multicore Setup

From this example, we can see the general steps for setting up multicore code, for large or compact memory models (LMM or CMM):

  • Use static volatile to declare global variables for sharing between functions running in different cogs. 
    Example: static volatile int t, n;
  • Declare a stack array with 40 longs (required) + extra for local variables and calculations. Be liberal with the extra memory, and use testing to pare it down after your prototyping is done.
    Example: unsigned int stack[40 + 25];  
  • Launch a function in another cog with cogstart and the following parameters:the address of the function, an optional value (or NULL), the address of the stack array, and the size of the stack array (in bytes).
    Example: cogstart(adder, NULL, stack, sizeof(stack));
  • The cogstart function returns the ID of the destination cog.  If you need to keep track of where a particular function is running, declare an int variable assigned to the cogstart function call. 
    Example: int cog = cogstart(adder, NULL, stack, sizeof(stack));

NOTE: You can get the address of either a function or an array by just using its name.  So, adder( ) calls the function, but adder returns the function's address in memory.  Likewise, stack[0] returns the value of the stack array's zeroth element, but stack returns the array's address in memory.


Did You Know?

LMM & CMM  — This is an example of launching either large memory model (LMM) or compact memory model (CMM) code into another cog.  These memory models fetch machine codes (numbers that represent your program) from the Propeller chip's 32 KB main RAM, and code running in the cog processes them.

COGC & PASM — (memory models) There are other, higher performance options such as COGC (C that fits entirely inside a cog) and PASM (Propeller Assembly Language).  Code for both of these types of programs get loaded into a cog's 2 KB of memory, which increases execution speed because the cog doesn't have to wait for its turn to access main RAM and grab machine codes.

More on Memory Models — Additional examples and more memory model options can be found in the Propeller GCC Demos folder. 


 

Try This

Let's find out which cog the cogstart function launches the adder function into.

  • Try modifying the cogstart function like this:
 

 

Your Turn

Add a subtracter function to the project and launch it into a third cog. Make subtracter decrement a new global variable with the same initial value as n, repeating every t ms. Display the changing values of both variables.

  • Save the project under a new name.
  • Set up the forward declaration for the subtracter function to run in another cog.
  • Add another global variable to share with this new function.  Remember to make it static volatile.
  • Declare a stack array for subtracter, with a different name, such as otherStack.
  • Initialize your new global variable.
  • Add a new cogstart function call to launch the new subtracter function. Be sure to use the name of the new stack array you just created.
  • Write your subtracter function for use in another cog.  And, remember to use the additional global variable you created to hold the result of your subtraction operation.
  • In the main function, add code to the while(1) loop's print statement to also display the value of the new global variable as it gets decremented by the subtracter function.