Diving Into Multicore

You probably didn't pick up Propeller HAT because it was some ordinary, run-of-the-mill Microcontroller board. No siree. You want to take advantage of those 8 cores and play to the Propeller's strengths.

Well, fortunately, it's really not all that hard.

What's a cog, got to do, got to do with it?

In the world of Propeller, where almost everything is a pun based on the name of the chip, you'll often find the cores referred to as "cogs". The Propeller has 8 cogs, and ( wait for it... ) a "hub".

Propeller HUB and Cog Diagram

The Hub

The Hub is the central controller of the Propeller, it houses the lion's share of the chips memory/storage - 8192, 32-bit longs ( also known as 32kb or 32,768 bytes ). The Hub's job is to dish out tasks to the Cogs, control their access to its memory and make sure they don't fight over resources or tread on each other's toes.

In other words, the Hub is the boss! What it doesn't have, however, is the ability to run code. The Hub sits in its tiny silicon throne and dishes out commands, but it can't execute any itself. The Hub relies wholly and totally on the Cogs to carry out their instructions and only gets involved when they need to communicate, share data or receive new sets of instructions.

The Hub also has some other fancy features, such as configuration registers and lock bits- we won't worry about those yet!

The Cogs

Now. Onto the Cogs. Each Cog has 512, 32-bit longs ( also known as 2kb or 2048 bytes ) of memory. This memory serves as both storage for the instuctions that Cog has been told to execute, and as running memory for remembering what it's doing.

2kb is a pretty decent amount of memory for a Cog, but you have to be mindful that the larger your running code is, the less space will be available for it to store what it's working on. Think of it like filling a frying pan- the more food you pop into the frying pan the less space you have to stir it!

Alongside the memory, each Cog has its own CPU capable of executing the instructions its given, it also has its own counters and video generator- but we'll worry about those later too.

The most important thing to remember is that each one of the 8 Cogs has its own DIRA and OUTA register for setting the direction and output state of all 32 IO pins on the Propeller. When you're passing a function or code to a Cog you need to make sure that the Cog sets up its own DIRA register, since setting the direction of a pin elsewhere will likely happen in another Cog and lead to confusing results!

Now, for some code!

CON
  _CLKMODE = xtal1 + pll16x
  _XINFREQ = 6_000_000

  MY_LED_PIN = 0 ' Use pin A0
  SECOND_PIN = 1 ' Use pin A1

VAR
  long stack[128] ' Allocate 128 longs of stack space for our new COG

PUB main
  cognew(blink_second_pin, @stack) ' Start a new COG

  DIRA[MY_LED_PIN] := 1 ' Set the LED pin to an output

  repeat ' Repeat forever
    OUTA[MY_LED_PIN] := 1   ' Turn the LED on
    waitcnt(cnt + clkfreq)  ' Wait 1 second
                            ' cnt is the clock tick counter,
                            ' clkfreq is the number of clock ticks in a second
    OUTA[MY_LED_PIN] := 0   ' Turn the LED MY_LED_PIN
    waitcnt(cnt + clkfreq)  ' Wait 1 second

PUB blink_second_pin
  DIRA[SECOND_PIN] := 1

  repeat
    OUTA[SECOND_PIN] := 1    ' Turn second LED on
    waitcnt(cnt + clkfreq/2) ' Wait half a second
    OUTA[SECOND_PIN] := 0    ' Turn second LED off
    waitcnt(cnt + clkfreq/2) ' Wait half a second

You'll notice that the simple act of launching a new Cog has introduced a lot of extra code to our program. Let's break it down again...

blinksecondpin

The majority of this is the work we actually want the Cog to do, which is all contained within the PUBlic method blink_second_pin. It's very similar to our main public method because it's blinking another pin.

To make it clear that the second Cog is running, we pick a different pin, and a faster blink rate.

As before, we use a CONstant to define which pin we'd like to blink, in this case in A1.

clkfreq/2

Since clkfreq is the number of clock ticks in a single second, we can incur a wait of half a second simply by dividing it by two.

DIRA and OUTA strike again

Every single Cog has its own DIRA and OUTA registers, so it's absolutely imperative to remember to set the direction of the pin you want to use before trying to change its value. If you forget to set a pin to an output (DIRA[PIN] := 1) then it wont output its value when you change it (OUTA[PIN] := X).

cognew

This example introduces another feature of SPIN, the cognew1 command. Cognew does what it says on the tin, and initiates a new Cog to run the SPIN method that you pass it. Cognew has a cousin known as coginit which also runs a spin method in a Cog, but instead of finding the first available unused Cog it gives a new task to the one you specify.

At this point, it's important to note that the code calling cognew is running in a Cog and not the Hub. So this means you have 7 remaining Cogs to run 7 additional tasks.

The cognew command has a second required parameter, the stack pointer...

stack

A new VAR section has made its way into this example too. In this instance our only variable is the stack2 we want to reserve for our new Cog. This is a portion of Hub memory which the Cog can use to store temporary data that it needs to carry it its task.

When we call cognew we pass it the stack variable, the @ prefix gives us the address of our stack array, rather than its value. Giving it the address means that the newly iniated Cog will know that it's safe to store values at this location. The stack should always be sized appropriately for whatever task the Cog is allocated. At this stage we don't really care about optimisation, so we'll use a "safe" and spacious stack that's 128 32-bit longs in size.

If you've ever come across the term "stack overflow", it's what happens when an application tries to use more stack space than there has been allocated for it- this means it'll try and store things in memory locations after the stack which have potentially been reserved for something else.

Wiring up your Propeller HAT

To see the result of this code, you'll need a couple of LEDs plugged into outputs A0 and A1, like so:

Propeller Multicore Layout

Further Reading

  • 1 A little more detail about Cogs: http://www.parallax.com/propeller/qna/Default.htm#QnaTopics/QnaCogs.htm
  • 2 Learn more about the need for Stack on Propeller Cogs: http://www.parallax.com/propeller/qna/Default.htm#CodeTeqTopics/StackSpace.htm

Shopping basket

Need something for this project? You can use the links below to add products to your Pimoroni Shop basket for easy checkout.

Propeller HAT
£15.00
LED - 5mm - pack of 10
Red £1.00
LED - 5mm - pack of 10
Blue £1.00
LED - 5mm - pack of 10
Green £1.00
LED - 5mm - pack of 10
Yellow £1.00
Jumper Jerky Junior
Male to Male £2.50
Jumper Jerky Junior
Male to Female £2.50
Jumper Jerky Junior
Female to Female £2.50
Want to checkout or change something? Click here to view your cart.

Tutorial
Intermediate
Propeller HAT, Raspberry Pi, Micro-controller, Parallax Propeller, SPIN

Phil Howard

phil@pimoroni.com
@gadgetoid
Phil is Pimoroni's software guru, instantly recognisable by his somewhat pirate-themed moustache growing attempts. Usually found buried neck deep in Python libraries, he's also been known to escape on occasion and turn out crazy new products. If you need a helping hand, he's a prolific Twitter user and rampages around the forums like a T-Rex. Ask him if you need help with Pimoroni's software libraries, or Propeller HAT.