Skip to content

Commit af094bb

Browse files
BREAKING: FreeRTOS as a define, allow full multithread/core TCP/IP (#3063)
With the expanded performance and memory of the Pico 2, having a full operating system with threads could really improve developer life and get the best possible algorithm performance. This commit includes a large number of changes, all related and not really separable into smaller bits due to the refactoring involved. No changes should have been done to bare metal LWIP processing. * Adds a new ` __FREERTOS` define at the IDE/platform.IO level. This new define is required to build FreeRTOS apps and a breaking change. * Moves FreeRTOS into the cores/rp2040 subdirectory, allowing the core to use it without hokey weak-linked wrapper functions * Splits the FreeRTOS variantHooks into multiple files focused on a particular bit of management instead of a random blob of stuff. * All LWIP calls now will be wrapped into a work queue entry and sent to the new LWIP task. * Enable LWIP debugging to be turned on through the menus * New LWIP task started which handles *all* LWIP calls and Ethernet polling via callbacks. This ensures that even with multiple cores and multiple tasks, LWIP stays in sync and only runs on core 0. It also ensures no LWIP processing is done at IRQ levels, allowing for malloc/new/free/delete calls and full API access. The LWIP task also takes care of sys_check_timeouts. It is critically important that *ALL* LWIP calls go through this task via the work queue or a crash will happen at some later point in time. The wrappers take care of this already for every LWIP function used in the core. * Note that LWIP callbacks run in the LWIP thread and block other LWIP operations. So for an absolutely parallel web server, you'd want to have your tcp_recv/sent/err/poll just take the response data and put it into a work queue for your own task and return before processing. * Split Ethernet interrupt handling into an IRQ acknowledge and a posted message to the LWIP process to actually read out a packet. Adds a hardware semaphore which protects just the bits of code where a packet is being read of written from the HW (and not around any LWIP send/receive calls). * Fix race condition in WiFiServer where under heavy load with multithreading a connection gets a tcp_abort just before tcp_accept will be called. When we abort, we clear the this pointer, and so when we try and recover our object we crash. Check for aborted connections (and ignore) on WiFiServer, like we do in WiFiClient. * WiFiClient/WiFiUDP connection list (used for stopAll) adds mutex protection so it can safely work in parallel. * Just panic() on FreeRTOS fatal error, don't try and be fancy and blink a LED. GDB shows exactly where the crash is, now. * Rewrite CYW43 driver for LWIP thread processing. The SDK really wants to use an async_context, but that only works if the CYW43 driver is the only one there (it handles *both* Ethernet and IP with the same context). NULL out the context pointer and wrap the driver calls to the taskless FreeRTOS Ethernet architecture. * Includes the async_contexts for freertos and bare metal as part of the core build process. Users can use their own async_context_freertos if they so desire, instead of being stuck with the wrong one (bare metal) under FreeRTOS.
1 parent c2fe192 commit af094bb

File tree

143 files changed

+4316
-1719
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

143 files changed

+4316
-1719
lines changed

.github/workflows/pull-request.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ jobs:
2020
- name: Run codespell
2121
uses: codespell-project/actions-codespell@v2
2222
with:
23-
skip: ./ArduinoCore-API,./libraries/ESP8266SdFat,./libraries/Adafruit_TinyUSB_Arduino,./libraries/LittleFS/lib,./tools/pyserial,./pico-sdk,./.github,./docs/i2s.rst,./cores/rp2040/api,./libraries/FreeRTOS,./tools/libbearssl/bearssl,./include,./libraries/WiFi/examples/BearSSL_Server,./ota/uzlib,./libraries/http-parser/lib,./libraries/WebServer/examples/HelloServerBearSSL/HelloServerBearSSL.ino,./libraries/HTTPUpdateServer/examples/SecureBearSSLUpdater/SecureBearSSLUpdater.ino,./.git,./libraries/FatFS/lib/fatfs,./libraries/FatFS/src/diskio.h,./libraries/FatFS/src/ff.cpp,./libraries/FatFS/src/ffconf.h,./libraries/FatFS/src/ffsystem.cpp,./libraries/FatFS/src/ff.h,./libraries/lwIP_WINC1500/src/driver,./libraries/lwIP_WINC1500/src/common,./libraries/lwIP_WINC1500/src/bus_wrapper,./libraries/lwIP_WINC1500/src/spi_flash,./libraries/WiFi/examples/BearSSL_Validation/certs.h
24-
ignore_words_list: ser,dout,shiftIn,acount,froms
23+
skip: ./ArduinoCore-API,./libraries/ESP8266SdFat,./libraries/Adafruit_TinyUSB_Arduino,./libraries/LittleFS/lib,./tools/pyserial,./pico-sdk,./.github,./docs/i2s.rst,./cores/rp2040/api,./FreeRTOS,./tools/libbearssl/bearssl,./include,./libraries/WiFi/examples/BearSSL_Server,./ota/uzlib,./libraries/http-parser/lib,./libraries/WebServer/examples/HelloServerBearSSL/HelloServerBearSSL.ino,./libraries/HTTPUpdateServer/examples/SecureBearSSLUpdater/SecureBearSSLUpdater.ino,./.git,./libraries/FatFS/lib/fatfs,./libraries/FatFS/src/diskio.h,./libraries/FatFS/src/ff.cpp,./libraries/FatFS/src/ffconf.h,./libraries/FatFS/src/ffsystem.cpp,./libraries/FatFS/src/ff.h,./libraries/lwIP_WINC1500/src/driver,./libraries/lwIP_WINC1500/src/common,./libraries/lwIP_WINC1500/src/bus_wrapper,./libraries/lwIP_WINC1500/src/spi_flash,./libraries/WiFi/examples/BearSSL_Validation/certs.h
24+
ignore_words_list: ser,dout,shiftIn,acount,froms,ThirdParty
2525
- name: Check boards.txt was not edited after makeboards.py
2626
run: |
2727
./tools/makeboards.py

.gitmodules

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
path = libraries/Adafruit_TinyUSB_Arduino
2424
url = https://github.com/adafruit/Adafruit_TinyUSB_Arduino.git
2525
[submodule "libraries/FreeRTOS/lib/FreeRTOS-Kernel"]
26-
path = libraries/FreeRTOS/lib/FreeRTOS-Kernel
26+
path = FreeRTOS-Kernel
2727
url = https://github.com/earlephilhower/FreeRTOS-Kernel.git
2828
[submodule "tools/libbearssl/bearssl"]
2929
path = tools/libbearssl/bearssl
@@ -49,3 +49,6 @@
4949
[submodule "libraries/SdFat"]
5050
path = libraries/SdFat
5151
url = https://github.com/greiman/SdFat.git
52+
[submodule "FreeRTOS-Kernel"]
53+
path = FreeRTOS-Kernel
54+
url = https://github.com/earlephilhower/FreeRTOS-Kernel.git

boards.txt

Lines changed: 932 additions & 133 deletions
Large diffs are not rendered by default.

cores/rp2040/Arduino.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,6 @@ void analogWriteResolution(int res);
109109
} // extern "C"
110110
#endif
111111

112-
// FreeRTOS potential calls
113-
extern bool __isFreeRTOS;
114-
115112
// Ancient AVR defines
116113
#define HAVE_HWSERIAL0
117114
#define HAVE_HWSERIAL1

cores/rp2040/Bootsel.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ static bool __no_inline_not_in_flash_func(get_bootsel_button)() {
2626

2727
// Must disable interrupts, as interrupt handlers may be in flash, and we
2828
// are about to temporarily disable flash access!
29-
if (!__isFreeRTOS) {
30-
noInterrupts();
31-
}
29+
#ifndef __FREERTOS
30+
noInterrupts();
31+
#endif
3232
rp2040.idleOtherCore();
3333

3434
// Set chip select to Hi-Z
@@ -55,9 +55,9 @@ static bool __no_inline_not_in_flash_func(get_bootsel_button)() {
5555
IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_BITS);
5656

5757
rp2040.resumeOtherCore();
58-
if (!__isFreeRTOS) {
59-
interrupts();
60-
}
58+
#ifndef __FREERTOS
59+
interrupts();
60+
#endif
6161

6262
return button_state;
6363
}

cores/rp2040/CoreMutex.cpp

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -28,45 +28,46 @@ CoreMutex::CoreMutex(mutex_t *mutex, uint8_t option) {
2828
_mutex = mutex;
2929
_acquired = false;
3030
_option = option;
31+
#ifdef __FREERTOS
3132
_pxHigherPriorityTaskWoken = 0; // pdFALSE
32-
if (__isFreeRTOS) {
33-
auto m = __get_freertos_mutex_for_ptr(mutex);
33+
auto m = __get_freertos_mutex_for_ptr(mutex);
3434

35-
if (__freertos_check_if_in_isr()) {
36-
if (!__freertos_mutex_take_from_isr(m, &_pxHigherPriorityTaskWoken)) {
37-
return;
38-
}
39-
// At this point we have the mutex in ISR
40-
} else {
41-
// Grab the mutex normally, possibly waking other tasks to get it
42-
__freertos_mutex_take(m);
35+
if (portCHECK_IF_IN_ISR()) {
36+
if (!xSemaphoreTakeFromISR(m, &_pxHigherPriorityTaskWoken)) {
37+
return;
4338
}
39+
// At this point we have the mutex in ISR
4440
} else {
45-
uint32_t owner;
46-
if (!mutex_try_enter(_mutex, &owner)) {
47-
if (owner == get_core_num()) { // Deadlock!
48-
if (_option & DebugEnable) {
49-
DEBUGCORE("CoreMutex - Deadlock detected!\n");
50-
}
51-
return;
41+
// Grab the mutex normally, possibly waking other tasks to get it
42+
xSemaphoreTake(m, portMAX_DELAY);
43+
}
44+
#else
45+
uint32_t owner;
46+
if (!mutex_try_enter(_mutex, &owner)) {
47+
if (owner == get_core_num()) { // Deadlock!
48+
if (_option & DebugEnable) {
49+
DEBUGCORE("CoreMutex - Deadlock detected!\n");
5250
}
53-
mutex_enter_blocking(_mutex);
51+
return;
5452
}
53+
mutex_enter_blocking(_mutex);
5554
}
55+
#endif
5656
_acquired = true;
5757
}
5858

5959
CoreMutex::~CoreMutex() {
6060
if (_acquired) {
61-
if (__isFreeRTOS) {
62-
auto m = __get_freertos_mutex_for_ptr(_mutex);
63-
if (__freertos_check_if_in_isr()) {
64-
__freertos_mutex_give_from_isr(m, &_pxHigherPriorityTaskWoken);
65-
} else {
66-
__freertos_mutex_give(m);
67-
}
61+
#ifdef __FREERTOS
62+
auto m = __get_freertos_mutex_for_ptr(_mutex);
63+
if (portCHECK_IF_IN_ISR()) {
64+
xSemaphoreGiveFromISR(m, &_pxHigherPriorityTaskWoken);
65+
portYIELD_FROM_ISR(_pxHigherPriorityTaskWoken);
6866
} else {
69-
mutex_exit(_mutex);
67+
xSemaphoreGive(m);
7068
}
69+
#else
70+
mutex_exit(_mutex);
71+
#endif
7172
}
7273
}

cores/rp2040/CoreMutex.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,5 +43,7 @@ class CoreMutex {
4343
mutex_t *_mutex;
4444
bool _acquired;
4545
uint8_t _option;
46+
#ifdef __FREERTOS
4647
BaseType_t _pxHigherPriorityTaskWoken;
48+
#endif
4749
};

cores/rp2040/RP2040Support.h

Lines changed: 62 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -72,17 +72,18 @@ class _MFIFO {
7272
}
7373

7474
void registerCore() {
75-
if (!__isFreeRTOS) {
76-
multicore_fifo_clear_irq();
75+
#ifndef __FREERTOS
76+
multicore_fifo_clear_irq();
7777
#ifdef PICO_RP2350
78-
irq_set_exclusive_handler(SIO_IRQ_FIFO, _irq);
79-
irq_set_enabled(SIO_IRQ_FIFO, true);
78+
irq_set_exclusive_handler(SIO_IRQ_FIFO, _irq);
79+
irq_set_enabled(SIO_IRQ_FIFO, true);
8080
#else
81-
irq_set_exclusive_handler(SIO_IRQ_PROC0 + get_core_num(), _irq);
82-
irq_set_enabled(SIO_IRQ_PROC0 + get_core_num(), true);
81+
irq_set_exclusive_handler(SIO_IRQ_PROC0 + get_core_num(), _irq);
82+
irq_set_enabled(SIO_IRQ_PROC0 + get_core_num(), true);
8383
#endif
84-
}
84+
#else
8585
// FreeRTOS port.c will handle the IRQ hooking
86+
#endif
8687
}
8788

8889
void push(uint32_t val) {
@@ -113,14 +114,14 @@ class _MFIFO {
113114
if (!_multicore) {
114115
return;
115116
}
116-
if (__isFreeRTOS) {
117-
__freertos_idle_other_core();
118-
} else {
119-
mutex_enter_blocking(&_idleMutex);
120-
__otherCoreIdled = false;
121-
multicore_fifo_push_blocking(_GOTOSLEEP);
122-
while (!__otherCoreIdled) { /* noop */ }
123-
}
117+
#ifdef __FREERTOS
118+
__freertos_idle_other_core();
119+
#else
120+
mutex_enter_blocking(&_idleMutex);
121+
__otherCoreIdled = false;
122+
multicore_fifo_push_blocking(_GOTOSLEEP);
123+
while (!__otherCoreIdled) { /* noop */ }
124+
#endif
124125
}
125126

126127
void resumeOtherCore() {
@@ -129,9 +130,9 @@ class _MFIFO {
129130
}
130131
mutex_exit(&_idleMutex);
131132
__otherCoreIdled = false;
132-
if (__isFreeRTOS) {
133-
__freertos_resume_other_core();
134-
}
133+
#ifdef __FREERTOS
134+
__freertos_resume_other_core();
135+
#endif
135136

136137
// Other core will exit busy-loop and return to operation
137138
// once __otherCoreIdled == false.
@@ -151,18 +152,18 @@ class _MFIFO {
151152

152153
private:
153154
static void __no_inline_not_in_flash_func(_irq)() {
154-
if (!__isFreeRTOS) {
155-
multicore_fifo_clear_irq();
156-
noInterrupts(); // We need total control, can't run anything
157-
while (multicore_fifo_rvalid()) {
158-
if (_GOTOSLEEP == multicore_fifo_pop_blocking()) {
159-
__otherCoreIdled = true;
160-
while (__otherCoreIdled) { /* noop */ }
161-
break;
162-
}
155+
#ifndef __FREERTOS
156+
multicore_fifo_clear_irq();
157+
noInterrupts(); // We need total control, can't run anything
158+
while (multicore_fifo_rvalid()) {
159+
if (_GOTOSLEEP == multicore_fifo_pop_blocking()) {
160+
__otherCoreIdled = true;
161+
while (__otherCoreIdled) { /* noop */ }
162+
break;
163163
}
164-
interrupts();
165164
}
165+
interrupts();
166+
#endif
166167
}
167168

168169
bool _multicore = false;
@@ -192,23 +193,19 @@ class RP2040 {
192193

193194
void begin(int cpuid) {
194195
_epoch[cpuid] = 0;
195-
#if !defined(__riscv) && !defined(__PROFILE)
196-
if (!__isFreeRTOS) {
197-
// Enable SYSTICK exception
198-
exception_set_exclusive_handler(SYSTICK_EXCEPTION, _SystickHandler);
199-
systick_hw->csr = 0x7;
200-
systick_hw->rvr = 0x00FFFFFF;
201-
} else {
202-
#endif
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-
}
211-
#if !defined(__riscv) && !defined(__PROFILE)
196+
#if !defined(__riscv) && !defined(__PROFILE) && !defined(__FREERTOS)
197+
// Enable SYSTICK exception
198+
exception_set_exclusive_handler(SYSTICK_EXCEPTION, _SystickHandler);
199+
systick_hw->csr = 0x7;
200+
systick_hw->rvr = 0x00FFFFFF;
201+
#else
202+
// Only start 1 instance of the PIO SM
203+
if (cpuid == 0) {
204+
int off = 0;
205+
_ccountPgm = new PIOProgram(&ccount_program);
206+
_ccountPgm->prepare(&_pio, &_sm, &off);
207+
ccount_program_init(_pio, _sm, off);
208+
pio_sm_set_enabled(_pio, _sm, true);
212209
}
213210
#endif
214211
}
@@ -255,21 +252,17 @@ class RP2040 {
255252
@returns CPU clock cycles since power up
256253
*/
257254
inline uint32_t getCycleCount() {
258-
#if !defined(__riscv) && !defined(__PROFILE)
255+
#if !defined(__riscv) && !defined(__PROFILE) && !defined(__FREERTOS)
259256
// Get CPU cycle count. Needs to do magic to extend 24b HW to something longer
260-
if (!__isFreeRTOS) {
261-
uint32_t epoch;
262-
uint32_t ctr;
263-
do {
264-
epoch = (uint32_t)_epoch[sio_hw->cpuid];
265-
ctr = systick_hw->cvr;
266-
} while (epoch != (uint32_t)_epoch[sio_hw->cpuid]);
267-
return epoch + (1 << 24) - ctr; /* CTR counts down from 1<<24-1 */
268-
} else {
269-
#endif
270-
return ccount_read(_pio, _sm);
271-
#if !defined(__riscv) && !defined(__PROFILE)
272-
}
257+
uint32_t epoch;
258+
uint32_t ctr;
259+
do {
260+
epoch = (uint32_t)_epoch[sio_hw->cpuid];
261+
ctr = systick_hw->cvr;
262+
} while (epoch != (uint32_t)_epoch[sio_hw->cpuid]);
263+
return epoch + (1 << 24) - ctr; /* CTR counts down from 1<<24-1 */
264+
#else
265+
return ccount_read(_pio, _sm);
273266
#endif
274267
}
275268
/**
@@ -278,20 +271,16 @@ class RP2040 {
278271
@returns CPU clock cycles since power up
279272
*/
280273
inline uint64_t getCycleCount64() {
281-
#if !defined(__riscv) && !defined(__PROFILE)
282-
if (!__isFreeRTOS) {
283-
uint64_t epoch;
284-
uint64_t ctr;
285-
do {
286-
epoch = _epoch[sio_hw->cpuid];
287-
ctr = systick_hw->cvr;
288-
} while (epoch != _epoch[sio_hw->cpuid]);
289-
return epoch + (1LL << 24) - ctr;
290-
} else {
291-
#endif
292-
return ccount_read(_pio, _sm);
293-
#if !defined(__riscv) && !defined(__PROFILE)
294-
}
274+
#if !defined(__riscv) && !defined(__PROFILE) && !defined(__FREERTOS)
275+
uint64_t epoch;
276+
uint64_t ctr;
277+
do {
278+
epoch = _epoch[sio_hw->cpuid];
279+
ctr = systick_hw->cvr;
280+
} while (epoch != _epoch[sio_hw->cpuid]);
281+
return epoch + (1LL << 24) - ctr;
282+
#else
283+
return ccount_read(_pio, _sm);
295284
#endif
296285
}
297286

cores/rp2040/SerialUSB.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -181,9 +181,9 @@ static int _bps = 115200;
181181
static bool _rebooting = false;
182182
static void CheckSerialReset() {
183183
if (!_rebooting && (_bps == 1200) && (!_dtr)) {
184-
if (__isFreeRTOS) {
185-
__freertos_idle_other_core();
186-
}
184+
#ifdef __FREERTOS
185+
__freertos_idle_other_core();
186+
#endif
187187
_rebooting = true;
188188
// Disable NVIC IRQ, so that we don't get bothered anymore
189189
irq_set_enabled(USBCTRL_IRQ, false);

0 commit comments

Comments
 (0)