可以使用多种工具编写不同版本的 ARM 内核,但最流行的一组工具是 GNU ARM 工具链。更多地了解如何使用 ARM 内核进行嵌入式开发,以及如何安装并使用 GNU 工具。
如果您对在最普遍的微处理器上开发嵌入式系统感兴趣,那么 Advanced RISC Machines (ARM) 内核是您的最佳选择。本文通过描述一组常用的工具(GNU ARM 工具链),帮助您开始理解嵌入式系统开发的软件部分。
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 架构提供的一个独特特性是 —— 迫使程序员用全新的、独特的方式思考 —— 可以根据系统标志的当前状态选择执行每个指令。该特性能够避免在某些算法中使用分支,从而保持基于管道的指令和数据缓存机制以最佳的性能运行(因为分支导致不必要地清除缓存)。
GNU 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 工具链
要下载 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 命令将文件提取到您的主目录。
清单 1. 提取下载后的 GNU 工具链
$ 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 目录。这样,工具就可以使用了。
配置 Linux 以使用 GNU 工具链
在工具链的下载页面,您还可以找到几个有用的 PDF 文件和一份详细的入门指南,它们归档了所包含的工具。本文仅提供精简的介绍,帮助您设置好并运行工具链。
如果从事的 ARM 编程超出 Intel® 处理器编程,另一种方法是将符号链接添加到您的 /usr/local/bin 目录,它引用工具链 bin 目录中的工具。通过这种方式,您可以使用快捷方式(比如 as)交互地运行 arm-none-linux-gnueabi-as 命令。清单 2 给出了如何设置这些符号链接的示例。
清单 2. 设置引用 ARM 工具的符号链接
# 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 容易!
为 ARM 架构编写程序
有许多关于用流行的 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 命令让它变成可执行脚本。
清单 3. 构建 ARM Linux Hello World 应用程序
#!/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 中的代码添加到其中。
清单 4. ARM Linux Hello World
@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 内核。