source: rtems-docs/porting/interrupts.rst @ 12dccfe

5
Last change on this file since 12dccfe was 12dccfe, checked in by Sebastian Huber <sebastian.huber@…>, on 01/09/19 at 15:14:05

Remove superfluous "All rights reserved."

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