source: rtems-docs/porting/interrupts.rst @ 2b175c3

4.115
Last change on this file since 2b175c3 was 489740f, checked in by Chris Johns <chrisj@…>, on 05/20/16 at 02:47:09

Set SPDX License Identifier in each source file.

  • Property mode set to 100644
File size: 15.2 KB
Line 
1.. comment SPDX-License-Identifier: CC-BY-SA-4.0
2
3Interrupts
4##########
5
6Introduction
7============
8
9Interrupt Levels
10================
11
12RTEMS is designed assuming that a CPU family has a level associated with
13interrupts.  Interrupts below the current interrupt level are masked and
14do not interrupt the CPU until the interrupt level is lowered.  This
15design provides for 256 distinct interrupt levels even though most CPU
16implementations support far fewer levels.  Interrupt level 0 is assumed to
17map to the hardware settings for all interrupts enabled.
18
19Over the years that RTEMS has been available, there has been much
20discussion on how to handle CPU families which support very few interrupt
21levels such as the i386, PowerPC, and HP-PA RISC. XXX
22
23Interrupt Level Mask
24--------------------
25
26The CPU_MODES_INTERRUPT_MASK macro defines the number of bits actually used in the interrupt field of the task mode.  How those bits map to the CPU interrupt levels is defined by the routine _CPU_ISR_Set_level().
27
28The following illustrates how the CPU_MODES_INTERRUPT_MASK is set on a CPU
29family like the Intel i386 where the CPU itself only recognizes two
30interrupt levels - enabled and disabled.
31.. code:: c
32
33    #define CPU_MODES_INTERRUPT_MASK   0x00000001
34
35Obtaining the Current Interrupt Level
36-------------------------------------
37
38The _CPU_ISR_Get_level function returns the current interrupt level.
39.. code:: c
40
41    uint32_t _CPU_ISR_Get_level( void )
42
43Set the Interrupt Level
44-----------------------
45
46The _CPU_ISR_Set_level routine maps the interrupt level in the Classic API
47task mode onto the hardware that the CPU actually provides.  Currently,
48interrupt levels that do not map onto the CPU in a generic fashion are
49undefined.  Someday, it would be nice if these were "mapped" by the
50application via a callout.  For example, the Motorola m68k has 8 levels 0
51- 7, and levels 8 - 255 are currently undefined.  Levels 8 - 255 would be
52available for bsp/application specific meaning. This could be used to
53manage a programmable interrupt controller via the rtems_task_mode
54directive.
55
56The following is a dummy implementation of the _CPU_ISR_Set_level routine:
57.. code:: c
58
59    #define _CPU_ISR_Set_level( new_level ) \\
60    { \\
61    }
62
63The following is the implementation from the Motorola M68K:
64.. code:: c
65
66    XXX insert m68k implementation here
67
68Disable Interrupts
69------------------
70
71The _CPU_ISR_Disable routine disable all external interrupts.  It returns
72the previous interrupt level in the single parameter _isr_cookie.  This
73routine is used to disable interrupts during a critical section in the
74RTEMS executive.  Great care is taken inside the executive to ensure that
75interrupts are disabled for a minimum length of time.  It is important to
76note that the way the previous level is returned forces the implementation
77to be a macro that translates to either inline assembly language or a
78function call whose return value is placed into _isr_cookie.
79
80It is important for the porter to realize that the value of _isr_cookie
81has no defined meaning except that it is the most convenient format for
82the _CPU_ISR_Disable, _CPU_ISR_Enable, and _CPU_ISR_Disable routines to
83manipulate.  It is typically the contents of the processor status
84register.  It is NOT the same format as manipulated by the
85_CPU_ISR_Get_level and _CPU_ISR_Set_level routines. The following is a
86dummy implementation that simply sets the previous level to 0.
87.. code:: c
88
89    #define _CPU_ISR_Disable( _isr_cookie ) \\
90    { \\
91    (_isr_cookie) = 0;   /* do something to prevent warnings \*/ \\
92    }
93
94The following is the implementation from the Motorola M68K port:
95.. code:: c
96
97    XXX insert m68k port here
98
99Enable Interrupts
100-----------------
101
102The _CPU_ISR_Enable routines enables interrupts to the previous level
103(returned by _CPU_ISR_Disable).  This routine is invoked at the end of an
104RTEMS critical section to reenable interrupts.  The parameter _level is
105not modified but indicates that level that interrupts should be enabled
106to.  The following illustrates a dummy implementation of the
107_CPU_ISR_Enable routine:
108.. code:: c
109
110    #define _CPU_ISR_Enable( _isr_cookie )  \\
111    { \\
112    }
113
114The following is the implementation from the Motorola M68K port:
115.. code:: c
116
117    XXX insert m68k version here
118
119Flash Interrupts
120----------------
121
122The _CPU_ISR_Flash routine temporarily restores the interrupt to _level
123before immediately disabling them again.  This is used to divide long
124RTEMS critical sections into two or more parts.  This routine is always
125preceded by a call to _CPU_ISR_Disable and followed by a call to
126_CPU_ISR_Enable.  The parameter _level is not modified.
127
128The following is a dummy implementation of the _CPU_ISR_Flash routine:
129.. code:: c
130
131    #define _CPU_ISR_Flash( _isr_cookie ) \\
132    { \\
133    }
134
135The following is the implementation from the Motorola M68K port:
136.. code:: c
137
138    XXX insert m68k version here
139
140Interrupt Stack Management
141==========================
142
143Hardware or Software Managed Interrupt Stack
144--------------------------------------------
145
146The setting of the CPU_HAS_SOFTWARE_INTERRUPT_STACK indicates whether the
147interrupt stack is managed by RTEMS in software or the CPU has direct
148support for an interrupt stack.  If RTEMS is to manage a dedicated
149interrupt stack in software, then this macro should be set to TRUE and the
150memory for the software managed interrupt stack is allocated in``_ISR_Handler_initialization``.  If this macro is set to FALSE, then
151RTEMS assumes that the hardware managed interrupt stack is supported by
152this CPU.  If the CPU has a hardware managed interrupt stack, then the
153porter has the option of letting the BSP allcoate and initialize the
154interrupt stack or letting RTEMS do this.  If RTEMS is to allocate the
155memory for the interrupt stack, then the macro
156CPU_ALLOCATE_INTERRUPT_STACK should be set to TRUE.  If this macro is set
157to FALSE, then it is the responsibility of the BSP to allocate the memory
158for this stack and initialize it.
159
160If the CPU does not support a dedicated interrupt stack, then the porter
161has two options: (1) execute interrupts on the stack of the interrupted
162task, and (2) have RTEMS manage a dedicated interrupt stack.
163
164NOTE: If CPU_HAS_SOFTWARE_INTERRUPT_STACK is TRUE, then the macro
165CPU_ALLOCATE_INTERRUPT_STACK should also be set to TRUE.
166
167Only one of CPU_HAS_SOFTWARE_INTERRUPT_STACK and
168CPU_HAS_HARDWARE_INTERRUPT_STACK should be set to TRUE.  It is possible
169that both are FALSE for a particular CPU.  Although it is unclear what
170that would imply about the interrupt processing procedure on that CPU.
171
172Allocation of Interrupt Stack Memory
173------------------------------------
174
175Whether or not the interrupt stack is hardware or software managed, RTEMS
176may allocate memory for the interrupt stack from the Executive Workspace.
177If RTEMS is going to allocate the memory for a dedicated interrupt stack
178in the Interrupt Manager, then the macro CPU_ALLOCATE_INTERRUPT_STACK
179should be set to TRUE.
180
181NOTE: This should be TRUE is CPU_HAS_SOFTWARE_INTERRUPT_STACK is TRUE.
182.. code:: c
183
184    #define CPU_ALLOCATE_INTERRUPT_STACK TRUE
185
186If the CPU_HAS_SOFTWARE_INTERRUPT_STACK macro is set to TRUE, then RTEMS automatically allocates the stack memory in the initialization of the Interrupt Manager and the switch to that stack is performed in ``_ISR_Handler`` on the outermost interrupt.  The _CPU_Interrupt_stack_low and _CPU_Interrupt_stack_high variables contain the addresses of the the lowest and highest addresses of the memory allocated for the interrupt stack.  Although technically only one of these addresses is required to switch to the interrupt stack, by always providing both addresses, the port has more options avaialble to it without requiring modifications to the portable parts of the executive.  Whether the stack  grows up or down, this give the CPU dependent code the option of picking the version it wants to use.
187.. code:: c
188
189    SCORE_EXTERN void               \*_CPU_Interrupt_stack_low;
190    SCORE_EXTERN void               \*_CPU_Interrupt_stack_high;
191
192NOTE: These two variables are required if the macro
193CPU_HAS_SOFTWARE_INTERRUPT_STACK is defined as TRUE.
194
195Install the Interrupt Stack
196---------------------------
197
198The _CPU_Install_interrupt_stack routine XXX
199
200This routine installs the hardware interrupt stack pointer.
201
202NOTE:  It need only be provided if CPU_HAS_HARDWARE_INTERRUPT_STAC is TRUE.
203.. code:: c
204
205    void _CPU_Install_interrupt_stack( void )
206
207ISR Installation
208================
209
210Install a Raw Interrupt Handler
211-------------------------------
212
213The _CPU_ISR_install_raw_handler XXX
214.. code:: c
215
216    void _CPU_ISR_install_raw_handler(
217    unsigned32  vector,
218    proc_ptr    new_handler,
219    proc_ptr   \*old_handler
220    )
221
222This is where we install the interrupt handler into the "raw" interrupt
223table used by the CPU to dispatch interrupt handlers.
224
225Interrupt Context
226-----------------
227
228Maximum Number of Vectors
229-------------------------
230
231There are two related macros used to defines the number of entries in the
232_ISR_Vector_table managed by RTEMS.  The macro
233CPU_INTERRUPT_NUMBER_OF_VECTORS is the actual number of vectors supported
234by this CPU model.  The second macro is the
235CPU_INTERRUPT_MAXIMUM_VECTOR_NUMBER.  Since the table is zero-based, this
236indicates the highest vector number which can be looked up in the table
237and mapped into a user provided handler.
238.. code:: c
239
240    #define CPU_INTERRUPT_NUMBER_OF_VECTORS      32
241    #define CPU_INTERRUPT_MAXIMUM_VECTOR_NUMBER \\
242    (CPU_INTERRUPT_NUMBER_OF_VECTORS - 1)
243
244Install RTEMS Interrupt Handler
245-------------------------------
246
247The _CPU_ISR_install_vector routine installs the RTEMS handler for the
248specified vector.
249
250XXX Input parameters:
251vector      - interrupt vector number
252old_handler - former ISR for this vector number
253new_handler - replacement ISR for this vector number
254.. code:: c
255
256    void _CPU_ISR_install_vector(
257    unsigned32  vector,
258    proc_ptr    new_handler,
259    proc_ptr   \*old_handler
260    )
261
262.. code:: c
263
264    *old_handler = _ISR_Vector_table[ vector ];
265
266If the interrupt vector table is a table of pointer to isr entry points,
267then we need to install the appropriate RTEMS interrupt handler for this
268vector number.
269.. code:: c
270
271    _CPU_ISR_install_raw_handler( vector, new_handler, old_handler );
272
273We put the actual user ISR address in _ISR_vector_table.  This will be
274used by the ``_ISR_Handler`` so the user gets control.
275.. code:: c
276
277    _ISR_Vector_table[ vector ] = new_handler;
278
279Interrupt Processing
280====================
281
282Interrupt Frame Data Structure
283------------------------------
284
285When an interrupt occurs, it is the responsibility of the interrupt
286dispatching software to save the context of the processor such that an ISR
287written in a high-level language (typically C) can be invoked without
288damaging the state of the task that was interrupted.  In general, this
289results in the saving of registers which are NOT preserved across
290subroutine calls as well as any special interrupt state.  A port should
291define the ``CPU_Interrupt_frame`` structure so that application code can
292examine the saved state.
293.. code:: c
294
295    typedef struct {
296    unsigned32 not_preserved_register_1;
297    unsigned32 special_interrupt_register;
298    } CPU_Interrupt_frame;
299
300Interrupt Dispatching
301---------------------
302
303The ``_ISR_Handler`` routine provides the RTEMS interrupt management.
304.. code:: c
305
306    void _ISR_Handler()
307
308This discussion ignores a lot of the ugly details in a real implementation
309such as saving enough registers/state to be able to do something real.
310Keep in mind that the goal is to invoke a user's ISR handler which is
311written in C.  That ISR handler uses a known set of registers thus
312allowing the ISR to preserve only those that would normally be corrupted
313by a subroutine call.
314
315Also note that the exact order is to a large extent flexible.  Hardware
316will dictate a sequence for a certain subset of ``_ISR_Handler`` while
317requirements for setting the RTEMS state variables that indicate the
318interrupt nest level (``_ISR_Nest_level``) and dispatching disable
319level (``_Thread_Dispatch_disable_level``) will also
320restrict the allowable order.
321
322Upon entry to ``_ISR_Handler``, ``_Thread_Dispatch_disable_level`` is
323zero if the interrupt occurred while outside an RTEMS service call.
324Conversely, it will be non-zero if interrupting an RTEMS service
325call.  Thus, ``_Thread_Dispatch_disable_level`` will always be
326greater than or equal to ``_ISR_Nest_level`` and not strictly
327equal.
328
329Upon entry to the "common" ``_ISR_Handler``, the vector number must be
330available.  On some CPUs the hardware puts either the vector number or the
331offset into the vector table for this ISR in a known place.  If the
332hardware does not provide this information, then the assembly portion of
333RTEMS for this port will contain a set of distinct interrupt entry points
334which somehow place the vector number in a known place (which is safe if
335another interrupt nests this one) and branches to ``_ISR_Handler``.
336.. code:: c
337
338    save some or all context on stack
339    may need to save some special interrupt information for exit
340    #if ( CPU_HAS_SOFTWARE_INTERRUPT_STACK == TRUE )
341    if ( _ISR_Nest_level == 0 )
342    switch to software interrupt stack
343    #endif
344    _ISR_Nest_level++;
345    _Thread_Dispatch_disable_level++;
346    (\*_ISR_Vector_table[ vector ])( vector );
347    --_ISR_Nest_level;
348    if ( _ISR_Nest_level )
349    goto the label "exit interrupt (simple case)"
350    #if ( CPU_HAS_SOFTWARE_INTERRUPT_STACK == TRUE )
351    restore stack
352    #endif
353    if ( _Thread_Dispatch_disable_level )
354    goto the label "exit interrupt (simple case)"
355    if ( _Thread_Dispatch_necessary )
356    call _Thread_Dispatch() or prepare to return to _ISR_Dispatch
357    prepare to get out of interrupt
358    return from interrupt  (maybe to _ISR_Dispatch)
359    LABEL "exit interrupt (simple case):
360    prepare to get out of interrupt
361    return from interrupt
362
363Some ports have the special routine ``_ISR_Dispatch`` because
364the CPU has a special "interrupt mode" and RTEMS must switch back
365to the task stack and/or non-interrupt mode before invoking``_Thread_Dispatch``.  For example, consider the MC68020 where
366upon return from the outermost interrupt, the CPU must switch
367from the interrupt stack to the master stack before invoking``_Thread_Dispatch``.  ``_ISR_Dispatch`` is the special port
368specific wrapper for ``_Thread_Dispatch`` used in this case.
369
370ISR Invoked with Frame Pointer
371------------------------------
372
373Does the RTEMS invoke the user's ISR with the vector number and a pointer
374to the saved interrupt frame (1) or just the vector number (0)?
375.. code:: c
376
377    #define CPU_ISR_PASSES_FRAME_POINTER 0
378
379NOTE: It is desirable to include a pointer to the interrupt stack frame as
380an argument to the interrupt service routine.  Eventually, it would be
381nice if all ports included this parameter.
382
383Pointer to _Thread_Dispatch Routine
384-----------------------------------
385
386With some compilation systems, it is difficult if not impossible to call a
387high-level language routine from assembly language.  This is especially
388true of commercial Ada compilers and name mangling C++ ones.  This
389variable can be optionally defined by the CPU porter and contains the
390address of the routine _Thread_Dispatch.  This can make it easier to
391invoke that routine at the end of the interrupt sequence (if a dispatch is
392necessary).
393.. code:: c
394
395    void (\*_CPU_Thread_dispatch_pointer)();
396
397.. COMMENT: COPYRIGHT (c) 1988-2002.
398
399.. COMMENT: On-Line Applications Research Corporation (OAR).
400
401.. COMMENT: All rights reserved.
402
Note: See TracBrowser for help on using the repository browser.