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

NVIC + ICU IELSR allocator. More...

#include <stdint.h>
#include "ra8_elc_regs.h"
#include "ra8_err.h"
Include dependency graph for ra8_isr.h:

Go to the source code of this file.

Typedefs

typedef void(* ra8_isr_handler_t) (void *ctx)
 Driver-supplied callback invoked on interrupt entry.

Enumerations

enum  ra8_isr_slot_count_t : uint16_t { k_ra8_isr_slot_count = 96U }
 Size of the ISR allocator's slot pool – one slot per ICU IELSR route. More...
enum  ra8_isr_prio_t : uint8_t {
  k_ra8_isr_prio_max = 15U ,
  k_ra8_isr_prio_default = 8U
}
 Cortex-M85 NVIC priority range. More...
enum  ra8_isr_invalid_slot_t : uint16_t { k_ra8_isr_slot_none = 0xFFFFU }
 Sentinel for "no slot allocated". More...

Functions

ra8_err_t ra8_isr_init (void)
 Initialise the ra8_isr table.
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.
ra8_err_t ra8_isr_unregister (ra8_elc_event_t event)
 Release a previously-allocated IELSR slot.
void ra8_isr_dispatch (uint16_t slot)
 Invoke the handler stored in dispatch-table slot slot.
ra8_err_t ra8_isr_set_priority (ra8_elc_event_t event, uint8_t priority)
 Change the NVIC priority of a registered slot.
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.
ra8_err_t ra8_isr_set_dtc (uint16_t slot, bool enable)
 Enable or disable DTC activation on a registered IELSR slot.
void ra8_isr_globals_enable (void)
 Globally enable maskable interrupts (PRIMASK = 0).
void ra8_isr_globals_disable (void)
 Globally mask maskable interrupts (PRIMASK = 1).

Detailed Description

NVIC + ICU IELSR allocator.

Tag
[Ring 3 / HAL] {World: S}

substrate module that owns every write to the Cortex-M NVIC and every write to the RA8D2 ICU's IELSR registers. Drivers that need an interrupt register an (event, handler) pair with ra8_isr_register() and the substrate:

  1. Allocates a free IELSR slot from an internal pool.
  2. Stores the caller's handler + context in a dispatch table.
  3. Writes the event number into the IELSR slot so the ICU maps the event to the matching NVIC line.
  4. Sets the NVIC priority and enables the NVIC line.

At runtime, the vector-table entry for each IELSR slot calls ra8_isr_dispatch(slot) which looks up the stored handler and invokes it with its stored context. This is the mechanism every + driver uses for interrupt delivery.

Why centralise IELSR allocation?

The RA ICU's "events are programmable, NVIC lines are assigned at runtime" model means multiple drivers independently assigning IELSR slots will collide. Putting one module in charge of the free-list means:

  • Driver code says only "I want an interrupt for SCI0 RXI" – it never picks an NVIC line number directly.
  • The sequencing (write IELSR, clear pending, enable NVIC) is in one place so the fencing and memory-barrier rules from HUM Ch 14 are centrally enforced.
  • 's NSC veneer surface is ra8_isr_*, not the 20+ per-driver files that need interrupts.

ELC event numbers

The event argument is the ELC event number from HUM Ch 19 "Event Link Controller (ELC)" (p 817). Each peripheral has well-known events (SCI0_RXI, GPT0_CCMPA, etc.). The ra8_elc facility provides the enum.

Slot lifetime

Slots persist until explicitly unregistered. A driver that wants to re-route an event calls ra8_isr_unregister first.

Threading

Not thread-safe. Driver init runs from single-threaded init context; IRQ handlers run in their own context with the caller stored state but never reach the allocator.

Definition in file ra8_isr.h.

Typedef Documentation

◆ ra8_isr_handler_t

typedef void(* ra8_isr_handler_t) (void *ctx)

Driver-supplied callback invoked on interrupt entry.

Parameters
[in]ctxUser-supplied context pointer recorded at registration time.
Note
Called from handler mode on the Cortex-M85. Must return quickly and must not take any ra8_mstp / ra8_pwr locks.

Definition at line 128 of file ra8_isr.h.

Enumeration Type Documentation

◆ ra8_isr_invalid_slot_t

enum ra8_isr_invalid_slot_t : uint16_t

Sentinel for "no slot allocated".

Enumerator
k_ra8_isr_slot_none 

RA8 ISR slot none.

Definition at line 114 of file ra8_isr.h.

◆ ra8_isr_prio_t

enum ra8_isr_prio_t : uint8_t

Cortex-M85 NVIC priority range.

NVIC priorities on the RA8D2 Cortex-M85 implement the upper 4 bits of the 8-bit priority register, so the effective priority range is 0..15 (lower = higher priority).

Enumerator
k_ra8_isr_prio_max 

Lowest priority.

k_ra8_isr_prio_default 

Middle priority.

Definition at line 105 of file ra8_isr.h.

◆ ra8_isr_slot_count_t

enum ra8_isr_slot_count_t : uint16_t

Size of the ISR allocator's slot pool – one slot per ICU IELSR route.

The RA8D2 ICU exposes ra8_icu_regs.h::k_ra8_icu_num_ielsr (96) IELSR registers, each routing one ELC event to an NVIC line. The allocator keeps a pool of exactly that many slots: a larger pool would let a slot be allocated and its NVIC line enabled while ra8_icu_ielsr() returns NULL for it, so the interrupt would be NVIC-enabled with no ICU event route (#237). A static_assert in ra8_isr.c pins this count to k_ra8_icu_num_ielsr so the two capacity constants cannot silently diverge.

Enumerator
k_ra8_isr_slot_count 

One slot per ICU IELSR route (= k_ra8_icu_num_ielsr).

Definition at line 92 of file ra8_isr.h.

Function Documentation

◆ ra8_isr_dispatch()

void ra8_isr_dispatch ( uint16_t slot)

Invoke the handler stored in dispatch-table slot slot.

Called from the Cortex-M85 vector-table trampoline when any ICU NVIC line fires. The trampoline feeds its NVIC index (which matches the IELSR slot number). ra8_isr_dispatch looks up the handler + context stored at registration time and calls the handler with the stored context. The IELSR.IR flag is cleared before the handler runs so a nested identical event re-raises the interrupt instead of being latched.

Parameters
[in]slotSlot number 0..k_ra8_isr_slot_count - 1.
Precondition
Called from Cortex-M85 handler mode.
slot has been registered via ra8_isr_register.
Postcondition
IELSR.IR is cleared.
Dispatch-table handler was invoked exactly once.
Note
Thread safety: re-entrant in the sense that the handler itself may enable nested interrupts; the dispatcher does not take any locks.
Since
0.1.0

Definition at line 362 of file ra8_isr.c.

References k_ra8_ielsr_ir_bit, k_ra8_isr_slot_count, ra8_icu_ielsr(), and s_slots.

◆ ra8_isr_globals_disable()

void ra8_isr_globals_disable ( void )

Globally mask maskable interrupts (PRIMASK = 1).

Companion to ra8_isr_globals_enable. Used by the application around critical sections where an ISR firing mid-update would corrupt shared state (the per-driver ra8_register_guard.h helpers wrap this for short scopes; this function exists for application-level sequencing during shutdown / reset).

Precondition
None.
Postcondition
PRIMASK is set; subsequent NVIC interrupts will pend until PRIMASK is cleared again.
Note
Faults still fire (NMI / HardFault are not maskable via PRIMASK). Use FAULTMASK if you need that, but the project does not expose a wrapper for it.
Since
0.1.0
Precondition
Module state is consistent.
Postcondition
Caller-visible state matches the documented contract.

Definition at line 445 of file ra8_isr.c.

References ra8_hw_irq_disable().

◆ ra8_isr_globals_enable()

void ra8_isr_globals_enable ( void )

Globally enable maskable interrupts (PRIMASK = 0).

The Cortex-M85 boots with PRIMASK clear, but SystemInit masks IRQs with cpsid i to give the application a quiet boot window for driver setup. Once every IRQ source has been registered via ra8_isr_register and the application is ready to start servicing interrupts, call this to drop the global mask. Standard CMSIS convention is "mask at boot, unmask once main() has finished deterministic init".

Precondition
Every IRQ source the application uses has been wired up.
Every shared data structure that ISRs touch is initialized.
Postcondition
PRIMASK is clear; pending NVIC interrupts will dispatch.
Note
Symmetric counterpart of ra8_isr_globals_disable. Calling ra8_isr_globals_enable while PRIMASK is already clear is a safe no-op.
Since
0.1.0
Postcondition
Caller-visible state matches the documented contract.

Definition at line 439 of file ra8_isr.c.

References ra8_hw_irq_enable().

Referenced by app_bringup_clocks(), arm_ipc_wake(), c6_cam_app_run(), cm_bringup_clocks(), ez_bringup_clocks(), internal_lcd_bringup_clocks(), internal_npu_infer_execute(), internal_rtc_demo_setup_or_halt(), lcd_bringup_clocks(), main(), mg_bringup_clocks(), and sfr_bringup_clocks().

◆ ra8_isr_init()

ra8_err_t ra8_isr_init ( void )
nodiscard

Initialise the ra8_isr table.

Zeros every IELSR slot, clears every dispatch-table entry, and disables every NVIC line 0..k_ra8_isr_slot_count. After this call every ra8_isr_register starts from a clean table.

Returns
ra8_err_t error code.
Return values
k_ra8_okTable cleared.
Precondition
Caller is in single-threaded init context.
Postcondition
IELSR0..IELSR95 all zero.
NVIC lines 0..95 are disabled.
Every dispatch-table entry is (NULL handler, NULL ctx).
Note
Thread safety: not thread-safe.
Since
0.1.0

Definition at line 229 of file ra8_isr.c.

References internal_ielsr_clear(), internal_nvic_clear_pending(), internal_nvic_disable(), k_ra8_isr_slot_count, k_ra8_ok, ra8_log_info, s_slots, and s_tag.

Referenced by arm_ipc_wake(), dtc_arm_setup_or_halt(), dtc_coh_setup_or_halt(), dtc_demo_setup_or_halt(), internal_c6_cam_setup_or_halt(), internal_gpt_irq_demo_arm(), internal_init_bind_owner(), internal_lus_arm_wake(), internal_npu_infer_setup_or_halt(), lpi_arm_wake(), ra8_nsc_periph_init(), and uart_irq_setup_or_halt().

◆ ra8_isr_lookup_slot()

ra8_err_t ra8_isr_lookup_slot ( ra8_elc_event_t event,
uint16_t * out_slot )
nodiscard

Look up the slot allocated to a registered event.

Diagnostic accessor used by unit tests to verify the allocator state. Returns k_ra8_isr_slot_none via *out_slot if the event is not registered.

Parameters
[in]eventELC event number.
[out]out_slotSlot number on success, or k_ra8_isr_slot_none if unregistered.
Returns
k_ra8_ok or k_ra8_err_null_ptr.
Precondition
out_slot is non-NULL.
Postcondition
No hardware state is modified.
Since
0.1.0

Definition at line 408 of file ra8_isr.c.

References internal_find_event(), k_ra8_ok, RA8_CHECK_NULL_PTR, and s_tag.

Referenced by internal_usbfs_storm_guard_init().

◆ ra8_isr_register()

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 )
nodiscard

Allocate an IELSR slot for an ELC event + handler.

Walks the free list, picks the first open slot, writes the event number into the corresponding IELSR register, stores (handler, ctx) in the dispatch table, clears the pending bit in the NVIC, sets the priority, and enables the NVIC line.

On success, *out_slot holds the assigned slot number. The caller normally ignores this value and only cares that the interrupt will fire; the slot number is useful for unregistration and for diagnostic dumps.

Parameters
[in]eventELC event number from ra8_elc_event_t.
[in]handlerCallback invoked on interrupt entry. Must not be NULL.
[in]ctxCaller-supplied context handed to the handler on every invocation. May be NULL.
[in]priorityNVIC priority 0..k_ra8_isr_prio_max.
[out]out_slotSlot number on success. May be NULL if the caller does not need it.
Returns
ra8_err_t error code.
Return values
k_ra8_okSlot allocated, IELSR written, NVIC line enabled.
k_ra8_err_null_ptrhandler was NULL.
k_ra8_err_invalid_argpriority out of range.
k_ra8_err_no_memNo free IELSR slot.
k_ra8_err_existsevent is already mapped by a previous registration.
Precondition
IRQs masked or single-threaded init context.
ra8_isr_init() has been called.
Postcondition
On success, a single NVIC line is enabled with the given priority and its IELSR entry matches event.
Subsequent firings of event invoke handler(ctx) via the vector-table trampoline.
Note
Thread safety: not thread-safe.
See also
ra8_isr_unregister
Since
0.1.0

Definition at line 296 of file ra8_isr.c.

References internal_find_event(), internal_find_free(), internal_ielsr_write(), internal_nvic_clear_pending(), internal_nvic_enable(), internal_nvic_set_priority(), k_ra8_err_exists, k_ra8_err_invalid_arg, k_ra8_err_no_mem, k_ra8_isr_prio_max, k_ra8_isr_slot_none, k_ra8_ok, RA8_CHECK_NULL_PTR, ra8_log_error, s_slots, and s_tag.

Referenced by arm_ipc_wake(), dtc_arm_bringup_or_halt(), dtc_coh_bringup_or_halt(), dtc_demo_bringup_or_halt(), internal_gpt_irq_demo_arm(), internal_init_bind_owner(), internal_lus_arm_wake(), internal_npu_infer_execute(), lpi_arm_wake(), ra8_board_sw_attach_irq(), ra8_cnecc_attach_isr(), ra8_gpio_attach_irq(), ra8_ipc_install_isr(), ra8_pdm_stream_enable(), and uart_irq_register_isrs().

◆ ra8_isr_set_dtc()

ra8_err_t ra8_isr_set_dtc ( uint16_t slot,
bool enable )
nodiscard

Enable or disable DTC activation on a registered IELSR slot.

The Data Transfer Controller has no software-start register (HUM Ch 18.3 p 796: "The DTC is activated by an interrupt request"); a peripheral or ELC event activates it only when that event's ICU.IELSRn slot has its DTCE bit (bit 24) set. With DTCE clear the same event is taken by the CPU as an ordinary interrupt instead. Because ra8_isr owns every IELSR slot (ra8_isr_register writes the IELS event field, the dispatcher clears IR), arming or disarming DTC on an allocated slot belongs here rather than open-coded in every DTC application.

The write is a read-modify-write that touches only DTCE: the IELS event-select field written by ra8_isr_register is preserved, and the write-0-to-clear IR status flag is left untouched – its own read value is written back, which retains it (see ra8_isr_dispatch and issue #170). The DTC clears DTCE itself when a block completes (HUM Figure 18.5 p 801), so a repeating transfer re-arms with enable = true before each activation.

Parameters
[in]slotIELSR slot 0..k_ra8_isr_slot_count - 1, as returned by ra8_isr_register through its out_slot parameter.
[in]enabletrue sets DTCE (route the event to the DTC); false clears it (route the event to the CPU).
Returns
ra8_err_t error code.
Return values
k_ra8_okIELSRn.DTCE for the slot now equals enable.
k_ra8_err_invalid_argslot is out of range.
k_ra8_err_not_foundslot is not currently registered.
k_ra8_err_hw_errorThe IELSR accessor returned NULL.
Precondition
slot was assigned by a prior ra8_isr_register call.
IRQs masked or single-threaded init context.
Postcondition
On k_ra8_ok, IELSRn.DTCE for the slot reflects enable.
No other IELSR field (IELS / IR) is modified.
Note
Thread safety: not thread-safe.
See also
ra8_isr_register
ra8_isr_dispatch
Since
0.1.0

Definition at line 415 of file ra8_isr.c.

References k_ra8_err_hw_error, k_ra8_err_invalid_arg, k_ra8_err_not_found, k_ra8_ielsr_dtce_mask, k_ra8_isr_slot_count, k_ra8_ok, ra8_icu_ielsr(), and s_slots.

Referenced by dtc_arm_run_armed(), and dtc_arm_run_disarmed().

◆ ra8_isr_set_priority()

ra8_err_t ra8_isr_set_priority ( ra8_elc_event_t event,
uint8_t priority )
nodiscard

Change the NVIC priority of a registered slot.

Parameters
[in]eventELC event number that was previously registered.
[in]priorityNew priority 0..k_ra8_isr_prio_max.
Returns
ra8_err_t error code.
Return values
k_ra8_okPriority updated.
k_ra8_err_not_foundevent not registered.
k_ra8_err_invalid_argpriority out of range.
Precondition
IRQs masked.
Postcondition
NVIC IPR byte for the slot reflects the new priority.
Since
0.1.0

Definition at line 394 of file ra8_isr.c.

References internal_find_event(), internal_nvic_set_priority(), k_ra8_err_invalid_arg, k_ra8_err_not_found, k_ra8_isr_prio_max, k_ra8_isr_slot_none, k_ra8_ok, and s_slots.

◆ ra8_isr_unregister()

ra8_err_t ra8_isr_unregister ( ra8_elc_event_t event)
nodiscard

Release a previously-allocated IELSR slot.

Parameters
[in]eventELC event number to tear down.
Returns
ra8_err_t error code.
Return values
k_ra8_okSlot released.
k_ra8_err_not_foundevent was not registered.
Precondition
IRQs masked or single-threaded init context.
Postcondition
NVIC line for the slot is disabled.
IELSR entry for the slot is zero.
Dispatch-table entry for the slot is (NULL, NULL).
Note
Thread safety: not thread-safe.
Since
0.1.0

Definition at line 335 of file ra8_isr.c.

References internal_find_event(), internal_ielsr_clear(), internal_nvic_clear_pending(), internal_nvic_disable(), k_ra8_err_not_found, k_ra8_isr_slot_count, k_ra8_isr_slot_none, k_ra8_ok, and s_slots.

Referenced by ra8_cnecc_attach_isr(), ra8_cnecc_detach_isr(), ra8_gpio_detach_irq(), ra8_ipc_uninstall_isr(), and ra8_pdm_stream_disable().