前言
接手一个项目,调试全靠串口日志,测试同事测试产品的时候无法拿到日志,刚好产品RAM够大,且刚好有SD卡。所以就诞生了将日志缓存在RAM上,在特定条件下将它写到SD卡上的想法。
开工。
写完代码之后发现机器偶尔会无法启动,无法开机,无任何日志。
有的时候是烧录完成代码后重启无法启动,有些情况下是烧录完成代码后运行正常,放置一段时间后无法启动。
有时候有些无法启动的机器放置一段时间又成功启动。
一段一段代码,屏蔽,编译,验证,发现一个非常无法理解的事,TFCardLogBufferPut
函数注释后设备可以正常启动。
看代码:
1 2 3 4 5 6 7 8 9 10 11
| #define TF_CARD_LOG_BUFFER_SIZE (64*1024ul) uint8_t tfCardLogBuffer[TF_CARD_LOG_BUFFER_SIZE] = {0}; uint32_t tfCardLogBufferIndex = 0;
void TFCardLogBufferPut(uint8_t data) { tfCardLogBuffer[tfCardLogBufferIndex] = data; tfCardLogBufferIndex++; if( tfCardLogBufferIndex>=(sizeof(tfCardLogBuffer)/sizeof(tfCardLogBuffer[0]))-1 ) { tfCardLogBufferIndex = 0; } }
|
我实在无法理解,这段代码是如何导致设备无法启动的,后面搁置了一段时间,遂归结于小众芯片不完善导致。
但是后面这个需求实在是过于旺盛,遂重新开始验证代码。
当时代码是这样的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| int main(int argc, char **argv) { hal_uartInit(); ax32xx_uart0SendByte('a'); ax32xx_uart0SendByte('b'); hal_uartSendData('1'); hal_uartSendData('2'); ax32xx_uart0SendByte('c'); hal_sysInit(); }
void hal_uartSendData(u8 data) { ax32xx_uart0SendByte(data); TFCardLogBufferPut(data); }
void ax32xx_uart0SendByte(u8 data) { R_UART_DATA0 = data; while((R_UART_PEND0 & 0x2)==0); R_UART_PEND0 |= 1; }
#define TF_CARD_LOG_BUFFER_SIZE (64*1024ul) uint8_t tfCardLogBuffer[TF_CARD_LOG_BUFFER_SIZE] = {0}; uint32_t tfCardLogBufferIndex = 0;
void TFCardLogBufferPut(uint8_t data) { tfCardLogBuffer[tfCardLogBufferIndex] = data; tfCardLogBufferIndex = (tfCardLogBufferIndex>=(sizeof(tfCardLogBuffer)/sizeof(tfCardLogBuffer[0]))-1) ? 0 : tfCardLogBufferIndex+1; }
|
运行后日志是这样的:
1 2 3 4 5
| # 正常运行 的日志 ab12c ...
# 无法启动 的日志 ab1
|
后面我就猜想了很多:
- 这个soc不能支持这么大的数值 64KB
- 这个位置的内存被改写了
- 和冷启动热启动有关
- 芯片异常
后面我猜想,试试把TFCardLogBufferPut
函数里面的tfCardLogBufferIndex
的数值和data
数值输出出来看看。
这一看不得了,越界了!
代码:
输出日志:
这个tfCardLogBufferIndex
越界了啊!而且初始化赋0并没有成功!这就非常坑爹了!
后面尝试在调用这个函数之前再次赋0,发现程序运行正常,设备也正常启动。
查看map文件这个数组和变量也是存放在BSS段的,这就非常令人费解了。
看到这里我直接就怀疑,就是这个芯片的锅。
但是别急这还不是让人最震惊的!后面还有让人更加鼻血飙升的。
最后找FAE排查问题很久,最后发现BSS段在hal_sysInit
函数内初始化。
看代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| void ax32xx_sysInit(u32 *saddr,u32 *eaddr) { debg("sys init\n"); ax32xx_wdtClear();
ax32xx_sysIcacheInit(); ax32xx_sysDcacheInit();
ax32xx_sysBSSClear(); }
|
实在是震惊,无法理解这个RAM的初始化为什么放在main函数里面,实在无法理解进入main函数了,c语言环境都没有OK。
最烦的就是这种xx芯片,总是会有一些让人血压飙升的操作。
参考ARM MDK在编译的时候BSS初始化的操作是放在__main
函数里面,需要注意的是这个__main
不是c语言里面的main
函数,它是从MDK自带的库文件里链接的一个函数。这个__main
函数会初始化这些段,并调用__rt_entry
,最终在__rt_entry
中将会调用c语言里面的main
函数。
教训总结
在对数组进行访问的时候一定要先检查范围。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| void TFCardLogBufferPut(uint8_t data) { tfCardLogBuffer[tfCardLogBufferIndex] = data; tfCardLogBufferIndex = (tfCardLogBufferIndex>=(sizeof(tfCardLogBuffer)/sizeof(tfCardLogBuffer[0]))-1) ? 0 : tfCardLogBufferIndex+1; }
void TFCardLogBufferPut(uint8_t data) { if( tfCardLogBufferIndex>=(sizeof(tfCardLogBuffer)/sizeof(tfCardLogBuffer[0]))-1 ) { tfCardLogBufferIndex = 0; } tfCardLogBuffer[tfCardLogBufferIndex] = data; tfCardLogBufferIndex++; }
|