【问题标题】:Assembly code for creating interrupts vector on LPC2148 ARM processor在 LPC2148 ARM 处理器上创建中断向量的汇编代码
【发布时间】:2011-03-23 19:59:31
【问题描述】:

我最近才开始使用 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 指令,我将不胜感激。

塔尔

【问题讨论】:

    标签: assembly embedded arm


    【解决方案1】:

    我将删除条件代码,因为它只会使事情复杂化:

    // 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
    
    _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
    
            .size _boot, . - _boot
            .endfunc
    

    让我们先看看重置/启动向量:

    b   _start
    

    该指令是一个无条件分支(跳转)到标记为“_start”的代码,我在您的 sn-p 中没有看到它。基本上它将是初始化堆栈和处理器寄存器的程序集,也许一些内存区域然后可能跳转到执行大部分初始化的 C 例程。

    接下来是执行无效指令时 ARM 将进入的“未定义”向量(如果我没记错的话):

    ldr pc, _undf
    

    该指令使用“_undf”符号处的地址加载pc 寄存器(“程序计数器”或指令指针)。如果我们查看_undf 变量,它包含由符号__undf 表示的地址。所以ldr pc, _undf 指令用__undf 的地址加载pc - 跳转到__undf:

    __undf 我们看到:

    __undf: b     .  
    

    这只是指向同一地址的一个分支 - 一个无限循环(. 符号表示“此处”或“此位置”)。

    因此,对于这些向量中的大多数(遵循与未定义向量相同的技术),它们只会跳转到小的无限循环。您可以将这些标签处的无限循环替换为适用于相关向量的代码,但许多项目不这样做,因为这些向量触发将代表某种严重错误。

    最后,最有趣的向量槽是 IRQ 向量:

    ldr   pc,[pc,#-0xFF0]           // IRQ - read the VIC
    

    这看起来像 NXP 设备的处理程序。

    它使用从与pc 寄存器相关的内存位置读取的值加载pc 寄存器。由于在 ARM 架构上,IRQ 向量始终位于地址 0x00000018(我将忽略可以将向量映射到其他地方的实现或使用不同向量模型的 Cortex-M3 等 ARM)并且因为在指令流水线对使用PC 寄存器值进行寻址的影响中,这条指令将读取的内存位置将是0xFFFFF030,这是“向量中断”中内存映射的VICVectAddr 寄存器的地址控制器`(VIC):

     'apparent'      Pipeline      offset in      effective
      PC value        effect       the opcode      address
    -------------   ------------   -----------    ----------
      0x00000018  +  0x00000008  -  0x0000ff0  == 0xfffff030
    

    该设备寄存器将包含刚刚发生的中断的中断处理程序的地址(当然,VIC 需要正确初始化,以便它知道该地址)。

    因此,当ldr pc,[pc,#-0xFF0] 指令执行时,它将加载pc 寄存器以及相应设备的中断处理程序例程的地址(基本上,跳转到正确的中断处理程序)。

    【讨论】:

      【解决方案2】:

      中断向量表的许多/大多数处理器实现是具有中断处理程序的地址列表。复位向量包含复位处理程序的第一条指令所在的内存地址。中断向量是中断处理程序代码的第一条指令所在的地址,等等。

      传统上 ARM 不这样做。大多数 ARM 内核实际上在该地址执行,因此该表包含可执行指令。因为该表中每 4 个字节都有一个入口点,所以您基本上想要一些分支指令。您可以使用多条指令,但正如您所见,第二条或第三条指令成为其他异常的入口点。如果您确定永远不会有其他例外情况之一,您可以侥幸逃脱,但这通常是不明智的。如果您将自己限制在一条指令上,您的选择是 pc 相关分支或具有附近地址的加载 pc。因此,ldr pc 正在将某些代码的第一条指令的地址加载到程序计数器中,以便它实质上分支到该代码。乙。是一个汇编器(仅限gnu?)语法捷径意味着分支到自我,或无限循环中的分支。如果目的地离异常表足够近,你可以简单地 b __irq。有各种解决方案和各种原因让您选择一种或另一种方式,我不会涉及。

      相对较新的 Cortex-M3 和其他仅限 thumb2 的 ARM 处理器(thumb 是 ARM 指令集的简化形式,thumb2 是 thumb 指令集的扩展)。您可以在 Cortex-M3 上仅运行 thumb 或 thumb2,但不能运行任何 ARM 指令,因此指令表的传统 (ARM) 模型要么必须更改为 16 位、2 字节指令表,要么具有一起改变。他们所做的是选择与大多数其他处理器非常相似的模型,他们使用了一个地址表。例外情况是在地址 0 处,表中有一个 32 位值被加载到堆栈指针中,这对于嵌入式来说是一个很好的特性。复位后,内核将读取地址 0 的 32 位,将该值放入堆栈指针,读取地址 0x4 的 32 位并将该值放入程序计数器,实质上是跳转到该地址。请注意,cortex-m3 中的异常列表与传统 ARM 内核中的列表不同。例如,cortex-m3 表中有很多中断处理程序向量。

      【讨论】:

        【解决方案3】:

        尝试猜测:

            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  
        

        ldr 似乎 load r用指向 ISR 的指针来注册 pc,所以这是一种跳转到这些例程的方式。

        #if 0
        #else
        

        这些闻起来像条件​​编译指令,就像 C 的预处理器一样。很快,这个

            ldr   pc,_swi                   // SWI - _swi
        

        调用存储在_swi中的例程,定义为

        _swi:   .word __swi                     // SWI
        

        (因为#if 0 永远不会为真,所以只有#else 部分很重要),并且__swi 在下面几行中定义:

        __swi:  b     .                         // SWI
        

        继续猜测那个点可能意味着你可以指定你的中断例程的地址,因此使用那个。

        【讨论】:

        • 谢谢,现在我更明白发生了什么。你说我可以自己指定地址是什么意思?你的意思是现在它是未定义的?
        • @Tal:是的,我认为它是未定义的。你的编译器的帮助应该说明那个点是什么
        猜你喜欢
        • 1970-01-01
        • 2020-01-21
        • 2011-09-01
        • 2012-07-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-04-28
        • 1970-01-01
        相关资源
        最近更新 更多