Exception process in wince & arm-repost-the first post on the forum will be reprinted, and there will be a lot of originality in the future

Source: Internet
Author: User
Tags prefetch
Posted on Walzer

Author: Walzer
Date: 2005.3.11

I was specifically corrected by Intel's Application Engineer, "interrupted vector table", which should be called exception vector. according to my understanding, only IRQ and FIQ should be used as interrupt. For example, reset and several abort are called exceptions.

Generally, when a hardware exception occurs, the CPU jumps to the 0x00000000 address access interruption vector table (normal exception vectors ), however, the ARM920T/arm10 series CPU support placing the interrupt vector table in the high address 0xffff0000 (high exception vectors ). when CP15: bi13. of the coprocessor is CP15: bi13. that is, when CP15: bit13 = 0, the jump to the low address; When CP15: bit13 = 1, the jump to the high address. according to an Intel Application Engineer, the address is physical address before MMU enabling, and virtual address after enabling. The address remains to be confirmed.

Because wince uses the same linker as other applications when compiling NK. nb0 or xboot. nb0, from 0x00000000 ~ 0x00001000 this section is reserved and is reserved as the PE Header for the application (portable executable header. I have not understood what to use yet. It is described in detail on msdn ). our code starts from the line 0x00001000. however, there is a jump command "B 1000" at 0x00000000 ". in fact, there are several lines of code in the middle, but I don't know what to do. It's just a matter of jumping over and not executing it.

Because of the above, the exception vectors cannot be placed in the normal low address in wince. You can only use the high address to place them.

In wince500/private/winceos/coreos/nk/kernel/ARM/armtrap. in seconds, the following vectorinsturctions code will be copied to the 0xffff0000 location, occupying 8*4 = 32bit length. the IRQ interrupt that we use most often is at 0xffff0018. 0x14 is retained for future extension and is not used currently. according to the arm system, there should be seven kinds of interruptions. in fact, only IRQ and FIQ will jump to oeminterrupthandler (the latter is 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
This is only the first jump. After jump to 0xffff03e0, This is the vector table ). this vector table is not in armtrap. s, but exvector in the same directory. defined separately in S. Here is the entry address of each 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

In fact, whether it is predictionvectors or vectortable, they are placed in the ROM at the beginning, which requires them to be moved to the specified position when the system starts up, the following code is intended for this purpose.
; 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. After running this step, different exceptions will be entered into their ISR. these ISR are still in armtrap. s. take irqhandler as an example. jump from 0xffff03f8 to the ISR entrance: nested_entry irqhandler, which is a compilation of a long time later. Forget the first line and faint. these are all done by wince and do not need to be modified by the OEM. In fact, for the moment, we need to understand an identifier and then click "call ".

The celoginterrupt function is called for the first time. It is located in wince500/private/winceos/coreos/nk/kernel/logger. in C, the substantive work is an interrupt counter, which adds a global variable for calculating the number of interruptions.

Next we call our friendly "oeminterrupthandler". We provide Intr. c In Intel's BSP, which implements the oeminterrupthandler function. What does this function do? First, we use oemaddresstalbe to shadow the interrupt controller registers on the CPU to the virtual address. In fact, we only care about one of the following points: ichp [IRQ], which indicates the IRQ highest priority field, therefore, we only need to extract the five bits for judgment. of course, what we get here is the physical IRQ of the hardware (some parts are translated as the device interrupt number). We need to use oalintrtranslateirq
To the logical sysintr number (scheduling interrupt number) that can be used in the system ).

The IRQ Number can be found in BSP (bxxxxx_intr.h), and sysintr is divided into three categories: manually assigned by the OEM as stipulated by wince/dynamic acquisition during driver running. wince in public/common/oak/INC/nkintr. h specifies some core sysintr, such as sysintr_nop, resched, break, chain, timeing, rtc_alarm, etc. In BSP, bsp_cfg.h defines sysintr of peripheral devices, such as OHCI, UART, keypad, etc. in the previous two cases, sysintr is in the Registry platform. REG must also write a value, and it must be the same as defined in the program. the third is the sysintr dynamically obtained during the runtime. I haven't met it yet. It's not very clear.

The sysintr number corresponding to the IRQ is returned from oeminterrupthandler and placed in R0.

Next, call the function pointer "pfnoemintroccurs" and trace it in private. The pointer is in schedule. c points to the fakedoemintroccurs function, and this function is on the top of the value assignment statement. The specific operation is: Return dwsysintr. haha, the name of a function is fake. I want to catch it myself and deserve it.

Next, "CMP r0, # sysintr_resched", the polling time slice is used up, so we have to schedule it. pendevents will wait for the next time slice to solve the problem. the specific practice is very rigorous, but I still don't quite understand the arm assembly, so I'm amazed.

In this way, the ISR in the Assembly is finished.

The device driver at the top layer is now used.
In the initialize process, device drvier must have an initialization interruption process. A typical practice is to call the getisrinfo function to read IRQ and sysintr from the Registry (of course, interfacetype and busnumber are also read), and then a m_histevent handler will be declared earlier, createvent is used to create an ist event. if the event is successfully created, interruptinitialize (sysintr, m_histevent) is used to associate the scheduling interrupt number with istevent. you can find the interruptinitialize API usage in wince help.

For interruptinitialize, you can find the line ln1355 in wince500/private/winceos/coreos/CORE/dll/coredll. Def:
Interruptinitialize = xxx_interruptinitialize @ 627

That is, interruptinitialize actually belongs to coredll. DLL, and when called, it is actually to run the xxx_interruptinitialize function (the first three words are estimated to be politically sensitive words written by the MS programmer. When the result is submitted, the code is blocked by the system as XXX, haha) the xxx function cannot see the source code, but we can trace and capture its compilation.
Xxx_interruptinitialize:
03f70744 mov R12, SP
03f70748 running dB SP !, {R0-R3}
03f7074c MongoDB SP !, {R4, R12, LR}
03f70750 sub sp, SP, #0x14
$ M20120:
03f70754 mov R3, #0
03f70758 sub R3, R3, # 0xe, 22
03f7075c LDR R3, [R3]
03f70760 sub R3, R3, #0x14
03f70764 LDR R3, [R3]
03f70768 TST R3, #1
03f7076c beq | $ m20120 + 44 h (03f70798) |
03f70770 LDR R3, [PC, #0x74]
03f70774 LDR R3, [R3]
03f70778 CMP R3, #0
03f7077c beq | $ m20120 + 44 h (03f70798) |
03f70780 LDR R3, [PC, #0x64]
03f70784 LDR R3, [R3]
03f70788 add R3, R3, #0x69, 30
03f7078c LDR R3, [R3]
03f70790 STR R3, [Sp, # 0xc]
03f70794 B | $ m20120 + 4ch (03f707a0) |
03f70798 LDR R3, [PC, #0x48]
03f7079c STR R3, [Sp, # 0xc]
03f707a0 LDR R3, cbdata
03f707a4 LDR R2, pvdata
03f707a8 LDR R1, hevent
03f707ac LDR r0, idint
03f707b0 LDR R4, [Sp, # 0xc]
03f707b4 mov LR, PC
03f707b8 BX r4
03f707bc STR r0, [Sp, #0x10]
03f707c0 LDR R3, [Sp, #0x10]
03f707c4 STR R3, [Sp, #4]
03f707c8 add r0, SP, #0
03f707cc BL | killthreadifneeded_t ::~ Killthreadifneeded_t (03f60928) |
03f707d0 LDR R3, [Sp, #4]
03f707d4 STR R3, [Sp, #8]
03f707d8 LDR r0, [Sp, #8]
03f707dc add SP, SP, #0x14
03f707e0 ldmia sp, {R4, SP, LR}
03f707e4 BX lR
@ _ @ I don't understand. Hope you can give me some advice.

In addition, you can see ln579 in wince500/public/common/oak/INC/mkfuncs. h.
Bool xxx_interruptinitialize (DWORD idint, handle hevent, lpvoid pvdata, DWORD cbdata );
# Define interruptinitialize xxx_interruptinitialize
The include path of the header file is mkfuncs. h-> kfuncs. h-> WINBASE. h-> Windows. h. there may be other branches, but if you want to call them, include <windows. h> that's all.

Next, let's briefly describe the main call hierarchy xxx_inteeruptinitialize-> SC _interruptinitialize (wince500/private/winceos/coreos/nk/kernel/intrapi. c)-> dointerruptenable (same as the intrapi. c)-> oeminterruptenable (wince500/platform/common/intr/common/OEM. c)-> oalintrenableirqs (wince500/platform/common/intr/PXA27X/Intr. c)

The three paragraphs are mainly used to show that the interruption is not all enable after the entire system is started up, but enable is implemented only when interruptinitialize is called according to the currently loaded module, this can be an unmask operation on the bit corresponding to the intr mask register, or an gpio edge detect enable, depending on the hardware approach to interrupt implementation.

Next, after interruptinitialize in the driver, set the thread priority in cesetpriority. now let's jump to the driver's ist to check out. The starting point is generally a big while with a waitforsingleobject (m_histevent, infinite ),

OK. After the device Initialization is complete, interrupt service thread maintains a suspend state and waitforsingleobject. after the interruption occurs, the IRQ will be converted to sysintr in oeminterrupthandler and the system will be notified (this is taken for granted, I have not seen how to notify the system, it is estimated that the Code MS is not public), because sysintr and istevent are already associated, The system receives the sysintr and sets the related event. in this way, ist jumps out of waitforsingleobject and continues execution. Then, it reads the interrupt Status Register of its module for judgment and operates on other register control hardware. After completion, it calls the interruptdone function to inform the system, then I went back to waitforsinigleobject and waited.

In the entire interrupt processing process, only one piece of code left is not seen/found. That is, how to notify the system after converting to sysintr, and setevent of the interrupt handling event of the relevant module. I guess it is in private. If you want to learn more, you can start with the xxx_interruptxxxxxx series functions mentioned above.

-------------------------------------------------------

The following is an example of how to enable a Stuart interrupt in the existing code for application use (Intel PXA270 platform). The steps are as follows:

1. first, check whether irq_stuart is defined in platform/windowtv/src/common/PXA27X/INC/bulverde_intr.h and whether the defined interrupt number is the same as that in PXA27X manual. add it as needed.

2. in/platform/windowtv/src/common/intr/PXA27X/Intr. add irq_stuart to the g_inpriorties array in C. this array is used when interrupt priority registers is initialized in oalintrinit ().

3. Add the system interrupt number sysintr_stuart to/platform/windowtv/src/INC/bsp_cfg and H. Use define here.

4. In/platform/windowtv/src/kernel/oal/Intr. C, add the software and hardware interrupt number associated with oalintrstatictranslate (sysintr_stuart, irq_stuart) in the bspintrinit () function );

Finally, create an event in the driver, and use interruptinitialize (sysintr, istevent, 0, 0) to associate the system interrupt number with the event, then, the hardware interruption will be able to go into the system interruption and then set the event. what do drivers do if they get events ......

----- The end -----
Copyright @ Walzer 2005

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.