Skip to content

Commit d9f86a0

Browse files
authored
Merge pull request #344 from lf-lang/rp2040-multicore
Rp2040 multithreaded target support
2 parents c30161c + b58a3a5 commit d9f86a0

4 files changed

Lines changed: 137 additions & 8 deletions

File tree

low_level_platform/api/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Zephyr")
99
target_compile_definitions(lf-low-level-platform-api INTERFACE PLATFORM_ZEPHYR)
1010
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Rp2040")
1111
target_compile_definitions(lf-low-level-platform-api INTERFACE PLATFORM_RP2040)
12+
target_link_libraries(lf-low-level-platform-api INTERFACE pico_stdlib)
13+
target_link_libraries(lf-low-level-platform-api INTERFACE pico_multicore)
14+
target_link_libraries(lf-low-level-platform-api INTERFACE pico_sync)
1215
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "FlexPRET")
1316
target_compile_definitions(lf-low-level-platform-api INTERFACE PLATFORM_FLEXPRET)
1417
target_link_libraries(lf-low-level-platform-api INTERFACE fp-sdk)

low_level_platform/api/platform/lf_rp2040_support.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,16 @@
2121
#define LF_TIME_BUFFER_LENGTH 80
2222
#define _LF_TIMEOUT 1
2323

24+
#ifndef LF_SINGLE_THREADED
25+
#warning "Threaded support on rp2040 is still experimental"
26+
27+
typedef recursive_mutex_t lf_mutex_t;
28+
typedef struct {
29+
semaphore_t notifs[NUM_CORES];
30+
lf_mutex_t* mutex;
31+
} lf_cond_t;
32+
typedef int lf_thread_t;
33+
34+
#endif // LF_SINGLE_THREADED
35+
2436
#endif // LF_PICO_SUPPORT_H

low_level_platform/impl/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@ if(${CMAKE_SYSTEM_NAME} STREQUAL "Zephyr")
5959
zephyr_library_named(lf-low-level-platform-impl)
6060
zephyr_library_sources(${LF_LOW_LEVEL_PLATFORM_FILES})
6161
zephyr_library_link_libraries(kernel)
62+
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Rp2040")
63+
add_library(lf-low-level-platform-impl STATIC ${LF_LOW_LEVEL_PLATFORM_FILES})
64+
if (DEFINED NUMBER_OF_WORKERS AND ${NUMBER_OF_WORKERS} GREATER 2)
65+
message(FATAL_ERROR "RP2040 can have at most 2 workers (one per core).\
66+
Number of requested workers is ${NUMBER_OF_WORKERS}.")
67+
endif()
6268
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "FlexPRET")
6369
add_library(lf-low-level-platform-impl STATIC ${LF_LOW_LEVEL_PLATFORM_FILES})
6470
target_link_libraries(lf-low-level-platform-impl PRIVATE fp-sdk)

low_level_platform/impl/src/lf_rp2040_support.c

Lines changed: 116 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,8 @@ THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3131
* @author{Abhi Gundrala <gundralaa@berkeley.edu>}
3232
*/
3333

34-
#if !defined(LF_SINGLE_THREADED)
35-
#error "Only the single-threaded runtime has support for RP2040"
36-
#endif
37-
3834
#include "platform/lf_rp2040_support.h"
3935
#include "low_level_platform.h"
40-
#include "utils/util.h"
4136
#include "tag.h"
4237

4338
#include <pico/stdlib.h>
@@ -67,6 +62,8 @@ static uint32_t _lf_num_nested_crit_sec = 0;
6762
*/
6863
void _lf_initialize_clock(void) {
6964
// init stdio lib
65+
// may fail, but failure may be ok/expected if printing is not needed
66+
// (i.e. if neither USB nor UART are enabled)
7067
stdio_init_all();
7168
// init sync structs
7269
critical_section_init(&_lf_crit_sec);
@@ -161,7 +158,7 @@ int lf_disable_interrupts_nested() {
161158
return 1;
162159
}
163160
// check crit sec count
164-
// enter non-rentrant state by disabling interrupts
161+
// enter non-reentrant state by disabling interrupts
165162
// lock second core execution
166163
if (_lf_num_nested_crit_sec == 0) {
167164
// block if associated spin lock in use
@@ -202,9 +199,120 @@ int lf_enable_interrupts_nested() {
202199
*/
203200
int _lf_single_threaded_notify_of_event() {
204201
// notify main sleep loop of event
205-
sem_release(&_lf_sem_irq_event);
202+
if (sem_release(&_lf_sem_irq_event)) {
203+
return 0;
204+
}
205+
return 1;
206+
}
207+
208+
#else // LF_SINGLE_THREADED
209+
210+
#warning "Threaded runtime on RP2040 is still experimental"
211+
212+
/**
213+
* @brief Get the number of cores on the host machine.
214+
*/
215+
int lf_available_cores() { return 2; }
216+
217+
static void* (*thread_1)(void*);
218+
static void* thread_1_args;
219+
static int num_create_threads_called = 0;
220+
static semaphore_t thread_1_done;
221+
static void* thread_1_return;
222+
223+
#define MAGIC_THREAD1_ID 314159
224+
225+
void core1_entry() {
226+
thread_1_return = thread_1(thread_1_args);
227+
sem_reset(&thread_1_done, 1);
228+
}
229+
230+
int lf_thread_create(lf_thread_t* thread, void* (*lf_thread)(void*), void* arguments) {
231+
// make sure this fn is only called once
232+
if (num_create_threads_called != 0) {
233+
return 1;
234+
}
235+
thread_1 = lf_thread;
236+
thread_1_args = arguments;
237+
num_create_threads_called += 1;
238+
sem_init(&thread_1_done, 0, 1);
239+
multicore_launch_core1(core1_entry);
240+
*thread = MAGIC_THREAD1_ID;
241+
return 0;
242+
}
243+
244+
int lf_thread_join(lf_thread_t thread, void** thread_return) {
245+
if (thread != MAGIC_THREAD1_ID) {
246+
return 1;
247+
}
248+
sem_acquire_blocking(&thread_1_done);
249+
// release in case join is called again
250+
if (!sem_release(&thread_1_done)) {
251+
// shouldn't be possible; lf_thread_join is only called from main thread
252+
return 1;
253+
}
254+
if (thread_return) {
255+
*thread_return = thread_1_return;
256+
}
257+
return 0;
258+
}
259+
260+
int lf_mutex_init(lf_mutex_t* mutex) {
261+
recursive_mutex_init(mutex);
262+
return 0;
263+
}
264+
265+
int lf_mutex_lock(lf_mutex_t* mutex) {
266+
recursive_mutex_enter_blocking(mutex);
267+
return 0;
268+
}
269+
270+
int lf_mutex_unlock(lf_mutex_t* mutex) {
271+
recursive_mutex_exit(mutex);
206272
return 0;
207273
}
208-
#endif // LF_SINGLE_THREADED
274+
275+
// condition variables "notify" threads using a semaphore per core.
276+
// although there are only two cores, may not use just a single semaphore
277+
// as a cond_broadcast may be called from within an interrupt
278+
int lf_cond_init(lf_cond_t* cond, lf_mutex_t* mutex) {
279+
for (int i = 0; i < NUM_CORES; i++) {
280+
sem_init(&(cond->notifs[i]), 0, 1);
281+
}
282+
cond->mutex = mutex;
283+
return 0;
284+
}
285+
286+
int lf_cond_broadcast(lf_cond_t* cond) {
287+
for (int i = 0; i < NUM_CORES; i++) {
288+
sem_reset(&(cond->notifs[i]), 1);
289+
}
290+
return 0;
291+
}
292+
293+
int lf_cond_signal(lf_cond_t* cond) {
294+
return lf_cond_broadcast(cond); // spurious wakeups, but that's ok
295+
}
296+
297+
int lf_cond_wait(lf_cond_t* cond) {
298+
semaphore_t* mailbox = &(cond->notifs[get_core_num()]);
299+
lf_mutex_unlock(cond->mutex);
300+
sem_acquire_blocking(mailbox);
301+
lf_mutex_lock(cond->mutex);
302+
return 0;
303+
}
304+
305+
int _lf_cond_timedwait(lf_cond_t* cond, instant_t absolute_time_ns) {
306+
semaphore_t* mailbox = &(cond->notifs[get_core_num()]);
307+
absolute_time_t a = from_us_since_boot(absolute_time_ns / 1000);
308+
bool acquired_permit = sem_acquire_block_until(mailbox, a);
309+
return acquired_permit ? 0 : LF_TIMEOUT;
310+
}
311+
312+
void initialize_lf_thread_id() {}
313+
314+
int lf_thread_id() { return get_core_num(); }
315+
316+
#endif // !LF_SINGLE_THREADED
209317

210318
#endif // PLATFORM_RP2040

0 commit comments

Comments
 (0)