source: rtems/doc/porting/interrupts.t @ eed01493

4.104.114.84.95
Last change on this file since eed01493 was eed01493, checked in by Joel Sherrill <joel.sherrill@…>, on 02/11/03 at 23:44:41

2003-02-11 Joel Sherrill <joel@…>

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