嵌入式软件开发.ppt

上传人:本田雅阁 文档编号:2600345 上传时间:2019-04-15 格式:PPT 页数:49 大小:827.01KB
返回 下载 相关 举报
嵌入式软件开发.ppt_第1页
第1页 / 共49页
嵌入式软件开发.ppt_第2页
第2页 / 共49页
嵌入式软件开发.ppt_第3页
第3页 / 共49页
嵌入式软件开发.ppt_第4页
第4页 / 共49页
嵌入式软件开发.ppt_第5页
第5页 / 共49页
点击查看更多>>
资源描述

《嵌入式软件开发.ppt》由会员分享,可在线阅读,更多相关《嵌入式软件开发.ppt(49页珍藏版)》请在三一文库上搜索。

1、嵌入式软件开发,嵌入式开发过程,“PC软件”,独立的嵌入式应用,当程序员开始开发一个基于ARM应用的时候,你可以使用ARM的ADS编写类似于“HELLO WORLD”的程序,使用ARMulator或者在评估板上来调试,但当你把他移植到独立的嵌入式应用设备中时,下面这些问题就成为我们首要考虑的: 硬件环境中所使用的C库函数 目标板上的存储器资源 应用程序的初始化,议程,PC软件的构造 定制标准C库函数到目标板 定制IMAGE的存储器映射到目标板 复位和初始化 深层次的存储器器映象考虑 编译和调试IMAGE,ADS默认的标准C库,ANSI C,input/ output,error handlin

2、g,stack & heap setup,other,Semihosting Support,应用程序调用的C库函数 eg: fputc(),设备驱动层 使用semihosting SWIs eg: _sys_write(),调试工具环境,C Library,Debug Agent,C库函数功能是支持PC软件的,而目标板上的可执行软件则依赖相关的硬件资源;在ARM体系中,我们可以采用semihosting通过相应的驱动来进行调试。,ADS默认的存储器映射,在默认的情况下,我们链接、定位、运行在0x8000 heap 被直接放置在数据区的上面 堆栈的基地址是通过调试环境从C库函数的Startup

3、 Code 里读取出来的。 ARMulator = from configuration file (peripherals.ami) default = 0x08000000 Multi-ICE = from debugger internal variable $top_of_memory default = 0x80000,RO,RW,ZI,0x8000,链接时确定,由调试环境提供,Heap(malloc,alloc),Stack,C Library,User Code,应用程序启动,_main copy code and data zero uninitialized data,程序入

4、口点,Agenda,一个PC软件的构造 定制标准C库函数到目标板 定制IMAGE的存储器映射到目标板 复位和初始化 深层次的存储器器映象考虑 编译和调试IMAGE,重定向C库函数 (1),Semihosting Support,ANSI C,input/ output,你可以使用适合你目标板运行的驱动来替换标准C库中的设备驱动。 Eg: printf( ) 可打印到LCD上,而不是打印控制台上,input/ output,ANSI C,C Library,User Code,Debug Agent,Target Hardware,Retarget,重定向C库函数(2),要重定向C库函数,简单的

5、办法是使用你自己的可执行的semihosting SWIs来代替原来的C库函数,从而来满足你的系统要求 比如说, the printf()系列函数(sprintf()除外) 都会调用fputc(). 在默认情况下fputc()的执行使用了semihosting SWI. 用下面的语句来代替: extern void sendchar(char *ch); int fputc(int ch, FILE *f) /* e.g. write a character to an LCD */ char tempch = ch; sendchar( 可查看在ADS Embedded example目录下

6、的retarget.c,可看到更多的重定向例子 你可以确定有不在连接时使用semihosting SWI 的吗?.,消除C库函数中的semi hosting,为了确保在连接时没有函数使用了semi hosting SWIs ,你可以在程序中加入下面的句子: #pragma import(_use_no_semihosting_swi) 如果在程序中仍然使用了semihosting ,编译时将会报错: Error: Symbol _semihosting_swi_guard multiply defined 修改: 如果使用 (check -verbose linker output for o

7、ccurrences of I use_ semihosting_ swi), 那么连接器将会把那些使用了smeihosting 的程序列出来, 然后: 提供你自己可运行的功能函数。 在ADS 1.2 编译器和库函数手册, 表4-2给出了所有使用了semihosting的C库函数。 注意: 连接器在用户自己的应用代码中不会出现任何有关 semihosting SWI使用的报告。,Agenda,一个PC软件的构造 定制标准C库函数到目标板 定制IMAGE的存储器映射到目标板 复位和初始化 深层次的存储器器映象考虑 编译和调试IMAGE,分散加载(Scatterloading),在一个实际应用当中

8、,你可能并不想在0x8000处开始运行。 大多数嵌入式系统都有存储器设备,他们的地址空间是在整个存储器映射中交叉出现的。 分散加载提供了一种把你的代码和数据放在不同的存储器定位上的办法 分散加载定义了两种类型的存储器区域。 Load 区: - 在reset/load时保留了应用程序的代码和数据 (典型应用为 ROM). Execution 区 在程序执行的同时保留了程序的代码和数据。在应用程序启动 期间,每个load区都可创建一个或多个可执行区。 分散加载了的应用把详细的存储器映射保存在一个描述文件中,作为一个参数给armlink使用 eg: armlink program.o -scatte

9、r scatter.scf -o program.axf,分散加载 (简单例子),只读代码和数据保存在ROM中 C库函数初始化代码 (在 _main) 将 : 从ROM拷贝RW数据到RAM 在RAM中的ZI 数据初始化,Scatter 描述文件,通配符(*)语法允许简单的对CODE 和DATA 进行分组,EXEC_ROM 0x0000 0x4000 * (+RO) ,链接器放置规则,在每个可执行区,链接器通过一些基本的规则来放置CODE 和DATA 基本的排序方法是通过属性来安排的: RO 领先于RW ,RW 领先于ZI 有相同的属性时,CODE 在DATA之前放置。 更多的排序方法决定于:

10、输入的组名按字母排序, 在ARMLINK命令行中指定的顺序。 eg: armlink file1.o file2.o ,在SCATTOR 文件中的对象排序,为了把特定的CODE 和DATA 放在指定的地址上,你可以不考虑标准的放置规则 使用+FIRST 和 +LAST ,直接把第一个和最后一个对象放在可执行区。 图例:把VECTOR表放在区的开始。,LOAD_ROM 0x0000 0x4000 EXEC_ROM 0x0000 0x4000 vectors.o (Vectors, +FIRST) file1.o (+RO) file2.o (+RO) : ,在可执行区内,scattor 文件中要

11、排序的对象对输出image没有影响 链接器的标准放置规则仍然适用,ROOT区,LOAD_ROM 0x0000 0x4000 ; start address and length EXEC_ROM 0x0000 0x4000 ; root (load = exec address) _main.o (+RO) ; copying code * (Region$Table) ; RO/RW addresses to copy * (ZISection$Table) ; ZI addresses to zero RAM 0x10000 0x8000 * (+RO) ; All other RO ar

12、eas * (+RW,+ZI) ; program variables ,Must be in a root region,outside root region,一个 root 区是一个可执行区,它的加载地址等于执行地址。,Root区要点,一个 root 区是一个可执行区,它的加载地址等于执行地址. 每个scatter描述文件必须最少包含一个root区,并且最少要包含下列内容: _main.o 含有拷贝code/data的代码 Region$Table 和 ZISection$Table 含有将要拷贝的code/data的地址,他是由链接器产生的,不是一个对象文件。(所以*必须用) Erro

13、r: L6202E: Section Region$Table cannot be assigned to a non-root region. Error: L6202E: Section ZISection$Table cannot be assigned to a non-root region. 注意: 如果 * (+RO)被定位在 root 区,在此之前的将被自动放置 Main应用程序的入口点必须放在root区。 Error: L6203E: Entry point (0x08000000) lies within non-root region EXE_FLASH.,Run-tim

14、e 存储器管理,Semihosting Support,ANSI C,Stack & Heap Setup,Stack & Heap Setup,ANSI C,C Library,User Code,Debug Agent,Target Hardware,Retarget,如何设置stack和 heap来满足我们的目标存储器?,我们已经通过执行_user_initial_stackheap()把C标准库的运行存储器模式修改到目标平台上。,Stack 和 Heap 初始化,C Library,User Code,_main copy code and data zero uninitialize

15、d data,Image Entry Point,Run-time 存储器模式,你必须决定在放置stack和heap时所使用的区域是单一的区(one-region model)或是不同的两个区(two-region model),Heap,Stack,Stack,One region model,Two region model,HB,SB,SB,HB,HL,单一存储器模式是默认方式 为了实现多区域模式,你可以使用 use_two_region_memory 在所有的模式下,软件堆栈检查要许可。 编译开关是: -apcs /swst 指定堆栈限制 (为 two-region 模式),heap

16、is checked against stack pointer,Heap,heap is checked against heap limit,(SL),_user_initial_stackheap( ),可以用C或汇编来写,他要返回: Heap 基地址在R0 ,STACK 的基地址在R1. Heap 的限制地址在R2,STACK的限制地址在R3,EXPORT _user_initial_stackheap _user_initial_stackheap LDR r0, =0x80000 ;HB LDR r1, =0x88000 ;SB ; r2 not used (HL) ; r3 no

17、t used (SL) MOV pc, lr,Heap,Stack,Heap 的限制地址在单一模式是不被使用的。 Stack 的限制地址只在软件堆栈检查许可的情况下才有效。,HB = 0x80000,SB = 0x88000,警告!,当使用分散加载时你必须执行 _user_initial_stackheap() 在C库初始化代码内的_user_initial_stackheap() 的默认执行是在映像文件的RW/ZI数据段后放置HEAP。 使用 Image$RW$Base / Image$ZI$Base 连接符号 这些符号对scatterloading是无效的。 在ADS 1.1和早期版本的软

18、件中: 符号被设置为0X0, heap被定位在这! Heap的并发使用,无论是直接(e.g. with malloc()或间接(by use of argc/argv)的都可能破坏向量表或其他代码,典型的结果是不可预知的程序在运行时出错了。 在ADS 1.2: 符号没有定义,应用程序不会联接: Error: L6218E: Undefined symbol Image$ZI$Limit (referred from sys_stackheap.o).,Agenda,一个PC软件的构造 裁减标准C库函数到目标板 定制IMAGE的存储器映射到目标板 复位和初始化 深层次的存储器器映象考虑 编译和调

19、试IMAGE,The Vector Table,AREA Vectors, CODE, READONLY IMPORT Reset_Handler ; import other exception handlers ; ENTRY B Reset_Handler B Undefined_Handler B SWI_Handler B Prefetch_Handler B Data_Handler NOP ; Reserved vector B IRQ_Handler ; FIQ_Handler will follow directly END,在使用scatterloading+FIRST时直

20、接定位在0X0(或 0xFFFF0000) ENTRY 直接告诉链接器这是一个入口点,防止某些段被删除,中断向量表,初始化步骤,C Library,User Code,_main copy code and data zero uninitialized data,Image Entry Point,$Sub$main( ) enable caches & interrupts,ROM or RAM at 0x0?,需要一个有效的地址在 0x0,这项功能可被编码在像RESET HANDLER 一样的模块中 在本章结束的时候,我们还会讲到。,ROM,0x10000,0x18000,0x4000,

21、0x0000,Aliased ROM,ROM/RAM Remapping,RAM,0x10000,0x18000,0x4000,0x0000,ROM,ROM at 0x0,Reset Handler,ROM/RAM Remapping,下面的例子可像Reset handler 一样在源码中编码。 ; - Integrator CM control reg CM_ctl_reg EQU 0x1000000C ; Address of CM Control Register Remap_bit EQU 0x04 ; Bit 2 is remap bit of CM_ctl ENTRY ; On r

22、eset, an alias of ROM is at 0x0, so jump to real ROM. LDR pc, =Instruct_2 Instruct_2 ; Remap by setting Remap bit of the CM_ctl register LDR r1, =CM_ctl_reg LDR r0, r1 ORR r0, r0, #Remap_bit STR r0, r1 ; RAM is now at 0x0. ; The exception vectors must be copied from ROM to RAM (in _main) ; Reset_Han

23、dler follows on from here 这个功能也可在有mmu时使用,ROM/RAM的重定向,初始化栈的指针,; - Amount of memory (in bytes) allocated for stacks Len_FIQ_Stack EQU 256 Len_IRQ_Stack EQU 256 Offset_FIQ_Stack EQU 0 Offset_IRQ_Stack EQU Offset_FIQ_Stack + Len_FIQ_Stack Reset_Handler LDR r0, stack_base ; located by scatter file ; Ente

24、r each mode in turn and set up the stack pointer MSR CPSR_c, #Mode_FIQ:OR:I_Bit:OR:F_Bit ; No interrupts SUB sp, r0, #Offset_FIQ_Stack MSR CPSR_c, #Mode_IRQ:OR:I_Bit:OR:F_Bit ; No interrupts SUB sp, r0, #Offset_IRQ_Stack ; System mode stack is set up last MSR CPSR_c, #Mode_SYS:OR:I_Bit:OR:F_Bit ; No

25、 interrupts SUB sp, r0, #Offset_SYS_Stack ; Set up stack limit if needed LDR r10, stack_limit ; located by scatter file,局部存储器设置,run-time的存储器必须在C库初始化前定义 如果你使用的ARM7芯片还有MMU/MPU,它必须设置; ROM/RAM 的重新映射必须完成。 TCMs(Tightly coupled memory) 如果有TCM,典型的必须使能它。 请注意:在TCM使能之前,要屏蔽ROM 在Cache打开之前要返回。 在c库初始化代码运行之后,如果cach

26、e被使能,可以避免与cache相关的问题;,扩展功能,系统初始化代码通常在进入主应用之前运行 当然,reset handler 不是一个适合使能中断和使能caches地方。 在reset handler最后应该放一个C运行库初始化代码 EG。 IMPORT _main B _main 我们可使用$Sub和$Super功能来包装符号 extern void $Super$main(void); void $Sub$main(void) cache_enable(); / enables caches int_enable(); / enables interrupts sys_to_usr_mo

27、de(); / change mode - see next slide $Super$main(); / calls original main() 相关描述可在ADS 1.2 Linker and Utilities Guide - 4.4章查阅到。,运行模式考虑,主应用程序运行在何种模式是要考虑的重要问题。 用户模式(User mode)是非特权模式(unprivileged mode)-保护你的系统 系统初始化代码只能运行在特权模式(privileged mode)。 需要执行特权操作 比如:使能中断。 如果你的应用要运行在管理模式,简单的在管理模式下退出你的reset handler

28、 就可。 如果你想在用户模式下运行你的应用,你需在$Sub$main( )改变为用户模式 当然, _user_initial_stackheap( ) 必须有权使用你的应用模式寄存器。 解决办法是在系统模式里退出reset handler 所有C库初始化代码有权使用用户寄存器,但是仍然可以执行特权操作。,Agenda,一个PC软件的构造 裁减标准C库函数到目标板 定制IMAGE的存储器映射到目标板 复位和初始化 深层次的存储器器映象考虑 编译和调试IMAGE,长跳转Veneers,ROM_LOAD 0x0000 ROM_EXEC 0x0000 * (+RO) RAM 0x80000000 fa

29、rfunc.o (+RO) * (+RW,+ZI) ,/* main.c */ int main(void) farfunc(); /* farfunc.c */ void farfunc(void); : ,代码段可被远距离分开放置 (比BL的跳转范围还远) 链接器可自动增加长跳转Veneers,远距离的函数可被成功调用。,0x00000000 bl Ven$AA$L$farfunc : : Ven$AA$L$farfunc ldr pc,pc,#-4 dcd 0x80000000 : : 0x80000000 : mov pc,lr,存储器映射寄存器,你可以使用scatterloading

30、来放置外设寄存器的存储器映射 在文件中定义它 e.g. timer_reg.c struct volatile unsigned reg1; /* timer control */ volatile unsigned reg2; /* timer value */ timer_reg; 在存储器映射的请求地址上增加另外的可执行区来放置他们: LOAD_FLASH 0x24000000 0x04000000 : TIMER 0x40000000 UNINIT timer_reg.o (+ZI) : UNINIT 显示在 ZI 段没有被初始化为0。,Stack 和 Heap 区(1),你也可以在S

31、CATTER文件中放置stack和heap 在汇编原文件里定义stack 和heap 区 比如. stackheap.s 这个空间直接保留一个为0的存储器块 AREA stack, DATA, NOINIT SPACE 0x3000 ; Reserve stack space AREA heap, DATA, NOINIT SPACE 0x3000 ; Reserve heap space END,Stack 和 Heap 区(2),增加一个可执行区来定位这个区域 LOAD_FLASH 0X24000000 0x04000000 : STACK 0x1000 UNINIT ; length =

32、 0x3000 stackheap.o (stack) ; stack = 0x4000 to 0x1000 HEAP 0x15000 UNINIT ; length = 0x3000 stackheap.o (heap) ; heap = 0x15000 to 0x18000 Heap的基地址起始为 0x15000. Stack的最大地址为 0x4000.,Stack 和 Heap 区 (3),链接器将产生一个为每个可执行区的基地址和限制地址的符号指针 在你的代码中引入这些符号 IMPORT |Image$STACK$ZI$Base| IMPORT |Image$STACK$ZI$Limit

33、| IMPORT |Image$HEAP$ZI$Base| IMPORT |Image$HEAP$ZI$Limit| stack_base DCD |Image$STACK$ZI$Limit| stack_limit DCD |Image$STACK$ZI$Base| heap_base DCD |Image$HEAP$ZI$Base| heap_limit DCD |Image$HEAP$ZI$Limit| 使用DCD指令为这些段命名,_user_initial_stackheap( ),在reset handler,这个stack指针 (r13)和stack 限制值(r10) 通常设置了,

34、他们分别通过R1和R3作为参数传递给 _user_initial_stackheap,IMPORT _use_two_region_memory EXPORT _user_initial_stackheap _user_initial_stackheap LDR r0, heap_base ; SB value setup in reset handler LDR r2, heap_limit ; SL value setup in reset handler MOV pc, lr,Stack,Heap,这个_user_initial_stackheap( )例子实现了两个存储器区域模式。 必

35、须引用 _use_two_region_memory,在这HEAP被检查,它是HEAP的限制值,而不是STACK指针,存储器映射例子,16 bit RAM,0x10000,0x18000,0x4000,0x0000,Fast 32 bit RAM,Vector Table,Stack,Exception Handlers,Flash,0x24000000,0x28000000,Peripherals,0x40000000,外设控制寄存器的地址映射,直接在FLASH运行的代码大小,16位 RAM 被用来保存数据和HEAP区,一些紧急的代码和数据可放在快速的RAM区,SCATTER文件例子,这个s

36、catter文件执行上页所显示的存储器映射。,Agenda,一个PC软件的构造 裁减标准C库函数到目标板 定制IMAGE的存储器映射到目标板 复位和初始化 深层次的存储器器映象考虑 编译和调试IMAGE,不使用段的消除和程序的入口点,在默认的情况下,链接器将从最终的image文件中删除一些从不使用的代码段,或从未使用的数据段。 要查看哪些段被删除了,在链接时用: -info unused. 为了确保不删除重要的段(比如:中断向量表) : 使用汇编指令entry标示所有的入口点(c库有一个入口点:_main(), 使用-entry选择其中一个入口点作为image的入口,否则,链接器将给警 告:

37、Image does not have an entry point. (Not specified or not set due to multiple choices) 在生成ROMmable image 使推荐使用下面的链接命令: armlink obj1.o obj2.o -scatter scatter.scf -info unused -entry 0x0 -o prog.axf,输出选项,链接器产生ELF/DWARF2 格式的映像文件, 选择适当的调试器下载调试 为把elf映像文件转为 ROMmable格式 使用 fromelf, 例如.: fromelf image.axf -

38、bin -o image.bin 产生binary格式的文件可烧入到适当的 ROM, Flash或 EPROM-Emulator, 等. 其他 ROMmable 格式的文件也可由 fromelf产生, 例如.: Motorola 32 bit Hex (-m32) Intel 32 bit Hex (-i32) Intellec Hex (-ihf).,调试ROM映像文件,编译时加调试表(-G)来进行源码级调试。 在ROM( EPROM 、 Flash 、 EPROM-Emulator )设备里烧入IMAGE文件,然后,把IMAGE文件加载到RAM里: For AXD, select File

39、-Load Memory From File with load address 0x0 On command line, use: loadbinary image.bin 0x0 从ELF格式的IMAGE文件里装载含调试信息的符号表 For AXD, select File-Load Debug Symbols On command line, use: loadsymbols image.axf,附加信息,附加信息: 例子代码在ADSExamplesembedded目录 ADS 1.2 Developer Guide 第6章 : Writing Code for ROM ADS 1.2

40、Compilers and Libraries Guide 第 4章 : The C and C+ Libraries ADS 1.2 Linker and Utilities Guide,测验,1. 默认情况下,应用程序的STACK和HEAP如何放置的? 2. 如何确认在C库里没有链接进semihosting SWI功能? 3. 在scatter描述文件里,如何确定中断向量表放在0x0? 4. 哪个函数被用来放置应用stack和heap? 5. 在c库初始化(main)前,使能cache,可以避免cache相关的问题? 6. 在两个区域的stack 和 heap模式,什么符号必须引入执行?,ROM/RAM 重定向(remmap),ROM,0x10000,0x18000,0x4000,0x0000,Aliased ROM,Reset Handler,1. 复位时,ROM通常定位到0x0; 2. 跳转到实际的ROM地址:0x10000 3. 这时,把0x0的ROM替换为RAM,把中断向量表拷贝到0。,Reset Handler,Reset Handler,Reset Handler,Reset Handler,ROM,0x10000,0x18000,Branch to real ROM,Remove alias,1,2,3,

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 其他


经营许可证编号:宁ICP备18001539号-1