我的几乎微观的基于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
它之前读取.因此我的串口输出损坏了.
这是该行的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_TxBufProduceCount
了索引计算中增量之前的值dbg_TxBuf
,但从"全局"的角度来看,它似乎不是处理事件的顺序正确,尤其是因为我宣布双方dbg_TxBuf[]
和dbg_TxBufProduceCount
作为volatile
.在写入dbg_TxBufProduceCount
内存后,编译器是否应该将增量值写入ch
内存?
顺便说一下,我正在使用Keil 8051 C编译器,v7.10.我不知道v7.10何时发布,但似乎我们对编译器的支持于2005年5月结束.
在将ch写入内存后,编译器是否应该将dbg_TxBufProduceCount的递增值写入内存?
不,没有理由应该这样做.没有任何事情对单个声明中发生的操作施加任何排序.这是一个写入两个volatile变量的语句 - 它可以按任意顺序写入它们.从概念上讲,它与以下内容没有什么不同:
i = a++ + b++;
即使a
并且b
是易失性的,编译器也可以自由地生成代码以便以任何顺序写入它们.未指定单个语句的部分的评估顺序以及该指令的副作用可见的顺序.