本文整理自:http://keendawn.blog.163.com/blog/static/8888074320116205833478/
要想知道ecos的中断ISR是怎么与硬件中断向量联系起来的,是怎么被调用的?
那就要看下面这两个关键的函数:
- cyg_drv_interrupt_create()
- cyg_drv_interrupt_attach()
- void cyg_interrupt_create(
- cyg_vector_t vector, /* Vector to attach to */
- cyg_priority_t priority, /* Queue priority */
- cyg_addrword_t data, /* Data pointer */
- cyg_ISR_t *isr, /* Interrupt Service Routine */
- cyg_DSR_t *dsr, /* Deferred Service Routine */
- cyg_handle_t *handle, /* returned handle */
- cyg_interrupt *intr /* put interrupt here */
- ) __THROW;
- void cyg_interrupt_attach( cyg_handle_t interrupt ) __THROW;
其中文意义对照如下:
cyg_interrupt_create(
中断号,
中断优先级,
传递的中断参数,
ISR函数,
DSR函数,
被返回的中断句柄,
存放与此中断相关的内核数据的变量空间);
cyg_interrupt_attach(中断句柄);
这样实际上去研究一下cyg_interrupt_create函数的定义内容,应该就能搞明白我们的问题了!
由于其函数声明在kapi.h中,很自然的就想到其定义应在kapi.c文件中。
找到 文件: ....\ecos\ecos-current\packages\kernel\current\src\common\kapi.cxx
找到这两个函数的定义如下:
- /*---------------------------------------------------------------------------*/
- /* Interrupt handling */
- externC void cyg_interrupt_create(
- cyg_vector_t vector, /* Vector to attach to */
- cyg_priority_t priority, /* Queue priority */
- cyg_addrword_t data, /* Data pointer */
- cyg_ISR_t *isr, /* Interrupt Service Routine */
- cyg_DSR_t *dsr, /* Deferred Service Routine */
- cyg_handle_t *handle, /* returned handle */
- cyg_interrupt *intr /* put interrupt here */
- ) __THROW
- {
- CYG_ASSERT_SIZES( cyg_interrupt, Cyg_Interrupt );
- Cyg_Interrupt *t = new((void *)intr) Cyg_Interrupt (
- (cyg_vector)vector,
- (cyg_priority)priority,
- (CYG_ADDRWORD)data,
- (cyg_ISR *)isr,
- (cyg_DSR *)dsr );
- t=t;
- CYG_CHECK_DATA_PTR( handle, "Bad handle pointer" );
- *handle = (cyg_handle_t)intr;
- }
- void cyg_interrupt_attach( cyg_handle_t interrupt ) __THROW
- {
- ((Cyg_Interrupt *)interrupt)->attach();
- }
注意:Cyg_Interrupt是个C++类,,可不要找成了struct cyg_interrupt,注意哟,cyg_interrupt_create函数的最后一个参数就是这个cyg_interrupt struct类型的,在cyg/kernel/kapidata.h中有个struct cyg_interrupt定义,虽然名字和内容都很相似,但实际上不是。
真正的class Cyg_Interrupt定义在cyg/kernel/intr.hxx中,这个头文件没干别的,就是声明这个class了,可见这是一个很大的class,如下:
- // -------------------------------------------------------------------------
- // Interrupt class. This both represents each interrupt and provides a static
- // interface for controlling the interrupt hardware.
- class Cyg_Interrupt
- {
- friend class Cyg_Scheduler;
- friend void interrupt_end( cyg_uint32,
- Cyg_Interrupt *,
- HAL_SavedRegisters *);
- friend void cyg_interrupt_post_dsr( CYG_ADDRWORD intr_obj );
- friend void cyg_interrupt_call_pending_DSRs( void );
-
- cyg_vector vector; // Interrupt vector
- cyg_priority priority; // Queuing priority
-
- cyg_ISR *isr; // Pointer to ISR
- cyg_DSR *dsr; // Pointer to DSR
- CYG_ADDRWORD data; // Data pointer
-
- // DSR handling interface called by the scheduler
- // Check for pending DSRs
- static cyg_bool DSRs_pending();
- // Call any pending DSRs
- static void call_pending_DSRs();
- static void call_pending_DSRs_inner();
- // DSR handling interface called by the scheduler and HAL
- // interrupt arbiters.
- void post_dsr(); // Post the DSR for this interrupt
-
- // Data structures for handling DSR calls. We implement two DSR
- // handling mechanisms, a list based one and a table based
- // one. The list based mechanism is safe with respect to temporary
- // overloads and will not run out of resource. However it requires
- // extra data per interrupt object, and interrupts must be turned
- // off briefly when delivering the DSR. The table based mechanism
- // does not need unnecessary interrupt switching, but may be prone
- // to overflow on overload. However, since a correctly programmed
- // real time application should not experience such a condition,
- // the table based mechanism is more efficient for real use. The
- // list based mechainsm is enabled by default since it is safer to
- // use during development.
- #ifdef CYGIMP_KERNEL_INTERRUPTS_DSRS_TABLE
-
- static Cyg_Interrupt *dsr_table[CYGNUM_KERNEL_CPU_MAX]
- [CYGNUM_KERNEL_INTERRUPTS_DSRS_TABLE_SIZE]
- CYGBLD_ANNOTATE_VARIABLE_INTR;
- static cyg_ucount32 dsr_table_head[CYGNUM_KERNEL_CPU_MAX]
- CYGBLD_ANNOTATE_VARIABLE_INTR;
- static volatile cyg_ucount32 dsr_table_tail[CYGNUM_KERNEL_CPU_MAX]
- CYGBLD_ANNOTATE_VARIABLE_INTR;
- #endif
- #ifdef CYGIMP_KERNEL_INTERRUPTS_DSRS_LIST
- // Number of DSR posts made
- volatile cyg_ucount32 dsr_count CYGBLD_ANNOTATE_VARIABLE_INTR;
- // next DSR in list
- Cyg_Interrupt* volatile next_dsr CYGBLD_ANNOTATE_VARIABLE_INTR;
- // head of static list of pending DSRs
- static Cyg_Interrupt* volatile dsr_list[CYGNUM_KERNEL_CPU_MAX]
- CYGBLD_ANNOTATE_VARIABLE_INTR;
- # ifdef CYGSEM_KERNEL_INTERRUPTS_DSRS_LIST_FIFO
- // tail of static list of pending DSRs
- static Cyg_Interrupt* volatile dsr_list_tail[CYGNUM_KERNEL_CPU_MAX]
- CYGBLD_ANNOTATE_VARIABLE_INTR;
- # endif
- #endif // defined CYGIMP_KERNEL_INTERRUPTS_DSRS_LIST
- #ifdef CYGIMP_KERNEL_INTERRUPTS_CHAIN
- // The default mechanism for handling interrupts is to attach just
- // one Interrupt object to each vector. In some cases, and on some
- // hardware, this is not possible, and each vector must carry a chain
- // of interrupts.
- Cyg_Interrupt *next; // Next Interrupt in list
- // Chaining ISR inserted in HAL vector
- static cyg_uint32 chain_isr(cyg_vector vector, CYG_ADDRWORD data);
- // Table of interrupt chains
- static Cyg_Interrupt *chain_list[CYGNUM_HAL_ISR_TABLE_SIZE];
-
- #endif
- // Interrupt disable data. Interrupt disable can be nested. On
- // each CPU this is controlled by disable_counter[cpu]. When the
- // counter is first incremented from zero to one, the
- // interrupt_disable_spinlock is claimed using spin_intsave(), the
- // original interrupt enable state being saved in
- // interrupt_disable_state[cpu]. When the counter is decremented
- // back to zero the spinlock is cleared using clear_intsave().
- // The spinlock is necessary in SMP systems since a thread
- // accessing data shared with an ISR may be scheduled on a
- // different CPU to the one that handles the interrupt. So, merely
- // blocking local interrupts would be ineffective. SMP aware
- // device drivers should either use their own spinlocks to protect
- // data, or use the API supported by this class, via
- // cyg_drv_isr_lock()/_unlock(). Note that it now becomes
- // essential that ISRs do this if they are to be SMP-compatible.
- // In a single CPU system, this mechanism reduces to just
- // disabling/enabling interrupts.
- // Disable level counter. This counts the number of times
- // interrupts have been disabled.
- static volatile cyg_int32 disable_counter[CYGNUM_KERNEL_CPU_MAX]
- CYGBLD_ANNOTATE_VARIABLE_INTR;
- // Interrupt disable spinlock. This is claimed by any CPU that has
- // disabled interrupts via the Cyg_Interrupt API.
- static Cyg_SpinLock interrupt_disable_spinlock CYGBLD_ANNOTATE_VARIABLE_INTR;
- // Saved interrupt state. When each CPU first disables interrupts
- // the original state of the interrupts are saved here to be
- // restored later.
- static CYG_INTERRUPT_STATE interrupt_disable_state[CYGNUM_KERNEL_CPU_MAX]
- CYGBLD_ANNOTATE_VARIABLE_INTR;
-
- public:
- Cyg_Interrupt // Initialize interrupt
- (
- cyg_vector vector, // Vector to attach to
- cyg_priority priority, // Queue priority
- CYG_ADDRWORD data, // Data pointer
- cyg_ISR *isr, // Interrupt Service Routine
- cyg_DSR *dsr // Deferred Service Routine
- );
- ~Cyg_Interrupt();
-
- // ISR return values
- enum {
- HANDLED = 1, // Interrupt was handled
- CALL_DSR = 2 // Schedule DSR
- };
- // Interrupt management
-
- void attach(); // Attach to vector
- void detach(); // Detach from vector
-
-
- // Static Interrupt management functions
- // Get the current service routine
- static void get_vsr(cyg_vector vector, cyg_VSR **vsr);
- // Install a vector service routine
- static void set_vsr(
- cyg_vector vector, // hardware vector to replace
- cyg_VSR *vsr, // my new service routine
- cyg_VSR **old = NULL // pointer to old vsr, if required
- );
- // Static interrupt masking functions
- // Disable interrupts at the CPU
- static void disable_interrupts();
- // Re-enable CPU interrupts
- static void enable_interrupts();
- // Are interrupts enabled at the CPU?
- static inline cyg_bool interrupts_enabled()
- {
- return (0 == disable_counter[CYG_KERNEL_CPU_THIS()]);
- }
-
- // Get the vector for the following calls
- inline cyg_vector get_vector()
- {
- return vector;
- }
-
- // Static PIC control functions
-
- // Mask a specific interrupt in a PIC
- static void mask_interrupt(cyg_vector vector);
- // The same but not interrupt safe
- static void mask_interrupt_intunsafe(cyg_vector vector);
- // Clear PIC mask
- static void unmask_interrupt(cyg_vector vector);
- // The same but not interrupt safe
- static void unmask_interrupt_intunsafe(cyg_vector vector);
- // Acknowledge interrupt at PIC
- static void acknowledge_interrupt(cyg_vector vector);
- // Change interrupt detection at PIC
- static void configure_interrupt(
- cyg_vector vector, // vector to control
- cyg_bool level, // level or edge triggered
- cyg_bool up // hi/lo level, rising/falling edge
- );
- #ifdef CYGPKG_KERNEL_SMP_SUPPORT
- // SMP support for associating an interrupt with a specific CPU.
-
- static void set_cpu( cyg_vector, HAL_SMP_CPU_TYPE cpu );
- static HAL_SMP_CPU_TYPE get_cpu( cyg_vector );
-
- #endif
- };
在这里....\packages\kernel\current\src\intr\intr.cxx找到了intr.cxx文件,因为cyg_interrupt_create函数实际上调用了class Cyg_Interrupt的构造函数,我们就来看看这个构造函数:
- Cyg_Interrupt::Cyg_Interrupt(
- cyg_vector vec, // Vector to attach to
- cyg_priority pri, // Queue priority
- CYG_ADDRWORD d, // Data pointer
- cyg_ISR *ir, // Interrupt Service Routine
- cyg_DSR *dr // Deferred Service Routine
- )
- {
- CYG_REPORT_FUNCTION();
- CYG_REPORT_FUNCARG5("vector=%d, priority=%d, data=%08x, isr=%08x, "
- "dsr=%08x", vec, pri, d, ir, dr);
-
- vector = vec;
- priority = pri;
- isr = ir;
- dsr = dr;
- data = d;
- #ifdef CYGIMP_KERNEL_INTERRUPTS_DSRS_LIST
- dsr_count = 0;
- next_dsr = NULL;
- #endif
- #ifdef CYGIMP_KERNEL_INTERRUPTS_CHAIN
- next = NULL;
-
- #endif
- CYG_REPORT_RETURN();
-
- };
看来整个cyg_interrupt_create函数也就是在构造这个类对象了。
这样重要的好戏是在cyg_interrupt_attach函数里完成了,看cyg_interrupt_attach的源代码,只一行,再次列出如下:
- void cyg_interrupt_attach( cyg_handle_t interrupt ) __THROW
- {
- ((Cyg_Interrupt *)interrupt)->attach();
- }
- // -------------------------------------------------------------------------
- // Attach an ISR to an interrupt vector.
- void
- Cyg_Interrupt::attach(void)
- {
- CYG_REPORT_FUNCTION();
- CYG_ASSERT( vector >= CYGNUM_HAL_ISR_MIN, "Invalid vector");
- CYG_ASSERT( vector <= CYGNUM_HAL_ISR_MAX, "Invalid vector");
- CYG_INSTRUMENT_INTR(ATTACH, vector, 0);
- HAL_INTERRUPT_SET_LEVEL( vector, priority );
-
- #ifdef CYGIMP_KERNEL_INTERRUPTS_CHAIN
- CYG_ASSERT( next == NULL , "Cyg_Interrupt already on a list");
- cyg_uint32 index;
- HAL_TRANSLATE_VECTOR( vector, index );
- if( chain_list[index] == NULL )
- {
- int in_use CYGBLD_ATTRIB_UNUSED;
- // First Interrupt on this chain, just assign it and register
- // the chain_isr with the HAL.
-
- chain_list[index] = this;
- HAL_INTERRUPT_IN_USE( vector, in_use );
- CYG_ASSERT( 0 == in_use, "Interrupt vector not free.");
- HAL_INTERRUPT_ATTACH( vector, chain_isr, &chain_list[index], NULL );
- }
- else
- {
- // There are already interrupts chained, add this one into the
- // chain in priority order.
-
- Cyg_Interrupt **p = &chain_list[index];
- while( *p != NULL )
- {
- Cyg_Interrupt *n = *p;
- if( n->priority < priority ) break;
-
- p = &n->next;
- }
- next = *p;
- *p = this;
- }
-
- #else
-
- {
- int in_use CYGBLD_ATTRIB_UNUSED;
- HAL_INTERRUPT_IN_USE( vector, in_use );
- CYG_ASSERT( 0 == in_use, "Interrupt vector not free.");
- HAL_INTERRUPT_ATTACH( vector, isr, data, this );
- }
- #endif
- CYG_REPORT_RETURN();
- }
- #define HAL_INTERRUPT_ATTACH( _vector_, _isr_, _data_, _object_ ) \
- CYG_MACRO_START \
- cyg_uint32 _index_; \
- HAL_TRANSLATE_VECTOR( _vector_, _index_ ); \
- \
- if( hal_interrupt_handlers[_index_] == (CYG_ADDRESS)HAL_DEFAULT_ISR ) \
- { \
- hal_interrupt_handlers[_index_] = (CYG_ADDRESS)_isr_; \
- hal_interrupt_data[_index_] = (CYG_ADDRWORD)_data_; \
- hal_interrupt_objects[_index_] = (CYG_ADDRESS)_object_; \
- } \
- CYG_MACRO_END
- #define CYG_MACRO_START do {
- #define CYG_MACRO_END } while (0)
- // Create the interrupt handler table, with all handlers set to the 'safe'
- // default.
- volatile CYG_ADDRESS hal_interrupt_handlers[CYGNUM_HAL_ISR_COUNT] =
- {(CYG_ADDRESS)HAL_DEFAULT_ISR,
- (CYG_ADDRESS)HAL_DEFAULT_ISR,
- .
- .
- .
- .
- (CYG_ADDRESS)HAL_DEFAULT_ISR,
- (CYG_ADDRESS)HAL_DEFAULT_ISR};
- volatile CYG_ADDRWORD hal_interrupt_data[CYGNUM_HAL_ISR_COUNT];
- volatile CYG_ADDRESS hal_interrupt_objects[CYGNUM_HAL_ISR_COUNT];
但是,追踪到这个数组,好像也就追不下去了, 这样的一个ISR table,又是怎样被索引调用的呢?
我们只顾忙着追踪,应该回想一下,我们知道HAL层是ecos中和硬件结构相关的地址, 我们上面分析的部分HAL层代码实际上是具体于某一CPU架构的。 而我上面看的HAL代码就是NIOS II的HAL层,只是上面的一点HAL代码好像还看不出与CPU结构相关的特殊性。
那如果我们还要继续trace, 看来就必须要和CPU结构密切相关了,也能感觉的到是和硬件中断向量有关的东东, 那我们就以NIOS II为例继续追踪, 你会发现,和上面hal_intr.c文件同一目录下, ....\packages\hal\nios2\arch\current\src\ 有一个vector.S汇编文件,一看到它的名字,就有感觉,打开它,你会发现, 它分了4大块,分别定义了4种不同情况下,CPU发生异常or接收到中断时,要执行的代码,
- /*
- * ========================================================================
- * _hardware_reset
- *
- * This is the reset entry point for Nios II.
- *
- * At reset, only the cache line which contain the reset vector is
- * initialized. Therefore the code within the first cache line is required
- * to initialize the instruction cache for the remainder of the code.
- *
- * Note that the automatically generated linker script requires that
- * the .init section is less than 0x20 bytes long.
- */
- L106 _hardware_reset:
- /*
- * ========================================================================
- * _exception_vector
- *
- * This is the exception entry point. It is responsible for determing if the
- * exception was caused by an hardware interrupt, or a software exception.
- * It then vectors accordingly using the VSR table to handle the exception.
- */
- L283 _exception_vector:
- /*
- * ========================================================================
- * _interrupt_handler
- *
- * This is the default handler for hardware interrupts.
- */
- L342 _interrupt_handler::
- /*
- * ========================================================================
- * _software_exception_handler
- *
- * This is the default handler for software exceptions.
- */
- .globl _software_exception_handler
- .type _software_exception_handler, @function
- L613 _software_exception_handler:
- 顾名思义,我们要找的应该在_interrupt_handler中,果不其然,其中有如下一段代码:
- L465 /*
- * Having located the interrupt source, r4 contains the index of the
- * interrupt to be handled.
- *
- * This is converted into an offset into the handler table,
- * and stored in r15.
- */
-
- slli r15, r4, 2
-
- L475 /*
- * Load into r1 the address of the handler for this interrupt. This is
- * obtained from the interrupt handler table: hal_interrupt_handlers.
- */
-
- movhi r1, %hiadj(hal_interrupt_handlers)
- addi r1, r1, %lo(hal_interrupt_handlers)
- add r1, r1, r15
- ldw r1, (r1)
-
- /*
- * Load into r5 the data associated with the interrupt handler. This is
- * obtained from the table: hal_interrupt_data.
- */
-
- movhi r5, %hiadj(hal_interrupt_data)
- addi r5, r5, %lo(hal_interrupt_data)
- add r5, r5, r15
- ldw r5, (r5)
-
- /*
- * Call the interrupt handler The input arguments are the interrupt number
- * and the associated data obtained above. Save off r15 explicitly since
- * its a caller-saved register and is used below.
- */
- addi sp, sp, -4
- stw r15, (sp)
- L502 callr r1
- ldw r15, (sp)
- L504 addi sp, sp, 4
至此,层层追踪结束,大功告成。 终于搞明白了ecos中ISR是怎么响应到硬件中断事件的。 其实,剥茧抽丝,层层追踪,兜兜转转,你会发现class Cyg_Interrupt这个C++类,只是做了一个高层的包装,
真正起作用的还是底层HAL相关的代码。