本文目的
本文將記錄如何在沁恆的risc-v芯片上進行hardfalt問題追蹤,以及找出程序卡死的地方
適用芯片
- CH573/CH571
- CH583/CH582/CH581
- CH565/569
- CH32V series
說明
hardfault 問題追蹤
人為制造hardfault
(下面使用CH582為例)
這里我們寫了個能夠的產生hardfault中斷的代碼,調用下面函數即可
//對非4字節對齊的地址取uint32_t
void hardfault_test(void) {
printf("hardfault test\r\n");
DelayMs(100);
for(uint32_t i=0;i<16;i++) {
printf("test:%08X\r\n",*(uint32_t *)(0x500+i));
}
}
修改hardfault中斷函數,加入一些打印信息
在官網默認例程的下面路徑文件
EXAM/SRC/StdPeriphDriver/CH58x_sys.c
里面有個hardfault的handler函數,默認情況下是復位,當開發過程中發現代碼容易異常復位時候,
我們可以考慮是不是產生了hard fault中斷,我們可以修改該代碼如下:
__attribute__((section(".highcode"))) //put in ram
__attribute__((interrupt("WCH-Interrupt-fast"))) //disable soft push/pull
void HardFault_Handler(void) {
uint32_t v_mepc,v_mcause,v_mtval;
printf("hardfault\n");
v_mepc=__get_MEPC();
v_mcause=__get_MCAUSE();
v_mtval=__get_MTVAL();
printf("mepc:%08x\n",v_mepc);
printf("mcause:%08x\n",v_mcause);
printf("mtval:%08x\n",v_mtval);
while(1);
}
獲取打印結果
hardfault
mepc:0000090c
mcause:00000004
mtval:00000501
上面我們加了 mepc mcause 和mtval 三個參數的打印,
這個實際上是分別對應 產生hardfault前的指令運行的地址,產生該異常的原因,和產生該異常時候存取的地址
mepc
這個我們可以在編譯輸出的lst文件打開查看即可:
ch58x_hardfaut_catch_test.lst
很明顯就在我們人為造的函數里面,我們可以找到0x90c地址對應的行:
000008dc <hardfault_test>:
8dc: 1141 addi sp,sp,-16
8de: 00001517 auipc a0,0x1
8e2: 56e50513 addi a0,a0,1390 # 1e4c <_sbrk+0x54>
8e6: c606 sw ra,12(sp)
8e8: c422 sw s0,8(sp)
8ea: c226 sw s1,4(sp)
8ec: c04a sw s2,0(sp)
8ee: 2ecd jal ce0 <puts>
8f0: 06400513 li a0,100
8f4: 20000097 auipc ra,0x20000
8f8: 9d6080e7 jalr -1578(ra) # 200002ca <mDelaymS>
8fc: 50000413 li s0,1280
900: 00001917 auipc s2,0x1
904: 55c90913 addi s2,s2,1372 # 1e5c <_sbrk+0x64>
908: 51000493 li s1,1296
90c: 400c lw a1,0(s0)
90e: 854a mv a0,s2
910: 0405 addi s0,s0,1
912: 2c45 jal bc2 <iprintf>
914: fe941ce3 bne s0,s1,90c <hardfault_test+0x30>
918: 40b2 lw ra,12(sp)
91a: 4422 lw s0,8(sp)
91c: 4492 lw s1,4(sp)
91e: 4902 lw s2,0(sp)
920: 0141 addi sp,sp,16
922: 8082 ret
mcause
這里我們不是正常的中斷,而是hardfault,所以折了的mcause對應的是Load address misaligned
Interrupt | Exception Code | Description |
---|---|---|
1 | 0 | User software interrupt |
1 | 1 | Supervisor software interrupt |
1 | 2 | Hypervisor software interrupt |
1 | 3 | Machine software interrupt |
1 | 4 | User timer interrupt |
1 | 5 | Supervisor timer interrupt |
1 | 6 | Hypervisor timer interrupt |
1 | 7 | Machine timer interrupt |
1 | 8 | User external interrupt |
1 | 9 | Supervisor external interrupt |
1 | 10 | Hypervisor external interrupt |
1 | 11 | Machine external interrupt |
1 | ≥12 | Reserved |
0 | 0 | Instruction address misaligned |
0 | 1 | Instruction access fault |
0 | 2 | Illegal instruction |
0 | 3 | Breakpoint |
0 | 4 | Load address misaligned |
0 | 5 | Load access fault |
0 | 6 | Store/AMO address misaligned |
0 | 7 | Store/AMO access fault |
0 | 8 | Environment call from U-mode |
0 | 9 | Environment call from S-mode |
0 | 10 | Environment call from H-mode |
0 | 11 | Environment call from M-mode |
0 | ≥12 | Reserved |
mtval
mtval:00000501
這里我們正是對0x501地址取uint32_t 才產生的hardfault
代碼卡死的地方追蹤
待編輯
其他
串口發送放到RAM里面
有時候出現hardfault可能比較特殊,比如flash讀取異常,這時候我們可能需要把hardfault以及相關調用放到ram里面
下面我們在CH582上直接通過查表法,使用串口1直接輸出對應值的字符串,方便我們用串口調試工具觀看
uint8_t char_mapping[] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
__attribute__((section(".highcode")))
void uart1_send_uint32(uint32_t num){
for( uint32_t i=0; i<8; i++ ) {
while(R8_UART1_TFC == UART_FIFO_SIZE);
R8_UART1_THR = char_mapping[(num>>28)&0xf];
num <<=4;
}
}
__attribute__((section(".highcode")))
void uart1_send_newline(){
while(R8_UART1_TFC == UART_FIFO_SIZE);
R8_UART1_THR = '\n';
}
__attribute__((section(".highcode"))) //put in ram
__attribute__((interrupt("WCH-Interrupt-fast"))) //disable soft push/pull
void HardFault_Handler(void) {
SetSysClock(CLK_SOURCE_PLL_60MHz);
DelayMs(1);
uint32_t v_mepc,v_mcause,v_mtval;
//v_mepc=__get_MEPC();
//v_mcause=__get_MCAUSE();
//v_mtval=__get_MTVAL();
__asm volatile("csrr %0,"
"mepc"
: "=r"(v_mepc));
__asm volatile("csrr %0,"
"mcause"
: "=r"(v_mcause));
__asm volatile("csrr %0,"
"mtval"
: "=r"(v_mtval));
uart1_send_uint32(v_mepc);
uart1_send_newline();
uart1_send_uint32(v_mcause);
uart1_send_newline();
uart1_send_uint32(v_mtval);
uart1_send_newline();
while(1);
}