了解Cortex M4上的循环计数

时间:2016-01-11 15:24:31

标签: assembly arm stm32f4discovery cortex-m

我正在使用带有Cortex M4的STM32F407,我正在通过在调用我在汇编中实现的函数(在C中)之前和之后直接读取(mkv|avi|mpe?g|wmv|flv)来测量函数的循环计数。我想了解我得到的结果。

[.]\w{3,4}$

执行上述(包括函数调用)需要21个周期。当我添加一条DWT_CYCCNT指令时:

08000610 <my_function>:
 8000610:       f04f 20ff       mov.w   r0, #4278255360 ; 0xff00ff00
 8000614:       f04f 11ff       mov.w   r1, #16711935   ; 0xff00ff
 8000618:       ea81 0100       eor.w   r1, r1, r0
 800061c:       ea81 0100       eor.w   r1, r1, r0
 8000620:       ea81 0100       eor.w   r1, r1, r0
 8000624:       ea81 0100       eor.w   r1, r1, r0
 8000628:       4770            bx      lr
 800062a:       bf00            nop

这突然变成了28个周期。

添加另一个eor不会改变周期数(仍为28)。再添加一个会使循环计数器按预期增加1(所以29)。

为什么?

  • 根据ARM08000610 <my_function>: 8000610: f04f 20ff mov.w r0, #4278255360 ; 0xff00ff00 8000614: f04f 11ff mov.w r1, #16711935 ; 0xff00ff 8000618: ea81 0100 eor.w r1, r1, r0 800061c: ea81 0100 eor.w r1, r1, r0 8000620: ea81 0100 eor.w r1, r1, r0 8000624: ea81 0100 eor.w r1, r1, r0 8000628: ea81 0100 eor.w r1, r1, r0 800062c: 4770 bx lr 800062e: bf00 nop 应始终为1个周期。
  • 我不知道3阶段管道如何解释这种行为。
  • 说明都是字对齐的,所以没有问题。
  • 我怀疑它与闪存访问有关,虽然我尝试将此代码放在IWRAM中并从那里执行,但这并没有改变任何内容。
  • 看看我的二进制文件的objdump,我可以确认我的测量没有任何问题。
  • 最后,我试图强制使用16位Thumb编码,但这并没有帮助我理解正在发生的事情。

有什么想法吗? :)

(此问题与#18960524有些相似,但没有eor和加载指令可能会搞砸。)

1 个答案:

答案 0 :(得分:4)

核心没有缓存 * ,但系统肯定有 - 即ST&#39;&# 34; ART Accelerator&#34;。

the TRM第3.5.2节所述,这个东西位于总线路径中,从闪存中进行全宽(128位)提取,然后将这些指令提供给内核的ICode接口,因为它要求他们。

第3.5.1节记录了闪存等待状态的数量与时钟速度和电压配置的关系,对于STM32F407,这意味着7个周期的最坏情况。我可以从问题的本质中猜测你可能还没有启用加速器的预取或指令缓存功能,这意味着每个16字节的指令都是你的。当从闪存中拖入下一个块时,将暂停 n 周期等待那些等待状态。

数学比我现在想要解决的问题更加尴尬,但足以说21个周期是至少7个执行周期的重叠组合,2个管道补充(每个1-3个循环用于调用和返回)以及至少2 * n 等待状态从闪存中获取至少2个块。

现在,需要注意的一点是,第一个函数是28个字节长,而第二个函数是32个 - 即恰好是两个16字节的块。第二个注意事项:M4的ICode接口只执行32位读取,然后从中读取管道的提取阶段(我假设当管道仅消耗前半个字时,它只是将其拇指旋转一个周期)。我非常有信心你在第二个例子中看到的是两者之间令人不快的互动 - 有一些有根据的猜测我想象这种情况:

  • 当管道的提取阶段正在引入0x800062c处的指令时,ICode接口正在为0x8000630处的下一个字启动总线请求。
  • 当管道解码bx lr并从相同的指令字中取出0x800062e时,ICode接口会暂时停止,但加速器正在等待闪存以传递读取0x8000630-0x8000640。
  • 分支执行,ICode接口现在必须坐下来等待 n -1个循环,加速器才能完成读取,现在它将被丢弃,之前可以请求保持的lr地址(然后等待另一个 n 周期来实际获取它)。

看起来像FLASH_ACR应该更准确地了解你的配置是什么,如果你真的想尝试计算每个周期 - 除非你将整个事情计时到ARM的零等待状态配置#39;核心时间假定(note the first paragraph),您将不得不考虑的不仅仅是核心。更一般地说,我建议在不彻底研究供应商的文档的情况下对微控制器进行编程&#34;就在那里&#34;只需走进Mordor&#34; ;)

* Cortex-M7是ARM实际拥有自己的内部缓存的M级核心中的第一个。