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

ThreadX-aware watchdog supervisor implementation. More...

#include "ra8_wdt_supervisor.h"
#include <stdint.h>
#include <string.h>
#include "ra8_attributes.h"
#include "ra8_err.h"
#include "ra8_wdt.h"
#include "tx_api.h"
Include dependency graph for ra8_wdt_supervisor.c:

Go to the source code of this file.

Data Structures

struct  ra8_wdt_sup_slot_t
 One row of the supervisor registry. More...
struct  ra8_wdt_sup_state_t
 Module state – entirely static. More...

Enumerations

enum  ra8_wdt_sup_internal_t : uint32_t {
  k_ra8_wdt_sup_min_stack = 512U ,
  k_ra8_wdt_sup_max_priority = 31U ,
  k_ra8_wdt_sup_default_tick_ms = 1U ,
  k_ra8_wdt_sup_slot_free = 0U ,
  k_ra8_wdt_sup_slot_used = 1U ,
  k_ra8_wdt_sup_mutex_id = 0x57445353U ,
  k_ra8_wdt_sup_thread_id = 0x57445354U
}
 Internal numeric constants used by the implementation. More...

Functions

static uint32_t internal_default_now (void)
 Default monotonic-time hook – scales tx_time_get to ms.
static void internal_default_refresh (void)
 Default WDT-refresh hook.
static ra8_err_t internal_validate_cfg (const ra8_wdt_sup_cfg_t *cfg)
 Validate the public configuration block.
static bool internal_is_overdue (uint32_t now, uint32_t last_checkin, uint32_t deadline)
 Compute whether now - last_checkin exceeds deadline.
static void internal_thread_entry (ULONG arg)
 The supervisor thread's entry point.
ra8_err_t ra8_wdt_supervisor_init (const ra8_wdt_sup_cfg_t *cfg)
 Initialise the supervisor registry and runtime hooks.
ra8_err_t ra8_wdt_supervisor_deinit (void)
 Reset the supervisor to its uninitialized state (test helper).
static void internal_fill_slot (uint8_t idx, const char *name, uint32_t deadline_ms)
 Initialise an unused slot with the caller's parameters.
ra8_err_t ra8_wdt_supervisor_register_thread (const char *name, uint32_t deadline_ms, uint8_t *out_handle)
 Register a worker thread with the supervisor.
ra8_err_t ra8_wdt_supervisor_checkin (uint8_t handle)
 Record a thread check-in, resetting its deadline window.
ra8_err_t ra8_wdt_supervisor_start (void)
 Spawn the supervisor thread.
ra8_err_t ra8_wdt_supervisor_tick (bool *out_did_refresh)
 Run one supervisor tick synchronously (test / introspection hook).
ra8_err_t ra8_wdt_supervisor_set_now_hook (ra8_wdt_sup_now_fn_t now)
 Override the monotonic-time hook (test injection point).
ra8_err_t ra8_wdt_supervisor_set_refresh_hook (ra8_wdt_sup_refresh_fn_t refresh)
 Override the WDT-refresh hook (test injection point).
uint8_t ra8_wdt_supervisor_thread_count (void)
 Read the number of currently-registered worker threads.

Variables

static ra8_wdt_sup_state_t s_state
 Singleton module state.

Detailed Description

ThreadX-aware watchdog supervisor implementation.

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

Static-allocation registry with a TX_MUTEX guard and a single TX_THREAD that wakes every refresh_period_ms. See ra8_wdt_supervisor.h for the design rationale.

Definition in file ra8_wdt_supervisor.c.

Enumeration Type Documentation

◆ ra8_wdt_sup_internal_t

enum ra8_wdt_sup_internal_t : uint32_t

Internal numeric constants used by the implementation.

Enumerator
k_ra8_wdt_sup_min_stack 

Minimum acceptable stack size.

k_ra8_wdt_sup_max_priority 

Highest legal ThreadX priority.

k_ra8_wdt_sup_default_tick_ms 

Default tick: 1 kHz kernel.

k_ra8_wdt_sup_slot_free 

Slot tag: empty.

k_ra8_wdt_sup_slot_used 

Slot tag: registered.

k_ra8_wdt_sup_mutex_id 

'WDSS' marker for the mutex.

k_ra8_wdt_sup_thread_id 

'WDST' marker for the thread.

Definition at line 35 of file ra8_wdt_supervisor.c.

Function Documentation

◆ internal_default_now()

uint32_t internal_default_now ( void )
static

Default monotonic-time hook – scales tx_time_get to ms.

Assumes a 1 kHz kernel tick (the most common ThreadX default for Cortex-M cores). Override via ra8_wdt_supervisor_set_now_hook if the kernel runs at a different rate.

Returns
Current monotonic time in milliseconds.
Precondition
None.
Postcondition
No state is mutated.
Return values
k_ra8_okOperation succeeded.
Precondition
Module state is consistent.
Postcondition
Caller-visible state matches the documented contract.
Note
Not thread-safe unless documented otherwise.
Since
0.1.0

Definition at line 98 of file ra8_wdt_supervisor.c.

References k_ra8_wdt_sup_default_tick_ms, RA8_INTERNAL, and tx_time_get.

Referenced by ra8_wdt_supervisor_init(), and ra8_wdt_supervisor_set_now_hook().

◆ internal_default_refresh()

void internal_default_refresh ( void )
static

Default WDT-refresh hook.

Wraps ra8_wdt_refresh_deferred so the test build can swap it out without pulling the WDT register layout into the unit test image.

Precondition
WDT block has been armed.
Postcondition
WDTRR has been written with the unlock sequence.
Precondition
Module state is consistent.
Postcondition
Caller-visible state matches the documented contract.
Note
Not thread-safe unless documented otherwise.
Since
0.1.0

Definition at line 118 of file ra8_wdt_supervisor.c.

References RA8_INTERNAL, and ra8_wdt_refresh_deferred().

Referenced by ra8_wdt_supervisor_init(), and ra8_wdt_supervisor_set_refresh_hook().

◆ internal_fill_slot()

void internal_fill_slot ( uint8_t idx,
const char * name,
uint32_t deadline_ms )
static

Initialise an unused slot with the caller's parameters.

Helper extracted from ra8_wdt_supervisor_register_thread so the outer function fits the project size threshold (NASA Power-of-10 Rule 4 / clang-tidy readability-function-size). The caller MUST already hold s_state.mutex.

Parameters
[in]idxIndex of a free slot in s_state.slots.
[in]nameCaller-supplied thread name (NUL-terminated).
[in]deadline_msPer-thread deadline in milliseconds.
Precondition
Caller holds s_state.mutex.
idx < k_ra8_wdt_sup_max_threads.
Postcondition
s_state.slots[idx] is in the slot_used state.
Caller-visible state matches the documented contract.
Note
Not thread-safe unless documented otherwise.
Since
0.1.0

Definition at line 288 of file ra8_wdt_supervisor.c.

References k_ra8_wdt_sup_name_max, k_ra8_wdt_sup_slot_used, memset(), RA8_INTERNAL, and s_state.

Referenced by ra8_wdt_supervisor_register_thread().

◆ internal_is_overdue()

bool internal_is_overdue ( uint32_t now,
uint32_t last_checkin,
uint32_t deadline )
static

Compute whether now - last_checkin exceeds deadline.

Uses unsigned subtraction so 32-bit wrap-around is handled implicitly as long as the gap is < 2^31 ms (~24.8 days), which is always the case for the supervisor's ms-scale ticks.

Parameters
[in]nowCurrent monotonic time (ms).
[in]last_checkinTime of slot's last check-in (ms).
[in]deadlineSlot's deadline budget (ms).
Returns
true if the slot is overdue, false if alive.
Precondition
None.
Postcondition
No state is mutated.
Return values
k_ra8_okOperation succeeded.
Precondition
Module state is consistent.
Postcondition
Caller-visible state matches the documented contract.
Note
Not thread-safe unless documented otherwise.
Since
0.1.0

Definition at line 184 of file ra8_wdt_supervisor.c.

References RA8_INTERNAL.

Referenced by ra8_wdt_supervisor_tick().

◆ internal_thread_entry()

void internal_thread_entry ( ULONG arg)
static

The supervisor thread's entry point.

Loops forever, sleeping refresh_period_ms between ticks. Each iteration calls ra8_wdt_supervisor_tick to evaluate the registry and conditionally refresh the WDT.

Parameters
[in]argUnused (ThreadX entry-fn signature requires a ULONG).
Precondition
s_state.initialized is true.
Postcondition
Loops forever (NASA Rule 2: bounded body, unbounded outer).
Precondition
Module state is consistent.
Postcondition
Caller-visible state matches the documented contract.
Note
Not thread-safe unless documented otherwise.
Since
0.1.0

Definition at line 209 of file ra8_wdt_supervisor.c.

References RA8_INTERNAL, ra8_wdt_supervisor_tick(), s_state, and tx_thread_sleep.

Referenced by internal_h_thread_create(), ra8_wdt_supervisor_start(), and tx_application_define().

◆ internal_validate_cfg()

ra8_err_t internal_validate_cfg ( const ra8_wdt_sup_cfg_t * cfg)
static

Validate the public configuration block.

Parameters
[in]cfgCaller-supplied configuration.
Returns
k_ra8_ok if the block is acceptable, else a specific error.
Return values
k_ra8_err_null_ptrcfg or cfg->stack was null.
k_ra8_err_invalid_argStack too small / period zero / priority bad.
Precondition
None.
Postcondition
No state is mutated.

See implementation.

Precondition
Module state is consistent.
Postcondition
Caller-visible state matches the documented contract.
Note
Not thread-safe unless documented otherwise.
Since
0.1.0

Definition at line 141 of file ra8_wdt_supervisor.c.

References k_ra8_err_invalid_arg, k_ra8_err_null_ptr, k_ra8_ok, k_ra8_wdt_sup_max_priority, k_ra8_wdt_sup_min_stack, ra8_wdt_sup_cfg_t::priority, RA8_INTERNAL, ra8_wdt_sup_cfg_t::refresh_period_ms, ra8_wdt_sup_cfg_t::stack, and ra8_wdt_sup_cfg_t::stack_size_bytes.

Referenced by ra8_wdt_supervisor_init().

◆ ra8_wdt_supervisor_checkin()

ra8_err_t ra8_wdt_supervisor_checkin ( uint8_t handle)
nodiscard

Record a thread check-in, resetting its deadline window.

Called by registered worker threads at known points in their loops (typically once per iteration, before any blocking call). Updates the slot's last_checkin_ms to the current monotonic time so the supervisor sees the thread as alive on its next tick.

Parameters
[in]handleHandle returned by ra8_wdt_supervisor_register_thread.
Returns
ra8_err_t
Return values
k_ra8_okCheck-in recorded.
k_ra8_err_invalid_arghandle is out of range.
k_ra8_err_not_foundhandle refers to a free slot.
k_ra8_err_not_initializedra8_wdt_supervisor_init not called.
Precondition
handle < k_ra8_wdt_sup_max_threads.
Slot was previously registered.
Postcondition
Slot's last_checkin_ms equals current monotonic time.
Note
Thread-safe via TX_MUTEX.
Since
0.1.0

Definition at line 342 of file ra8_wdt_supervisor.c.

References k_ra8_err_invalid_arg, k_ra8_err_not_found, k_ra8_err_not_initialized, k_ra8_err_rtos_error, k_ra8_ok, k_ra8_wdt_sup_max_threads, k_ra8_wdt_sup_slot_used, s_state, tx_mutex_get, tx_mutex_put, TX_SUCCESS, and TX_WAIT_FOREVER.

Referenced by internal_sys_thread_entry(), internal_ui_thread_entry(), worker_a_entry(), and worker_b_entry().

◆ ra8_wdt_supervisor_deinit()

ra8_err_t ra8_wdt_supervisor_deinit ( void )
nodiscard

Reset the supervisor to its uninitialized state (test helper).

Tears down the registry, deletes the mutex, and clears module state. Intended for unit tests that need a fresh slate between cases. Safe to call when never initialized.

Returns
ra8_err_t
Return values
k_ra8_okAlways succeeds.
Precondition
None.
Postcondition
Module state is identical to its pre-init posture.
Note
Not thread-safe.
Since
0.1.0

Definition at line 254 of file ra8_wdt_supervisor.c.

References k_ra8_ok, memset(), s_state, tx_mutex_delete, tx_thread_delete, and tx_thread_terminate.

◆ ra8_wdt_supervisor_init()

ra8_err_t ra8_wdt_supervisor_init ( const ra8_wdt_sup_cfg_t * cfg)
nodiscard

Initialise the supervisor registry and runtime hooks.

Validates cfg, zeroes the static registry, creates the registry mutex, and stores cfg for later use by ..._start. Does not spawn the supervisor thread yet – callers register their workers first, then call ra8_wdt_supervisor_start.

Algorithm:

  1. Reject null cfg and out-of-range fields.
  2. Clear every registry slot to free.
  3. Create the TX_MUTEX guarding the registry.
  4. Stash cfg in module state.
Parameters
[in]cfgPointer to a populated configuration block.
Returns
ra8_err_t
Return values
k_ra8_okSupervisor initialized, ready for register / start.
k_ra8_err_null_ptrcfg was null or cfg->stack was null.
k_ra8_err_invalid_argStack too small / period zero / bad priority.
k_ra8_err_busyra8_wdt_supervisor_init was already called.
Precondition
cfg is non-null and points to a populated block.
Caller is in single-threaded init context.
Postcondition
Registry is empty (every slot free).
Internal mutex is created and unlocked.
Note
Not thread-safe; call once during boot.
Example:
static uint8_t sup_stack[1024];
const ra8_wdt_sup_cfg_t cfg = {
.stack = sup_stack, .stack_size_bytes = sizeof sup_stack,
.priority = 4, .refresh_period_ms = 50,
};
ra8_err_t ra8_wdt_supervisor_init(const ra8_wdt_sup_cfg_t *cfg)
Initialise the supervisor registry and runtime hooks.
One-shot supervisor configuration block.
See also
ra8_wdt_supervisor_register_thread
ra8_wdt_supervisor_start
Since
0.1.0

Definition at line 229 of file ra8_wdt_supervisor.c.

References internal_default_now(), internal_default_refresh(), internal_validate_cfg(), k_ra8_err_busy, k_ra8_err_rtos_error, k_ra8_ok, memset(), s_state, tx_mutex_create, TX_NO_INHERIT, and TX_SUCCESS.

Referenced by internal_wdt_setup(), and wdt_sup_demo_bring_up().

◆ ra8_wdt_supervisor_register_thread()

ra8_err_t ra8_wdt_supervisor_register_thread ( const char * name,
uint32_t deadline_ms,
uint8_t * out_handle )
nodiscard

Register a worker thread with the supervisor.

Allocates one of the k_ra8_wdt_sup_max_threads static slots, copies up to k_ra8_wdt_sup_name_max - 1 bytes of name for diagnostics, stamps the deadline budget, and primes last_checkin_ms to "now" so the first tick does not count the thread as overdue before it has had a chance to run.

Parameters
[in]nameShort diagnostic name (must be NUL-terminated).
[in]deadline_msMaximum allowed gap between consecutive check-ins, in milliseconds. Must be > 0.
[out]out_handleReceives the registered handle on success.
Returns
ra8_err_t
Return values
k_ra8_okSlot allocated, *out_handle populated.
k_ra8_err_null_ptrname or out_handle was null.
k_ra8_err_invalid_argdeadline_ms was zero.
k_ra8_err_no_memAll slots are taken.
k_ra8_err_not_initializedra8_wdt_supervisor_init not called.
Precondition
name is NUL-terminated and non-null.
deadline_ms > 0.
Postcondition
On success, registry holds one more populated slot.
On failure, *out_handle is set to k_ra8_wdt_sup_handle_invalid.
Note
Thread-safe via TX_MUTEX.
See also
ra8_wdt_supervisor_checkin
Since
0.1.0

Definition at line 306 of file ra8_wdt_supervisor.c.

References internal_fill_slot(), k_ra8_err_invalid_arg, k_ra8_err_no_mem, k_ra8_err_not_initialized, k_ra8_err_null_ptr, k_ra8_err_rtos_error, k_ra8_ok, k_ra8_wdt_sup_handle_invalid, k_ra8_wdt_sup_max_threads, k_ra8_wdt_sup_slot_free, s_state, tx_mutex_get, tx_mutex_put, TX_SUCCESS, and TX_WAIT_FOREVER.

Referenced by internal_wdt_setup(), and wdt_sup_demo_bring_up().

◆ ra8_wdt_supervisor_set_now_hook()

ra8_err_t ra8_wdt_supervisor_set_now_hook ( ra8_wdt_sup_now_fn_t now)
nodiscard

Override the monotonic-time hook (test injection point).

Pass nullptr to restore the default ThreadX-tick reader. NASA Rule 9 deviation: function pointer used for Dependency Inversion.

Parameters
[in]nowReplacement hook or nullptr.
Returns
Always k_ra8_ok.
Precondition
Caller is in single-writer init context.
Postcondition
Subsequent supervisor logic uses now.
Since
0.1.0

Definition at line 444 of file ra8_wdt_supervisor.c.

References internal_default_now(), k_ra8_ok, and s_state.

◆ ra8_wdt_supervisor_set_refresh_hook()

ra8_err_t ra8_wdt_supervisor_set_refresh_hook ( ra8_wdt_sup_refresh_fn_t refresh)
nodiscard

Override the WDT-refresh hook (test injection point).

Pass nullptr to restore the default that calls ra8_wdt_refresh_deferred. NASA Rule 9 deviation: function pointer used for Dependency Inversion.

Parameters
[in]refreshReplacement hook or nullptr.
Returns
Always k_ra8_ok.
Precondition
Caller is in single-writer init context.
Postcondition
Subsequent supervisor ticks call refresh in place of the WDT.
Since
0.1.0

Definition at line 450 of file ra8_wdt_supervisor.c.

References internal_default_refresh(), k_ra8_ok, and s_state.

Referenced by internal_wdt_setup().

◆ ra8_wdt_supervisor_start()

ra8_err_t ra8_wdt_supervisor_start ( void )
nodiscard

Spawn the supervisor thread.

Creates the supervisor's TX_THREAD with the stack region from cfg, then returns. The supervisor's loop:

  1. tx_thread_sleep(refresh_period_ms) worth of ticks.
  2. Snapshot every populated slot under the mutex.
  3. For each slot, evaluate now - last_checkin_ms <= deadline_ms.
  4. If every slot passes, call the refresh hook -> ra8_wdt_refresh_deferred. Otherwise skip the refresh and let the WDT do its job.
Returns
ra8_err_t
Return values
k_ra8_okSupervisor thread created.
k_ra8_err_not_initializedra8_wdt_supervisor_init not called.
k_ra8_err_busyAlready started.
k_ra8_err_rtos_thread_createUnderlying tx_thread_create failed.
Precondition
ra8_wdt_supervisor_init returned k_ra8_ok.
At least one worker has been registered.
Postcondition
Supervisor thread is runnable.
First refresh occurs at t = refresh_period_ms.
Note
Not thread-safe; call once after registration.
Since
0.1.0

Definition at line 366 of file ra8_wdt_supervisor.c.

References internal_thread_entry(), k_ra8_err_busy, k_ra8_err_not_initialized, k_ra8_err_rtos_error, k_ra8_ok, s_state, TX_AUTO_START, TX_NO_TIME_SLICE, TX_SUCCESS, and tx_thread_create.

Referenced by internal_wdt_setup(), and wdt_sup_demo_bring_up().

◆ ra8_wdt_supervisor_thread_count()

uint8_t ra8_wdt_supervisor_thread_count ( void )

Read the number of currently-registered worker threads.

Diagnostic accessor for tests and dump helpers.

Returns
Count in [0, k_ra8_wdt_sup_max_threads].
Precondition
None.
Postcondition
No state is mutated.
Since
0.1.0
Return values
k_ra8_okOperation succeeded.
Precondition
Module state is consistent.
Postcondition
Caller-visible state matches the documented contract.
Note
Not thread-safe unless documented otherwise.

Definition at line 456 of file ra8_wdt_supervisor.c.

References k_ra8_wdt_sup_max_threads, k_ra8_wdt_sup_slot_used, and s_state.

◆ ra8_wdt_supervisor_tick()

ra8_err_t ra8_wdt_supervisor_tick ( bool * out_did_refresh)
nodiscard

Run one supervisor tick synchronously (test / introspection hook).

Production builds run the same logic from the supervisor thread's loop. Exposing it as a callable function lets unit tests drive the supervisor without spinning a real ThreadX kernel and lets a host application probe "would I refresh right now?" out-of-band.

Parameters
[out]out_did_refreshOptional. Receives true if every registered thread was within deadline and the refresh hook was therefore called.
Returns
ra8_err_t
Return values
k_ra8_okTick complete.
k_ra8_err_not_initializedra8_wdt_supervisor_init not called.
Precondition
ra8_wdt_supervisor_init returned k_ra8_ok.
Postcondition
*out_did_refresh reflects whether the refresh hook ran.
Module state is otherwise unchanged.
Note
Thread-safe via TX_MUTEX.
Since
0.1.0

Definition at line 393 of file ra8_wdt_supervisor.c.

References internal_is_overdue(), k_ra8_err_not_initialized, k_ra8_err_rtos_error, k_ra8_ok, k_ra8_wdt_sup_max_threads, k_ra8_wdt_sup_slot_used, s_state, tx_mutex_get, tx_mutex_put, TX_SUCCESS, and TX_WAIT_FOREVER.

Referenced by internal_thread_entry().

Variable Documentation

◆ s_state

ra8_wdt_sup_state_t s_state
static

Singleton module state.

Note
Static; do not access outside this TU.

Definition at line 77 of file ra8_wdt_supervisor.c.