ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_esp_hosted_rtos_pool.c
Go to the documentation of this file.
1
21
22#include <stddef.h>
23#include <stdint.h>
24#include <string.h>
25
26#include "ra8_attributes.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
52#define RA8_ESP_HOSTED_TX_SHIM_IMPL
53#include "ra8_esp_hosted_tx_shim_internal.h"
54#else
55#include "tx_api.h"
56#endif
57
59
68static const char* s_tag = "ESPH_MEM";
69
95
112typedef struct {
113 void* base;
114 uint32_t magic;
115 uint32_t size;
117
118static_assert(sizeof(ra8_esp_hosted_alloc_hdr_t) <= (size_t)k_ra8_esp_hosted_hdr_bytes,
119 "allocation header must fit the documented 16-byte slot");
120
136typedef struct {
137 TX_QUEUE cb;
138 void* storage;
139 uint32_t enqueued;
140 bool used;
142
164
175
186
197
209static char s_tx_name_esph_buf[] = "esph_buf";
210
222static char s_tx_name_esph_q[] = "esph_q";
223
224/* ----------------------------------------------------------------------- */
225/* Fixed-storage allocator */
226/* ----------------------------------------------------------------------- */
227
229{
230 if (s_pool.ready) {
231 ra8_log_error(s_tag, "pool already initialised");
233 }
234 (void)memset(&s_pool, 0, sizeof(s_pool));
235 if (tx_byte_pool_create(&s_pool.transport,
238 (ULONG)sizeof(s_transport_mem)) != TX_SUCCESS) {
239 ra8_log_error(s_tag, "transport byte pool refused");
241 }
242 if (tx_byte_pool_create(&s_pool.queue_pool,
245 (ULONG)sizeof(s_queue_mem)) != TX_SUCCESS) {
246 (void)tx_byte_pool_delete(&s_pool.transport);
247 (void)memset(&s_pool, 0, sizeof(s_pool));
248 ra8_log_error(s_tag, "queue byte pool refused");
250 }
251 s_pool.ready = true;
252 return k_ra8_ok;
253}
254
256{
257 if (!s_pool.ready) {
258 ra8_log_error(s_tag, "pool not initialised");
260 }
261 ra8_err_t worst = k_ra8_ok;
262 for (uint32_t i = 0U; i < (uint32_t)k_ra8_esp_hosted_max_queues; ++i) {
263 if (!s_pool.queues[i].used) {
264 continue;
265 }
266 if (tx_queue_delete(&s_pool.queues[i].cb) != TX_SUCCESS) {
267 worst = k_ra8_err_rtos_error;
268 }
269 if (tx_byte_release(s_pool.queues[i].storage) != TX_SUCCESS) {
270 worst = k_ra8_err_rtos_error;
271 }
272 s_pool.queues[i].used = false;
273 }
274 if (tx_byte_pool_delete(&s_pool.queue_pool) != TX_SUCCESS) {
275 worst = k_ra8_err_rtos_error;
276 }
277 if (tx_byte_pool_delete(&s_pool.transport) != TX_SUCCESS) {
278 worst = k_ra8_err_rtos_error;
279 }
280 (void)memset(&s_pool, 0, sizeof(s_pool));
281 return worst;
282}
283
284void priv_ra8_esp_hosted_rtos_pool_stats(uint32_t* out_available, uint32_t* out_fragments)
285{
286 ULONG available = 0U;
287 ULONG fragments = 0U;
288 if (s_pool.ready) {
289 (void)tx_byte_pool_info_get(&s_pool.transport,
290 nullptr,
291 &available,
292 &fragments,
293 nullptr,
294 nullptr,
295 nullptr);
296 }
297 if (out_available != nullptr) {
298 *out_available = (uint32_t)available;
299 }
300 if (out_fragments != nullptr) {
301 *out_fragments = (uint32_t)fragments;
302 }
303}
304
305void* priv_ra8_esp_hosted_rtos_alloc(size_t size, size_t align)
306{
307 if (!s_pool.ready) {
308 return nullptr;
309 }
310 if ((size == 0U) || (size > (size_t)k_ra8_esp_hosted_alloc_max)) {
311 return nullptr;
312 }
313 if ((align == 0U) || (align > (size_t)k_ra8_esp_hosted_align_max) ||
314 ((align & (align - 1U)) != 0U)) {
315 return nullptr;
316 }
317 void* base = nullptr;
318 const ULONG total = (ULONG)(size + (size_t)k_ra8_esp_hosted_hdr_bytes + align - 1U);
319 /* Two ways to end up with no memory, and both must return null: the pool
320 refused, or -- a contract break by the allocator -- it reported success
321 without writing the block pointer. `tx_byte_allocate` writes its
322 out-parameter only on the success path, so the second term is what makes
323 that invariant locally provable; without it the header write below
324 computes its destination from address 0, which the clang static analyzer
325 reports as a null dereference (docs/STATIC_ANALYSIS.md).
326 ONE decision rather than a second early return: this file is at its
327 MISRA-C 15.5 single-exit budget, and both conditions get full MC/DC from
328 `test_pool_exhaustion_reports_null`. */
329 if ((tx_byte_allocate(&s_pool.transport, &base, total, TX_NO_WAIT) != TX_SUCCESS) ||
330 (base == nullptr)) {
331 return nullptr;
332 }
333 const uintptr_t raw = (uintptr_t)base + (uintptr_t)k_ra8_esp_hosted_hdr_bytes;
334 const uintptr_t aligned = (raw + (uintptr_t)align - 1U) & ~((uintptr_t)align - 1U);
336 hdr.base = base;
337 hdr.magic = (uint32_t)k_ra8_esp_hosted_hdr_magic;
338 hdr.size = (uint32_t)size;
339 (void)memcpy((void*)(aligned - (uintptr_t)k_ra8_esp_hosted_hdr_bytes), &hdr, sizeof(hdr));
340 return (void*)aligned;
341}
342
344{
345 RA8_CHECK_NULL_PTR(ptr, s_tag, "release of a null pointer");
347 (void)memcpy(&hdr,
348 (const void*)((uintptr_t)ptr - (uintptr_t)k_ra8_esp_hosted_hdr_bytes),
349 sizeof(hdr));
350 if (hdr.magic != (uint32_t)k_ra8_esp_hosted_hdr_magic) {
351 ra8_log_error(s_tag, "release of a pointer this port never handed out");
353 }
354 if (tx_byte_release(hdr.base) != TX_SUCCESS) {
356 }
357 return k_ra8_ok;
358}
359
360ra8_err_t priv_ra8_esp_hosted_rtos_block_size(const void* ptr, size_t* out_size)
361{
362 RA8_CHECK_NULL_PTR(ptr, s_tag, "block_size of a null pointer");
363 RA8_CHECK_NULL_PTR(out_size, s_tag, "out_size is NULL");
365 (void)memcpy(&hdr,
366 (const void*)((uintptr_t)ptr - (uintptr_t)k_ra8_esp_hosted_hdr_bytes),
367 sizeof(hdr));
368 if (hdr.magic != (uint32_t)k_ra8_esp_hosted_hdr_magic) {
370 }
371 *out_size = (size_t)hdr.size;
372 return k_ra8_ok;
373}
374
375uint32_t priv_ra8_esp_hosted_rtos_queue_words(uint32_t item_bytes)
376{
377 const uint32_t word = (uint32_t)k_ra8_esp_hosted_queue_word_bytes;
378 const uint32_t words = (item_bytes + (word - 1U)) / word;
379 if ((item_bytes == 0U) || (words > (uint32_t)k_ra8_esp_hosted_queue_words_max)) {
380 return 0U;
381 }
382 return words;
383}
384
385/* ----------------------------------------------------------------------- */
386/* Memory vtable slots */
387/* ----------------------------------------------------------------------- */
388
407RA8_INTERNAL static void* internal_h_memcpy(void* dest, const void* src, uint32_t size)
408{
409 if ((dest == nullptr) || (src == nullptr)) {
410 return nullptr;
411 }
412 return memcpy(dest, src, (size_t)size);
413}
414
433RA8_INTERNAL static void* internal_h_memset(void* buf, int val, size_t len)
434{
435 if (buf == nullptr) {
436 return nullptr;
437 }
438 return memset(buf, val, len);
439}
440
457RA8_INTERNAL static void* internal_h_malloc(size_t size)
458{
460}
461
479RA8_INTERNAL static void* internal_h_calloc(size_t blk_no, size_t size)
480{
481 if ((blk_no == 0U) || (size == 0U)) {
482 return nullptr;
483 }
484 if (blk_no > ((size_t)k_ra8_esp_hosted_alloc_max / size)) {
485 return nullptr;
486 }
487 void* const p =
489 if (p != nullptr) {
490 (void)memset(p, 0, blk_no * size);
491 }
492 return p;
493}
494
508RA8_INTERNAL static void internal_h_free(void* ptr)
509{
511}
512
533RA8_INTERNAL static void* internal_h_realloc(void* mem, size_t newsize)
534{
535 if (mem == nullptr) {
537 }
538 if (newsize == 0U) {
540 return nullptr;
541 }
542 size_t oldsize = 0U;
543 if (priv_ra8_esp_hosted_rtos_block_size(mem, &oldsize) != k_ra8_ok) {
544 return nullptr;
545 }
546 void* const fresh = priv_ra8_esp_hosted_rtos_alloc(newsize, (size_t)k_ra8_esp_hosted_align_none);
547 if (fresh == nullptr) {
548 return nullptr;
549 }
550 (void)memcpy(fresh, mem, (oldsize < newsize) ? oldsize : newsize);
552 return fresh;
553}
554
574RA8_INTERNAL static void* internal_h_malloc_align(size_t size, size_t align)
575{
576 return priv_ra8_esp_hosted_rtos_alloc(size, align);
577}
578
593{
595}
596
597/* ----------------------------------------------------------------------- */
598/* Queue vtable slots */
599/* ----------------------------------------------------------------------- */
600
618{
619 if ((handle == nullptr) || !s_pool.ready) {
620 return nullptr;
621 }
622 for (uint32_t i = 0U; i < (uint32_t)k_ra8_esp_hosted_max_queues; ++i) {
623 if ((handle == (const void*)&s_pool.queues[i]) && s_pool.queues[i].used) {
624 return &s_pool.queues[i];
625 }
626 }
627 return nullptr;
628}
629
648RA8_INTERNAL static void* internal_h_create_queue(uint32_t qnum_elem, uint32_t qitem_size)
649{
650 const uint32_t words = priv_ra8_esp_hosted_rtos_queue_words(qitem_size);
651 if (!s_pool.ready || (words == 0U)) {
652 return nullptr;
653 }
654 if ((qnum_elem == 0U) || (qnum_elem > (uint32_t)k_ra8_esp_hosted_queue_elems_max)) {
655 return nullptr;
656 }
657 for (uint32_t i = 0U; i < (uint32_t)k_ra8_esp_hosted_max_queues; ++i) {
658 if (s_pool.queues[i].used) {
659 continue;
660 }
661 void* ring = nullptr;
662 const ULONG bytes = (ULONG)qnum_elem * (ULONG)words * (ULONG)k_ra8_esp_hosted_queue_word_bytes;
663 if (tx_byte_allocate(&s_pool.queue_pool, &ring, bytes, TX_NO_WAIT) != TX_SUCCESS) {
664 return nullptr;
665 }
666 if (tx_queue_create(&s_pool.queues[i].cb, s_tx_name_esph_q, (UINT)words, ring, bytes) !=
667 TX_SUCCESS) {
668 (void)tx_byte_release(ring);
669 return nullptr;
670 }
671 s_pool.queues[i].storage = ring;
672 s_pool.queues[i].enqueued = 0U;
673 s_pool.queues[i].used = true;
674 return &s_pool.queues[i];
675 }
676 ra8_log_error(s_tag, "queue table exhausted");
677 return nullptr;
678}
679
701RA8_INTERNAL static int internal_h_queue_item(void* queue_handle, void* item, int timeout)
702{
703 ra8_esp_hosted_queue_slot_t* const slot = internal_queue_slot(queue_handle);
704 if ((slot == nullptr) || (item == nullptr)) {
705 return RET_INVALID;
706 }
707 const UINT rc =
708 tx_queue_send(&slot->cb, item, (ULONG)priv_ra8_esp_hosted_rtos_ms_to_ticks(timeout));
709 if (rc == TX_SUCCESS) {
710 slot->enqueued++;
711 return RET_OK;
712 }
713 return (rc == TX_QUEUE_FULL) ? RET_FAIL_TIMEOUT : RET_FAIL;
714}
715
737RA8_INTERNAL static int internal_h_dequeue_item(void* queue_handle, void* item, int timeout)
738{
739 ra8_esp_hosted_queue_slot_t* const slot = internal_queue_slot(queue_handle);
740 if ((slot == nullptr) || (item == nullptr)) {
741 return RET_INVALID;
742 }
743 const UINT rc =
744 tx_queue_receive(&slot->cb, item, (ULONG)priv_ra8_esp_hosted_rtos_ms_to_ticks(timeout));
745 if (rc == TX_SUCCESS) {
746 if (slot->enqueued > 0U) {
747 slot->enqueued--;
748 }
749 return RET_OK;
750 }
751 return (rc == TX_QUEUE_EMPTY) ? RET_FAIL_TIMEOUT : RET_FAIL;
752}
753
770RA8_INTERNAL static int internal_h_queue_msg_waiting(void* queue_handle)
771{
772 const ra8_esp_hosted_queue_slot_t* const slot = internal_queue_slot(queue_handle);
773 if (slot == nullptr) {
774 return RET_INVALID;
775 }
776 return (int)slot->enqueued;
777}
778
796RA8_INTERNAL static int internal_h_reset_queue(void* queue_handle)
797{
798 ra8_esp_hosted_queue_slot_t* const slot = internal_queue_slot(queue_handle);
799 if (slot == nullptr) {
800 return RET_INVALID;
801 }
802 if (tx_queue_flush(&slot->cb) != TX_SUCCESS) {
803 return RET_FAIL;
804 }
805 slot->enqueued = 0U;
806 return RET_OK;
807}
808
826RA8_INTERNAL static int internal_h_destroy_queue(void* queue_handle)
827{
828 ra8_esp_hosted_queue_slot_t* const slot = internal_queue_slot(queue_handle);
829 if (slot == nullptr) {
830 return RET_INVALID;
831 }
832 int rc = RET_OK;
833 if (tx_queue_delete(&slot->cb) != TX_SUCCESS) {
834 rc = RET_FAIL;
835 }
836 if (tx_byte_release(slot->storage) != TX_SUCCESS) {
837 rc = RET_FAIL;
838 }
839 slot->storage = nullptr;
840 slot->enqueued = 0U;
841 slot->used = false;
842 return rc;
843}
844
846{
847 RA8_CHECK_NULL_PTR(out, s_tag, "vtable is NULL");
848 out->_h_memcpy = internal_h_memcpy;
849 out->_h_memset = internal_h_memset;
850 out->_h_malloc = internal_h_malloc;
851 out->_h_calloc = internal_h_calloc;
852 out->_h_free = internal_h_free;
853 out->_h_realloc = internal_h_realloc;
854 out->_h_malloc_align = internal_h_malloc_align;
855 out->_h_free_align = internal_h_free_align;
856 out->_h_create_queue = internal_h_create_queue;
857 out->_h_queue_item = internal_h_queue_item;
858 out->_h_dequeue_item = internal_h_dequeue_item;
859 out->_h_queue_msg_waiting = internal_h_queue_msg_waiting;
860 out->_h_reset_queue = internal_h_reset_queue;
861 out->_h_destroy_queue = internal_h_destroy_queue;
862 return k_ra8_ok;
863}
esp-hosted port header: RTOS handle types, return codes and budgets.
#define RET_FAIL
Operation failed for an unspecified reason.
#define RET_FAIL_TIMEOUT
The requested wait expired before the operation could complete.
#define RET_INVALID
A parameter was out of contract (null handle, bad size).
#define RET_OK
Operation completed.
static const char * s_tag
Logging / check tag.
Definition ra8_app.c:17
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
Validation and Error-Checking Macros for ra8-firmware.
#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_arg
Invalid function argument.
Definition ra8_err.h:152
@ 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
Public entry point of the RA8D2 + ThreadX port of esp-hosted.
@ k_ra8_esp_hosted_queue_pool_bytes
Bytes reserved for queue message storage, shared across all queues; one esp-hosted buffer handle is 2...
@ k_ra8_esp_hosted_max_queues
Message queues the port can hand out before refusing.
@ k_ra8_esp_hosted_pool_bytes
Bytes reserved for the transport buffer pool: enough for the receive ring, a transmit frame,...
uint32_t priv_ra8_esp_hosted_rtos_ms_to_ticks(int timeout_ms)
Convert an esp-hosted millisecond timeout to a ThreadX wait option.
Module-private contract of the esp-hosted RTOS abstraction slice.
static ra8_esp_hosted_pool_state_t s_pool
Singleton state of the memory and queue half.
static int internal_h_dequeue_item(void *queue_handle, void *item, int timeout)
Dequeue one message, blocking for at most timeout.
static ra8_esp_hosted_queue_slot_t * internal_queue_slot(const void *handle)
Resolve an opaque queue handle to its live table row.
static void * internal_h_realloc(void *mem, size_t newsize)
Resize a block, preserving its contents up to the shorter length.
ra8_err_t priv_ra8_esp_hosted_rtos_release(void *ptr)
Release a block obtained from priv_ra8_esp_hosted_rtos_alloc.
static void * internal_h_calloc(size_t blk_no, size_t size)
Serve a zeroed array allocation from the transport byte pool.
ra8_err_t priv_ra8_esp_hosted_rtos_block_size(const void *ptr, size_t *out_size)
Read back the payload size recorded for an allocated block.
static void * internal_h_memset(void *buf, int val, size_t len)
Fill a byte range for the vendored core.
static void * internal_h_memcpy(void *dest, const void *src, uint32_t size)
Copy a byte range for the vendored core.
static void * internal_h_malloc(size_t size)
Serve an unaligned allocation from the transport byte pool.
static void * internal_h_malloc_align(size_t size, size_t align)
Serve an aligned allocation from the transport byte pool.
static void internal_h_free_align(void *ptr)
Return an aligned block to the transport byte pool.
static uint8_t s_queue_mem[k_ra8_esp_hosted_queue_pool_bytes]
Backing storage for the queue message-storage pool.
static char s_tx_name_esph_buf[]
Writable ThreadX object name for the esph_buf object.
static uint8_t s_transport_mem[k_ra8_esp_hosted_pool_bytes]
Backing storage for the transport buffer pool.
static int internal_h_queue_item(void *queue_handle, void *item, int timeout)
Enqueue one message, blocking for at most timeout.
ra8_esp_hosted_pool_const_t
Numeric constants of the fixed-storage allocator and queue table.
@ k_ra8_esp_hosted_align_none
"No extra alignment" selector.
@ k_ra8_esp_hosted_queue_elems_max
Deepest ring the port will make.
@ k_ra8_esp_hosted_align_max
Strictest alignment served.
@ k_ra8_esp_hosted_alloc_max
Largest single payload served.
@ k_ra8_esp_hosted_queue_word_bytes
Bytes in one ThreadX message word.
@ k_ra8_esp_hosted_hdr_magic
'R8MB' sentinel in each header.
@ k_ra8_esp_hosted_queue_words_max
ThreadX message-size cap, words.
@ k_ra8_esp_hosted_hdr_bytes
Per-block header footprint.
static int internal_h_reset_queue(void *queue_handle)
Discard every message a queue is holding.
ra8_err_t priv_ra8_esp_hosted_rtos_pool_init(void)
Create the two byte pools over their static backing arrays.
uint32_t priv_ra8_esp_hosted_rtos_queue_words(uint32_t item_bytes)
Round an esp-hosted queue element size up to whole ThreadX words.
static void internal_h_free(void *ptr)
Return a block to the transport byte pool.
ra8_err_t priv_ra8_esp_hosted_rtos_bind_pool(hosted_osi_funcs_t *out)
Populate the memory and queue slots of the vtable.
static int internal_h_queue_msg_waiting(void *queue_handle)
Report how many messages a queue is currently holding.
ra8_err_t priv_ra8_esp_hosted_rtos_pool_deinit(void)
Destroy every queue and both byte pools.
static char s_tx_name_esph_q[]
Writable ThreadX object name for the esph_q object.
static void * internal_h_create_queue(uint32_t qnum_elem, uint32_t qitem_size)
Create a fixed-capacity message queue for the vendored core.
void * priv_ra8_esp_hosted_rtos_alloc(size_t size, size_t align)
Allocate an aligned block from the transport byte pool.
void priv_ra8_esp_hosted_rtos_pool_stats(uint32_t *out_available, uint32_t *out_fragments)
Read live transport-pool occupancy.
static int internal_h_destroy_queue(void *queue_handle)
Delete a queue and return its ring storage to the queue pool.
void * memset(void *dst, int value, size_t n)
Fill memory with a constant byte value.
void * memcpy(void *dst, const void *src, size_t n)
Copy memory area between non-overlapping regions.
Lightweight Logging Interface for ra8-firmware.
#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
unsigned long ULONG
ThreadX-compatible unsigned long (host stub).
Bookkeeping written immediately below every allocated payload.
uint32_t magic
k_ra8_esp_hosted_hdr_magic while live.
uint32_t size
Payload bytes the caller asked for.
void * base
Exact pointer tx_byte_allocate returned.
Module state of the memory and queue half – entirely static.
TX_BYTE_POOL transport
Transport buffer pool.
TX_BYTE_POOL queue_pool
Queue message-storage pool.
ra8_esp_hosted_queue_slot_t queues[k_ra8_esp_hosted_max_queues]
Table.
bool ready
True while both pools live.
One row of the fixed queue table.
void * storage
Message ring carved from the queue pool.
uint32_t enqueued
Messages currently held.
TX_QUEUE cb
ThreadX queue control block; must stay first.