作者:王静芸平桂 | 来源:互联网 | 2022-11-30 10:42
我尽力尝试调试EFR32BG12处理器上的硬故障。我一直在这里遵循Silicon Labs知识库中的说明:
https://www.silabs.com/community/mcu/32-bit/knowledge-base.entry.html/2014/05/26/debug_a_hardfault-78gc
我也一直在这里使用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)。将该位置1时,为异常返回而堆叠的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 */
}
这种分析听起来正确吗?
我想我遇到的问题是我不知道会导致这种行为的原因...我尝试在所有中断处理程序中放置断点,以查看其中是否有一个中断了堆栈,但似乎没有任何模式-有时没有调用中断处理程序,但问题仍然存在。
但是,在那种情况下,我很难看到程序如何尝试在超出代码实际末尾的位置执行代码...我觉得函数指针可能是候选对象,但是在那种情况下我希望看到问题出现,例如在使用函数指针的地方。但是,在发生错误的位置附近,我看不到任何函数指针。
也许我可以从上面提供的调试信息中提取更多信息?这个问题是可以重现的,所以如果我没有尝试过,但是您认为可以提供一些见识的东西,我很想听听。
谢谢你尽你所能的帮助!
1> Michael Coop..:
经过大约一个月的追踪,我设法找出了问题的原因。我希望我可以在这里提供足够的信息,这对其他人很有用。
最后,问题是由于将指向非静态局部变量的指针传递给状态机而导致的,该状态机随后更改了该内存位置的值。因为局部变量不再在作用域内,所以该内存位置是堆栈中的随机点,并且更改那里的值会破坏堆栈。
很难找到问题的原因有两个:
根据代码的编译方式,更改后的内存位置可能不是很重要,例如另一个局部变量,这将导致更微妙的错误。只有当我很幸运时,更改才会影响PC寄存器并导致硬故障。
即使我找到了始终产生硬故障的代码版本,实际的硬故障也通常发生在调用堆栈的某个位置,当函数返回并将堆栈值弹出到PC中时。这使得很难确定问题的原因-我所知道的是在该函数返回之前,某些东西正在破坏堆栈。
一些工具确实有助于确定问题的原因:
早期,我已经确定了一个代码块,其中通常使用GPIO引脚发生硬故障。我会在进入模块之前将引脚切换为高电平,而在退出模块时切换为低电平。然后,我进行了许多测试,检查在发生硬故障时引脚是高还是低,并使用某种二进制搜索来确定始终包含所有硬故障的最小代码块。
硬故障将许多重要的寄存器压入堆栈。这些帮助我确认PC寄存器在哪里损坏,也帮助我了解由于堆栈损坏而导致PC寄存器损坏。
从该代码块之前的某个地方开始,并在关注局部变量的同时继续前进,我能够识别出破坏堆栈的函数调用。我可以使用Simplicity Studio的内存视图来确认这一点。
最后,详细地逐步浏览了令人讨厌的功能,我意识到当我取消引用存储的指针并写入该内存位置时,就会出现问题。回顾指针值的设置位置,我意识到它已被设置为指向一个超出范围的非静态局部变量。
感谢@SeanHoulihane和@cooperised,他们帮助我消除了一些可能的原因,并使我对调试工具有了更多的信心。