我的近乎微观的基于8051的嵌入式系统有一个调试串口,我编写了一个简单的串口输出功能,它运行正常,直到我做了一些小的调整,以减少其内存占用。然后是以下行,WAS工作正常(并且不是更改的一部分)......
dbg_TxBuf[(dbg_TxBufProduceCount++) & (sizeof(dbg_TxBuf) - 1)] = ch;
......停止了正常工作。变量dbg_TxBuf
和dbg_TxBufProduceCount
是全局变量,仅由输出函数和串行端口ISR使用(根本没有更改):
#define DBG_TX_BUFFER_SIZE 16 // MUST be a power-of-2 (this line is actually in a separate file, not that it matters)
volatile uint8_t xdata dbg_TxBuf[DBG_TX_BUFFER_SIZE]; // must be sized by a power-of-2, and MUST BE LESS THAN 256 since the 'count' vars are uint8
volatile uint8_t xdata dbg_TxBufProduceCount; // akin to a 'head' index but more useful since it allows use of every byte in the buf
volatile uint8_t xdata dbg_TxBufConsumeCount; // akin to a 'tail' index but more useful since it allows use of every byte in the buf
具体而言,正在发生的事情是编译器现在优化代码行的方式是dbg_TxBufProduceCount
在ch
写入dbg_TxBuf
之前递增(在内存中) }。然后,串行端口ISR“偶尔”(实际上经常)看到dbg_TxBufConsumeCount != dbg_TxBufProduceCount
并在输出函数向其dbg_TxBuf[(dbg_TxBufConsumeCount++) & (sizeof(dbg_TxBuf) - 1)]
写入ch
之前读取935> dbg_TxBuf[(dbg_TxBufProduceCount++) & (sizeof(dbg_TxBuf) - 1)] = ch;
DC84: 9006D6 MOV DPTR,#dbg_TxBufProduceCount
DC87: E0 MOVX A,@DPTR <--- loads the value in dbg_TxBufProduceCount into the A register
DC88: FE MOV R6,A <--- saves a copy of it in R6
DC89: 04 INC A <--- increments it
DC8A: F0 MOVX @DPTR,A <--- writes the incremented value
DC8B: EE MOV A,R6 <--- gets the original copy of ProduceCount back in A
DC8C: 7C00 MOV R4,#00 Begin computing address of dbg_TxBuf[~]...
DC8E: 540F ANL A,#0F <--- A = A & (sizeof(dbg_TxBuf) - 1)
DC90: 24D8 ADD A,#0D8 <--- A is now the low byte of &dbg_TxBuf[~]
DC92: F582 MOV DPL,A <--- put that in DPL
DC94: EC MOV A,R4 <--- (an inefficient way of loading the...
DC95: 3406 ADDC A,#06 <--- ...immediate value 0x06 into A)
DC97: F583 MOV DPH,A <--- DPTR now points to dbg_TxBuf[~]
DC99: EF MOV A,R7 <--- load 'ch' into A
DC9A: F0 MOVX @DPTR,A <--- write 'ch' to *DPTR
。因此,我的串口输出损坏。
这是该行的8051反汇编:
dbg_TxBufProduceCount
从“本地”角度来看,编译器正确地处理后增量运算符,因为它在将{1}}中的索引计算增量之前使用dbg_TxBuf
的值,但是从“全球”的角度来看,在我看来,它并没有正确处理事件序列,特别是因为我将dbg_TxBuf[]
和dbg_TxBufProduceCount
都声明为volatile
。编译器是否应该在 之后将dbg_TxBufProduceCount
的增量值写入内存 ,并将ch
写入内存?
BTW我正在使用Keil 8051 C编译器,v7.10。我不知道v7.10何时发布,但似乎我们对编译器的支持于2005年5月结束。
答案 0 :(得分:6)
编译器在将ch写入内存后,是否应该将dbg_TxBufProduceCount的递增值写入内存?
没有。它没有理由应该。没有任何事情对单个声明中发生的操作施加任何排序。这是一个写入两个volatile变量的语句 - 它可以按任意顺序写入它们。从概念上讲,它与以下内容没有什么不同:
i = a++ + b++;
即使a
和b
是易失性的,编译器也可以自由地生成代码以便以任何顺序写入它们。单个语句的部分评估顺序以及该指令的副作用可见的顺序是未指定的。