Skip to content

Commit 2b5f578

Browse files
committed
initial draft for multithreaded rp2040
1 parent bf6ecd4 commit 2b5f578

File tree

4 files changed

+217
-6
lines changed

4 files changed

+217
-6
lines changed

core/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ endif()
9393

9494
# Link with thread library, unless we are on the Zephyr platform.
9595
if(NOT DEFINED LF_SINGLE_THREADED OR DEFINED LF_TRACE)
96-
if (NOT PLATFORM_ZEPHYR)
96+
if (NOT (PLATFORM_ZEPHYR OR ${CMAKE_SYSTEM_NAME} STREQUAL "Rp2040"))
9797
find_package(Threads REQUIRED)
9898
target_link_libraries(reactor-c PUBLIC Threads::Threads)
9999
endif()

core/platform/lf_rp2040_support.c

Lines changed: 171 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,6 @@ THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3131
* @author{Abhi Gundrala <[email protected]>}
3232
*/
3333

34-
#if !defined(LF_SINGLE_THREADED)
35-
#error "Only the single-threaded runtime has support for RP2040"
36-
#endif
37-
3834
#include "lf_rp2040_support.h"
3935
#include "platform.h"
4036
#include "utils/util.h"
@@ -51,6 +47,11 @@ THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
5147
*/
5248
static critical_section_t _lf_crit_sec;
5349

50+
/**
51+
* critical section struct for atomics implementation
52+
*/
53+
static critical_section_t _lf_atomics_crit_sec;
54+
5455
/**
5556
* binary semaphore for lf event notification
5657
* used by external isr or second core thread.
@@ -63,13 +64,14 @@ static uint32_t _lf_num_nested_crit_sec = 0;
6364

6465
/**
6566
* Initialize basic runtime infrastructure and
66-
* synchronization structs for an single-threaded runtime.
67+
* synchronization structs for a single-threaded runtime.
6768
*/
6869
void _lf_initialize_clock(void) {
6970
// init stdio lib
7071
stdio_init_all();
7172
// init sync structs
7273
critical_section_init(&_lf_crit_sec);
74+
critical_section_init(&_lf_atomics_crit_sec);
7375
sem_init(&_lf_sem_irq_event, 0, 1);
7476
}
7577

@@ -214,6 +216,170 @@ int _lf_single_threaded_notify_of_event() {
214216
sem_release(&_lf_sem_irq_event);
215217
return 0;
216218
}
219+
220+
#else // LF_SINGLE_THREADED
221+
222+
#warning "Threaded runtime on RP2040 is still experimental"
223+
224+
/**
225+
* @brief Get the number of cores on the host machine.
226+
*/
227+
int lf_available_cores() {
228+
// Right now, runtime creates 1 main thread and 1 worker thread
229+
// In the future, this may be changed to 2 (if main thread also functions
230+
// as a worker thread)
231+
return 1;
232+
}
233+
234+
static void *(*thread_1) (void *);
235+
static void* thread_1_args;
236+
static int num_create_threads_called = 0;
237+
static semaphore_t thread_1_done;
238+
static void* thread_1_return;
239+
240+
#define MAGIC_THREAD1_ID 314159
241+
242+
void core1_entry() {
243+
thread_1_return = thread_1(thread_1_args);
244+
sem_release(&thread_1_done);
245+
246+
// infinite loop; core1 should never exit
247+
while (1){
248+
tight_loop_contents();
249+
}
250+
}
251+
252+
int lf_thread_create(lf_thread_t* thread, void *(*lf_thread) (void *), void* arguments) {
253+
// make sure this fn is only called once
254+
if (num_create_threads_called != 0) {
255+
return 1;
256+
}
257+
thread_1 = lf_thread;
258+
thread_1_args = arguments;
259+
num_create_threads_called += 1;
260+
sem_init(&thread_1_done, 0, 1);
261+
multicore_launch_core1(core1_entry);
262+
*thread = MAGIC_THREAD1_ID;
263+
return 0;
264+
}
265+
266+
int lf_thread_join(lf_thread_t thread, void** thread_return) {
267+
if (thread != MAGIC_THREAD1_ID) {
268+
return 1;
269+
}
270+
sem_acquire_blocking(&thread_1_done);
271+
sem_release(&thread_1_done); // in case join is called again
272+
if (thread_return) {
273+
*thread_return = thread_1_return;
274+
}
275+
return 0;
276+
}
277+
278+
int lf_mutex_init(lf_mutex_t* mutex) {
279+
mutex_init(mutex);
280+
return 0;
281+
}
282+
283+
int lf_mutex_lock(lf_mutex_t* mutex) {
284+
mutex_enter_blocking(mutex);
285+
return 0;
286+
}
287+
288+
int lf_mutex_unlock(lf_mutex_t* mutex) {
289+
mutex_exit(mutex);
290+
return 0;
291+
}
292+
293+
int lf_cond_init(lf_cond_t* cond, lf_mutex_t* mutex) {
294+
sem_init(&(cond->sema), 0, 1);
295+
cond->mutex = mutex;
296+
return 0;
297+
}
298+
299+
int lf_cond_broadcast(lf_cond_t* cond) {
300+
sem_reset(&(cond->sema), 1);
301+
return 0;
302+
}
303+
304+
int lf_cond_signal(lf_cond_t* cond) {
305+
sem_reset(&(cond->sema), 1);
306+
return 0;
307+
}
308+
309+
int lf_cond_wait(lf_cond_t* cond) {
310+
mutex_exit(cond->mutex);
311+
sem_acquire_blocking(&(cond->sema));
312+
mutex_enter_blocking(cond->mutex);
313+
return 0;
314+
}
315+
316+
int lf_cond_timedwait(lf_cond_t* cond, instant_t absolute_time_ns) {
317+
absolute_time_t a = from_us_since_boot(absolute_time_ns / 1000);
318+
bool acquired_permit = sem_acquire_block_until(&(cond->sema), a);
319+
return acquired_permit ? 0 : LF_TIMEOUT;
320+
}
321+
322+
323+
// Atomics
324+
// Implemented by just entering a critical section and doing the arithmetic.
325+
// This is somewhat inefficient considering enclaves. Since we get a critical
326+
// section in between different enclaves
327+
328+
329+
/**
330+
* @brief Add `value` to `*ptr` and return original value of `*ptr`
331+
*/
332+
int _rp2040_atomic_fetch_add(int *ptr, int value) {
333+
critical_section_enter_blocking(&_lf_atomics_crit_sec);
334+
int res = *ptr;
335+
*ptr += value;
336+
critical_section_exit(&_lf_atomics_crit_sec);
337+
return res;
338+
}
339+
/**
340+
* @brief Add `value` to `*ptr` and return new updated value of `*ptr`
341+
*/
342+
int _rp2040_atomic_add_fetch(int *ptr, int value) {
343+
critical_section_enter_blocking(&_lf_atomics_crit_sec);
344+
int res = *ptr + value;
345+
*ptr = res;
346+
critical_section_exit(&_lf_atomics_crit_sec);
347+
return res;
348+
}
349+
350+
/**
351+
* @brief Compare and swap for boolaen value.
352+
* If `*ptr` is equal to `value` then overwrite it
353+
* with `newval`. If not do nothing. Retruns true on overwrite.
354+
*/
355+
bool _rp2040_bool_compare_and_swap(bool *ptr, bool value, bool newval) {
356+
critical_section_enter_blocking(&_lf_atomics_crit_sec);
357+
bool res = false;
358+
if (*ptr == value) {
359+
*ptr = newval;
360+
res = true;
361+
}
362+
critical_section_exit(&_lf_atomics_crit_sec);
363+
return res;
364+
}
365+
366+
/**
367+
* @brief Compare and swap for integers. If `*ptr` is equal
368+
* to `value`, it is updated to `newval`. The function returns
369+
* the original value of `*ptr`.
370+
*/
371+
int _rp2040_val_compare_and_swap(int *ptr, int value, int newval) {
372+
critical_section_enter_blocking(&_lf_atomics_crit_sec);
373+
int res = *ptr;
374+
if (*ptr == value) {
375+
*ptr = newval;
376+
}
377+
critical_section_exit(&_lf_atomics_crit_sec);
378+
return res;
379+
}
380+
381+
382+
217383
#endif // LF_SINGLE_THREADED
218384

219385

include/core/platform.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,8 @@ int lf_cond_timedwait(lf_cond_t* cond, instant_t absolute_time_ns);
218218
*/
219219
#if defined(PLATFORM_ZEPHYR)
220220
#define lf_atomic_fetch_add(ptr, value) _zephyr_atomic_fetch_add((int*) ptr, value)
221+
#elif defined(PLATFORM_RP2040)
222+
#define lf_atomic_fetch_add(ptr, value) _rp2040_atomic_fetch_add((int*) ptr, value)
221223
#elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
222224
// Assume that an integer is 32 bits.
223225
#define lf_atomic_fetch_add(ptr, value) InterlockedExchangeAdd(ptr, value)
@@ -235,6 +237,8 @@ int lf_cond_timedwait(lf_cond_t* cond, instant_t absolute_time_ns);
235237
*/
236238
#if defined(PLATFORM_ZEPHYR)
237239
#define lf_atomic_add_fetch(ptr, value) _zephyr_atomic_add_fetch((int*) ptr, value)
240+
#elif defined(PLATFORM_RP2040)
241+
#define lf_atomic_add_fetch(ptr, value) _rp2040_atomic_add_fetch((int*) ptr, value)
238242
#elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
239243
// Assume that an integer is 32 bits.
240244
#define lf_atomic_add_fetch(ptr, value) InterlockedAdd(ptr, value)
@@ -254,6 +258,8 @@ int lf_cond_timedwait(lf_cond_t* cond, instant_t absolute_time_ns);
254258
*/
255259
#if defined(PLATFORM_ZEPHYR)
256260
#define lf_bool_compare_and_swap(ptr, value, newval) _zephyr_bool_compare_and_swap((bool*) ptr, value, newval)
261+
#elif defined(PLATFORM_RP2040)
262+
#define lf_bool_compare_and_swap(ptr, value, newval) _rp2040_bool_compare_and_swap((bool*) ptr, value, newval)
257263
#elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
258264
// Assume that a boolean is represented with a 32-bit integer.
259265
#define lf_bool_compare_and_swap(ptr, oldval, newval) (InterlockedCompareExchange(ptr, newval, oldval) == oldval)
@@ -273,6 +279,8 @@ int lf_cond_timedwait(lf_cond_t* cond, instant_t absolute_time_ns);
273279
*/
274280
#if defined(PLATFORM_ZEPHYR)
275281
#define lf_val_compare_and_swap(ptr, value, newval) _zephyr_val_compare_and_swap((int*) ptr, value, newval)
282+
#elif defined(PLATFORM_RP2040)
283+
#define lf_val_compare_and_swap(ptr, value, newval) _rp2040_val_compare_and_swap((int*) ptr, value, newval)
276284
#elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
277285
#define lf_val_compare_and_swap(ptr, oldval, newval) InterlockedCompareExchange(ptr, newval, oldval)
278286
#elif defined(__GNUC__) || defined(__clang__)

include/core/platform/lf_rp2040_support.h

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,41 @@
2020
#define LF_TIME_BUFFER_LENGTH 80
2121
#define _LF_TIMEOUT 1
2222

23+
#ifndef LF_SINGLE_THREADED
24+
#warning "Threaded support on rp2040 is still experimental"
25+
26+
typedef mutex_t lf_mutex_t;
27+
typedef struct {
28+
semaphore_t sema;
29+
mutex_t* mutex;
30+
} lf_cond_t;
31+
typedef int lf_thread_t;
32+
33+
34+
/**
35+
* @brief Add `value` to `*ptr` and return original value of `*ptr`
36+
*/
37+
int _rp2040_atomic_fetch_add(int *ptr, int value);
38+
/**
39+
* @brief Add `value` to `*ptr` and return new updated value of `*ptr`
40+
*/
41+
int _rp2040_atomic_add_fetch(int *ptr, int value);
42+
43+
/**
44+
* @brief Compare and swap for boolaen value.
45+
* If `*ptr` is equal to `value` then overwrite it
46+
* with `newval`. If not do nothing. Retruns true on overwrite.
47+
*/
48+
bool _rp2040_bool_compare_and_swap(bool *ptr, bool value, bool newval);
49+
50+
/**
51+
* @brief Compare and swap for integers. If `*ptr` is equal
52+
* to `value`, it is updated to `newval`. The function returns
53+
* the original value of `*ptr`.
54+
*/
55+
int _rp2040_val_compare_and_swap(int *ptr, int value, int newval);
56+
57+
58+
#endif // LF_THREADED
59+
2360
#endif // LF_PICO_SUPPORT_H

0 commit comments

Comments
 (0)