我一直在帮助实验室学习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上运行(在事实上,我相当肯定它不是)。为什么参数的顺序在这里如此重要?
答案 0 :(得分:1)
我的猜测是这是一个或多个核心的ip中的错误。
在arm 7天中,你给了一个从arm而不是源代码到核心的布局,让编译器解决ip bug,而不是修复bug,召回所有单位,废弃如果在供应商投资了面具或已经投入生产之后发现了这个错误,那么正在进行中。
随着时间的推移(和其他人)有更多东西你可以阅读并确定你拥有哪个特定的核心,并遵循勘误(虽然像Linux这样的软件在这方面做得很糟糕,将错误的勘误应用于错误的核心)来了解什么错误避免。一些“不可预测的结果”实际上是可以预测的,只是被破坏了,并且可以被手臂用来确定这是克隆还是被盗的ip。
答案 1 :(得分:1)
“ Rn 必须与ARMv6之前的架构中的 Rd 不同”这一事实表明,这是对原始三级ARM管道中如何实现乘法的设计限制。在ARMv6意味着具有ARM7或更早版本设计的CPU之前,这些都使用了简单的三级流水线。与大多数指令不同,乘法需要多个周期才能执行,并且根据指令集限制,您的怀疑是正确的,每个周期都会修改目标寄存器 Rd 以计算结果。
论文Verifying ARM6 Multiplication by Anthony Fox通过在图4中显示(下面重新格式化以适应Stack Exchange标记的限制)来支持这一点,在ARM6执行乘法指令期间如何修改 Rd 芯
t 3
- 获取指令
- 增加程序计数器
- 将
mul1
设为reg[Rs]
- 将
borrow
设为false将
count1
设为零如果累积,请将
reg[Rd]
设置为reg[Rn]
,否则为零- 将
mul
设为mul1[1:0]
- 将
mul2
设为mul1[31:2]
- 将
borrow2
设为borrow
- 将
mshift
设为MSHIFT2(borrow,mul,count1)
吨<子>名词子> 的
- 将
alub
设置为reg[Rm]
左移mshift
- 将
alua
设为reg[Rd]
- 将
mul1
设为mul2[29:0]
- 将
borrow
设为mul[1]
将
count1
设为mshift[4:1] + 1
将
reg[Rd]
设为ALU6*(borrow2,mul,alua,alub)
- 将
mul
设为mul1[1:0]
- 将
mul2
设为mul1[31:2]
- 将
borrow2
设为borrow
- 将
mshift
设为MSHIFT2(borrow,mul,count1)
- 更新
NZC
的{{1}}标记(如果设置了CPSR
标记)- 如果最后一次迭代,则解码下一条指令
图。 4:ARM6执行乘法指令。每个周期都是分开的 分为两个阶段。重复 t n 循环,直到
S
是真的。 当MULX(mul2,borrow,mshift)
等于Rd
或十五时,注册Rd
不会更新。
由于Rm
在初始设置周期 t 3 期间和重复的 t n 期间被修改循环结果将是reg[Rd]
的垃圾,因为“设置Rd == Rm
到alua
向左移动reg[Rm]
”的步骤预计会读取<的原始未修改值< em> Rm ,而不是存储在 Rd 中的当前中间值。
如上所述,某些ARM7 CPU有一个“快速乘法器”,每个周期处理8位而不是每个周期2位,但它似乎也在计算过程中修改了寄存器。