本文目的
本文将记录如何在沁恒的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);
}