【问题标题】:Is is necessary to use volatile when writing to hardware in C or C++?用 C 或 C++ 写入硬件时是否需要使用 volatile?
【发布时间】:2021-01-30 02:41:30
【问题描述】:

在 C 或 C++ 中写入硬件(比如 FIFO)时是否需要使用 volatile。从在线文档中很容易确认volatile在读取硬件时是必要的,但是写入呢?我担心优化器可能会消除将值数组写入 FIFO 的循环,而只写入最后一个条目。

【问题讨论】:

  • 当您说“写入硬件”时,您的意思是像 MMIO 之类的东西? IE。总是在同一个地址的东西?这就是您担心连续写入可能会优化到最后一个的原因吗?
  • 是的,这是必要的。否则编译器可能会优化所有内容,甚至不会保留最后一次写入。
  • @MarcoBonelli:我确实是指写入 FPGA 中的单个地址,这是一个信箱。我将连续值写入该地址,FPGA 对其进行解释和操作。许多其他微处理器连接的 I/O 芯片以相同的方式工作,并且许多外围设备集成到现代处理器和 DSP 中
  • @12431234123412341234123 我的意思是……不一定。 MMIO 和 PMIO 是一回事。
  • @12431234123412341234123:你能引用你的声明的来源吗?我检查了 K&R、Modern C、Practical C、C in a Nutshell、Misra 2004 以及其他可能没有成功的内容。

标签: c++ c hardware volatile


【解决方案1】:

试试吧。

#define MYFIFOV (*((volatile unsigned char *)0x1000000))
#define MYFIFO (*((unsigned char *)0x1000000))

void funv ( void )
{
    MYFIFOV=0;
    MYFIFOV=0;
}
void fun ( void )
{
    MYFIFO=0;
    MYFIFO=0;
}
00000000 <funv>:
   0:   e3a03401    mov r3, #16777216   ; 0x1000000
   4:   e3a02000    mov r2, #0
   8:   e5c32000    strb    r2, [r3]
   c:   e5c32000    strb    r2, [r3]
  10:   e12fff1e    bx  lr

00000014 <fun>:
  14:   e3a03401    mov r3, #16777216   ; 0x1000000
  18:   e3a02000    mov r2, #0
  1c:   e5c32000    strb    r2, [r3]
  20:   e12fff1e    bx  lr

strb 表示存储字节。如果没有 volatile,其中一个写入已被优化。所以是的,没有 volatile,可以优化写入。编译器如何以及何时决定这样做可能会有所不同。但假设它可能发生并因此给您带来问题。

【讨论】:

    【解决方案2】:

    写入硬件时是否需要使用volatile

    通常是的。

    我担心优化器可能会消除将值数组写入 FIFO 的循环,而只写入最后一个条目。

    您的担忧是有效的。给定非易失性对象,优化器确实可以执行这种消除。事实上,如果它能够证明写入的值永远不会被读取,那么它可能会完全消除所有写入。


    这里引用 C++ 标准(最新草案):

    [intro.abstract]

    本文档中的语义描述定义了一个参数化的非确定性抽象机器。 本文档对一致性实现的结构没有任何要求。 特别是,它们不需要复制或模仿抽象机器的结构。 相反,需要符合要求的实现来模拟(仅)抽象机的可观察行为,如下所述。

    执行格式良好的程序的一致实现应产生与具有相同程序和相同输入的抽象机的相应实例的可能执行之一相同的可观察行为。 ...

    对一致性实现的最低要求是:

    1. 通过 volatile glvalues 进行的访问严格按照抽象机的规则进行评估。
    2. 在程序终止时,写入文件的所有数据都应与根据抽象语义执行程序可能产生的结果之一相同。
    3. 交互式设备的输入和输出动态应该以这样一种方式发生,即在程序等待输入之前实际交付提示输出。

    什么构成交互式设备是由实现定义的。

    这些统称为程序的可观察行为。

    【讨论】:

      【解决方案3】:

      是的,你必须使用volatile

      来自 C11 标准,5.1.2.3 程序执行 - 第 4 段:

      在抽象机器中,所有 表达式按照语义的指定进行评估。一个实际的 如果可以,实现不需要评估表达式的一部分 推断它的值没有被使用并且没有需要的副作用 生产....

      当您不使用volatile 时,编译器可能会认为没有有用的副作用并删除写入。

      【讨论】:

        【解决方案4】:

        根据 C 标准(C99 §6.7.3 脚注 106,第 109 页 here):

        volatile 声明可用于描述对应于内存映射输入/输出端口的对象或由异步中断函数访问的对象。 除非评估表达式的规则允许,否则对如此声明的对象的操作不应被实现“优化”或重新排序。

        如果您使用的是兼容的 C 编译器,那么您认为在写入内存映射硬件时需要使用 volatile 是正确的。


        根据您正在使用的特定机器和编译器,volatile 的使用最多可能是多​​余的:

        实现可能会定义抽象语义和实际语义之间的一一对应关系:在每个序列点,实际对象的值将与抽象语义指定的值一致。关键字volatile 将是多余的。


        按照 C++,according to the latest draft:

        注 5:volatile 是对实现的提示,以避免涉及对象的激进优化,因为对象的值可能会通过实现无法检测的方式进行更改。此外,对于某些实现,易失性可能表示需要特殊的硬件指令才能访问该对象。有关详细语义,请参见 [intro.execution]。 一般来说,volatile 的语义在 C++ 中与在 C 中的语义相同。 ——尾注

        【讨论】:

        • 请注意,注释不是规范性的,在技术上与它们相矛盾并不会使语言实现不符合标准。
        猜你喜欢
        • 1970-01-01
        • 2011-12-19
        • 2017-04-26
        • 1970-01-01
        • 2014-12-07
        • 2023-04-02
        • 2013-06-16
        • 2015-09-28
        • 1970-01-01
        相关资源
        最近更新 更多