ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_esp_hosted_rtos.c File Reference

Thread, sleep, timer and clock vtable slots, plus the bind entry. More...

#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include "ra8_attributes.h"
#include "ra8_cgc.h"
#include "ra8_check.h"
#include "ra8_err.h"
#include "ra8_esp_hosted_port.h"
#include "ra8_esp_hosted_rtos_internal.h"
#include "ra8_log.h"
#include "tx_api.h"
#include "port_esp_hosted_host_os.h"
Include dependency graph for ra8_esp_hosted_rtos.c:

Go to the source code of this file.

Data Structures

struct  ra8_esp_hosted_thread_slot_t
 One row of the fixed thread table. More...
struct  ra8_esp_hosted_timer_slot_t
 One row of the fixed timer table. More...
struct  ra8_esp_hosted_rtos_state_t
 Module state of the thread, timer and clock half – entirely static. More...

Typedefs

typedef void(* ra8_esp_hosted_thread_entry_t) (void const *arg)
 Signature of the thread body the vendored core hands to the port.
typedef void(* ra8_esp_hosted_timer_cb_t) (void *arg)
 Signature of the expiry callback the vendored core hands to the port.

Enumerations

enum  ra8_esp_hosted_rtos_const_t : uint32_t {
  k_ra8_esp_hosted_ms_per_tick = 1U ,
  k_ra8_esp_hosted_us_per_ms = 1000U ,
  k_ra8_esp_hosted_ms_per_sec = 1000U ,
  k_ra8_esp_hosted_hz_per_mhz = 1000000U ,
  k_ra8_esp_hosted_spin_cycles_per_iter = 4U ,
  k_ra8_esp_hosted_spin_iters_max = 250000U ,
  k_ra8_esp_hosted_delay_iters_max = 1000000U ,
  k_ra8_esp_hosted_default_cpu_hz = 1000000000U ,
  k_ra8_esp_hosted_thread_stack_bytes = 5120U ,
  k_ra8_esp_hosted_name_max = 16U
}
 Numeric constants of the object tables, sleeps and spin sizing. More...

Functions

uint32_t priv_ra8_esp_hosted_rtos_slot_take (bool *used, uint32_t count)
 Claim the first free row of an occupancy bitmap.
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 void internal_copy_name (char *dst, size_t cap, const char *src)
 Copy a caller-supplied object name into a fixed buffer.
static void internal_thread_entry (ULONG index)
 ThreadX entry shim that calls the core-supplied thread body.
static void internal_timer_expiry (ULONG index)
 ThreadX expiry shim that calls the core-supplied timer callback.
static void internal_spin_us (uint32_t usec)
 Burn a bounded number of core cycles standing in for a short delay.
uint32_t priv_ra8_esp_hosted_rtos_ms_to_ticks (int timeout_ms)
 Convert an esp-hosted millisecond timeout to a ThreadX wait option.
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.
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.
static int internal_h_thread_cancel (void *thread_handle)
 Terminate and delete a thread, freeing its table row.
static void internal_h_thread_yield (void)
 Give up the remainder of the current time slice.
static unsigned int internal_h_msleep (unsigned int mseconds)
 Sleep the calling thread for a whole number of milliseconds.
static unsigned int internal_h_sleep (unsigned int seconds)
 Sleep the calling thread for a whole number of seconds.
static unsigned int internal_h_usleep (unsigned int useconds)
 Delay for a number of microseconds, honestly.
static unsigned int internal_h_blocking_delay (unsigned int number)
 Busy-delay for a bounded number of loop iterations.
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 int internal_h_timer_stop (void *timer_handle)
 Stop a timer and free its table row.
static uint64_t internal_h_get_time_ms (void)
 Read milliseconds since kernel start, without a 32-bit wrap.
bool priv_ra8_esp_hosted_rtos_is_ready (void)
 Report whether the RTOS substrate is currently initialised.
ra8_err_t priv_ra8_esp_hosted_rtos_init (void)
 Bring the RTOS substrate up: byte pools and empty object tables.
ra8_err_t priv_ra8_esp_hosted_rtos_deinit (void)
 Tear the RTOS substrate down, deleting every outstanding object.
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.

Variables

static const char * s_tag = "ESPH_RTOS"
 Log tag for the synchronisation half of the port.
static ra8_esp_hosted_rtos_state_t s_rtos
 Singleton state of the thread, timer and clock half.
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.

Detailed Description

Thread, sleep, timer and clock vtable slots, plus the bind entry.

Tag
[Ring 4 / PORT] {World: NS}

See port/esp-hosted/src/ra8_esp_hosted_rtos_internal.h for the contracts. Every kernel object the vendored core asks for is taken from a fixed table in this translation unit – there is no heap on this board – and the handles the core stores are the addresses of those table rows. The memory and queue halves live in ra8_esp_hosted_rtos_pool.c, which also owns the host-build ThreadX model's shared state.

Since
0.1.0

Definition in file ra8_esp_hosted_rtos.c.

Typedef Documentation

◆ ra8_esp_hosted_thread_entry_t

typedef void(* ra8_esp_hosted_thread_entry_t) (void const *arg)

Signature of the thread body the vendored core hands to the port.

Named rather than spelled inline at every use so the thread table row and the _h_thread_create slot cannot drift apart, and so neither declaration carries a nested function-pointer parameter that hides the enclosing function's own name from a reader – or from the MC/DC citation checker, which anchors a decision to the function it sits in.

Parameters
[in]argThe sr_arg the core supplied at creation.
Example:
static void body(void const* arg) { (void)arg; }
void(* ra8_esp_hosted_thread_entry_t)(void const *arg)
Signature of the thread body the vendored core hands to the port.
See also
ra8_esp_hosted_thread_slot_t
Since
0.1.0

Definition at line 97 of file ra8_esp_hosted_rtos.c.

◆ ra8_esp_hosted_timer_cb_t

typedef void(* ra8_esp_hosted_timer_cb_t) (void *arg)

Signature of the expiry callback the vendored core hands to the port.

The mirror of ra8_esp_hosted_thread_entry_t for timers; it takes a mutable void* because that is what the core's own timer contract states.

Parameters
[in]argThe arg the core supplied at start.
Example:
static void on_expiry(void* arg) { (void)arg; }
void(* ra8_esp_hosted_timer_cb_t)(void *arg)
Signature of the expiry callback the vendored core hands to the port.
See also
ra8_esp_hosted_timer_slot_t
Since
0.1.0

Definition at line 113 of file ra8_esp_hosted_rtos.c.

Enumeration Type Documentation

◆ ra8_esp_hosted_rtos_const_t

enum ra8_esp_hosted_rtos_const_t : uint32_t

Numeric constants of the object tables, sleeps and spin sizing.

Collected here so the timing assumptions the port makes are stated once and are visible to anyone changing the kernel tick rate.

Invariant
k_ra8_esp_hosted_ms_per_tick matches TX_TIMER_TICKS_PER_SECOND in port/threadx/inc/tx_user.h.
k_ra8_esp_hosted_thread_stack_bytes is at least RPC_TASK_STACK_SIZE, the deepest stack the core asks for.
Example:
static_assert(k_ra8_esp_hosted_ms_per_tick == 1U, "1 kHz kernel tick");
@ k_ra8_esp_hosted_ms_per_tick
Milliseconds in one kernel tick.
See also
priv_ra8_esp_hosted_rtos_ms_to_ticks
Since
0.1.0
Enumerator
k_ra8_esp_hosted_ms_per_tick 

Milliseconds in one kernel tick.

k_ra8_esp_hosted_us_per_ms 

Microseconds in one millisecond.

k_ra8_esp_hosted_ms_per_sec 

Milliseconds in one second.

k_ra8_esp_hosted_hz_per_mhz 

Hertz in one megahertz.

k_ra8_esp_hosted_spin_cycles_per_iter 

Core cycles per spin pass.

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_default_cpu_hz 

Fallback core rate, 1 GHz.

k_ra8_esp_hosted_thread_stack_bytes 

Bytes per thread stack.

k_ra8_esp_hosted_name_max 

Object name buffer size.

Definition at line 67 of file ra8_esp_hosted_rtos.c.

Function Documentation

◆ internal_copy_name()

void internal_copy_name ( char * dst,
size_t cap,
const char * src )
static

Copy a caller-supplied object name into a fixed buffer.

ThreadX keeps the name pointer rather than the characters, so the port must own storage that outlives the caller's literal. Truncates rather than refusing: a shortened diagnostic name is never worth failing a create.

The copy is a bounded character loop that writes the terminator itself rather than a strlen-then-memcpy pair. The name comes from the vendored core, so it may be longer than cap or – on a corrupted caller – unterminated; this form reads at most cap bytes of it either way, and the terminator is written on every path instead of being inferred from a preceding memset.

Parameters
[out]dstDestination buffer.
[in]capDestination capacity in bytes, including the terminator.
[in]srcName to copy; a null pointer yields an empty name.
Precondition
dst covers cap bytes.
cap is at least one.
Postcondition
dst is NUL-terminated.
At most cap - 1 characters are copied.
Note
Thread-safe; touches only caller storage.
The loop is bounded by cap - 1 (NASA Power of 10 Rule 2).
Since
0.1.0

Definition at line 271 of file ra8_esp_hosted_rtos.c.

References memset(), and RA8_INTERNAL.

Referenced by internal_h_thread_create(), and internal_h_timer_start().

◆ internal_h_blocking_delay()

unsigned int internal_h_blocking_delay ( unsigned int number)
static

Busy-delay for a bounded number of loop iterations.

Deliberately not a time unit: upstream's contract for this slot is a raw spin count for the few places the scheduler must not run. The count is clamped so the loop stays statically bounded.

Parameters
[in]numberIterations requested.
Returns
The iteration count requested, clamped or not.
Return values
numberAlways; the return echoes the request, as upstream's does.
Precondition
The caller genuinely cannot yield here.
The caller accepts that the duration depends on the core rate.
Postcondition
At most k_ra8_esp_hosted_delay_iters_max iterations executed.
No module state is modified and the scheduler never ran.
Note
Safe from any context, including interrupt handlers.
Since
0.1.0

Definition at line 608 of file ra8_esp_hosted_rtos.c.

References internal_h_blocking_delay(), and k_ra8_esp_hosted_delay_iters_max.

Referenced by internal_h_blocking_delay(), and priv_ra8_esp_hosted_rtos_bind().

◆ internal_h_get_time_ms()

uint64_t internal_h_get_time_ms ( void )
static

Read milliseconds since kernel start, without a 32-bit wrap.

tx_time_get returns a 32-bit tick count that wraps after about 49.7 days at a 1 kHz tick, and the vendored core uses this value for elapsed-time arithmetic that a wrap would silently corrupt. Each call compares the new reading against the previous one and, when it has gone backwards, adds one full 2^32-tick epoch to a 64-bit accumulator. The detection holds as long as this is called at least once per epoch, which the transport's per-transaction use guarantees by a very wide margin.

Returns
Milliseconds since kernel start.
Return values
0The kernel has just started and the substrate is down.
1..UINT64_MAXThe extended millisecond count.
Precondition
The substrate is initialised, or the call reports zero.
The caller polls at least once per 2^32 ticks.
Postcondition
The recorded previous reading equals the value just read.
The accumulator has grown by one epoch if and only if a wrap was observed.
Note
Not reentrant: two threads racing here can both observe the same wrap. The transport calls it from one thread.
Since
0.1.0

Definition at line 745 of file ra8_esp_hosted_rtos.c.

References k_ra8_esp_hosted_ms_per_tick, RA8_INTERNAL, s_rtos, and tx_time_get.

Referenced by priv_ra8_esp_hosted_rtos_bind().

◆ internal_h_msleep()

unsigned int internal_h_msleep ( unsigned int mseconds)
static

Sleep the calling thread for a whole number of milliseconds.

One kernel tick is one millisecond, so the conversion is the identity; a zero request still yields, which is what the core expects of a zero-length sleep in a polling loop.

Parameters
[in]msecondsMilliseconds to sleep.
Returns
The milliseconds requested.
Return values
msecondsAlways; ThreadX does not report a short sleep.
Precondition
The caller is a thread, not an interrupt handler.
The substrate is initialised, or the call returns immediately.
Postcondition
At least mseconds milliseconds of kernel time have passed.
No module state is modified.
Note
Thread context only.
Since
0.1.0

Definition at line 528 of file ra8_esp_hosted_rtos.c.

References k_ra8_esp_hosted_ms_per_tick, RA8_INTERNAL, s_rtos, and tx_thread_sleep.

Referenced by internal_h_sleep(), internal_h_usleep(), and priv_ra8_esp_hosted_rtos_bind().

◆ internal_h_sleep()

unsigned int internal_h_sleep ( unsigned int seconds)
static

Sleep the calling thread for a whole number of seconds.

Converts to milliseconds and reuses the millisecond sleep, so there is one place the tick rate is applied.

Parameters
[in]secondsSeconds to sleep.
Returns
The seconds requested.
Return values
secondsAlways; ThreadX does not report a short sleep.
Precondition
The caller is a thread, not an interrupt handler.
The substrate is initialised, or the call returns immediately.
Postcondition
At least seconds seconds of kernel time have passed.
No module state is modified.
Note
Thread context only.
Since
0.1.0

Definition at line 550 of file ra8_esp_hosted_rtos.c.

References internal_h_msleep(), k_ra8_esp_hosted_ms_per_sec, and RA8_INTERNAL.

Referenced by priv_ra8_esp_hosted_rtos_bind().

◆ internal_h_thread_cancel()

int internal_h_thread_cancel ( void * thread_handle)
static

Terminate and delete a thread, freeing its table row.

Terminates before deleting, which is what ThreadX requires of a running thread, and frees the row last so a concurrent lookup never sees a half-deleted object.

Parameters
[in]thread_handleHandle from _h_thread_create.
Returns
RET_OK on success, a negative RET_* code otherwise.
Return values
RET_OKThe thread is gone and its row is free.
RET_INVALIDThe handle was not usable.
RET_FAILThreadX refused the terminate or the delete.
Precondition
The substrate is initialised.
The thread is not the caller itself.
Postcondition
The table row is free for reuse.
The stack region is available to a later create.
Note
Not thread-safe against a concurrent create. Validation is by return code because the vtable signature has no error channel.
Since
0.1.0

Definition at line 473 of file ra8_esp_hosted_rtos.c.

References k_ra8_esp_hosted_max_threads, priv_ra8_esp_hosted_rtos_slot_index(), RA8_INTERNAL, RET_FAIL, RET_INVALID, RET_OK, s_rtos, TX_SUCCESS, tx_thread_delete, and tx_thread_terminate.

Referenced by priv_ra8_esp_hosted_rtos_bind(), and priv_ra8_esp_hosted_rtos_deinit().

◆ internal_h_thread_create()

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 )
static

Start a thread from the fixed thread table.

Copies the name into port-owned storage, claims a table row, and starts the thread on its pre-reserved stack. A stack request larger than one reserved region is refused rather than silently shrunk.

Parameters
[in]tnameDiagnostic name; may be null.
[in]tprioThreadX priority (0 is highest).
[in]tstack_sizeRequested stack size in bytes.
[in]start_routineThread body.
[in]sr_argArgument handed to the body.
Returns
Opaque thread handle, or null on failure.
Return values
nullptrThe substrate is down, an argument is out of contract, or the thread table is full.
non-nullA handle usable with _h_thread_cancel.
Precondition
The substrate is initialised.
tstack_size is between TX_MINIMUM_STACK and the reserved size.
Postcondition
On success one table row is occupied and the thread is running.
On failure no row stays claimed.
Note
Not thread-safe against a concurrent create.
Since
0.1.0

Definition at line 416 of file ra8_esp_hosted_rtos.c.

References ra8_esp_hosted_thread_slot_t::arg, ra8_esp_hosted_thread_slot_t::cb, ra8_esp_hosted_thread_slot_t::entry, internal_copy_name(), internal_thread_entry(), k_ra8_esp_hosted_max_threads, k_ra8_esp_hosted_thread_stack_bytes, ra8_esp_hosted_thread_slot_t::name, priv_ra8_esp_hosted_rtos_slot_take(), RA8_INTERNAL, ra8_log_error, s_rtos, s_tag, s_thread_stacks, TX_AUTO_START, TX_MINIMUM_STACK, TX_NO_TIME_SLICE, TX_SUCCESS, and tx_thread_create.

Referenced by priv_ra8_esp_hosted_rtos_bind().

◆ internal_h_thread_yield()

void internal_h_thread_yield ( void )
static

Give up the remainder of the current time slice.

Maps straight onto tx_thread_relinquish; the vendored SPI half-duplex driver uses it to let a peer thread make progress.

Precondition
The caller is a thread, not an interrupt handler.
The substrate is initialised, or the call is a no-op.
Postcondition
Control may have passed to another ready thread.
No module state is modified.
Note
Thread context only; ThreadX rejects a relinquish from an ISR.
Since
0.1.0

Definition at line 506 of file ra8_esp_hosted_rtos.c.

References RA8_INTERNAL, and s_rtos.

Referenced by priv_ra8_esp_hosted_rtos_bind().

◆ internal_h_timer_start()

void * internal_h_timer_start ( const char * name,
int duration_ms,
int type,
ra8_esp_hosted_timer_cb_t timeout_handler,
void * arg )
static

Create and start a software timer.

H_TIMER_TYPE_ONESHOT becomes a ThreadX timer with a zero reschedule count, so the kernel stops it after one expiry; H_TIMER_TYPE_PERIODIC reschedules at the same interval, so the port never has to re-arm it by hand from the expiry context.

Parameters
[in]nameDiagnostic name; may be null.
[in]duration_msInterval in milliseconds; must be positive.
[in]typeH_TIMER_TYPE_ONESHOT or H_TIMER_TYPE_PERIODIC.
[in]timeout_handlerCallback run on each expiry.
[in]argArgument handed to the callback.
Returns
Opaque timer handle, or null on failure.
Return values
nullptrAn argument was out of contract or the timer table is full.
non-nullA handle usable with _h_timer_stop.
Precondition
The substrate is initialised.
timeout_handler does not block.
Postcondition
On success one table row is occupied and the timer is running.
On failure no row stays claimed.
Note
Not thread-safe against a concurrent create.
Since
0.1.0

Definition at line 648 of file ra8_esp_hosted_rtos.c.

References ra8_esp_hosted_timer_slot_t::arg, ra8_esp_hosted_timer_slot_t::cb, ra8_esp_hosted_timer_slot_t::cb_fn, H_TIMER_TYPE_ONESHOT, H_TIMER_TYPE_PERIODIC, internal_copy_name(), internal_timer_expiry(), k_ra8_esp_hosted_max_timers, ra8_esp_hosted_timer_slot_t::name, priv_ra8_esp_hosted_rtos_ms_to_ticks(), priv_ra8_esp_hosted_rtos_slot_take(), RA8_INTERNAL, ra8_log_error, s_rtos, s_tag, and TX_SUCCESS.

Referenced by priv_ra8_esp_hosted_rtos_bind().

◆ internal_h_timer_stop()

int internal_h_timer_stop ( void * timer_handle)
static

Stop a timer and free its table row.

Deactivates before deleting, which is what ThreadX wants of a running timer, and frees the row last.

Parameters
[in]timer_handleHandle from _h_timer_start.
Returns
RET_OK on success, a negative RET_* code otherwise.
Return values
RET_OKThe timer is stopped, deleted and its row is free.
RET_INVALIDThe handle was not usable.
RET_FAILThreadX refused the deactivate or the delete.
Precondition
The substrate is initialised.
The expiry callback is not currently running.
Postcondition
The table row is free for reuse.
The callback will not run again.
Note
Not thread-safe against a concurrent create. Validation is by return code because the vtable signature has no error channel.
Since
0.1.0

Definition at line 702 of file ra8_esp_hosted_rtos.c.

References k_ra8_esp_hosted_max_timers, priv_ra8_esp_hosted_rtos_slot_index(), RA8_INTERNAL, RET_FAIL, RET_INVALID, RET_OK, s_rtos, and TX_SUCCESS.

Referenced by priv_ra8_esp_hosted_rtos_bind(), and priv_ra8_esp_hosted_rtos_deinit().

◆ internal_h_usleep()

unsigned int internal_h_usleep ( unsigned int useconds)
static

Delay for a number of microseconds, honestly.

There is no microsecond timer on this part – ra8_time.h has a millisecond tick and nothing finer – so this splits the request: the whole milliseconds are slept on the RTOS, and only the sub-millisecond remainder is spun out on a bounded cycle-count loop sized from the cached core rate. Nothing is rounded to zero and nothing is faked. Accuracy on the spun part is roughly a factor of two, because cache state, branch prediction and any interrupt taken mid-spin all move it; the slept part is accurate to the kernel tick, so plus or minus one millisecond.

Parameters
[in]usecondsMicroseconds to delay.
Returns
The microseconds this call believes it delayed for.
Return values
0useconds was zero; nothing was done.
usecondsThe split delay was executed.
Precondition
The caller is a thread when useconds reaches a millisecond.
The cached core rate is set, or the spun part is skipped.
Postcondition
At least the whole-millisecond part has elapsed on the kernel clock.
No module state is modified.
Note
Thread context only once the request reaches one millisecond.
Since
0.1.0

Definition at line 577 of file ra8_esp_hosted_rtos.c.

References internal_h_msleep(), internal_spin_us(), k_ra8_esp_hosted_us_per_ms, and RA8_INTERNAL.

Referenced by priv_ra8_esp_hosted_rtos_bind().

◆ internal_spin_us()

void internal_spin_us ( uint32_t usec)
static

Burn a bounded number of core cycles standing in for a short delay.

The loop counter is volatile so the compiler cannot delete it. The loop itself runs at most k_ra8_esp_hosted_spin_iters_max passes and exits early once the requested, already-clamped iteration count is reached.

Parameters
[in]usecMicroseconds to approximate.
Precondition
The cached core rate is set, or the call becomes a no-op.
The caller accepts a factor-of-two accuracy bound.
Postcondition
At least the computed iteration count has executed.
No module state is modified.
Note
Safe from any context; it neither blocks nor allocates.
Since
0.1.0

Definition at line 348 of file ra8_esp_hosted_rtos.c.

References internal_spin_us(), k_ra8_esp_hosted_spin_iters_max, priv_ra8_esp_hosted_rtos_us_spin_iters(), and s_rtos.

Referenced by internal_h_usleep(), and internal_spin_us().

◆ internal_thread_entry()

void internal_thread_entry ( ULONG index)
static

ThreadX entry shim that calls the core-supplied thread body.

ThreadX hands a ULONG to a thread; the vendored core wants a void const*. The row index is passed instead of a pointer so a stale entry cannot dereference freed storage.

Parameters
[in]indexThread table row, passed as the ThreadX entry input.
Precondition
index names a live thread row.
The substrate is initialised.
Postcondition
The core-supplied body has been entered, or nothing happened.
No table state is modified.
Note
Runs on the created thread; not called from anywhere else.
Since
0.1.0

Definition at line 299 of file ra8_esp_hosted_rtos.c.

References k_ra8_esp_hosted_max_threads, RA8_INTERNAL, and s_rtos.

◆ internal_timer_expiry()

void internal_timer_expiry ( ULONG index)
static

ThreadX expiry shim that calls the core-supplied timer callback.

Same indirection as the thread entry shim, and for the same reason: ThreadX carries a ULONG, the core wants a void*.

Parameters
[in]indexTimer table row, passed as the ThreadX expiry input.
Precondition
index names a live timer row.
The substrate is initialised.
Postcondition
The core-supplied callback has been entered, or nothing happened.
No table state is modified.
Note
Runs in the ThreadX timer context; the callback must not block.
Since
0.1.0

Definition at line 322 of file ra8_esp_hosted_rtos.c.

References k_ra8_esp_hosted_max_timers, RA8_INTERNAL, and s_rtos.

◆ priv_ra8_esp_hosted_rtos_bind()

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.

Writes the ten rows this translation unit implements. It does not call the sibling binders: each unit binds only the rows whose implementations it can see, so a row can never be assigned from a unit that cannot name the function behind it. priv_ra8_esp_hosted_osi_bind_all calls all three. Every slot outside this group – memory, queue, mutex, semaphore, GPIO, bus, logging, transport – is left exactly as the caller had it.

Parameters
[out]outVtable to populate. Must be non-null.
Returns
ra8_err_t Error code.
Return values
k_ra8_okThe ten thread, sleep, timer and clock slots are set.
k_ra8_err_null_ptrout was null.
Precondition
out points at storage that outlives the vendored core.
priv_ra8_esp_hosted_rtos_init has run, or the slots will report failures.
Postcondition
Every slot in this group is non-null.
No slot outside this group is written.
Note
Not thread-safe; call once during bring-up.
Warning
Binding without initialising first produces a vtable whose allocators fail every call rather than one that faults.
Example:
hosted_osi_funcs_t g_hosted_osi_funcs
The OS-abstraction vtable the vendored core calls through.
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.
See also
priv_ra8_esp_hosted_rtos_bind_pool
priv_ra8_esp_hosted_rtos_bind_sync
Since
0.1.0

Definition at line 814 of file ra8_esp_hosted_rtos.c.

References internal_h_blocking_delay(), internal_h_get_time_ms(), internal_h_msleep(), internal_h_sleep(), internal_h_thread_cancel(), internal_h_thread_create(), internal_h_thread_yield(), internal_h_timer_start(), internal_h_timer_stop(), internal_h_usleep(), k_ra8_ok, RA8_CHECK_NULL_PTR, and s_tag.

Referenced by priv_ra8_esp_hosted_osi_bind_all().

◆ priv_ra8_esp_hosted_rtos_deinit()

ra8_err_t priv_ra8_esp_hosted_rtos_deinit ( void )
nodiscard

Tear the RTOS substrate down, deleting every outstanding object.

Walks each object table and deletes whatever is still in use – timers are deactivated then deleted, threads terminated then deleted, queues, mutexes and semaphores deleted – then deletes both byte pools and clears the module state. Objects the vendored core still holds handles to are deleted regardless, so the core must be stopped first.

Returns
ra8_err_t Error code.
Return values
k_ra8_okEverything was released.
k_ra8_err_not_initializedThe substrate was not up.
k_ra8_err_rtos_errorThreadX refused to delete an object or a pool.
Precondition
The vendored transport has been stopped.
No interrupt handler is currently posting to a port semaphore.
Postcondition
Every object table is empty.
The substrate reports not ready and both pools are gone.
Note
Not thread-safe; call from the same context as the init.
Warning
Tearing down while a thread is blocked on a port object leaves that thread waiting on a deleted control block.
Example:
ra8_err_t priv_ra8_esp_hosted_rtos_deinit(void)
Tear the RTOS substrate down, deleting every outstanding object.
See also
priv_ra8_esp_hosted_rtos_init
Since
0.1.0
NASA Power of 10 Compliance:
  • Rule 2: every table walk is bounded by its compile-time table size.
  • Rule 5: two preconditions and two postconditions are checked.

Definition at line 787 of file ra8_esp_hosted_rtos.c.

References internal_h_thread_cancel(), internal_h_timer_stop(), k_ra8_err_not_initialized, k_ra8_err_rtos_error, k_ra8_esp_hosted_max_threads, k_ra8_esp_hosted_max_timers, k_ra8_ok, memset(), priv_ra8_esp_hosted_rtos_pool_deinit(), priv_ra8_esp_hosted_rtos_sync_deinit(), ra8_log_error, RET_OK, s_rtos, and s_tag.

Referenced by internal_unwind().

◆ priv_ra8_esp_hosted_rtos_init()

ra8_err_t priv_ra8_esp_hosted_rtos_init ( void )
nodiscard

Bring the RTOS substrate up: byte pools and empty object tables.

Creates the two TX_BYTE_POOL instances – the transport buffer pool sized by k_ra8_esp_hosted_pool_bytes and the queue-storage pool sized by k_ra8_esp_hosted_queue_pool_bytes – over static arrays, clears every object table and caches the CPU rate used to size the sub-millisecond spin in _h_usleep. This is the only allocation the RTOS slice ever performs and it happens exactly here, during initialisation.

A second call is an error, not a no-op: a silent success would hand the caller pools whose contents the first caller still owns, and would hide a double bring-up that is always a bug in the calling sequence.

Returns
ra8_err_t Error code.
Return values
k_ra8_okThe substrate is ready; the vtable may be bound.
k_ra8_err_invalid_stateThe substrate was already initialised.
k_ra8_err_rtos_errorThreadX refused to create a byte pool.
Precondition
The ThreadX kernel is running, or this runs from tx_application_define.
No vendored esp-hosted entry point has been called yet.
Postcondition
On success the substrate reports ready and every object table is empty.
On failure no pool is left half-created and the substrate reports not ready.
Note
Not thread-safe; call once from a single-threaded bring-up path.
Warning
The CPU-rate query is best-effort; a failure leaves the documented fallback rate in place and only affects _h_usleep accuracy.
Example:
if (priv_ra8_esp_hosted_rtos_init() != k_ra8_ok) { report(); }
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
ra8_err_t priv_ra8_esp_hosted_rtos_init(void)
Bring the RTOS substrate up: byte pools and empty object tables.
See also
priv_ra8_esp_hosted_rtos_deinit
priv_ra8_esp_hosted_rtos_bind
Since
0.1.0
NASA Power of 10 Compliance:
  • Rule 3: the only allocation is this init-time pool carve.
  • Rule 5: two preconditions and two postconditions are checked.

Definition at line 767 of file ra8_esp_hosted_rtos.c.

References k_ra8_clock_id_cpuclk0, k_ra8_err_invalid_state, k_ra8_esp_hosted_default_cpu_hz, k_ra8_ok, memset(), priv_ra8_esp_hosted_rtos_pool_init(), priv_ra8_esp_hosted_rtos_sync_init(), ra8_cgc_get_clock_hz(), ra8_log_error, ra8_log_warn, RA8_RETURN_ON_ERROR, s_rtos, s_tag, and tx_time_get.

Referenced by internal_bring_up().

◆ priv_ra8_esp_hosted_rtos_is_ready()

bool priv_ra8_esp_hosted_rtos_is_ready ( void )
nodiscard

Report whether the RTOS substrate is currently initialised.

Reads the single module-state flag so callers can decide whether a teardown is needed without provoking an error return, and so host tests can assert the state machine without reaching into the module.

Returns
Whether the substrate is up.
Return values
truepriv_ra8_esp_hosted_rtos_init completed and no teardown has run.
falseThe substrate has never been up, failed, or was torn down.
Precondition
None; safe to call at any time, including before any init.
The caller tolerates a value a concurrent teardown may stale.
Postcondition
No module state is modified.
The value reflects the flag at the moment of the read.
Note
Safe from interrupt context; a single aligned load.
Example:
bool priv_ra8_esp_hosted_rtos_is_ready(void)
Report whether the RTOS substrate is currently initialised.
See also
priv_ra8_esp_hosted_rtos_init
Since
0.1.0

Definition at line 762 of file ra8_esp_hosted_rtos.c.

References s_rtos.

◆ priv_ra8_esp_hosted_rtos_ms_to_ticks()

uint32_t priv_ra8_esp_hosted_rtos_ms_to_ticks ( int timeout_ms)
nodiscard

Convert an esp-hosted millisecond timeout to a ThreadX wait option.

The vendored core expresses every timeout in milliseconds through an int parameter, and ThreadX takes a tick count. Two values are special: zero means "do not block" and HOSTED_BLOCK_MAX (all ones) means "block until satisfied". HOSTED_BLOCK_MAX reaches the vtable as -1 after its conversion to int, so the rule this function implements is "any negative value blocks forever" – deliberately wider than "exactly -1", so a sign-extension bug elsewhere can never turn an intended block into a zero-tick busy loop. The kernel runs at 1 kHz (TX_TIMER_TICKS_PER_SECOND is 1000), so the positive case is the identity; it is still spelled out here rather than inlined at 30 call sites so that changing the tick rate is a one-line change and so the mapping is directly testable.

Parameters
[in]timeout_msTimeout in milliseconds: 0 = do not block, negative = block forever, positive = that many milliseconds.
Returns
ThreadX wait option in ticks.
Return values
0timeout_ms was zero: TX_NO_WAIT.
0xFFFFFFFFtimeout_ms was negative: TX_WAIT_FOREVER.
1..0x7FFFFFFFThe millisecond count, unchanged.
Precondition
The kernel tick is 1 kHz.
The caller passes the value the vendored core supplied, unmodified.
Postcondition
No state is modified.
The result is never TX_WAIT_FOREVER for a non-negative input.
Note
Pure function; safe from any context including interrupts.
Warning
Do not pre-clamp the argument at the call site; the negative case is load-bearing.
MC/DC:
Three-way decision with no compound condition: timeout_ms < 0 is tested first, then timeout_ms == 0. Independence is trivial because each decision has a single condition; the vectors that cover every outcome are -1 (and (int)HOSTED_BLOCK_MAX), 0, and any positive value.
Example:
uint32_t priv_ra8_esp_hosted_rtos_ms_to_ticks(int timeout_ms)
Convert an esp-hosted millisecond timeout to a ThreadX wait option.
unsigned long ULONG
ThreadX-compatible unsigned long (host stub).
See also
priv_ra8_esp_hosted_rtos_queue_words
Since
0.1.0

Definition at line 364 of file ra8_esp_hosted_rtos.c.

References k_ra8_esp_hosted_ms_per_tick, and TX_WAIT_FOREVER.

Referenced by internal_h_dequeue_item(), internal_h_get_semaphore(), internal_h_lock_mutex(), internal_h_queue_item(), and internal_h_timer_start().

◆ priv_ra8_esp_hosted_rtos_slot_index()

uint32_t priv_ra8_esp_hosted_rtos_slot_index ( const void * handle,
const void * base,
size_t stride,
uint32_t count,
const bool * used )
nodiscard

Resolve an opaque handle to its row index in a fixed table.

Every handle the port hands the vendored core is the address of a table row. Rather than trusting that address, this scans the table for pointer identity and checks the occupancy flag, so a foreign pointer or a handle retained past its destroy is rejected instead of followed. The tables hold at most eight rows, so the scan is cheaper than any bookkeeping that would replace it.

Parameters
[in]handleOpaque handle the core is holding.
[in]baseAddress of the table's first row.
[in]strideBytes between consecutive rows.
[in]countNumber of rows in the table.
[in]usedOccupancy flags for the same table.
Returns
Row index, or count when the handle is not live.
Return values
countThe handle is null, foreign, or names a freed row.
0..count-1The live row index.
Precondition
base and used describe the same table.
stride is sizeof one row.
Postcondition
No state is modified.
A returned index always has its occupancy flag set.
Note
Thread-safe for reads; the tables are mutated only during bring-up and teardown.
Warning
Passing a stride that is not the real row size makes every lookup miss rather than fault.
MC/DC:
Decision (handle == nullptr) || (base == nullptr) || (used == nullptr) (3 conditions) plus (row == handle) && used[i] (2 conditions). Four vectors cover the first – all non-null, then each argument null in turn – and three cover the second: a matching live row, a non-matching row, and a matching row whose flag is clear.
Example:
h, s_rows, sizeof(s_rows[0]), k_max, s_used);
static ra8_esp_hosted_gpio_edge_row_t s_rows[k_ra8_esp_hosted_gpio_row_max]
Every pin currently under software edge detection.
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.
See also
priv_ra8_esp_hosted_rtos_slot_take
Since
0.1.0

Definition at line 230 of file ra8_esp_hosted_rtos.c.

Referenced by internal_h_destroy_mutex(), internal_h_destroy_semaphore(), internal_h_get_semaphore(), internal_h_lock_mutex(), internal_h_post_semaphore(), internal_h_post_semaphore_from_isr(), internal_h_thread_cancel(), internal_h_timer_stop(), and internal_h_unlock_mutex().

◆ priv_ra8_esp_hosted_rtos_slot_take()

uint32_t priv_ra8_esp_hosted_rtos_slot_take ( bool * used,
uint32_t count )
nodiscard

Claim the first free row of an occupancy bitmap.

The one place a table row is taken, shared by every object kind so none of them can grow its table by accident. Returning count rather than a sentinel index keeps the caller's bound check and the failure check the same comparison.

Parameters
[in,out]usedOccupancy flags; must cover count entries.
[in]countNumber of rows in the table.
Returns
Index of the claimed row, or count when the table is full.
Return values
countEvery row was occupied, or used was null.
0..count-1The claimed row, now marked in use.
Precondition
used covers count entries.
The caller releases the row on any later failure.
Postcondition
Exactly one flag changes from false to true on success.
No flag changes when the table is full.
Note
Not thread-safe against a concurrent claim; creates happen during single-threaded bring-up.
Warning
A claimed row is in use even if the caller then fails to create the ThreadX object; release it explicitly on that path.
MC/DC:
Two single-condition decisions, used == nullptr and !used[i]; no compound condition, so a null table, a table with a free row, and a full table cover every outcome.
Example:
const uint32_t idx = priv_ra8_esp_hosted_rtos_slot_take(s_used, k_max);
uint32_t priv_ra8_esp_hosted_rtos_slot_take(bool *used, uint32_t count)
Claim the first free row of an occupancy bitmap.
See also
priv_ra8_esp_hosted_rtos_slot_index
Since
0.1.0

Definition at line 216 of file ra8_esp_hosted_rtos.c.

Referenced by internal_h_create_mutex(), internal_h_create_semaphore(), internal_h_thread_create(), and internal_h_timer_start().

◆ priv_ra8_esp_hosted_rtos_us_spin_iters()

uint32_t priv_ra8_esp_hosted_rtos_us_spin_iters ( uint32_t cpu_hz,
uint32_t usec )
nodiscard

Size the busy-wait loop that stands in for a microsecond delay.

There is no microsecond timer on this target: ra8_time.h offers a millisecond tick and a millisecond busy delay, and nothing finer. Rather than fake a microsecond sleep or round every sub-millisecond request to zero, _h_usleep spins, and this function decides for how long. The estimate is cpu_hz / 1e6 cycles per microsecond divided by k_ra8_esp_hosted_spin_cycles_per_iter, the measured cost of one iteration of the volatile-counter loop on the Cortex-M85.

The result is clamped to k_ra8_esp_hosted_spin_iters_max so the loop is statically bounded (NASA Power of 10 Rule 2) whatever the caller asks for. Accuracy is roughly a factor of two: cache state, the branch predictor and any interrupt taken mid-spin all move the real duration, and none of them is modelled. That is stated rather than hidden – a caller needing better than that needs a hardware timer, not a better guess.

Parameters
[in]cpu_hzCore clock in hertz; zero means "rate unknown".
[in]usecMicroseconds to spin for.
Returns
Loop iterations to execute.
Return values
0cpu_hz or usec was zero: do not spin at all.
1..k_ra8_esp_hosted_spin_iters_maxThe clamped iteration count.
Precondition
cpu_hz is the live core rate, or zero.
The caller runs the loop with a volatile counter so it is not deleted.
Postcondition
No state is modified.
The result never exceeds k_ra8_esp_hosted_spin_iters_max.
Note
Pure function; safe from any context.
Warning
The duration is an estimate, not a guarantee; do not use it for a protocol timing requirement.
MC/DC:
Decision (cpu_hz == 0U) || (usec == 0U) (2 conditions). Vector 1: cpu_hz=1e9, usec=10 -> false/false. Vector 2: cpu_hz=0, usec=10 -> true/(short-circuit). Vector 3: cpu_hz=1e9, usec=0 -> false/true. Vectors 1+2 prove the rate condition's independent influence; 1+3 prove the same for the duration. N+1 = 3 vectors for N=2: minimal MC/DC.
Example:
const uint32_t iters = priv_ra8_esp_hosted_rtos_us_spin_iters(cpu_hz, 250U);
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.
See also
priv_ra8_esp_hosted_rtos_ms_to_ticks
Since
0.1.0

Definition at line 375 of file ra8_esp_hosted_rtos.c.

References k_ra8_esp_hosted_hz_per_mhz, k_ra8_esp_hosted_spin_cycles_per_iter, and k_ra8_esp_hosted_spin_iters_max.

Referenced by internal_spin_us().

Variable Documentation

◆ s_rtos

Singleton state of the thread, timer and clock half.

Zero-initialised at link time; brought up by priv_ra8_esp_hosted_rtos_init.

Note
Static; do not access outside this TU.
Warning
Direct modification bypasses every ThreadX consistency check.
Since
0.1.0

Definition at line 198 of file ra8_esp_hosted_rtos.c.

Referenced by internal_h_get_time_ms(), internal_h_msleep(), internal_h_thread_cancel(), internal_h_thread_create(), internal_h_thread_yield(), internal_h_timer_start(), internal_h_timer_stop(), internal_spin_us(), internal_thread_entry(), internal_timer_expiry(), priv_ra8_esp_hosted_rtos_deinit(), priv_ra8_esp_hosted_rtos_init(), and priv_ra8_esp_hosted_rtos_is_ready().

◆ s_tag

const char* s_tag = "ESPH_RTOS"
static

Log tag for the synchronisation half of the port.

Shared by every diagnostic this translation unit emits.

Note
Static; do not access outside this TU.
Warning
Changing it changes log-scraping expectations on the bench.
Since
0.1.0

Definition at line 49 of file ra8_esp_hosted_rtos.c.

◆ s_thread_stacks

Fixed stack storage, one region per thread table row.

Sized by k_ra8_esp_hosted_thread_stack_bytes, which covers the deepest stack the vendored core asks for.

Note
Static; owned exclusively by the thread table.
Warning
A thread whose requested stack exceeds one region is refused, not silently given a smaller one.
Since
0.1.0

Definition at line 210 of file ra8_esp_hosted_rtos.c.

Referenced by internal_h_thread_create().