单片机
返回首页

ARM的启动代码(3):异常向量

2016-07-13 来源:eefocus

ARM的启动代码是非常重要的代码,直接关系到系统的稳定性和可靠性(这里主要讨论arm7, arm9;cortex系列的会在后续的文章中讨论)。上次我们通过两则文章讨论了ARM启动代码的过程,

ARM的启动代码(1):介绍

ARM的启动代码(2):AT91SAM9260启动详解

 这次我们聊聊ARM的代码的具体编写。那么什么样的代码会涉及到这些问题呢?

1.Bootloader或者位于启动序列上进行加载其他应用程序的程序;

2.单独的二进制镜像,直接可以在ARM处理器上直接执行。

 

这两种代码都需要对 ARM 的启动过程有深入理解。说深入理解,其实只有一条,闹闹记住,ARM7,ARM9的异常向量表从地址0开始。这是铁打不能改变的事实。这样一来,所有的程序都要用0地址存储自己的向量表,这岂不是成了稀缺资源。所以不同家的ARM芯片都提供了一些办法解决这种问题。

 

对于arm7,很多芯片使用片内的flash。如at91sam7x256。为了方便,经常需要bootloader + 应用程序的方式。At91sam7x256提供了一个叫boot Memory的地址,1Mbytes, 从0x0~0x000F FFFF。可以映射成为内部的Flash和内部的SRAM。这个映射是:

Boot Memory: 0x0~0x000F FFFF, 1Mbytes

Internal Flash: 0x0010 0000~0x001F FFFF, 1Mbytes

Internal SRAM:0x0020 0000~0x002F FFFF, 1Mbytes

当Boot Memory映射成Internal Flash,flash的地址仍然从0x0010 0000开始,但是从0x0访问,等同于访问0x0010 0000;当映射成为 Internal SRAM,SRAM地址不变,访问0x0地址,等同于访问0x0020 0000。其实硬件做起来很简单,就是将地址线用逻辑电路稍微处理一下。

 

由于代码都要存放在FLASH里,否则,没电以后,啥都没有了,也无法再次启动。所以,异常向量要从0x0开始,那么自然也要把向量放在这个位置,7x256上电以后默认boot memory 映射从flash开始。也就是说,把向量放在0x0010 0000即可解决这样的问题。

 

当0x0010 0000被占用以后,bootloader 的向量问题解决了,那用户代码的中断向量怎么办呢?不可能把bootloader的向量擦了,把用户自己的向量写入,那岂不是bootloader也完了?这里有个小技巧,如果用户程序从0x0010 1000开始,向量依然从这个位置开始。只不过,在打开中断,向量真正起作用前,将0x0010 1000这个地方向量到SRAM的首地址上,然后切换Boot Memory映射SRAM。那么向量依然从0x0开始。这样的话,无论多少级boot代码,都可以完美的解决该问题。

 

ARM9除了以上的方法,还有个终极的利器,那就是MMU,你高兴放哪就放哪,在向量起作用之前,用MMU将其地址变换为0即可。一点技术含量都没有。

 

道理总是简单的,实现起来总是有点点弯弯绕。我们看看实际的实现吧。这是7x256的向量代码:

 

__vector:

    LDR     PC, [PC,#24]    ; Absolute jump can reach 4 GByte

    LDR     PC, [PC,#24]    ; Branch to undef_handler

    LDR     PC, [PC,#24]    ; Branch to swi_handler

    LDR     PC, [PC,#24]    ; Branch to prefetch_handler

    LDR     PC, [PC,#24]    ; Branch to data_handler

    DC32    0               ; Reserved

    LDR     PC, [PC,#24] ; Branch to irq_handler

    LDR     PC, [PC,#24] ; Branch to fiq_handler

 

 

    DC32    _program_start

    DC32    ARM_ExceptUndefInstrHndlr

    DC32    ARM_ExceptSwiHndlr

    DC32    ARM_ExceptPrefetchAbortHndlr

    DC32    ARM_ExceptDataAbortHndlr

    DC32    0

    DC32    ARM_ExceptIrqHndlr

DC32    ARM_ExceptFiqHndlr

 

这里相对比较简单,对这个指令做一下解释。LDR     PC, [PC,#24]是将当前PC+24的地址的值载入到PC寄存器中。由于ARM流水线的问题,当前执行的指令,地址已经是后面两条了。所以,是+24并不是+32。也就是把_program_start加载入PC指针里。这段代码已经消除了指令当前位置对跳转位置的影响,可以随意的拷贝到任意的地方去执行。这段代码放在0x20 0000地方,可以正常执行;放在0x10 0000地方也可以正常执行。

 

RTEMS的ARM9(CSB337)启动向量:

vector_block:

        ldr     pc, Reset_Handler

        ldr     pc, Undefined_Handler

        ldr     pc, SWI_Handler

        ldr     pc, Prefetch_Handler

        ldr     pc, Abort_Handler

        nop

        ldr     pc, IRQ_Handler

        ldr     pc, FIQ_Handler

 

Reset_Handler:           b       bsp_reset

Undefined_Handler:      b       Undefined_Handler

SWI_Handler:             b       SWI_Handler

Prefetch_Handler:       b       Prefetch_Handler

Abort_Handler:           b       Abort_Handler

                        nop

IRQ_Handler:            b       IRQ_Handler

FIQ_Handler:            b       FIQ_Handler

Rtems是个复杂的操作系统,在汇编代码里安装的只是一个简单的复位向量。其它向量都只是简单的死循环。操作系统运行起来以后,还要再次安装向量的。向量指向操作系统的复杂的处理函数。但不管这些,向量存储的地址是没有改变的。Link脚本上可以看到向量被放在内部的SRAM的首地址上。(CSB337)

 

SECTIONS

{

    .base :

    {

        _sram_base = .;

 

/* reserve room for the vectors and function pointers */

arm_exception_table = .;    

. += 64;

连接器虽然把位置空出来了,但连接器依然不知道将vector_block放到什么位置。怎么办?这里的代码解释了一切。

 

        /*

         * Initialize the MMU. After we return, the MMU is enabled,

         * and memory may be remapped. I hope we don't remap this

         * memory away.

         */

        ldr     r0, =mem_map

        bl      mmu_init

 

        /*

         * Initialize the exception vectors. This includes the

         * exceptions vectors (0x00000000-0x0000001c), and the

         * pointers to the exception handlers (0x00000020-0x0000003c).

         */

        mov     r0, #0

        adr     r1, vector_block

        ldmia   r1!, {r2-r9}

        stmia   r0!, {r2-r9}

        ldmia   r1!, {r2-r9}

        stmia   r0!, {r2-r9}

 

Gnu的工具链并不针对某一个具体的平台。所以解决方案从某种意义上说,更具有普遍意义。先调用mmu_init,这是干什么,实际上是将MMU初始化,将我们定义的.base地址放到0x0位置去。然后紧接着下面的几行代码,是将上面的中断向量到0x0位置去。一共64个字节,实现4GB内的地址绝对跳转。

进入单片机查看更多内容>>
相关视频
  • RISC-V嵌入式系统开发

  • SOC系统级芯片设计实验

  • 云龙51单片机实训视频教程(王云,字幕版)

  • 2022 Digi-Key KOL 系列: 你见过1GHz主频的单片机吗?Teensy 4.1开发板介绍

  • TI 新一代 C2000™ 微控制器:全方位助力伺服及马达驱动应用

  • MSP430电容触摸技术 - 防水Demo演示

精选电路图
  • 用数字电路CD4069制作的万能遥控轻触开关

  • 红外线探测报警器

  • 短波AM发射器电路设计图

  • RS-485基础知识:处理空闲总线条件的两种常见方法

  • 带有短路保护系统的5V直流稳压电源电路图

  • 基于ICL296的大电流开关稳压器电源电路

    相关电子头条文章