【发布时间】:2011-05-21 20:00:28
【问题描述】:
我对 ARM 编程相当陌生。我注意到有几种架构,如 ARMv4、ARMv5、ARMv6 等。它们之间有什么区别?他们有不同的指令集或行为吗?
最重要的是,如果我为 ARMv6 编译一些 C 代码,它会在 ARMv5 上运行吗?那么在 ARMv6 上运行的 ARMv5 代码呢?或者如果我在编写内核汇编代码,我是否只需要担心差异?
【问题讨论】:
标签: c architecture arm instruction-set
我对 ARM 编程相当陌生。我注意到有几种架构,如 ARMv4、ARMv5、ARMv6 等。它们之间有什么区别?他们有不同的指令集或行为吗?
最重要的是,如果我为 ARMv6 编译一些 C 代码,它会在 ARMv5 上运行吗?那么在 ARMv6 上运行的 ARMv5 代码呢?或者如果我在编写内核汇编代码,我是否只需要担心差异?
【问题讨论】:
标签: c architecture arm instruction-set
在一般架构之间移植时要检查的非常快速和脏的区域列表:
【讨论】:
ARM 世界有点混乱。
对于 C 程序员来说,事情很简单:所有 ARM 架构都提供常规的 32 位平面寻址编程模型。只要您使用 C 源代码,您可能会看到的唯一区别是字节顺序和性能。大多数 ARM 处理器(甚至是旧型号)都可以是 big-endian 和 little-endian;然后由逻辑板和操作系统做出选择。好的 C 代码 endian 中性:无论平台字节序如何,它都能正确编译和工作(endian 中性有利于可靠性和可维护性,但也有利于性能:非中性代码是访问相同的代码数据通过不同大小的指针,这对编译器用于优化代码的严格别名规则造成严重破坏。
如果考虑二进制兼容性(即重用已编译一次的代码),情况就完全不同了:
一个给定的处理器可以实现多个指令集。最新的只知道 ARM 代码的处理器是 StrongARM,它是 ARMv4 的代表,已经很老了(15 年)。 ARM7TDMI(ARMv4T 架构)既了解 ARM 又了解 Thumb,几乎所有后续的 ARM 系统都了解,除了 Cortex-M。 ARM 和 Thumb 代码可以在同一个应用程序中混合在一起,只要在约定改变的地方插入适当的胶水;这称为 thumb interworking,可以由 C 编译器自动处理。
Cortex-M0 只知道 Thumb 指令。它知道一些扩展,因为在“普通”ARM 处理器中,操作系统必须使用 ARM 代码(用于处理中断);因此,Cortex-M0 知道一些 Thumb-for-OS 的东西。这对于应用程序代码无关紧要。
另一个 Cortex-M 只知道 Thumb-2。 Thumb-2大部分向后兼容 Thumb,至少在汇编级别。
因此,如果使用编译器开关编译某些代码,告知这是针对 ARMv6 的,那么编译器可能会使用 ARMv6 具有但不是 ARMv5 的少数指令之一。这是一种常见情况,几乎在所有平台上都会遇到:例如,如果您在 PC 上使用 GCC 编译 C 代码,使用 -march=core2 标志,则生成的二进制文件可能无法在较旧的 Pentium 处理器上运行。
调用约定是一组规则,用于指定函数如何交换参数和返回值。处理器只知道它的寄存器,没有堆栈的概念。调用约定说明参数在哪些寄存器中,以及它们是如何编码的(例如,如果有一个char 参数,它进入寄存器的低 8 位,但调用者应该清除/符号扩展高24位,还是不是?)。它描述了堆栈结构和对齐方式。它规范了结构字段的对齐条件和填充。
ARM 有两个主要约定,称为 ATPCS(旧)和 AAPCS(新)。它们在浮点值的主题上有很大的不同。对于整数参数,它们大多相同(但 AAPCS 需要更严格的堆栈对齐)。当然,约定因指令集和 Thumb 互通的存在而异。
在某些情况下,可能有一些同时符合 ATPCS 和 AAPCS 的二进制代码,但这并不可靠,并且不匹配时不会发出警告。所以底线是:你不能在使用不同调用约定的系统之间实现真正的二进制兼容性。
ARM 架构可以使用可选元素进行扩展,这些元素将自己的指令添加到核心指令集中。 FPU 就是这样一个可选的协处理器(在实践中很少遇到)。另一个协处理器是 NEON,它是一些较新的 ARM 处理器上的 SIMD 指令集。
使用协处理器的代码不会在没有该协处理器的处理器上运行,除非操作系统捕获相应的操作码并在软件中模拟协处理器(这或多或少会在使用浮点参数时发生) ATPCS 调用约定,它是慢)。
总而言之,如果你有 C 代码,然后重新编译它。不要尝试重用为其他架构或系统编译的代码。
【讨论】:
将 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 非常适合用于教育目的。了解您可能不想划分或使用未对齐访问的原因。
【讨论】:
如果差异对您来说真的那么重要,您应该能够从 ARM 的公共文档中弄清楚。
但是用高级语言(即使它只是和 C 一样“高级”)编写的全部意义在于不用担心。你要做的就是重新编译。即使在内核中,也不需要用汇编语言编写太多内容;当你确实必须在汇编中编写一些东西(即不仅仅是为了获得最大性能),这通常不仅仅是因为CPU的选择(例如,什么是直接内存映射的?)。
【讨论】:
只要您坚持使用用户代码(内核代码当然不同),ARM 本身就相当兼容。在托管操作系统环境中,您可能会坚持使用 ARMv5(ARM926 处理器)。
最大的不同来自:
【讨论】: