wiki:icu_device_api

Version 7 (modified by alain, 7 years ago) (diff)

--

ICU device API

A) General principles

The ICU (Interrupt Controller Unit) device describes an internal peripheral, acting in all clusters containing cores. He is in charge of concentrating all IRQs (interrupt requests) generated by a peripheral to signal the completion of an I/O operation. Each IRQ should be routed to the core that started the I/O operation. The ICU device must also help the kernel to select the ISR (Interrupt Service Routine) that must be executed by the target core.

This component can be implemented as a dedicated hardware, centralized or distributed in all clusters, or emulated in software, as long as it implements the API defined below.

ALMOS-MK defines three types of IRQs, that are handled by this generic ICU device:

  • HWI : The HardWare? Interrupts are generated by a local internal peripheral. They are connected to the local ICU, to be routed to a given local core.
  • WTI : The Write Triggered Interrupts are actually mailboxes implemented in the local ICU. They can be used to implement software IPIs (Inter-Processor-Interrupts), or to route an IRQ generated by an external peripheral to a given local core.
  • PTI : The Programmable Timer Interrupts are actually timers generating periodic interrupts controled by softare, contained in the local ICU, and routed to a given local core.

The max numbers of interrupts of each type in a given cluster are defined by the CONFIG_IRQ_HWI_MAX, CONFIG_IRQ_WTI_MAX, and CONFIG_IRQ_PTI_MAX parameters. The actual numbers for a given manycore architecture are defined in the boot_info file.

The generic ICU device provides three main services:

  1. It allows the kernel to selectively enable/disable any IRQ (identified by its type and index) for a given core. It is the kernel responsibility to enable a given IRQ to the core that started the I/O operation core, as a given IRQ event should be handled by only one core.
  2. It makes a global OR between all enabled IRQs for a given core, to interrupt the core when at least one enabled IRQ is active. Of course, the core is interrupted only if the interrupts are not globally masked for the target core.
  3. It is capable to return the highest priority active IRQ of each type. The priority scheme depends on the ICU device implementation.

To select the ISR to be executed for a given IRQ routed to a given core, the ICU device uses three interrupts vectors, implemented as three arrays (HWI/WTI/PTI) stored in the core descriptor. Each entry in one interrupt vector array (defining one IRQ type and index) contains an extended pointer on the device descriptor that is the "source" of the interrupt. This device descriptor contains a link to the ISR to be executed to handle the interrupt event.

As the generic ICU device works for a single client (the local kernel instance), it does not use the device descriptor queue. All commands are immediately executed, using the device lock to get exclusive access to the ICU device state.

B) Access Functions

1) void dev_icu_init( device_t * icu )

This function makes two initialisations: It initialises the ICU specific fields of the device descriptor. it initialises the implementation specific ICU hardware device and associated data structures if required. It must be executed once by a thread running in the cluster containing the ICU device descriptor. The <icu> argument is a local pointer on the ICU device descriptor.

2) error_t dev_icu_enable_irq( uint32_t irq_type , uint32_t irq_id , xptr_t src_dev )

This function enables the routing of a given IRQ, defined by its type and index, to the calling core. It registers it in the calling core interrupt vector the extended pointer on the "source" device descriptor. The <irq_type> and <irq_id> arguments define the IRQ type and index. The <src_dev> argument is the remote pointer on the source device. It returns 0 if success / returns EINVAL if illegal arguments.

3) error_t dev_icu_disable_irq( uint32_t irq_type , uint32_t irq_id )

This function must be executed by a thread running in the cluster containing the ICU device descriptor. It disables one IRQ defined by its type and index, for the calling core, and remove it from the interrupt vector. The <irq_type> and <irq_id> arguments define the IRQ type and index. It returns 0 if success / returns EINVAL if illegal arguments.

4) error_t dev_icu_set_period( core_t * core , uint32_t pti_id , uint32_t period )

This function set the period value for a PTI timer identified by its index in the local ICU device descriptor. The <core> argument is local pointer on the target core. The <pti_id> argument is the timer index. The <period> argument is the period value (number of cycles). It returns 0 if success / returns EINVAL if illegal arguments.

5) error_t dev_icu_send_ipi( cxy_t cxy , lid_t lid )

This function send an IPI (Inter Processor Interrupt) to a core identified by its cluster identifier and local index. This IPI force a context switch, and the handling of pending RPC(s). The <cxy> argument is the destination cluster identifier. The <lid> argument is the destination core local index. It returns 0 if success / returns EINVAL if illegal arguments.

6) void dev_icu_irq_handler()

This function is called by a core when it receives an IRQ from the ICU device. It access the local ICU device descriptor to get the highest priority active IRQ of each type (HWI / WTI / PTI).

  • If it is a WTI or an HWI, the function uses the calling core interrupt-vectors to call the relevant ISR, registered in the source device descriptor.
  • If it is a PTI, the source device descriptor is the ICU device itself, and it call the core_clock() function to execute all operations related to the TICK event. /