1659 views|0 replies

6587

Posts

0

Resources
The OP
 

About ARM's interrupt service routine [Copy link]

The vector written for the interrupt in 2440test is a bit hidden and goes around in circles. No wonder it is so difficult to understand. Let's unravel this thing and see clearly what the process is. Interrupt vector b HandlerIRQ ;handler for IRQ interrupt It is very natural, because all microcontrollers are like that. The interrupt vector is usually placed at the beginning. People who have used a microcontroller will be very familiar with it, so I will not say more. Exception service routine Here we don't use interrupts but exceptions. After all, interrupts are just a kind of abnormal situation. Haha, the main analysis below is "interrupt exceptions". To put it bluntly, they are the interrupts we usually use in single-chip microcomputers! ! ! All interrupts caused by devices, such as TIMER interrupts, UART interrupts, external interrupts, etc., have a unified entrance, that is, interrupt exception IRQ! Then distinguish from the IRQ service function what the current interrupt is, and then jump to the corresponding interrupt service routine. It seems that ARM is more complicated than MCU, but the principle remains the same. The above is the idea, follow this idea to continue the analysis. HandlerIRQ is obviously a label. We found it. HandlerIRQ HANDLER HandleIRQ Here is a macro definition. Let's find this macro and see how it is defined: MACRO [color=#000000 ] $HandlerLabel HANDLER $HandleLabel $HandlerLabel [color=#000000 ] sub sp,sp,#4 ;decrement sp(to store jump addrESS)stmfd sp!,{r0} ;PUSH the work register to stack(lr does not push because it return to originaladdress) ldr r0,=$HandleLabel ;load the address of HandleXXX to r0ldr r0,[r0] ;load the contents(service routine start address) of HandleXXXstr r0,[sp,#4] ;store the contents(ISR) of HandleXXX to stackldmfd sp!,{r0,pc} ;POP the work register and pc(jump to ISR)MEND


  用 HandlerIRQ 将这个宏展开之后得到的结果实际是这样的HandlerIRQ


  sub     sp,sp,#4      ;decrement sp(to store jump address)stmfd     sp!,{r0}      ;PUSH the work register to stack(lr does not push because it return to originaladdress)


  ldr r0,=HandleIRQ ;load the address of HandleXXX to r0ldr r0,[r0]      ;load the contents(service routine start address) of HandleXXXstr r0,[sp,#4] ;store the contents(ISR) of HandleXXX to stackldmfd sp!,{r0,pc} ;POP the work register and pc(jump to ISR)##2


  至于具体的跳转原理下面再说


  好了,这样的话就容易看的多了,很明显,     HandlerIRQ 还是一个标号,IRQ异常向量就是跳转到这里执行的,这里粗略看一下,应该是保存现场,然后跳转到真正的处理函数,那么很容易发现了这么一句 ldr r0,=HandleIRQ ,没错,我们又找到了一个标号 HandleIRQ ,看来真正的处理函数应该是这个 HandleIRQ ,继续寻找AREA RamData, DATA,READWRITE


  ^ _ISR_STARTADDRESS         ; _ISR_STARTADDRESS=0x33FF_FF00HandleReset      # 4


  HandleUndef      # 4


  HandleSWI         # 4


  HandlePabort # 4


  HandleDabort # 4


  HandleReserved # 4


  HandleIRQ         # 4


  最后我们发现在这里找到了 HandleIRQ ,^ 其实就是 MAP ,这段程序的意思是,从 _ISR_STARTADDRESS开始,预留一个变量,每个变量一个标号,预留的空间为 4个字节,也就是 32BIT,其实这里放的是真正的C写的处理函数的地址,说白了,就是函数指针 - -这样做的话就很灵活了


  接着,我们需要安装IRQ处理句柄,说白了,就是设置处理函数的地址,让PC指针可以正确的跳转。


  于是我们在接着的找到安装句柄的语句


  ; Setup IRQ handler


  ldr     r0,=HandleIRQ ;This routine is neededldr     r1,=IsrIRQ      ;if there is not 'subs pc,lr,#4' at 0x18, 0x1cstr     r1,[r0]


  说白了就是将 IsrIRQ 的地址填到 HandleIRQ对应的地址里面,前面说了 HandleIRQ 放的是中断处理的函数的入口地址,我们继续找 IsrIRQ


  IsrIRQ


  sub     sp,sp,#4 ;reserved for PC


  stmfd     sp!,{r8-r9}


  ldr     r9,=INTOFFSET


  ldr     r9,[r9]


  ldr     r8,=HandleEINT0


  add     r8,r8,r9,lsl #2


  ldr     r8,[r8]


  str     r8,[sp,#8]


  ldmfd     sp!,{r8-r9,To understand this code, you need to learn about the interrupt system of 2440. INTOFFSET stores the offset number of the current interrupt. Based on the offset, you can know which interrupt source caused the interrupt. Note that we are talking about interrupts, not exceptions. Let's take a look at what the original table looks like^ _ISR_STARTADDRESS ; _ISR_STARTADDRESS=0x33FF_FF00##3 HandleReset # 4 HandleUndef # 4 HandleSWI # 4 ] HandlePabort # 4 HandleDabort # 4[/ color] HandleReserved # 4 [color=#00 0000] HandleIRQ # 4 HandleFIQ # 4 HandleEINT0 # 4 HandleEINT1 # 4 [size =4] HandleEINT2 # 4 [color=#0000 00] HandleEINT3 # 4 ....... As you can see, the first few are exceptions. HandleEINT0 is where the IRQ exception vector is stored. This way you can understand why that instruction is executed in IsrIRQ above. ldr r8,=HandleEINT0 add r8,r8,r9,lsl #2 The reason is very simple. HandleEINT0 is the entry of all IRQ interrupt vector tables. On top of this address, add an appropriate offset, INTOFFSET, then we know which IRQ is requesting the interrupt now. As for how to jump specifically? First of all, as we said, the memory starting from HandleEINT0 stores the function pointer of the interrupt service function. In the ARM system, each pointer variable occupies 4 bytes. Here is why 4 bytes of space are allocated for each label, which contains the function pointer! ! ! Let's see how to jump. Let's continue to see IsrIRQ, which implements the jump. str r8,[sp,#8] ldmfd sp!,{r8-r9,pc} In fact, the core is these two sentences. First, find the address of the current interrupt service routine, put it in R8, then pop the stack and pop it to PC, then PC will naturally jump to the interrupt service routine. As for the stack problem here, it is a very tricky one. You need to understand the ARM interrupt architecture. If you need to understand it, you can read the ARM Architecture and Programming carefully. Our focus here is to study how to jump. Finally, let's take a look at how the terminal vector is installed in the C code, for example, how the external interrupt of the key is set up. Refer to the code in /src/keyscan.c. It's very simple, there are only 3 functions in it. It is the main function of the key test Key_ISR is the key interrupt service function In KeyScan_Test, we found that there is such a sentence pISR_EINT0 = pISR_EINT2 = pISR_EINT8_23 = (U32)Key_ISR; Can you understand it? Key_ISR is the key interrupt service function mentioned above. The name of the function represents the address of the function! ! ! ! Send the address of the interrupt service function, note that it is an address, which is a U32 type variable. Send it to several variables. We take pISR_EINT0 as an example and check the header file definition. In 2440addr.h found in // Interrupt vector #define pISR_EINT0 (*(unsigned *)(_ISR_STARTADDRESS+0x20))_ISR_STARTADDRESS Does it feel familiar? Yes, it is mentioned in the assembly code just analyzed^ _ISR_STARTADDRESS ; _ISR_STARTADDRESS=0x33FF_FF00##4 HandleReset # 4 HandleUndef # 4 Yes, the address is here, and _ISR_STARTADDRESS+0x20 is to skip the previous exception vector and enter the entrance of the IRQ interrupt vector, so let's talk about the end. pISR_EINT0 = (U32)Key_ISR; The completed operation is to store the address of Key_ISR into the IRQ vector table of HandleEINT0 # 4! ! ! When a key interrupt occurs, an IRQ exception interrupt occurs The current PC value -4 is saved in LR_IRQ, and then executed b HandlerIRQ Then execute HandlerIRQ sub sp,sp,#4 ; Reserve one to store the PC address stmfd sp!,{r0} ; Save R0 because ldr r0,=HandleIRQ is used below; Load the address of HandleIRQ (service program) to R0ldr r0,[r0] str r0,[sp,#4] ; Save to the reserved place just now ldmfd sp!,{r0,pc}; Pop the stack, restore R0, and pop the calculated HandleIRQ address to PC. The stack grows downward, so SUB SP, SP, #4 is equivalent to PUSH XX, but this XX is not used at this time, because it is implemented by forcibly moving the SP pointer. Then get the address of the service program, and put this value back to the empty space on the stack just reserved, and finally POP out R0 to restore, and send the address of the service program just obtained to PC, so the effect is to jump to HandleIRQ. Next, look at how to install HandleIRQ just now ; Setup IRQ handler ldr r0,=HandleIRQ ;This routine is neededldr r1,=IsrIRQ ;if there is not 'subs pc,lr,#4' at 0x18, 0x1cstr r1,[r0] It can be seen that the value of the address of IsrIRQ is saved in HandleIRQ, that is to say, the IRQ service program above actually refers to IsrIRQ at this time! So the next thing is to transfer to IsrIRQ execution: IsrIRQ sub sp,sp,#4 ; Reserve a value to save PC stmfd sp!,{r8-r9} ldr r9,=INTOFFSET ; Calculate the offset, explained below: ldr r9,[r9] ldr r8,=HandleEINT0 add r8,r8,r9,lsl #2 ldr r8,[r8] str r8,[sp,#8] ; Because two registers R8 R9 are saved, SP is shifted down by 8 bits ldmfd sp!,{r8-r9,pc} ; Restore registers and pop to PC, same as above##5 How to save, operate SP, the last part of popping to PC is the same as the above example, let's talk about the calculation part in the middle to calculate the offset, in fact, the principle is very simple, first INTOFFSET saves which IRQ interrupt is currently, for example 0 represents HandleEINT0, 1 represents HandleEINT1 .....Wait, this is not random, there is a table, this is from the S3C2440 datasheet, you can check it yourself. Then we get the vector table of the interrupt handling function. The first address of this table is HandleEINT0, so it is natural to think, how to look up the table? Isn't that simple? HandleEINT0 + INTOFFSET is it? The base address plus the offset will get an item in the table. Of course, because each item of the interrupt handling vector occupies 4 bytes, it is processed with lsl #2. Shifting left by 2 bits is equivalent to multiplying by 4, and the offset is multiplied by 4. This should be easy to understand. In this example, we find HandleEINT0 and read the value inside. It contains the address of the HandleEINT0 service function. Where does this address come from? It is set in the C program. Let's look at the keyscan.c program and find a void KeyScan_Test(void) function, which contains the following sentence: pISR_EINT0 = pISR_EINT2 = pISR_EINT8_23 = (U32)Key_ISR; Three key interrupt service routines are installed here. We only focus on interrupt 0, that is, pISR_EINT0 = (U32)Key_ISR; What does this sentence mean? Let's look at the definition of pISR_EINT0 first. It is defined in 2440addr.h. #define pISR_EINT0 (*(unsigned *)(_ISR_STARTADDRESS+0x20)) Did you see it? Isn't _ISR_STARTADDRESS the entry address of the exception vector just mentioned? After adding 0x20, it actually points to HandleEINT0!!! So, the above means that the entry address of the Key_ISR processing function is sent to HandleEINT0. Let's look at Key_ISR again. This is a typical service routine. _irq is added as a compilation keyword to tell the compiler that this function is an interrupt service routine and needs to save the required registers to avoid being destroyed. For details, please refer to the description on page 283 of "ARM Architecture and Programming". static void __irq Key_ISR(void) ] { ... [ color=#000000] } After adding the _irq keyword, the compiler will handle all the saving actions and you don't need to worry too much. But this is a keyword of the ARM-CC compiler, and there is no such thing in GCC, so it is better to save it yourself when GCC handles interrupts. So far, the entire interrupt process has been explained. I really learned a lot in the process of analysisc program, find a void KeyScan_Test(void) function, which contains the following sentence: pISR_EINT0 = pISR_EINT2 = pISR_EINT8_23 = (U32)Key_ISR; Three key interrupt service routines are installed here. We only focus on interrupt 0, that is, pISR_EINT0 = (U32)Key_ISR; What does this sentence mean? Let's look at the definition of pISR_EINT0 first. It is defined in 2440addr.h. #define pISR_EINT0 (*(unsigned *)(_ISR_STARTADDRESS+0x20)) Did you see it? Isn't _ISR_STARTADDRESS the entry address of the exception vector just mentioned? After adding 0x20, it actually points to HandleEINT0!!! So, the above means that the entry address of the Key_ISR processing function is sent to HandleEINT0. Let's look at Key_ISR again. This is a typical service routine. _irq is added as a compilation keyword to tell the compiler that this function is an interrupt service routine and needs to save the required registers to avoid being destroyed. For details, please refer to the description on page 283 of "ARM Architecture and Programming". static void __irq Key_ISR(void) ] { ... [ color=#000000] } After adding the _irq keyword, the compiler will handle all the saving actions and you don't need to worry too much. But this is a keyword of the ARM-CC compiler, and there is no such thing in GCC, so it is better to save it yourself when GCC handles interrupts. So far, the entire interrupt process has been explained. I really learned a lot in the process of analysisc program, find a void KeyScan_Test(void) function, which contains the following sentence: pISR_EINT0 = pISR_EINT2 = pISR_EINT8_23 = (U32)Key_ISR; Three key interrupt service routines are installed here. We only focus on interrupt 0, that is, pISR_EINT0 = (U32)Key_ISR; What does this sentence mean? Let's look at the definition of pISR_EINT0 first. It is defined in 2440addr.h. #define pISR_EINT0 (*(unsigned *)(_ISR_STARTADDRESS+0x20)) Did you see it? Isn't _ISR_STARTADDRESS the entry address of the exception vector just mentioned? After adding 0x20, it actually points to HandleEINT0!!! So, the above means that the entry address of the Key_ISR processing function is sent to HandleEINT0. Let's look at Key_ISR again. This is a typical service routine. _irq is added as a compilation keyword to tell the compiler that this function is an interrupt service routine and needs to save the required registers to avoid being destroyed. For details, please refer to the description on page 283 of "ARM Architecture and Programming". static void __irq Key_ISR(void) ] { ... [ color=#000000] } After adding the _irq keyword, the compiler will handle all the saving actions and you don't need to worry too much. But this is a keyword of the ARM-CC compiler, and there is no such thing in GCC, so it is better to save it yourself when GCC handles interrupts. So far, the entire interrupt process has been explained. I really learned a lot in the process of analysish defines #define pISR_EINT0 (*(unsigned *)(_ISR_STARTADDRESS+0x20)) Did you see it? Isn't _ISR_STARTADDRESS the entry address of the exception vector just mentioned? After adding 0x20, it actually points to HandleEINT0!!! So, the above means that the entry address of the Key_ISR processing function is sent to HandleEINT0. Let's look at Key_ISR again. This is a typical service program. _irq is added as a compilation keyword to tell the compiler that this function is an interrupt service program and the required registers must be saved to avoid being destroyed. For details, please refer to the description on page P283 of "ARM Architecture and Programming". static void __irq Key_ISR(void) { ....... } After adding the _irq keyword, the compiler will handle all the saving actions and you don't need to worry too much. But this is a keyword of the ARM-CC compiler, and there is no such thing in GCC, so it is better to save it yourself when GCC handles interrupts. So far, the entire interrupt process has been explained. I really learned a lot in the process of analysish defines #define pISR_EINT0 (*(unsigned *)(_ISR_STARTADDRESS+0x20)) Did you see it? Isn't _ISR_STARTADDRESS the entry address of the exception vector just mentioned? After adding 0x20, it actually points to HandleEINT0!!! So, the above means that the entry address of the Key_ISR processing function is sent to HandleEINT0. Let's look at Key_ISR again. This is a typical service program. _irq is added as a compilation keyword to tell the compiler that this function is an interrupt service program and the required registers must be saved to avoid being destroyed. For details, please refer to the description on page P283 of "ARM Architecture and Programming". static void __irq Key_ISR(void) { ....... } After adding the _irq keyword, the compiler will handle all the saving actions and you don't need to worry too much. But this is a keyword of the ARM-CC compiler, and there is no such thing in GCC, so it is better to save it yourself when GCC handles interrupts. So far, the entire interrupt process has been explained. I really learned a lot in the process of analysis

This post is from Microcontroller MCU
 

Guess Your Favourite
Just looking around
Find a datasheet?

EEWorld Datasheet Technical Support

Related articles more>>
Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号
快速回复 返回顶部 Return list