1

I'd like to be able to use something like this to make access to my ports clearer:

typedef struct {
    unsigned rfid_en: 1;
    unsigned lcd_en: 1;
    unsigned lcd_rs: 1;
    unsigned lcd_color: 3;
    unsigned unused: 2;
} portc_t;

extern volatile portc_t *portc;

But is it safe? It works for me, but...

1) Is there a chance of race conditions?

2) Does gcc generate read-modify-write cycles for code that modifies a single field?

3) Is there a safe way to update multiple fields?

4) Is the bit packing and order guaranteed? (I don't care about portability in this case, so gcc-specific options to make it Do What I Mean are fine.)

4

2 回答 2

1
  1. 处理竞争条件必须通过操作系统级别的调用(确实会使用 read-modify-writes)来完成,GCC 不会这样做。

  2. 同上,没有 GCC 不会为volatile. 但是,CPU 通常会以原子方式进行写入(仅仅是因为它是一条指令)。如果位域保持在一个int例子中,这也是正确的,但这取决于 CPU/实现;我的意思是有些人可以保证最多 8 字节的值,而其他人只能保证最多 4 字节的值。因此,在这种情况下,不能混淆位(即一些从一个线程写入,而其他从另一个线程不会发生)。

  3. 同时设置多个字段的唯一方法是将这些值设置在一个中间变量中,然后将该变量分配给volatile.

  4. C 标准规定位是打包在一起的(当你开始混合类型时似乎可能会有例外,但我从未见过;每个人都总是使用unsigned ...)。

注意:定义某些东西volatile不会导致编译器生成读取-修改-写入。什么volatile是告诉编译器必须始终对该指针/地址进行分配,并且可能不会被优化掉。

这是关于同一主题的另一篇文章。我发现还有很多其他地方可以找到更多详细信息。

于 2013-07-05T19:16:57.547 回答
-1

关键字 volatile 与竞争条件或正在访问代码的线程无关。该关键字告诉编译器不要将值缓存在寄存器中。它告诉编译器生成代码,以便每次访问都转到分配给变量的位置,因为每次访问可能会看到不同的值。内存映射外设就是这种情况。如果您的 MPU 有自己的缓存,这将无济于事。内存映射通常有特殊指令或未缓存区域,以确保读取位置,而不是缓存副本。至于线程安全,请记住,即使是内存访问也可能不是线程安全的,因为它是在两条指令中完成的。例如,在 8051 汇编器中,您必须一次获得一个字节的 16 位值。

于 2017-01-12T16:09:55.260 回答