From 9f5102e9e8ca99eb0ee0662f52190ff08d386db2 Mon Sep 17 00:00:00 2001 From: wangfq Date: Thu, 13 Aug 2026 15:44:07 +0800 Subject: [PATCH] =?UTF-8?q?feat(vd960DBN):=20fault=5Fdiag=20=E2=80=94=20.n?= =?UTF-8?q?oinit=20RAM=20=E7=8E=B0=E5=9C=BA=20+=20=E6=89=A7=E8=A1=8C?= =?UTF-8?q?=E8=BD=A8=E8=BF=B9=20marker=20=E5=AE=9A=E4=BD=8D=E7=A5=9E?= =?UTF-8?q?=E7=A7=98=E5=A4=8D=E4=BD=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 现象: LUP 帧后复位, RST=0x00000000 (无标志) 且 HardFault 打印缺失 (上次 0x10000000=SFT 确认 HardFault 路径, 这次连打印都没有) 方案: .noinit 段复位不清零, 跨复位留痕 - Link.ld: 新增 .noinit 段 (startup 只清 .bss 不碰) - fault_diag.h: FaultDiag{magic,mcause,mepc,mtval,marker,boot_cnt} + FAULT_MARKER 轨迹宏 - HardFault_Handler: 纯 RAM 写 mcause/mepc/mtval (官方 __get_MCAUSE/MEPC/MTVAL, 不依赖 UART/printf, 栈坏也能留痕) + 尽力打印 - main 开头 fault_diag_init(): 打印上次现场 + 递增 boot_cnt - 轨迹 marker: 主循环 LOOP_TOP/UART_SRV/SIM_SPI/POLL_BLE + LUP 链 LUP_FRAME/INGEST/EVT_FEED 入口出口, 复位后看 marker 停在哪一步 --- .../APP/ch32v20x_it.c | 48 +++++++++++++--- .../APP/include/fault_diag.h | 56 +++++++++++++++++++ .../APP/iot_mqtt_srv.c | 6 ++ .../APP/loop_uart_proto.c | 6 +- .../APP/peripheral_main.c | 8 ++- .../BLE/OnlyUpdateApp_Peripheral/Ld/Link.ld | 12 +++- 6 files changed, 123 insertions(+), 13 deletions(-) create mode 100644 vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/include/fault_diag.h diff --git a/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/ch32v20x_it.c b/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/ch32v20x_it.c index 2ec8bc9..bbf4c0c 100644 --- a/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/ch32v20x_it.c +++ b/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/ch32v20x_it.c @@ -17,6 +17,7 @@ #include "eth_driver.h" #include "CONFIG.h" #include "debug.h" +#include "fault_diag.h" /********************************************************************* * LOCAL FUNCTIONS @@ -39,6 +40,35 @@ void NMI_Handler(void) { } +/* 故障诊断现场: .noinit 段 (复位不清零) */ +FaultDiag g_fault_diag __attribute__((section(".noinit"))); + +/********************************************************************* + * @fn fault_diag_init + * + * @brief main 开头调用: 打印上次复位现场 + 重置 + 递增 boot_cnt + * + * @return none + */ +void fault_diag_init(void) +{ + if (g_fault_diag.magic == 0xFA57FA57UL) { + PRINT("FAULT_DIAG: boot_cnt=%lu mcause=0x%08lx mepc=0x%08lx mtval=0x%08lx marker=0x%08lx\n", + (unsigned long)g_fault_diag.boot_cnt, + (unsigned long)g_fault_diag.mcause, + (unsigned long)g_fault_diag.mepc, + (unsigned long)g_fault_diag.mtval, + (unsigned long)g_fault_diag.marker); + } + /* 重置现场, boot_cnt 保留递增 */ + g_fault_diag.magic = 0; + g_fault_diag.mcause = 0; + g_fault_diag.mepc = 0; + g_fault_diag.mtval = 0; + g_fault_diag.marker = 0; + g_fault_diag.boot_cnt++; +} + /********************************************************************* * @fn HardFault_Handler * @@ -48,16 +78,16 @@ void NMI_Handler(void) */ void HardFault_Handler(void) { - /* 诊断 (2026-08-13): 抓 RISC-V 异常现场, 定位 HardFault 源头 - mcause: 5=Load访问错误 7=Store访问错误 2=非法指令 4/6=未对齐 - mepc : 异常指令 PC (用 .map 文件反查函数) - mtval : 错误访问地址 (Load/Store fault 时=野指针地址) */ - uint32_t _mcause = 0, _mepc = 0, _mtval = 0; - __asm__ volatile("csrr %0, 0x342" : "=r"(_mcause)); - __asm__ volatile("csrr %0, 0x341" : "=r"(_mepc)); - __asm__ volatile("csrr %0, 0x343" : "=r"(_mtval)); + /* 纯 RAM 写现场 (不依赖 UART/printf): 栈坏/中断禁用也能留痕 */ + g_fault_diag.magic = 0xFA57FA57UL; + g_fault_diag.mcause = __get_MCAUSE(); + g_fault_diag.mepc = __get_MEPC(); + g_fault_diag.mtval = __get_MTVAL(); + /* 尽力打印 (UART 可用时), 失败不影响 RAM 现场 */ PRINT("HARDFAULT: mcause=0x%08lx mepc=0x%08lx mtval=0x%08lx\n", - (unsigned long)_mcause, (unsigned long)_mepc, (unsigned long)_mtval); + (unsigned long)g_fault_diag.mcause, + (unsigned long)g_fault_diag.mepc, + (unsigned long)g_fault_diag.mtval); NVIC_SystemReset(); while(1) { diff --git a/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/include/fault_diag.h b/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/include/fault_diag.h new file mode 100644 index 0000000..145dfe9 --- /dev/null +++ b/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/include/fault_diag.h @@ -0,0 +1,56 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : fault_diag.h + * Author : wfq + * Version : V1.0 + * Date : 2026-08-13 + * Description : 故障诊断 — .noinit RAM 现场 + 执行轨迹 marker + * + * 背景: HardFault 时 UART/printf 可能不可用 (栈坏/中断禁用), 打印丢现场; + * RST=0x00000000 的复位无任何标志, 无法从 RSTSCKR 判断复位源。 + * 方案: 全局记录放 .noinit 段 (复位不清零, 链接脚本已加): + * - HardFault_Handler 纯 RAM 写 mcause/mepc/mtval (不依赖 UART) + * - 关键路径 FAULT_MARKER() 记录执行轨迹 + * - main 开头 fault_diag_init() 打印上次现场 + 递增 boot_cnt + * 用法: FAULT_MARKER(MK_XXX) 插在嫌疑路径各阶段; 复位后看 marker 停在哪。 + *******************************************************************************/ + +#ifndef __FAULT_DIAG_H +#define __FAULT_DIAG_H + +#include + +typedef struct { + uint32_t magic; /* 0xFA57FA57 = 现场有效 */ + uint32_t mcause; /* RISC-V 异常原因 (5/7=野指针 2=非法指令 4/6=未对齐) */ + uint32_t mepc; /* 异常指令 PC (对照 .map 反查函数) */ + uint32_t mtval; /* 错误访问地址 */ + uint32_t marker; /* 最后执行位置标记 */ + uint32_t boot_cnt; /* 复位计数 */ +} FaultDiag; + +/* 定义在 ch32v20x_it.c (.noinit 段) */ +extern FaultDiag g_fault_diag; + +/* main 开头调用: 打印上次现场 + 重置 + boot_cnt++ */ +void fault_diag_init(void); + +/* 执行轨迹标记 */ +#define FAULT_MARKER(s) (g_fault_diag.marker = (s)) + +#define MK_MAIN_ENTRY 0xA0000001UL /* main 入口 */ +#define MK_INIT_DONE 0xA0000002UL /* 初始化完成, 进主循环前 */ +#define MK_LOOP_TOP 0xA0000010UL /* 主循环 while 开头 */ +#define MK_UART_SRV_IN 0xA0000011UL /* uart_srv 调用前 */ +#define MK_UART_SRV_OUT 0xA0000012UL /* uart_srv 返回后 */ +#define MK_SIM_SPI_IN 0xA0000013UL /* sim_snap_spi 调用前 */ +#define MK_POLL_BLE_IN 0xA0000014UL /* poll_dbn_ble 调用前 */ +#define MK_LUP_FRAME_IN 0xA0000020UL /* lup_process_frame 入口 */ +#define MK_LUP_FRAME_OUT 0xA0000021UL /* lup_process_frame 出口 */ +#define MK_INGEST_IN 0xA0000030UL /* iot_sensor_ingest 入口 */ +#define MK_INGEST_OUT 0xA0000031UL /* iot_sensor_ingest 出口 */ +#define MK_EVT_FEED_IN 0xA0000032UL /* iot_evt_feed 入口 */ +#define MK_EVT_FEED_OUT 0xA0000033UL /* iot_evt_feed 出口 */ +#define MK_EVT_CB_IN 0xA0000034UL /* iot_evt_sensor_cb 入口 */ +#define MK_EVT_CB_OUT 0xA0000035UL /* iot_evt_sensor_cb 出口 */ + +#endif /* __FAULT_DIAG_H */ diff --git a/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/iot_mqtt_srv.c b/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/iot_mqtt_srv.c index 3836733..1ca60d5 100644 --- a/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/iot_mqtt_srv.c +++ b/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/iot_mqtt_srv.c @@ -25,6 +25,7 @@ #include "storage.h" #include "offlog.h" #include "snapshot.h" +#include "fault_diag.h" #include "ch32v20x_iwdg.h" #include #include @@ -121,12 +122,14 @@ static void iot_evt_enqueue(uint8_t type, uint8_t ch, uint32_t value) { /* 沿检测: 每收到一帧有效 0xC0 调用一次 (两条消费路径都要喂) */ static void iot_evt_feed(const LUP_SensorReport *sr) { uint8_t i; + FAULT_MARKER(MK_EVT_FEED_IN); if (!_evt_prev_valid) { // 首帧: 只建快照 (上电已有车不算进入沿) for (i = 0; i < LUP_COIL_COUNT; i++) { _evt_prev_car[i] = (i < sr->coil_count) ? sr->coils[i].car_state : 0; _evt_prev_loop[i] = (i < sr->coil_count) ? sr->coils[i].loop_state : 0; } _evt_prev_valid = 1; + FAULT_MARKER(MK_EVT_FEED_OUT); return; } for (i = 0; i < sr->coil_count && i < LUP_COIL_COUNT; i++) { @@ -154,6 +157,7 @@ static void iot_evt_feed(const LUP_SensorReport *sr) { _evt_prev_car[i] = cs->car_state; _evt_prev_loop[i] = cs->loop_state; } + FAULT_MARKER(MK_EVT_FEED_OUT); } /* 统一帧摄取: 事件沿检测 + 刷新 loop_data 缓存 (单一数据源) @@ -162,10 +166,12 @@ static void iot_evt_feed(const LUP_SensorReport *sr) { 帧竞争的概率实测 <2% → loop_data 发布 34s 前的陈旧快照(车走后 iscar 仍 true), 且陈旧 diff 锁死 fast_mode 300ms 刷屏。事件与快照必须同源! */ static void iot_sensor_ingest(const LUP_SensorReport *sr) { + FAULT_MARKER(MK_INGEST_IN); iot_evt_feed(sr); // 事件沿检测 memcpy(&_cached_sr, sr, sizeof(LUP_SensorReport)); // 刷新 loop_data 快照 _cached_sr_valid = 1; snap_enqueue(sr); /* 快照入队: 中断安全, 主循环 snap_flush 落盘 */ + FAULT_MARKER(MK_INGEST_OUT); } /* lup 传感回调: uart_srv 消费路径的帧从这里喂入 (lup_process_frame 已过校验) */ diff --git a/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/loop_uart_proto.c b/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/loop_uart_proto.c index f83c91c..0f0941b 100644 --- a/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/loop_uart_proto.c +++ b/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/loop_uart_proto.c @@ -14,6 +14,7 @@ #include "CONFIG.h" #include "loop_uart_proto.h" #include "cmcng.h" +#include "fault_diag.h" #include /*=========================================================================== @@ -681,7 +682,9 @@ void lup_process_frame(const uint8_t *pkg, uint16_t len) int csr; uint8_t i; - if (len < 6) return; + FAULT_MARKER(MK_LUP_FRAME_IN); + + if (len < 6) { FAULT_MARKER(MK_LUP_FRAME_OUT); return; } // Debug: print raw PRINT("LUP Rx:"); @@ -712,4 +715,5 @@ void lup_process_frame(const uint8_t *pkg, uint16_t len) // Try matching with pending command lup_cmd_on_response(pkg, len); + FAULT_MARKER(MK_LUP_FRAME_OUT); } diff --git a/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/peripheral_main.c b/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/peripheral_main.c index a6c8cdb..2d78f9e 100644 --- a/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/peripheral_main.c +++ b/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/peripheral_main.c @@ -27,6 +27,7 @@ #include "tcp_json_srv.h" #include "loop_uart_proto.h" #include "iot_mqtt_srv.h" +#include "fault_diag.h" /********************************************************************* * GLOBAL TYPEDEFS @@ -300,6 +301,7 @@ void Main_Circulation(void) uint32_t _counter = 0; while(1) { + FAULT_MARKER(MK_LOOP_TOP); TMOS_SystemProcess(); if(g_net_state.flag < 2) @@ -336,10 +338,13 @@ void Main_Circulation(void) WCHNET_HandleGlobalInt(); } + FAULT_MARKER(MK_UART_SRV_IN); uart_srv(); + FAULT_MARKER(MK_UART_SRV_OUT); snap_flush(); /* 快照落盘: 无条件挂主循环 (原在 iot_enable 分支, TCP 模式不落盘) */ + FAULT_MARKER(MK_SIM_SPI_IN); sim_snap_spi(); /* 实验第一步(2026-08-13): 模拟快照区 SPI 写密度, 定位复位触发源 */ - + FAULT_MARKER(MK_POLL_BLE_IN); poll_dbn_ble(); if (g_sub_code_enable.iot_enable) { @@ -374,6 +379,7 @@ int main(void) /* 诊断: 复位原因寄存器 (bit31=IWDG bit30=WWDG bit29=LPWR bit26=NRST bit25=POR bit24=软复位) 每次上电必打, 区分看门狗死锁 vs 掉电 vs 外部复位 */ PRINT("RST_REASON: 0x%08lx\n", (unsigned long)OFFLOG_RCC_RSTSCKR); + fault_diag_init(); /* 打印上次复位现场 (HardFault mcause/mepc + 执行轨迹 marker) */ PRINT("%s\n", VER_LIB); PRINT("SystemCoreClock:%d\n", SystemCoreClock); /* ===== 实验: 禁用全部 Flash/SPI 操作 (2026-08-12) ===== diff --git a/vd960DBN/BLE/OnlyUpdateApp_Peripheral/Ld/Link.ld b/vd960DBN/BLE/OnlyUpdateApp_Peripheral/Ld/Link.ld index d66e3c1..1e72274 100644 --- a/vd960DBN/BLE/OnlyUpdateApp_Peripheral/Ld/Link.ld +++ b/vd960DBN/BLE/OnlyUpdateApp_Peripheral/Ld/Link.ld @@ -171,15 +171,23 @@ SECTIONS { . = ALIGN(4); PROVIDE( _sbss = .); - *(.sbss*) + *(.sbss*) *(.gnu.linkonce.sb.*) *(.bss*) - *(.gnu.linkonce.b.*) + *(.gnu.linkonce.b.*) *(COMMON*) . = ALIGN(4); PROVIDE( _ebss = .); } >RAM AT>FLASH + /* 故障诊断现场: 复位不清零 (startup 只清 .bss, 不碰 .noinit) */ + .noinit (NOLOAD) : + { + . = ALIGN(4); + *(.noinit*) + . = ALIGN(4); + } >RAM + PROVIDE( _end = _ebss); PROVIDE( end = . );