分类: 嵌入式
2011-04-04 20:19:20
如果您对在最普遍的微处理器上开发嵌入式系统感兴趣,那么 Advanced RISC Machines (ARM) 内核是您的最佳选择。本文通过描述一组常用的工具(GNU ARM 工具链),帮助您开始理解嵌入式系统开发的软件部分。
嵌入式系统开发人员最关注的问题之一是,如果通过最少的电能获得最大的处理能力。对于如何在处理器功率和电池消耗之间找到平衡点,ARM 处理器家族拥有最好的设计。
通过积累 20 多年来开发多个版本的经验,ARM 内核获得了最先进的技术。运行在移动电话上(比如 T-Mobile G1 Android)的最新芯片级系统 (SoC) 处理器组合了双核(ARM9 和 ARM11)处理器,从而改进了低功耗平台上的多媒体应用程序的性能。
最新的 ARM 内核支持两种操作状态:ARM 状态,其中内核执行 32 位整词对齐指令;和 THUMB 状态,其中内核执行 16 位半词对齐指令。ARM 模式为处理器提供最大的功率和寻址范围,而 THUMB 模式允许以紧凑、节省内存的方式编写应用程序的某些部分,从而节省内存开销。在这两种模式之间切换是很容易的,并且对很多算法而言,代码的行数可以大大减少。
ARM 处理器通过利用改进的 Harvard 架构 提升了性能。在这种架构中,处理器使用独立的数据和指令缓存,但它们通过相同的总线访问额外内存。另外,指令被装入分为 5 个阶段的 “管道”,从而使并行处理发生在最近加载到管道中的 5 个指令上。换句话说,这 5 个独立的行为(获取、解码、ALU、内存和写)涉及到并行发生的指令。只要管道的流通稳定,代码就能享受到并行带来的速度优势。但如果有一个分支在管道之 外编写代码,就必须重置整个管道,这会损害性能。所以在设计代码时要谨慎,尽量避免使用分支。
ARM 架构提供的一个独特特性是 —— 迫使程序员用全新的、独特的方式思考 —— 可以根据系统标志的当前状态选择执行每个指令。该特性能够避免在某些算法中使用分支,从而保持基于管道的指令和数据缓存机制以最佳的性能运行(因为分支导致不必要地清除缓存)。
大部分 ARM 系统编程发生在使用交叉编译工具的非 ARM 工作站上,其目标是在 ARM 平台上使用。GNU ARM 工具链是一种编程环境,它允许您在设计、开发和使用 ARM 模拟器进行测试时选择自己最喜欢的工作站环境。
GNU 工具链驻留在 CodeSourcery 上(见 参考资料 部分的链接),可以免费下载使用。它也被称为 Sourcery G++ Lite。 除了 GNU C Library 之外,其他所有工具都是根据标准的 GNU Public License version 3 (GPL3) 进行授权的。GNU C Library 根据 GPL version 2.1 进行授权。GNU 工具链中包含的工具有二进制实用程序(binutils)、GNU Compiler Collection (GCC)、GNU Remote Debugger (GDB)、GNU make 和 GNU 内核实用程序。
此外,Sourcery G++ Lite 包还包含大量关于 GNU 工具链的文档,其中包括 GNU Coding Standards 文档 —— 在 GNU 汇编程序文档下面,是很好的读物,因为您可以找到许多关于 ARM 的信息:代码、语法和指令等。
要下载 GNU 工具链,请访问 CodeSourcery 下载站点(见 参考资料)并选择 IA32 GNU/Linux TAR 文件:
arm-2008q3-72-arm-none-linux-gnueabi-i686-pc-linux-gnu.tar.bz2 |
有针对主要客户机操作系统的各种 GNU 版本,但本文主要关注在 Linux® 下安装和使用工具链的 Lite 版本。
由于 ARM 设计在其发展过程中已经经历不同的版本,所以这个 Lite 包包含了处理器设计的 3 个最常见版本的不同 C 库,这 3 个版本是 ARM v4T、ARM v5T 和 ARM 7。
接下来,使用 bunzip2 命令将文件提取到您的主目录。
$ bunzip2 arm-2008q3-72-arm-none-linux-gnueabi-i686-pc-linux-gnu.tar.bz2 $ tar -xvf arm-2008q3-72-arm-none-linux-gnueabi-i686-pc-linux-gnu.tar . . . (Files listed while being extracted from the archive.) . . . $ |
现在,修改您的 PATH 环境变量以访问工具链的 bin 目录。这样,工具就可以使用了。
在工具链的下载页面,您还可以找到几个有用的 PDF 文件和一份详细的入门指南,它们归档了所包含的工具。本文仅提供精简的介绍,帮助您设置好并运行工具链。
如果从事的 ARM 编程超出 Intel® 处理器编程,另一种方法是将符号链接添加到您的 /usr/local/bin 目录,它引用工具链 bin 目录中的工具。通过这种方式,您可以使用快捷方式(比如 as)交互地运行 arm-none-linux-gnueabi-as 命令。清单 2 给出了如何设置这些符号链接的示例。
# cd /usr/local/bin # which arm-none-linux-gnueabi-as /home/bzimmerly/Sourcery_G++_Lite/bin/arm-none-linux-gnueabi-as # ln -s /home/bzimmerly/Sourcery_G++_Lite/bin/arm-none-linux-gnueabi-as as # ls -l as lrwxrwxrwx 1 bzimmerly bzimmerly 76 2009-03-13 02:48 as -> /home/bzimmerly /Sourcery_G++_Lite/bin/arm-none-linux-gnueabi-as # ./as --version GNU assembler (Sourcery G++ Lite 2008q1-126) 2.18.50.20080215 Copyright 2007 Free Software Foundation, Inc. This program is free software; you may redistribute it under the terms of the GNU General Public License version 3 or later. This program has absolutely no warranty. This assembler was configured for a target of `arm-none-linux-gnueabi'. # |
使用 which 命令查找该命令的完整路径名,这样方便将其复制粘贴到 ln 命令。然后,使用 ln -s 命令创建符号链接。完成之后,列出符号链接并运行它,核实是否已经成功创建。通过创建引用 GNU 工具链 bin 目录中的所有工具的简单符号链接,您就不需要输入冗长的名字了。当然,每次运行汇编程序时,输入 as 总比输入 arm-none-linux-gnueabi-as 容易!
有许多关于用流行的 C 语言编写 ARM 程序的指南,但关于用汇编语言编写的指南却很少。在本文,我打算打破传统,用汇编语言编写程序 —— 这通常被认为是编程的 “邪端”。这是一个简单的 “Hello World” 类型的程序,其目标是针对特定的 ARM Linux 版本。
本文描述的示例程序被设计为运行在运行 Android Linux 的 T-Mobile G1 移动电话上。它是用通用的方式编写的,因此也可以运行在其他基于 ARM 的 Linux 平台上(仅进行标准的系统调用)。然而,您至少要使用 2.6.16 版本的 Linux 内核,它允许您使用新的 Embedded Application Binary Interface (EABI) 内核系统调用。
注意:要阅读有关为硬件进行 ARM 编程而不是在 Linux 下进行编程的文章,请查看 参考资料 小节提供的 Embedded.com 文章链接。
使用您最喜欢的编辑器,通过清单 3 中的代码创建一个称为 build 的脚本。这个脚本运行 GNU ARM 汇编程序,其后是一个连接器(linker)。创建这个程序的目的是实现一个非常小的可执行程序,所以我使用连接器开关 --strip-all 去除所有调试信息。创建该脚本之后,发出 chmod +x build 命令让它变成可执行脚本。
#!/bin/bash echo Building the ARM Linux Hello World... arm-none-linux-gnueabi-as -alh -o hw.o hw.S > hw.lst arm-none-linux-gnueabi-ld --strip-all -o hw hw.o |
接下来,创建名为 hw.S 的源模块,并将清单 4 中的代码添加到其中。
@filename: hw.S .text .align 2 .global _start @ ssize_t sys_write(unsigned int fd, const char * buf, size_t count) @ r7 r0 r1 r2 _start: adr r1, msg @ Address mov r0, #1 @ STDOUT mov r2, #16 @ Length mov r7, #4 @ sys_write svc 0x00000000 @ int sys_exit(int status) @ r7 r0 mov r0, #0 @ Return code mov r7, #1 @ sys_exit svc 0x00000000 .align 2 msg: .asciz "Hello Android!\n\n" |
在 GNU 汇编语言的语法中,“at” 符号 (@) 用于指定行注释。汇编语言忽略 @ 之后的任何内容,直至行末。
这个程序使用两个标准的 Linux 系统调用:sys_write 和 sys_exit。在这两个调用中,以上的汇编语言代码等效于 C 语言的注释形式。这样可以更加容易看到 ARM 寄存器如何恰当地映射到系统调用使用的调用参数。需要记住一个规则,参数是自左至右的,从 r0 到 r6。寄存器 r7 很特别,因为它存储使用的系统调用号码。
svc 0x00000000 指令告诉 ARM 处理器调用 “超级用户”,在本例中是 Linux 内核。
这个工具链为调试底层程序提供流行的 GDB。当程序的运行目标是带有 JTAT 或 ICE 单元的单板计算机时,您可以使用 Sourcery G++ Lite 调试器 (gdb) 远程调试 ARM 代码。
如果您希望像我一样测试代码 —— 在运行在移动电话上的 Android Linux 系统上 —— 您需要使用配套的 USB 连接线将电话连接到工作站,然后使用 Android 软件开发工具箱的 adb push 命令将该程序转移到移动电话中。在移动电话上,在一个包含可执行代码 (/data/local/bin) 的目录中,通过发出 chmod 555 hw 命令让程序变得可执行。(Android 上的 chmod 命令不使用 +x,所以 555 是必要的)。
最后,使用 adb shell 命令连接到电话,并使用 cd 切换到正确的目录,然后使用 ./hw 运行它。如果一切按计划进行,程序的响应应该和在我手机上的响应一样,会出现问候语 “Hello Android!”。
如果您对本文的 ARM 汇编编程感兴趣,可以通过 参考资料 小节提供的链接更多地了解这个处理器设计。深入学习 ARM 内核的最好资源是 ARM 开发宝典 ARM ARM,它是 ARM Architecture Reference Manual 的缩略。
从职业方面考虑:目前全球移动电话的数量十分庞大,并且逐年增加。现在,我们仅需在口袋中携带一个存储量为数 GB 的具有双核处理器的微型计算机,就可以完全访问 Internet,获取即时信息或进行娱乐。移动电话供应商对高级程序员存在巨大的需求。对于流行的 ARM,有很多有趣的工作可以做。您通常可以尽情使用这些工具,并从中享受快乐。编程是科学和艺术的结合,并且是一种最能获得乐趣的职业。
学习
获得产品和技术