




已阅读5页,还剩22页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
一直以来对于ARM体系中所描述的RO,RW和ZI数据存在似是而非的理解,这段时间对其仔细了解了一番,发现了一些规律,理解了一些以前书本上有的但是不理解的东西,我想应该有不少人也有和我同样的困惑,因此将我的一些关于RO,RW和ZI的理解写出来,希望能对大家有所帮助。要了解RO,RW和ZI需要首先了解以下知识:ARM程序的组成此处所说的“ARM程序”是指在ARM系统中正在执行的程序,而非保存在ROM中的bin映像(image)文件,这一点清注意区别。一个ARM程序包含3部分:RO,RW和ZIRO是程序中的指令和常量RW是程序中的已初始化变量ZI是程序中的未初始化的变量由以上3点说明可以理解为:RO就是readonly,RW就是read/write,ZI就是zeroARM映像文件的组成所谓ARM映像文件就是指烧录到ROM中的bin文件,也成为image文件。以下用Image文件来称呼它。Image文件包含了RO和RW数据。之所以Image文件不包含ZI数据,是因为ZI数据都是0,没必要包含,只要程序运行之前将ZI数据所在的区域一律清零即可。包含进去反而浪费存储空间。Q:为什么Image中必须包含RO和RW?A:因为RO中的指令和常量以及RW中初始化过的变量是不能像ZI那样“无中生有”的。ARM程序的执行过程从以上两点可以知道,烧录到ROM中的image文件与实际运行时的ARM程序之间并不是完全一样的。因此就有必要了解ARM程序是如何从ROM中的image到达实际运行状态的。实际上,RO中的指令至少应该有这样的功能:1. 将RW从ROM中搬到RAM中,因为RW是变量,变量不能存在ROM中。2. 将ZI所在的RAM区域全部清零,因为ZI区域并不在Image中,所以需要程序根据编译器给出的ZI地址及大小来将相应得RAM区域清零。ZI中也是变量,同理:变量不能存在ROM中在程序运行的最初阶段,RO中的指令完成了这两项工作后C程序才能正常访问变量。否则只能运行不含变量的代码。说了上面的可能还是有些迷糊,RO,RW和ZI到底是什么,下面我将给出几个例子,最直观的来说明RO,RW,ZI在C中是什么意思。1; RO看下面两段程序,他们之间差了一条语句,这条语句就是声明一个字符常量。因此按照我们之前说的,他们之间应该只会在RO数据中相差一个字节(字符常量为1字节)。Prog1:#include void main(void);Prog2:#include const char a = 5;void main(void);Prog1编译出来后的信息如下:=Code RO Data RW Data ZI Data Debug948 60 0 96 0 Grand Totals=Total RO Size(Code + RO Data) 1008 ( 0.98kB)Total RW Size(RW Data + ZI Data) 96 ( 0.09kB)Total ROM Size(Code + RO Data + RW Data) 1008 ( 0.98kB)=Prog2编译出来后的信息如下:=Code RO Data RW Data ZI Data Debug948 61 0 96 0 Grand Totals=Total RO Size(Code + RO Data) 1009 ( 0.99kB)Total RW Size(RW Data + ZI Data) 96 ( 0.09kB)Total ROM Size(Code + RO Data + RW Data) 1009 ( 0.99kB)=以上两个程序编译出来后的信息可以看出:Prog1和Prog2的RO包含了Code和RO Data两类数据。他们的唯一区别就是Prog2的RO Data比Prog1多了1个字节。这正和之前的推测一致。如果增加的是一条指令而不是一个常量,则结果应该是Code数据大小有差别。2; RW同样再看两个程序,他们之间只相差一个“已初始化的变量”,按照之前所讲的,已初始化的变量应该是算在RW中的,所以两个程序之间应该是RW大小有区别。Prog3:#include void main(void);Prog4:#include char a = 5;void main(void);Prog3编译出来后的信息如下:=Code RO Data RW Data ZI Data Debug948 60 0 96 0 Grand Totals=Total RO Size(Code + RO Data) 1008 ( 0.98kB)Total RW Size(RW Data + ZI Data) 96 ( 0.09kB)Total ROM Size(Code + RO Data + RW Data) 1008 ( 0.98kB)=Prog4编译出来后的信息如下:=Code RO Data RW Data ZI Data Debug948 60 1 96 0 Grand Totals=Total RO Size(Code + RO Data) 1008 ( 0.98kB)Total RW Size(RW Data + ZI Data) 97 ( 0.09kB)Total ROM Size(Code + RO Data + RW Data) 1009 ( 0.99kB)=可以看出Prog3和Prog4之间确实只有RW Data之间相差了1个字节,这个字节正是被初始化过的一个字符型变量“a”所引起的。3; ZI再看两个程序,他们之间的差别是一个未初始化的变量“a”,从之前的了解中,应该可以推测,这两个程序之间应该只有ZI大小有差别。Prog3:#include void main(void);Prog4:#include char a;void main(void);Prog3编译出来后的信息如下:=Code RO Data RW Data ZI Data Debug948 60 0 96 0 Grand Totals=Total RO Size(Code + RO Data) 1008 ( 0.98kB)Total RW Size(RW Data + ZI Data) 96 ( 0.09kB)Total ROM Size(Code + RO Data + RW Data) 1008 ( 0.98kB)=Prog4编译出来后的信息如下:=Code RO Data RW Data ZI Data Debug948 60 0 97 0 Grand Totals=Total RO Size(Code + RO Data) 1008 ( 0.98kB)Total RW Size(RW Data + ZI Data) 97 ( 0.09kB)Total ROM Size(Code + RO Data + RW Data) 1008 ( 0.98kB)=编译的结果完全符合推测,只有ZI数据相差了1个字节。这个字节正是未初始化的一个字符型变量“a”所引起的。注意:如果一个变量被初始化为0,则该变量的处理方法与未初始化华变量一样放在ZI区域。即:ARM C程序中,所有的未初始化变量都会被自动初始化为0。总结:1; C中的指令以及常量被编译后是RO类型数据。2; C中的未被初始化或初始化为0的变量编译后是ZI类型数据。3; C中的已被初始化成非0值的变量编译后市RW类型数据。附:程序的编译命令(假定C程序名为tst.c):armcc -c -o tst.o tst.carmlink -noremove -elf -nodebug -info totals -info sizes -map -list aa.map -o tst.elf tst.o编译后的信息就在aa.map文件中。ROM主要指:NAND Flash,Nor FlashRAM主要指:PSRAM,SDRAM,SRAM,DDRAMImage$?$Limit 的含义对于刚学习ARM的人来说,如果分析它的启动代码,往往不明白下面几个变量的含义:|Image$RO$Limit|、|Image$RW$Base|、|Image$ZI$Base|。首先申明我使用的调试软件为ADS1.2,当我们把程序编写好以后,就要进行编译和链接了,在ADS1.2中选择MAKE按钮,会出现一个Errors and Warnings 的对话框,在该栏中显示编译和链接的结果,如果没有错误,在文件的最后应该能看到Image component sizes,后面紧跟的依次是Code,RO Data ,RW Data ,ZI Data ,Debug 各个项目的字节数,最后会有他们的一个统计数据:Code 163632 ,RO Data 20939 ,RW Data 53 ,ZI Data 17028Tatal RO size (Code+ RO Data) 184571 (180.25kB)Tatal RW size(RW Data+ ZI Data) 17081(16.68 kB)Tatal ROM size(Code+ RO Data+ RW Data) 184624(180.30 kB)后面的字节数是根据用户不同的程序而来的,下面就以上面的数据为例来介绍那几个变量的计算。在ADS的Debug Settings中有一栏是Linker/ARM Linker,在output选项中有一个RO base选项,下面应该有一个地址,我这里是0x0c100000,后面的RW base 地址是0x0c200000,然后在Options选项中有Image entry point ,是一个初始程序的入口地址,我这里是0x0c100000 。有了上面这些信息我们就可以完全知道这几个变量是怎么来的了:|Image$RO$Base| = Image entry point = 0x0c100000 ;表示程序代码存放的起始地址|Image$RO$Limit|=程序代码起始地址+代码长度+1=0x0c100000+Tatal RO size+1= 0x0c100000 + 184571 + 1 = 0x0c100000 +0x2D0FB + 1= 0x0c12d0fc|Image$RW$Base| = 0x0c200000 ;由RW base 地址指定|Image$RW$Limit| =|Image$RW$Base|+ RW Data 53 = 0x0c200000+0x37(4的倍数,0到55,共56个单元)=0x0c200037|Image$ZI$Base| = |Image$RW$Limit| + 1 =0x0c200038|Image$ZI$Limit| = |Image$ZI$Base| + ZI Data 17028=0x0c200038 + 0x4284=0x0c2042bc也可以由此计算:|Image$ZI$Limit| = |Image$RW$Base| +TatalRWsize(RWData+ZIData) 17081=0x0c200000+0x42b9+3(要满足4的倍数)=0x0c2042bc*Scatter file (分散加载描述文件)用于armlink的输入参数,他指定映像文件内部各区域的download与运行时位置。Armlink将会根据scatter file生成一些区域相关的符号,他们是全局的供用户建立运行时环境时使用。(注意:当使用了scatter file 时将不会生成以下符号:Image$RW$Base,Image$RW$Limit,Image$RO$Base,Image$RO$Limit,Image$ZI$Base,Image$ZI$Limit)二 什么时候使用scatter file当然首要的条件是你在利用ADS进行项目开发,下面我们看看更具体的一些情况。1 存在复杂的地址映射:例如代码和数据需要分开放在在多个区域。2 存在多种存储器类型:例如包含 Flash,ROM,SDRAM,快速SRAM。我们根据代码与数据的特性把他们放在不同的存储器中,比如中断处理部分放在快速SRAM内部来提高响应速度,而把不常用到的代码放到速度比较慢的Flash内。3 函数的地址固定定位:可以利用Scatter file实现把某个函数放在固定地址,而不管其应用程序是否已经改变或重新编译。4 利用符号确定堆与堆栈:5 内存映射的IO:采用scatter file可以实现把某个数据段放在精确的地指处。因此对于嵌入式系统来说scatter file是必不可少的,因为嵌入式系统采用了ROM,RAM,和内存映射的IO。三 scatter file 实例1 简单的内存映射LOAD_ROM 0x0000 0x8000EXEC_ROM 0x0000 0x8000*(+RO)RAM 0x10000 0x6000*(+RW, +ZI)LOAD_ROM(下载区域名称) 0x0000(下载区域起始地址) 0x8000(下载区域最大字节数)EXEC_ROM(第一执行区域名称) 0x0000(第一执行区域起始地址) 0x8000(第一执行区域最大字节数)*(+RO(代码与只读数据)RAM(第二执行区域名称) 0x10000(第二执行区域起始地址) 0x6000(第二执行区域最大字节数)*(+RW(读写变量), +ZI(未初始化变量)2 复杂内存映射LOAD_ROM_1 0x0000EXEC_ROM_1 0x0000program1.o(+RO)DRAM 0x18000 0x8000program1.o (+RW, +ZI)LOAD_ROM_2 0x4000EXEC_ROM_2 0x4000program2.o(+RO)SRAM 0x8000 0x8000program2.o (+RW, +ZI)LOAD_ROM_1 0x0000(下载区域一起始地址)EXEC_ROM_1 0x0000(第一执行区域开始地址)program1.o(+RO) (program1.o内的Code与RO data 放在第一执行区域)DRAM 0x18000(第二执行区域开始地址) 0x8000(第二执行区域最大字节数)program1.o (+RW, +ZI) (program1.o内的RW data与 ZI data 放在第二执行区域)LOAD_ROM_2 0x4000(下载区域二起始地址)EXEC_ROM_2 0x4000program2.o(+RO) (program2.o内的Code与RO data 放在第一执行区域)SRAM 0x8000 0x8000program2.o (+RW, +ZI) (program2.o内的RW data与 ZI data 放在第二执行区域)2.1 BNF 符号与语法:由引号赖标示的符号保持其字面原意,如A”+”B标示A+B。A := B :定义A为B。A:标示可选部分,如ABC用来标示ABC或AC。A+:用来标示A可以重复任意次,如A+可标示A,AA,AAA, A*:同A+。A | B:用来标示选择其一,不能全选。如A|B用来标示A或者B。(A B):标示一个整体,当和|符号或复杂符号的多次重复一起使用时尤其强大,如(AB)+(C|D)标示ABC,ABD,ABABC,ABABD, 2.2分散加载文件各部分描述如图2.1所示为一个完整的分散加载脚本描述结构图。下面我们对图示中各个部分进行讲述。2.2.1 加载区描述每个加载区有:名称:供连接器确定不同下载区域基地址:相对或绝对地址属性:可选最大字节数:可选执行区域列:确定执行时各执行区域的类型与位置load_region_name (base_address | (+ offset) attribute_list max_size execution_region_deion+load_region_name:下载区域名称,最大有效字符数31。(并不像执行区域段名用于Load$region_name,而是仅仅用于标示下载区域)。base_address:本区域内部目标被连接到的地址(按字对齐)。+offset:相对前一个下载区域的偏移量(4的整数倍,如果为第一个区域)。2.2.2 执行区描述每个执行区有:名称:供连接器确定不同下载区域基地址:相对或绝对地址属性:确定执行区域的属性最大字节数:可选输入段:确定放在该执行区域的模块exec_region_name (base_address | + offset) attribute_list max_sizeinput_section_deion+exec_region_name:执行区域名称,最大有效字符数31。base_address:本执行区域目标要被联接到的位置,按字对齐。+offset:相对于前一个执行区域结束地址的偏移量,4的整数倍;如果没有前继之能够行区域(本执行区域为该下载区域的第一个执行区域),则该偏移量是相对于该下载区域的基址偏移量。attribute_list:PI,OVERLAY,ABSOLUTE,FIXED,UNINIT。PI: 位置独立。OVERLAY: 覆盖。ABSOLUTE: 绝对地址。FIXED: 固定地址,下载地址与执行地址具有该地址指示确定。UNINIT: 未初始化数据。RELOC:无法明确指定执行区域具有该属性,而只能通过继承前一个执行区或父区域获得。对于PI,OVERLAY,ABSOLUTE,FIXED,我们只能选择一个,缺省属性为ABSOLUTE。一个执行区域要么直接继承其前面的执行区域的属性或者具有属性为ABSOLUTE。具有PI,OVERLAY,RELOC属性的执行区域允许其地址空间重叠,对于BSOLUTE,FIXED 属性执行区域地址空间重叠Armlink会报错。max_size:可选,他用于指使Armlink在实际分配空间大于指定值时报错。input_section_deion:指示输入段的内容。基本语法22.2.3 输入段描述输入段:模块名:目标文件名,库成员名,库文件名。名称可以使用通配符。输入段名,或输入段属性(READ-ONLY,CODE)。module_select_pattern(+ input_section_attr | input_section_pattern)(, + input_section_attr | , input_section_pattern)*)2.2.3.1module_select_pattern:选择的模块名称(目标文件,库文件成员,库文件),模块名可以使用通配符(*匹配任意多个字符,?匹配任意一个字符),名称不区分字母大小写,它是供选择的样本。例1:*libtx.a (+RO)libtx.a为threadX库文件。例2:tx_ill.o (INIT)tx_ill.o为threadX中断向量目标文件。2.2.3.2input_section_attr:输入段属性选择子,每个选择子以”+”开头,选择子不区分大小写字符。选择子可选:RO-CODE,RO-DATA,RO( selects both RO-CODE and RO-DATA),RW-DATA,RW-CODE,RW( selects both RW-CODE and RW-DATA),ZI,ENTRY( that is a section containing an ENTRY point)。以下同义词可以选择:CODE (for RO-CODE),CONST( for RO-DATA),TEXT (for RO),DATA (for RW),BSS (for ZI)。还有两个伪属性:FIRST,LAST。如果各段的先后顺序比较重要时,可以使用FIRST,LAST标示一个执行区域的第一个和最后一个段。例1:os_main_init.o (INIT ,+FIRST)FIRST表示放于本执行区域的开始处。例2:*libtx.a (+RO)RO 表示*libtx.a的只读部分。2.2.3.3input_section_pattern:输入段名。例1:os_main_init.o (INIT ,+FIRST)INIT 为os_main_init.o的一个段。例2:os_stackheap.o (heap)heap 为os_stackheap.o的一个段。例3:os_stackheap.o (stack)stack为os_stackheap.o的一个段。提高篇3.1 在scatter file中指定胶合段胶合段用于实现ARM代码到Thumb代码的切换或者实现代码的长转移。使用scatter file可以指定怎样放置胶合输入段。通常,在scatter file中一个执行区域可以拥有胶合段选择*(Venner$Code)。Armlink把胶合输入段放到拥有段选择符*(Veneer$Code)的执行区域中,这样做是安全的。可能由于地址范围问题或者受执行区域大小限制无法完成把胶合段分配个某个执行区域。如果当发生胶合段无法加到指定区域时,他将会被加到那些包含了生成胶合段的可重载输入段的执行区域。3.2 创建根执行区域根执行区域就是指那些执行与加载时地址相同的区域。当你为映像文件指定初始化入口点或者由于你只使用一个ENTRY导向符而使得连接器创建初始化入口位置时,你就必须确保该入口点位于根执行区域。如果入口点不在根执行区域,连接将会失败,连接器会报错。如:ENTRY point (0x00000000) lies within non-root region ER_ROM可以通过以下方式实现在scatter file中指定根执行区域。 显示或缺省的指定执行区的属性为ABSOLUTE,同时使得加载区域与第一个执行区域具有相同的地址。 使用FIXED属性使得执行区域的加载地址与其执行时地址保持不变。3.3 创建根执行区域可以通过在scatter file中为某个执行区域指定FIXED属性来实现该区域加载于运行时地址保持不变。FIXED可以用来在一个加载区中创建多个根执行区域。因此我们可以通过它实现把某个函数或一段数据放到目标地址,从而可以通过指针方便地访问该地址。比如,我们可以实现把常量表和checksum放到ROM上的固定地址处。注意: 为了使得代码更加易于维护和调试,请尽量少使用scatter file来指定放置位置,而是应该尽可能多地让连接器来确定函数和数据的位置。3.3.1 怎样把函数或数据放到指定地址通常,编译器处理来自单个源文件的RO,RW,和ZI段。这些区域包括源文件的代码与数据。如果打算把单个函数或数据项放到某个固定地址,我们就必须让编译器单独处理这些函数和数据。我么可以通过以下方式处理单个目标: 把函数和数据放到其源文件。 使用编译选项 zo为每个函数单独生成一个目标文件。(参看ARM Compiler Guide) 在C,C+源文件内利用 #pragma arm section 来生成多命名段。 在汇编源文件内利用AREA 导向符来生成可重载段。3.3.2 怎样放置单个目标文件的内容3.3.3 怎样使用ARM的 section pragma 通常把函数和数据放到其源代码文件,然后放到其目标文件的相应段中。然而,我们也可以#pragma 和scatter file实现单独处理某个命名段。/ file adder.cint x1 = 5; / in.dataint y1100; / in.bssint const z13 = 1,2,3; / in.constdataint sub1(int x) / in.textreturn x-1;#pragma arm section rwdata = foo, code =fooint x2 = 5; / in foo (data part of region)char *s3 = abc; / s3 in foo, abc in .constdataint add1(int x)return x+1; / in foo (.text part of region)#pragma arm section code, rwdata / return to default placementFLASH 0x24000000 0x4000000FLASH 0x24000000 0x4000000init.o (Init, +First) ;place code from init.o first* (+RO) ;sub1(), z132bitRAM 0x0000vectors.o (Vect, +First)* (+RW,+ZI) ;x1, y1ADDER 0x08000000adder.o (foo) ;x2, string s3, and add1()ARM的异常处理过程分析 (2012-05-23 22:29) 标签: 异常 ARM 分类: ARM 近来翻了翻uC/OS-II官网给出来的ARM7-ARM9移植手册(AN-104),分析了在ARM中移植的问题,想想从来没有认真的学习过ARM的汇编,趁着这个机会复习复习吧。其实底层的东西才是创造力的心脏。其中的移植代码中存在的很多问题比如中断的关闭和开启,任务级别的情景切换,中断到任务的情景切换都是我们在平时移植中讲到,我也不在此强调了。在官网中提供的移植过程中存在异常处理机制,这个本不是在移植过程中考虑的,但是文档中确实提供了一个比较好的处理方式。我在此对这一段时间的学习做一个总结。首先需要了解ARM的异常处理机制,异常是每一种处理器都必须考虑的问题之一,关键在于如何让处理,返回地址在什么位置都是需要考虑的,ARM中支持7种异常,其中包括复位、未定义指令异常、软中断异常、预取指令中止、数据中止、IRQ、IFQ。每一种异常运行在特定的处理器模式下。我在此逐一的分析。一般异常发生后,CPU都会进行一系列的操作,这些操作有一部分是CPU自动完成,有一部分是需要我们程序员完成。首先说明CPU会自动完成的部分,用ARM结构手册中的代码描述如下:R14_ = return link /这个可以参看寄存器的说明,两个作用SPSR_ = CPSRCPSR4:0 = exception mode numberCPSR5 = 0;/AEM指令If =Reset or Fiq then/只有在复位和FIQ模式下才会关闭FIQ中断CPSR6 = 1;CPSR7 = 1;/任何异常模式下都会关闭IRQ中断PC = exception vector address从上面的代码中我们可以发现CPU自动处理的过程包括如下:1、 拷贝CPSR到SPSR_2、 设置适当的CPSR位: 改变处理器状态进入ARM状态;改变处理器模式进入相应的异常模式;设置中断禁止位禁止相应中断。3、 更新LR_,这个寄存器中保存的是异常返回时的链接地址 4、 设置PC到相应的异常向量以上的操作都是CPU自动完成,异常的向量表如下:返回地址问题异常的返回地址也是需要我们注意的地方,不同的异常模式返回地址也是存在差异的,这主要是因为各种异常产生的机理存在差别所导致的。这样我们的需要在异常进入处理函数之前或者在返回时调整返回地址,一般采用进入异常处理函数前进行手动调整。下面每一种异常R14保存的值都给了出来,其中也包含了CPU自动处理的部分,根据保存的R14就可以知道怎样实现地址的返回。复位异常:可以看出该模式下的先对来说返回地址也比较简单,不需要做太多的描述。未定义的指令异常:返回的方式也比较简单:MOVS PC, R14软中断异常:返回的方式也比较简单:MOVS PC, R14预取指令中止异常:返回需要做下面的调整:SUBSPC, R14, #4数据中止返回地址需要做下面的调整:如果需要重新访问数据则:SUBSPC, R14, #8如果不需要重新访问数据则:SUBSPC, R14, #4IRQ中断的处理过程:返回地址需要做下面的调整:SUBS PC,R14,#4IFQ中断:返回地址需要做下面的调整:SUBS PC, R14 ,#4从上面的代码可以知道,对于每一种异常,保存的返回地址都是不一样的,一般都需要我们手动的跳转,当然调整的时机也需要我们选择,是在进入处理前跳转还是返回时调整都是需要我们程序员控制的。在ARM Developer Suite Developer Guide中对ARM处理器的异常处理操作提供能更加详细的解释,每一种异常下的处理方式如下文描述:异常返回时另一个非常重要的问题是返回地址的确定,在前面曾提到进入异常时处理器会有一个保存LR 的动作,但是该保存值并不一定是正确的返回地址,下面以一个简单的指令执行流水状态图来对此加以说明。我们知道在ARM 架构里,PC值指向当前执行指令的地址加8处,也就是说, 当执行指令A(地址0x8000)时,PC 等于指令C 的地址(0x8008)。假如指令A 是“BL”指令,则当执行该指令时,会把PC(=0x8008)保存到LR 寄存器里面,但是接下去处理器会马上对LR 进行一个自动的调整动作:LR=LR-0x4。这样,最终保存在 LR 里面的是 B 指令的地址,所以当从 BL 返回时,LR 里面正好是正确的返回地址。同样的调整机制在所有LR自动保存操作中都存在,比如进入中断响应时,处理器所做的LR 保存中,也进行了一次自动调整,并且调整动作都是LR=LR-0x4。下面,我们对不同类型的异常的返回地址依次进行说明:假设在指令A 处(地址0x8000)发生了异常,进入异常响应后,LR 上经过调整保存的地址值应该是B 的地址0x8004。1、 如果发生的是软件中断,即A 是“SWI”指令异常是由指令本身引起的,从 SWI 中断返回后下一条执行指令就是B,正好是LR 寄存器保存的地址, 所以只要直接把LR 恢复给PC。MOVS pc, lr2、 发生的是Undefined instruction异常异常是由指令本身引起的,从异常返回后下一条执行指令就是B,正好是LR 寄存器保存的地址, 所以只要直接把LR 恢复给PC。MOVS pc, lr3、 发生的是IRQ或FIQ中断因为指令不可能被中断打断,所以A指令执行完以后才能响应中断,此时PC已更新,指向指令D的地址(地址0x800C),LR 上经过调整保存的地址值是C 的地址0x8008。中断返回后应该执行B指令,所以返回操作是:SUBS pc, lr, #44、 发生的是Prefetch Abort异常该异常并不是处理器试图从一个非法地址取指令时触发,取出的指令只是被标记为非法,按正常处理流程放在流水线上,在执行阶段触发Prefetch Abort异常,此时LR 上经过调整保存的地址值是B 的地址0x8004。异常返回应该返回到A指令,尝试重新取指令,所以返回操作是:SUBS pc, lr, #45、 发生的是“Data Abort”CPU访问存储器时触发该异常,此时PC指向指令D的地址(地址0x800C),LR 上经过调整保存的地址值是C 的地址0x8008。异常返回后,应回到指令A,尝试重新操作存储器,所以返回操作是:SUBS pc, lr, #8以上就是ARM异常的CPU操作部分,接下来就是程序员应该完成的操作。1. 由于CPU会自动跳转到对应的异常向量中,因此只需要在在各个异常向量中存放对应的操作,最简单的都是存放一个B指令跳转到对应的异常处理函数的操作即可。但由于B指令的跳转返回只有+-32M,而异常处理函数的地址可能会超过+-32M,因此可以采用另一种方式实现方式:在异常向量中保存一条指令LDR PC addr,其中的addr中就保存了异常处理函数的地址,当然addr的相对地址要小于+-32M。这样也就解决了跳转范围的问题。2. 接下来就是异常处理函数对应的操作,可以在进入异常处理之前就进行返回地址的调整,这样后面就不用进行处理啦,当然也可以在返回过程中再调整。一般都是在这个过程中进行调整。进行压栈操作,保存对应的环境变量。调用实际的处理过程等。3. 出栈,恢复CPU的状态和寄存器的值。由于第一步中已经调整好返回地址,这一步不需要再次调整。当然如果之前没有调整,这里则需要进行相应的调整。在uC/OS-II的官网移植中采用通用异常处理函数的方式实现异常的处理,下面我们来分析其中的部分代码:首先是处理器部分的移植,包括异常向量、异常的ID号,存储异常处理函数地址的地址等:/*ARM的异常ID号,支持7种类型的异常,每一种异常都存在一个ID号*/#define OS_CPU_ARM_EXCEPT_RESET 0x00#define OS_CPU_ARM_EXCEPT_UNDEF_INSTR 0x01#define OS_CPU_ARM_EXCEPT_SWI 0x02#define OS_CPU_ARM_EXCEPT_PREFETCH_ABORT 0x03#define OS_CPU_ARM_EXCEPT_DATA_ABORT 0x04#define OS_CPU_ARM_EXCEPT_ADDR_ABORT 0x05#define OS_CPU_ARM_EXCEPT_IRQ 0x06#define OS_CPU_ARM_EXCEPT_FIQ 0x07#define OS_CPU_ARM_EXCEPT_NBR 0x08/*异常向量地址*/#define OS_CPU_ARM_EXCEPT_RESET_VECT_ADDR (OS_CPU_ARM_EXCEPT_RESET * 0x04 + 0x00) /0x00#define OS_CPU_ARM_EXCEPT_UNDEF_INSTR_VECT_ADDR (OS_CPU_ARM_EXCEPT_UNDEF_INSTR * 0x04 + 0x00)/0x04#define OS_CPU_ARM_EXCEPT_SWI_VECT_ADDR (OS_CPU_ARM_EXCEPT_SWI * 0x04 + 0x00)/0x08#define OS_CPU_ARM_EXCEPT_PREFETCH_ABORT_VECT_ADDR (OS_CPU_ARM_EXCEPT_PREFETCH_ABORT * 0x04 + 0x00)/0x0c#define OS_CPU_ARM_EXCEPT_DATA_ABORT_VECT_ADDR (OS_CPU_ARM_EXCEPT_DATA_ABORT * 0x04 + 0x00)/0x10/*这个异常是ARM中不支持的异常*/#define OS_CPU_ARM_EXCEPT_ADDR_ABORT_VECT_ADDR (OS_C
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 毛织织造故障排除工艺考核试卷及答案
- 食用菌种植考核试卷及答案
- 发电厂设备磨损检测标准制定工艺考核试卷及答案
- 固体饮料均质均质化工艺考核试卷及答案
- 风动工具振动分析工艺考核试卷及答案
- 机床床身焊接接缝热处理工艺考核试卷及答案
- 特种玻璃电子束切割声波阻尼工艺考核试卷及答案
- 机器人气动安全设计工艺考核试卷及答案
- 精密定位工艺考核试卷及答案
- 塑料注塑成型工艺废弃物处理考核试卷及答案
- 2024年秋季云南高中学业水平合格考历史试卷真题(含答案详解)
- 中国抗癌协会神经内分泌肿瘤诊治指南(2025年版)解读
- T/CSMT-YB 006-2023精密数字温度计性能测试与评价方法
- 组建乐团协议书
- 兼职人员聘用协议书
- GB/T 45595-2025离心式制冷剂压缩机
- 2020公路工程质量检验评定标准第二册机电工程
- 酒店安全考试试题及答案
- 人教版小学四年级上册数学 第7单元 条形统计图 教案
- 中医适宜技术在妇科的应用
- 乡土中国考试题及答案
评论
0/150
提交评论