【问题标题】:compiler execution sequence编译器执行顺序
【发布时间】:2018-05-04 14:37:46
【问题描述】:

我正在用 C 语言编写一个微控制器,我遇到了以下情况:要启用/禁用内部时钟发生器,我需要先将保护密钥写入特定寄存器,然后进行启用/禁用操作,写在另一个寄存器中。 用户手册对此操作指定以下内容: snip from user manual

正如您在文本突出显示的部分中看到的那样,进行这两个操作(写保护键和启用振荡器)的代码非常敏感,因为任何操作都应该在这两个寄存器访问之间执行。因此,我开始关注可能导致这两个操作的非顺序执行的任何情况。 我知道在执行敏感代码时临时禁用中断是很常见的,但我想知道是否有任何编译器优化可以在这两个寄存器访问之间插入另一个操作。那么,在C编程中,有没有什么编译器指令可以保证不会发生呢?

实际上,我什至不知道认为编译器会混合用 C 语言编写的指令序列是否有意义。我记得听说过它有时会发生,在速度优化过程中。如果我错了,抱歉并提前感谢您的澄清。

【问题讨论】:

  • 你可能需要volatile关键字-
  • 确实,对volatile 的访问无法重新排序。但是编译器仍然可以随意订购非易失性访问,其中 AFAIR 包括 between 两个易失性访问。因此,我选择将此序列编码为汇编程序 sn-p 并将其内联到 C 代码中(例如__asm__ volatile(" ... ")
  • 如果这是在您的目标平台上常见的事情,那么运行时库很可能已经有可用的功能,因此您不需要自己编写程序集。
  • 该寄存器的指针已经是易失性的。但是,我知道我应该将操作寄存器的函数声明为 volatile 或使用带有 volatile 关键字的汇编命令,正如@FelixPalmen 指出的那样。对吗?
  • @LuisPossatti 谈到内联汇编时,标准中根本没有任何内容。在程序集块上使用gccvolatile 会强制编译器包含程序集片段,即使它对 C 程序的可观察行为没有任何影响——否则,优化器可能会决定此代码不是需要:)

标签: c compilation microcontroller


【解决方案1】:

在 C 中切换位的过程涉及读-修改-写指令序列。 在这些指令之间可能会发生一些不需要的操作,通常的方法是禁用中断或使用位带。

什么是位带,它允许您使用一条指令切换位(无需详细说明它是如何发生的)。

如果您的微控制器允许您使用位带,那么这应该可以解决您的所有问题。

如果您不能使用位带,那么您必须坐下来思考哪些中断可能会给您带来问题并暂时禁用它们。

回到问题的核心 - 您必须声明所有寄存器 volatile 有两个主要原因:

  1. 不像普通内存只能由程序自己修改,那些内存映射寄存器中存储的值可以随时更改,编译器所做的只是优化所有对寄存器的读取和写入。 Volatile 可以防止这种情况发生。通过声明变量 volatile,您实际上是在要求编译器在读取或写入该变量时尽可能低效。具体来说,编译器应该生成代码来执行对 volatile 变量的每次读取以及对 volatile 变量的每次写入,即使您连续写入两次或读取它并忽略结果也是如此。不能跳过任何一次读取或写入。换句话说,不允许对 volatile 变量进行编译器优化。
  2. 简而言之,目标代码中 volatile 变量 A 和 B 的访问顺序必须与源代码中这些访问的顺序相同。不允许编译器出于任何原因对 volatile 变量访问重新排序。 (考虑如果引用的内存位置是这种情况下的硬件寄存器,可能会出现什么问题)。 Volatile 向您保证一切都按照您在代码中指定的顺序发生,并且不会进行重新排序。

总结一下 - 使用位带并将寄存器声明为易失性。

【讨论】:

  • 感谢您指出位带的东西,我会检查一下。但是,我问的问题不是意外中断,特别是编译后的指令执行顺序。
  • 编辑了更具体的答案
  • 现在好多了。感谢您的指正。不会投票,因为还没有足够的声誉:(
猜你喜欢
  • 1970-01-01
  • 2014-01-09
  • 1970-01-01
  • 1970-01-01
  • 2023-03-13
  • 2019-12-31
  • 2010-09-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多