source: rtems-docs/porting/interrupts.rst @ 3a71759

4.115
Last change on this file since 3a71759 was 6733466, checked in by Amar Takhar <amar@…>, on 01/17/16 at 00:08:48

Split document into seperate files by section.

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