日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

(原创)eCos驱动分析 之 ISR是如何与硬件中断联系起来的?

發布時間:2023/12/15 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 (原创)eCos驱动分析 之 ISR是如何与硬件中断联系起来的? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
要想知道ecos的中斷ISR是怎么與硬件中斷向量聯系起來的,是怎么被調用的?那就要看下面這兩個關鍵的函數:cyg_drv_interrupt_create()cyg_drv_interrupt_attach()
這兩個函數都聲明在cyg/kernel/kapi.h中,其形式如下:
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;
(注: __THROW是在C++中用的,是用來拋出異常的,詳見我的博文http://keendawn.blog.163.com/blog/static/888807432011611193510/這里可以視而不見.)其中文意義對照如下: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這個類來完成,那就來對Cyg_Interrupt探個究竟吧:(注意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 prioritycyg_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 DSRsstatic cyg_bool ? ? DSRs_pending();
// Call any pending DSRsstatic 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_TABLEstatic 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 madevolatile cyg_ucount32 dsr_count CYGBLD_ANNOTATE_VARIABLE_INTR;?
// next DSR in listCyg_Interrupt* volatile next_dsr CYGBLD_ANNOTATE_VARIABLE_INTR;?
// static list of pending DSRsstatic Cyg_Interrupt* volatile dsr_list[CYGNUM_KERNEL_CPU_MAX]CYGBLD_ANNOTATE_VARIABLE_INTR;#endif
#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 vectorstatic cyg_uint32 chain_isr(cyg_vector vector, CYG_ADDRWORD data); ? ?
// Table of interrupt chainsstatic 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 tocyg_priority ? ?priority, ? ? ? // Queue priorityCYG_ADDRWORD ? ?data, ? ? ? ? ? // Data pointercyg_ISR ? ? ? ? *isr, ? ? ? ? ? // Interrupt Service Routinecyg_DSR ? ? ? ? *dsr ? ? ? ? ? ?// Deferred Service Routine);
~Cyg_Interrupt();// ISR return valuesenum {HANDLED ?= 1, ? ? ? ? ? ? ? ? ? // Interrupt was handledCALL_DSR = 2 ? ? ? ? ? ? ? ? ? ?// Schedule DSR};
// Interrupt managementvoid ? ? ? ?attach(); ? ? ? ? ? ? ? // Attach to vector

void ? ? ? ?detach(); ? ? ? ? ? ? ? // Detach from vector// Static Interrupt management functions
// Get the current service routinestatic void get_vsr(cyg_vector vector, cyg_VSR **vsr);
// Install a vector service routinestatic void set_vsr(cyg_vector vector, ? ? ? ? ? ? ?// hardware vector to replacecyg_VSR *vsr, ? ? ? ? ? ? ? ? ? // my new service routinecyg_VSR **old = NULL ? ? ? ? ? ?// pointer to old vsr, if required);

// Static interrupt masking functions
// Disable interrupts at the CPUstatic void disable_interrupts();
// Re-enable CPU interruptsstatic 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 callsinline cyg_vector get_vector()?{return vector;}// Static PIC control functions// Mask a specific interrupt in a PICstatic void mask_interrupt(cyg_vector vector);// The same but not interrupt safestatic void mask_interrupt_intunsafe(cyg_vector vector);
// Clear PIC maskstatic void unmask_interrupt(cyg_vector vector);// The same but not interrupt safestatic void unmask_interrupt_intunsafe(cyg_vector vector);
// Acknowledge interrupt at PICstatic void acknowledge_interrupt(cyg_vector vector);
// Change interrupt detection at PICstatic void configure_interrupt(cyg_vector vector, ? ? ? ? ? ? ?// vector to controlcyg_bool level, ? ? ? ? ? ? ? ? // level or edge triggeredcyg_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 ? ?};
這只是聲明了這個class,這個class的構造/析構函數和成員函數的實現,還要找cyg/kernel/intr.hxx頭文件相對應的C文件,在這里....\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 tocyg_priority ? ?pri, ? ? ? ? ? ? ? ?// Queue priorityCYG_ADDRWORD ? ?d, ? ? ? ? ? ? ? ? ?// Data pointercyg_ISR ? ? ? ? *ir, ? ? ? ? ? ? ? ?// Interrupt Service Routinecyg_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函數傳進來的 中斷號、ISR、DSR等 分配給類的成員變量,好像也沒什么特別的。看來整個cyg_interrupt_create函數也就是在構造這個類對象了。
這樣重要的好戲是在cyg_interrupt_attach函數里完成了,看cyg_interrupt_attach的源代碼,只一行,再次列出如下:void cyg_interrupt_attach( cyg_handle_t interrupt ) __THROW{((Cyg_Interrupt *)interrupt)->attach();}它也就是調用了class Cyg_Interrupt的attach成員函數,那我們到intr.cxx中看看attach這個成員函數都干了些啥?// -------------------------------------------------------------------------// Attach an ISR to an interrupt vector.
voidCyg_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;// 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;

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();}
attach成員函數又調用了 HAL_INTERRUPT_ATTACH 這個HAL層的宏,那就繼續追蹤這個宏吧,
它定義在<cyg/hal/hal_intr.h>里,如下:
#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(注: CYG_MACRO_START 和 CYG_MACRO_END 宏定義在<cyg/infra/cyg_type.h>中L176 ? ?#define CYG_MACRO_START do {L177 ? ?#define CYG_MACRO_END ? } while (0))
這個宏的主要干了3件事,以中斷號作為索引,將 isr地址、data 和 0bject地址(也就是class Cyg_Interrupt的對象) 3者分別存入hal_interrupt_handlers、hal_interrupt_data and hal_interrupt_objects 3個數組中。
這3個數組定義在hal_intr.h頭文件相對應的hal_intr.c 文件中,這個C文件也沒干其他事,就只定義了這3個全局數組,如下:
// 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];
最關鍵的就是hal_interrupt_handlers數組,它放的就是所有終端ISR的地址。
但是,追蹤到這個數組,好像也就追不下去了,這樣的一個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, 2L475 ? ?/** 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, r15ldw 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, r15ldw 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, -4stw r15, (sp)L502 ? ?callr r1ldw r15, (sp)L504 ? ?addi sp, sp, 4
你看,r4中存有要相應的中斷號,它又存入r15中,根據r15的值,在hal_interrupt_handlers數組中得到相應的ISR地址存入r1中,在L502行callr r1,就調用了中斷ISR。
至此,層層追蹤結束,大功告成。終于搞明白了ecos中ISR是怎么響應到硬件中斷事件的。其實,剝繭抽絲,層層追蹤,兜兜轉轉,你會發現class Cyg_Interrupt這個C++類,只是做了一個高層的包裝,真正起作用的還是底層HAL相關的代碼。完!

總結

以上是生活随笔為你收集整理的(原创)eCos驱动分析 之 ISR是如何与硬件中断联系起来的?的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。