source: rtems-docs/porting/interrupts.rst @ 42d50d7

5
Last change on this file since 42d50d7 was 7497f5e, checked in by Joel Sherrill <joel@…>, on 10/28/16 at 20:57:11

porting: Review and tidy up multiple formatting issues.

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