ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_isr.c
Go to the documentation of this file.
1
16
17#include "ra8_isr.h"
18
19#include <stdint.h>
20
21#include "ra8_attributes.h"
22#include "ra8_check.h"
23#include "ra8_err.h"
24#include "ra8_hw_intrinsics.h"
25#include "ra8_icu_regs.h"
26#include "ra8_log.h"
27
28static const char* s_tag = "ISR";
29
40typedef enum : uintptr_t {
41 k_ra8_isr_nvic_iser_base = 0xE000E100UL,
42 k_ra8_isr_nvic_icer_base = 0xE000E180UL,
43 k_ra8_isr_nvic_icpr_base = 0xE000E280UL,
44 k_ra8_isr_nvic_ipr_base = 0xE000E400UL,
46
55
67
78
79/* The slot pool must not exceed the ICU IELSR capacity: a slot with index
80 * >= k_ra8_icu_num_ielsr could be allocated and its NVIC line enabled, but
81 * ra8_icu_ielsr() returns NULL for it, leaving the interrupt NVIC-enabled with
82 * no ICU event route (#237). Pin the two capacity constants together so they
83 * cannot silently diverge. */
84static_assert((uint16_t)k_ra8_isr_slot_count == (uint16_t)k_ra8_icu_num_ielsr,
85 "ra8_isr slot pool must equal the ICU IELSR capacity (see #237)");
86
87/* =============================================================================
88 * NVIC pokes (no-op on host)
89 * =============================================================================
90 *
91 * The fake maps the SCB / NVIC window via ra8_fake_mmap (core
92 * region base 0xE0000000, size 0x100000). Writes land in host
93 * RAM; the tests observe the register state via the same aliases.
94 */
95
108RA8_INTERNAL static void internal_nvic_enable(uint16_t n)
109{
110 const uint16_t word = n / k_ra8_isr_nvic_bits_per_word;
111 const uint16_t bit = n % k_ra8_isr_nvic_bits_per_word;
112 volatile uint32_t* iser =
113 (volatile uint32_t*)(k_ra8_isr_nvic_iser_base + ((uintptr_t)word * sizeof(uint32_t)));
114 *iser = (uint32_t)1UL << bit;
115}
116
129RA8_INTERNAL static void internal_nvic_disable(uint16_t n)
130{
131 const uint16_t word = n / k_ra8_isr_nvic_bits_per_word;
132 const uint16_t bit = n % k_ra8_isr_nvic_bits_per_word;
133 volatile uint32_t* icer =
134 (volatile uint32_t*)(k_ra8_isr_nvic_icer_base + ((uintptr_t)word * sizeof(uint32_t)));
135 *icer = (uint32_t)1UL << bit;
136}
137
151{
152 const uint16_t word = n / k_ra8_isr_nvic_bits_per_word;
153 const uint16_t bit = n % k_ra8_isr_nvic_bits_per_word;
154 volatile uint32_t* icpr =
155 (volatile uint32_t*)(k_ra8_isr_nvic_icpr_base + ((uintptr_t)word * sizeof(uint32_t)));
156 *icpr = (uint32_t)1UL << bit;
157}
158
172RA8_INTERNAL static void internal_nvic_set_priority(uint16_t n, uint8_t prio)
173{
174 volatile uint8_t* ipr = (volatile uint8_t*)(k_ra8_isr_nvic_ipr_base + (uintptr_t)n);
175 *ipr = (uint8_t)(prio << k_ra8_isr_nvic_prio_shift);
176}
177
195RA8_INTERNAL static void internal_ielsr_write(uint16_t slot, ra8_elc_event_t event)
196{
197 volatile uint32_t* ielsr = ra8_icu_ielsr(slot);
198 if (ielsr != nullptr) { /* GCOVR_EXCL_BR_LINE -- slot bounds already validated */
199 /* HUM Ch 14.2 "IELSRn : ICU Event Link Setting Register n", p 547 */
200 *ielsr = (uint32_t)event & k_ra8_ielsr_iels_mask;
201 }
202}
203
216RA8_INTERNAL static void internal_ielsr_clear(uint16_t slot)
217{
218 volatile uint32_t* ielsr = ra8_icu_ielsr(slot);
219 if (ielsr != nullptr) { /* GCOVR_EXCL_BR_LINE -- slot bounds already validated */
220 *ielsr = 0U;
221 }
222}
223
224/* =============================================================================
225 * Public API
226 * =============================================================================
227 */
228
230{
231 ra8_log_info(s_tag, "ra8_isr_init");
232
233 for (uint16_t slot = 0U; slot < k_ra8_isr_slot_count; ++slot) {
234 s_slots[slot].handler = nullptr;
235 s_slots[slot].ctx = nullptr;
236 s_slots[slot].event = (ra8_elc_event_t)0U;
237 s_slots[slot].priority = 0U;
238 s_slots[slot].in_use = false;
239
243 }
244 return k_ra8_ok;
245}
246
263{
264 for (uint16_t slot = 0U; slot < k_ra8_isr_slot_count; ++slot) {
265 if (s_slots[slot].in_use && s_slots[slot].event == event) {
266 return slot;
267 }
268 }
269 return k_ra8_isr_slot_none;
270}
271
286RA8_INTERNAL static uint16_t internal_find_free(void)
287{
288 for (uint16_t slot = 0U; slot < k_ra8_isr_slot_count; ++slot) {
289 if (!s_slots[slot].in_use) {
290 return slot;
291 }
292 }
293 return k_ra8_isr_slot_none;
294}
295
297 ra8_isr_handler_t handler,
298 void* ctx,
299 uint8_t priority,
300 uint16_t* out_slot)
301{
302 RA8_CHECK_NULL_PTR(handler, s_tag, "handler must not be NULL");
303 if (priority > k_ra8_isr_prio_max) {
305 }
306
307 const uint16_t existing = internal_find_event(event);
308 if (existing != k_ra8_isr_slot_none) {
309 return k_ra8_err_exists;
310 }
311
312 const uint16_t slot = internal_find_free();
313 if (slot == k_ra8_isr_slot_none) {
314 ra8_log_error(s_tag, "no free slot");
315 return k_ra8_err_no_mem;
316 }
317
318 s_slots[slot].handler = handler;
319 s_slots[slot].ctx = ctx;
320 s_slots[slot].event = event;
321 s_slots[slot].priority = priority;
322 s_slots[slot].in_use = true;
323
324 internal_ielsr_write(slot, event);
326 internal_nvic_set_priority(slot, priority);
328
329 if (out_slot != nullptr) {
330 *out_slot = slot;
331 }
332 return k_ra8_ok;
333}
334
336{
337 const uint16_t slot = internal_find_event(event);
338 if (slot == k_ra8_isr_slot_none) {
339 return k_ra8_err_not_found;
340 }
341 /* internal_find_event returns a slot < k_ra8_isr_slot_count or slot_none, so
342 * the write below is in bounds; the explicit guard makes that provable to the
343 * static analyzer (matches the bound in ra8_isr_dispatch). */
344 /* GCOVR_EXCL_BR_START -- unreachable given the contract above */
345 if (slot >= k_ra8_isr_slot_count) {
346 /* GCOVR_EXCL_BR_STOP */
347 return k_ra8_err_not_found; /* GCOVR_EXCL_LINE -- slot bound proven */
348 }
349
353
354 s_slots[slot].handler = nullptr;
355 s_slots[slot].ctx = nullptr;
356 s_slots[slot].event = (ra8_elc_event_t)0U;
357 s_slots[slot].priority = 0U;
358 s_slots[slot].in_use = false;
359 return k_ra8_ok;
360}
361
362void ra8_isr_dispatch(uint16_t slot)
363{
364 if (slot >= k_ra8_isr_slot_count) {
365 return;
366 }
367 const ra8_isr_handler_t handler = s_slots[slot].handler;
368 void* const ctx = s_slots[slot].ctx;
369
370 /* Clear IELSR.IR via the accessor in ra8_icu_regs.h. The IR flag is
371 * WRITE-ZERO-to-clear, not write-one-to-clear: HUM Ch 14.2.17 p 547 states
372 * under "IR flag (Interrupt Status Flag)" -> [Clearing condition] that "the
373 * IR flag is cleared to 0 by writing 0". ORing a 1 into the bit -- which
374 * this did until issue #170 -- therefore cannot ever clear it. The ICU then
375 * re-asserts the request the instant the handler returns and the core
376 * live-locks in an interrupt storm that starves the main loop. Bench-proven
377 * on an EK-RA8D2: lpm_periodic_idle sat in IRQ0_Handler with
378 * IELSR0 = 0x00010080 (IR set) and emitted no UART at all; writing IR = 0
379 * through J-Link released it and the app printed immediately.
380 *
381 * Read-modify-write with the IR bit masked OFF: every other field (IELS,
382 * DTCE) keeps its value and only IR receives the clearing zero. */
383 volatile uint32_t* ielsr = ra8_icu_ielsr(slot);
384 if (ielsr != nullptr) { /* GCOVR_EXCL_BR_LINE -- validated above */
385 /* HUM Ch 14.2.17 "IELSRn : Interrupt Controller Unit Event Link Setting Register n", p 546-547 */
386 *ielsr = *ielsr & ~((uint32_t)1U << k_ra8_ielsr_ir_bit);
387 }
388
389 if (handler != nullptr) {
390 handler(ctx);
391 }
392}
393
395{
396 if (priority > k_ra8_isr_prio_max) {
398 }
399 const uint16_t slot = internal_find_event(event);
400 if (slot == k_ra8_isr_slot_none) {
401 return k_ra8_err_not_found;
402 }
403 s_slots[slot].priority = priority;
404 internal_nvic_set_priority(slot, priority);
405 return k_ra8_ok;
406}
407
409{
410 RA8_CHECK_NULL_PTR(out_slot, s_tag, "out_slot must not be NULL");
411 *out_slot = internal_find_event(event);
412 return k_ra8_ok;
413}
414
415ra8_err_t ra8_isr_set_dtc(uint16_t slot, bool enable)
416{
417 if (slot >= k_ra8_isr_slot_count) {
419 }
420 if (!s_slots[slot].in_use) {
421 return k_ra8_err_not_found;
422 }
423 volatile uint32_t* ielsr = ra8_icu_ielsr(slot);
424 if (ielsr == nullptr) { /* GCOVR_EXCL_BR_LINE -- slot bounds already validated */
425 return k_ra8_err_hw_error; /* GCOVR_EXCL_LINE -- slot bound proves reg */
426 }
427 /* HUM Ch 14.2.17 "IELSRn : ICU Event Link Setting Register n", p 547: DTCE[24]
428 * enables DTC activation. The read-modify-write touches only DTCE -- the IELS
429 * event field ra8_isr_register wrote is preserved, and the write-0-to-clear IR
430 * status flag is retained by writing back its own read value (ORing/ANDing the
431 * DTCE mask leaves bit 16 unchanged; see ra8_isr_dispatch and issue #170). */
432 const uint32_t cur = *ielsr;
433 const uint32_t next =
434 enable ? (cur | (uint32_t)k_ra8_ielsr_dtce_mask) : (cur & ~(uint32_t)k_ra8_ielsr_dtce_mask);
435 *ielsr = next;
436 return k_ra8_ok;
437}
438
440{
441 /* PRIMASK clear -- maskable IRQs may now dispatch (host no-op via seam). */
443}
444
446{
447 /* PRIMASK set -- subsequent maskable IRQs pend until re-enabled (host
448 * no-op via seam). */
450}
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
ra8_elc_event_t
Partial list of ELC events (populate as drivers need them).
Error Code Definitions for ra8-firmware.
@ k_ra8_err_no_mem
Static buffer exhausted (no dynamic memory on this project).
Definition ra8_err.h:142
@ 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_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
@ k_ra8_err_hw_error
Generic hardware fault detected (error flag set, fault interrupt).
Definition ra8_err.h:310
@ 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
Shared CPU-intrinsic seam for the HAL drivers (host/target split).
static void ra8_hw_irq_enable(void)
Enable maskable interrupts (clear PRIMASK).
static void ra8_hw_irq_disable(void)
Disable maskable interrupts (set PRIMASK).
Interrupt Control Unit (ICU) register layout for the Renesas RA8D2.
@ k_ra8_icu_num_ielsr
IELSR slot count on RA8D2 (FSP R_ICU_Type).
@ k_ra8_ielsr_ir_bit
IR interrupt status flag (W0C).
static volatile uint32_t * ra8_icu_ielsr(uint16_t index)
Get pointer to IELSR register N.
@ k_ra8_ielsr_dtce_mask
DTCE bit mask.
@ k_ra8_ielsr_iels_mask
IELS field mask (10 bits).
void ra8_isr_globals_disable(void)
Globally mask maskable interrupts (PRIMASK = 1).
Definition ra8_isr.c:445
ra8_err_t ra8_isr_init(void)
Initialise the ra8_isr table.
Definition ra8_isr.c:229
ra8_err_t ra8_isr_lookup_slot(ra8_elc_event_t event, uint16_t *out_slot)
Look up the slot allocated to a registered event.
Definition ra8_isr.c:408
ra8_err_t ra8_isr_set_dtc(uint16_t slot, bool enable)
Enable or disable DTC activation on a registered IELSR slot.
Definition ra8_isr.c:415
void ra8_isr_globals_enable(void)
Globally enable maskable interrupts (PRIMASK = 0).
Definition ra8_isr.c:439
static void internal_nvic_clear_pending(uint16_t n)
Clear pending flag on NVIC line n.
Definition ra8_isr.c:150
static uint16_t internal_find_free(void)
Find the first free dispatch-table slot.
Definition ra8_isr.c:286
static void internal_nvic_set_priority(uint16_t n, uint8_t prio)
Set NVIC priority byte for line n.
Definition ra8_isr.c:172
static void internal_ielsr_clear(uint16_t slot)
Clear an IELSR slot (set event = 0, IR bit clear).
Definition ra8_isr.c:216
static void internal_ielsr_write(uint16_t slot, ra8_elc_event_t event)
Write the event number into an IELSR slot.
Definition ra8_isr.c:195
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
static ra8_isr_slot_t s_slots[k_ra8_isr_slot_count]
Per-slot dispatch table.
Definition ra8_isr.c:77
ra8_err_t ra8_isr_set_priority(ra8_elc_event_t event, uint8_t priority)
Change the NVIC priority of a registered slot.
Definition ra8_isr.c:394
static void internal_nvic_enable(uint16_t n)
Enable NVIC line n.
Definition ra8_isr.c:108
ra8_err_t ra8_isr_unregister(ra8_elc_event_t event)
Release a previously-allocated IELSR slot.
Definition ra8_isr.c:335
ra8_isr_nvic_t
NVIC register-base offsets used by the dispatcher.
Definition ra8_isr.c:40
@ k_ra8_isr_nvic_ipr_base
IPR base: priority bytes.
Definition ra8_isr.c:44
@ k_ra8_isr_nvic_icer_base
ICER base: disables.
Definition ra8_isr.c:42
@ k_ra8_isr_nvic_iser_base
ISER base: enables.
Definition ra8_isr.c:41
@ k_ra8_isr_nvic_icpr_base
ICPR base: clear pending.
Definition ra8_isr.c:43
static uint16_t internal_find_event(ra8_elc_event_t event)
Search the dispatch table for a previously-registered event.
Definition ra8_isr.c:262
void ra8_isr_dispatch(uint16_t slot)
Invoke the handler stored in dispatch-table slot slot.
Definition ra8_isr.c:362
ra8_isr_nvic_layout_t
Bit-per-word geometry of ISER / ICER arrays.
Definition ra8_isr.c:51
@ k_ra8_isr_nvic_prio_shift
Upper 4 priority bits used.
Definition ra8_isr.c:53
@ k_ra8_isr_nvic_bits_per_word
32 bits per ISER/ICER entry.
Definition ra8_isr.c:52
static void internal_nvic_disable(uint16_t n)
Disable NVIC line n.
Definition ra8_isr.c:129
NVIC + ICU IELSR allocator.
@ k_ra8_isr_slot_none
RA8 ISR slot none.
Definition ra8_isr.h:115
void(* ra8_isr_handler_t)(void *ctx)
Driver-supplied callback invoked on interrupt entry.
Definition ra8_isr.h:128
@ k_ra8_isr_prio_max
Lowest priority.
Definition ra8_isr.h:106
@ k_ra8_isr_slot_count
One slot per ICU IELSR route (= k_ra8_icu_num_ielsr).
Definition ra8_isr.h:93
Lightweight Logging Interface for ra8-firmware.
#define ra8_log_info(tag, message)
RA8 log info.
Definition ra8_log.h:364
#define ra8_log_error(tag, message)
RA8 log error.
Definition ra8_log.h:335
Dispatch-table entry for one IELSR slot.
Definition ra8_isr.c:60
ra8_elc_event_t event
Event mapped to this slot.
Definition ra8_isr.c:63
ra8_isr_handler_t handler
Driver callback, NULL if free.
Definition ra8_isr.c:61
void * ctx
Context passed to handler.
Definition ra8_isr.c:62
bool in_use
True when allocated.
Definition ra8_isr.c:65
uint8_t priority
Last-set NVIC priority.
Definition ra8_isr.c:64