我使用基于8051微控制器的嵌入式系统,该系统有一个调试串口。我编写了一个简单的串口输出函数,一直运行良好,直到我对其进行了一些小修改以减少内存占用。然后以下代码行不再起作用(并且不是修改的一部分)...
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
具体来说,编译器现在优化代码行的方式是,在将ch写入dbg_TxBuf之前,先在内存中递增dbg_TxBufProduceCount。然后串口ISR“偶尔”(实际上相当频繁)发现dbg_TxBufConsumeCount!=dbg_TxBufProduceCount,并在输出函数将ch写入之前读取dbg_TxBuf [(dbg_TxBufConsumeCount++)& (sizeof(dbg_TxBuf)-1)]。因此,我在串口上获得了损坏的输出。
这是该行的8051反汇编:
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
从“本地”角度来看,编译器正确地处理了后缀递增运算符,即在计算
dbg_TxBuf
中的索引时使用了dbg_TxBufProduceCount
的值,而在递增之前。但是从“全局”角度来看,我认为它没有正确处理事件序列,特别是因为我将dbg_TxBuf []
和dbg_TxBufProduceCount
都声明为volatile
。编译器不应该在写入ch
到内存之后才将递增后的dbg_TxBufProduceCount
值写入内存吗?顺便说一下,我正在使用Keil 8051 C编译器v7.10。我不知道v7.10是什么时候发布的,但似乎我们对编译器的支持在2005年5月就结束了。