当前位置:首页 > 嵌入式 > 嵌入式软件
[导读]1.中断/异常相量的装入和执行方式。中断和异常都是异步发生的事件,当该事件发生,系统将停止目前正在执行的代码转而执行事件响应的服务程序。而事件服务程序的入口点就是中

1.中断/异常相量的装入和执行方式。

中断和异常都是异步发生的事件,当该事件发生,系统将停止目前正在执行的代码转而执行事件响应的服务程序。而事件服务程序的入口点就是中断/异常向量所在的位置。arm的中断向量可以是0x0开始的低地址向量,也可以是在FFFF0000位置的高向量地址。winCE下使用高地址作为trap区,所以在CE下arm使用高地址向量。下面我们来了解一下中断/异常向量的安装和执行过程。

在kernelStart的过程中通过程序将如下代码复制到ffff0000的位置.

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

而在ffff03e0的位置放上如下的数据,每一项(32bit)对应一个异常的跳转地址也就是winCE的异常/中断向量跳转表。该表项的内容就是发生异常后将要执行的服务程序的入口地址。具体如下。

VectorTable

DCD -1 ; reset

DCD UndefException ; undefined instruction

DCD SWIHandler ; SVC

DCD PrefetchAbort ; Prefetch abort

IF :DEF:ARMV4T :LOR: :DEF:ARMV4I

DCD OEMDataAbortHandler ; data abort

ELSE

DCD DataAbortHandler ; data abort

ENDIF

DCD -1 ; unused vector

DCD IRQHandler ; IRQ

DCD FIQHandler ; FIQ

在上面的这些代码/数据在内存空间上按照上述要求放置好以后,每次触发一个异常就自动运行到相应跳转表项所对应的地址执行。

2.异常/中断服务程序

在arm下,由于有7种异常状态包括reset、Undef exception、software interrupt(swi)、Prefech Abort、DataAbort、IRQ、FIQ七种异常/中断。reset仅在复位时发生,其他6种都是在系统运行时发生。当任何一个异常发生并得到响应时,ARM 内核自动完成以下动作:

拷贝 CPSR 到 SPSR_

设置适当的 CPSR 位:

改变处理器状态进入 ARM 状态

改变处理器模式进入相应的异常模式

设置中断禁止位禁止相应中断

更新 LR_

设置 PC 到相应的异常向量

同时不管异常发生在ARM 还是Thumb 状态下,处理器都将自动进入ARM 状态。并且中断使能会自动被关闭。在这个时候由于部分通用寄存器是不同模式公用的,所以还需要保存这些将会被破坏的寄存器,待到处理完成的时候恢复这些寄存器被中断前的状态。另外在进入异常模式后,lr的值不一定就是我们所需恢复执行的位置,该位置受到异常类型和流水线误差的影响。在SWI模式下,LR就是返回值。在IRQ和FIQ中LR=LR-4,DataAbort下LR=LR-8;具体原因我们就不讨论了,有兴趣可以参看<基于ARM 的嵌入式程序开发要点>一文。下面分别对这些服务程序进行分析。

2-1.undef exception服务程序

undef exception在执行到过非法的指令时产生,通常来模拟一些处理器不支持的功能,如浮点运算。简单说一下undef exception的过程:当当前指令为一条处理器不支持的指令时,处理器会自动动将该指令送交各协处理器(如MMU、FPU)处理,如果这些协处理器都无法识别这条指令的时候,就产生该异常。下面开始看相应的代码。

NESTED_ENTRY UndefException

sub lr, lr, #4 ; (lr) = address of undefined instruction

stmdb sp, {r0-r3, lr}

mov r1, #ID_UNDEF_INSTR

b CommonHandler

ENTRY_END UndefException

上面就是undef Exception的服务程序的入口处(已经将不参与编译和Thumb模式下的代码去掉),通过lr-=4计算出触发异常前的指令地址,同时保存r0-r3和lr入undef_exception stack用于最后恢复现场和取得异常指令本身,随后进入分发程序CommonHandler.CommonHandler是一个公共的异常服务程序,它通过不同的传入参数来进行处理,在这里mov r1,#ID_UNDEF_INSTR就是指定异常模式为undef Exception.

2-2.swi服务程序

按在ARM处理器的设计意图,系统软件的系统调用(SystemCalls)都是通过SWI指令完成。SWI相当于一个中断指令,不同的是SWI不是由外部中断源产生的,同时对应于SWI的异常向量位于0xc的位置或0xffff 000c的位置。也就是说当执行一个swi指令后,当前程序流中断,并转入0xc或0xffff000c执行,同时将CPSR_mode(当前程序状态寄存器)复制入SPSR_svc,转入SVC模式运行(使用特权模式的寄存器组)。也就是说系统通过执行SWI引发系统swi异常后切换入特权模式,系统调用功能号由swi xx后的xx决定,在运行完指定功能的代码后返回异常时的地址并恢复用户模式。我们看看,Wince中这部分代码是如何实现的。

DCD SWIHandler ; SVC<<--------------------------SWI入口点。

LEAF_ENTRY SWIHandler

IF {FALSE}

...

ENDIF

movs pc, lr

ENTRY_END SWIHandler

上面IF {FALSE}到ENDIF之间的代码在编译的时候是得不到编译的(事实上这部分代码是用于开发中调试使用的,针对特殊的硬件平台,一般与我们使用的硬件平台无关。所以下面摘抄的代码都不将不参与编译的内容写入),因此SWI服务程序就是一句话。movs pc, lr也就是直接回到SWI的地方,同时将SPSR_svc恢复到CPSR_mode中。这个过程中并没有进行在系统态执行特定系统指令序的工作,而仅仅是简单的返回,所以这不是系统调用,系统调用还需要根据调用号的不同运行指定的核心态代码。也就是说Wince的系统调用不是通过SWI来完成的,而是通过其他的异常处理手段达成的。

2-3 中断服务程序

IRQ(大概是最熟悉的异常方式了)在外部中断源在需要向处理器请求服务时发生,比如:时钟、外围器件FIFO上/下溢出、按键等等。IRQHandler就是中断的处理句柄,下面我们来具体看看。

----------------------------------------------------------------------------------[!--empirenews.page--]

NESTED_ENTRY IRQHandler

sub lr, lr, #4 ; fix return address

stmfd sp!, {r0-r3, r12, lr} ;保存将要用到的寄存器和lr压入stack_irq

PROLOG_END

和上面一样,服务程序的入口处都是例行公事的计算返回位置以抵消流水线误差。再将要用到的寄存器压入STACK_IRQ,这样,准备工作就做完了。

; Test interlocked API status.

;INTERLOCKED_START EQU USER_KPAGE+0x380

;INTERLOCKED_END EQU USER_KPAGE+0x400

sub r0, lr, #INTERLOCKED_START

cmp r0, #INTERLOCKED_END-INTERLOCKED_START

bllo CheckInterlockedRestart

上面这部分的内容是关于互锁的检测,由于如信号量这些同步手段都必须作为原子操作进行,不允许打断。所以如果中断发生在互锁API的执行过程中,就需要专门的处理了。这些API都是放在INTERLOCKED_START和INTERLOCKED_END之间的,通过LR很容易就检查出是否是INTERLOCKEDXXX的过程中。这里并不关心互锁的实现就绕开这部分代码继续往下看,当作中断没有发生在interlock过程处理。

; CAREFUL! The stack frame is being altered here. It"s ok since

; the only routine relying on this was the Interlock Check. Note that

; we re-push LR onto the stack so that the incoming argument area to

; OEMInterruptHandler will be correct.

;mrs r1, spsr ; (r1) = saved status reg

stmfd sp!, {r1} ; save SPSR onto the IRQ stack

mov r0,lr ; parameter to OEMInterruptHandler

msr cpsr_c, #SVC_MODE:OR:0x80 ; switch to supervisor mode w/IRQs disabled

stmfd sp!, {lr} ; save LR onto the SVC stack

stmfd sp!, {r0} ; save IRQ LR (in R0) onto the SVC stack (param)

;

; Now we call the OEM"s interrupt handler code. It is up to them to

; enable interrupts if they so desire. We can"t do it for them since

; there"s only on interrupt and they haven"t yet defined their nesting.

;

CALL OEMInterruptHandler

ldmfd sp!, {r1} ; dummy pop (parameter)

ldmfd sp!, {lr} ; restore SVC LR from the SVC stack

msr cpsr_c, #IRQ_MODE:OR:0x80 ; switch back to IRQ mode w/IRQs disabled

; Restore the saved program status register from the stack.

;

ldmfd sp!, {r1} ; restore IRQ SPSR from the IRQ stack

msr spsr, r1 ; (r1) = saved status reg

ldr lr, =KData ; (lr) = ptr to KDataStruct

cmp r0, #SYSINTR_RESCHED ;->时间片已到,进行调度

beq %F10

;SYSINTR_DEVICES EQU 8 ;是否设备中断,中断号是否有效

;SYSINTR_MAX_DEVICES EQU 32

sub r0, r0, #SYSINTR_DEVICES

cmp r0, #SYSINTR_MAX_DEVICES

;由此可以看出windowsCE的系统中断号最大支持32种从9-40.

;其中第16号(24)被定义为SYSINTR_FIRMWARE

; If not a device request (and not SYSINTR_RESCHED)

ldrhsb r0, [lr, #bResched] ; (r0) = reschedule flag

bhs %F20 ; not a device request

;PendEvents EQU 0x340 ; offset 0x10*sizeof(DWORD) of aInfo

;device 中断

ldr r2, [lr, #PendEvents] ; (r2) = pending interrupt event mask

mov r1, #1

orr r2, r2, r1, LSL r0 ; (r2) = new pending mask

str r2, [lr, #PendEvents] ; save it

;*PendEvents = *PendEvents|(1< ;

; mark reschedule needed

;情况1:r0=SYSINTR_RESCHED=1

;情况2: r0 =r0-SYSINTR_DEVICES>=SYSINTR_MAX_DEVICES

10 ldrb r0, [lr, #bResched] ; (r0) = reschedule flag

orr r0, r0, #1 ; set "reschedule needed bit"

strb r0, [lr, #bResched] ; update flag

20 mrs r1, spsr ; (r1) = saved status register value

and r1, r1, #0x1F ; (r1) = interrupted mode

cmp r1, #USER_MODE ; previously in user mode?

cmpne r1, #SYSTEM_MODE ; if not, was it system mode?

cmpeq r0, #1 ; user or system: is resched == 1

;if(SytemMode(spsr)||UserMode(spsr))&&r0!=1) return;

ldmnefd sp!, {r0-r3, r12, pc}^ ; can"t reschedule right now so return

*************************************************************************************

sub lr, lr, #4

ldmfd sp!, {r0-r3, r12}

stmdb lr, {r0-r3}

ldmfd sp!, {r0}

str r0, [lr] ; save resume address

mov r1, #ID_RESCHEDULE ; (r1) = exception ID

b CommonHandler

ENTRY_END IRQHandler

将spsr_irq压入IRQ堆栈保存。为调用OEMInterruptHandler作准备。(通常中断处理程序切换入系统态执行的目的在于避免使用终端模式下的寄存器,以方便是实现终端套嵌,这儿切入系统态时终端使能是关闭的,对于模态切换的原因我很迷惑。)OEMInterrupt需要在特权模式下执行,所以这里增加了切换入特权(SVC)模式的内容。紧接着将要用与传递参数的寄存器保存。设定传入参数,r0就可以开始调用OEMInterruptHandler了,这里的调用规则遵循windowsCE的规范而不是ATPCS的规范。具体过程参考ARM Parameter Passing@msdn。下面是函数原形。int OEMInterruptHandler(unsigned int ra);这里传入的参数就是上面的r0,事实上r0代表的参数ra并没有实质的作用在这里仅仅是形式上的实现一下而已,不过在这儿可以看到这个传入的ra实际上就是被中断的地址,如果需要知道被中断的位置可以通过ra来查询,而msdn里面说这个参数是保留的。返回的参数也是保存在r0中。其中返回值是系统中断类型。其中SYSINTR_RESCHED为系统时钟中断,每次时间片用完,该时钟便产生中断,并设置kData结构的bResched位,进入调度流程。如果中断类型是系统设备中断,那就设置PendEvents,待再次调度的时候处理中断。所以OEMInterruptHandler必须提前就要对中断进行响应对该中断源设置mask,防止在这过程中同一中断不停发生,导致中断饱和影响程序流的执行,直道中断处理真正完成后再次开放该中断的mask。在这里还可以看到的是系统设备中断号的范围是从SYSINTR_DEVICES到SYSINTR_MAX_DEVICES,也就是从9-40一共32个设备中断号,其中SYSINTR_FIRMWARE为8+16号,这个在编写OAL的中断服务程序时需要注意。如果当前的返回值既不是设备中断号又不是调度中断号,则读出当前调度标示,根据该标示进行判断是否调度/或返回.如果是进入调度流程则恢复初始的寄存器状态,再按CommonHandler的要求保存寄存器。进入CommonHandler,等待分发。[!--empirenews.page--]

2-3 FIQ服务程序

照例看看程序

NESTED_ENTRY FIQHandler

sub lr, lr, #4 ; fix return address

stmfd sp!, {r0-r3, r12, lr}

PROLOG_END

CALL OEMInterruptHandlerFIQ

ldmfd sp!, {r0-r3, r12, pc}^ ; restore regs & return for NOP

ENTRY_END FIQHandler

LTORG

FIQ是arm体系下特有的异常方式,其工作过程与IRQ类似都是由外部引脚触发但设计用途不同,IRQ用于通常的外部中断源的处理,是作为统一、通用的与外部器件交互的手段,而IRQ仅仅用于处理周期短同时又需要快速处理的场合其触发的事件源通常也来此外部FIQ中断。如:更换电池、数据传输这类工作。可想而知FIQ讲究的是快速,精干。因此FIQ服务程序通常没有分发,而仅仅是针对单一的工作进行处理保证处理的实时性。因此FIQ的处理相对IRQ就简单很多,直接调用 OEMInterruptHandlerFIQ进行处理后返回就完成了整个 FIQ服务程序。

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: 驱动电源

在工业自动化蓬勃发展的当下,工业电机作为核心动力设备,其驱动电源的性能直接关系到整个系统的稳定性和可靠性。其中,反电动势抑制与过流保护是驱动电源设计中至关重要的两个环节,集成化方案的设计成为提升电机驱动性能的关键。

关键字: 工业电机 驱动电源

LED 驱动电源作为 LED 照明系统的 “心脏”,其稳定性直接决定了整个照明设备的使用寿命。然而,在实际应用中,LED 驱动电源易损坏的问题却十分常见,不仅增加了维护成本,还影响了用户体验。要解决这一问题,需从设计、生...

关键字: 驱动电源 照明系统 散热

根据LED驱动电源的公式,电感内电流波动大小和电感值成反比,输出纹波和输出电容值成反比。所以加大电感值和输出电容值可以减小纹波。

关键字: LED 设计 驱动电源

电动汽车(EV)作为新能源汽车的重要代表,正逐渐成为全球汽车产业的重要发展方向。电动汽车的核心技术之一是电机驱动控制系统,而绝缘栅双极型晶体管(IGBT)作为电机驱动系统中的关键元件,其性能直接影响到电动汽车的动力性能和...

关键字: 电动汽车 新能源 驱动电源

在现代城市建设中,街道及停车场照明作为基础设施的重要组成部分,其质量和效率直接关系到城市的公共安全、居民生活质量和能源利用效率。随着科技的进步,高亮度白光发光二极管(LED)因其独特的优势逐渐取代传统光源,成为大功率区域...

关键字: 发光二极管 驱动电源 LED

LED通用照明设计工程师会遇到许多挑战,如功率密度、功率因数校正(PFC)、空间受限和可靠性等。

关键字: LED 驱动电源 功率因数校正

在LED照明技术日益普及的今天,LED驱动电源的电磁干扰(EMI)问题成为了一个不可忽视的挑战。电磁干扰不仅会影响LED灯具的正常工作,还可能对周围电子设备造成不利影响,甚至引发系统故障。因此,采取有效的硬件措施来解决L...

关键字: LED照明技术 电磁干扰 驱动电源

开关电源具有效率高的特性,而且开关电源的变压器体积比串联稳压型电源的要小得多,电源电路比较整洁,整机重量也有所下降,所以,现在的LED驱动电源

关键字: LED 驱动电源 开关电源

LED驱动电源是把电源供应转换为特定的电压电流以驱动LED发光的电压转换器,通常情况下:LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: LED 隧道灯 驱动电源
关闭