HiSpark编程指南

- 调测资料
  - SDK Sample使用指南
    - 前言
    - 概述
    - Sample框架
    - 外设 Sample
    - SLE UART Sample
    - SLE UART DMA Sample
    - SLE Mouse Sample
    - SLE amic vdt with dongle
    - SLE microphone
    - SLE measure dis
    - BLE Lowpower Sample
    - BLE UART Sample
    - BLE Keyboard Sample
    - BLE SLE TAG Sample
    - BLE Mouse Sample
    - USB Keyboard Sample
    - USB Mouse Sample
    - USB AMIC VDT Sample
    - RCU
    - 多连接
    - air mouse
    - SLE MOUSE with Dongle
    - SLE OTA dongle
    - game mouse
    - BLE UUID
    - 缩略语
  - SDK二次开发网络安全注意事项
    - 前言
    - 产品安全解决方案
    - 结论
  - SDK开发环境搭建用户指南
    - 前言
    - 开发环境搭建
    - 编译SDK
    - 新建APP
  - 内存维测指导书
    - 前言
    - 简介
    - 内存维测功能
  - 射频AT测试指南
    - 前言
    - 射频测试AT指令
    - AT指令组包示例
    - 注意事项
  - 维测指导书
    - 前言
    - 概述
    - 日志说明
    - 日志分析
    - 问题定位
    - 典型案例
    - 常见问题FAQ
- 软件资料
  - AT命令使用指南
    - 前言
    - 指令说明
    - 通用AT指令
    - BT业务AT指令
    - 新增AT指令方法
  - DFU升级APK开发指南
    - 前言
    - 功能特性
    - 系统框架
    - 工程文件部署
    - 界面实现
    - 升级界面Fragment实现
    - 关于界面Fragment实现
    - 应用清单AndroidManifest
  - DFX软件开发指南
    - 前言
    - DIAG维测功能
    - 死机诊断
    - Dump解析
  - EFUSE使用指南
    - 前言
    - 概述
    - 使用软件接口读写EFUSE
    - 使用BurnTool烧写EFUSE
  - NFC TAG软件开发指导书
    - 前言
    - 概述
    - 开发指导
  - NV存储用户指南
    - 前言
    - NV简介
    - NV编译预置
    - NV API指南
    - 定制化NV结构使用介绍
    - 通过NV自定义传输数据最大长度
  - Release notes
    - 1.0.18版本描述文件
    - 1.0.17版本描述文件
    - 1.0.16版本描述文件
  - SDK开发指南
    - 前言
    - 概述
    - 系统接口
  - SDK接口变更说明书
    - 前言
    - BLE SDK接口变更说明
  - SP31 SLP使用指南
    - 开源版本
  - USB HID复合设备开发指南
    - 前言
    - 概述
    - 实现方式
  - 代码框架说明
    - 前言
    - SDK模块框架
    - SDK目录结构
    - SDK目录说明
  - 低功耗FAQ
    - 前言
    - 系统无法睡眠
    - IO无法唤醒系统
    - 唤醒后外设异常
    - 低功耗导致OTA升级异常
    - 功耗比预期高
    - 锁中断导致BT业务阻塞
    - 概率性狗超时
    - 供电模式&软件配置
    - 提前唤醒时间问题
  - 低功耗sample使用指南
    - 前言
    - 编译&测试
    - 系统状态管理
    - 用户指南
  - 低功耗开发指南
    - 前言
    - 低功耗概述
    - 低功耗方案
    - 开发指引
    - 功耗优化
    - 常见问题
  - 功率配置说明书
    - 前言
    - 射频功率配置说明
  - 升级方案使用指南
    - 前言
    - 概述
    - 升级包制作
    - 升级包传输
    - 升级包保存
    - 升级包本地升级
    - FAQ
  - 快速入门指南
    - 前言
    - 文档目录
    - 环境搭建
    - 基础功能开发
    - 调优
    - 特性开发
    - 测试报告
  - 星闪应用层SLE-Link协议
    - 前言
    - 概述
    - SLE Frame格式定义
    - 服务接口定义
    - 交互流程示例
  - 星闪软件开发指导书
    - 前言
    - 概述
    - Device Discovery接口
    - Connection Manager接口
    - SSAP Server接口
    - SSAP client接口
    - Low Latency接口
    - Transmission Manager接口
    - SLE Passkey接口
    - SLE Channel Map 接口
  - 红外学习使用指南
    - 前言
    - 红外学习方式
    - 通过比较器进行红外学习
    - 使用指南
  - 蓝牙软件开发指导书
    - 前言
    - 概述
    - GAP接口
    - GATT server接口
    - GATT client接口
  - 设备驱动开发指南
    - 前言
    - Pinctrl
    - GPIO
    - UART
    - SPI
    - I2C
    - ADC
    - DMA
    - PWM
    - WDT
    - Timer
    - Systick
    - TCXO
    - Flash
    - SFC
    - QDEC
    - KEYSCAN
    - PDM
    - USB_HID
    - USB_DFU
    - I2S
    - TRNG
    - 附：MIDDLEWARE
  - 音频编解码开发指导书
    - 前言
    - 概述
    - Audio Encoder
    - Audio Decoder
    - 调试与功能
- 工具资料
  - BurnTool工具使用指导书
    - 前言
    - BurnTool工具简介
    - 操作指南
    - FAQ
  - HiSpark Studio使用指南
    - 前言
    - 工具简介
    - 开发环境搭建
    - 工程管理
    - 工程配置
    - 编译运行
    - 软件包烧录
    - 栈分析和镜像分析
    - 工程调试
    - 串口控制台工具
    - Kconfig配置
    - GUI工程创建与使用
    - 高阶分析功能使用
    - 常见错误

### 该页内容

- 前言
- 概述
- 日志说明
  - 平台日志接口
    - 普通串口打印
    - DIAG串口打印
  - 平台日志分类
    - SDK启动日志
    - APP睡眠日志
    - APP panic日志
    - APP异常重启
    - 异常类型描述
- 日志分析
  - 死机信息获取
    - 普通打印口维测信息获取
    - 死机Dump信息获取
    - 连接J-Link调试器导出
  - 狗超时重启
    - ULP_WDT超时
  - hardfault、MEM fault、BUS fault、Usage fault异常
    - APP核异常
  - NMI异常
    - XIP异常
    - XIP CACHE异常
    - MDMA异常
    - SMDMA异常
    - A核NMI异常
  - panic异常
    - APP PANIC异常
    - PANIC ID
  - 强制狗复位
- 问题定位
  - 外设问题定位方法
  - 死机问题定位方法
  - 低功耗问题定位方法
    - 管脚漏电排查
  - OSAL接口维测方法
  - 内存维测功能
    - 查看系统heap总体信息
    - 查看task栈使用情况
    - 查看每个task的内存分配情况
    - 查看中断中、启动阶段的内存分配情况
    - 内存维测开关
  - OS及中断维测
    - 维测原理
    - 维侧日志解析
  - 锁中断时间与中断时间维测
    - 维测原理
    - 维测日志解析
  - 查询系统CPU占用率维测功能
    - 查看各个任务和中断的CPU占用情况
    - 维测开关
- 典型案例
  - 案例一 栈溢出
  - 案例二 通过MEPC分析
  - 案例三 通过RA分析
  - 案例四 访问死机
  - 案例五 内存Malloc失败
- 常见问题FAQ
  - 异常信息查看
    - 异常信息汇总
    - CPU寄存器信息
    - 通用寄存器信息
  - ADC常见问题排查

# 前言

概述

本文档主要用于BS2X平台日志分析指导，包括平台日志分类、重启场景、panic类型等。为开发人员、验证人员、使用人员提供参考，提高使用者问题定位效率。

读者对象

本文档主要适用以下工程师：

- 技术支持工程师

- 软件开发工程师

符号约定

在本文中可能出现下列标志，它们所代表的含义如下。

符号 | 说明
--- | ---
| 表示如不避免则将会导致死亡或严重伤害的具有高等级风险的危害。
| 表示如不避免则可能导致死亡或严重伤害的具有中等级风险的危害。
| 表示如不避免则可能导致轻微或中度伤害的具有低等级风险的危害。
| 用于传递设备或环境安全警示信息。如不避免则可能会导致设备损坏、数据丢失、设备性能降低或其它不可预知的结果。 “须知”不涉及人身伤害。
| 对正文中重点信息的补充说明。 “说明”不是安全警示信息，不涉及人身、设备及环境伤害信息。

修改记录

文档版本 | 发布日期 | 修改说明
--- | --- | ---
03 | 2025-06-20 | 更新“维侧日志解析”小节内容。
02 | 2025-05-30 | 删除“BT睡眠日志”小节内容。 删除“BT panic日志”小节内容。 更新“异常类型描述”小节内容。 删除“BT核NMI异常”小节内容。 删除“BT PANIC异常”小节内容。 更新“ADC常见问题排查”小节内容。
01 | 2024-07-05 | 第一次正式版本发布。

# 概述

文档针对PLT的各类业务，从日志类型、异常场景等方面给PLT维测指导。

# 日志说明

## 平台日志接口

BS2X SDK中提供了普通串口打印和HSO(协议日志)串口打印两种串口LOG打印方式，帮助用户添加、分析维测信息。

### 普通串口打印

普通打印方式通过串口工具直接解析，默认使用UART_BUS0【软件配置参数所选UART句柄名称】（UART_L0【硬件PIN脚名称】）打印串口日志以及发送AT命令，UART BUS具体对照如下表1所示。使用普通打印接口需要包含soc_osal.h头文件，调用osal_printk接口格式化输出LOG。

表 1 UART默认对应表

软件配置参数UART句柄名称 | 硬件PIN脚名称
--- | ---
UART_BUS0 | UART_L0
UART_BUS1 | UART_H0
UART_BUS2 | UART_L1

### DIAG串口打印

DIAG打印方式通过DebugKits工具解析日志，默认使用UART_BUS1【软件配置参数所选UART句柄名称】（UART_H0【硬件PIN脚名称】）打印DIAG日志。HSO维测功能模块提供以下功能：

- 日志打印。用户可以通过日志打印接口将调试信息打印到DebugKits的Message界面。

- 命令的注册和处理。用户可以通过注册命令和命令处理函数，来实现在DebugKits工具的命令行界面输入命令来控制单板侧的操作。

- 系统维测信息获取。DIAG支持获取系统统计类信息（如内存使用、任务信息），帮助用户定位问题。

以用户在chips/bs2x/main_init/app_os_init.c中增加调试日志为例，流程如下：

1. 在chips/bs2x/main_init/app_os_init.c中调用日志打印接口，输出调试信息。需要包含log_def.h、log_common.h头文件。

   函数 | 说明
   --- | ---
   oml_pf_log_print0(mid, lognum, lvl, fmt) | mid：本条打印归属的模块，调试使用LOG_PFMODULE模块。 lognum：打印的log编号，调试使用LOG_NUM_DEBUG。 lvl：打印等级，定义在middleware/utils/common_headers/log_types.h中。 fmt：打印字符串。
   oml_pf_log_print1(mid, lognum, lvl, fmt, p1) | 前四个参数含义同oml_pf_log_print0接口描述一致，p1表示打印字符串里携带的1个参数
   oml_pf_log_print2(mid, lognum, lvl, fmt, p1, p2) | 前四个参数含义同oml_pf_log_print0接口描述一致，p1，p2表示打印字符串里携带的2个参数
   oml_pf_log_print3(mid, lognum, lvl, fmt, p1, p2, p3) | 前四个参数含义同oml_pf_log_print0接口描述一致，p1，p2，p3表示打印字符串里携带的3个参数
   oml_pf_log_print4(mid, lognum, lvl, fmt, p1, p2, p3, p4) | 前四个参数含义同oml_pf_log_print0接口描述一致，p1，p2，p3，p4表示打印字符串里携带的4个参数

2. 用户在使用DIAG日志之前，需要先确认调用接口的文件是否被添加到模块当中。例如在chips/bs2x/main_init/app_os_init.c文件中使用LOG_PFMODULE模块打印接口，需要在chips/bs2x/dfx/include/log_def_pf.h添加文件ID，文件ID的格式为：调用日志接口的文件的文件名大写并加_C后缀，如：MAIN_C。

3. 编译程序，编译过程中将生成output/bs2x/database_evb目录。

4. 在DebugKits中执行update HDB，将“步骤3”生成的数据库更新至DebugKits的数据库中。打开DebugKits的Message界面查看日志信息（请参见《BS2XV100 DebugKits工具 使用指南》）。

> 说明： 请注意，DIAG日志只能支持长度32位及以下的参数，如："%d"、"%u"、"%x"、"%p"，无法支持长度大于32位的参数，如"%ld"、"%s"，无法支持浮点参数，如"%f"。 普通打印方式直接在接口调用的位置通过串口进行输出，如果日志过多会阻塞当前调用者线程，可能会出现非预期现象；而DIAG打印方式是通过事件方式抛消息给log线程进行打印，不会阻塞打印调用者，但是会导致codesize增大，内存足够的情况下建议使用，相对来说会比普通方式更灵活。请根据实际场景选择使用。

## 平台日志分类

### SDK启动日志

如图1所示，开机启动，Flashboot初始化后，串口会打印系统启动原因，系统启动次数及系统异常启动次数。APP启动后，会打印日志初始化、AT初始化以及SDK版本号。如图2所示，HSO同步打印系统上电表示及重启原因，重启次数以及异常重启次数。

图 1 平台启动日志（串口）

图 2 平台启动日志（HSO）

### APP睡眠日志

App默认每隔一段时间会通过HSO日志记录一次调度空闲时间，打印睡眠统计的同时会喂狗（若出现长时间没有打印，可能会狗超时，因此会喂狗），如图1所示。该维测依赖于PM_MCPU_MIPS_STATISTICS_ENABLE宏，默认关闭，根据实际情况选择，用户可不关心。

图 1 APP睡眠日志

### APP panic日志

Kernel panic是指操作系统在监测到内部的致命错误，并无法安全处理此错误时采取的动作。内核触发到某种异常情况，运行kernel_panic函数，并尽可能把异常发生时获取的全部信息打印出来。

- 原理

  导致异常的原因多种多样，通过异常打印的调用信息，找到调用kernel_panic的原因。常见的原因包括内核堆栈溢出、内核空间的除0异常、内存访问越界、内核陷入死锁等。

- 触发方法

  内核态读0地址。

APP核发生panic异常时会打印panic原因0x2003以及对应的panic id，如图APP panic日志所示。panic id见表1所示。

图 1 APP panic异常

### APP异常重启

APP核发生xip异常，hardfault，狗超时等异常时，会打印重启原因0x200x以及pc和lr值，如图1及表1所示。

图 1 APP异常重启

表 1 APP重启原因

异常类型 | 日志类型 | 重启原因
--- | --- | ---
主动重启 | Log_FLATFORM | 0x2001
panic | Log_FLATFORM | 0x2003
hardfault | Log_FLATFORM | 0x2004
nmi | Log_FLATFORM | 0x2005
狗超时 | Log_FLATFORM | 0x2008
xip异常 | Log_FLATFORM | 0x2009
xip cache异常 | Log_FLATFORM | 0x200a
mdma异常 | Log_FLATFORM | 0x200b
smdma异常 | Log_FLATFORM | 0x200c
强制狗复位 | Log_FLATFORM | 0x200d
lp狗/uvlo重启 | Log_FLATFORM | 0x200f

### 异常类型描述

表 1 重启描述分析（middleware/utils/dfx/dfx_reboot/reboot/shared/non_os_reboot.h）

id | 描述 | 重启异常场景原因分析
--- | --- | ---
0x0 | 未知重启原因 | 用户代码中直接写寄存器重启导致
0x2001 | 应用核主动重启 | -
0x2003 | APP panic异常 | 需要查看“PANIC ID”中panic id，进一步分析
0x2004 | Hardfault异常 | 非法地址； 空指针； XXX模块未初始化； XXX模块没有时钟； 需要结合临终遗言中mepc，ra，mtval及通用寄存器进一步定位。
0x2008 | APP狗超时 | 结合cpu trace及日志分析。 一般是一直在执行高优先级任务/中断流程，导致低优先级喂狗任务无法得到调度。
0x200D | APP硬狗超时 | 系统卡死后，无法响应狗超时中断，需要结合cpu trace分析
0x200f | ulp狗超时/uvlo重启 | 添加ulp喂狗日志进一步分析
0x2010 | Pin reset | -
0x2040 | 升级完成 | -
0x2042 | 升级失败 | -
0xf0f0 | 系统上电 | -

# 日志分析

常见的异常场景包括：watchdog超时，hardfault，mem fault，bus fault，use fault， panic异常，强制狗复位等。发生异常时，会打印异常重启原因，并引发系统重启。其中APP的重启异常用0x2xxx表示，具体有0x2000~0x2010、0x2040~0x2044、0x2080、0x2100。

## 死机信息获取

### 普通打印口维测信息获取

- 当硬件与串口工具处于连接状态时，如果单板发生死机，串口工具能够接收到复位前DFX流程打印的死机信息。用户可以通过分析死机信息，定位死机原因。普通打印口死机LOG分为4个部分：

- 死机现场：记录死机发生时RISCV通用寄存器以及部分CSR寄存器的现场值。各个寄存器的描述请参考附录中异常信息查看。

- 线程栈信息：记录死机发生时正在运行的线程栈的栈顶、栈大小以及栈使用峰值。

- 栈回溯：回溯死机栈信息，traceback 0为栈顶位置，sp addr，current sp为当前栈位置，sp content为栈内容。

- CPU Trace：记录CPU过去一段时间的执行的PC与LR。step time存储记录时间，值越大，记录的时间越接近死机现场。

  > 说明： 注意：此处是数采功能，记录的PC值和真实的执行轨迹可能有所差异，并不一样能完全对应！

  Trace路径的分析有两种方式：

  方式一：用户可以根据编译时生成的application.lst文件查找LR和PC地址对应的函数，定位死机执行现场。

  方式二：用户使用IDE提供的CFBB Tools解析Trace，打开工具，选择用户使用的target。

  导入CPU Trace Information到CPU Trace end的Trace信息，点击解析可以获取到过去一段时间的执行信息，定位死机原因：

### 死机Dump信息获取

DebugKits工具提供了死机发生时，将死机现场内存以二进制格式导出功能，提供参考信息，帮助开发者定位分析死机问题。工具的详细安装使用请参考《BS2XV100 DebugKits工具使用指南》，此处只介绍Dump信息导出流程。

首先进入工具页面点击options再选择change chip，在弹框中选择BS2X确定芯片类型，参考图选择芯片。

然后点击Connection选择Connect在弹窗中选择连接方式以及对应的通道序号进行连接，参考图连接芯片。

如果是第一次连接芯片或者芯片有新版本的程序，需要更新HDB并重新连接之后，日志的打印才能正确的显示，在Option菜单下选择Update HDB，选择生成的database目录（\output\bs2x\database_evb）再点击OK即可完成配置，具体参考下图：配置database。

通过DebugKits工具可以实时观测芯片运行时打印的具体信息，可以参考图 DebugKits日志打印。当死机发生时Message视图将会打印出last word以及last dump信息。

last word信息是死机发生时，将PC地址、返回地址、栈地址、CPU寄存器信息发送出来，用户可以根据last word的具体信息分析错误类型、定位错误原因，具体可以参考下图：last word信息。

last dump信息将会把内存中的部分信息打包生成bin文件存储到DebugKits的安装目录中的DumpInfo子目录之下，具体参考下图：last dump生成文件。

> 说明： 注意：将内存dump到DebugKits中需要一定时间（1～2 min），因此如果需要使用last dump分析死机信息，不要在发生死机故障后立即复位，否则last dump信息可能无法完整获取。

### 连接J-Link调试器导出

在允许连接JTAG调试器的场景下，用户可连接J-Link仿真器等工具进一步获取死机现场的相关信息。以下主要介绍通过J-Link仿真器获取死机相关信息的过程。

本小节主要介绍在死机现场使用J-Link调试器导出死机的过程。

使用J-Link仿真器调试需先连接芯片SWD管脚，具体位置以实际产品为准。硬件连接后，执行工具包（sdk\tools\bin\jlink_tool\bs2x）中的Commandline_bs2x_mcpu.bat脚本，脚本将自动进行连接（其他核的连接操作可参照此方法进行）。

J-Link连接MCPU示意图

连接J-Link后可以使用Jlink命令调试芯片，获取芯片运行信息，Jlink常用命令如下：J-Link常用命令

命令 | 描述
--- | ---
con/connect | 连接。
h/halt | 暂停，停止。
g/go | 继续，运行。
mem32 mem16 mem8 | I/O读32bit：mem32 <Addr>, <NumItems> (hex) (Addr表示内存地址，NumItems表示从Addr开始连续读几个32bit ) I/O读16bit：mem16 <Addr>, <NumItems> (hex) I/O读8bit：mem8 <Addr>, <NumItems> (hex)
w4 w2 w1 | I/O写4Byte：w4 <Addr>, <Data> (hex) I/O写2Byte：w2 <Addr>, <Data> (hex) I/O写1Byte：w1 <Addr>, <Data> (hex)
readcsr | 读riscv csr寄存器：ReadCSR <RegIndex>
writecsr | 写riscv csr寄存器：WriteCSR <RegIndex>,<Value>

查询信息步骤如下：

- 查询CPU寄存器信息，如图 查询CPU寄存器值所示。

- 查询内存信息，获取维测变量值或普通变量值。

在\output\bs2x\xxx_core\xxx_bs2x_xxx\xxx.nm中获取nm文件，在nm文件中获取想要查询的变量地址如图 变量地址中“g-exception-dump-callback”变量的地址为0x20025950。

通过J-Link获取状态信息，如图 变量值。

> 说明： RISC-V架构处理器需要使用V10及以上版本的J-Link仿真器。 芯片连接J-Link需要保证处于工作状态，睡眠状态时无法连接。

## 狗超时重启

狗超时重启分为：CWDT、ULP WDT 重启。其中APP发生狗超时（CWDT超时），会有HSO日志和UART日志打印，显示对应的pc和lr，并且会导出内存信息。

APP核发生狗超时时，系统会进行重启。系统重启后，串口会打印异常时的pc和lr，如图1所示。HSO日志也会打印重启原因0x2008及对应的pc和lr，如图2所示。可以在lst文件中查找pc值确定问题归属。APP核的pc指针范围如表3-1所示。

表 1 APP代码空间范围

内存名称 | 开始地址 | 结束地址
--- | --- | ---
ITCM | 0x42000 | 0x56000
DTCM | 0x20000000 | 0x20010000
SFC | 0x90100000 | 0x90180000

图 1 CWDT超时重启UART打印

图 2 CWDT超时重启HSO日志打印

### ULP_WDT超时

APP进入睡眠时，CWDT将掉电，WDT功能由ULP_WDT实现。发生狗超时时，系统会进行重启。系统重启后，串口和HSO日志也会打印重启原因0x200f，如图1所示。

图 1 WDT超时重启UART打印

## hardfault、MEM fault、BUS fault、Usage fault异常

系统发生上述异常时，HSO日志会记录异常时的pc、lr和mtval，如图1所示。

系统重启后，也会打印重启原因及对应的pc、lr和mtval，如图2所示。。

### APP核异常

APP核发生上述重启会打印重启原因0x2004。

图 1 APP hardfault重启HSO日志

图 2 APP hardfault重启UART打印

## NMI异常

系统发生如下NMI异常时，重启后会打印如下重启原因。HSO日志会记录pc、lr和mtval。

### XIP异常

系统发生XIP重启会打印重启原因0x2009。HSO日志会输出pc、lr和mtval。如图1和图2所示

图 1 XIP重启HSO日志

图 2 XIP重启UART打印

### XIP CACHE异常

系统发生XIP cache异常会打印重启原因0x200a。HSO日志会记录pc、lr和mtval。如图1和图2所示

图 1 XIP cache异常HSO日志

图 2 XIP cache异常UART打印

### MDMA异常

系统发生MDMA异常会打印重启原因0x200b。HSO日志会记录pc、lr和mtval。如图1和图2所示

图 1 MDMA异常HSO日志

图 2 MDMA异常UART打印

### SMDMA异常

系统发生SMDMA异常会打印重启原因0x200c。HSO日志会记录pc、lr和mtval。如图1和图2所示

图 1 SMDMA异常HSO日志

图 2 SMDMA异常UART打印

### A核NMI异常

A核发生NMI异常会打印重启原因0x2005。HSO日志会记录pc、lr和mtval。如图1和图2所示

图 1 A核NMI异常HSO日志

图 2 A核NMI异常UART打印

## panic异常

发生panic异常时，会打印panic id，code以及调用的lr。并获取对端的pc，lr。其中normal core表示对端。Normal core为0表示A核异常。Normal core为1表示bt核异常。

### APP PANIC异常

APP Panic异常会打印重启原因0x2003和panic日志。如图1和图2所示。

图 1 panic异常HSO日志

图 2 panic异常UART打印

### PANIC ID

表 1 PANIC ID描述（middleware/utils/dfx/panic/public/panic.h）

id（十进制值） | 描述 | 备注
--- | --- | ---
0 | no panic | NO USE
1 | arm core | NO USE
2 | HAL | NO USE
3 | DSP | NO USE
4 | malloc | BT/APP
5 | uart | BT/APP
6 | flash | APP
7 | adc | APP
8 | watchdog | NO USE
9 | log | BT/APP
10 | dma | BT/APP
11 | assert | BT/APP
12 | reboot | NO USE
13 | update | NO USE
14 | I2C0 | APP
15 | I2C1 | APP
16 | I2C2 | APP
17 | SPI0 | APP
18 | SPI1 | APP
19 | SPI2 | APP
20 | SPI4 | APP
21 | SPI5 | APP
22 | RPC | BT/APP
23 | mem monitor | BT/APP
24 | panic exit | NO USE
25 | create task | liteOS
26 | memcpy buffer overlap | NO USE
27 | xip | APP
28 | btc malloc fail | BTC
29 | low power | BT/APP
30 | system status | BT/APP
31 | Btc int err | BTC
32 | Lpc wakeup fail | APP
33 | Clock glb err | BT/APP
34 | Pmu cmu | APP
35 | Pmu ldo | APP
36 | Chip wdt frst | APP
37 | Non os | NO USE
38 | Lpc veto | BT/APP
39 | timer | BT/APP
40 | lpc | BT/APP
41 | rtc | BT/APP
42 | memory | BT/APP
43 | Cpu hifi | APP
44 | Exception test | APP
45 | kv | SSB ONLY
46 | eflash | APP
47 | lib | NO USE
48 | codeloader | SSB ONLY
49 | criticla | BT/APP
50 | ipc | BT/APP
51 | int | BT/APP
52 | os | liteOS
53 | Lpc wakeup time | BT/APP
54 | btc bt frm dismatch1 | BTC
55 | btc bt frm dismatch2 | BTC
56 | btc ble frm dismatch | BTC
57 | btc wakeup | BTC
58 | bth memory | BTH
59 | mpu config | BT/APP
60 | gpu | wear APP
61 | sdio | wear APP
62 | ir | wear APP
63 | btc cmd | BTC
64 | cap | APP
65 | epmu | APP
66 | bts writeproperty | BTS
67 | mmc host | wear APP
68 | btc oscen | BTC
69 | I2C3 | APP
70 | RGB888 | wear APP
71 | ap commu | BTC
72 | Cpu clocks | BT/APP
73 | clocks switch | APP
74 | pmu | NO USE
75 | log dump | BT/APP
76 | coul | APP
77 | dsp0 power off | BT/APP
78 | dsp1 power off | BT/APP
98 | UT | PLT UT
99 | testsuite | PLT UT

## 强制狗复位

系统发生强制狗复位时，不会主动触发hardfault或者nmi异常。等待芯片内部的狗超时后，芯片会主动触发复位。芯片复位后，检测到复位原因0x200d，并触发一次panic异常，系统会导出上次的cpu trace并触发重启。系统重启后，会打印panic重启，重启原因：0x2003；panic id：0x24。如图1所示。

图 1 ULP RTC超时重启打印

# 问题定位

## 外设问题定位方法

外设类异常，绝大部分都是初始化没有完成导致的异常。

外设初始化主要由以下步骤：

1. 上电、开时钟。

2. 配置管脚模式。

3. 注册hal层回调函数。

4. 注册回调。

5. 初始化寄存器配置。

排查思路：

- 首先确认时钟/电源是否打开，当前默认大部分驱动时钟都是关闭的，使用前，查看XXX驱动初始化代码中，CONFIG_XXX_SUPPORT_LPC宏是否打开，如果没有打开，访问XXX驱动相关的寄存器会立刻挂死。

- 查看uapi_xxx_init返回值，确认初始化是否完成。

- 软件流程排查（请参见《BS2XV100 设备驱动开发指南》）。

- 排查硬件：上电是否正常，外围电流是否正常，同样的软件在不同硬件上烧录，查看结果是否一致。

- 其他外设问题，查到外设说明。

## 死机问题定位方法

图 1 定位步骤

## 低功耗问题定位方法

### 管脚漏电排查

睡眠状态下，芯片功耗不达标，需要用户按照如下流程排查IO漏电：

1. 对于板级电路IO管脚：

   - 若管脚有外接上拉电阻，该管脚需配置为浮空。

   - 管脚有外接下拉电阻，该管脚可配置为下拉。

   - 其余情况，该管脚配置为下拉。

2. 对于接入外部设备的管脚：

   - 请查阅该外设手册，若外设内部带上拉/下拉电阻，根据上述说明进行配置。

   - 其他问题可以参考《BS2XV100 低功耗FAQ》。

## OSAL接口维测方法

BS2X SDK提供了OSAL接口帮助用户使用RTOS，用户在使用接口前请参见《BS2XV100 SDK 开发指南》，避免出现接口使用错误，客户在使用OSAL接口开发时，可以通过判断OSAL接口返回值或者打印OSAL接口返回值进行维测。OSAL接口错误码请参考《BS2XV100 SDK 开发指南》。

## 内存维测功能

内存的维测在任何一个系统中都是重要的维测手段，比如：想知道系统中，当前内存的分配和占用情况；想知道各个task的栈占用情况；想知道某个task内存分配具体分配情况。这些内存信息对于内存优化、以及内存定位（比如内存泄露）至关重要。

本文描述如何在B2X系列上如何进行内存维测，包括：

- 查看系统heap内存总体分配和使用情况。

- 查看每个task的malloc情况（当前分配和峰值分配）。

- 查看每个task的栈分配和使用情况；（当前使用和峰值使用）。

- 查看每个内存在哪里分配。

  AIOT的内存维测命令已经集成到AT命令中，用户可以直接使用这些命令。

### 查看系统heap总体信息

该维测功能，用来查看系统中heap内存使用看情况；

查看系统heap信息命令：AT+HEAPSTAT

### 查看task栈使用情况

该维测功能，用来查看各个Task的栈的分配使用使用情况；

可以通过该命令，来优化调整stack的分配大小，节约内存；

查看任务栈使用情况：AT+TASKSTACK

### 查看每个task的内存分配情况

查看某个task的malloc情况：AT+TASKMALLOC=“任务id”；

这里的“任务id”，可以通过“AT+HEAPSTAT”或“AT+TASKSTACK”命令获取。

### 查看中断中、启动阶段的内存分配情况

系统中，除了task中分配内存外，还有中断中分配的内存、系统启动阶段的分配的内存，这些地方分配的地方没有对应的task，因此通过归类到一个特殊id中；

可以通过“AT+HEAPSTAT” 命令，获取non-task alloc对应的id；

查看非任务中内存分配情况的命令，也是：AT+TASKMALLOC=“id”，显示格式也是一样；

### 内存维测开关

由于内存维测需要消耗一定内存空间，默认情况下是关闭的；因此需要手动打开；

以BS21E为例，开启内存维测功能需要打开下面两个宏：打开“kernel\liteos\liteos_v208.6.0_b017\Huawei_LiteOS\tools\build\config\bs21e.config”，将

    # LOSCFG_MEM_TASK_STAT is not set
    # LOSCFG_MEM_DFX_SHOW_CALLER_RA is not set

改成：

    LOSCFG_MEM_TASK_STAT=y
    LOSCFG_MEM_DFX_SHOW_CALLER_RA=y”

注意事项

> 说明：

> - 必须要找对项目对应的config文件。

> - LOSCFG_MEM_DFX_SHOW_CALLER_RA用来查看在哪里调用了内存分配的函数，该宏打开会占用一定内存空间；如果只是进行内存统计，可以不打开。

> - 功能开启后需要对SDK进行全量编译；否则可能会有不生效的情况。

## OS及中断维测

### 维测原理

使用LiteOs提供的OSCFG_HWI_PRE_POST_PROCESS和LOSCFG_BASE_CORE_TSK_MONITOR（liteos menuconfig中默认开启）特性，记录中断和任务调用轨迹。该功能默认不开启，用户使用的时候，需要手动开启OS_DFX_SUPPORT和USER_PRINT_OS_DFX两个宏。

注意：

1. 开启该功能，可能会导致部分任务栈空间增加，导致死机（参考“案例一 栈溢出”），需要调整对应任务栈大小。

2. 开启该功能，会导致无法中断频繁打印，不需要观测的中断，可以手动关闭。参考下图，关闭不需要观测的中断。

3. 该功能，可能会造成一些性能问题，定位狗超时问题时，可以临时打开该功能。

### 维侧日志解析

1. 中断维测：

   下面的维测表示进入75号中断处理函数（hwi_pre表示）；75号中断处理期间，又被66号中断打断（支持中断嵌套，66优先级更高），66号中断连续处理两次后，75号中断无法退出，大概率是中断栈爆了，需要修改中断栈大小。参考下图：

2. 任务调度维测：

## 锁中断时间与中断时间维测

### 维测原理

使用osal提供的锁中断时间和中断时间维测特性，记录锁中断时间和中断时间。该功能默认不开启，用户使用的时候，需要手动添加osal_adapt组件和开启OSAL_IRQ_RECORD_DEBUG这个宏。通过osal_irq_record_flag_set函数参数设置1统计锁中断时间，参数设置2统计中断时间。

注意：

1. 开启该功能，可能会导致部分任务栈空间增加，导致死机（参考“案例一 栈溢出”），需要调整对应任务栈大小。

2. 开启该功能，不需要观测的中断，可以手动关闭。参考下图，关闭不需要观测的中断。

3. 在app_os_init中使用维测时间打印函数，将记录的时间打印出来。

### 维测日志解析

## 查询系统CPU占用率维测功能

CPU占用率的统计是分析系统负载的重要的维测手段，比如：通过CPU占用率判断当前系统负载是否超出设计规格；通过系统中各个任务的CPU占用率，判断各个任务的CPU占用率是否符合设计的预期。

### 查看各个任务和中断的CPU占用情况

可以通过该命令，查看各个Task和中断的CPU占用率情况：AT+CPUP

> 说明：

> - CPU占用率是以百分比显示。

> - CPU占用率的计算方法：任务(或者中断)运行总时间/系统运行总时间.

> - CPUP列显示系统启动至今总的CPU占用率。

> - CPUP 10.0s列显示系统最近10s的CPU占用率。

> - CPUP 1.0s列显示系统最近1s的CPU占用率。

### 维测开关

CPU占用率统计对性能有影响，一般只在开发阶段时需要了解各个任务的占用率，因此默认情况该功能是关闭的；如果要使用该功能需要手动打开。

以BS21E为例，打开“kernel\liteos\liteos_v208.6.0_b017\Huawei_LiteOS\tools\build\config\bs21e.config”，CPUP维测功能需要打开下面宏:

    LOSCFG_KERNEL_EXTKERNEL=y
    LOSCFG_KERNEL_CPUP=y
    LOSCFG_CPUP_INCLUDE_IRQ=y
    LOSCFG_CPUP_SAMPLE_PERIOD=1000
    LOSCFG_CPUP_HISTORY_RECORD_NUM=10
    LOSCFG_DEBUG_VERSION=y
    LOSCFG_DEBUG_KERNEL=y
    LOSCFG_DEBUG_TASK=y
    LOSCFG_DEBUG_HWI=y

注意事项

> 说明：

> - 必须要找对项目对应的config文件。

> - 功能开启后需要对SDK进行全量编译；否则可能会有不生效的情况。

> - 关闭配置项LOSCFG_CPUP_INCLUDE_IRQ后，系统中的中断耗时会被统计到中断发生的任务中，即被中断打断的任务中。

# 典型案例

## 案例一 栈溢出

栈溢出

死机现场如下：

死在OsHeapAlloc，那么可在下图位置加日志，在死机时把栈统计打出来：

打印结果如下：

可以得出app栈爆结论，需要加大线程栈大小。

## 案例二 通过MEPC分析

根据mepc分析死机

死机现场如下：

问题分析

1. 挂死在“app”中，且可以看到mepc为0x90122458，mtval为：0xffffffff。

2. 在lst中找到mepc所对应的汇编代码。

3. 确认是软件操作非法地址writel(0xffffffff, 0)导致的死机。

## 案例三 通过RA分析

PC异常，根据RA分析死机

死机现场如下：

问题分析

1. 挂死在“Swt_Task”中，且pc非法，只能看到ra。

2. 根据lst文件，发现在跳转“g_hal_funcs->set_group”的异常。

3. 怀疑是g_hal_funcs->set_group为空导致的，继续查看lst文件中，pwm初始化代码。发现只有ir中有调用，而且出现问题的时候，没有执行红外学习。

4. 找客户确认，客户的确把pwm初始化代码删掉。添加初始化代码后，问题解决。

问题拓展

1. 如果客户判断一下uapi_pwm_open返回值，就能快速发现这个问题。

   软件代码可以增加判断，异常不让系统挂死。

2. 为什么挂死的时候，pc会变成deadbeee？

   注意：芯片把0x0~0x10000初始化为0xDEADBEEF，mepc的bit0 硬控为0

   - 分析：汇编代码g_hal_funcs

   g_hal_funcs是bss段，初始化为0，没有赋值的时候g_hal_funcs里面的值是0

   - 汇编流程

## 案例四 访问死机

软件执行过程中因为代码逻辑或者内存踩踏等问题，常常造成部分数据变成空地址或者其他非法地址，访问时出现死机。本案例通过实例分析，结合BS2X提供的死机维测方式分析定位空指针执行。如图执行空指针造成的死机log：

首先参考普通打印口维测信息获取章节分析死机现场，当前mcause为1，参考分析mcause对照表，死机原因是指令访问错误，同时mepc、ra均被设置为难以通过application.lst或者application.asm汇编文件直接追溯的地址值，对于此类问题，通常使用CPU Trace工具解析串口Trace，分析死机前执行流程，使用工具解析串口Trace，进入异常前，最后一个执行的函数为plt_at_dumptest：

通过application.lst文件查找plt_at_dumptest函数偏移0x10位置，此处进行了空指针访问操作，造成挂死。

总结：对于mcause为0x0、0x1、0x2等类型的指令访问挂死，在死机现场的寄存器难以追溯异常位置时，使用串口Trace解析工具解析Trace，结合application.lst文件追溯死机流程。

## 案例五 内存Malloc失败

代码执行过程中如果出现panic内存不足以及malloc失败，可以借助当前的内存维测接口进行维测：

参考“内存维测开关”章节，打开内存维测开关，打开之后进入panic时就会打印内存使用信息，如下图在打开内存维测之后，内存申请不足时，维测信息内会打印各个线程内存使用信息，以及死机时各个线程的内存信息。

参考下图，app线程peak峰值异常，根据内存详细使用信息，可以看到0x4523e地址频繁申请内存。

结合lst文件观察调用位置，可以看到异常调用函数异常申请，没有释放。

# 常见问题FAQ

## 异常信息查看

### 异常信息汇总

表 1 异常信息描述

成员 | 描述
--- | ---
task | 死机的任务名称。
thrdPid | 死机的任务ID。
type | 死机类型。

### CPU寄存器信息

表 1 死机相关CPU寄存器描述

成员 | 描述
--- | ---
mepc | 机器异常程序计数器。当发生异常时，mepc指向导致异常的指令；对于中断，mepc指向中断处理后应该恢复的位置。
mstatus | 机器状态寄存器。
mtval | 机器陷入寄存器。保存地址异常中出错的地址或者发生指令异常的指令本身，对于其他错误，其值为零。
mcause | 机器异常寄存器。保存目前异常或者中断的原因，通过查询表 mcause&ccause异常描述表得到目前异常或者中断的类型。
ccause | 与mcause类似，ccause为mcause的补充说明，对于某些异常通过读取ccause寄存器的内容可以进一步明确异常类型。
ra | 返回地址。

表 2 mcause&ccause异常描述表

异常码 | mcause异常描述 | ccause异常描述
--- | --- | ---
0x0 | Instruction address misaligned | Not available
0x1 | Instruction access fault | Memory map region access fault
0x2 | Illegal instruction | AXIM error response
0x3 | Breakpoint | AHBM error response
0x4 | Load address misaligned | Crossing PMP entries
0x5 | Load access fault | System register access fault
0x6 | Store/AMO address misaligned | No PMP entry matched
0x7 | Store/AMO access fault | PMP access fault
0x8 | Environment call from U-mode | CMO access fault
0x9 | Environment call from S-mode | CSR access fault
0xa | Reserved | LDM/STMIA instruction
0xb | Environment call from M-mode | ITCM write access fault
0xc | Instruction page fault | Not available
0xd | Load page fault | Not available
0xe | Reserved | Not available
0xf | Store/AMO page fault | Not available
＞0xf | Reserved | Not available

### 通用寄存器信息

表 1 通用寄存器描述

寄存器 | ABI 名字 | 保存者 | 作用
--- | --- | --- | ---
整型通用寄存器 | x0 | zero | — | 硬编码恒为0，写入数据忽略，读取永远为0
x1 | ra | Caller（调用者） | 函数调用的返回地址
x2 | sp | Callee（被调用者） | 栈指针
x3 | gp | — | 全局指针
x4 | tp | — | 线程指针
x5-x7 | t0-2 | Caller | 临时寄存器
x8 | s0/fp | Callee | 需要保存的寄存器或者帧指针寄存器
x9 | s1 | Callee | 需要保存的寄存器，保存原进程中的关键数据，避免在函数调用过程中被破坏
x10~x11 | a0~1 | Caller | 函数参数/返回值
x12~x17 | a2~7 | Caller | 函数参数
x18~x27 | s2~11 | Callee | 需要保存的寄存器
x28~x31 | t3~6 | Caller | 临时寄存器
浮点型通用寄存器 | f0~f7 | ft0~7 | Caller | FP临时变量
f8~f9 | fs0~1 | Callee | FP保存的寄存器
f10~f11 | fa0~fa1 | Caller | FP参数/返回值
f12~f17 | fa2~fa7 | Caller | FP参数
f18~f27 | fs2~fs11 | Callee | FP保存的寄存器
f28~f31 | ft8~ft11 | Caller | FP临时变量

## ADC常见问题排查

1. 采样挂死

   采样挂死有三种常见原因：

   - 时钟电源被异常关闭：ADC上电时已经打开需要的时钟，如果进入低功耗或在其他业务流程中关闭了需要的时钟，可能导致校准采样等无法完成。需要排查的时钟包括：XO_2_AFE 0x57008410 bit4；XO_2_ADC 0x57008410 bit1。需要排查的电源包括NFCLDO 0x5702C230 bit0:3 配置为0xA；AFELDO,ADCLDO,VREFLDO。如果使用ADC进行音频采样，还需要打开电源MICLDO并配置为2.0V。

   - AMIC与GADC复用，因为音频采样与电压采集复用的是同一套电路，所以需要分时复用。

   - ADC采样未加锁中断，可能因为被其他中断打断导致采样挂死。

2. 采样值异常

   采样值异常有多重原因和表现形式，下面枚举一些典型场景及排查方式：

   - 采样值与实际值误差比例恒定，即采样值越大偏差越大：因为BS2X使用的ADC量程为0~1.5V，常常使用分压电路将待测电压分压后传入ADC，分压电阻的精度可能引入误差，要求使用1%精度的电阻来降低偏差，且转换公式要严格按照电阻值带入，切莫做近似处理，因为不支持浮点运算，建议先乘法再除法。

   - 首次上电、睡眠唤醒后采样值偏低：如上文所述，分压电路中一般存在对地电容，实测100nF电容充电到稳定的时长约为60ms。

   - 使用记录好的校准值采样时，采集到的电压不准确：因为刷入记录好的校准值替换的是校准的流程，降低了大约180ms的启动时间，在此期间电路可能还未稳定，如果使用记录好的校准值，建议延时10~20ms再采样。

   排查方式：

   - ADC有一组较为关键的校准值，存储在0x570360D4~0x570360F4共9位,其默认值为：

     0x00008000 0x00004000 0x00002000 0x00001000 0x00000800 0x00000800 0x00000400 0x00000200

     一般校准的结果与默认值的绝对差值在0x20以内，偏差较大则表明校准失常,如果全部为默认值则表示未进行校准。多次校准出的校准值可能不同，但相差极小。

   - ADC的三路LDO有各自的trim值存在efuse中，初始化时会将其读取并写入ADC，可以比对两组地址里的值是否相等：

     efuse: 0x5702886C[0-3], AFELDO_trim: 0x570363F0[4-7]

     efuse: 0x5702886C[4-7], ADCLDO_trim: 0x570363E8[4-7]

     efuse: 0x5702886C[8-11], VREFLDO_trim: 0x570363F4[5-8]