在LPC2148 ARM处理器上创建中断向量的汇编代码
我最近刚刚开始使用 LPC2148 ARM 处理器。
我试图理解一些有关创建中断向量的汇编代码。
这是代码:
// Runtime Interrupt Vectors
// -------------------------
Vectors:
b _start // reset - _start
ldr pc,_undf // undefined - _undf
ldr pc,_swi // SWI - _swi
ldr pc,_pabt // program abort - _pabt
ldr pc,_dabt // data abort - _dabt
nop // reserved
ldr pc,[pc,#-0xFF0] // IRQ - read the VIC
ldr pc,_fiq // FIQ - _fiq
#if 0
// Use this group for production
_undf: .word _reset // undefined - _reset
_swi: .word _reset // SWI - _reset
_pabt: .word _reset // program abort - _reset
_dabt: .word _reset // data abort - _reset
_irq: .word _reset // IRQ - _reset
_fiq: .word _reset // FIQ - _reset
#else
// Use this group for development
_undf: .word __undf // undefined
_swi: .word __swi // SWI
_pabt: .word __pabt // program abort
_dabt: .word __dabt // data abort
_irq: .word __irq // IRQ
_fiq: .word __fiq // FIQ
__undf: b . // undefined
__swi: b . // SWI
__pabt: b . // program abort
__dabt: b . // data abort
__irq: b . // IRQ
__fiq: b . // FIQ
#endif
.size _boot, . - _boot
.endfunc
我根本不明白这里发生了什么。 如果有人可以向我解释这个过程,特别是这里如何使用 ldr 指令,我将不胜感激。
塔尔
I have just recently started to work with LPC2148 ARM processor.
I am trying to understand some assembly code regarding the creation of the interrupts vector.
here is the code:
// Runtime Interrupt Vectors
// -------------------------
Vectors:
b _start // reset - _start
ldr pc,_undf // undefined - _undf
ldr pc,_swi // SWI - _swi
ldr pc,_pabt // program abort - _pabt
ldr pc,_dabt // data abort - _dabt
nop // reserved
ldr pc,[pc,#-0xFF0] // IRQ - read the VIC
ldr pc,_fiq // FIQ - _fiq
#if 0
// Use this group for production
_undf: .word _reset // undefined - _reset
_swi: .word _reset // SWI - _reset
_pabt: .word _reset // program abort - _reset
_dabt: .word _reset // data abort - _reset
_irq: .word _reset // IRQ - _reset
_fiq: .word _reset // FIQ - _reset
#else
// Use this group for development
_undf: .word __undf // undefined
_swi: .word __swi // SWI
_pabt: .word __pabt // program abort
_dabt: .word __dabt // data abort
_irq: .word __irq // IRQ
_fiq: .word __fiq // FIQ
__undf: b . // undefined
__swi: b . // SWI
__pabt: b . // program abort
__dabt: b . // data abort
__irq: b . // IRQ
__fiq: b . // FIQ
#endif
.size _boot, . - _boot
.endfunc
I dont understand what is going on here at all.
If someone could explain to me the process, especially how the ldr instruction is used here, i would appreciate it.
Tal
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
许多/大多数处理器的中断向量表实现是具有中断处理程序的地址列表。复位向量包含复位处理程序的第一条指令所在的内存地址。中断向量是中断处理程序代码的第一条指令所在的地址等。
传统上 ARM 不会这样做。大多数 ARM 内核实际上在该地址执行,因此该表包含可执行指令。因为该表中每 4 个字节就有一个入口点,所以您基本上希望有一些分支指令的风格。您可以使用多个指令,但正如您所看到的,第二条或第三条指令成为其他异常的入口点。如果您确定永远不会遇到其他例外情况之一,您可以侥幸逃脱,但这通常是不明智的。如果您将自己限制在一条指令上,那么您的选择是与计算机相关的分支或具有附近地址的加载计算机。因此,ldr pc,某物正在将某些代码的第一条指令的地址加载到程序计数器中,以便它本质上分支到该代码。 b.是一个汇编程序(仅限 gnu?)语法快捷方式,意味着分支到自身,或无限循环中的分支。如果目的地距离异常表足够近,您可以简单地 b __irq。有多种解决方案和多种原因让您选择一种或另一种方式,我不会详细讨论。
相对较新的Cortex-M3和其他仅使用thumb2的ARM处理器(thumb是ARM指令集的简化形式,thumb2是thumb指令集的扩展)。您可以在 Cortex-M3 上仅运行拇指或拇指2,但不能运行任何 ARM 指令,因此指令表的传统 (ARM) 模型要么必须更改为 16 位、2 字节指令表,要么具有一起改变一切。他们所做的是采用与大多数其他处理器非常相似的模型,他们采用了地址表。例外是在地址 0 处,表中有一个 32 位值,该值被加载到堆栈指针中,这对于嵌入式来说是一个很好的功能。复位后,内核将读取地址 0 处的 32 位,将该值放入堆栈指针中,读取地址 0x4 处的 32 位并将该值放入程序计数器中,实质上分支到该地址。请注意,cortex-m3 中的异常列表与传统 ARM 内核中的列表不同。例如,cortex-m3 表中有许多中断处理程序向量。
Many/most processors implementation of an interrupt vector table is to have a list of addresses to interrupt handlers. The reset vector contains the address in memory where the first instruction of the reset handler lives. the interrupt vector is an address where the first instruction of the interrupt handler code lives, etc.
Traditionally ARM does not do it this way. Most ARM cores actually execute at that address, so the table contains executable instructions. Because there is an entry point every 4 bytes in that table you basically want to have some flavor of branch instruction. You can use more than one instruction, but as you can see that second or third instruction becomes an entry point for other exceptions. If you are sure you will never have one of those other exceptions, you can get away with it, but it is usually unwise. If you limit yourself to one instruction your choices are a pc relative branch or a load pc with some nearby address. So the ldr pc,something is loading an address of the first instruction of some code into the program counter so that it essentially branches to that code. b . is an assembler (gnu only?) syntax short cut meaning branch to self, or branch in an infinite loop. If the destination is close enough to exception table you can simply b __irq. There are various solutions and various reasons why you would choose one way or another which I wont get into.
The relatively newer Cortex-M3 and other thumb2 only ARM processors (thumb is a reduced form of the ARM instruction set, and thumb2 is an extension to the thumb instruction set). You can run thumb only or thumb2 on a Cortex-M3, but you cannot run any ARM instructions, so the traditional (ARM) model of a table of instructions either has to change to a table of 16 bit, 2 byte instructions or it has to change all together. What they did was go for a model very similar to most other processors, they went with a table of addresses. The exception to that is at address 0 there is a 32 bit value in the table which is loaded into the stack pointer, which is a nice feature for embedded. Post reset, the core will read 32 bits at address zero, put that value in the stack pointer, read 32 bits at address 0x4 and put that value in the program counter, essentially branching to that address. Note the list of exceptions in a cortex-m3 is different than the list in a traditional ARM core. There are many interrupt handler vectors in the cortex-m3 table for example.
尝试猜测:
ldr 似乎load r注册了带有指向ISR 的指针的pc,因此它是跳转到这些例程的一种。
这些味道就像条件编译指令,就像 C 的预处理器一样。很快,这
将调用存储在 _swi 中的例程,该例程定义为
(因为
#if 0
永远不会为真,因此只有#else
部分很重要),并且 __swi 是定义了下面几行:继续猜测该点可能意味着您可以指定您的中断例程的地址,因此使用该地址。
Trying to guess:
ldr seems to load register pc with pointers to the ISR, so it's a sort of jump to those routines.
these smells like conditional compilation instructions, just like the C's preprocessor. Shortly, this
calls the routine stored at _swi, which is defined as
(because
#if 0
will never be true, so only the#else
part matters), and __swi is defined a few lines below:Keeping on guessing that dot could mean you can specify the address of your interrupt routine, therefore using that one.
我将删除条件代码,因为它只会使事情变得复杂:
让我们首先看一下重置/启动向量:
该指令是一个无条件分支(跳转)到标记为“_start”的代码,我没有看到在你的片段中。基本上,它将是初始化堆栈和处理器寄存器的程序集,也许某些内存区域可能会跳转到执行大部分初始化的 C 例程。
接下来是执行无效指令时 ARM 将转到的“未定义”向量(如果我的记忆正确的话):
该指令加载 pc 寄存器(“程序计数器”或指令指针)地址位于“_undf”符号处。如果我们查看
_undf
变量,它包含由符号__undf
表示的地址。因此,ldr pc, _undf
指令使用__undf
的地址加载pc
- 跳转到__undf:
。在
__undf
处,我们看到:这只是到同一地址的分支 - 一个无限循环(
.
符号表示“此处”或“此位置”)。因此,对于大多数这些向量(遵循与未定义向量相同的技术),它们只会跳转到小的无限循环。您可以使用适合相关向量的代码替换这些标签处的无限循环,尽管许多项目不会这样做,因为这些向量触发将代表某种严重错误。
最后,最有趣的向量槽是 IRQ 向量:
这看起来像 NXP 设备的处理程序。
它将从相对于 pc 寄存器的内存位置读取的值加载到 pc 寄存器中。由于在 ARM 架构上,IRQ 向量始终位于地址 0x00000018(我将忽略可以将向量映射到其他位置的实现或使用不同向量模型的 ARM(例如 Cortex-M3))并且因为考虑到指令流水线对使用
PC
寄存器值进行寻址的影响,该指令将读取的内存位置将为0xFFFFF030
,这是内存的地址 - “向量中断控制器”(VIC) 中映射的 VICVectAddr 寄存器:该设备寄存器将包含刚刚发生的中断的中断处理程序的地址(当然,VIC 需要正确初始化,以便知道该地址)。
因此,当执行
ldr pc,[pc,#-0xFF0]
指令时,它将使用适当设备的中断处理程序例程的地址加载pc
寄存器(基本上,跳转到正确的中断处理程序)。I'm going to remove the conditional code, since it just complicates things:
Lets look first at the reset/start vector:
That instruction is an unconditional branch (a jump) to the the code labeled "_start" which I don't see in your snippet. Basically it'll be the assembly that initializes the stack and processor registers, maybe some memory areas then probably jumps to a C routine that performs the bulk of the initialization.
Next is the "undefined" vector that the ARM will go to when an invalid instruction is executed (if my memory is right):
That instruction loads the
pc
register (the 'program counter' or instruction pointer) with the address at the "_undf" symbol. If we look at the_undf
variable, it contains the address represented by symbol__undf
. So theldr pc, _undf
instruction loadspc
with__undf
's address - jumping to__undf:
.And at
__undf
we see:That's just a branch to the same address - an infinite loop (the
.
symbol means 'here' or 'this location').So for most of these vectors (which follow the same technique as the undefined vector), they'll just jump to little infinite loops. You could replace the infinite loops at those labels with code that's appropriate for the vector in question, though many projects don't because those vectors firing would represent some sort of serious error.
Finally, the vector slot that's most interesting is the IRQ vector:
This looks like the handler for an NXP device.
It loads the
pc
register with the value read from a memory location that's relative to thepc
register. Since on the ARM architecture the IRQ vector is always at address 0x00000018 (I'm going to ignore implementations that can map the vectors elsewhere or ARMs like the Cortex-M3 that use a different vector model) and because of the instruction pipeline's effect on using thePC
register value for addressing, the memory location that will be read by this instruction will be0xFFFFF030
, which is the address of the memory-mappedVICVectAddr
register in the 'Vectored Interrupt Controller` (VIC):This device register will contain the address of the interrupt handler for the interrupt that just occurred (of course the VIC needs to be properly initialized so it knows that address).
So when the
ldr pc,[pc,#-0xFF0]
instruction executes, it'll load thepc
register with the address of the interrupt handler routine for the appropriate device (basically, jumping to the right interrupt handler).