私の顕微鏡には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
具体的に何が起こっている方法は、コンパイラは今ラインを最適化していることですコードのdbg_TxBufProduceCount
は、ch
がdbg_TxBuf
に書き込まれる前に(メモリ内で)インクリメントされています。次に、シリアルポートISRは「時々」(実際には非常に頻繁に)、ch
と出力ポイントが書き込まれる前にdbg_TxBufConsumeCount != dbg_TxBufProduceCount
が表示され、dbg_TxBuf[(dbg_TxBufConsumeCount++) & (sizeof(dbg_TxBuf) - 1)]
が読み込まれます。したがって、シリアルポートで出力が壊れてしまいます。
ここでは、その行の8051解体だ:それは前の増分にdbg_TxBufProduceCount
の値を使っていることに
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_TxBuf[]
とdbg_TxBufProduceCount
の両方をvolatile
と宣言しています。コンパイラは、dbg_TxBufProduceCount
のインクリメントされた値をメモリに書き込んではならないのですが、の後には、ch
がメモリに書き込まれますか?
私はKeil 8051 Cコンパイラ、v7.10を使用しています。 V7.10がリリースされたとき、私は知りませんが、それは表示される2005年5月に終わったコンパイラへの支持
[mcve]はどこですか?コンパイラは抽象マシンを厳密に実装する可能性が高いです。 'volatile'修飾子が何をすることができるのかを知りたいかもしれません。 – Olaf
あまりにも手を振る。 Cコードを見るとアセンブリよりもはるかに役立ちます。 –
@Olaf - 私は間違いなく「最小限」の部分を持っています。私がISRを含むすべてを含んでいれば、完全な部分を提供するのはちょっと時間がかかるでしょう。そのために、誰かがもっと詳細を望む場合に備えてアセンブリを含めました。本当に問題の要点は、私が「具体的に何が起こっているのか」に完全に含まれています..."パラグラフと"ローカルの観点から... "パラグラフ最後に残りの部分はあまり重要ではない詳細です。 – phonetagger