本站首页    管理页面    写新日志    退出


«September 2025»
123456
78910111213
14151617181920
21222324252627
282930


公告

☆★☆★☆★☆★☆★☆
生活的点点记录,以及一些体会...........

喜欢是淡淡的爱,爱是深深的喜欢.

时间会见证一切.......................

欢迎大家指出错误,共同进步..........

期待中..............................

☆★☆★☆★☆★☆★☆


我的分类(专题)

日志更新

最新评论

留言板

链接

世纪音频

 

 


Blog信息
blog名称:
日志总数:162
评论数量:312
留言数量:0
访问次数:947092
建立时间:2005年5月17日




[wince study]Exception Process in WinCE & ARM(转
文章收藏

oceanblue 发表于 2008/5/8 12:39:21

今天INTEL的Application Engineer特地纠正了“中断向量表”的说法,应该叫Exception Vector.按我理解,应该只有IRQ和FIQ算做interrupt吧,像reset和几个abort叫做exception的确更合适些。 一般而言, 硬件的异常产生后,CPU将跳转到0x00000000地址访问中断向量表(normal exception vectors),  但ARM920T / ARM9 / ARM10 系列的CPU支持把中断向量表放到高地址0xFFFF0000(high exception vectors). 该跳转地址的决定因素为协处理器的CP15:BI13. 即CP15:BIT13 = 0时, 跳转到低地址; CP15:BIT13 = 1时, 跳转到高地址. 根据INTEL应用工程师的回答,该地址在MMU使能之前为physical address, 使能之后为virtual address, 该点有待确认. 由于WinCE在编译nk.nb0或xboot.nb0时, 和其他application使用了同一个Linker的缘故, 从0x00000000~0x00001000这段是reserved的,留作给application的PE头(Portable Executable  header, 什么用的我还没搞懂, MSDN上有详细说明). 而我们的代码从0x00001000这行开始放起.但在0x00000000有句跳转指令"b 1000". 其实这中间还有几行代码, 但不知道干什么用的,反正被跳过去不执行就是了. 因为上述缘故, WinCE里就不能把exception vectors放在normal的低地址了, 只能选用高地址放置. 在WINCE500\PRIVATE\WINCEOS\COREOS\NK\KERNEL\ARM\armtrap.s中, 下面这段VectorInsturctions代码将被复制到0xFFFF0000位置, 占据8*4=32bit的长度. 我们最经常使用的IRQ中断在0xFFFF0018的位置. 其中0x14是保留给以后扩展的,目前并没有用到.按照ARM体系, 应该有7种中断. 而实际上只有IRQ和FIQ会跳到OEMInterruptHandler里面(后者是OEMInterruptHandlerFIQ).VectorInstructions        ldr     pc, [pc, #0x3E0-8]              ; reset        ldr     pc, [pc, #0x3E0-8]              ; undefined instruction        ldr     pc, [pc, #0x3E0-8]              ; SVC        ldr     pc, [pc, #0x3E0-8]              ; Prefetch abort        ldr     pc, [pc, #0x3E0-8]              ; data abort        ldr     pc, [pc, #0x3E0-8]              ; unused vector location        ldr     pc, [pc, #0x3E0-8]              ; IRQ        ldr     pc, [pc, #0x3E0-8]              ; FIQ这只是第一次跳转, 跳转到0xFFFF03E0后,这里才是中断向量表(vector table). 这个vector table并不在armtrap.s里,而是在同目录的exvector.s里单独定义, 这里就是各Interrupt Service Routine的入口地址了.VectorTable        DCD     -1                             ; reset        DCD     UndefException          ; undefined instruction        DCD     SWIHandler                ; SVC        DCD     PrefetchAbort            ; Prefetch abort        DCD     DataAbortHandler       ; data abort        DCD     -1                             ; unused vector        DCD     IRQHandler                ; IRQ        DCD     FIQHandler                ; FIQ 实际上不论是ExceptionVectors或者VectorTable, 一开始都是放置在ROM里面的,这就需要在系统startup的时候把它们搬运到指定的位置,下面这段代码应该就是干的这事.; Setup the vector area.;;       (r8) = ptr to exception vectors         add     r7, pc, #VectorInstructions - (.+8)        ldmia   r7!, {r0-r3}                    ; load 4 instructions        stmia   r8!, {r0-r3}                    ; store the 4 vector instructions        ldmia   r7!, {r0-r3}                    ; load 4 instructions        stmia   r8!, {r0-r3}                    ; store the 4 vector instructions         ; convert VectorTable to Physical Address        ldr     r0, =VectorTable                ; (r0) = VA of VectorTable        mov     r1, r11                         ; (r1) = &OEMAddressTable[0]        bl      PaFromVa        mov     r7, r0                          ; (r7) = PA of VectorTable        add     r8, r8, #0x3E0-(8*4)            ; (r8) = target location of the vector table        ldmia   r7!, {r0-r3}        stmia   r8!, {r0-r3}        ldmia   r7!, {r0-r3}        stmia   r8!, {r0-r3} OK了,运行到这步后,各种不同的exception就要分别进入其ISR了. 这些ISR仍然是在armtrap.s里面的. 以IRQHandler为例. 从0xFFFF03F8跳转到ISR入口处: NESTED_ENTRY IRQHandler, 后面好长一段汇编, 算了一下145行, 晕倒, 看不懂. 这些都是WINCE做好的不需要OEM再去修改。其实我们暂时先看懂一个标识符就OK了: CALL 第一次CALL了这个函数CeLogInterrupt, 它位于wince500\private\winceos\coreos\nk\kernel\logger.c里面, 实质性工作就是一个interrupt counter,把一个计算中断数量的全局变量加一. 接下来CALL了我们亲切的"OEMInterruptHandler". 在INTEL的BSP里提供了intr.c, 里头实现了这个OEMInterruptHandler函数. 这个函数都做些什么呢? 首先通过OEMAddressTalbe把CPU上的Interrupt Controller Registers影射到virtual address, 其实我们只关心这里面的一个地方: ICHP[IRQ], 这里指示了IRQ Highest Priority Field,所以我们只要把这5个BIT取出来判断就可以了. 当然从这里取得的只是硬件的physical IRQ号(有的地方翻译成设备中断号), 得通过OALIntrTranslateIrq转换成能够在系统里使用的logical SysIntr号(调度中断号).   IRQ号可以在BSP里找到定义(Bxxxxx_intr.h), 而SysIntr则分为三类: WINCE规定好的 / OEM自行分配 /  DRIVER运行时动态获取. WINCE在public\common\oak\inc\nkintr.h中规定一些较为核心的SYSINTR,如SYSINTR_NOP, RESCHED, BREAK, CHAIN, 还有TIMEING, RTC_ALARM等, 而在BSP里bsp_cfg.h则定义了外围设备的SYSINTR, 如OHCI, UART, KEYPAD等. 前面这两种情况的SYSINTR, 在注册表platform.reg里也必须写值, 并且得和程序里定义的一样. 第三种是运行时动态获取的SYSINTR, 我还没碰到过, 不是很清楚. 从OEMInterruptHandler中返回了一个该IRQ所对应的SysIntr号, 放置在R0里. 接下来, call了这个函数指针"pfnOEMIntrOccurs", 到private里追踪, 该指针在schedule.c里指向了函数FakedOEMIntrOccurs, 而这个函数就在该赋值语句的上面几行,具体操作为:return dwSysIntr. 哈被耍了, 人家函数名就叫Fake, 自己要去追的, 活该. 再往下走, "cmp  r0, #SYSINTR_RESCHED", 轮询的时间片已经用完了, 只好进行调度了,PendEvents, 等下个时间片再来解决了.具体做法十分严谨,不过我还不太看得懂这堆ARM汇编, 望洋兴叹了. 这样汇编里的ISR就走完了。 现在从最高层面上的device driver往下走.device drvier在initialize过程中,肯定有个初始化中断的过程. 典型的做法是首先调用GetISRInfo函数从注册表里把IRQ和SysIntr读出来(当然同时还读了InterfaceType, BusNumber等其他东东), 然后前面会声明个m_hISTEvent的handler, 在这里就CreatEvent, 建立IST事件. 如果EVENT建立成功, 下面就InterruptInitialize(SysIntr, m_hISTEvent),把这个调度中断号和ISTEVENT关联起来. 具体这个InterruptInitialize是在PRIVATE里面实现的,不用管它就当做API用好了. 最后在CeSetPriority把THREAD的优先级设一下就OK了.跳到driver的IST去看看,开始的地方一般就有个WaitForSingleObject(m_hISTEvent, INFINITE), OK,那么在设备初始化完成后,Interrupt Service Thread就保持一个SUSPEND状态,并不断地WaitForSingleObject. 中断发生后,在OEMInterruptHandler中会把这个IRQ转换成SYSINTR并通知系统( 这个就是想当然的了,我没有看到具体怎样通知系统的, 估计这段代码MS没有公开), 则由于前面已经将SYSINTR和ISTEVENT相关联, 所以系统收到这个SYSINTR后,就把SET相关的EVENT. 从而IST从WaitForSingleObject里跳出往下继续执行,进而读取自己模块的Interrupt Status Register进行判断,操作其他register控制硬件, 完成后调用InterruptDone函数告知系统,然后又回到WaitForSinigleObject里去傻等了. 现在整个中断处理过程中,只剩下一块的代码没有看到/找到。那就是转换成SysIntr后,如何告知系统,并且把相关的模块的中断处理事件SetEvent. 我估计是在private里面。


阅读全文(3077) | 回复(0) | 编辑 | 精华
 



发表评论:
昵称:
密码:
主页:
标题:
验证码:  (不区分大小写,请仔细填写,输错需重写评论内容!)



站点首页 | 联系我们 | 博客注册 | 博客登陆

Sponsored By W3CHINA
W3CHINA Blog 0.8 Processed in 0.031 second(s), page refreshed 144792403 times.
《全国人大常委会关于维护互联网安全的决定》  《计算机信息网络国际联网安全保护管理办法》
苏ICP备05006046号