今天在測試硬件通信模塊時候發現一個奇怪的問題,發送數據和接收數據進行比較復制時候頻繁數據錯誤。
測試流程如下:發送一個字節和接收一個字節,進行比較,當返回數據和發送數據不相等的時候,錯誤計數器累加。
數據收發抽象如下:
uint16 i = 0;
uint16 j = 0;
uint32 error_num = 0;
XX_send_data(i++);
j = XX_rece_data();
if(i != (j + 1)) {
error_num++;
}
這段代碼按照理論上來說,只要發送數據i和接收數據一樣,那錯誤計數器應該不會出錯,但是實際上出錯了,最開始懷疑是由於FPGA硬件模塊數據發送和接收時序有問題,但是多次大批量功能仿真以及時序仿真都沒問題。
這下我就開始懷疑這段短小的C程序是否工作正常,因此,
在error_num++這句話這里打斷點,查看每次出錯數據都是i=0,j=65535的時候出錯,為什么會全部出錯在這里呢?
百思不得其解,后來詢問資深工程師飛哥,飛哥一看就看出問題出現在哪里了,讓我汗顏呀!!~~
原來錯誤是這樣發生的:(程序運行在32位處理器)
這段程序從表面上看是沒有任何問題的,但是細致分析就會發現,當i=65535發送數據,發送完成后執行i++,i此時變為0,j調用接收函數后,返回值正確也是65535,但是,那看來問題就出現在if(i != (j+1))這里了。
原來, j+1 這種算術運算的運算值是默認存到32寄存器中,因此j+1運算結果為32位數據,因此,當j=65535時候,(j+1)實則為65536,(0 != 65536),因此產生不匹配。錯誤碼自加。
有很多種方法消除這種錯誤,修改代碼如下即可解決問題。
uint16 i = 0;
uint16 j = 0;
uint32 error_num = 0;
XX_send_data(i++);
j = XX_rece_data();
if(i !=(
uint16
) (j + 1)) {
error_num++;
}
終於找到問題了,看來以后寫代碼還得仔細了,吸取教訓。
// 看了回復,發現很多人都以純軟件的方式來理解這段代碼,但是嵌入式C語言里面一定要詳細和處理器行為結合起來,以下添加詳細解釋
這張截圖為這段代碼對應的匯編代碼,詳細閱讀這段匯編代碼即可找出問題所在。

第一句匯編代碼ldhu r3,-10(fp) 意思為從內存或者cache中加載一個半字,並且擴展成無符號類型,這句話就是把變量i加載到寄存器R3中,R3為32位寄存器,這時候雖然變量i為uint16但是比較的時候還是變成了32位
第二句匯編代碼ldhu r2,-12(fp) 同上一句話一樣,這里是加載變量j到寄存器R2中
第三句匯編代碼addi r2,r2,1 /*
問題所在*/ 這句話就是執行的j+1,可見結果是保存在r2中,r2為32位寬的寄存器,所以當i=0,j=65535時,j+1 = 65536並未溢出,而是直接賦值給r2,此時r2即為65536,r3為0
最后一句匯編代碼 beq r3,r2,0x800258<main+92>這句話是比較r3和r2的值,如果相等就跳轉到0x800258<main+92>這個地址執行程序。如果不相等就直接運行下一句語句,也就是error_num++
從上面的分析看出,嵌入式行業一定要對處理器非常熟悉,出現些稀奇古怪的問題,才能最終找到問題所在。