1 ARM 微控制器开发的新工具
Keil 是颇受业界欢迎的 51 单片机开发工具,它拥有流畅的用户界面与强大的仿真功能。 ARM 将 Keil 公司收购之后,正式推出了针对 ARM 微控制器的开发工具 RealView Microcontroller Development Kit (简称 RealView MDK 或者 MDK ),它将 ARM 开发工具 RealView Development Suite (简称 RVDS )的编译器 RVCT 与 Keil 的工程管理、调试仿真工具集成在一起,是一款非常强大的 ARM 微控制器开发工具。
很多嵌入式系统开发工程师对 ARM 的老版本开发工具 ADS 非常熟悉,而 RealView MDK 与 ADS 相比较,从外观、仿真流程以及内部二进制编译、链接工具上都有了不少改进,用法稍有不同。本文的主旨是介绍通用的流程以及一些注意事项,帮助 ADS 用户将老的、遗留的 ADS 工程转化成在 RealView MDK 中进行开发调试的工程。
为了给国内嵌入式 ARM 开发工程师提供高性价比的开发工具, 2007 年 5 月 22 日, ARM 公司和英蓓特公司正式联合推出 “RealView MDK 中国版 ” 。 该新产品是特别为满足中国软件和系统开发厂商需求而推出的,起价低于 2 500 元人民币。专业人士认为,相比其他以欧美市场定价的开发工具,此款 RealView MDK 易于纳入设计工具的整体预算中。中国版 RealView MDK 即日起可通过 ARM 授权培训中心暨 ARM 在中国的工具分销商 —— 深圳市英蓓特信息技术有限公司(简称 “ 英蓓特 ” )获得。 此外,有关中国版 RealView MDK 的特性和其他信息, ARM 公司专门设立了新的网站 http://www.realview.com.cn/ ,开发工程师可以进一步了解 RealView MDK 及其相关资讯。
2 工具结构的改进
作为 ARM 的新一代微控制器开发工具, RealView MDK 不但包含 ARM 的最新版本编译、链接工具(即 RVDS3.0 的编译、链接工具),而且根据微控制器调试开发的特点采用了与 ADS 、 RVDS 完全不同的调试、仿真环境 uVision debugger 与 simulator 。因此, MDK 与 ADS 在工具架构组成上有一些不同,这些区别包括:不同的工程管理器,不同版本的 ARM 编译器( compiler ),不同的调试器( debugger ),不同的仿真器( simulator ),以及不同的硬件调试单元,详见表 1 。
表 1 工具结构对比
2.1 编译工具例化形式
在 ADS 中,当用户要将高级语言代码编译成目标文件时,需要根据目标机器码的不同( 16 位的 Thumb 代码或者 32 位的 ARM 代码),以及高级语言的不同( C 代码或者 C++ 代码)选择不同的编译器可执行文件; RVCT3.0 编译器则将它们全部统一为 armcc ,仅仅通过不同的编译选项进行区分。表 2 较为详细地列出了其中的差别。
表 2 MDK 与 ADS 编译器的例化形式对比
![](http://blogimg.chinaunix.net/blog/upfile2/081201114716.gif)
2.2 POSIX 格式
MDK 集成了 RVDS 的编译工具 RVCT ,与 ADS 相比,除去编译、链接工具的可执行二进制文件不同之外,两个不同版本编译器的很多编译、链接选项也有所不同。有关编译、链接选项的变化,用户可以参考 ARM 工具文档 “RVCT Compiler and Libraries Guide” 中的 Table E2 Mapping of compiler options 。
RVCT 采用了 POSIX 格式的编译、链接选项,所有的多字符选项前必须使用双中划线。例如: ADS 的编译选项 -cpu ,在 MDK 中需要改写成 --cpu ,否则用户在 MDK 中直接使用 ADS 的 makefile 时,工具会产生一个如下警告:
Warning: L3910W: Old syntax, please use '--cpu'
2.3 ARM ABI 的变化 ARM
ABI 是 Application Binary Interface for the ARM Architecture 的简称,是一系列 ARM 体系架构标准的集合,囊括了 ARM 二进制代码交互、开发工具以及操作系统等方面。
对目标文件进行链接之前, MDK 工具的链接器会严格检查各个目标文件( objects ),判断它们是否复合 ARM 体系结构的 ABI 标准。而 MDK 与 ADS 编译、链接工具所遵循的 ARM ABI 是不同版本的,所以将 ADS 的遗留工程直接移植到 MDK 并进行链接时,用户可能会遇到如下的错误或者警告:
Error: L6238E: foo.o(.text) contains invalid call from '~PRES8' function to 'REQ8' function
Warning: L6306W: '~PRES8' section foo.o(.text) should not use the address of 'REQ8' function foobar
这是因为新工具的 ABI 要求在函数调用时,系统必须保证堆栈指针 8 字节对齐,即每次进栈或者出栈的寄存器数目必须为偶数。这是为了能够更加高效地使用 STM 与 LDR 指令对 “double” 或者 “long long” 类型的数据进行访问。而老的 ARM 开发工具 ADS 并没有考虑到新的 ARM 内核架构,其 ABI 对于堆栈的操作仅仅要求 4 字节对齐。所以当用户将在 ADS 中编译、链接成功的工程代码移植到 MDK 上,或者将老的、 ADS 遗留的目标文件、库文件在新工具 MDK 中进行链接时, MDK 的链接器就会报出以上的错误。
对于以上情况,用户可以通过简单修改代码并重新编译、链接,或者使用特殊的编译选项来解决。
2.3.1 重新编译所有代码
当用户拥有该 ADS 遗留工程的所有源代码时,使用 MDK 重新编译、链接全部代码是最好的解决方法。 MDK 中的新版本编译工具会重新生成满足堆栈 8 字节对齐要求的目标文件,避免由于堆栈不对齐引起的链接错误。
当工程中包含汇编代码时,用户可能还需要做少量的代码修改。这些修改包括:
① 检查汇编源码中的指令,确保堆栈操作指令是 8 字节对齐的。
例 1 中, ADS 的遗留代码一次性将 5 个寄存器压栈,由于 ARM 的指令寄存器宽度为 32 位(即 4 字节),显然 5 个寄存器入栈之后,堆栈指针不能够满足 64 位、 8 字节对齐。为了解决这种问题,可以将另外一个并不需要压栈的寄存器( R12 )同时压栈,这样当 6 个 32 位寄存器进栈之后,堆栈就能满足 64 位对齐了。
例 1
STMFD sp!, {r0r3, lr} ; 将 R0,R1,R2,R3,LR (奇数)寄存器入栈
↓
STMFD sp!, {r0r3, r12, lr} ; 将偶数个寄存器入栈
② 在每个汇编文件的开头,添加 “PRESERVE8” 指令,见例 2 。
例 2
AREA Init, CODE, READONLY
↓
PRESERVE8
AREA Init, CODE, READONLY
2.3.2 使用 --apcs /adsabi 编译选项
当用户没有该 ADS 遗留工程的全部源码,只拥有库文件或者目标文件时,可以通过 --apcs/adsabi 编译选项强制 MDK 的编译器产生复合 ADS ABI 要求的目标文件,以达到与遗留的 ADS 库文件、目标文件兼容的目的。
注意: ARM 新工具将不会继续支持 --apcs/adsabi 选项。建议用户及时更新工具到最新版本。
2.4 分散加载注意事项
MDK 同样支持 ADS 的分散加载文件,但是当分散加载文件中涉及到必须被放置 ROOT Region 中的 C 库函数时,有时用户需要做少量修改。
ROOT Region 的 load address 与 execution address 相同,所以这部分代码在系统初始化时无需进行搬移操作,很多库函数(如 __scatter*.o 或者 __dc*.o )必须被放置在 Root Region 中。
例 3 分散加载文件的修改。
;ADS 中的分散加载文件
ROM_LOAD 0x0 {
ROM_EXEC 0x0 {
vectors.o (Vect, +First)
_ _main.o (+RO)
* (Region $$ Table)
* (ZISection $$ Table)
}
RAM_EXEC 0x100000 {
*.o (+RO,+RW,+ZI)
}
}
;MDK 中的分散加载文件 1
ROM_LOAD 0x0 {
ROM_EXEC 0x0 {
vectors.o (Vect, +First)
* (InRoot $$ Sections)
}
RAM_EXEC 0x100000 {
*.o (+RO,+RW,+ZI)
}
}
;MDK 中的分散加载文件 2
ROM_LOAD 0x0 {
ROM_EXEC 0x0 {
vectors.o (Vect, +First)
__main.o(*)
* (Region $$ Table)
__scatter*.o(*)
__dc*.o(*)
}
RAM_EXEC 0x100000 {
*.o (+RO,+RW,+ZI)
}
}
在 ADS 中,用户必须在分散加载文件中明确地将特定 section 代码放置在 Root Region 中。而 MDK 为了支持新 RW 压缩机制,采用了新的 region table 格式,这种新的格式并不包含 ZISection $$ Table ,而且新的 scatterloading (__scatter*.o) 与 decompressor (__dc*.o) 必须被放置在 root region 中。所以例 3 中 ADS 的分散加载文件应该被修改成新的形式。例 3 中提供了两种修改分散加载文件的方法,分散加载文件 1 通过 InRoot $$ Sections 自动将所有必需的库目标放置在 root region 中,而分散加载文件 2 则详细地注明了 __scatter*.o 与 __dc*.o 的位置。
2.5 C 库函数的差异
为了与新的 ABI 一致, MDK 中的库函数名称与 ADS 可能会不同。 ADS 中的 __rt_* 库函数被替换为 __aeabi_* 。如果用户的 ADS 工程中曾经重定义( retarget )过这些库函数,那么在移植到 MDK 时,需要重新实现这些函数,以满足新 ABI 的要求。表 3 列出了部分库函数的对应关系。
表 3 部分库函数对比
3 移植实例
结合以上对 MDK 与 ADS 差异的描述,本节将以实例的形式,叙述如何将 ADS1.2 上的遗留代码移植到 MDK 上。
以 Philips 公司的 LPC2294 ( ARM7TDMI )为处理器,将一个在 ADS1.2 上开发的,由 LPC2294 控制 LED 闪烁的工程移植到 MDK 上来。从图 1 可以看出,该工程( Legacy_ADS.mcp )共有 4 个源文件( Startup.s 、 tartget.c 、 IRQ.s 、 main.c ),以及 1 个分散加载文件( Scatterload )。
![](http://blogimg.chinaunix.net/blog/upfile2/081201114907.gif)
图 1 ADS 遗留工程
使用 ADS1.2 编译器:
编译选项为: -O1-g+
链接选项为: -info totals -entry 0x00000000 -scatter ./src/Scatterload.scf -info sizes
得到最终代码尺寸信息如下:
Total RO Size(Code + RO Data)1640 (1.60kB)
Total RW Size(RW Data + ZI Data)1128 (1.10kB)
Total ROM Size(Code + RO Data + RW Data)1640 (1.60kB)
![](http://blogimg.chinaunix.net/blog/upfile2/081201123902.gif)
其具体步骤如下:
( 1 ) 在 MDK 中新建工程
打开 MDK ,在主菜单中选择 Project(New…(uVision Project ,并给新工程命名为 New_MDK.uv2 ,单击 “ 保存 ” 按钮,见图 2 。
![](http://blogimg.chinaunix.net/blog/upfile2/081201115956.gif)
图 2 在 MDK 中新建工程
在 MDK 自动弹出的器件选择窗口( Select Device for Target )中选择该工程所对应的处理器型号 “LPC2294” ,并单击 “ 确定 ” 按钮,见图 3 。当 MDK 提示用户是否自动添加启动代码时,选择 “ 否 ” 。
( 2 ) 添加源文件,并设置工程属性
将 Legacy_ADS.mcp 工程中所有的源文件都添加到新的 New_MDK.uv2 工程中来,见图 4 。
单击工程属性快捷键 ,打开工程属性设置窗口,并选择 C/C++ 标签页,设置编译器属性。用户可以根据以前 ADS 工程的编译属性设置,也可以根据当前具体需求重新设置编译属性。在本例中,将 ADS 遗留工程的编译属性 “O1g+” 修改为 “O1g W” 后,拷贝到 Misc Controls 文本框中,见图 5 。这是因为由于编译器版本的变化,其对应的编译选项也有所变化。
注意: W 选项可以抑止所有的 warning 。
![](http://blogimg.chinaunix.net/blog/upfile2/081201120016.gif)
图 3 在 MDK 中选择合适的处理器
![](http://blogimg.chinaunix.net/blog/upfile2/081201120043.gif)
图 4 将 ADS 工程的遗留源代码全部添加到新工程中
![](http://blogimg.chinaunix.net/blog/upfile2/081201122533.gif)
图 5 编译选项的设置
对 ADS 工程中的链接选项作适当修改如下,使其复合 POSIX 格式。
--info totals--entry 0x00000000--scatter ./src/Scatterload.scf --info sizes
选择 Linker 标签,将修改过的链接选项拷贝至 MDK 工程属性的 Linker 属性中,并单击 “ 确定 ” 按钮,见图 6 。
![](http://blogimg.chinaunix.net/blog/upfile2/081201123621.gif)
图 6 链接选项的设置
( 3 ) Build 工程并适当修改代码
当所有的工程属性都设置好之后,单击 “Build all target file” 快捷键 ,对整个工程进行编译、链接。在 MDK 窗口的 build 输出一栏中,我们会发现系统出现了一个链接错误 L6238E (见图 7 )。这是由于 MDK 中新版本编译、链接工具与 ADS 的老版本 build 工具采用不同的 ABI 造成的(详见 2.3 小节)。
为此我们打开该工程中的汇编文件 startup.s ,在该程序第 55 行添加 PRESERVE8 指令,如下所示:
CODE32
PRESERVE8
AREAvectors,CODE,READONLY
( 4 ) 重新编译链接该工程
代码修改完毕之后,单击 “Build all target file” 快捷键 ,对该工程进行二次编译、链接。 MDK 将成功生成 New_MDK.axf 文件,并显示其代码尺寸信息为:
Program Size: Code=1576RO-data=64 RW-data=0 ZI-data=1128
这些信息同样可以从链接生成的 New_MDK.map 文件中得到。
( 5 ) 代码调试与固化
与其他 ARM 开发工具相比较, MDK 拥有非常出色的仿真功能,可以帮助用户在纯软件平台上进行较为精确的调试。用户可以在工程属性设置窗口选择 Simulator 调试(见图 8 ),或者通过硬件调试工具( uLink )进行调试。
![](http://blogimg.chinaunix.net/blog/upfile2/081201123701.gif)
图 7 链接错误 L6238E
![](http://blogimg.chinaunix.net/blog/upfile2/081201123727.gif)
图 8 选择 uVision Simulator 作为调试平台
当选择 Simulator 调试时,单击 debug 快捷键 ,打开 Simulator 调试窗口,见图 9 。为了验证该程序在 LPC2294 硬件平台上是否能够正确执行,通过 GPIO 口驱动 LED 进行循环闪烁,用户可以单击 Peripherals>GPIO>Port2 ,将 GPIO 端口 2 的仿真界面打开,见图 9 。
单击运行快捷键 ,可以看到在 GPIO 端口 2 的仿真调试窗口中, IO 口的输出在不停地循环变化。
当程序通过了仿真调试之后,用户就可以通过 MDK 的硬件调试工具 uLink 将最终代码固化在非易失性的存储器中了。 uLink 的设置如图 10 和图 11 所示。
![](http://blogimg.chinaunix.net/blog/upfile2/081201123847.gif)
图 11 选择 MDK 默认的 LPC2294 Flash 烧写代码
4 总结