Skip to content

Commit 11a3675

Browse files
Andrew BoieAnas Nashif
authored andcommitted
doc: add interrupt implementation details
Issue: ZEP-634 Signed-off-by: Andrew Boie <[email protected]>
1 parent 7482406 commit 11a3675

File tree

1 file changed

+124
-0
lines changed

1 file changed

+124
-0
lines changed

doc/kernel/other/interrupts.rst

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,130 @@ The following code demonstrates a direct ISR:
206206
...
207207
}
208208
209+
Implementation Details
210+
======================
211+
212+
Interrupt tables are set up at build time using some special build tools. The
213+
details laid out here apply to all architectures except x86, which are
214+
covered in the `x86 Details`_ section below.
215+
216+
Any invocation of :c:macro:`IRQ_CONNECT` will declare an instance of
217+
struct _isr_list which is placed in a special .intList section:
218+
219+
.. code-block:: c
220+
221+
struct _isr_list {
222+
/** IRQ line number */
223+
s32_t irq;
224+
/** Flags for this IRQ, see ISR_FLAG_* definitions */
225+
s32_t flags;
226+
/** ISR to call */
227+
void *func;
228+
/** Parameter for non-direct IRQs */
229+
void *param;
230+
};
231+
232+
Zephyr is built in two phases; the first phase of the build produces
233+
zephyr_prebuilt.elf which contains all the entries in the .intList section
234+
preceded by a header:
235+
236+
.. code-block:: c
237+
238+
struct {
239+
void *spurious_irq_handler;
240+
void *sw_irq_handler;
241+
u32_t num_isrs;
242+
u32_t num_vectors;
243+
struct _isr_list isrs[]; <- of size num_isrs
244+
};
245+
246+
This data consisting of the header and instances of struct _isr_list inside
247+
zephyr_prebuilt.elf is then used by the gen_isr_tables.py script to generate a
248+
C file defining a vector table and software ISR table that are then compiled
249+
and linked into the final application.
250+
251+
The priority level of any interrupt is not encoded in these tables, instead
252+
:c:macro:`IRQ_CONNECT` also has a runtime component which programs the desired
253+
priority level of the interrupt to the interrupt controller. Some architectures
254+
do not support the notion of interrupt priority, in which case the priority
255+
argument is ignored.
256+
257+
Vector Table
258+
------------
259+
A vector table is generated when CONFIG_GEN_IRQ_VECTOR_TABLE is enabled. This
260+
data structure is used natively by the CPU and is simply an array of function
261+
pointers, where each element n corresponds to the IRQ handler for IRQ line n,
262+
and the function pointers are:
263+
264+
#. For 'direct' interrupts declared with :c:macro:`IRQ_DIRECT_CONNECT`, the
265+
handler function will be placed here.
266+
#. For regular interrupts declared with :c:macro:`IRQ_CONNECT`, the address
267+
of the common software IRQ handler is placed here. This code does common
268+
kernel interrupt bookkeeping and looks up the ISR and parameter from the
269+
software ISR table.
270+
#. For interrupt lines that are not configured at all, the address of the
271+
spurious IRQ handler will be placed here. The spurious IRQ handler
272+
causes a system fatal error if encountered.
273+
274+
Some architectures (such as the Nios II internal interrupt controller) have a
275+
common entry point for all interrupts and do not support a vector table, in
276+
which case the CONFIG_GEN_IRQ_VECTOR_TABLE option should be disabled.
277+
278+
Some architectures may reserve some initial vectors for system exceptions
279+
and declare this in a table elsewhere, in which case
280+
CONFIG_GEN_IRQ_START_VECTOR needs to be set to properly offset the indicies
281+
in the table.
282+
283+
SW ISR Table
284+
------------
285+
This is an array of struct _isr_table_entry:
286+
287+
.. code-block:: c
288+
289+
struct _isr_table_entry {
290+
void *arg;
291+
void (*isr)(void *);
292+
};
293+
294+
This is used by the common software IRQ handler to look up the ISR and its
295+
argument and execute it. The active IRQ line is looked up in an interrupt
296+
controller register and used to index this table.
297+
298+
x86 Details
299+
-----------
300+
301+
The x86 architecture has a special type of vector table called the Interrupt
302+
Descriptor Table (IDT) which must be laid out in a certain way per the x86
303+
processor documentation. It is still fundamentally a vector table, and the
304+
gen_idt tool uses the .intList section to create it. However, on APIC-based
305+
systems the indexes in the vector table do not correspond to the IRQ line. The
306+
first 32 vectors are reserved for CPU exceptions, and all remaining vectors (up
307+
to index 255) correspond to the priority level, in groups of 16. In this
308+
scheme, interrupts of priority level 0 will be placed in vectors 32-47, level 1
309+
48-63, and so forth. When the gen_idt tool is constructing the IDT, when it
310+
configures an interrupt it will look for a free vector in the appropriate range
311+
for the requested priority level and set the handler there.
312+
313+
There are some APIC variants (such as MVIC) where priorities cannot be set
314+
by the user and the position in the vector table does correspond to the
315+
IRQ line. Systems like this will enable CONFIG_X86_FIXED_IRQ_MAPPING.
316+
317+
On x86 when an interrupt or exception vector is executed by the CPU, there is
318+
no foolproof way to determine which vector was fired, so a software ISR table
319+
indexed by IRQ line is not used. Instead, the :c:macro:`IRQ_CONNECT` call
320+
creates a small assembly language function which calls the common interrupt
321+
code in :cpp:func:`_interrupt_enter` with the ISR and parameter as arguments.
322+
It is the address of this assembly interrupt stub which gets placed in the IDT.
323+
For interrupts declared with :c:macro:`IRQ_DIRECT_CONNECT` the parameterless
324+
ISR is placed directly in the IDT.
325+
326+
On systems where the position in the vector table corresponds to the
327+
interrupt's priority level, the interrupt controller needs to know at
328+
runtime what vector is associated with an IRQ line. gen_idt additionally
329+
creates an _irq_to_interrupt_vector array which maps an IRQ line to its
330+
configured vector in the IDT. This is used at runtime by :c:macro:`IRQ_CONNECT`
331+
to program the IRQ-to-vector association in the interrupt controller.
332+
209333
Suggested Uses
210334
**************
211335

0 commit comments

Comments
 (0)