【发布时间】:2015-09-22 14:35:44
【问题描述】:
我试图了解 arm-none-eabi-gcc 为 stm32f103 芯片组生成的一些汇编程序,它的运行速度似乎正好是我预期的一半。我对汇编器不是很熟悉,但是因为每个人都说如果你想了解你的编译器在做什么,请阅读 asm,我看到了我能走多远。它是一个简单的功能:
void delay(volatile uint32_t num) {
volatile uint32_t index = 0;
for(index = (6000 * num); index != 0; index--) {}
}
时钟速度是 72MHz,上面的函数给了我 1ms 的延迟,但我预计是 0.5ms(因为 (6000*6)/72000000 = 0.0005)。
汇编器是这样的:
delay:
@ args = 0, pretend = 0, frame = 16
@ frame_needed = 0, uses_anonymous_args = 0
@ link register save eliminated.
sub sp, sp, #16 stack pointer = stack pointer - 16
movs r3, #0 move 0 into r3 and update condition flags
str r0, [sp, #4] store r0 at location stack pointer+4
str r3, [sp, #12] store r3 at location stack pointer+12
ldr r3, [sp, #4] load r3 with data at location stack pointer+4
movw r2, #6000 move 6000 into r2 (make r2 6000)
mul r3, r2, r3 r3 = r2 * r3
str r3, [sp, #12] store r3 at stack pointer+12
ldr r3, [sp, #12] load r3 with data at stack pointer+12
cbz r3, .L1 Compare and Branch on Zero
.L4:
ldr r3, [sp, #12] 2 load r3 with data at location stack pointer+12
subs r3, r3, #1 1 subtract 1 from r3 with 'set APSR flag' if any conditions met
str r3, [sp, #12] 2 store r3 at location sp+12
ldr r3, [sp, #12] 2 load r3 with data at location sp+12
cmp r3, #0 1 status = 0 - r3 (if r3 is 0, set status flag)
bne .L4 1 branch to .L4 if not equal
.L1:
add sp, sp, #16 add 16 back to the stack pointer
@ sp needed
bx lr
.size delay, .-delay
.align 2
.global blink
.thumb
.thumb_func
.type blink, %function
我已经通过查找评论了我认为每条指令的含义。所以我相信 .L4 部分是延迟函数的循环,它有 6 条指令长。我确实意识到时钟周期并不总是与指令相同,但是由于存在如此大的差异,并且由于这是一个我认为可以有效预测和流水线化的循环,我想知道我看到 2 个时钟是否有充分的理由每条指令的周期数。
背景: 在我正在进行的项目中,我需要使用 5 个输出引脚来控制线性 ccd,并且据说时序要求相当严格。绝对频率不会被最大化(我会为引脚提供比 CPU 能力更慢的时钟),但引脚之间的相对时序很重要。因此,与其使用处于我能力极限并且可能使相对时序复杂化的中断,我正在考虑使用循环来提供引脚电压变化事件之间的短延迟(大约 100 ns),或者甚至在展开的汇编程序中对整个部分进行编码,因为我有充足的程序存储空间。有一段时间引脚没有变化,在此期间我可以运行 ADC 对信号进行采样。
虽然我要问的奇怪行为不是表演障碍,但我宁愿在继续之前理解它。
编辑:从评论中,arm tech ref 给出了指令时间。我已将它们添加到程序集中。但它仍然只有 9 个周期,而不是我预期的 12 个。跳跃本身是一个循环吗?
TIA,皮特
我想我必须把这个交给 ElderBug,尽管 Dwelch 提出了一些可能也非常相关的观点,所以谢谢大家。从这个开始,我将尝试使用展开的组件来切换更改时相隔 20ns 的引脚,然后返回 C 等待更长的等待时间,然后进行 ADC 转换,然后返回到组件以重复该过程,密切关注组件gcc 的输出以大致了解我的时间是否正常。顺便说一句,修改后的 wait_cycles 函数确实如您所说的那样工作。再次感谢。
【问题讨论】:
-
请记住,当您执行加载时,您仍然需要等待该请求进入内存和返回值所需的时间(提示:>0 个周期)在下一条指令可以使用它之前,不管how well the core tries to pipeline everything else。
-
都很有趣。我没有看过的手臂参考页面,如果我理解正确,我会用似乎是什么时间来更新问题。部分解释。