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

ThreadX inter-thread queue (TX_QUEUE) demo on the EK-RA8D2. More...

#include <stdint.h>
#include "ra8_board_ek_ra8d2.h"
#include "ra8_boot_entry.h"
#include "ra8_cgc.h"
#include "ra8_err.h"
#include "ra8_isr.h"
#include "ra8_time.h"
#include "tx_api.h"
Include dependency graph for main.c:

Go to the source code of this file.

Enumerations

enum  ipc_demo_cfg_t : uint32_t {
  k_ipc_demo_baud = 115200U ,
  k_ipc_demo_period_ms = 1000U ,
  k_ipc_demo_period_ticks = 1000U ,
  k_ipc_demo_thread_stack = 2048U ,
  k_ipc_demo_producer_prio = 5U ,
  k_ipc_demo_consumer_prio = 4U ,
  k_ipc_demo_queue_capacity = 8U
}
 Compile-time configuration for the ThreadX IPC demo. More...
enum  ipc_demo_payload_t : uint32_t {
  k_ipc_demo_payload_ping = 0x676E6970U ,
  k_ipc_demo_payload_pong = 0x676E6F70U
}
 32-bit ASCII payload tags exchanged across the queue. More...

Functions

static void ipc_demo_panic_halt (void)
 Halt forever in WFI on a fatal init failure.
static void ipc_demo_log (const uint8_t *s, uint32_t len)
 Write a static ASCII string to SCI8.
static void ipc_demo_setup_or_halt (void)
 Bring CGC + SCI8 up.
static void ipc_producer_thread_entry (ULONG thread_input)
 Producer thread body – 1 Hz "ping" enqueue.
static void ipc_consumer_thread_entry (ULONG thread_input)
 Consumer thread body – blocking receive + log.
void tx_application_define (void *first_unused_memory)
 ThreadX application init callback.
void main (void)
 Application entry.

Variables

static const uint8_t k_ipc_demo_banner [] = "[ipc_demo] M85 starting\r\n"
 Static log lines streamed over SCI8 (must remain ASCII).
static const uint8_t k_ipc_demo_send_msg [] = "[ipc_demo] -> ping\r\n"
static const uint8_t k_ipc_demo_recv_msg [] = "[ipc_demo] <- pong\r\n"
volatile uint32_t g_ipc_demo_tick = 0U
 HIL liveness counter – incremented per consumer wakeup.
static TX_QUEUE s_ipc_queue
 ThreadX queue control block + backing storage.
static ULONG s_ipc_queue_storage [k_ipc_demo_queue_capacity]
static TX_THREAD s_producer_thread
 ThreadX control block + stack for the producer thread.
static uint8_t s_producer_stack [k_ipc_demo_thread_stack]
static TX_THREAD s_consumer_thread
 ThreadX control block + stack for the consumer thread.
static uint8_t s_consumer_stack [k_ipc_demo_thread_stack]

Detailed Description

ThreadX inter-thread queue (TX_QUEUE) demo on the EK-RA8D2.

Tag
[Ring 6 / APP] {World: S}

Single-core (Cortex-M85, CPU0) ThreadX demo exercising the ThreadX queue primitive (tx_queue_create / tx_queue_send / tx_queue_receive) as a producer/consumer IPC channel between two threads. This used to drive the on-chip IPC FIFO between the M85 and a (never-flashed) M33 peer, which left the send FIFO permanently full after four 1 Hz iterations and printed send err on every subsequent tick. The HIL gate explicitly fails on send err or <no reply>, so the demo is recast as an entirely-local ThreadX queue exchange:

  1. Brings the chip up via ra8_cgc_init() (XTAL -> PLL1, CPUCLK0 = 1 GHz, PCLKA = 125 MHz, SCICLK = PLL1R / 4) and configures SCI8 TXD = PD_02, RXD = PD_03 at 115200 8N1 – the on-board J-Link OB CDC channel surfaces those pins as a host-side virtual COM port.
  2. tx_application_define creates an 8-slot TX_QUEUE (one ULONG per slot, see ThreadX docs "tx_queue_create" – message_size is in 32-bit words, NOT bytes; queue_size is the total backing-buffer byte count).
  3. ipc_producer_thread (priority 5) wakes every 1000 ms and tx_queue_sends the 32-bit ASCII tag "ping" with TX_WAIT_FOREVER (the queue cannot fill: capacity 8 > 1 message in flight at a time).
  4. ipc_consumer_thread (priority 4 – higher) is permanently blocked in tx_queue_receive with TX_WAIT_FOREVER. Each pop yields one "ping" -> log -> ping followed by <- pong (the demo reports both the receive and the synthetic reply token; no second core is involved).

Failure modes guarded against:

  • Message-size mismatch (TX_QUEUE_ERROR / TX_PTR_ERROR). The historic bug was creating a queue with message_size argument in bytes instead of ULONGs. Here both tx_queue_create and the storage byte count are computed from the same TX_1_ULONG constant so they cannot drift.
  • Wait-option misuse on empty queue. The consumer uses TX_WAIT_FOREVER (returns TX_SUCCESS when a producer enqueues); the previous code polled with TX_NO_WAIT-shape loops that returned TX_QUEUE_EMPTY on every miss.
  • CGC not initialized -> SysTick at MOCO. ipc_demo_setup calls ra8_cgc_init() first; without it tx_thread_sleep ticks ~119x slower than expected (same root cause that broke threadx_blink – see commit a0e8909f).
Threads
Name Priority Period Action
producer 5 1000ms tx_queue_send("ping") + log "-> ping"
consumer 4 block tx_queue_receive() + log "<- pong"
Since
0.1.0

Definition in file main.c.

Enumeration Type Documentation

◆ ipc_demo_cfg_t

enum ipc_demo_cfg_t : uint32_t

Compile-time configuration for the ThreadX IPC demo.

SCI8 is the J-Link OB CDC channel on the EK-RA8D2 board. period_ms is the inter-iteration sleep used by the producer. queue_capacity is the slot count for the inter-thread queue; the consumer always drains faster than the 1 Hz producer can fill, so two slots would be enough, but 8 gives slack for any one-off scheduler hiccup.

Enumerator
k_ipc_demo_baud 

Ipc demo baud.

k_ipc_demo_period_ms 

Ipc demo period ms.

k_ipc_demo_period_ticks 

1000 ticks @ 1 kHz SysTick = 1 s.

k_ipc_demo_thread_stack 

Ipc demo thread stack.

k_ipc_demo_producer_prio 

Ipc demo producer prio.

k_ipc_demo_consumer_prio 

Higher prio -> blocks on receive.

k_ipc_demo_queue_capacity 

Slots in the inter-thread queue.

Definition at line 93 of file main.c.

◆ ipc_demo_payload_t

enum ipc_demo_payload_t : uint32_t

32-bit ASCII payload tags exchanged across the queue.

Each queue slot holds a single ULONG (32-bit). Using printable ASCII tags keeps the protocol observable from a debugger memory window: "ping" shows up as 0x67_6E_69_70 in little-endian.

Enumerator
k_ipc_demo_payload_ping 

"ping" little-endian.

k_ipc_demo_payload_pong 

"pong" little-endian.

Definition at line 111 of file main.c.

Function Documentation

◆ ipc_consumer_thread_entry()

void ipc_consumer_thread_entry ( ULONG thread_input)
static

Consumer thread body – blocking receive + log.

Sits in tx_queue_receive with TX_WAIT_FOREVER. When the producer enqueues a token the consumer unblocks, validates the payload is the expected "ping" magic, logs the synthetic <- pong reply (the demo loops back locally; no second core is involved), and bumps g_ipc_demo_tick for the J-Link memprobe.

Using TX_WAIT_FOREVER rather than TX_NO_WAIT is the key difference from the broken pre-fix path: TX_NO_WAIT would return TX_QUEUE_EMPTY on every miss and force a busy-poll loop with no scheduler yield, which was the second classic ThreadX queue bug.

Parameters
[in]thread_inputUnused ThreadX cookie.
Precondition
Queue was created with at least 1 ULONG of capacity.
Producer thread is registered (or will be – consumer just blocks until it appears).
Postcondition
Each successful receive increments g_ipc_demo_tick by one.
Mismatched payloads are dropped silently (defensive guard).
Since
0.1.0

Definition at line 306 of file main.c.

References g_ipc_demo_tick, ipc_demo_log(), k_ipc_demo_payload_ping, k_ipc_demo_recv_msg, s_ipc_queue, TX_SUCCESS, and TX_WAIT_FOREVER.

Referenced by tx_application_define().

◆ ipc_demo_log()

void ipc_demo_log ( const uint8_t * s,
uint32_t len )
static

Write a static ASCII string to SCI8.

Parameters
[in]sNUL-terminated source buffer (caller-owned, static).
[in]lenLength in bytes excluding the NUL terminator.
Precondition
s non-NULL and len > 0.
ra8_board_uart_console_init has been called.
Postcondition
Bytes are queued on the SCI8 TX path (best-effort polling).
Errors are dropped – the log path is non-essential.
Since
0.1.0

Definition at line 167 of file main.c.

References ra8_board_uart_console_write().

Referenced by ipc_consumer_thread_entry(), and ipc_producer_thread_entry().

◆ ipc_demo_panic_halt()

void ipc_demo_panic_halt ( void )
static

Halt forever in WFI on a fatal init failure.

Precondition
Called only after a non-recoverable boot error.
Postcondition
CPU is parked at WFI; only debugger / reset wakes it.
Since
0.1.0

Definition at line 147 of file main.c.

Referenced by ipc_demo_setup_or_halt(), main(), and tx_application_define().

◆ ipc_demo_setup_or_halt()

void ipc_demo_setup_or_halt ( void )
static

Bring CGC + SCI8 up.

Panic-halts on any failure.

Order matters: ra8_cgc_init MUST run before the kernel enters its scheduler tick, otherwise SysTick (programmed by _tx_initialize_low_level against RA8_BOOT_CLOCK_HZ = 1e9) ticks at the wrong rate and tx_thread_sleep becomes ~119x slower than expected. Without that fix the consumer thread's -> ping log lines would appear every ~2 minutes instead of every 1 s, and the HIL UART scrape would time out.

Precondition
Reset_Handler has run.
ra8_isr_init has been called by Reset_Handler / SystemInit.
Postcondition
On success, SCI8 is alive at 115200 8N1.
On any failure, the function does not return.
Since
0.1.0

Definition at line 195 of file main.c.

References ipc_demo_panic_halt(), k_ipc_demo_baud, k_ra8_clock_id_cpuclk0, k_ra8_ok, ra8_board_uart_console_init(), ra8_cgc_get_clock_hz(), ra8_cgc_init(), and ra8_time_init().

Referenced by main().

◆ ipc_producer_thread_entry()

void ipc_producer_thread_entry ( ULONG thread_input)
static

Producer thread body – 1 Hz "ping" enqueue.

Each iteration writes the "ping" token into the shared TX_QUEUE with TX_WAIT_FOREVER. The queue capacity (8) far exceeds the single message in flight at any time, so the call returns TX_SUCCESS immediately. Logs -> ping to SCI8 so the UART scrape can observe the producer side, then sleeps one period.

Parameters
[in]thread_inputUnused ThreadX cookie.
Precondition
ipc_demo_setup_or_halt succeeded (CGC + SCI8 alive).
Queue was created by tx_application_define.
Postcondition
Each loop iteration enqueues exactly one ULONG.
Each loop iteration writes one log line to SCI8.
Since
0.1.0

Definition at line 262 of file main.c.

References ipc_demo_log(), k_ipc_demo_banner, k_ipc_demo_payload_ping, k_ipc_demo_period_ticks, k_ipc_demo_send_msg, s_ipc_queue, TX_SUCCESS, tx_thread_sleep, and TX_WAIT_FOREVER.

Referenced by tx_application_define().

◆ main()

void main ( void )

Application entry.

The application entry point Reset_Handler hands control to.

Brings up CGC + SCI8 then drops into the ThreadX scheduler.

Precondition
Reset_Handler has copied .data and zeroed .bss.
SystemInit has set VTOR, FPU, and priority grouping.
Postcondition
On success the producer/consumer pair runs forever.
On any HAL bring-up failure the function halts in WFI.
Since
0.1.0

Definition at line 405 of file main.c.

References ipc_demo_panic_halt(), ipc_demo_setup_or_halt(), and ra8_isr_globals_enable().

◆ tx_application_define()

void tx_application_define ( void * first_unused_memory)

ThreadX application init callback.

Parameters
[in]first_unused_memoryPointer to first free byte after kernel internal allocations. Unused (static stacks).
Precondition
Called from _tx_initialize_kernel_enter with IRQs masked.
HAL bring-up has completed (SCI8 ready).
Postcondition
Queue and both threads are registered with the scheduler.
On any creation failure, the function spins in WFI.

Definition at line 337 of file main.c.

References ipc_consumer_thread_entry(), ipc_demo_panic_halt(), ipc_producer_thread_entry(), k_ipc_demo_consumer_prio, k_ipc_demo_producer_prio, k_ipc_demo_thread_stack, s_consumer_stack, s_consumer_thread, s_ipc_queue, s_ipc_queue_storage, s_producer_stack, s_producer_thread, TX_AUTO_START, TX_NO_TIME_SLICE, TX_SUCCESS, and tx_thread_create.

Variable Documentation

◆ g_ipc_demo_tick

volatile uint32_t g_ipc_demo_tick = 0U

HIL liveness counter – incremented per consumer wakeup.

Bumped by ipc_consumer_thread on every successful tx_queue_receive. Read externally by scripts/hil/jlink_memprobe.sh to gate the demo independent of UART scraping.

Note
Read externally by J-Link only; firmware never reads back.
Since
0.1.0

Definition at line 133 of file main.c.

Referenced by ipc_consumer_thread_entry().

◆ k_ipc_demo_banner

const uint8_t k_ipc_demo_banner[] = "[ipc_demo] M85 starting\r\n"
static

Static log lines streamed over SCI8 (must remain ASCII).

Definition at line 117 of file main.c.

Referenced by ipc_producer_thread_entry().

◆ k_ipc_demo_recv_msg

const uint8_t k_ipc_demo_recv_msg[] = "[ipc_demo] <- pong\r\n"
static

Definition at line 119 of file main.c.

Referenced by ipc_consumer_thread_entry().

◆ k_ipc_demo_send_msg

const uint8_t k_ipc_demo_send_msg[] = "[ipc_demo] -> ping\r\n"
static

Definition at line 118 of file main.c.

Referenced by ipc_producer_thread_entry().

◆ s_consumer_stack

uint8_t s_consumer_stack[k_ipc_demo_thread_stack]
static

Definition at line 241 of file main.c.

Referenced by tx_application_define().

◆ s_consumer_thread

TX_THREAD s_consumer_thread
static

ThreadX control block + stack for the consumer thread.

Definition at line 240 of file main.c.

Referenced by tx_application_define().

◆ s_ipc_queue

TX_QUEUE s_ipc_queue
static

ThreadX queue control block + backing storage.

tx_queue_create takes the message size in 32-bit ULONGs (TX_1_ULONG == 1) and the queue size in BYTES of backing storage (sizeof(s_ipc_queue_storage) == capacity * sizeof(ULONG)). Mixing those two units is the canonical ThreadX queue bug – using bytes for message_size causes tx_queue_create to silently slice the buffer wrong and tx_queue_send returns TX_QUEUE_ERROR / TX_PTR_ERROR.

Definition at line 232 of file main.c.

Referenced by ipc_consumer_thread_entry(), ipc_producer_thread_entry(), and tx_application_define().

◆ s_ipc_queue_storage

ULONG s_ipc_queue_storage[k_ipc_demo_queue_capacity]
static

Definition at line 233 of file main.c.

Referenced by tx_application_define().

◆ s_producer_stack

uint8_t s_producer_stack[k_ipc_demo_thread_stack]
static

Definition at line 237 of file main.c.

Referenced by tx_application_define().

◆ s_producer_thread

TX_THREAD s_producer_thread
static

ThreadX control block + stack for the producer thread.

Definition at line 236 of file main.c.

Referenced by tx_application_define().