ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_esp_hosted_rtos.c
Go to the documentation of this file.
1
20
21#include <stddef.h>
22#include <stdint.h>
23#include <string.h>
24
25#include "ra8_attributes.h"
26#include "ra8_cgc.h"
27#include "ra8_check.h"
28#include "ra8_err.h"
29#include "ra8_esp_hosted_port.h"
31#include "ra8_log.h"
32
33#ifdef RA8_OFF_TARGET
34#include "ra8_esp_hosted_tx_shim_sync_internal.h"
35#else
36#include "tx_api.h"
37#endif
38
40
49static const char* s_tag = "ESPH_RTOS";
50
79
97typedef void (*ra8_esp_hosted_thread_entry_t)(void const* arg);
98
113typedef void (*ra8_esp_hosted_timer_cb_t)(void* arg);
114
136
158
188
199
211
212/* ----------------------------------------------------------------------- */
213/* Table helpers */
214/* ----------------------------------------------------------------------- */
215
216uint32_t priv_ra8_esp_hosted_rtos_slot_take(bool* used, uint32_t count)
217{
218 if (used == nullptr) {
219 return count;
220 }
221 for (uint32_t i = 0U; i < count; ++i) {
222 if (!used[i]) {
223 used[i] = true;
224 return i;
225 }
226 }
227 return count;
228}
229
230uint32_t priv_ra8_esp_hosted_rtos_slot_index(const void* handle,
231 const void* base,
232 size_t stride,
233 uint32_t count,
234 const bool* used)
235{
236 if ((handle == nullptr) || (base == nullptr) || (used == nullptr)) {
237 return count;
238 }
239 for (uint32_t i = 0U; i < count; ++i) {
240 const void* const row = (const void*)((const uint8_t*)base + ((size_t)i * stride));
241 if ((row == handle) && used[i]) {
242 return i;
243 }
244 }
245 return count;
246}
247
271RA8_INTERNAL static void internal_copy_name(char* dst, size_t cap, const char* src)
272{
273 (void)memset(dst, 0, cap);
274 if (src == nullptr) {
275 return;
276 }
277 const size_t limit = cap - 1U;
278 size_t n = 0U;
279 while ((n < limit) && (src[n] != '\0')) {
280 dst[n] = src[n];
281 n++;
282 }
283 dst[n] = '\0';
284}
285
300{
301 const uint32_t idx = (uint32_t)index;
302 if (idx >= (uint32_t)k_ra8_esp_hosted_max_threads) {
303 return;
304 }
305 if (s_rtos.thread_used[idx]) {
306 s_rtos.threads[idx].entry(s_rtos.threads[idx].arg);
307 }
308}
309
323{
324 const uint32_t idx = (uint32_t)index;
325 if (idx >= (uint32_t)k_ra8_esp_hosted_max_timers) {
326 return;
327 }
328 if (s_rtos.timer_used[idx]) {
329 s_rtos.timers[idx].cb_fn(s_rtos.timers[idx].arg);
330 }
331}
332
348static void internal_spin_us(uint32_t usec)
349{
350 const uint32_t iters = priv_ra8_esp_hosted_rtos_us_spin_iters(s_rtos.cpu_hz, usec);
351 volatile uint32_t sink = 0U;
352 for (uint32_t i = 0U; i < (uint32_t)k_ra8_esp_hosted_spin_iters_max; ++i) {
353 if (i >= iters) {
354 break;
355 }
356 sink = sink + 1U;
357 }
358}
359
360/* ----------------------------------------------------------------------- */
361/* Testable arithmetic */
362/* ----------------------------------------------------------------------- */
363
365{
366 if (timeout_ms < 0) {
367 return (uint32_t)TX_WAIT_FOREVER;
368 }
369 if (timeout_ms == 0) {
370 return (uint32_t)TX_NO_WAIT;
371 }
372 return (uint32_t)timeout_ms / (uint32_t)k_ra8_esp_hosted_ms_per_tick;
373}
374
375uint32_t priv_ra8_esp_hosted_rtos_us_spin_iters(uint32_t cpu_hz, uint32_t usec)
376{
377 if ((cpu_hz == 0U) || (usec == 0U)) {
378 return 0U;
379 }
380 const uint32_t cycles_per_us = cpu_hz / (uint32_t)k_ra8_esp_hosted_hz_per_mhz;
381 const uint32_t iters = (cycles_per_us * usec) / (uint32_t)k_ra8_esp_hosted_spin_cycles_per_iter;
382 if (iters == 0U) {
383 return 1U;
384 }
385 if (iters > (uint32_t)k_ra8_esp_hosted_spin_iters_max) {
386 return (uint32_t)k_ra8_esp_hosted_spin_iters_max;
387 }
388 return iters;
389}
390
391/* ----------------------------------------------------------------------- */
392/* Thread and sleep vtable slots */
393/* ----------------------------------------------------------------------- */
394
416RA8_INTERNAL static void* internal_h_thread_create(const char* tname,
417 uint32_t tprio,
418 uint32_t tstack_size,
419 ra8_esp_hosted_thread_entry_t start_routine,
420 void* sr_arg)
421{
422 if (!s_rtos.ready || (start_routine == nullptr)) {
423 return nullptr;
424 }
425 if ((tstack_size < (uint32_t)TX_MINIMUM_STACK) ||
426 (tstack_size > (uint32_t)k_ra8_esp_hosted_thread_stack_bytes)) {
427 return nullptr;
428 }
429 const uint32_t idx =
431 if (idx == (uint32_t)k_ra8_esp_hosted_max_threads) {
432 ra8_log_error(s_tag, "thread table exhausted");
433 return nullptr;
434 }
435 ra8_esp_hosted_thread_slot_t* const slot = &s_rtos.threads[idx];
436 slot->entry = start_routine;
437 slot->arg = sr_arg;
438 internal_copy_name(slot->name, sizeof(slot->name), tname);
439 if (tx_thread_create(&slot->cb,
440 slot->name,
442 (ULONG)idx,
443 s_thread_stacks[idx],
444 (ULONG)tstack_size,
445 (UINT)tprio,
446 (UINT)tprio,
449 s_rtos.thread_used[idx] = false;
450 return nullptr;
451 }
452 return slot;
453}
454
473RA8_INTERNAL static int internal_h_thread_cancel(void* thread_handle)
474{
475 const uint32_t idx = priv_ra8_esp_hosted_rtos_slot_index(thread_handle,
476 s_rtos.threads,
477 sizeof(s_rtos.threads[0]),
479 s_rtos.thread_used);
480 if (idx == (uint32_t)k_ra8_esp_hosted_max_threads) {
481 return RET_INVALID;
482 }
483 int rc = RET_OK;
484 if (tx_thread_terminate(&s_rtos.threads[idx].cb) != TX_SUCCESS) {
485 rc = RET_FAIL;
486 }
487 if (tx_thread_delete(&s_rtos.threads[idx].cb) != TX_SUCCESS) {
488 rc = RET_FAIL;
489 }
490 s_rtos.threads[idx].entry = nullptr;
491 s_rtos.thread_used[idx] = false;
492 return rc;
493}
494
507{
508 if (s_rtos.ready) {
509 tx_thread_relinquish();
510 }
511}
512
528RA8_INTERNAL static unsigned int internal_h_msleep(unsigned int mseconds)
529{
530 if (s_rtos.ready) {
532 }
533 return mseconds;
534}
535
550RA8_INTERNAL static unsigned int internal_h_sleep(unsigned int seconds)
551{
552 (void)internal_h_msleep(seconds * (unsigned int)k_ra8_esp_hosted_ms_per_sec);
553 return seconds;
554}
555
577RA8_INTERNAL static unsigned int internal_h_usleep(unsigned int useconds)
578{
579 if (useconds == 0U) {
580 return 0U;
581 }
582 const uint32_t whole_ms = (uint32_t)useconds / (uint32_t)k_ra8_esp_hosted_us_per_ms;
583 const uint32_t rem_us = (uint32_t)useconds % (uint32_t)k_ra8_esp_hosted_us_per_ms;
584 if (whole_ms > 0U) {
585 (void)internal_h_msleep(whole_ms);
586 }
587 internal_spin_us(rem_us);
588 return useconds;
589}
590
608static unsigned int internal_h_blocking_delay(unsigned int number)
609{
610 const uint32_t iters = (number > (unsigned int)k_ra8_esp_hosted_delay_iters_max)
612 : (uint32_t)number;
613 volatile uint32_t sink = 0U;
614 for (uint32_t i = 0U; i < (uint32_t)k_ra8_esp_hosted_delay_iters_max; ++i) {
615 if (i >= iters) {
616 break;
617 }
618 sink = sink + 1U;
619 }
620 return number;
621}
622
623/* ----------------------------------------------------------------------- */
624/* Timer and clock vtable slots */
625/* ----------------------------------------------------------------------- */
626
648RA8_INTERNAL static void* internal_h_timer_start(const char* name,
649 int duration_ms,
650 int type,
651 ra8_esp_hosted_timer_cb_t timeout_handler,
652 void* arg)
653{
654 if (!s_rtos.ready || (timeout_handler == nullptr) || (duration_ms <= 0)) {
655 return nullptr;
656 }
657 if ((type != H_TIMER_TYPE_ONESHOT) && (type != H_TIMER_TYPE_PERIODIC)) {
658 return nullptr;
659 }
660 const uint32_t idx =
662 if (idx == (uint32_t)k_ra8_esp_hosted_max_timers) {
663 ra8_log_error(s_tag, "timer table exhausted");
664 return nullptr;
665 }
666 ra8_esp_hosted_timer_slot_t* const slot = &s_rtos.timers[idx];
667 slot->cb_fn = timeout_handler;
668 slot->arg = arg;
669 internal_copy_name(slot->name, sizeof(slot->name), name);
670 const ULONG ticks = (ULONG)priv_ra8_esp_hosted_rtos_ms_to_ticks(duration_ms);
671 const ULONG repeat = (type == H_TIMER_TYPE_PERIODIC) ? ticks : 0U;
672 if (tx_timer_create(&slot->cb,
673 slot->name,
675 (ULONG)idx,
676 ticks,
677 repeat,
678 TX_AUTO_ACTIVATE) != TX_SUCCESS) {
679 s_rtos.timer_used[idx] = false;
680 return nullptr;
681 }
682 return slot;
683}
684
702RA8_INTERNAL static int internal_h_timer_stop(void* timer_handle)
703{
704 const uint32_t idx = priv_ra8_esp_hosted_rtos_slot_index(timer_handle,
705 s_rtos.timers,
706 sizeof(s_rtos.timers[0]),
708 s_rtos.timer_used);
709 if (idx == (uint32_t)k_ra8_esp_hosted_max_timers) {
710 return RET_INVALID;
711 }
712 int rc = RET_OK;
713 if (tx_timer_deactivate(&s_rtos.timers[idx].cb) != TX_SUCCESS) {
714 rc = RET_FAIL;
715 }
716 if (tx_timer_delete(&s_rtos.timers[idx].cb) != TX_SUCCESS) {
717 rc = RET_FAIL;
718 }
719 s_rtos.timers[idx].cb_fn = nullptr;
720 s_rtos.timer_used[idx] = false;
721 return rc;
722}
723
746{
747 if (!s_rtos.ready) {
748 return 0U;
749 }
750 const uint32_t now = (uint32_t)tx_time_get();
751 if (now < s_rtos.last_ticks) {
752 s_rtos.tick_epoch += ((uint64_t)UINT32_MAX + 1U);
753 }
754 s_rtos.last_ticks = now;
755 return (s_rtos.tick_epoch + (uint64_t)now) * (uint64_t)k_ra8_esp_hosted_ms_per_tick;
756}
757
758/* ----------------------------------------------------------------------- */
759/* Lifecycle and binding */
760/* ----------------------------------------------------------------------- */
761
763{
764 return s_rtos.ready;
765}
766
768{
769 if (s_rtos.ready) {
770 ra8_log_error(s_tag, "RTOS substrate already initialised");
772 }
773 (void)memset(&s_rtos, 0, sizeof(s_rtos));
776 uint32_t cpu_hz = 0U;
778 cpu_hz = (uint32_t)k_ra8_esp_hosted_default_cpu_hz;
779 ra8_log_warn(s_tag, "core rate query failed; usleep uses the fallback rate");
780 }
781 s_rtos.cpu_hz = cpu_hz;
782 s_rtos.last_ticks = (uint32_t)tx_time_get();
783 s_rtos.ready = true;
784 return k_ra8_ok;
785}
786
788{
789 if (!s_rtos.ready) {
790 ra8_log_error(s_tag, "RTOS substrate not initialised");
792 }
793 ra8_err_t worst = k_ra8_ok;
794 for (uint32_t i = 0U; i < (uint32_t)k_ra8_esp_hosted_max_timers; ++i) {
795 if (s_rtos.timer_used[i] && (internal_h_timer_stop(&s_rtos.timers[i]) != RET_OK)) {
796 worst = k_ra8_err_rtos_error;
797 }
798 }
799 for (uint32_t i = 0U; i < (uint32_t)k_ra8_esp_hosted_max_threads; ++i) {
800 if (s_rtos.thread_used[i] && (internal_h_thread_cancel(&s_rtos.threads[i]) != RET_OK)) {
801 worst = k_ra8_err_rtos_error;
802 }
803 }
805 worst = k_ra8_err_rtos_error;
806 }
808 worst = k_ra8_err_rtos_error;
809 }
810 (void)memset(&s_rtos, 0, sizeof(s_rtos));
811 return worst;
812}
813
815{
816 RA8_CHECK_NULL_PTR(out, s_tag, "vtable is NULL");
817 out->_h_thread_create = internal_h_thread_create;
818 out->_h_thread_cancel = internal_h_thread_cancel;
819 out->_h_thread_yield = internal_h_thread_yield;
820 out->_h_msleep = internal_h_msleep;
821 out->_h_usleep = internal_h_usleep;
822 out->_h_sleep = internal_h_sleep;
823 out->_h_blocking_delay = internal_h_blocking_delay;
824 out->_h_timer_start = internal_h_timer_start;
825 out->_h_timer_stop = internal_h_timer_stop;
826 out->_h_get_time_ms = internal_h_get_time_ms;
827 return k_ra8_ok;
828}
esp-hosted port header: RTOS handle types, return codes and budgets.
#define H_TIMER_TYPE_ONESHOT
Timer that fires once and then stops.
#define RET_FAIL
Operation failed for an unspecified reason.
#define RET_INVALID
A parameter was out of contract (null handle, bad size).
#define RET_OK
Operation completed.
#define H_TIMER_TYPE_PERIODIC
Timer that reschedules itself after every expiry.
static const char * s_tag
Logging / check tag.
Definition ra8_app.c:17
Annotation-attribute framework macros for ra8-firmware.
#define RA8_BOUNDED_LOOP(symbol)
NASA Power-of-10 Rule 2: every loop in a FUNCTION has a constant bound.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
High-level Clock Generation Circuit driver.
ra8_err_t ra8_cgc_get_clock_hz(ra8_clock_id_t id, uint32_t *out_hz)
Query the current frequency of a clock-tree domain.
Definition ra8_cgc.c:132
@ k_ra8_clock_id_cpuclk0
Cortex-M85 CPUCLK0.
Definition ra8_cgc.h:70
Validation and Error-Checking Macros for ra8-firmware.
#define RA8_RETURN_ON_ERROR(err, tag, message)
Early return on error, propagating the code upward.
Definition ra8_check.h:184
#define RA8_CHECK_NULL_PTR(ptr, tag, message)
Reject nullptr pointer, returning k_ra8_err_null_ptr.
Definition ra8_check.h:243
Error Code Definitions for ra8-firmware.
@ k_ra8_err_invalid_state
Module in wrong state for requested operation.
Definition ra8_err.h:161
@ k_ra8_err_not_initialized
Module not initialized – _init() not yet called successfully.
Definition ra8_err.h:235
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
@ k_ra8_err_rtos_error
Generic RTOS error (reserved for future use).
Definition ra8_err.h:357
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
static void internal_timer_expiry(ULONG arg)
ThreadX expiry function: run one sampling pass.
Public entry point of the RA8D2 + ThreadX port of esp-hosted.
@ k_ra8_esp_hosted_max_threads
Threads the port can start: the SPI transaction pump, the receive dispatcher, and headroom for the RP...
@ k_ra8_esp_hosted_max_timers
Software timers the port can arm concurrently.
static unsigned int internal_h_blocking_delay(unsigned int number)
Busy-delay for a bounded number of loop iterations.
ra8_err_t priv_ra8_esp_hosted_rtos_deinit(void)
Tear the RTOS substrate down, deleting every outstanding object.
uint32_t priv_ra8_esp_hosted_rtos_ms_to_ticks(int timeout_ms)
Convert an esp-hosted millisecond timeout to a ThreadX wait option.
static void * internal_h_timer_start(const char *name, int duration_ms, int type, ra8_esp_hosted_timer_cb_t timeout_handler, void *arg)
Create and start a software timer.
static void internal_timer_expiry(ULONG index)
ThreadX expiry shim that calls the core-supplied timer callback.
ra8_err_t priv_ra8_esp_hosted_rtos_bind(hosted_osi_funcs_t *out)
Populate the thread, sleep, timer and clock slots of the vtable.
void(* ra8_esp_hosted_timer_cb_t)(void *arg)
Signature of the expiry callback the vendored core hands to the port.
static void internal_copy_name(char *dst, size_t cap, const char *src)
Copy a caller-supplied object name into a fixed buffer.
static unsigned int internal_h_sleep(unsigned int seconds)
Sleep the calling thread for a whole number of seconds.
static void internal_thread_entry(ULONG index)
ThreadX entry shim that calls the core-supplied thread body.
void(* ra8_esp_hosted_thread_entry_t)(void const *arg)
Signature of the thread body the vendored core hands to the port.
static unsigned int internal_h_msleep(unsigned int mseconds)
Sleep the calling thread for a whole number of milliseconds.
uint32_t priv_ra8_esp_hosted_rtos_us_spin_iters(uint32_t cpu_hz, uint32_t usec)
Size the busy-wait loop that stands in for a microsecond delay.
bool priv_ra8_esp_hosted_rtos_is_ready(void)
Report whether the RTOS substrate is currently initialised.
static ra8_esp_hosted_rtos_state_t s_rtos
Singleton state of the thread, timer and clock half.
ra8_err_t priv_ra8_esp_hosted_rtos_init(void)
Bring the RTOS substrate up: byte pools and empty object tables.
uint32_t priv_ra8_esp_hosted_rtos_slot_take(bool *used, uint32_t count)
Claim the first free row of an occupancy bitmap.
static void internal_h_thread_yield(void)
Give up the remainder of the current time slice.
static void internal_spin_us(uint32_t usec)
Burn a bounded number of core cycles standing in for a short delay.
static uint64_t internal_h_get_time_ms(void)
Read milliseconds since kernel start, without a 32-bit wrap.
ra8_esp_hosted_rtos_const_t
Numeric constants of the object tables, sleeps and spin sizing.
@ k_ra8_esp_hosted_us_per_ms
Microseconds in one millisecond.
@ k_ra8_esp_hosted_spin_iters_max
Spin bound (NASA Rule 2).
@ k_ra8_esp_hosted_delay_iters_max
Busy-delay iteration bound.
@ k_ra8_esp_hosted_ms_per_sec
Milliseconds in one second.
@ k_ra8_esp_hosted_default_cpu_hz
Fallback core rate, 1 GHz.
@ k_ra8_esp_hosted_ms_per_tick
Milliseconds in one kernel tick.
@ k_ra8_esp_hosted_spin_cycles_per_iter
Core cycles per spin pass.
@ k_ra8_esp_hosted_thread_stack_bytes
Bytes per thread stack.
@ k_ra8_esp_hosted_hz_per_mhz
Hertz in one megahertz.
@ k_ra8_esp_hosted_name_max
Object name buffer size.
static uint8_t s_thread_stacks[k_ra8_esp_hosted_max_threads][k_ra8_esp_hosted_thread_stack_bytes]
Fixed stack storage, one region per thread table row.
static void * internal_h_thread_create(const char *tname, uint32_t tprio, uint32_t tstack_size, ra8_esp_hosted_thread_entry_t start_routine, void *sr_arg)
Start a thread from the fixed thread table.
uint32_t priv_ra8_esp_hosted_rtos_slot_index(const void *handle, const void *base, size_t stride, uint32_t count, const bool *used)
Resolve an opaque handle to its row index in a fixed table.
static int internal_h_timer_stop(void *timer_handle)
Stop a timer and free its table row.
static int internal_h_thread_cancel(void *thread_handle)
Terminate and delete a thread, freeing its table row.
static unsigned int internal_h_usleep(unsigned int useconds)
Delay for a number of microseconds, honestly.
Module-private contract of the esp-hosted RTOS abstraction slice.
ra8_err_t priv_ra8_esp_hosted_rtos_sync_deinit(void)
Delete every outstanding mutex and semaphore.
ra8_err_t priv_ra8_esp_hosted_rtos_sync_init(void)
Clear the mutex and semaphore tables and mark them usable.
ra8_err_t priv_ra8_esp_hosted_rtos_pool_init(void)
Create the two byte pools over their static backing arrays.
ra8_err_t priv_ra8_esp_hosted_rtos_pool_deinit(void)
Destroy every queue and both byte pools.
void * memset(void *dst, int value, size_t n)
Fill memory with a constant byte value.
Lightweight Logging Interface for ra8-firmware.
#define ra8_log_warn(tag, message)
RA8 log warn.
Definition ra8_log.h:347
#define ra8_log_error(tag, message)
RA8 log error.
Definition ra8_log.h:335
unsigned int UINT
ThreadX-compatible unsigned int (host stub).
#define TX_SUCCESS
#define tx_thread_delete
#define tx_thread_create
#define tx_thread_terminate
#define tx_time_get
#define tx_thread_sleep
#define TX_NO_TIME_SLICE
#define TX_AUTO_START
#define TX_WAIT_FOREVER
unsigned long ULONG
ThreadX-compatible unsigned long (host stub).
static void internal_thread_entry(ULONG arg)
The supervisor thread's entry point.
Opaque thread stand-in for the host build.
Module state of the thread, timer and clock half – entirely static.
uint32_t last_ticks
Most recent 32-bit tick reading.
ra8_esp_hosted_timer_slot_t timers[k_ra8_esp_hosted_max_timers]
Timers.
bool thread_used[k_ra8_esp_hosted_max_threads]
Thread occupancy.
bool ready
True while the substrate is initialised.
uint32_t cpu_hz
Cached core rate used to size the spin loop.
bool timer_used[k_ra8_esp_hosted_max_timers]
Timer occupancy.
uint64_t tick_epoch
Accumulated rollovers, in ticks.
ra8_esp_hosted_thread_slot_t threads[k_ra8_esp_hosted_max_threads]
Threads.
One row of the fixed thread table.
char name[k_ra8_esp_hosted_name_max]
Bounded name copy.
void * arg
Argument handed to the body.
ra8_esp_hosted_thread_entry_t entry
Core-supplied thread body.
TX_THREAD cb
ThreadX control block; must stay first.
One row of the fixed timer table.
char name[k_ra8_esp_hosted_name_max]
Bounded name copy.
void * arg
Argument handed to the callback.
TX_TIMER cb
ThreadX control block; must stay first.
ra8_esp_hosted_timer_cb_t cb_fn
Core-supplied expiry callback.
#define TX_MINIMUM_STACK
TX MINIMUM STACK.
Definition tx_user.h:43