




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、.ADS1 2进行嵌入式软件开发 jepsen1 新浪嵌入式应用程序通常都是在样机环境下调试与开发的,这种环境与最终产品之间并不完全一样。因此,在系统调试阶段就考虑应用程序在最终目的硬件中的运行情况是非常重要的。本文旨在讨论怎么将一个开发/调试环境下的嵌入式应用程序转移到最终独立运行的目的系统中去,并提到了ARM ADS1.2开发工具包的一些功能特性及其在这个过程中所起到的作用。使用ADS开发嵌入式程序时,需要着重考虑以下几个提出问题:1.与硬件相关的C语言库函数的使用;2.某些C语言库函数使用了调试环境中的资源,要把这些使用的资源重定向到目的系统中的硬件上来;3.可执行映象文件的存储器映射必
2、须根据目的硬件的存储器分布进展裁剪;4.在主程序执行前,嵌入式应用程序必须先完成系统的初始化。一个完好的初始化包括用户的启动执行代码和ADS中C库函数的初始化过程。图1 Semihosting的实现举个例子图2 C语言库函数构造图3缺省的存储器映射图4连接器布局规那么缺省的工程工程设置刚开场一个嵌入式应用软件开发时,ADS用户可能并不完全清楚目的硬件的一些参数指标。比方有关外设、存储器地址分布,甚至处理器类型等一些细节,可能还没有最终确定。为了在所有这些细节全部就绪前就能进展软件开发,ADS工具有一套程序构建和调试的缺省设置。理解这套缺省的工程工程设置方法,对于把握最终的移植步骤非常有好处。A
3、DS1.2 C语言函数库Semihosting在ADS的C语言函数库中,某些ANSIC的功能是由主机的调试环境来提供的,这套机制有一个专门术语叫Semihosting。Semihosting通过一组软件中断SWI指令来实现。如图1所示,当一个Semihosting软中断被执行时,调试系统先识别这个SWI恳求,然后挂起正在运行的程序,调用Semihosting的效劳,完成后再恢复原来的程序执行。因此,主机执行的任务对于程序来说是透明的。C语言库函数构造从概念上来讲,C语言库函数可以被分成两部分,一是ANSIC语言标准本身的一部分,一是只受某一特定ANSIC层次支持的函数,如图2所示。其中一些AN
4、SIC的功能是由主机调试环境调用驱动程序级的函数完成的。例如,ADS的库函数printf把输出信息输出到调试器的控制台窗口,这个功能通过调用_sys_write实现,_sys_write执行了一个把字符串输出到主机控制台的Semihosting软中断效劳程序。缺省的存储器映射假设用户在程序编译时没有指定映象的存储器映射分布,ADS将为生成的目的代码和数据分配一个缺省的存储器映射图,如图3所示。目的印象被连接至地址0x8000,存储和执行区域都位于该地址开场的空间。RO只读部分放在前面,接着是RW读写部分,最后是ZI零初始化部分。在ZI部分之上紧跟着HEAP,所以HEAP确实切地址要在连接时才能
5、确定。STACK的基地址是在应用程序启动时由一个Semihosting操作提供。这项Semihosting操作返回的地址值视不同调试环境而定:ARMulator返回配置文件peripherals.ami中的设置值;缺省为0x 08000000。Multi-ICE返回的是调试器内部变量$top_of_memory的值;缺省为0x 00080000。连接器布局规那么连接器对代码和数据在存储器系统中的分配,遵循一套规那么,如图4所示。映象首先按照属性以RO-RW-ZI的次序进展排列,在同一种属性里面代码先于数据。然后连接器将输入段根据名字的字母顺序进展排列,输入段的名字与汇编代码里面的块名字指示一致
6、在汇编程序中用AREA要害字。在输入段中,来自不同对象的代码和数据放置次序与在连接器命令行中指定的对象文件次序一致。在需要灵敏分配代码和数据放置位置的情况下,建议用户不要简单地依靠这些规那么。后面会介绍一种怎么控制代码和数据布局的机制Scatterloading。图5缺省的ADS初始化过程图6 C库函数重定向图7 scatter文件语法图8分散加载的简单样例启动应用程序大多数嵌入式系统在进入应用主程序之前有一个初始化的过程,该过程完成系统的启动和初始化功能。缺省的ADS初始化过程如图5所示。总体上,初始化过程可以分成两部分来看:_main负责设置运行映像存储器映射;_rt_entry负责库函数
7、的初始化。_main完成代码和数据的复制,并把ZI数据区清零。这一步只有当代码和数据区在存储和运行时处于不同的存储器位置时才有意义。接着_main跳进_rt_entry,进展STACK和HEAP等的初始化。最后_rt_entry跳进应用程序的入口main。当应用程序执行完时,_rt_entry又将控制权交还给调试器。函数main在ADS中有非凡的意义。当一个程序工程工程中存在main时,连接器会把_main和_rt_entry中的初始化代码连接进来;假设没有main函数,初始化过程就不会被连接,结果就会导致一些标准的C库函数无效。根据目的环境裁减C库函数缺省状态下C库函数利用Semihotst
8、ing机制来实现设备驱动的功能。但一个真正的嵌入式系统,要使用到详细的外设或硬件独立于主机环境运行。C库函数重定向用户可以定义自己的C语言库函数,连接器在连接时自动使用这些新的功能函数。这个过程叫做重定向C语言库函数,如图6所示。举个例子来说,用户有一个I/O设备如UART。本来库函数fputc是把字符输出到调试器控制窗口中去的,但用户把输出设备改成了UART端口,这样一来,所有基于fputc函数的printf系列函数输出都被重定向到UART端口上去了。下面是实现fputc重定向的一个例子:externvoidsendcharchar*ch;intfputcintch,FILE*fcharte
9、mpch=ch;sendchar&tempch;returnch;这个例子简单地将输入字符重新定向到另一个函数sendchar,sendchar假定是一个另外定义的串口输出函数。在这个地方,fputc就似乎目的硬件和标准C库函数之间的一个抽象层。在C语言库函数中禁用Semihosting在一个独立的嵌入式应用程序中,应当不存在SemihostingSWI操作。因此,用户必须确定在所有调用到的库函数中没有使用Semihosting。为了保证这一点,在程序中可以引进一个符号要害字_use_no_semihosting:在C代码中,使用#prgrama#pragmaimport_use_no
10、_semihosting_swi在汇编程序中,使用IMPORT IMPORT_use_no_semihosting_swi这样,当有使用SWI机制的库函数被连接时,连接器会进展报错:Error:Symbol_semihosting_swi_guardmultiplydefined为了确定详细是哪一个函数,连接时翻开-verbose选项。这样在结果信息输出时,该库函数上将有一个_I_use_semihosting_swi的标记。Loadingmembersys_wxit.ofromc_a_un.1.Definition:_sys_exit Reference:_I_use_semihosting
11、_swi用户必需要把这些函数定义成自己的执行内容。有一点需要注重,连接器只能报告库函数中被调用的Semihosting,对用户自定义函数中使用的Semihosting那么不会报错。根据目的硬件定制存储器映射分散装载Scatlerloading在实际的嵌入式系统中,ADS提供的缺省存储器映射是不能满足要求的。用户的目的硬件通常有多个存储器设备位于不同的位置,并且这些存储器设备在程序装载和运行时可能还有不同的配置。Scattertoading可以通过一个文本文件来指定一段代码或数据在加载和运行时在存储器中的不同位置。这个文本文件scatterfile在命令行中由-scatter开关指定,例如:ar
12、mlink_scatterscat.scffilel.ofile2.0在scatterfile中可以为每一个代码或数据区在装载和执行时指定不同的存储区域地址,Scatlertoading的存储区块可以分成二种类型:装载区:当系统启动或加载时应用程序的存放区。执行区:系统启动后,应用程序进展执行和数据访问的存储器区域,系统在实时运行时可以有一个或多个执行块。映像中所有的代码和数据都有一个装载地址和运行地址二者可能一样也可能不同,视详细情况而定。在系统启动时,C函数库中的_main初始化代码会执行必要的复制及清零操作,使应用程序的相应代码和数据段从装载状态转入执行状态。1.scatter文件语法s
13、catter文件是一个简单的文本文件,包含一些简单的语法。My_Region0x00000x1000thecontextofregion每个块由一个头标题开场定义,头中至少包含块的名字和起始地址,另外还有最大长度和其他一些属性选项。块定义的内容包括在紧接的一对花括号内,依靠于详细的系统情况。一个加载块必须至少含有一个执行块;理论中通常有多个执行块。一个执行块必须至少含有一个代码或数据段;这些通常来自源文件或库函数等的目的文件;通配符号*可以匹配指定属性项中所有没有在文件中定义的余下部分。2.简单分散加载样例图8所示样例中,只有一个加载块,包含了所有的代码和数据,起始地址为0。这个加载块一共对应
14、两个执行块。一个包含所有的RO代码和数据,执行地址与装载地址一样;同时另一个起始地址为0x10000的执行块,包含所有的RW和ZI数据。这样当系统开场启动时,从第一个执行块开场运行执行地址等于装载地址,在执行过程中,有一段初始化代码会把装载块中的一部分代码转移到另外的执行块中。下面是这个scatter描绘文件,该文件描绘了上述存储器映射方式。LOAD_ROM0x4000EXE_ROM0x00000x4000;Rootregion*+RO;AllcodeandconstantdataRAM0x 100000x8000*+RW,+ZI;Allnon-constantdata3.在分散文件中放置对象
15、在大多数应用中,并不是像前例那样,简单地把所有属性都放在一起,用户需要控制特定代码和数据段的放置位置。这可以通过在scatter文件中对单个目的文件进展定义实现,而不是只简单地依靠通配符。为了覆盖标准的连接器布局规那么,我们可以使用+FIRST和+LAST分散加载指令。典型的例子是在执行块的开场处放置中断向量表格:LOAD_ROM0x00000x4000EXEC_ROM0x00000x4000vectors.oVect,+FIRST*+RO;moreexecregions.在这个scatter文件中,保证了vextors.o中的Vect域被放置于地址0x0000。4.RootRegion根区根
16、区是一个执行块,它的加载地址与执行地址是一致的。每个scatter文件至少有一个根区。分散加载有一个限制:创立执行块的代码和数据即完成复制和清零的代码和数据无法自行复制到另一个位置。因此,在根区中必须含有下面的部分:_main.o,包含复制代码/数据的代码;连接器输出变量$Table和ZISection$Table,包含被复制代码/数据的地址。由于上面两个部分的属性是只读的,因此他们被*+RO通配符语法匹配。假设*+RO被用在了非根区中,那么在根区中必须显式地指明另一个RO区域。下面是一个例子:LOAD_ROM0x00000x4000EXE_ROM0x00000x4000;rootregion
17、_main.o+RO;copyingcode*Region$Tabl0e;RO/RWaddressestocopy*ZISection$Table;ZIaddressestozeroRAM0x 100000x8000*+RO;allotherROsections*+RW,+ZI;allRWandZIsections放置堆栈和heap Scatterloading机制提供了一种指定代码和静态数据布局的方法。下面介绍怎么放置应用程序的堆栈和heap。*_user_initial_stackheap重定向应用程序的堆栈和heap是在C库函数初始化过程中建立起来的。可以通过重定向对应的子程序来改变堆栈
18、和heap的位置,在ADS的库函数中,即_user_initial_stackheap函数。_user_initial_stackheap可以用C或汇编来实现,它必须返回如下参数:r0:heap基地址;r1:堆栈基地址;r2:heap长度限制值需要的话;r3:堆栈长度限制值。当用户使用分散装载功能的时候,必须重调用_user_initial_stackheap,要不然连接器会报错:Error:L6218E:Undefined symbol Image$ZI$Limitreferred from sys_stackheap.o*存储器模型ADS提供了两种实时存储器模型。缺省时为one-regio
19、n,应用程序的堆栈和heap位于同一个存储器区块,使用的时候相向生长,当在heap区分配一块存储器空间时需要检查堆栈指针。另一种情况是堆栈和heap使用两块独立的存储器区域。对于速度非凡快的RAM,可选择只用来作堆栈使用。为了使用这种two-region模型,用户需要导入符号use_two_region_memory,heap使用需要检查heap的长度限制值。对这两种模型来说,缺省情况下对堆栈的生长都不进展检查。用户可以在程序编译时使用-apcs/swst编译器选项来进展软件堆栈检查。假设使用two-region模型,必须得在执行_user_initial_stackheap时指定一个堆栈限制
20、值。图9重定向_user_initial_stackheap图10根底初始化过程图11 ROM/RAM重定向和映射表1系统复位和初始化目前情况,一般假设程序从C库函数的初始化入口_main开场执行。实际上,所有的嵌入式程序在启动时都要执行一些系统级的初始化操作。在此讨论这方面的内容。初始化过程图10中显示了一个基于ARM的嵌入式系统的根底初始化过程。可以看到,在_main之前参加了一个复位处理模块reset handler,它在系统上电复位时立即启动。标识为$sub$main的新代码块在进入主程序之前执行。复位处理模块reset handler通常是一小段汇编代码,在系统复位时执行。它至少完成
21、应用程序中使用到的所有处理器形式的堆栈初始化工作。对于含有本地存储器系统的内核比方含cache的ARM内核,配置工作也必须在这一段初始化过程中完成。当完成系统初始化之后,通常程序会跳向_main,开场C库函数的初始化过程。系统初始化过程一般还包括另外一些内容,中断使能等,这些大多安排在C库函数的初始化完成之后执行。$sub$main完成这部分功能。向量表vector table所有的ARM系统都有一张中断向量表当出现异常需要处理时,必须调用向量表。向量表一般要位于0地址处。表2表3表4表5表6表7表8表9表10存储器配置*ROM/RAM重定向当系统启动的时候,为了保证0地址处有正确的启动代码存
22、在,需要非易失性的存储器。一种简单的方法,就是把系统0x0000开场的一块地址分配给ROM。其缺点是,由于ROM的访问速度比RAM慢很多,当执行中断响应需要从中断向量表跳转时,会给系统性能带来损失;同时,在ROM中的向量表内容也不能被用户程序动态修改。另外一种可行的方案如图11所示。ROM位于地址0x1000开场的地方,但是在系统复位时又被存储器控制器映射到0x0000地址处。这样当系统启动之后,在地址0x0000看到的是ROM,系统执行这块ROM中的启动代码,启动代码跳转到真正的ROM的地址,并让存储器控制器移除对ROM的地址映射。这时0x0000地址处的存储器又恢复回了RAM。_main中
23、的代码把向量表copy到0x0000处的RAM中去,使得异常时能被正确响应。表1为ARM汇编中执行ROM/RAM重定向和映射的一个例子。它以ARM公司的Integrator平台为根底的,该方法适用于类似ROM/RAM重定向方法的所有平台。第一条指令完成从ROM的映射地址0x00000到真实地址的跳转。地址标号instrUCt_2是ROM的真实地址0x 180004。然后通过设置Integrator平台上的相应控制存放器,移除ROM的地址映射。代码在系统一启动就被执行。所有关于地址重定向/映射的操作必须在C库函数初始化之前完成。*本地存储器配置许多ARM处理器都有片上存储器系统,如cache和严
24、密耦合存储器TCM、存储器治理单元MMU或存储器保护单元MPU。这些设备都要在系统初始化过程中正确配置,并且有一些非凡的要求需要考虑。由前文可知,_main中的C库函数初始化代码负责程序运行时的存储器系统设置。因此,整个存储器系统本身必须得在_main之前完成初始化工作,如MMU或MPU必须在reset handler里面完成配置。严密耦合存储器TCM的初始化同样须在_main之前完成通常在MMU/MPU之前,因为一般程序都需要把代码和数据分散装入TCM。需要注重的是当TCM被使能后,不再访问被TCM屏蔽的存储器。关于cache的一致性问题,假设cache在_main之前使能的话,那么当_ma
25、in里面进展从装载区到执行区的代码和数据拷贝时因为在拷贝过程中指令和数据在本质上都是被当作数据处理,指令会出如今数据缓冲区。防止此问题的方法是在C库函数初始化完成后再使能cache。*Scatter loading与存储器配置不管是通过ROM/RAM重定向还是MMU配置的方法,假设系统在启动和运行时存储器分布不一致,scatterloading文件中的定义就要按照系统重定向后的存储器分布情况进展。以上文ROM/RAM重定向为例:LOAD_ROM 0x10000 0x8000EXE_ROM 0x10000 0x8000reset_handler.o+RO,+FIRST.RAM 0x0000 0x
26、4000vectors.o+RO,+FIRST.装载区LOAD_ROM被放置在0x10000处,代表了重定向之后代码和数据的装载地址。堆栈的初始化程序中可能用到的处理器形式,都需要定义一个堆栈指针。在表2中,堆栈位于stack_base标识的地址中。这个符号可以是存储器系统中的一个直接地址,也可以在另外的汇编文件中定义,由scatter文件来定义分配地址。表2代码为FIQ和IRQ形式各分配了一个256字节的堆栈,用户可以用同样的方法为其他形式也分配堆栈。最简单的方法就是进入相应的形式,然后为SP存放器指定相应的值。假设想使用软件堆栈检查,还必须指定一个堆栈长度限制值。堆栈指针和堆栈限制的数值会
27、作为参数自动传递到C库函数的初始化代码_user_initial_stackheap中,在_user_initial_stackheap中不应当修改这些值。硬件初始化$sub$main一般来说,应当把所有的系统初始化代码与主应用程序别分开来,但是有几个例外,比方cache和中断的使能,需要在C库函数初始化之后执行。表3代码显示了怎么使用$sub和$supper。连接器把呼叫main的函数交换成呼叫$sub$main,完成cache和中断的使能,并最终跳向main。执行形式考虑为主应用程序选择一个处理器执行形式非常重要,这取决于系统的初始化代码。许多在启动过程中使用到的功能,如MMU/MPU的配
28、置、中断的使能等,只能在特权级形式下进展。假设需要在特权极形式下运行自己的应用程序,只要在退出初始化过程之前改变到相应的形式就行了,没有其他任何问题。假设使用user形式,必须保证所有只能在特权形式下执行的功能完成之后,才能进入user形式。因为system形式和user形式使用一样的存放器组,reset handler应当从system形式退出,_user_initial_stackheap在system形式下完成应用程序堆栈的初始化。这样在处理器进入user形式后,所有的堆栈空间都已经被正确设置好了。对存储器布局的进一步考虑在scatter文件中分配硬件地址虽然可以在一个scatter文件
29、中描绘代码和数据的分分布局,但是目的硬件中的外设存放器,堆栈和heap配置仍然直接采用硬件地址在程序源代码中进展设置。假设把所有存储器地址相关的信息都在scatter文件中进展定义,防止在源文件中引用绝对硬件地址,对程序的工程化治理是有大好处的。*在scatter文件中定义目的外设地址通常外设存放器的地址在程序文件或头文件中定义,也可以声明一个构造类型指向外设存放器,构造的地址定位在scatter文件中完成。举个例子来说,目的定时器上有2个32位的存放器,可以用表4来映射这些存放器。为了把构造放置在指定的存储器地址上面,创立一个新的执行区见表5。scatter文件便把timer_regs构造定位在了地址0x 40000000。注重,在启动过程当中这些存放
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年智能交通系统专业资格考试试卷及答案
- 2025年职业生涯规划与发展考试试题及答案
- 2025年特殊教育服务与支持考试试题及答案
- 2025年广告与市场传播专业考生模拟考试试题及答案
- 2025年互联网金融专业试卷及答案
- 2025年公共关系与危机管理考试题及答案
- 2025年法律硕士考试试题及答案
- 2025年护士资格认证考试试题及答案
- 养殖合同协议书找谁弄
- 2025年多协议通信适配器合作协议书
- 夜场水烟合作协议书
- 河南省青桐鸣大联考普通高中2024-2025学年高三考前适应性考试地理试题及答案
- 管道劳务分包协议书
- 2025年中考理化生实验操作考试考务培训大纲
- 拆迁服务方案
- 2025-2030中国锂电子电池行业市场深度调研及前景趋势与投资研究报告
- 天津市部分区小学2025年小升初数学自主招生备考卷含解析
- 2021年高考地理试卷(全国甲卷)(空白卷)
- (二模)2024~2025学年度苏锡常镇四市高三教学情况调研(二)数学试卷(含答案详解)
- 2024初级社会工作者职业资格笔试题库附答案
- 江苏省南京市建邺区2023-2024学年八年级下学期期末考试物理试题【含答案解析】
评论
0/150
提交评论