33

I'm fairly new to programming for ARM. I've noticed there are several architectures like ARMv4, ARMv5, ARMv6, etc. What is the difference between these? Do they have different instruction sets or behaviors?

Most importantly, if I compile some C code for ARMv6, will it run on ARMv5? What about ARMv5 code running on ARMv6? Or would I only have to worry about the difference if I were writing kernel assembly code?

4

5 回答 5

40

ARM 世界有点混乱。

对于 C 程序员来说,事情很简单:所有 ARM 架构都提供了一个常规的 32 位平面寻址编程模型。只要您使用 C 源代码,您可能会看到的唯一区别是字节顺序和性能。大多数 ARM 处理器(甚至是旧型号)都可以是 big-endian 和 little-endian;然后由逻辑板和操作系统做出选择。好的 C 代码是端中立的:无论平台端如何,它都能正确编译和工作(端中立有利于可靠性和可维护性,但也有利于性能:非中立代码是通过不同大小的指针访问相同数据的代码,这对编译器用于优化代码的严格别名规则造成严重破坏)。

如果您考虑二进制兼容性(即重用已编译一次的代码),情况就完全不同了:


  • 有几个指令集:
    1. 带有 26 位程序计数器的原始 ARM 指令集(非常古老,现在不太可能遇到)
    2. 带有 32 位程序计数器的 ARM 指令集(通常称为“ARM 代码”)
    3. Thumb 指令集(16 位简化操作码)
    4. Thumb-2 指令集(带扩展的 Thumb)

一个给定的处理器可以实现几个指令集。最新的只知道 ARM 代码的处理器是 StrongARM,它是 ARMv4 的代表,已经很老了(15 年)。ARM7TDMI(ARMv4T 架构)既了解 ARM 又了解 Thumb,几乎所有后续的 ARM 系统都了解,除了 Cortex-M。ARM 和 Thumb 代码可以在同一个应用程序中混合在一起,只要在约定改变的地方插入适当的胶水;这称为拇指互通,可以由 C 编译器自动处理。

Cortex-M0 只知道 Thumb 指令。它知道一些扩展,因为在“普通”ARM 处理器中,操作系统必须使用 ARM 代码(用于处理中断);因此,Cortex-M0 知道一些 Thumb-for-OS 的东西。这对于应用程序代码无关紧要。

另一个 Cortex-M 只知道 Thumb-2。Thumb-2大多向后兼容 Thumb,至少在汇编级别上是这样。


  • 一些架构添加了额外的指令。

因此,如果使用编译器开关编译某些代码,告知这是针对 ARMv6 的,那么编译器可能会使用 ARMv6 的少数指令之一,但不会使用 ARMv5。这是一种常见情况,几乎在所有平台上都会遇到:例如,如果您在 PC 上使用 GCC 使用-march=core2标志编译 C 代码,则生成的二进制文件可能无法在较旧的 Pentium 处理器上运行。


  • 有几种调用约定。

调用约定是一组规则,指定函数如何交换参数和返回值。处理器只知道它的寄存器,没有堆栈的概念。调用约定说明参数在哪些寄存器中,以及它们是如何编码的(例如,如果有一个char参数,它会进入寄存器的低 8 位,但调用者是否应该清除/符号扩展高 24 位,或不 ?)。它描述了堆栈结构和对齐方式。它标准化结构字段的对齐条件和填充。

ARM 有两个主要约定,称为 ATPCS(旧)和 AAPCS(新)。它们在浮点值的主题上有很大的不同。对于整数参数,它们大多相同(但 AAPCS 需要更严格的堆栈对齐)。当然,约定因指令集和 Thumb 互通的存在而异。

在某些情况下,可能会有一些同时符合 ATPCS 和 AAPCS 的二进制代码,但这并不可靠,并且不匹配时不会发出警告。所以底线是:你不能在使用不同调用约定的系统之间实现真正的二进制兼容性。


  • 有可选的协处理器。

ARM 体系结构可以使用可选元素进行扩展,这些元素将自己的指令添加到核心指令集中。FPU 就是这样一个可选的协处理器(在实践中很少遇到)。另一个协处理器是 NEON,它是一些较新的 ARM 处理器上的 SIMD 指令集。

使用协处理器的代码不会在没有协处理器的处理器上运行,除非操作系统捕获相应的操作码并在软件中模拟协处理器(使用 ATPCS 调用时浮点参数或多或少会发生这种情况约定,而且速度很)。


总而言之,如果你有 C 代码,那么重新编译它。不要尝试重用为其他架构或系统编译的代码。

于 2010-12-08T18:45:51.030 回答
5

把这个 ARM 与 ARM 的事情想象成 wintel 计算机与 intel mac。假设即使您在两台计算机上都有相同的英特尔芯片(系列),那么您的部分 C 代码可以编译一次并在两个处理器上运行得很好。您的程序发生变化的位置和原因与英特尔处理器无关,而与它周围的芯片和主板以及在这种情况下的操作系统有关。

对于 ARM 与 ARM,大多数差异不是核心,而是围绕核心的供应商特定逻辑。所以这是一个加载的问题,如果您的 C 代码是一些调用标准 api 调用的应用程序,那么它应该在 arm 或 intel 或 powerpc 或其他任何东西上编译。如果您的应用程序开始与片上或板载外围设备通信,那么无论处理器类型是什么,一块板,一块芯片都会有所不同,因此您的 C 代码必须为该芯片或主板编写。如果您为 ARMv6 编译二进制文件,它可以并且将有被认为在 ARMv4 上未定义的指令,并将导致异常。如果您为 ARMv4 编译,ARMv6 应该可以正常运行。

充其量,如果您处于此应用程序领域,那么您可能会看到的只是性能差异。其中一些与您在编译器选项中的选择有关。有时您可以帮助编写代码。我建议尽可能避免除法和浮点。我不喜欢乘法,但如果被推,我会乘法而不是除法。x86 让我们被非对齐访问宠坏了,如果您现在从对齐 I/O 开始,它会在您进入其他也喜欢对齐访问的芯片时为您节省时间,或者您会被各种操作系统和引导加载程序将 ARM 配置为做出反应,这些都不是您在 x86 上习惯的。同样保持这个习惯,你的 x86 代码会运行得更快。

获取一份 ARM ARM 的副本(google:ARM Architectural Reference Manual,你可以在很多地方免费下载,我不知道当前的 rev 是什么,rev I 什么的)。浏览 ARM 指令集,发现大多数指令在所有内核上都受支持,并且随着时间的推移添加了一些指令,例如除法和字节交换等。你会看到核心之间没有什么好害怕的。

从系统的角度思考,wintel 与 intel mac。ARM 不制造芯片,他们制造和许可内核。大多数在其芯片中使用 ARM 的供应商都有自己的特殊调味料。因此,这就像 wintel 与 mac 中间有相同的处理器,但在处理器接触和必须使用的所有东西方面完全不同。它并不止于 ARM 内核,ARM 销售外设、浮点单元、缓存等。例如,很少有 ARMv4 是相同的。如果您的代码涉及差异,您将遇到问题,如果您不会。

对于芯片的臂部分,除了 ARM ARM 之外,还有 TRM(技术参考手册)。但是,如果您使用的组件的 trm 错误,可能会让您头疼。TRM 可能有寄存器描述和其他 ARM ARM 没有的东西,但如果你生活在应用程序空间中,你可能不需要它们中的任何一个,也不需要 ARM ARM。如果没有别的,ARM ARM 非常适合用于教育目的。了解您可能不想划分或使用未对齐访问的原因。

于 2010-12-08T05:44:05.743 回答
4

ARM its self is fairly compatible, provided you stick to user code (kernel code of course is different). In a hosted-OS environment, you will likely stick to ARMv5 (ARM926 processors).

The big difference comes from:

  1. Cache behavior is widely different. Cache on some ARMs is even virtually addressed, which can make process switches painful.
  2. The FPU comes in several flavors (VFP, NEON, and more!). Many smaller processors do not even have an FPU.
  3. Thumb mode has changed dramatically. Thumb mode between ARMv5 is not portable to Thumb2 (ARMv6+), nor backwards compatible.
于 2010-12-07T20:17:14.793 回答
3

如果差异对您来说真的那么重要,您应该能够从 ARM 的公共文档中弄清楚。

但是用高级语言编写的全部意义(即使它只是和 C 一样“高级”)就是不用担心它。你所做的就是重新编译。即使在内核中,也不需要用汇编语言编写太多内容;当你必须在汇编中编写一些东西时(即不仅仅是为了获得最大性能),这通常不仅仅是因为 CPU 的选择(例如,什么是直接内存映射的?)。

于 2010-12-08T00:09:14.193 回答
3

通常在架构之间移植时要检查的非常快速和肮脏的区域列表:

  • 字节序:联合使用、数据类型转换、位域、数据共享
  • 对齐:对齐要求,以及可能的未对齐访问的性能特征
  • 记忆模型:弱与强?
  • 多核:一致性如何工作?
  • 杂项:有符号与无符号数据类型、数据结构打包、堆栈使用、枚举数据类型...
于 2014-03-16T07:12:02.957 回答