Skip to content

Commit

Permalink
Add multicore safety, FIFO, update pico-sdk (#122)
Browse files Browse the repository at this point in the history
Update pico-sdk to 1.1.2

Add methods to block the opposite core while doing flash updates.
Ensure opposite core is stopped in LittleFS and EEPROM while doing
flash updates.

Update documentation with new calls.
  • Loading branch information
earlephilhower committed May 7, 2021
1 parent 6cf0b30 commit 70a30dc
Show file tree
Hide file tree
Showing 9 changed files with 187 additions and 12 deletions.
98 changes: 97 additions & 1 deletion cores/rp2040/RP2040.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,96 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/

#include <hardware/pio.h>
#include <hardware/clocks.h>
#include <hardware/irq.h>
#include <hardware/pio.h>
#include <pico/multicore.h>
#include <pico/util/queue.h>

class _MFIFO {
public:
_MFIFO() { /* noop */ };
~_MFIFO() { /* noop */ };

void begin(int cores) {
constexpr int FIFOCNT = 8;
if (cores == 1) {
_multicore = false;
return;
}
mutex_init(&_idleMutex);
queue_init(&_queue[0], sizeof(uint32_t), FIFOCNT);
queue_init(&_queue[1], sizeof(uint32_t), FIFOCNT);
_multicore = true;
}

void registerCore() {
multicore_fifo_clear_irq();
irq_set_exclusive_handler(SIO_IRQ_PROC0 + get_core_num(), _irq);
irq_set_enabled(SIO_IRQ_PROC0 + get_core_num(), true);
}

void push(uint32_t val) {
while (!push_nb(val)) { /* noop */ }
}

bool push_nb(uint32_t val) {
// Push to the other FIFO
return queue_try_add(&_queue[get_core_num()^1], &val);
}

uint32_t pop() {
uint32_t ret;
while (!pop_nb(&ret)) { /* noop */ }
return ret;
}

bool pop_nb(uint32_t *val) {
// Pop from my own FIFO
return queue_try_remove(&_queue[get_core_num()], val);
}

int available() {
return queue_get_level(&_queue[get_core_num()]);
}

void idleOtherCore() {
if (!_multicore) return;
mutex_enter_blocking(&_idleMutex);
_otherIdled = false;
multicore_fifo_push_blocking(_GOTOSLEEP);
while (!_otherIdled) { /* noop */ }
}

void resumeOtherCore() {
if (!_multicore) return;
mutex_exit(&_idleMutex);
_otherIdled = false;
// Other core will exit busy-loop and return to operation
// once otherIdled == false.
}

private:
static void __no_inline_not_in_flash_func(_irq)() {
multicore_fifo_clear_irq();
noInterrupts(); // We need total control, can't run anything
while (multicore_fifo_rvalid()) {
if (_GOTOSLEEP == multicore_fifo_pop_blocking()) {
_otherIdled = true;
while (_otherIdled) { /* noop */ }
break;
}
}
interrupts();
}
bool _multicore = false;

mutex_t _idleMutex;
static volatile bool _otherIdled;
queue_t _queue[2];

static constexpr int _GOTOSLEEP = 0x66666666;
};

class RP2040 {
public:
Expand All @@ -33,8 +121,16 @@ class RP2040 {
static int f_cpu() {
return clock_get_hz(clk_sys);
}

void idleOtherCore() { fifo.idleOtherCore(); }
void resumeOtherCore() { fifo.resumeOtherCore(); }

// Multicore comms FIFO
_MFIFO fifo;
};

extern RP2040 rp2040;

// Wrapper class for PIO programs, abstracting common operations out
// TODO - Make dualcore safe
// TODO - Add unload/destructor
Expand Down
9 changes: 9 additions & 0 deletions cores/rp2040/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@
#include <pico/stdlib.h>
#include <pico/multicore.h>

RP2040 rp2040;

volatile bool _MFIFO::_otherIdled = false;

extern void setup();
extern void loop();

Expand All @@ -35,6 +39,7 @@ void initVariant() { }
extern void setup1() __attribute__((weak));
extern void loop1() __attribute__((weak));
static void main1() {
rp2040.fifo.registerCore();
if (setup1) {
setup1();
}
Expand Down Expand Up @@ -62,8 +67,12 @@ extern "C" int main() {
#endif

if (setup1 || loop1) {
rp2040.fifo.begin(2);
multicore_launch_core1(main1);
} else {
rp2040.fifo.begin(1);
}
rp2040.fifo.registerCore();

setup();
while (true) {
Expand Down
3 changes: 2 additions & 1 deletion cores/rp2040/wiring_private.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@ extern "C" void interrupts() {
// ERROR
return;
}
restore_interrupts(_irqStack[get_core_num()].top());
auto oldIrqs = _irqStack[get_core_num()].top();
_irqStack[get_core_num()].pop();
restore_interrupts(oldIrqs);
}

extern "C" void noInterrupts() {
Expand Down
64 changes: 64 additions & 0 deletions docs/multicore.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,69 @@ By adding a ``setup1()`` and ``loop1()`` function to your sketch you can make
use of the second core. Anything called from within the ``setup1()`` or
``loop1()`` routines will execute on the second core.

``setup()`` and ``setup1()`` will be called at the same time, and the ``loop()``
or ``loop1()`` will be started as soon as the core's ``setup()`` completes (i.e.
not necessarily simultaneously!).

See the ``Multicore.ino`` example in the ``rp2040`` example directory for a
quick introduction.

Pausing Cores
-------------

Sometimes an application needs to pause the other core on chip (i.e. it is
writing to flash or needs to stop processing while some other event occurs).

void rp2040.idleOtherCore()
~~~~~~~~~~~~~~~~~~~~~~~~~~~

Sends a message to stop the other core (i.e. when called from core 0 it
pauses core 1, and vice versa). Waits for the other core to acknowledge
before returning.

The other core will have its interrupts disabled and be busy-waiting in
an RAM-based routine, so flash and other peripherals can be accesses.

**NOTE** If you idle core 0 too long, then the USB port can become frozen.
This is because core 0 manages the USB and needs to service IRQs in a
timely manner (which it can't do when idled).

void rp2040.resumeOtherCore()
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Resumes processing in the other core, where it left off.

Communicating Between Cores
---------------------------

The RP2040 provides a hardware FIFO for communicating between cores, but it
is used exclusively for the idle/resume calls described above. Instead, please
use the following functions to access a softwarae-managed, multicore safe
FIFO.

void rp2040.fifo.push(uint32_t)
~~~~~~~~~~~~~~~~~~~~~~~~~~

Pushes a value to the other core. Will block if the FIFO is full.

bool rp2040.fifo.push_nb(uint32_t)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Pushes a value to the other core. If the FIFO is full, returns ``false``
immediately and doesn't block. If the push is successful, returns ``true``.

uint32_t rp2040.fifo.pop()
~~~~~~~~~~~~~~~~~~~~~

Reads a value from this core's FIFO. Blocks until one is available.

bool rp2040.fifo.pop_nb(uint32_t *dest)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Reads a value from this core's FIFO and places it in dest. Will return
``true`` if successful, or ``false`` if the pop would block.

int rp2040.fifo.available()
~~~~~~~~~~~~~~~~~~~~~~~~~~~

Returns the number of values available in this core's FIFO.
Binary file modified lib/libpico.a
Binary file not shown.
7 changes: 4 additions & 3 deletions libraries/EEPROM/EEPROM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,12 @@ bool EEPROMClass::commit() {
if (!_data)
return false;

// TODO - this only stops IRQs on 1 core, need to do it on both
uint32_t save = save_and_disable_interrupts();
noInterrupts();
rp2040.idleOtherCore();
flash_range_erase((intptr_t)_sector - (intptr_t)XIP_BASE, 4096);
flash_range_program((intptr_t)_sector - (intptr_t)XIP_BASE, _data, _size);
restore_interrupts(save);
rp2040.resumeOtherCore();
interrupts();

return true;
}
Expand Down
12 changes: 8 additions & 4 deletions libraries/LittleFS/src/LittleFS.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -173,20 +173,24 @@ int LittleFSImpl::lfs_flash_prog(const struct lfs_config *c,
lfs_block_t block, lfs_off_t off, const void *buffer, lfs_size_t size) {
LittleFSImpl *me = reinterpret_cast<LittleFSImpl*>(c->context);
uint8_t *addr = me->_start + (block * me->_blockSize) + off;
uint32_t save = save_and_disable_interrupts();
noInterrupts();
rp2040.idleOtherCore();
// Serial.printf("WRITE: %p, $d\n", (intptr_t)addr - (intptr_t)XIP_BASE, size);
flash_range_program((intptr_t)addr - (intptr_t)XIP_BASE, (const uint8_t *)buffer, size);
restore_interrupts(save);
rp2040.resumeOtherCore();
interrupts();
return 0;
}

int LittleFSImpl::lfs_flash_erase(const struct lfs_config *c, lfs_block_t block) {
LittleFSImpl *me = reinterpret_cast<LittleFSImpl*>(c->context);
uint8_t *addr = me->_start + (block * me->_blockSize);
// Serial.printf("ERASE: %p, %d\n", (intptr_t)addr - (intptr_t)XIP_BASE, me->_blockSize);
uint32_t save = save_and_disable_interrupts();
noInterrupts();
rp2040.idleOtherCore();
flash_range_erase((intptr_t)addr - (intptr_t)XIP_BASE, me->_blockSize);
restore_interrupts(save);
rp2040.resumeOtherCore();
interrupts();
return 0;
}

Expand Down
2 changes: 1 addition & 1 deletion pico-sdk
Submodule pico-sdk updated 40 files
+1 −2 cmake/pico_pre_load_toolchain.cmake
+3 −2 cmake/preload/toolchains/pico_arm_gcc.cmake
+15 −7 pico_sdk_version.cmake
+6 −2 src/boards/include/boards/adafruit_feather_rp2040.h
+3 −0 src/boards/include/boards/adafruit_itsybitsy_rp2040.h
+3 −0 src/boards/include/boards/adafruit_qtpy_rp2040.h
+2 −0 src/boards/include/boards/pico.h
+3 −1 src/boards/include/boards/pimoroni_keybow2040.h
+2 −0 src/boards/include/boards/pimoroni_picosystem.h
+2 −0 src/boards/include/boards/pimoroni_tiny2040.h
+3 −1 src/boards/include/boards/sparkfun_micromod.h
+2 −0 src/boards/include/boards/sparkfun_promicro.h
+3 −0 src/boards/include/boards/sparkfun_thingplus.h
+4 −2 src/boards/include/boards/vgaboard.h
+4 −4 src/common/pico_base/include/pico/types.h
+8 −1 src/common/pico_util/include/pico/util/queue.h
+7 −0 src/host/CMakeLists.txt
+39 −14 src/rp2_common/boot_stage2/CMakeLists.txt
+19 −0 src/rp2_common/boot_stage2/compile_time_choice.S
+82 −0 src/rp2_common/boot_stage2/include/boot_stage2/config.h
+1 −1 src/rp2_common/hardware_clocks/clocks.c
+1 −1 src/rp2_common/hardware_clocks/include/hardware/clocks.h
+3 −2 src/rp2_common/hardware_dma/dma.c
+1 −1 src/rp2_common/hardware_gpio/gpio.c
+4 −4 src/rp2_common/hardware_i2c/include/hardware/i2c.h
+47 −39 src/rp2_common/hardware_pio/include/hardware/pio.h
+12 −6 src/rp2_common/hardware_pwm/include/hardware/pwm.h
+11 −8 src/rp2_common/hardware_rtc/rtc.c
+4 −3 src/rp2_common/hardware_sync/include/hardware/sync.h
+18 −9 src/rp2_common/hardware_timer/timer.c
+2 −2 src/rp2_common/pico_runtime/runtime.c
+1 −1 src/rp2_common/pico_standard_link/CMakeLists.txt
+8 −4 src/rp2_common/pico_standard_link/binary_info.c
+1 −1 src/rp2_common/pico_stdio_usb/reset_interface.c
+2 −1 src/rp2_common/pico_stdio_usb/stdio_usb.c
+1 −1 src/rp2_common/pico_stdio_usb/stdio_usb_descriptors.c
+2 −1 test/kitchen_sink/CMakeLists.txt
+36 −7 test/kitchen_sink/kitchen_sink.c
+31 −1 test/pico_time_test/pico_time_test.c
+10 −7 tools/elf2uf2/main.cpp
4 changes: 2 additions & 2 deletions pico_base/pico/version.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

#define PICO_SDK_VERSION_MAJOR 1
#define PICO_SDK_VERSION_MINOR 1
#define PICO_SDK_VERSION_REVISION 0
#define PICO_SDK_VERSION_STRING "1.1.0"
#define PICO_SDK_VERSION_REVISION 2
#define PICO_SDK_VERSION_STRING "1.1.2"

#endif

0 comments on commit 70a30dc

Please sign in to comment.