View Categories

Raspberry Pi Pico – Use dualcore

RP2040 Dualcore #

The RP2040 chip in Raspberry Pi Pico has dual M0+ processor cores as shown in the system overview of the RP2040 chip below, taken from its datasheet.

A core is a functional unit on the processor capable of executing program code. By having multiple cores on a processor we can execute multiple segments of the code simultaneously. For Raspberry Pi Pico, by default, core0 executes the code whenever we program our board, whereas the second core is on standby. Using the second core enables us to make our Raspberry Pi Pico projects more powerful by completely using a separate thread of execution.

In the RP2040 Chip, we have two cores, 0 and 1. They both share the same memory which they can communicate with. In single core operation, core zero executes all of the code you have written. However, in a multicore program, core zero starts, then hands a series of executable functions to core one to process in parallel.

In most applications, you will want to communicate between the two cores. In the case of the RP2040, there are two FIFO buffers that can be accessed by the cores. A FIFO buffer is a first in first out buffer, which simply means that the first data written to the buffer is the first data read. One of the buffers can only be written to by one core, through a process called pushing, and read by the other core by a process called popping. You can see that neither core can write to, or read from, more than one buffer. This actually makes our life quite straightforward.

In the figure below you can see the depiction of the two cores.

When data is written to the buffer by either one of the cores, an interrupt is triggered in the other core. This signals that data is waiting for it and to tell the interrupt handler to do something with it!


Code to blink 2 LEDS #

import machine
import _thread
from time import sleep

led = machine.Pin("LED", machine.Pin.OUT)
led2 = machine.Pin(2, machine.Pin.OUT)

def CoreTask(): #Core1
    while True:
      led2.toggle()
      sleep(0.2)

_thread.start_new_thread(CoreTask, ()) # Start Core1

while True: #Core0
    led.toggle()
    sleep(0.3)

Multicore problems #

Source: https://forums.raspberrypi.com/viewtopic.php?t=326826

I know there are many posts relating to the still buggy* implementation of threads in the port-rp2 micropython, but since the Pico and Micropython are (mainly?) targeted to newbies as well as students I decided to post here a quick summary of my lessons learned using the two cores with the current Micropython port for the pico (port-rp2).

* – The main issue hampering this implementation is related to memory corruption and crashing and its documented here: https://github.com/micropython/micropython/issues/7124

1. Use global variables as much as possible. Try to avoid creating local variables, specially on Core 1. Remember the problem is memory related so be conscious about how you use the memory and where memory could leak.
2. Use const() for integer variables that will not change ever
3. Use the thread lock to manipulate variables that could be manipulated on both cores
4. Use the gc (garbage collector) module to manually collect memory. Using gc.mem_free() you can check if memory is leaking. You can use gc.collect() in either of the two main loops. If for some reason you need to use it in both cores, use locks.

Example code (could be used as a template):

Code: Select all

import _thread, gc

variable = 0
core0_increment = const(1)
core1_increment = const(2)

lock = _thread.allocate_lock()

def main():  # This is the main loop running on Core 0 
  global variable
  
  # check memory leakage
  print(gc.mem_free())
  gc.collect()      
  
  ...
        
  lock.acquire()
  variable = variable + core0_increment 
  lock.release()
    
  ... 
	
def second_thread():  # This is the main loop running on Core 1
  global variable

  while True:
    # Could also collect memory here
    #gc.collect()
        
    ....
        
    lock.acquire()
    variable = variable + core1_increment 
    lock.release()

_thread.start_new_thread(second_thread, ())

while True:
  main()        
		

These recommendations are based on countless hours of troubleshooting, reading forums posts, reading the issues found in the Micropython rp2 port and a lot of trial and error 

I hope it’s a good starting point but they might not be sufficient, so be ready to test your code extensively.

MicroPython problems with threading don’t seem to be an actual issue of running stuff on the second core and I have run C code on the second core invoked by MicroPython. Only when using ‘_thread’ and running MicroPython code on that second core do problems arise. It is suggested the issue is related to garbage collection but, whatever it is, it would seem to be be purely a MicroPython issue.

Products #


Source #

https://github.com/raspberrypi/pico-micropython-examples/blob/master/multicore/multicore.py