Skip to content

Commit bc5b2c2

Browse files
Fix rp2040.getCycleCount() from core1 (#2915)
Fixes #2914 There are 2 systick units, one per core. Set up and start core1's systick unit and track each core's epoch separately. Document a method of preserving 100% user-only code on core1 and add a core1_disable_systick boolean flag that works like the separate stack one.
1 parent 40b9d5b commit bc5b2c2

File tree

3 files changed

+45
-15
lines changed

3 files changed

+45
-15
lines changed

cores/rp2040/RP2040Support.h

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -190,8 +190,8 @@ class RP2040 {
190190
RP2040() { /* noop */ }
191191
~RP2040() { /* noop */ }
192192

193-
void begin() {
194-
_epoch = 0;
193+
void begin(int cpuid) {
194+
_epoch[cpuid] = 0;
195195
#if !defined(__riscv) && !defined(__PROFILE)
196196
if (!__isFreeRTOS) {
197197
// Enable SYSTICK exception
@@ -200,11 +200,14 @@ class RP2040 {
200200
systick_hw->rvr = 0x00FFFFFF;
201201
} else {
202202
#endif
203-
int off = 0;
204-
_ccountPgm = new PIOProgram(&ccount_program);
205-
_ccountPgm->prepare(&_pio, &_sm, &off);
206-
ccount_program_init(_pio, _sm, off);
207-
pio_sm_set_enabled(_pio, _sm, true);
203+
// Only start 1 instance of the PIO SM
204+
if (cpuid == 0) {
205+
int off = 0;
206+
_ccountPgm = new PIOProgram(&ccount_program);
207+
_ccountPgm->prepare(&_pio, &_sm, &off);
208+
ccount_program_init(_pio, _sm, off);
209+
pio_sm_set_enabled(_pio, _sm, true);
210+
}
208211
#if !defined(__riscv) && !defined(__PROFILE)
209212
}
210213
#endif
@@ -241,7 +244,7 @@ class RP2040 {
241244
/**
242245
@brief CPU cycle counter epoch (24-bit cycle). For internal use
243246
*/
244-
volatile uint64_t _epoch = 0;
247+
volatile uint64_t _epoch[2] = {};
245248
/**
246249
@brief Get the count of CPU clock cycles since power on.
247250
@@ -258,9 +261,9 @@ class RP2040 {
258261
uint32_t epoch;
259262
uint32_t ctr;
260263
do {
261-
epoch = (uint32_t)_epoch;
264+
epoch = (uint32_t)_epoch[sio_hw->cpuid];
262265
ctr = systick_hw->cvr;
263-
} while (epoch != (uint32_t)_epoch);
266+
} while (epoch != (uint32_t)_epoch[sio_hw->cpuid]);
264267
return epoch + (1 << 24) - ctr; /* CTR counts down from 1<<24-1 */
265268
} else {
266269
#endif
@@ -280,9 +283,9 @@ class RP2040 {
280283
uint64_t epoch;
281284
uint64_t ctr;
282285
do {
283-
epoch = _epoch;
286+
epoch = _epoch[sio_hw->cpuid];
284287
ctr = systick_hw->cvr;
285-
} while (epoch != _epoch);
288+
} while (epoch != _epoch[sio_hw->cpuid]);
286289
return epoch + (1LL << 24) - ctr;
287290
} else {
288291
#endif
@@ -666,8 +669,8 @@ class RP2040 {
666669

667670

668671
private:
669-
static void _SystickHandler() {
670-
rp2040._epoch += 1LL << 24;
672+
static void __no_inline_not_in_flash_func(_SystickHandler)() {
673+
rp2040._epoch[sio_hw->cpuid] += 1LL << 24;
671674
}
672675
PIO _pio;
673676
int _sm;

cores/rp2040/main.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,14 @@ void initVariant() { }
5151

5252
// Optional 2nd core setup and loop
5353
bool core1_separate_stack __attribute__((weak)) = false;
54+
bool core1_disable_systick __attribute__((weak)) = false;
5455
extern void setup1() __attribute__((weak));
5556
extern void loop1() __attribute__((weak));
5657
extern "C" void main1() {
58+
if (!core1_disable_systick) {
59+
// Don't install the SYSTICK exception handler. rp2040.getCycleCount will not work properly on core1
60+
rp2040.begin(1);
61+
}
5762
rp2040.fifo.registerCore();
5863
if (setup1) {
5964
setup1();
@@ -128,7 +133,7 @@ extern "C" int main() {
128133
_REENT_INIT_PTR(_impure_ptr1);
129134
}
130135

131-
rp2040.begin();
136+
rp2040.begin(0);
132137

133138
initVariant();
134139

docs/multicore.rst

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,25 @@ not necessarily simultaneously!).
1616
See the ``Multicore.ino`` example in the ``rp2040`` example directory for a
1717
quick introduction.
1818

19+
Core 1 Operation
20+
----------------
21+
22+
By default, core1 (the second core) has no non-user written code running on it.
23+
No interrupts, exceptions, or other background processing is done (but the core
24+
is still subject to hardware stalls due to on-die memory resource conflicts).
25+
When flash erase or write operations (i.e. ``LittleFS`` or ``EEPROM``) are called
26+
from core0, core1 **will** be paused.
27+
28+
If ``rp2040.getCycleCount`` is needed to operate on the second core, then a
29+
periodic (once ever 16M clock cycles) ``SYSTICK`` exception will happen behind
30+
the scenes. For extremely time-critical operations this may not be desirable
31+
and can be disabled by defining a new ``bool`` variable to ``true`` anywhere
32+
in your sketch:
33+
34+
.. code:: cpp
35+
36+
bool core1_disable_systick = true;
37+
1938
Stack Sizes
2039
-----------
2140

@@ -67,6 +86,9 @@ void rp2040.restartCore1()
6786
~~~~~~~~~~~~~~~~~~~~~~~~~~
6887

6988
Hard resets Core1 from Core 0 and restarts its operation from ``setup1()``.
89+
This can cause unpredictable behavior because globals and the heap
90+
are shared between cores and not re-initialized with this call. Use with
91+
extreme caution.
7092

7193
Communicating Between Cores
7294
---------------------------

0 commit comments

Comments
 (0)