The code following is the first part of u-boot to define interrupt vector table, and my question is how every line will be used. I understand the first 2 lines which is the starting point and the first instruction to implement: reset, and we define reset below. But when will we use these instructions below? According to System.map, every instruction has a fixed address, so _fiq is at 0x0000001C, when we want to execute fiq, we will copy this address into pc and then execute,right? But in which way can we jump to this instruction: ldr pc, _fiq? It's realised by hardware or software? Hope I make myself understood correctly.
>.globl _start
>_start:b reset
> ldr pc, _undefined_instruction
> ldr pc, _software_interrupt
> ldr pc, _prefetch_abort
> ldr pc, _data_abort
> ldr pc, _not_used
> ldr pc, _irq
> ldr pc, _fiq
>_undefined_instruction: .word undefined_instruction
>_software_interrupt: .word software_interrupt
>_prefetch_abort: .word prefetch_abort
>_data_abort: .word data_abort
>_not_used: .word not_used
>_irq: .word irq
>_fiq: .word fiq
If you understand reset then you understand all of them.
When the processor is reset then hardware sets the pc to 0x0000 and starts executing by fetching the instruction at 0x0000. When an undefined instruction is executed or tries to be executed the hardware responds by setting the pc to 0x0004 and starts executing the instruction at 0x0004. irq interrupt, the hardware finishes the instruction it is executing starts executing the instruction at address 0x0018. and so on.
00000000 <_start>:
0: ea00000d b 3c <reset>
4: e59ff014 ldr pc, [pc, #20] ; 20 <_undefined_instruction>
8: e59ff014 ldr pc, [pc, #20] ; 24 <_software_interrupt>
c: e59ff014 ldr pc, [pc, #20] ; 28 <_prefetch_abort>
10: e59ff014 ldr pc, [pc, #20] ; 2c <_data_abort>
14: e59ff014 ldr pc, [pc, #20] ; 30 <_not_used>
18: e59ff014 ldr pc, [pc, #20] ; 34 <_irq>
1c: e59ff014 ldr pc, [pc, #20] ; 38 <_fiq>
00000020 <_undefined_instruction>:
20: 00000000 andeq r0, r0, r0
00000024 <_software_interrupt>:
24: 00000000 andeq r0, r0, r0
00000028 <_prefetch_abort>:
28: 00000000 andeq r0, r0, r0
0000002c <_data_abort>:
2c: 00000000 andeq r0, r0, r0
00000030 <_not_used>:
30: 00000000 andeq r0, r0, r0
00000034 <_irq>:
34: 00000000 andeq r0, r0, r0
00000038 <_fiq>:
38: 00000000 andeq r0, r0, r0
Now of course in addition to changing the pc and starting execution from these addresses. The hardware will save the state of the machine, switch processor modes if necessary and then start executing at the new address from the vector table.
Our job as programmers is to build the binary such that the instructions we want to be run for each of these instructions is at the right address. The hardware provides one word, one instruction for each location. Now if you never expect to ever have any of these exceptions, you dont have to have a branch at address zero for example you can just have your program start, there is nothing magic about the memory at these addresses. If you expect to have these exceptions, then you have two choices for instructions that are one word and can jump out of the way of the exception that follows. One is a branch the other is a load pc. There are pros and cons to each.
When the hardware takes an exception, the program counter (PC) is automatically set to the address of the relevant exception vector and the processor begins executing instructions from that address. When the processor comes out of reset, the PC is automatically set to base+0
. An undefined instruction sets the PC to base+4
, etc. The base address of the vector table (base
) is either 0x00000000, 0xFFFF0000, or VBAR
depending on the processor and configuration. Note that this provides limited flexibility in where the vector table gets placed and you'll need to consult the ARM documentation in conjunction with the reference manual for the device that you are using to get the right value to be used.
The layout of the table (4 bytes per exception) makes it necessary to immediately branch from the vector to the actual exception handler. The reasons for the LDR PC, label
approach are twofold - because a PC-relative branch is limited to (24 << 2) bits (+/-32MB) using B
would constrain the layout of the code in memory somewhat; by loading an absolute address the handler can be located anywhere in memory. Secondly it makes it very simple to change exception handlers at runtime, by simply writing a different address to that location, rather than having to assemble and hotpatch a branch instruction.
There's little value to having a remappable reset vector in this way, however, which is why you tend to see that one implemented as a simple branch to skip over the rest of the vectors to the real entry point code.
User contributions licensed under CC BY-SA 3.0