Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions low_level_platform/api/platform/lf_patmos_support.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,14 @@
#define PRINTF_TIME "%" PRId64
#define PRINTF_MICROSTEP "%" PRIu32
#define PRINTF_TAG "(%" PRId64 ", %" PRIu32 ")"
#if !defined(LF_SINGLE_THREADED)
#include <pthread.h>
typedef pthread_t lf_thread_t;
typedef pthread_mutex_t lf_mutex_t;
typedef struct {
lf_mutex_t* mutex;
pthread_cond_t condition;
} lf_cond_t;
Comment on lines +25 to +32
#endif

#endif // LF_PATMOS_SUPPORT_H
138 changes: 137 additions & 1 deletion low_level_platform/impl/src/lf_patmos_support.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
#include <machine/exceptions.h>
#include <stdio.h>

int lf_disable_interrupts_nested(void);
int lf_enable_interrupts_nested(void);

// Keep track of physical actions being entered into the system
static volatile bool _lf_async_event = false;
// Keep track of whether we are in a critical section or not
Expand Down Expand Up @@ -112,6 +115,139 @@ int _lf_single_threaded_notify_of_event() {
_lf_async_event = true;
return 0;
}
#endif // LF_SINGLE_THREADED
#else // LF_SINGLE_THREADED

#define LF_PATMOS_MAX_CORES 64
static volatile int _lf_num_nested_critical_sections_by_core[LF_PATMOS_MAX_CORES] = {0};

Comment on lines +118 to +122
static inline volatile int* _lf_current_core_nested_counter() {
int cpuid = (int)get_cpuid();
if (cpuid < 0 || cpuid >= LF_PATMOS_MAX_CORES) {
return &_lf_num_nested_critical_sections_by_core[0];
}
Comment on lines +123 to +127
return &_lf_num_nested_critical_sections_by_core[cpuid];
}

int lf_disable_interrupts_nested() {
volatile int* nested_counter = _lf_current_core_nested_counter();
if ((*nested_counter)++ == 0) {
intr_disable();
}
return 0;
Comment on lines +131 to +136
Copy link

Copilot AI Apr 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the threaded (#else) branch, _lf_num_nested_critical_sections is a single global counter shared by all threads/cores. With multiple threads, this can prevent intr_disable() from being called on a core that enters its first critical section while another core is already in one (counter != 0), leaving interrupts enabled on that core. Make the nesting counter per-core/per-thread (similar to FlexPRET’s critical_section_num_nested[hartid]) or otherwise ensure interrupt masking is correctly applied independently on each core.

Copilot uses AI. Check for mistakes.
}
Comment on lines +131 to +137

int lf_enable_interrupts_nested() {
volatile int* nested_counter = _lf_current_core_nested_counter();
if (*nested_counter <= 0) {
return 1;
}

if (--(*nested_counter) == 0) {
intr_enable();
}
return 0;
}

int lf_available_cores() { return (int)get_cpucnt(); }

lf_thread_t lf_thread_self() {
lf_thread_t self = {0};
self.cpuid = (int)get_cpuid();
return self;
Comment on lines +154 to +156
Copy link

Copilot AI Apr 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lf_thread_t is defined as pthread_t in lf_patmos_support.h, but here lf_thread_self() treats it as a struct with a cpuid field (lf_thread_t self = {0}; self.cpuid = ...). This will not compile and also conflicts with how lf_thread_create/join cast to pthread_t. Make lf_thread_t consistent across header and implementation (e.g., use pthread_t everywhere and return pthread_self(), or define a Patmos-specific struct type in the header and adjust create/join accordingly).

Suggested change
lf_thread_t self = {0};
self.cpuid = (int)get_cpuid();
return self;
return pthread_self();

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The suggested change was not compatible with Patmos implementation

}

int lf_thread_create(lf_thread_t* thread, void* (*lf_thread)(void*), void* arguments) {
assert(thread != NULL);
return pthread_create((pthread_t*)thread, NULL, lf_thread, arguments);
}

int lf_thread_join(lf_thread_t thread, void** thread_return) {
return pthread_join((pthread_t)thread, thread_return);
}
Comment on lines +153 to +166

int lf_thread_id() { return (int)get_cpuid(); }

void initialize_lf_thread_id() {}

Comment on lines +168 to +171
Copy link

Copilot AI Apr 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lf_thread_id() / initialize_lf_thread_id() are also provided by low_level_platform/impl/src/lf_platform_util.c for all non-Zephyr, non-single-threaded builds. Adding Patmos-specific definitions here will cause duplicate symbol/linker errors in threaded Patmos builds. Either remove these definitions and rely on the shared implementation, or change the build/guards so only one implementation is compiled.

Suggested change
int lf_thread_id() { return (int)get_cpuid(); }
void initialize_lf_thread_id() {}

Copilot uses AI. Check for mistakes.
int lf_mutex_init(lf_mutex_t* mutex) {
int result;
pthread_mutexattr_t attr;

assert(mutex != NULL);

result = pthread_mutexattr_init(&attr);
if (result != 0) {
return result;
}

result = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
if (result != 0) {
pthread_mutexattr_destroy(&attr);
return result;
}

result = pthread_mutex_init((pthread_mutex_t*)mutex, &attr);
pthread_mutexattr_destroy(&attr);
return result;
}

int lf_mutex_lock(lf_mutex_t* mutex) {
assert(mutex != NULL);
return pthread_mutex_lock((pthread_mutex_t*)mutex);
}

int lf_mutex_unlock(lf_mutex_t* mutex) {
assert(mutex != NULL);
return pthread_mutex_unlock((pthread_mutex_t*)mutex);
}

int lf_cond_init(lf_cond_t* cond, lf_mutex_t* mutex) {
assert(cond != NULL);
assert(mutex != NULL);
cond->mutex = mutex;
return pthread_cond_init((pthread_cond_t*)&cond->condition, NULL);
}
Comment on lines +204 to +209
Copy link

Copilot AI Apr 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lf_cond_init() accepts an associated mutex but does not store or use it, which makes it impossible to implement lf_cond_wait() correctly. The core API assumes the condition variable knows its associated mutex (see low_level_platform.h docs and the POSIX implementation). Update lf_cond_t to include a mutex pointer (or otherwise bind the mutex here) and use it in lf_cond_wait/_lf_cond_timedwait.

Copilot uses AI. Check for mistakes.

int lf_cond_signal(lf_cond_t* cond) {
assert(cond != NULL);
return pthread_cond_signal((pthread_cond_t*)&cond->condition);
}

int lf_cond_broadcast(lf_cond_t* cond) {
assert(cond != NULL);
return pthread_cond_broadcast((pthread_cond_t*)&cond->condition);
}

int lf_cond_wait(lf_cond_t* cond) {
assert(cond != NULL);
assert(cond->mutex != NULL);
return pthread_cond_wait((pthread_cond_t*)&cond->condition, (pthread_mutex_t*)cond->mutex);
}

int _lf_cond_timedwait(lf_cond_t* cond, instant_t wakeup_time) {
struct timespec ts;
int rc;

assert(cond != NULL);
assert(cond->mutex != NULL);

instant_t now;
_lf_clock_gettime(&now);

if (now >= wakeup_time) {
return LF_TIMEOUT;
}

ts.tv_sec = wakeup_time / 1000000000LL;
ts.tv_nsec = wakeup_time % 1000000000LL;

rc = pthread_cond_timedwait((pthread_cond_t*)&cond->condition, (pthread_mutex_t*)cond->mutex, &ts);
if (rc == ETIMEDOUT) {
return LF_TIMEOUT;
}
return rc;
}
Comment on lines +227 to +249
Copy link

Copilot AI Apr 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_lf_cond_timedwait() always returns LF_TIMEOUT without waiting when now < wakeup_time. Callers (e.g., watchdog and clock waits) expect this to block until either signaled or the timeout expires; returning immediately will cause busy looping and incorrect timing behavior. Implement a real timed wait (e.g., via pthread_cond_timedwait() with an absolute timespec) or an equivalent Patmos-specific mechanism.

Copilot uses AI. Check for mistakes.

#endif

#endif // PLATFORM_PATMOS
2 changes: 1 addition & 1 deletion low_level_platform/impl/src/lf_platform_util.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ int map_priorities(int priority, int dest_min, int dest_max) {
(LF_SCHED_MAX_PRIORITY - LF_SCHED_MIN_PRIORITY));
}

#ifndef PLATFORM_ZEPHYR // on Zephyr, this is handled separately
#if !defined(PLATFORM_ZEPHYR) && !defined(PLATFORM_PATMOS) // on Zephyr and PATMOS, this is handled separately
#ifndef LF_SINGLE_THREADED
static int _lf_worker_thread_count = 0;

Expand Down
Loading