我在尝试排查EFR32BG12处理器上的一个硬错误,但是已经束手无策了。我一直在按照Silicon Labs知识库中的指示进行操作:
我还参考了Keil的应用笔记,以填补一些细节方面的内容:
http://www.keil.com/appnotes/files/apnt209.pdf
我已成功地使硬错误在一个特定位置频繁发生。当硬错误发生时,处理器将以下值(由处理器在调用硬错误处理程序之前推送到堆栈中的代码)给我:
Name Type Value Location
~~~~ ~~~~ ~~~~~ ~~~~~~~~
cfsr uint32_t 0x20000 (Hex) 0x2000078c
hfsr uint32_t 0x40000000 (Hex) 0x20000788
mmfar uint32_t 0xe000ed34 (Hex) 0x20000784
bfar uint32_t 0xe000ed38 (Hex) 0x20000780
r0 uint32_t 0x0 (Hex) 0x2000077c
r1 uint32_t 0x8 (Hex) 0x20000778
r2 uint32_t 0x0 (Hex) 0x20000774
r3 uint32_t 0x0 (Hex) 0x20000770
r12 uint32_t 0x1 (Hex) 0x2000076c
lr uint32_t 0xab61 (Hex) 0x20000768
pc uint32_t 0x38dc8 (Hex) 0x20000764
psr uint32_t 0x0 (Hex) 0x20000760
根据Keil的应用笔记,我认为CFSR值为0x20000表示使用错误,并设置了INVSTATE位,即:
INVSTATE: 无效状态:0=无无效状态,1=处理器尝试执行一个使用执行程序状态寄存器(EPSR)非法的指令。当此位被设置时,为异常返回推送的PC值指向尝试非法使用EPSR的指令。可能原因:a)将分支目标地址加载到LSB=0的PC上。b)在异常或中断处理期间堆栈化的PSR损坏。c)向量表包含一个LSB=0的向量地址。
异常推送到堆栈上的PC值(由知识库文章提供的代码提供)似乎是0x38dc8。 如果我在Simplicity Studio“反汇编”窗口中转到此地址,则会看到以下内容:
00038db8: str r5,[r5,#0x14]
00038dba: str r0,[r7,r1]
00038dbc: str r4,[r5,#0x14]
00038dbe: ldr r4,[pc,#0x1e4] ; 0x38fa0
00038dc0: strb r1,[r4,#0x11]
00038dc2: ldr r5,[r4,#0x64]
00038dc4: ldrb r3,[r4,#0x5]
00038dc6: movs r3,r6
00038dc8: strb r1,[r4,#0x15]
00038dca: ldr r4,[r4,#0x14]
00038dcc: cmp r7,#0x6f
00038dce: cmp r6,#0x30
00038dd0: str r7,[r6,#0x14]
00038dd2: lsls r6,r6,#1
00038dd4: movs r5,r0
00038dd6: movs r0,r0
这个地址似乎远远超出了我的代码范围。如果我在“内存”窗口中查看同一个地址,我能看到如下内容:
0x00038DC8 69647561 2E302F6F 00766177 00000005 audio/0.wav.....
0x00038DD8 00000000 000F4240 00000105 00000000 ....@B..........
0x00038DE8 00000000 00000000 00000005 00000000 ................
0x00038DF8 0001C200 00000500 00001000 00000000 .Â..............
0x00038E08 00000000 F00000F0 02F00001 0003F000 ....ð..ð..ð..ð..
0x00038E18 F00004F0 06010005 01020101 01011201 ð..ð............
0x00038E28 35010121 01010D01 6C363025 2E6E6775 !..5....%06lugn.
0x00038E38 00746164 00000001 000008D0 00038400 dat.....Ð.......
有趣的是,“audio/0.wav”是固件中的一个静态字符串。如果我理解正确,我在这里学到的是PC不知何故被设置为内存中的这个位置,这显然不是有效指令,导致了硬错误。
为了调试这个问题,我需要知道PC如何被设置为这个错误的值。我相信LR寄存器应该能给我一个想法。异常推送到堆栈上的LR寄存器似乎是0xab61。如果我在这个位置查看,我在反汇编窗口中看到以下内容:
1270 dp->sect = clst2sect(fs, clst);
0000ab58: ldr r0,[r7,#0x10]
0000ab5a: ldr r1,[r7,#0x14]
0000ab5c: bl 0x00009904
0000ab60: mov r2,r0
0000ab62: ldr r3,[r7,#0x4]
0000ab64: str r2,[r3,#0x18]
在我看来,问题似乎特别出现在这个调用过程中:
0000ab5c: bl 0x00009904
这让我想到问题是由于一个损坏的堆栈导致的,它使得clst2sect返回到内存中的无效部分而不是0xab60。clst2sect的代码非常无害:
/*-----------------------------------------------------------------------*/
/* Get physical sector number from cluster number */
/*-----------------------------------------------------------------------*/
DWORD clst2sect ( /* !=0:Sector number, 0:Failed (invalid cluster#) */
FATFS* fs, /* Filesystem object */
DWORD clst /* Cluster# to be converted */
)
{
clst -= 2; /* Cluster number is origin from 2 */
if (clst >= fs->n_fatent - 2) return 0; /* Is it invalid cluster number? */
return fs->database + fs->csize * clst; /* Start sector number of the cluster */
}
这个分析听起来大概是正确的吗?
我遇到的问题是我不知道可能会引起这种行为的原因…… 我已经在所有中断处理程序中设置了断点,以查看它们中的一个是否可能破坏堆栈,但似乎没有任何模式 - 有时候,没有调用任何中断处理程序,但问题仍然会发生。
即便如此,我很难看出程序如何尝试执行远超过实际代码结尾位置的代码... 我觉得函数指针可能是一个可能的候选项,但如果是那样,我期望看到问题出现在使用函数指针的地方。 但是,在错误发生的附近,我没有看到任何使用函数指针的地方。
也许有更多的信息可以从上面给出的调试信息中提取?问题是非常可重现的,因此,如果有我尚未尝试过但您认为可能会提供一些见解的内容,我很想听听您的建议。
感谢您提供的任何帮助!
r13
。 - cooperised