Skip to content

Commit 70a30dc

Browse files
Add multicore safety, FIFO, update pico-sdk (#122)
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.
1 parent 6cf0b30 commit 70a30dc

File tree

9 files changed

+187
-12
lines changed

9 files changed

+187
-12
lines changed

cores/rp2040/RP2040.h

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,96 @@
1818
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
1919
*/
2020

21-
#include <hardware/pio.h>
2221
#include <hardware/clocks.h>
22+
#include <hardware/irq.h>
23+
#include <hardware/pio.h>
24+
#include <pico/multicore.h>
25+
#include <pico/util/queue.h>
26+
27+
class _MFIFO {
28+
public:
29+
_MFIFO() { /* noop */ };
30+
~_MFIFO() { /* noop */ };
31+
32+
void begin(int cores) {
33+
constexpr int FIFOCNT = 8;
34+
if (cores == 1) {
35+
_multicore = false;
36+
return;
37+
}
38+
mutex_init(&_idleMutex);
39+
queue_init(&_queue[0], sizeof(uint32_t), FIFOCNT);
40+
queue_init(&_queue[1], sizeof(uint32_t), FIFOCNT);
41+
_multicore = true;
42+
}
43+
44+
void registerCore() {
45+
multicore_fifo_clear_irq();
46+
irq_set_exclusive_handler(SIO_IRQ_PROC0 + get_core_num(), _irq);
47+
irq_set_enabled(SIO_IRQ_PROC0 + get_core_num(), true);
48+
}
49+
50+
void push(uint32_t val) {
51+
while (!push_nb(val)) { /* noop */ }
52+
}
53+
54+
bool push_nb(uint32_t val) {
55+
// Push to the other FIFO
56+
return queue_try_add(&_queue[get_core_num()^1], &val);
57+
}
58+
59+
uint32_t pop() {
60+
uint32_t ret;
61+
while (!pop_nb(&ret)) { /* noop */ }
62+
return ret;
63+
}
64+
65+
bool pop_nb(uint32_t *val) {
66+
// Pop from my own FIFO
67+
return queue_try_remove(&_queue[get_core_num()], val);
68+
}
69+
70+
int available() {
71+
return queue_get_level(&_queue[get_core_num()]);
72+
}
73+
74+
void idleOtherCore() {
75+
if (!_multicore) return;
76+
mutex_enter_blocking(&_idleMutex);
77+
_otherIdled = false;
78+
multicore_fifo_push_blocking(_GOTOSLEEP);
79+
while (!_otherIdled) { /* noop */ }
80+
}
81+
82+
void resumeOtherCore() {
83+
if (!_multicore) return;
84+
mutex_exit(&_idleMutex);
85+
_otherIdled = false;
86+
// Other core will exit busy-loop and return to operation
87+
// once otherIdled == false.
88+
}
89+
90+
private:
91+
static void __no_inline_not_in_flash_func(_irq)() {
92+
multicore_fifo_clear_irq();
93+
noInterrupts(); // We need total control, can't run anything
94+
while (multicore_fifo_rvalid()) {
95+
if (_GOTOSLEEP == multicore_fifo_pop_blocking()) {
96+
_otherIdled = true;
97+
while (_otherIdled) { /* noop */ }
98+
break;
99+
}
100+
}
101+
interrupts();
102+
}
103+
bool _multicore = false;
104+
105+
mutex_t _idleMutex;
106+
static volatile bool _otherIdled;
107+
queue_t _queue[2];
108+
109+
static constexpr int _GOTOSLEEP = 0x66666666;
110+
};
23111

24112
class RP2040 {
25113
public:
@@ -33,8 +121,16 @@ class RP2040 {
33121
static int f_cpu() {
34122
return clock_get_hz(clk_sys);
35123
}
124+
125+
void idleOtherCore() { fifo.idleOtherCore(); }
126+
void resumeOtherCore() { fifo.resumeOtherCore(); }
127+
128+
// Multicore comms FIFO
129+
_MFIFO fifo;
36130
};
37131

132+
extern RP2040 rp2040;
133+
38134
// Wrapper class for PIO programs, abstracting common operations out
39135
// TODO - Make dualcore safe
40136
// TODO - Add unload/destructor

cores/rp2040/main.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@
2222
#include <pico/stdlib.h>
2323
#include <pico/multicore.h>
2424

25+
RP2040 rp2040;
26+
27+
volatile bool _MFIFO::_otherIdled = false;
28+
2529
extern void setup();
2630
extern void loop();
2731

@@ -35,6 +39,7 @@ void initVariant() { }
3539
extern void setup1() __attribute__((weak));
3640
extern void loop1() __attribute__((weak));
3741
static void main1() {
42+
rp2040.fifo.registerCore();
3843
if (setup1) {
3944
setup1();
4045
}
@@ -62,8 +67,12 @@ extern "C" int main() {
6267
#endif
6368

6469
if (setup1 || loop1) {
70+
rp2040.fifo.begin(2);
6571
multicore_launch_core1(main1);
72+
} else {
73+
rp2040.fifo.begin(1);
6674
}
75+
rp2040.fifo.registerCore();
6776

6877
setup();
6978
while (true) {

cores/rp2040/wiring_private.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,9 @@ extern "C" void interrupts() {
3333
// ERROR
3434
return;
3535
}
36-
restore_interrupts(_irqStack[get_core_num()].top());
36+
auto oldIrqs = _irqStack[get_core_num()].top();
3737
_irqStack[get_core_num()].pop();
38+
restore_interrupts(oldIrqs);
3839
}
3940

4041
extern "C" void noInterrupts() {

docs/multicore.rst

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,69 @@ By adding a ``setup1()`` and ``loop1()`` function to your sketch you can make
99
use of the second core. Anything called from within the ``setup1()`` or
1010
``loop1()`` routines will execute on the second core.
1111

12+
``setup()`` and ``setup1()`` will be called at the same time, and the ``loop()``
13+
or ``loop1()`` will be started as soon as the core's ``setup()`` completes (i.e.
14+
not necessarily simultaneously!).
15+
1216
See the ``Multicore.ino`` example in the ``rp2040`` example directory for a
1317
quick introduction.
18+
19+
Pausing Cores
20+
-------------
21+
22+
Sometimes an application needs to pause the other core on chip (i.e. it is
23+
writing to flash or needs to stop processing while some other event occurs).
24+
25+
void rp2040.idleOtherCore()
26+
~~~~~~~~~~~~~~~~~~~~~~~~~~~
27+
28+
Sends a message to stop the other core (i.e. when called from core 0 it
29+
pauses core 1, and vice versa). Waits for the other core to acknowledge
30+
before returning.
31+
32+
The other core will have its interrupts disabled and be busy-waiting in
33+
an RAM-based routine, so flash and other peripherals can be accesses.
34+
35+
**NOTE** If you idle core 0 too long, then the USB port can become frozen.
36+
This is because core 0 manages the USB and needs to service IRQs in a
37+
timely manner (which it can't do when idled).
38+
39+
void rp2040.resumeOtherCore()
40+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
41+
42+
Resumes processing in the other core, where it left off.
43+
44+
Communicating Between Cores
45+
---------------------------
46+
47+
The RP2040 provides a hardware FIFO for communicating between cores, but it
48+
is used exclusively for the idle/resume calls described above. Instead, please
49+
use the following functions to access a softwarae-managed, multicore safe
50+
FIFO.
51+
52+
void rp2040.fifo.push(uint32_t)
53+
~~~~~~~~~~~~~~~~~~~~~~~~~~
54+
55+
Pushes a value to the other core. Will block if the FIFO is full.
56+
57+
bool rp2040.fifo.push_nb(uint32_t)
58+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
59+
60+
Pushes a value to the other core. If the FIFO is full, returns ``false``
61+
immediately and doesn't block. If the push is successful, returns ``true``.
62+
63+
uint32_t rp2040.fifo.pop()
64+
~~~~~~~~~~~~~~~~~~~~~
65+
66+
Reads a value from this core's FIFO. Blocks until one is available.
67+
68+
bool rp2040.fifo.pop_nb(uint32_t *dest)
69+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
70+
71+
Reads a value from this core's FIFO and places it in dest. Will return
72+
``true`` if successful, or ``false`` if the pop would block.
73+
74+
int rp2040.fifo.available()
75+
~~~~~~~~~~~~~~~~~~~~~~~~~~~
76+
77+
Returns the number of values available in this core's FIFO.

lib/libpico.a

440 Bytes
Binary file not shown.

libraries/EEPROM/EEPROM.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,11 +105,12 @@ bool EEPROMClass::commit() {
105105
if (!_data)
106106
return false;
107107

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

114115
return true;
115116
}

libraries/LittleFS/src/LittleFS.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -173,20 +173,24 @@ int LittleFSImpl::lfs_flash_prog(const struct lfs_config *c,
173173
lfs_block_t block, lfs_off_t off, const void *buffer, lfs_size_t size) {
174174
LittleFSImpl *me = reinterpret_cast<LittleFSImpl*>(c->context);
175175
uint8_t *addr = me->_start + (block * me->_blockSize) + off;
176-
uint32_t save = save_and_disable_interrupts();
176+
noInterrupts();
177+
rp2040.idleOtherCore();
177178
// Serial.printf("WRITE: %p, $d\n", (intptr_t)addr - (intptr_t)XIP_BASE, size);
178179
flash_range_program((intptr_t)addr - (intptr_t)XIP_BASE, (const uint8_t *)buffer, size);
179-
restore_interrupts(save);
180+
rp2040.resumeOtherCore();
181+
interrupts();
180182
return 0;
181183
}
182184

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

pico-sdk

Submodule pico-sdk updated 40 files

pico_base/pico/version.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
#define PICO_SDK_VERSION_MAJOR 1
1515
#define PICO_SDK_VERSION_MINOR 1
16-
#define PICO_SDK_VERSION_REVISION 0
17-
#define PICO_SDK_VERSION_STRING "1.1.0"
16+
#define PICO_SDK_VERSION_REVISION 2
17+
#define PICO_SDK_VERSION_STRING "1.1.2"
1818

1919
#endif

0 commit comments

Comments
 (0)