同步ISR访问是否需要易失性?

时间:2017-10-01 10:25:39

标签: c++ c embedded stm32

在下面的代码片段中,中断例程使用许多数组中的一个来执行。使用的数组是同步选择的,不是异步(在ISR执行时它永远不会改变)。在单核微控制器上(如果架构很重要,这个问题假设为STM32L496),volatile声明中是否需要foo说明符?

int a[] = {1, 2, 3};
int b[] = {4, 5, 6};
int * foo; //int * volatile foo? int volatile * volatile foo?

main(){
    disable_interrupt();
    foo = a;
    enable_interrupt();
    ...
    disable_interrupt();
    foo = b;
    enable_interrupt();
}

void interrupt(){
    //Use foo
}

我的假设是volatile说明符是必需的,因为任何foo值的缓存都是正确的。

编辑:

为了澄清,最后的答案是volatile或其他一些同步是必需的,因为否则可以省略或重新排序对foo的写入。缓存不是唯一的问题。

1 个答案:

答案 0 :(得分:1)

volatile停止编译器优化它,强制编译器

  • 要始终读取内存,而不是来自寄存器的缓存值
  • 在易失读/写之前或之后不要移动东西

在复杂的CPU(例如x86)上,CPU可以在易失性访问之前或之后重新排序操作。

它通常用于memory-mapped-io,其中内存区域实际上是设备,并且可以更改(即使在单核CPU上),没有明显原因。

C++11的机制是使用std :: atomic来更改可能在不同执行线程上发生的值。

使用单核,代码将安全地修改该值并存储它。如果使用volatile,则在启用中断之前将其写入内存点。

如果你不使用volatile,那么代码在中断使用之前可能仍然在寄存器中有新值。

int * volatile foo;

描述foo可以改变,但它指向的值是稳定的。

int volatile * volatile foo

描述foo可以改变,它指向的东西也可以改变。我想你想要int * volatile foo;

更新

对于怀疑volatile是编译器障碍的人。

来自标准n4296

  

访问由volatile glvalue(3.10)指定的对象,修改对象,调用库I / O.   函数,或调用执行任何这些操作的函数都是副作用,这些是副作用   执行环境的状态。表达式(或子表达式)的评估通常包括   两个值计算(包括确定glvalue评估和提取的对象的身份   先前分配给对象进行prvalue评估的值)和副作用的启动。打电话的时候   到库I / O函数返回或评估对volatile对象的访问,考虑副作用   完成,即使调用隐含的某些外部操作(例如I / O本身)或volatile   访问权限可能尚未完成。

来自cppreference cv object

  

volatile对象 - 类型为volatile限定的对象,或volatile对象的子对象,或const-volatile对象的可变子对象。通过volatile限定类型的glvalue表达式进行的每次访问(读取或写入操作,成员函数调用等)都被视为可见的副作用,用于优化(即,在单个执行线程内,volatile访问无法优化或重新排序,具有在易失性访问之前排序或排序的另一个可见副作用。这使得易失性对象适合与信号处理程序通信,但不适用于另一个执行线程,请参阅std :: memory_order )。任何通过非易失性glvalue引用易失性对象的尝试(例如通过引用或指向非易失性类型的指针)都会导致未定义的行为。

这些似乎同意,存在编译器障碍,但与volatile对象交互的一些副作用可能尚未完成。对于单核处理器,如果C ++ 11原子不可用,它似乎是一种合适的机制。

来自:C++ standard : n4296

我们有: -

  

与全表达相关的每个值计算和副作用在每个值之前排序   计算和副作用与下一个要评估的完整表达相关联。

据我所知,任何具有副作用的操作都存在happens-before关系。

  

严格根据抽象机的规则评估对volatile对象的访问

据我所知,有规则(可能不透明)。

  

访问由volatile glvalue(3.10)指定的对象,修改对象,调用库I / O.   函数,或调用执行任何这些操作的函数都是副作用,这些是副作用   执行环境的状态。表达式(或子表达式)的评估通常包括   两个值计算(包括确定glvalue评估和提取的对象的身份   先前分配给对象进行prvalue评估的值)和副作用的启动。打电话的时候   到库I / O函数返回或评估对volatile对象的访问,考虑副作用   完成,即使调用隐含的某些外部操作(例如I / O本身)或volatile   访问权限可能尚未完成。

从中我了解到访问volatile(以及其他一些东西)会产生副作用,从而阻止编译器在易失性访问附近重新排序语句。