Chinaunix首页 | 论坛 | 博客
  • 博客访问: 347523
  • 博文数量: 88
  • 博客积分: 907
  • 博客等级: 准尉
  • 技术积分: 1230
  • 用 户 组: 普通用户
  • 注册时间: 2010-01-26 13:27
文章分类

全部博文(88)

文章存档

2017年(1)

2014年(3)

2013年(29)

2012年(21)

2011年(26)

2010年(8)

分类: BSD

2012-12-25 15:21:04

最近在看IRQ中断,于是找到了一篇ecos的中断代码解析。觉得不错,现转发与此。

http://bbs.lupaworld.com/home-space-uid-131820-do-blog-id-127909.html

最近定位一个问题,把eCosIRQ代码走读了一下,顺便做下笔记,共享之。

ARM中断发生时,ARM核会做下面一些工作:


1、下一条将要执行的指令地址+4赋值给 r14(lr)

2CPSR复制到SPSR

3CPSR的模式更改为IRQ模式

4、如果原来是thumb状态,则CPSR更改为ARM状态

5CPSRbit7设置为0,即关闭IRQ

6PC指针跳转到IRQ的中断向量地址(大多数情况为0x00000018


因此如果从中断服务中返回,只要将lr4复制给PC,同时恢复SPSRCPSR即可。这样被中断的代码可以继续运行下去。


另外在大多数的ARM实现中,线程运行的模式大都为SVC模式,下文以此为假设。


好了,这样就从 hal/arm/arch/current/src/vectors.S 开始IRQ之旅。


784 IRQ:

785 // Note: I use this exception stack while saving the context because

786 // the current SP does not seem to be always valid in this CPU mode.


中断发生时,首先需要保存现场,所谓保存现场,就是将一大堆寄存器保存在一个地方,当中断返回时,重新恢复这些寄存器,以恢复原来的CPU运行状态。


eCos将现场定义为一个数据结构,并将该数据结构保存在中断发生时的上下文的堆栈。比如说中断发生在thread的上下文,则该数据结构保存在thread的堆栈里。


在“hal/arm/arch/current/include/hal_arch.h” 定义了该数据结构。


103 typedef struct

104 {

105 // These are common to all saved states

106 cyg_uint32 d[HAL_NUM_THREAD_CONTEXT_REGS] ; // Data regs (r0..r10)

107 cyg_uint32 fp; // (r11) Frame. pointer

108 cyg_uint32 ip; // (r12)

109 cyg_uint32 sp; // (r13) Stack pointer

110 cyg_uint32 lr; // (r14) Link Reg

111 cyg_uint32 pc; // (r15) PC place holder

112 // (never used)

113 cyg_uint32 cpsr; // Condition Reg

114 // These are only saved for exceptions and interrupts

115 cyg_uint32 vector; // Vector number

116 cyg_uint32 svc_lr; // saved svc mode lr

117 cyg_uint32 svc_sp; // saved svc mode sp

118

119 } HAL_SavedRegisters;


后面的汇编马上开始保存该数据结构的寄存器。


787 ldr sp,.__exception_stack // get good stack

788 stmfd sp!,{r0-r5} // save some supervisor regs


代码已经进入IRQ模式,这里需要注意不同模式下的堆栈sp寄存器不是同一个寄存器。因此787行的sp并不是被中断的thread上下文的sp,而是中断模式的堆栈寄存器sp_irq 。因为后面的代码需要使用r0-r5,因此需要把被中断的上下文的r0-r5临时保存在堆栈中,堆栈为exception的堆栈:__exception_stack


789 sub r0,lr,#4 // PC at time of interrupt

790 mrs r1,spsr

791 mov r2,#CYGNUM_HAL_VECTOR_IRQ

792 mov r3,sp

793


r0为中断发生时threadPC位置,对应数据结构中的pc (为什么减4在文章最开始已经说明)

r1为中断发生时threadCPSR,对应数据结构中的cpsr

r2为中断向量,对应数据结构中的vector

r3为临时保存下sp _irq,后面要用


794 mrs r4,cpsr // switch to Supervisor Mode

795 bic r4,r4,#CPSR_MODE_BITS

796 // When handling an IRQ we must disable FIQ unless the current

797 // mode in CPSR is IRQ. If we were to get a FIQ while in another

798 // mode, the FIQ handling code would transform. the FIQ into an

799 // IRQ and call the non-reentrant IRQ handler again. As a result,

800 // for example, the stack pointer would be set to the beginning

801 // of the exception_stack clobbering the registers we have just

802 // saved.

803 orr r4,r4,#CPSR_SUPERVISOR_MODE|CPSR_FIQ_DISABLE

804 msr cpsr,r4

805


把当前的IRQ模式切换到SVC模式下,并关FIQ中断(IRQ在中断发生时,cpsrIRQ位已经被关掉。当前已经不是IRQ模式,如果用户thread的模式为SVC模式,那么当前的模式已经恢复到thread的模式下。


806 mov r5,sp // save original svc sp

807 mov r4,lr // save original svc lr

808 stmfd sp!,{r0-r2,r4,r5} // push svc_sp, svc_lr, vector, psr, pc

809


r5sp_svc,对应数据结构中的svc_sp

r4lr_svc,对应数据结构中的svc_lr

svc_sp, svc_lr, vector, cpsr, pc压栈

这里注意:因为已经是SVC模式,因此这里的堆栈sp已经是线程的堆栈了。



810 #ifdef CYGOPT_HAL_ARM_WITH_USER_MODE

811 // did exception occur in user mode ?

812 and r2, r1, #CPSR_MODE_BITS

813 cmp r2, #CPSR_USER_MODE

814 bne 1f

815 stmfd sp, {r8-r12, sp, lr}^ // get user mode regs

816 nop

817 sub sp, sp, #4*7

818 bal 2f

819 1:

820 #endif


这里对于用户线程为user模式的处理,一般我们都采用svc模式,这段代码暂不关心


821 // switch to pre-exception mode to get banked regs

822 mov r0,sp // r0 survives mode switch

823 mrs r2,cpsr // Save current psr for return

824 orr r1,r1,#CPSR_IRQ_DISABLE|CPSR_FIQ_DISABLE

825 bic r1,r1,#CPSR_THUMB_ENABLE

826 msr cpsr,r1

827 stmfd r0!,{r8-r12,sp,lr}

828 msr cpsr,r2 // back to svc mode

829 mov sp,r0 // update stack pointer

830


恢复到中断发生前的模式,并将当前模式设置为IRQFIQ关闭,使能thumb模式,以获取寄存器的值 。继续将lr,sp,r12,r11,r10,r9,r8 压栈。


831 2:

832 // now save pre-exception r0-r7 on current stack

833 ldmfd r3,{r0-r5}

834 stmfd sp!,{r0-r7}

835


__exception_stack中获取保存的r0-r5,连同r6,r7压入堆栈。

836 // sp needs fixing if exception occured in SVC mode.

837 ldr r1,[sp,#armreg_cpsr]

838 and r1,r1,#CPSR_MODE_BITS

839 cmp r1,#CPSR_SUPERVISOR_MODE

840 ldreq r1,[sp,#armreg_svcsp]

841 streq r1,[sp,#armreg_sp]

842


取出数据结构HAL_SavedRegisters中的cpsr,判断是否为svc模式,如果是则说明被中断的上下文为SVC模式,因此将svc模式的sp_svc复制 到sp。我们如果在这里恢复现场,那么把r0-r14出栈,则sp的值正好是svc_sp,也就是中断发生时的sp的值。


注意,上面我们没有分析用户线程运行在USER模式的代码,因此对于SVC模式来说,数据结构HAL_SavedRegisters中的lr,sp,lr_svc,sp_svc是重复的。


至此,数据结构 HAL_SavedRegisters已保存完整,即现场保存工作已经完成。

下面要进行中断ISR的调用。


843 mov v6,sp // Save pointer to register frame.

844

845 // mov r0,sp

846 // bl _show_frame_in

847


sp保存在v6寄存器中(v1r4, v6r9) ,这里的v6就是数据结构 HAL_SavedRegisters的指针


848 #ifdef CYGIMP_HAL_COMMON_INTERRUPTS_USE_INTERRUPT_STACK

849 // Switch to interrupt stack

850 ldr r2,.irq_level // current number of nested interrupts

851 ldr r0,[r2]

852 add r1,r0,#1

853 str r1,[r2] // if was zero, switch stacks


CYGIMP_HAL_COMMON_INTERRUPTS_USE_INTERRUPT_STACK表明为中断处理函数使用中断堆栈, 而不是线程堆栈。通常都会使用这个宏,最大的好处在于不用为每个线程的堆栈预留出中断的堆栈开销。

eCos支持中断嵌套,irq_level为全局变量,初始值为0,表明中断的嵌套层数。这里加1


854 cmp r0,#0

855 moveq r1,sp // save old stack pointer

856 ldreq sp,.__interrupt_stack

857 stmeqfd sp!,{r1}

858 10:

859 #endif

860


如果irq_level在加1前的值为0(即表明中断未嵌套),把sp设置为__interrupt_stack,并把旧的sp值压栈保存,从当前开始,堆栈从线程堆栈切换到中断堆栈。如果中断嵌套,则表明当前sp已经是中断堆栈了。


861 // The entire CPU state is now stashed on the stack,

862 // increment the scheduler lock and handle the interrupt

863

864 #ifdef CYGFUN_HAL_COMMON_KERNEL_SUPPORT

865 .extern cyg_scheduler_sched_lock

866 ldr r3,.cyg_scheduler_sched_lock

867 ldr r4,[r3]

868 add r4,r4,#1

869 str r4,[r3]

870 #endif

871


cyg_scheduler_sched_lock即是类Cyg_Scheduler_SchedLock的成员sched_lock,表示进程调度锁的计数


kernel/current/include/smp.hxx


257 class Cyg_Scheduler_SchedLock

258 {

259 static volatile cyg_ucount32 sched_lock // lock counter

260 CYGBLD_ATTRIB_ASM_ALIAS( cyg_scheduler_sched_lock )

261 CYGBLD_ANNOTATE_VARIABLE_SCHED

262 ;

..........

..........

..........

306 };


这里将cyg_scheduler_sched_lock1,即表明需要锁住调度


872 THUMB_MODE(r3,10)

873


THUMB_MODE宏在非thumb模式下为空,暂不关心


874 mov r0,v6

875 bl hal_IRQ_handler // determine interrupt source

876 mov v1,r0 // returned vector #

877


调用hal_IRQ_handler(),获取发生的中断号,返回值存放在v1


hal/arm/br104h/current/src/br104h_misc.c


241 //

242 // This routine is called to respond to a hardware interrupt (IRQ). It

243 // should interrogate the hardware and return the IRQ vector number.

244

245 int hal_IRQ_handler(void)

246 {

247 // Do hardware-level IRQ handling

248 int irq_status, vector;

249

250 HAL_READ_UINT32(CYG_DEVICE_IRQ_Status, irq_status);

251 //diag_init(); diag_printf("%s, status: %x\n", __PRETTY_FUNCTION__, irq_status);

252

253 for (vector = 0; vector <= 16; vector++) {

254 if (irq_status & (1<

255 }

256 return CYGNUM_HAL_INTERRUPT_NONE; // This shouldn't happen!

257 }


hal_IRQ_handler()和具体的芯片实现相关的,这里我把br104h的的实现列出来。


878 #if defined(CYGPKG_KERNEL_INSTRUMENT) && \

879 defined(CYGDBG_KERNEL_INSTRUMENT_INTR)

880 ldr r0,=RAISE_INTR // arg0 = type = INTR,RAISE

881 mov r1,v1 // arg1 = vector

882 mov r2,#0 // arg2 = 0

883 bl cyg_instrument // call instrument function

884 #endif


INSTRUMENT宏用于内核调试,暂不关心

886 ARM_MODE(r0,10) 

ARM_MODE宏在非thumb模式下为空

888 mov r0,v1 // vector #


r0保存中断向量


889

890 #if defined(CYGDBG_HAL_DEBUG_GDB_CTRLC_SUPPORT) \

891 || defined(CYGDBG_HAL_DEBUG_GDB_BREAK_SUPPORT)

892 // If we are supporting Ctrl-C interrupts from GDB, we must squirrel

893 // away a pointer to the save interrupt state here so that we can

894 // plant a breakpoint at some later time.

895

896 .extern hal_saved_interrupt_state

897 ldr r2,=hal_saved_interrupt_state

898 str v6,[r2]

899 #endif


GDB STUBctrl-c的处理,暂不关心

901 cmp r0,#CYGNUM_HAL_INTERRUPT_NONE // spurious interrupt

902 bne 10f


正常流程跳转到后面的标号10

904 #ifdef CYGIMP_HAL_COMMON_INTERRUPTS_IGNORE_SPURIOUS

905 // Acknowledge the interrupt

906 THUMB_CALL(r1,12,hal_interrupt_acknowledge)

907 #else

908 mov r0,v6 // register frame.

909 THUMB_CALL(r1,12,hal_spurious_IRQ)

910 #endif // CYGIMP_HAL_COMMON_INTERRUPTS_IGNORE_SPURIOUS

911 b spurious_IRQ


异常的情况为:有中断,但无法获取中断号,则处理处理spurious_IRQ,这种异常情况一般不会发生,这里暂不关心

后面的代码需要处理ISRhal_interrupt_datahal_interrupt_handlers定义在hal/arm/arch/current/src/vectors.S 如下所示:

1144 .globl hal_interrupt_handlers

1145 hal_interrupt_handlers:

1146 .rept CYGNUM_HAL_ISR_COUNT

1147 .long hal_default_isr

1148 .endr

1149

1150 .globl hal_interrupt_data

1151 hal_interrupt_data:

1152 .rept CYGNUM_HAL_ISR_COUNT

1153 .long 0

1154 .endr

1155

1156 .globl hal_interrupt_objects

1157 hal_interrupt_objects:

1158 .rept CYGNUM_HAL_ISR_COUNT

1159 .long 0

1160 .endr


缺省的isrhal_default_isr,缺省的dataobjects0。这是没有安装中断的数值,如果已经安装中断,即调用宏HAL_INTERRUPT_ATTACH的宏安装,这isr, data, object会被修改成需要处理的中断变量和函数。

913 10: ldr r1,.hal_interrupt_data

914 ldr r1,[r1,v1,lsl #2] // handler data

915 ldr r2,.hal_interrupt_handlers

916 ldr v3,[r2,v1,lsl #2] // handler (indexed by vector #)

917 mov r2,v6 // register frame. (this is necessary

918 // for the ISR too, for ^C detection)


需要调用的isr的函数原型为 xxxx_isr(cyg_vector_t vector, cyg_addrword_t data,
HAL_SavedRegisters *regs), 因此需要计算出r0/r1/r2三个参数:
r0v1vecotor,这里计算出r1为对应的datav3isrhandler, r2为寄存器frame的地址,即HAL_SavedRegisters的指针

920 #ifdef __thumb__

921 ldr lr,=10f

922 bx v3 // invoke handler (thumb mode)

923 .pool

924 .code 16

925 .thumb_func

926 IRQ_10T:

927 10: ldr r2,=15f

928 bx r2 // switch back to ARM mode

929 .pool

930 .code 32

931 15:

932 IRQ_15A:


这里对于thumb模式处理,暂不关心

933 #else

934 mov lr,pc // invoke handler (call indirect 

因为ARM的指令为流水线处理,因此这是pc已经是加8,因此lr的值为spurious_IRQ
因此ISR处理完毕后,返回处理spurious_IRQ


935 mov pc,v3 // thru v3)

936 #endif 

调用isr函数。

938 spurious_IRQ:

939

940 #ifdef CYGIMP_HAL_COMMON_INTERRUPTS_USE_INTERRUPT_STACK

941 // If we are returning from the last nested interrupt, move back

942 // to the thread stack. interrupt_end() must be called on the

943 // thread stack since it potentially causes a context switch.

944 ldr r2,.irq_level

945 ldr r3,[r2]

946 subs r1,r3,#1

947 str r1,[r2]

948 ldreq sp,[sp] // This should be the saved stack pointer

949 #endif


这里将irq_level1,如果减1后变为0(说明没有中断嵌套),则恢复线程上下文的sp(即寄存器frame指针)。因此可以看到,处理ISR的堆栈是中断的堆栈(如果CYGIMP_HAL_COMMON_INTERRUPTS_USE_INTERRUPT_STACK宏设能的话),而后面处理的DSR的堆栈是线程的堆栈。

至此,ISR调用完毕,后面开始处理DSR的工作。

950 // The return value from the handler (in r0) will indicate whether a

951 // DSR is to be posted. Pass this together with a pointer to the

952 // interrupt object we have just used to the interrupt tidy up routine.

953

954 // don't run this for spurious interrupts!

955 cmp v1,#CYGNUM_HAL_INTERRUPT_NONE

956 beq 17f


如果vector等于-1(中断异常流程:有中断,但无法获取中断号)转到后面的标号17

957 ldr r1,.hal_interrupt_objects

958 ldr r1,[r1,v1,lsl #2]

959 mov r2,v6 // register frame.

960

961 THUMB_MODE(r3,10)

962

963 bl interrupt_end // post any bottom layer handler

964 // threads and call scheduler

965 ARM_MODE(r1,10)



interrupt_end()的函数原型为:interrupt_end(cyg_uint32 isr_ret,Cyg_Interrupt *intr,HAL_SavedRegisters *regs)

取中断的object,这里r0=vector, r1=object, r2=HAL_SavedRegisters指针


调用interrupt_end()函数,处理post_dsr, dsr处理线程切换等,这个函数比较复杂,我们放到以后分析

966 17:

967

968 // mov r0,sp

969 // bl show_frame_out

970

971 // return from IRQ is same as return from exception

972 b return_from_exception


处理中断返回,恢复现场

670 return_from_exception:

671

672 ldr r0,[sp,#armreg_cpsr]

673

674 // return to supervisor mode is simple

675 and r1,r0,#CPSR_MODE_BITS

676 cmp r1,#CPSR_SUPERVISOR_MODE

677

678 #ifndef CYGOPT_HAL_ARM_PRESERVE_SVC_SPSR


CYGOPT_HAL_ARM_PRESERVE_SVC_SPSR宏在cdl的定义为

cdl_option CYGOPT_HAL_ARM_PRESERVE_SVC_SPSR {
display "Preserve svc spsr before returning to svc mode"
default_value 0
description "
This option secures exception and breakpoint processing
triggered during execution of application specific SWI
handlers."
}

看的不是很确切,反正缺省为0,暂不关心


679 msr spsr,r0

680 ldmeqfd sp,{r0-r14,pc}^


线程为SVC模式非常简单:取出保存的cpsr值给r0,如果cpsrSVC模式,则将r0赋值给spsr

680行这句代码非常关键,由于有“^ ”这个很特别的符号,汇编指令ldmeqfd将所有寄存器恢复,同时将spsr拷贝到cpsr。至此中断现场恢复,被中断的线程从中断的位置继续运行。

681 #else 
.........

.......

剩余代码为非SVC模式,暂不关心

阅读(1423) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~