分析ARM启动代码和中断处理过程

发布者:谁与争锋1最新更新时间:2015-11-13 来源: eefocus关键字:ARM  启动代码  中断处理 手机看文章 扫描二维码
随时随地手机看文章
之前有分析过44b0下的这个启动代码,差别不是非常大,今天再重新看了一遍。启动代码与 Bootloader不同,主要是指进入C语言之前的汇编代码,网上都称为是bootloader的stage1,一般通用的内容包括:
1. 定义程序入口点
2. 设置异常向量表
3. 初始化存储系统
4. 初始化用户程序的执行环境
5. 初始化堆栈指针寄存器,改变处理器的模式
6. 设置FIQ/IRQ中断处理程序入口
7. 进入C程序

1)编译器选择
    GBLL     THUMBCODE
     [ {CONFIG} = 16 
THUMBCODE SETL   {TRUE}
         CODE32
         |   
THUMBCODE SETL   {FALSE}
         ]
因为处理器分为16位 32位两种工作状态,程序的编译器也是分16位和32两种编译方式,所以这段程序用于根据处理器工作状态确定编译器编译方式,程序不难理解,主要解释一下 符号“[     |     ]”的意思,上面的程序是指:
if({CONFIG} = 16 )
     { THUMBCODE SETL {TRUE} 
       CODE32 }
else
       THUMBCODE SETL {FALSE}
还 是没有不明白CONFIG怎么区分是16位还是32位?哪里决定它的取值?应该是编译器指定的这个值。

2)宏定义
        MACRO
$HandlerLabel HANDLER $HandleLabel
$HandlerLabel
     sub     sp,sp,#4        
     stmfd     sp!,{r0}        
     ldr      r0,=$HandleLabel
     ldr      r0,[r0]         
     str      r0,[sp,#4]      
     ldmfd    sp!,{r0,pc}    
     MEND
$HandlerLabel是宏的地址标 号,HANDLER是宏名,$HandleLabel是宏的参数,$标号在宏指令展开时,标号会替换为用户定义的符号。在此后,所有遇 到$HandlerLabel HANDLER $HandleLabel这种形式的表达式都会被展开成$HandlerLabel到MEND之间的函数。
例如:ADC_IRQ HANDLER HandleADC即代表如下函数,语句ldr pc,=ADC_IRQ的作用也就是跳转到这个函数:
ADC_IRQ
     sub     sp,sp,#4        
     stmfd     sp!,{r0}        
     ldr      r0,=HandleADC
     ldr      r0,[r0]         
     str      r0,[sp,#4]      
     ldmfd    sp!,{r0,pc}
这段程序用于把ADC中断服务程序的首地址装载到pc中,跳转到中断处理函数,称之为 “加载程序”。HandleADC是一个地址标号,它的内容就是ADC中断服务程序的地址标号,即文件最后的那个表HandleADC # 4所示,将HandleADC # 4中的4换成中断服务程序的地址标号即是,程序在这里定义了一个数据区,存放各种中断服务程序的首地址。每个字空间都有一个标号,以Handle***命 名。

3)寄存器及堆栈设置
按照上面的顺序,可以比较容易读懂启动代码的作用,主要就是通过设置特殊功能寄存器来达到对系统参数的 设定。依次禁止看门狗,中断,设定时钟控制寄存器,存储器控制寄存器等等。
由于各个工作模式下的堆栈指针是相互独立的,所以要分别进入各个模式下 设置其堆栈指针,基本上都差不多,比如未定义指令模式下的设置:
     mrs     r0,cpsr
     bic     r0,r0,#MODEMASK
     orr     r1,r0,#UNDEFMODE|NOINT
     msr     cpsr_cxsf,r1        
     ldr     sp,=UndefStack
UnderStack是在程序后面用UnderStack #   256建立的一个堆栈空间的首地址,这部分空间建立在RAM中,256字节空间的堆栈大小。

4)初始化用户程序的执行环境
之前在 44B0里的启动代码里还有包括把ROM里的程序拷贝到RAM中并跳转到RAM运行程序,也就是把加载状态下的程序按照编译连接时的设置重新排布成运行时 的程序状态,以达到符号能够正确连接的目的,这里是涉及到了所谓的映像文件,但是2410这里没有这一段,即程序的加载态就是它的运行态,所以要求烧写程 序时必须要把它烧写在设置的RO地址上,否则程序将不能正确执行。下面这段程序实现RW数据初始化,只是把数据段复制到高地址,如果没有设置RW的话这段 代码也不会执行。
     ;Copy and paste RW data/zero initialized data
     ldr     r0, =|Image$$RO$$Limit| ; Get pointer to ROM data
     ldr     r1, =|Image$$RW$$Base|   ; and RAM copy
     ldr     r3, =|Image$$ZI$$Base|  
    
     ;Zero init base => top of initialised data
     cmp     r0, r1       ; Check that they are different
     beq     %F2
1       
     cmp     r1, r3      
     ldrcc     r2, [r0], #4            
     strcc     r2, [r1], #4   
     bcc     %B1
2       
     ldr     r1, =|Image$$ZI$$Limit| 
     mov     r2, #0
3       
     cmp     r3, r1       ; Zero init
     strcc     r2, [r3], #4
     bcc     %B3
b %F1(B1)的意思是在临近的地址标号跳转,F是向后寻找,B是向前寻找。 

5) 说说映象文件
用ADS编译产生的映像文件有.axf、.bin、.hex等等格式,就拿要直接烧进Flash里的.bin文件来说,在其他书上看 到有这么句话“映像文件一般由域组成,域由最多三个输出段(RO,RW,ZI)组成,输出段又由输入段组成。”
对于这段话,前两句是比较好理解 的,域就是整个映像文件,对于大部分程序来说就只有一个域,也就是烧进Flash里的那部分东东,称作加载域;输出段就是用AREA定义的RO,Rw,一 般就这两个,拿前面的bootloader来说,整体框架是这样的:
     AREA     Init,CODE,READONLY     ;<--RO段
     ENTRY
Entry                          ;<--CODE部分

… …

SMRDATA DATA               ;<--DATA部分

… …

     AREA          RamData, DATA,             READWRITE     ;<--RW段

… …

然而对于 输出段又由输入段组成却着实糊涂了好一阵,输入段是指源程序的代码(CODE)部分和数据(DATA)部分。上面这个框架中,在RO输出段的Entry下 开始一系列的汇编指令操作,这个应该是CODE输入段,而SMRDATA DATA引领DCD用于开辟一片数据存储空间,这部分应该是DATA输入段,它与RW里的数据不同之处在于这部分数据不能被修改。
在ADS编译前 在ARM-Linker里的Ro_Base和Rw_Base两个地址值,就是指两个输出段的起始地址,即程序是按照你设置的这种方式排布在内存中的,各个 地址标号根据这两个值确定。然而,用Ultraedit打开bin文件却发现其实Rw是跟在Ro后面的,也就是说,这两个段并没有按照你设置的地址起始, 由此引出映像文件的加载域和运行时域两个概念。[page]
加载域就是用Ultraedit打开看到的程序最原始的状态,而运行时域则是程序在执行时按照你设 定的方式排布的状态,显然,上面设置的两个地址是针对运行时域来设置的,程序要满足上面的设置才能正确连接。也就是程序开始阶段(加载域状态)是不能正确 连接的,不过开始时不需要用到Rw里的数据,程序是可以运行的,因此必须在需要用到Rw数据之前把它拷贝到上面设置的位置上,这就是bootloader 里初始化用户程序的执行环境部分的作用,把数据移动到正确的位置!
拷贝完Rw里的数据之后,所有的符号都可以正确连接,这时跳转到main函数里 去执行程序就可以了。2410的这段启动代码没有进行Ro的拷贝,所以如果你把程序烧在0x0地址,那么Ro就必须设置成0x0,如果你设置成 0x30000000,那么Ro就必须设置成0x30000000,如果Rw不设置,它将默认跟在Ro后面,否则就执行上面的搬迁代码,挪到正确的位置 上。由于本系统是采用NandFlash启动的,最初的启动代码必须要在0x0处的SRAM里执行,所以,如果要把这段启动代码当作NandFlash的 启动代码的话,Ro就必须设成0x0。

6).中断处理过程
要使用中断,首先需要清掉程序状态寄存器CPSR里的IRQ位,这个很容易被忽略了。再之后才是考虑与中断有关的相应寄 存器.

这个几个寄存器比较容易弄混了:
SRCPND/SUBSRCPND:只要中断产生的条件满足,例如外部电平,定时溢出等 等,SRCPND/SUBSRCPND的相应位就会被置位,它不管其他地方的设置如何,所以某一时刻可能有几个位同时被置位了(几个中断同时产生)。
INTMSK/INTSUBMSK: 这个是中断屏蔽位,清零表示允许中断请求,默认是禁止了所有的中断请求。
INTPND:它表示处理器接下来就要去处理的那个中断,某一时刻只可能 有一个位被置位。这个寄存器置位的必要条件是SRCPND/SUBSRCPND已经是1,而且INTMSK/INTSUBMSK相应位已经清零。
SRCPND/SUBSRCPND 和INTPND都不会自动清零,要程序向相应的位写1才能清零,这个有点奇怪。

2410不支持中断嵌套,中断产生后处理器进入到IRQ模 式,只有在等到这个中断处理完之后才能响应下一次中断。
如果同时产生多个中断,就涉及到了中断优先级的问题。SRCPND寄存器对应的32个中断 源总共被分为6个组,每个组由一个ARBITER(0~5)对其进行管理。中断必须先由所属组的ARBITER(0~5)进行第一次优先级判断然后再到 ARBITER6进行第二次判断。可以更改的只是组里的优先级顺序。
PRIORITY的各个位被分为两种类型,一种是ARB_MODE,另一种为 ARB_SEL,拿ARBITER0来说,这个组一共包含了四种中断源:EINT0~EINT3,分别对应Req0~Req3,很明显ARB_SEL0就 是决定了这四种中断的优先顺序,如果这个组里的两个中断同时产生,将会把排在前面的先传递给ARBITER06进行第二次判断。ARB_MODE0置1代 表开启优先级次序旋转,当该位置为1之后,ARB_SEL0的值会在每处理完一次中断后顺次改变。

中断处理流程
启动代码开始是一 个异常向量表,这个向量表是固定的,由处理器决定,必须要放在0x0地址那个地方,这个跟51单片机的中断向量表相类似。
    b     ResetHandler  
     b     HandlerUndef     ;handler for Undefined mode
     b     HandlerSWI     ;handler for SWI interrupt
     b     HandlerPabort     ;handler for PAbort
     b     HandlerDabort     ;handler for DAbort
     b     .         ;reserved
     b     HandlerIRQ     ;handler for IRQ interrupt 
     b     HandlerFIQ     ;handler for FIQ interrupt
当产生IRQ中断时,PC首先无条件地来到0x18这个地址处,这个 0x18就是处理器决定的IRQ中断的入口地址,所以要在这个地址处放一条跳转指令b HandlerIRQ,PC接着跳转到HandlerIRQ地址标号处,这里存放着一个宏语句:
HandlerIRQ       HANDLER HandleIRQ
按照上面说的宏展开,其实是执行这么一段语句:
     sub     sp,sp,#4         ;留下堆栈的第一个位置
    stmfd     sp!,{r0}         ;保护R0因为后面要用R0传递值
   ldr      r0,=HandleIRQ   ;将HandleIRQ这个地址标号的值传如R0
     ldr      r0,[r0]          ;取存放在HandleIRQ里的那个值
    str      r0,[sp,#4]       ;把取到的值压入栈
   ldmfd    sp!,{r0,pc}      ;恢复R0并把PC指向HandleIRQ里存放的地址值
HandleIRQ里存放是什么值呢?代码最后有个这样的表,这个表就是在SDRAM 里的另外一张异常向量表,这张表可以根据需要修改_ISR_STARTADDRESS的值来随意更改它的位置。
         ^    _ISR_STARTADDRESS
HandleReset      #    4
HandleUndef      #    4
HandleSWI        #    4
HandlePabort     #    4
HandleDabort     #    4
HandleReserved   #    4
HandleIRQ        #    4
HandleFIQ        #    4
这里实现结构化一片地址空间的目的,可见在 HandleIRQ这里预留了4个字节的空间,但是这个空间里现在放的是什么东西呢?
在代码的初始化过程中有这么一段代码:
   ldr     r0,=HandleIRQ     ; Setup IRQ handler  
     ldr     r1,=IsrIRQ          
     str     r1,[r0]
原 来是把IsrIRQ所在的地址值放到这个地方,那就是宏实现了把PC指向IsrIRQ的目的。程序来到IsrIRQ:
IsrIRQ  
     sub     sp,sp,#4       ;预留堆栈
     stmfd     sp!,{r8-r9}    ;保护R8,R9
    
     ldr     r9,=INTOFFSET  ;找出产生哪种中断
     ldr     r9,[r9]
     ldr     r8,=HandleEINT0
     add     r8,r8,r9,lsl #2 
     ldr     r8,[r8]
     str     r8,[sp,#8]
     ldmfd     sp!,{r8-r9,pc}   ;将PC指向相应的中断处理地址
假如产生了EINT0中断来到了这里, 那么PC将会跳转到HandleEINT0里存放的地址值,与上面的相同,程序里有这个表:
HandleEINT0        #    4
HandleEINT1        #    4
HandleEINT2        #    4
HandleEINT3        #    4
HandleEINT4_7     #    4
    .
    .
    .
这个表在2410addr.h头文件里也有对应的定义,指向的是同样的一块地方:
    .
    .
    .
#define pISR_EINT0      (*(unsigned *)(_ISR_STARTADDRESS+0x20))
#define pISR_EINT1      (*(unsigned *)(_ISR_STARTADDRESS+0x24))
#define pISR_EINT2      (*(unsigned *)(_ISR_STARTADDRESS+0x28))
    .
    .
    .
问 题是HandleEINT0存放的又是什么值呢?这就需要在初始化EINT0的时候写上这么一句:
pISR_EINT0 = (unsigned )_IsrEINT0Service;
也就是把 EINT0的中断处理函数的地址写到HandleEINT0地址处存放,那么到此PC就可以跳转到_IsrEINT0Service里去了,这里完成你所 需要的中断处理过程。

关键字:ARM  启动代码  中断处理 引用地址:分析ARM启动代码和中断处理过程

上一篇:ARM AMBA总线介绍
下一篇:基于ARM的硬件启动程序设计-初始化堆栈

推荐阅读最新更新时间:2024-03-16 14:39

基于ARM核的ADμC7024在医疗电子中的应用
0 引言 随着信息技术的迅猛发展和人民生活水平的提高,极大地推动了医疗电子设备的发展,当今医疗电子设备的发展趋势是高精度、实时性、低功耗和小尺寸,作为医疗电子设备中核心地位的MCU(微处理器)也随着这一发展趋势向前不断衍变着。由早期的8位MCU发展到目前的32位RISC(精简指令集计算机)MCU。美国ADI公司根据市场的需要最新推出了一款基于ARM(高级精简指令集计算机)核的微处理器ADμC7024便是目前32位RISC MCU的杰出代表。ADμC7024卓越的处理能力、集成众多片上外围器件和芯片低功耗的特点,完全胜任目前医疗电子设备的需求及未来的发展目标。 本文以ADμC7024在医疗电子中监护产品脉搏血氧计的
[单片机]
Syntiant如何利用孵化器和Arm生态系统进行AI开发
本文编译自Arm官方博客,作者 Syntiant公司硬件副总裁Dave Garrett Syntiant成立于2017年,联合创始人兼首席执行官Kurt Busch经历了一个激动人心的时刻。当他观看孩子用iPad聊天时,他看到了一个商机,就是将人工智能(AI)和机器学习(ML)创新带入语音激活系统,并改变它们的交互方式。 截至2021年1月,Syntiant已向市场交付了两个AI芯片,出货量超过1000万个,其技术已在Amazon Alexa上获得认证,并在CES上获得殊荣。不到四年,该公司就从包括亚马逊,应用材料,博世,英特尔投资,微软和摩托罗拉在内的全球一些最大的投资者和公司筹集了超过6500万美元的风险投资。 这
[嵌入式]
基于ARM处理器的嵌入式防火墙总体架构设计
集中式防火墙通常架构在内部网络与外部网络之间,用于对流入和流出网络的数据包进行实时检测,过 滤存在安全威胁的数据信息.集中式防火墙处理数据信息速度快、消耗低、延迟短,目前已经广泛应用于网络 规划建设中.但是,集中式防火墙的架构实现需要浪费大量资金成本,只能够对外部网络流入的安全威胁进行检测,无法保障内部网络的安全,而且其架构依赖于网 络拓扑结构,存在较多的问题和弊端。分布式防火墙能 够解决集中式防火墙实现过程中的诸多问题。 本文研究的是基于硬件的嵌入式防火墙实现机制,给出了基于ARM处理器的嵌入式防火墙安全保 护机制总体设计,提出了嵌入式防火墙软件架构和硬 件架构,详细分析了嵌入式防火墙实现过程中的关键 技术,包括芯片选型、硬
[单片机]
基于<font color='red'>ARM</font>处理器的嵌入式防火墙总体架构设计
如何安装arm-linux-gcc编译器
如何安装ram-linux-gcc,之前安装过几次,刚开始没有成功,觉得很麻烦,后来又一次,安装成功了,爽啊,所以想跟各位分享一下。 首先准备好arm-linux-gcc的包,比如说是arm-linux-gcc-fh.tar.gz,首先是解压缩,路径可以随便放,但是个人建议最好放在/opt这个目录下面,使用如下命令: tar xvzf arm-linux-gcc-fh.tar.gz(空格)C(空格/(注意这之间的空格),然后就解压缩好了,接下来就是要修改一下配置文件了,切换到root权限,然后,输入如下命令: vi /etc/bash.bashrc 然后在最后面一行加入如下语句: expor
[单片机]
ARM内核的中断技术
1 ARM7TDMI简介 ARM7TDMI是一款经典的通用32位微处理器,采用精简指令系统(RISC)和流水线结构。典型应用如GPS、PDA、双向寻呼机、移动电话、板卡间高速通信等。 ARM7TDMI 定义有7种工作模式,本文涉及到的有:用户模式,程序正常运行时的模式;快中断模式,处理器响应快中断而进入的模式;中断模式,处理响应常规中断而进入的模式;监督模式,操作系统的保护模式。处理器响应软件中断时即进入监督模式。下面重点介绍ARM7TDMI的中断特性。主要特性如表1所列。 表1 中断特性表 向量地址 中断类型 工作模式 优先级 返回地址 0x8 软件中断 SWI模式 6 pc-4 0x18 常规则中断 IRQ模式
[单片机]
基于ARM与MEMS的AHRS设计
目前MEMS传感器在消费类电子产品中得到广泛应用,但是MEMS角速率陀螺仪存在严重的零点漂移和随机误差,在捷联惯性导航解算中会产生积分误差,难以达到应用的精度。加速度计和磁场计能分别测量出重力加速度和地磁场这两个不相关的三维矢量,可以作为平台姿态的观测矢量来校准陀螺仪。扩展卡尔曼滤波可以结合这几种传感器的特点,以陀螺仪测量得到的角速率作预测更新,以重力加速度和磁场观测更新,得到更高精度的姿态角信息。   1 硬件结构   MEMS器件的AHRS硬件基本组成为三轴角速率陀螺仪、三轴加速度计、三轴磁阻传感器和STM32系列微处理器STM32F103U8T6。航向姿态参考系统的硬件结构如图1所示。   IMU采用整合了16位的三轴
[模拟电子]
基于<font color='red'>ARM</font>与MEMS的AHRS设计
ARM嵌入式汽车节能控制系统
  引言   由于各种原因,公交车总是不断重复加速—减速或停车—再加速的过程。通过加装本节能装置,当汽车需要制动时,在主控单元的控制下,可将汽车行驶时具有的巨大动能通过空气压缩机转化成高压气体的势能并储存起来,从而实现汽车减速或停车。当汽车需要启动或加速时,用储存起来的高压气体势能代替燃油来驱动汽车,从而实现汽车能量的回收再利用,达到节能的效果。同时由于汽车在起动或加速时能耗最大,如果汽车是用燃油驱动,则此时油料燃烧不充分,燃烧效果最差,而且产生的噪音最大。   系统工作原理   本系统主要由三部分组成,即检测部分,控制部分和执行机构。检测部分包括踏板位置传感器、曲轴位置传感器、压缩机活塞位置传感器、汽车运行速度传感器、储气
[汽车电子]
从0学ARM-基于Exynos4412的pwm详解
一、什么是PWM PWM,英文名Pulse Width Modulation,是脉冲宽度调制缩写,它是通过对一系列脉冲的宽度进行调制,等效出所需要的波形(包含形状以及幅值),对模拟信号电平进行数字编码,也就是说通过调节占空比的变化来调节信号、能量等的变化,占空比就是指在一个周期内,信号处于高电平的时间占据整个信号周期的百分比,例如方波的占空比就是50%. 二、PWM信号输出输出和作用 1. 如果要实现PWM信号输出如何输出呢? 1)可以直接通过芯片内部模块输出PWM信号,前提是这个I/O口要有集成的pwm控制器,只需要通过对应的寄存器即可,这种自带有PWM输出的功能模块在程序设计更简便,同时数据更精确。 2)但是如果IC内部没有
[单片机]
从0学<font color='red'>ARM</font>-基于Exynos4412的pwm详解
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
随便看看
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved