ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_ipc.c
Go to the documentation of this file.
1
27
28#include "ra8_ipc.h"
29
30#include <stdint.h>
31
32#include "ra8_attributes.h"
33#include "ra8_check.h"
34#include "ra8_elc_regs.h"
35#include "ra8_err.h"
36#include "ra8_ipc_regs.h"
37#include "ra8_isr.h"
38#include "ra8_log.h"
39
40static const char* s_tag = "IPC";
41
43typedef enum : uintptr_t {
44 k_ipc_ns_alias_offset = 0x10000000UL,
46
58
63typedef struct {
65 void* ctx;
67
77
82typedef struct {
83 bool installed;
85
88static void* s_ipc_context;
90
91/* =============================================================================
92 * Internal helpers
93 * =============================================================================
94 */
95
112static uint32_t internal_ra8_ipc_event_to_clr(uint32_t event_mask)
113{
114 uint32_t clr = 0U;
115 /* HUM Ch 3.2.14 "IPC0CLR0" p 216 -- CLR7..CLR0 share bit positions
116 * with IRQ7..IRQ0 in STA. */
117 clr |= (event_mask & k_ra8_ipc_clr_mask_irq_all);
118 if ((event_mask & k_ra8_ipc_event_msg_ready) != 0U) {
119 /* HUM Ch 3.2.14 "RST bit (FIFO Reset)" p 217 -- only way to
120 * drop STA.RDY without consuming a word. */
122 }
123 if ((event_mask & k_ra8_ipc_event_err_empty) != 0U) {
124 /* HUM Ch 3.2.14 "RCLR bit" p 217 */
126 }
127 if ((event_mask & k_ra8_ipc_event_err_full) != 0U) {
128 /* HUM Ch 3.2.14 "FCLR bit" p 217 */
130 }
131 return clr;
132}
133
138static volatile r_ipc_channel_regs_t* internal_ra8_ipc_get_regs(uint8_t channel)
139{
140 if ((uint16_t)channel >= (uint16_t)k_ra8_ipc_channel_count) {
141 return nullptr;
142 }
143#ifdef RA8_BUILD_FOR_CPU1
144 /* CPU1 (M33) has SECEXT disabled and runs as a permanent NS bus
145 * controller. The IPC channels CPU1 owns (ch0 + ch2) are
146 * NS-attributed via IPCSAR=0x00050000 set by CPU0's secure-boot.
147 * The chip routes NS IPC accesses through the bit-28-set NS alias
148 * (0x50020000), not the Secure alias the M85 HAL defaults to.
149 * Without this offset the first ``reg->CLR`` write inside
150 * ra8_ipc_init BusFaults and CPU1 wedges in cpu1_fault_handler
151 * (HUM Ch 3.2 p 205 + bench probe pinning PC at 0x020C0020). */
152 const uintptr_t ns_offset = k_ipc_ns_alias_offset;
153 return (volatile r_ipc_channel_regs_t*)((uintptr_t)ra8_ipc_channel(channel) + ns_offset);
154#else
155 return ra8_ipc_channel(channel);
156#endif
157}
158
179static uint16_t internal_ra8_ipc_unit_to_event(uint8_t unit)
180{
181 /* HUM Ch 18 "Event Link Controller" event-list -- IPC0_* feeds
182 * ELC_EVENT_IPC_IRQ0 = 0x05B; IPC1_* feeds 0x05C. */
183 if (unit == k_ra8_ipc_unit_ipc0) {
185 }
187}
188
204static void internal_ra8_ipc_dispatch_irq_lines(uint8_t channel, uint32_t mask)
205{
206 const uint32_t irq_bits = mask & k_ra8_ipc_event_irq_all;
207 if (irq_bits == 0U) {
208 return;
209 }
210 ra8_ipc_channel_state_t* const state = &s_ipc_channels[channel];
211 for (uint8_t i = 0U; i < k_ra8_ipc_irq_event_count; ++i) {
212 const uint32_t bit = (uint32_t)1U << (uint32_t)i;
213 if ((irq_bits & bit) == 0U) {
214 continue;
215 }
216 const ra8_ipc_irq_fn_t fn = state->per_event[i].fn;
217 if (fn != nullptr) {
218 fn(state->per_event[i].ctx, channel, (ra8_ipc_irq_event_id_t)i);
219 }
220 }
221}
222
223/* =============================================================================
224 * Lifecycle
225 * =============================================================================
226 */
227
228[[nodiscard]] ra8_err_t ra8_ipc_init(const ra8_ipc_config_t* cfg)
229{
230 RA8_CHECK_NULL_PTR(cfg, s_tag, "cfg must not be nullptr");
231 if ((uint16_t)cfg->channel >= (uint16_t)k_ra8_ipc_channel_count) {
233 }
234
236 RA8_CHECK_NULL_PTR(reg, s_tag, "channel mapping failed");
237
238 if (cfg->reset_fifo) {
239 /* HUM Ch 3.2.14 "IPC0CLR0" p 216 -- CLR.RST (bit 16) drops the
240 * FIFO and clears STA.RDY/STA.FULL. */
242 }
243 if (cfg->clear_status) {
244 /* HUM Ch 3.2.14 "IPC0CLR0" p 216 -- ack every IRQn and both
245 * RERR/FERR error flags so init returns with a clean slate. */
247 }
248
249 ra8_ipc_channel_state_t* const state = &s_ipc_channels[cfg->channel];
250 state->event_mask = cfg->event_mask;
251 state->active = true;
252 for (uint8_t i = 0U; i < k_ra8_ipc_irq_event_count; ++i) {
253 state->per_event[i].fn = nullptr;
254 state->per_event[i].ctx = nullptr;
255 }
256
257 ra8_log_info_val(s_tag, "ipc_init channel", (uint32_t)cfg->channel);
258 return k_ra8_ok;
259}
260
261[[nodiscard]] ra8_err_t ra8_ipc_deinit(uint8_t channel)
262{
263 if ((uint16_t)channel >= (uint16_t)k_ra8_ipc_channel_count) {
265 }
266 volatile r_ipc_channel_regs_t* reg = internal_ra8_ipc_get_regs(channel);
267 RA8_CHECK_NULL_PTR(reg, s_tag, "channel mapping failed");
268
269 /* HUM Ch 3.2.14 "IPC0CLR0" p 216 -- clear every event bit then
270 * drop the FIFO so the next init starts clean. */
273
274 ra8_ipc_channel_state_t* const state = &s_ipc_channels[channel];
275 state->event_mask = 0U;
276 state->active = false;
277 for (uint8_t i = 0U; i < k_ra8_ipc_irq_event_count; ++i) {
278 state->per_event[i].fn = nullptr;
279 state->per_event[i].ctx = nullptr;
280 }
281 return k_ra8_ok;
282}
283
284[[nodiscard]] ra8_err_t ra8_ipc_reset_fifo(uint8_t channel)
285{
286 volatile r_ipc_channel_regs_t* reg = internal_ra8_ipc_get_regs(channel);
287 if (reg == nullptr) {
289 }
290 /* HUM Ch 3.2.14 "RST bit (FIFO Reset)" p 217 -- bit 16 drains the
291 * FIFO and clears RDY/FULL. */
293 return k_ra8_ok;
294}
295
296[[nodiscard]] ra8_err_t ra8_ipc_set_event_mask(uint8_t channel, uint32_t mask)
297{
298 if ((uint16_t)channel >= (uint16_t)k_ra8_ipc_channel_count) {
300 }
301 s_ipc_channels[channel].event_mask = mask;
302 return k_ra8_ok;
303}
304
305/* =============================================================================
306 * Channel-pair convention helpers
307 * =============================================================================
308 */
309
310[[nodiscard]] ra8_err_t
311ra8_ipc_channel_for_send(ra8_ipc_core_id_t core, uint8_t pair, uint8_t* out_channel)
312{
313 RA8_CHECK_NULL_PTR(out_channel, s_tag, "out_channel must not be nullptr");
314 if ((uint8_t)core > k_ra8_ipc_core_cpu1) {
316 }
317 if (pair > 1U) {
319 }
320 /* HUM Ch 3.1 "Overview" p 204 -- CPU0 sends through IPC1 (channels
321 * 2 and 3); CPU1 sends through IPC0 (channels 0 and 1). */
322 if (core == k_ra8_ipc_core_cpu0) {
323 *out_channel = (uint8_t)(k_ra8_ipc_channel_count / 2U) + pair;
324 } else {
325 *out_channel = pair;
326 }
327 return k_ra8_ok;
328}
329
330[[nodiscard]] ra8_err_t
331ra8_ipc_channel_for_recv(ra8_ipc_core_id_t core, uint8_t pair, uint8_t* out_channel)
332{
333 RA8_CHECK_NULL_PTR(out_channel, s_tag, "out_channel must not be nullptr");
334 if ((uint8_t)core > k_ra8_ipc_core_cpu1) {
336 }
337 if (pair > 1U) {
339 }
340 /* HUM Ch 3.1 "Overview" p 204 -- CPU0 receives from IPC0 (channels
341 * 0 and 1); CPU1 receives from IPC1 (channels 2 and 3). */
342 if (core == k_ra8_ipc_core_cpu0) {
343 *out_channel = pair;
344 } else {
345 *out_channel = (uint8_t)(k_ra8_ipc_channel_count / 2U) + pair;
346 }
347 return k_ra8_ok;
348}
349
350/* =============================================================================
351 * Send / receive
352 * =============================================================================
353 */
354
355[[nodiscard]] ra8_err_t ra8_ipc_send_event(uint8_t channel, ra8_ipc_irq_event_id_t event_id)
356{
357 if ((uint16_t)channel >= (uint16_t)k_ra8_ipc_channel_count) {
359 }
360 if ((uint16_t)event_id >= (uint16_t)k_ra8_ipc_irq_event_count) {
362 }
363 volatile r_ipc_channel_regs_t* reg = internal_ra8_ipc_get_regs(channel);
364 RA8_CHECK_NULL_PTR(reg, s_tag, "channel mapping failed");
365
366 /* Publish barrier: order any shared-SRAM payload the caller staged
367 * before this IRQ notification, so the peer cannot wake on a payload
368 * that is not yet visible (the dual-core ring protocol relies on this). */
370 /* HUM Ch 3.2.11 "IPC0ISET0" p 215 -- write 1 to SETn to assert
371 * IRQn on the receiving core and raise the IPCnIRQm interrupt. */
372 reg->ISET = (uint32_t)1U << (uint32_t)event_id;
373 return k_ra8_ok;
374}
375
376[[nodiscard]] ra8_err_t ra8_ipc_clear_event(uint8_t channel, ra8_ipc_irq_event_id_t event_id)
377{
378 if ((uint16_t)channel >= (uint16_t)k_ra8_ipc_channel_count) {
380 }
381 if ((uint16_t)event_id >= (uint16_t)k_ra8_ipc_irq_event_count) {
383 }
384 volatile r_ipc_channel_regs_t* reg = internal_ra8_ipc_get_regs(channel);
385 RA8_CHECK_NULL_PTR(reg, s_tag, "channel mapping failed");
386
387 /* HUM Ch 3.2.14 "CLRn bits" p 217 -- write 1 to CLRn to drop the
388 * matching IRQn status bit. */
389 reg->CLR = (uint32_t)1U << (uint32_t)event_id;
390 return k_ra8_ok;
391}
392
393[[nodiscard]] ra8_err_t ra8_ipc_send_message(uint8_t channel, uint32_t message)
394{
395 if ((uint16_t)channel >= (uint16_t)k_ra8_ipc_channel_count) {
397 }
398 volatile r_ipc_channel_regs_t* reg = internal_ra8_ipc_get_regs(channel);
399 RA8_CHECK_NULL_PTR(reg, s_tag, "channel mapping failed");
400
401 /* HUM Ch 3.2.10 "IPC0STA0 FULL bit" p 214 -- if the FIFO is full
402 * the write to TXD is silently dropped and FERR is set. */
403 if ((reg->STA & k_ra8_ipc_sta_mask_full) != 0U) {
404 return k_ra8_err_busy;
405 }
406
407 /* Publish barrier: make any shared-SRAM payload the caller staged
408 * visible to the peer before this FIFO word, so a consumer that wakes
409 * on the message cannot read a stale payload. */
411 /* HUM Ch 3.2.12 "IPC0TXD0" p 215 -- only 32-bit writes are valid;
412 * narrower accesses are ignored by the peripheral. */
413 reg->TXD = message;
414 return k_ra8_ok;
415}
416
417[[nodiscard]] ra8_err_t
418ra8_ipc_send_message_retry(uint8_t channel, uint32_t message, uint16_t max_retries)
419{
420 if ((uint16_t)channel >= (uint16_t)k_ra8_ipc_channel_count) {
422 }
423 if (max_retries > k_ra8_ipc_retry_max) {
424 max_retries = k_ra8_ipc_retry_max;
425 }
426 volatile r_ipc_channel_regs_t* reg = internal_ra8_ipc_get_regs(channel);
427 RA8_CHECK_NULL_PTR(reg, s_tag, "channel mapping failed");
428
429 /* Publish barrier (once before the attempts): order the caller's
430 * shared-SRAM payload before the first FIFO word reaches the peer. */
432 /* NASA Rule 2: bounded loop with the +1 covering the immediate-try
433 * case where retries==0 still gets a single attempt. */
434 for (uint16_t i = 0U; i <= max_retries; ++i) {
435 /* HUM Ch 3.2.10 "FULL bit" p 214 */
436 if ((reg->STA & k_ra8_ipc_sta_mask_full) == 0U) {
437 /* HUM Ch 3.2.12 "IPC0TXD0" p 215 */
438 reg->TXD = message;
439 return k_ra8_ok;
440 }
441 /* HUM Ch 3.2.14 "FCLR bit" p 217 -- knock down a possible FERR
442 * left over from a previous overflow so the dispatcher does not
443 * spin forever on a stuck error flag. */
445 }
447}
448
449[[nodiscard]] ra8_err_t
450ra8_ipc_send_burst(uint8_t channel, const uint32_t* data, uint32_t count, uint32_t* out_written)
451{
452 RA8_CHECK_NULL_PTR(data, s_tag, "data must not be nullptr");
453 RA8_CHECK_NULL_PTR(out_written, s_tag, "out_written must not be nullptr");
454 if ((uint16_t)channel >= (uint16_t)k_ra8_ipc_channel_count) {
456 }
457 if (count == 0U) {
459 }
460 volatile r_ipc_channel_regs_t* reg = internal_ra8_ipc_get_regs(channel);
461 RA8_CHECK_NULL_PTR(reg, s_tag, "channel mapping failed");
462
463 /* Publish barrier (once before the burst): order any shared-SRAM
464 * payload the caller staged before the first FIFO word reaches the peer. */
466 uint32_t written = 0U;
467 /* NASA Rule 2: bound is the user-supplied ``count`` and the inner
468 * STA.FULL check breaks early when the 4-stage FIFO fills (HUM
469 * Ch 3.1 p 204). */
470 for (uint32_t i = 0U; i < count; ++i) {
471 /* HUM Ch 3.2.10 "FULL bit" p 214 */
472 if ((reg->STA & k_ra8_ipc_sta_mask_full) != 0U) {
473 break;
474 }
475 /* HUM Ch 3.2.12 "IPC0TXD0" p 215 */
476 reg->TXD = data[i];
477 ++written;
478 }
479 *out_written = written;
480 return k_ra8_ok;
481}
482
483[[nodiscard]] ra8_err_t ra8_ipc_recv_message(uint8_t channel, uint32_t* out_msg)
484{
485 RA8_CHECK_NULL_PTR(out_msg, s_tag, "out_msg must not be nullptr");
486 if ((uint16_t)channel >= (uint16_t)k_ra8_ipc_channel_count) {
488 }
489 volatile r_ipc_channel_regs_t* reg = internal_ra8_ipc_get_regs(channel);
490 RA8_CHECK_NULL_PTR(reg, s_tag, "channel mapping failed");
491
492 /* HUM Ch 3.2.10 "RDY bit" p 214 -- a read of RXD while RDY=0
493 * returns 0 and sets RERR. */
494 if ((reg->STA & k_ra8_ipc_sta_mask_rdy) == 0U) {
495 return k_ra8_err_no_data;
496 }
497 /* HUM Ch 3.2.13 "IPC0RXD0" p 216 */
498 *out_msg = reg->RXD;
499 return k_ra8_ok;
500}
501
502[[nodiscard]] ra8_err_t
503ra8_ipc_recv_message_retry(uint8_t channel, uint32_t* out_msg, uint16_t max_retries)
504{
505 RA8_CHECK_NULL_PTR(out_msg, s_tag, "out_msg must not be nullptr");
506 if ((uint16_t)channel >= (uint16_t)k_ra8_ipc_channel_count) {
508 }
509 if (max_retries > k_ra8_ipc_retry_max) {
510 max_retries = k_ra8_ipc_retry_max;
511 }
512 volatile r_ipc_channel_regs_t* reg = internal_ra8_ipc_get_regs(channel);
513 RA8_CHECK_NULL_PTR(reg, s_tag, "channel mapping failed");
514
515 /* NASA Rule 2: bounded loop, max_retries clamped above. */
516 for (uint16_t i = 0U; i <= max_retries; ++i) {
517 /* HUM Ch 3.2.10 "RDY bit" p 214 */
518 if ((reg->STA & k_ra8_ipc_sta_mask_rdy) != 0U) {
519 /* HUM Ch 3.2.13 "IPC0RXD0" p 216 */
520 *out_msg = reg->RXD;
521 return k_ra8_ok;
522 }
523 /* HUM Ch 3.2.14 "RCLR bit" p 217 -- proactively wipe a stale
524 * RERR so the next dispatch loop is not perpetually firing on
525 * an old error. */
527 }
529}
530
531[[nodiscard]] ra8_err_t
532ra8_ipc_recv_burst(uint8_t channel, uint32_t* out_data, uint32_t capacity, uint32_t* out_read)
533{
534 RA8_CHECK_NULL_PTR(out_data, s_tag, "out_data must not be nullptr");
535 RA8_CHECK_NULL_PTR(out_read, s_tag, "out_read must not be nullptr");
536 if ((uint16_t)channel >= (uint16_t)k_ra8_ipc_channel_count) {
538 }
539 if (capacity == 0U) {
541 }
542 volatile r_ipc_channel_regs_t* reg = internal_ra8_ipc_get_regs(channel);
543 RA8_CHECK_NULL_PTR(reg, s_tag, "channel mapping failed");
544
545 uint32_t read_count = 0U;
546 /* NASA Rule 2: bound is the caller-supplied ``capacity`` plus an
547 * inner break when STA.RDY drops. */
548 for (uint32_t i = 0U; i < capacity; ++i) {
549 /* HUM Ch 3.2.10 "RDY bit" p 214 */
550 if ((reg->STA & k_ra8_ipc_sta_mask_rdy) == 0U) {
551 break;
552 }
553 /* HUM Ch 3.2.13 "IPC0RXD0" p 216 */
554 out_data[i] = reg->RXD;
555 ++read_count;
556 }
557 *out_read = read_count;
558 return k_ra8_ok;
559}
560
561/* =============================================================================
562 * Status / errors
563 * =============================================================================
564 */
565
566[[nodiscard]] ra8_err_t ra8_ipc_get_status(uint8_t channel, uint32_t* out_sta)
567{
568 RA8_CHECK_NULL_PTR(out_sta, s_tag, "out_sta must not be nullptr");
569 if ((uint16_t)channel >= (uint16_t)k_ra8_ipc_channel_count) {
571 }
572 volatile r_ipc_channel_regs_t* reg = internal_ra8_ipc_get_regs(channel);
573 RA8_CHECK_NULL_PTR(reg, s_tag, "channel mapping failed");
574
575 /* HUM Ch 3.2.10 "IPC0STA0" p 214 */
576 *out_sta = reg->STA;
577 return k_ra8_ok;
578}
579
580[[nodiscard]] ra8_err_t ra8_ipc_clear_status(uint8_t channel, uint32_t mask)
581{
582 if ((uint16_t)channel >= (uint16_t)k_ra8_ipc_channel_count) {
584 }
585 volatile r_ipc_channel_regs_t* reg = internal_ra8_ipc_get_regs(channel);
586 RA8_CHECK_NULL_PTR(reg, s_tag, "channel mapping failed");
587
588 const uint32_t clr = internal_ra8_ipc_event_to_clr(mask);
589 if (clr != 0U) {
590 /* HUM Ch 3.2.14 "IPC0CLR0" p 216 */
591 reg->CLR = clr;
592 }
593 return k_ra8_ok;
594}
595
596[[nodiscard]] ra8_err_t ra8_ipc_clear_errors(uint8_t channel)
597{
598 volatile r_ipc_channel_regs_t* reg = internal_ra8_ipc_get_regs(channel);
599 if (reg == nullptr) {
601 }
602 /* HUM Ch 3.2.14 "RCLR bit / FCLR bit" p 217 -- ack both error
603 * flags in one CLR write. */
605 return k_ra8_ok;
606}
607
608[[nodiscard]] ra8_err_t ra8_ipc_can_send(uint8_t channel, bool* out_can_send)
609{
610 RA8_CHECK_NULL_PTR(out_can_send, s_tag, "out_can_send must not be nullptr");
611 volatile r_ipc_channel_regs_t* reg = internal_ra8_ipc_get_regs(channel);
612 if (reg == nullptr) {
614 }
615 /* HUM Ch 3.2.10 "FULL bit" p 214 */
616 *out_can_send = ((reg->STA & k_ra8_ipc_sta_mask_full) == 0U);
617 return k_ra8_ok;
618}
619
620[[nodiscard]] ra8_err_t ra8_ipc_has_data(uint8_t channel, bool* out_has_data)
621{
622 RA8_CHECK_NULL_PTR(out_has_data, s_tag, "out_has_data must not be nullptr");
623 volatile r_ipc_channel_regs_t* reg = internal_ra8_ipc_get_regs(channel);
624 if (reg == nullptr) {
626 }
627 /* HUM Ch 3.2.10 "RDY bit" p 214 */
628 *out_has_data = ((reg->STA & k_ra8_ipc_sta_mask_rdy) != 0U);
629 return k_ra8_ok;
630}
631
632/* =============================================================================
633 * Security / privilege attribution
634 * =============================================================================
635 */
636
637[[nodiscard]] ra8_err_t ra8_ipc_get_attribution(uint8_t channel, ra8_ipc_attr_t* out_attr)
638{
639 RA8_CHECK_NULL_PTR(out_attr, s_tag, "out_attr must not be nullptr");
640 if ((uint16_t)channel >= (uint16_t)k_ra8_ipc_channel_count) {
642 }
643
644 /* HUM Ch 3.2.1 "IPCSAR" p 205 / Ch 3.2.2 "IPCPAR" p 208 -- the
645 * SAIPCIRn / PAIPCIRn bits sit at [16+n] in their respective 32-bit
646 * registers. */
647 const uint32_t sar = *ra8_ipc_ipcsar();
648 const uint32_t par = *ra8_ipc_ipcpar();
649 const uint32_t bit = (uint32_t)1U << ((uint32_t)k_ra8_ipc_attr_shift_ir_base + (uint32_t)channel);
650
651 out_attr->secure = ((sar & bit) != 0U);
652 out_attr->privileged = ((par & bit) != 0U);
653 return k_ra8_ok;
654}
655
656[[nodiscard]] ra8_err_t ra8_ipc_get_nmi_attribution(uint8_t unit, ra8_ipc_attr_t* out_attr)
657{
658 RA8_CHECK_NULL_PTR(out_attr, s_tag, "out_attr must not be nullptr");
659 if ((uint16_t)unit >= (uint16_t)k_ra8_ipc_nmi_unit_count) {
661 }
662 /* HUM Ch 3.2.1 "IPCSAR.SAIPCNMIu bits" p 205-207 -- SAIPCNMI0 at bit 8,
663 * SAIPCNMI1 at bit 9; PAIPCNMI mirrors the same positions. */
664 const uint32_t sar = *ra8_ipc_ipcsar();
665 const uint32_t par = *ra8_ipc_ipcpar();
666 const uint32_t bit = (uint32_t)1U << ((uint32_t)k_ra8_ipc_attr_shift_nmi_base + (uint32_t)unit);
667 out_attr->secure = ((sar & bit) != 0U);
668 out_attr->privileged = ((par & bit) != 0U);
669 return k_ra8_ok;
670}
671
673 ra8_ipc_attr_t* out_attr)
674{
675 RA8_CHECK_NULL_PTR(out_attr, s_tag, "out_attr must not be nullptr");
676 if ((uint8_t)group > k_ra8_ipc_sem_group_high) {
678 }
679 /* HUM Ch 3.2.1 "IPCSAR.SAIPCSEMg" p 205 */
680 const uint32_t sar = *ra8_ipc_ipcsar();
681 const uint32_t par = *ra8_ipc_ipcpar();
682 const uint32_t bit = (uint32_t)1U << (uint32_t)group;
683 out_attr->secure = ((sar & bit) != 0U);
684 out_attr->privileged = ((par & bit) != 0U);
685 return k_ra8_ok;
686}
687
688[[nodiscard]] ra8_err_t
689ra8_ipc_can_access(uint8_t channel, ra8_ipc_attr_t const* required, bool* out_can_access)
690{
691 RA8_CHECK_NULL_PTR(required, s_tag, "required must not be nullptr");
692 RA8_CHECK_NULL_PTR(out_can_access, s_tag, "out_can_access must not be nullptr");
693 ra8_ipc_attr_t live = {.secure = false, .privileged = false};
694 const ra8_err_t err = ra8_ipc_get_attribution(channel, &live);
695 if (err != k_ra8_ok) {
696 return err;
697 }
698 const bool secure_match = live.secure == required->secure;
699 const bool privileged_match = live.privileged == required->privileged;
700 *out_can_access = (bool)(secure_match && privileged_match);
701 return k_ra8_ok;
702}
703
704/* =============================================================================
705 * Maskable interrupt path
706 * =============================================================================
707 */
708
710{
711 s_ipc_callback = fn;
712 s_ipc_context = ctx;
713 return k_ra8_ok;
714}
715
716[[nodiscard]] ra8_err_t ra8_ipc_attach_event_handler(uint8_t channel,
717 ra8_ipc_irq_event_id_t event_id,
719 void* ctx)
720{
721 if ((uint16_t)channel >= (uint16_t)k_ra8_ipc_channel_count) {
723 }
724 if ((uint16_t)event_id >= (uint16_t)k_ra8_ipc_irq_event_count) {
726 }
727 ra8_ipc_irq_slot_t* const slot = &s_ipc_channels[channel].per_event[event_id];
728 slot->fn = fn;
729 slot->ctx = ctx;
730 return k_ra8_ok;
731}
732
733void ra8_ipc_dispatch(uint8_t channel)
734{
735 if ((uint16_t)channel >= (uint16_t)k_ra8_ipc_channel_count) {
736 return;
737 }
738 volatile r_ipc_channel_regs_t* reg = ra8_ipc_channel(channel);
739 if (reg == nullptr) {
740 return; /* GCOVR_EXCL_LINE -- channel already bounded */
741 }
742
743 /* HUM Ch 3.2.10 "IPC0STA0" p 214 -- snapshot the status register
744 * once so the IRQ/RDY/RERR/FERR bits do not race against the peer. */
745 const uint32_t sta = reg->STA;
746 const uint32_t configured = s_ipc_channels[channel].event_mask;
747 const uint32_t fired = sta & configured & k_ra8_ipc_internal_event_full_mask;
748
749 uint32_t message = 0U;
750 if ((fired & k_ra8_ipc_event_msg_ready) != 0U) {
751 /* HUM Ch 3.2.13 "IPC0RXD0" p 216 */
752 message = reg->RXD;
753 }
754
755 /* Decoded per-IRQ-line callbacks. */
757
758 /* Single shared callback. */
760 void* const ctx = s_ipc_context;
761 if ((fn != nullptr) && (fired != 0U)) {
762 fn(ctx, channel, fired, message);
763 }
764
765 if (fired != 0U) {
766 const uint32_t clr = internal_ra8_ipc_event_to_clr(fired);
767 if (clr != 0U) {
768 /* HUM Ch 3.2.14 "IPC0CLR0" p 216 */
769 reg->CLR = clr;
770 }
771 }
772}
773
791static void internal_ra8_ipc_isr(void* ctx)
792{
793 const uint8_t unit = (uint8_t)(uintptr_t)ctx;
794 if ((uint16_t)unit >= (uint16_t)k_ra8_ipc_nmi_unit_count) {
795 return; /* GCOVR_EXCL_LINE -- ISR ctx built only from a validated IPC unit */
796 }
797 /* HUM Ch 3.1 "Overview" p 204 -- channels 0/1 belong to IPC0
798 * (unit 0); channels 2/3 belong to IPC1 (unit 1). */
799 const uint8_t base = (uint8_t)((uint8_t)unit * 2U);
800 ra8_ipc_dispatch(base);
801 ra8_ipc_dispatch((uint8_t)(base + 1U));
802}
803
804[[nodiscard]] ra8_err_t ra8_ipc_install_isr(uint8_t unit, uint8_t priority)
805{
806 if ((uint16_t)unit >= (uint16_t)k_ra8_ipc_nmi_unit_count) {
808 }
809 if (s_ipc_isr_state[unit].installed) {
810 return k_ra8_err_exists;
811 }
812 const uint16_t event_raw = internal_ra8_ipc_unit_to_event(unit);
813 /* The IPC IRQ event values (0x05B / 0x05C, HUM Ch 18) are not yet
814 * enumerated in ``ra8_elc_event_t``; cast through ``uint16_t`` is
815 * value-preserving since the enum's underlying type is ``uint16_t``.
816 * The analyzer's enumerator-list check is suppressed for this
817 * boundary cross only. */
818 const ra8_elc_event_t event = (ra8_elc_event_t)event_raw;
819 /* HUM Ch 18 "Event Link Controller" event-list -- IPC IRQs route
820 * through the ICU like every other peripheral; ra8_isr_register
821 * picks an IELSR slot, writes the event, sets priority and enables
822 * the NVIC line. */
823 const ra8_err_t err =
824 ra8_isr_register(event, internal_ra8_ipc_isr, (void*)(uintptr_t)unit, priority, nullptr);
825 if (err != k_ra8_ok) {
826 return err;
827 }
828 s_ipc_isr_state[unit].installed = true;
829 return k_ra8_ok;
830}
831
832[[nodiscard]] ra8_err_t ra8_ipc_uninstall_isr(uint8_t unit)
833{
834 if ((uint16_t)unit >= (uint16_t)k_ra8_ipc_nmi_unit_count) {
836 }
837 if (!s_ipc_isr_state[unit].installed) {
838 return k_ra8_err_not_found;
839 }
840 const uint16_t event_raw = internal_ra8_ipc_unit_to_event(unit);
841 /* See ra8_ipc_install_isr() for the rationale on this lint suppression. */
842 const ra8_elc_event_t event = (ra8_elc_event_t)event_raw;
843 const ra8_err_t err = ra8_isr_unregister(event);
844 if (err != k_ra8_ok) {
845 return err;
846 }
847 s_ipc_isr_state[unit].installed = false;
848 return k_ra8_ok;
849}
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
Event Link Controller (ELC) register layout for the Renesas RA8D2.
ra8_elc_event_t
Partial list of ELC events (populate as drivers need them).
Error Code Definitions for ra8-firmware.
@ k_ra8_err_busy
Resource busy – blocking operation cannot proceed.
Definition ra8_err.h:195
@ k_ra8_err_no_data
No application data available (e.g.
Definition ra8_err.h:202
@ k_ra8_err_invalid_arg
Invalid function argument.
Definition ra8_err.h:152
@ k_ra8_err_exists
Item already exists – cannot create again.
Definition ra8_err.h:216
@ k_ra8_err_hw_timeout
Hardware timed out waiting for a flag or handshake.
Definition ra8_err.h:304
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
@ k_ra8_err_not_found
Requested item not found (lookup / search missed).
Definition ra8_err.h:173
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
static volatile r_ipc_channel_regs_t * internal_ra8_ipc_get_regs(uint8_t channel)
Validate channel id and return the channel reg pointer.
Definition ra8_ipc.c:138
ra8_err_t ra8_ipc_recv_message(uint8_t channel, uint32_t *out_msg)
Pop one 32-bit word from the channel RX FIFO.
Definition ra8_ipc.c:483
ra8_err_t ra8_ipc_send_message_retry(uint8_t channel, uint32_t message, uint16_t max_retries)
Bounded-retry variant of ra8_ipc_send_message.
Definition ra8_ipc.c:418
ra8_err_t ra8_ipc_install_isr(uint8_t unit, uint8_t priority)
Wire the IPC IRQ event into the ISR table.
Definition ra8_ipc.c:804
ra8_err_t ra8_ipc_attach_handler(ra8_ipc_event_fn_t fn, void *ctx)
Attach a single event callback shared across all channels.
Definition ra8_ipc.c:709
ra8_err_t ra8_ipc_clear_errors(uint8_t channel)
Clear RERR + FERR on the channel.
Definition ra8_ipc.c:596
ra8_err_t ra8_ipc_send_message(uint8_t channel, uint32_t message)
Push one 32-bit word into the channel TX FIFO.
Definition ra8_ipc.c:393
ra8_err_t ra8_ipc_channel_for_recv(ra8_ipc_core_id_t core, uint8_t pair, uint8_t *out_channel)
Pick the channel id this core reads from.
Definition ra8_ipc.c:331
static ra8_ipc_isr_state_t s_ipc_isr_state[k_ra8_ipc_nmi_unit_count]
Definition ra8_ipc.c:89
ra8_err_t ra8_ipc_get_attribution(uint8_t channel, ra8_ipc_attr_t *out_attr)
Read the security / privilege attribution for one channel.
Definition ra8_ipc.c:637
ra8_err_t ra8_ipc_has_data(uint8_t channel, bool *out_has_data)
Predicate: would ra8_ipc_recv_message succeed right now?
Definition ra8_ipc.c:620
ra8_err_t ra8_ipc_init(const ra8_ipc_config_t *cfg)
Initialise one IPC channel.
Definition ra8_ipc.c:228
static void internal_ra8_ipc_dispatch_irq_lines(uint8_t channel, uint32_t mask)
Walk every IRQn bit in mask and call the per-line callback registered for that channel/line,...
Definition ra8_ipc.c:204
ra8_err_t ra8_ipc_clear_event(uint8_t channel, ra8_ipc_irq_event_id_t event_id)
Clear one received IRQ event line on the local core.
Definition ra8_ipc.c:376
ra8_err_t ra8_ipc_set_event_mask(uint8_t channel, uint32_t mask)
Update the event-mask filter for an already-initialized channel.
Definition ra8_ipc.c:296
ra8_ipc_internal_const_t
Magic constants used inside the driver only.
Definition ra8_ipc.c:51
@ k_ra8_ipc_internal_event_full_mask
RA8 ipc internal event full mask.
Definition ra8_ipc.c:52
ra8_err_t ra8_ipc_get_status(uint8_t channel, uint32_t *out_sta)
Read the raw channel status register.
Definition ra8_ipc.c:566
ipc_alias_t
Secure->Non-secure address alias offset.
Definition ra8_ipc.c:43
@ k_ipc_ns_alias_offset
Ipc ns alias offset.
Definition ra8_ipc.c:44
static uint16_t internal_ra8_ipc_unit_to_event(uint8_t unit)
Read the ELC event id matching one IPC unit.
Definition ra8_ipc.c:179
ra8_err_t ra8_ipc_can_access(uint8_t channel, ra8_ipc_attr_t const *required, bool *out_can_access)
Predicate: does this core's attribution allow writing the channel?
Definition ra8_ipc.c:689
ra8_err_t ra8_ipc_channel_for_send(ra8_ipc_core_id_t core, uint8_t pair, uint8_t *out_channel)
Pick the channel id this core writes to.
Definition ra8_ipc.c:311
ra8_err_t ra8_ipc_clear_status(uint8_t channel, uint32_t mask)
Clear selected status bits on a channel.
Definition ra8_ipc.c:580
ra8_err_t ra8_ipc_get_nmi_attribution(uint8_t unit, ra8_ipc_attr_t *out_attr)
Read the attribution for one NMI unit.
Definition ra8_ipc.c:656
ra8_err_t ra8_ipc_send_burst(uint8_t channel, const uint32_t *data, uint32_t count, uint32_t *out_written)
Push up to count words into the channel TX FIFO.
Definition ra8_ipc.c:450
static ra8_ipc_channel_state_t s_ipc_channels[k_ra8_ipc_channel_count]
Definition ra8_ipc.c:86
ra8_err_t ra8_ipc_reset_fifo(uint8_t channel)
Issue a CLR.RST on one channel without altering callbacks.
Definition ra8_ipc.c:284
ra8_err_t ra8_ipc_get_sem_attribution(ra8_ipc_sem_attr_group_t group, ra8_ipc_attr_t *out_attr)
Read the attribution for an IPCSEM group.
Definition ra8_ipc.c:672
static ra8_ipc_event_fn_t s_ipc_callback
Definition ra8_ipc.c:87
ra8_err_t ra8_ipc_uninstall_isr(uint8_t unit)
Tear down a previously installed IPC ISR.
Definition ra8_ipc.c:832
ra8_err_t ra8_ipc_send_event(uint8_t channel, ra8_ipc_irq_event_id_t event_id)
Generate a maskable IRQ event on the peer core.
Definition ra8_ipc.c:355
void ra8_ipc_dispatch(uint8_t channel)
Drive the event callback for one channel.
Definition ra8_ipc.c:733
ra8_err_t ra8_ipc_can_send(uint8_t channel, bool *out_can_send)
Predicate: would ra8_ipc_send_message succeed right now?
Definition ra8_ipc.c:608
static void internal_ra8_ipc_isr(void *ctx)
ISR trampoline – dispatches both channels in one IPC unit.
Definition ra8_ipc.c:791
ra8_err_t ra8_ipc_recv_message_retry(uint8_t channel, uint32_t *out_msg, uint16_t max_retries)
Bounded-retry variant of ra8_ipc_recv_message.
Definition ra8_ipc.c:503
static void * s_ipc_context
Definition ra8_ipc.c:88
ra8_err_t ra8_ipc_recv_burst(uint8_t channel, uint32_t *out_data, uint32_t capacity, uint32_t *out_read)
Drain up to capacity words from the channel RX FIFO.
Definition ra8_ipc.c:532
static uint32_t internal_ra8_ipc_event_to_clr(uint32_t event_mask)
Translate a public ra8_ipc_event_t mask into the matching IPCnCLRm bit pattern.
Definition ra8_ipc.c:112
ra8_err_t ra8_ipc_attach_event_handler(uint8_t channel, ra8_ipc_irq_event_id_t event_id, ra8_ipc_irq_fn_t fn, void *ctx)
Attach a callback for a single IRQ event line on a channel.
Definition ra8_ipc.c:716
ra8_err_t ra8_ipc_deinit(uint8_t channel)
Tear down one IPC channel.
Definition ra8_ipc.c:261
Inter-Processor Communication (IPC) HAL driver – public API.
Inter-Processor Communication (IPC) register layout for the RA8D2.
static volatile r_ipc_channel_regs_t * ra8_ipc_channel(uint8_t channel)
Get a pointer to one IPC FIFO channel register window.
static volatile uint32_t * ra8_ipc_ipcpar(void)
Get a pointer to the IPCPAR Privileged Attribution register.
@ k_ra8_ipc_elc_event_irq0
Receiving-side IRQ for IPC0_*.
@ k_ra8_ipc_elc_event_irq1
Receiving-side IRQ for IPC1_*.
static volatile uint32_t * ra8_ipc_ipcsar(void)
Get a pointer to the IPCSAR Security Attribution register.
ra8_ipc_core_id_t
Logical CPU core identifiers used by attribution helpers.
@ k_ra8_ipc_core_cpu0
Cortex-M85 (primary core).
@ k_ra8_ipc_core_cpu1
Cortex-M33 (secondary core).
@ k_ra8_ipc_attr_shift_nmi_base
SAIPCNMI0 / PAIPCNMI0 position.
@ k_ra8_ipc_attr_shift_ir_base
SAIPCIR0 / PAIPCIR0 bit position.
@ k_ra8_ipc_sta_mask_full
FULL: FIFO full.
@ k_ra8_ipc_sta_mask_rdy
RDY: FIFO non-empty.
@ k_ra8_ipc_clr_mask_fclr
FCLR: clear FERR.
@ k_ra8_ipc_clr_mask_rst
RST: reset message FIFO.
@ k_ra8_ipc_clr_mask_rclr
RCLR: clear RERR.
@ k_ra8_ipc_clr_mask_irq_all
CLR7..CLR0 -> clear IRQn.
@ k_ra8_ipc_irq_event_count
8 IRQ event lines per channel.
@ k_ra8_ipc_nmi_unit_count
IPC0 + IPC1 NMI units.
@ k_ra8_ipc_channel_count
Total FIFO channels: 2 per unit x 2.
ra8_ipc_sem_attr_group_t
IPCSEMn attribution-group selector (HUM Ch 3.3.1 p 227-228).
@ k_ra8_ipc_sem_group_high
IPCSEM8..IPCSEM15 attribution group.
@ k_ra8_ipc_unit_ipc0
CPU1 -> CPU0 (M33 sends, M85 receives).
static void ra8_ipc_barrier(void)
Cross-core data-memory barrier for IPC publish + lock ordering.
@ k_ra8_ipc_retry_max
Hard cap accepted by the API.
void(* ra8_ipc_event_fn_t)(void *ctx, uint8_t channel, uint32_t event_mask, uint32_t message)
IPC event callback signature.
ra8_ipc_irq_event_id_t
Index of an IRQ event line within a channel (0..7).
@ k_ra8_ipc_event_irq3
Maskable IRQ event line 3.
@ k_ra8_ipc_event_irq4
Maskable IRQ event line 4.
@ k_ra8_ipc_event_irq0
Maskable IRQ event line 0.
@ k_ra8_ipc_event_msg_ready
RDY: receive FIFO non-empty.
@ k_ra8_ipc_event_irq5
Maskable IRQ event line 5.
@ k_ra8_ipc_event_fifo_full
FULL: send FIFO is full.
@ k_ra8_ipc_event_irq2
Maskable IRQ event line 2.
@ k_ra8_ipc_event_irq6
Maskable IRQ event line 6.
@ k_ra8_ipc_event_err_full
FERR: write-while-full.
@ k_ra8_ipc_event_irq1
Maskable IRQ event line 1.
@ k_ra8_ipc_event_err_empty
RERR: read-while-empty.
@ k_ra8_ipc_event_irq_all
IRQ7..IRQ0 union mask.
@ k_ra8_ipc_event_irq7
Maskable IRQ event line 7.
void(* ra8_ipc_irq_fn_t)(void *ctx, uint8_t channel, ra8_ipc_irq_event_id_t event_id)
Per-IRQ-event-line callback signature.
NVIC + ICU IELSR allocator.
ra8_err_t ra8_isr_register(ra8_elc_event_t event, ra8_isr_handler_t handler, void *ctx, uint8_t priority, uint16_t *out_slot)
Allocate an IELSR slot for an ELC event + handler.
Definition ra8_isr.c:296
ra8_err_t ra8_isr_unregister(ra8_elc_event_t event)
Release a previously-allocated IELSR slot.
Definition ra8_isr.c:335
Lightweight Logging Interface for ra8-firmware.
#define ra8_log_info_val(tag, message, value)
RA8 log info val.
Definition ra8_log.h:366
Per-FIFO-channel register window (5 x 32-bit at +0x20 stride).
volatile uint32_t ISET
+0x04 IRQ request set (W).
volatile uint32_t CLR
+0x10 Status / error / FIFO clear (W).
volatile uint32_t RXD
+0x0C FIFO receive data (R).
volatile uint32_t TXD
+0x08 FIFO transmit data (W).
volatile uint32_t STA
+0x00 Status (RDY/FULL/RERR/FERR/IRQn).
Snapshot of the security / privilege attribution for one IPC channel, decoded from IPCSAR / IPCPAR.
bool privileged
true -> unprivileged, false -> privileged (IPCPAR.PAIPCIRn).
bool secure
true -> non-secure, false -> secure (IPCSAR.SAIPCIRn).
Per-channel runtime state held by the driver.
Definition ra8_ipc.c:72
ra8_ipc_irq_slot_t per_event[k_ra8_ipc_irq_event_count]
Per-IRQ-line callback table.
Definition ra8_ipc.c:75
bool active
true after init.
Definition ra8_ipc.c:74
uint32_t event_mask
Filter mask.
Definition ra8_ipc.c:73
Per-channel configuration descriptor passed to ra8_ipc_init.
bool reset_fifo
true -> issue CLR.RST during init.
uint8_t channel
Channel id 0..3.
uint32_t event_mask
Bitmask of events the user cares about.
bool clear_status
true -> clear all IRQ + error bits.
Per-IRQ-event-line callback slot.
Definition ra8_ipc.c:63
void * ctx
Opaque context handed back to fn.
Definition ra8_ipc.c:65
ra8_ipc_irq_fn_t fn
User callback (NULL = unused).
Definition ra8_ipc.c:64
Per-IPC-unit ISR registration state.
Definition ra8_ipc.c:82
bool installed
true while ra8_ipc_install_isr owns the slot.
Definition ra8_ipc.c:83