2

我一直在帮助实验室进行 ARM7 汇编语言课程,今天遇到了一个学生输入以下表达式的问题:

MUL R0, R0, R1

代码没有编译。解决方案是将表达式更改为:

MUL R0, R1, R0

即 MUL 的前两个参数不能是同一个寄存器。我已经知道这一点,因为它是 ARM 文档的一部分:http: //infocenter.arm.com/help/topic/com.arm.doc.dui0489i/DUI0489I_arm_assembler_reference.pdf

学生很高兴他们的问题得到解决,但我很沮丧,因为我不知道为什么ARM7 要求像这样传递参数。我认为这可能与乘法器移位和加法时用于存储中间值的寄存器之一有关,但我什至不确定乘法是否在 ARM 上工作(事实上,我相当当然不是)。为什么参数的顺序在这里如此重要?

4

2 回答 2

4

“在 ARMv6 之前的架构中, Rn必须不同于Rd ”这一事实表明,这是在原始三级 ARM 流水线中如何实现乘法的设计限制。在 ARMv6 之前是指具有 ARM7 或更早设计的 CPU,这些都使用简单的三级流水线。与大多数指令乘法不同,执行需要多个周期,并且根据指令集限制,您的怀疑似乎是正确的,目标寄存器Rd在每个周期都被修改以计算结果。

Anthony Fox的论文验证 ARM6 乘法通过在图 4 中显示(重新格式化以适应 Stack Exchange 标记的限制)来支持这一点,在 ARM6 内核执行乘法指令期间如何修改Rd :

  • t 3

    • 获取指令
    • 增加程序计数器
    • 设置mul1reg[Rs]
    • 设置borrow为假
    • 设置count1为零


    • 如果累积则设置reg[Rd]reg[Rn],否则为零

    • 设置mulmul1[1:0]
    • 设置mul2mul1[31:2]
    • 设置borrow2borrow
    • 设置mshiftMSHIFT2(borrow,mul,count1)
  • _

    • 设置alubreg[Rm]左移mshift
    • 设置aluareg[Rd]
    • 设置mul1mul2[29:0]
    • 设置borrowmul[1]
    • 设置count1mshift[4:1] + 1


    • 设置reg[Rd]ALU6*(borrow2,mul,alua,alub)

    • 设置mulmul1[1:0]
    • 设置mul2mul1[31:2]
    • 设置borrow2borrow
    • 设置mshiftMSHIFT2(borrow,mul,count1)
    • 更新NZC标志CPSR(如果S设置了标志)
    • 如果最后一次迭代,则解码下一条指令

图 4:乘法指令的 ARM6 实现。每个周期分为两个阶段。重复t n循环,直到 MULX(mul2,borrow,mshift)为真。当等于或十五时,寄存器Rd不更新。RdRm

由于reg[Rd]在初始设置周期t 3和重复的t nRd == Rm周期期间都被修改,如果因为步骤“设置aluareg[Rm]左移mshift”期望读取Rm的原始未修改值,而不是当前中间值,则结果将是垃圾存储在Rd中。

某些 ARM7 CPU 有一个“快速乘法器”,每周期处理 8 位,而不是如上所述的每周期 2 位,但它似乎也在计算过程中修改了寄存器。

于 2017-08-20T15:44:43.327 回答
1

我的猜测是这是一个或多个内核的 ip 中的错误。

尤其是在 arm7 的日子里,你得到了一个从 arm 而不是源代码到核心的布局,让编译器解决一个 ip 错误,而不是修复错误,召回所有单元,废弃正在处理的单元,如果该错误是在供应商投资购买口罩或已经投入生产后发现的。

有了 time arm(和其他人),您可以阅读更多内容并确定您拥有哪个特定内核,并遵循勘误表(尽管 Linux 之类的软件在这方面做得很糟糕,将错误的勘误表应用于错误的内核)以了解要避免的错误。

一些“不可预测的结果”实际上是可以预测的,只是被破坏了,并且可以被 arm 用来确定这是一个克隆还是被盗的 ip。

于 2015-10-09T21:18:23.870 回答