60、调试与跟踪功能详解

调试与跟踪功能详解

1. 调试事件

在调试过程中,处理器会在以下情况下进入暂停模式或调试监视器:
- 软件开发人员发出停止(暂停)请求。
- 单步执行指令之后。
- 发生调试事件。

在Cortex - M处理器上,调试事件可能由以下原因引起:
- 执行断点指令(BKPT)。
- 程序执行命中断点单元指示的断点。
- 数据观察点和跟踪(DWT)单元发出的事件:该事件可能由数据观察点、程序计数器匹配或周期计数器匹配触发。
- 外部调试请求:在处理器边界有一个名为EDBGRQ的输入信号,该信号的连接因设备而异,可能接地、连接到供应商特定的调试组件,或用于多核同步调试支持。
- 向量捕获事件:这是一个可编程特性,启用后,可在系统复位后或某些故障异常发生时暂停处理器,由调试异常和监视器控制寄存器(DEMCR,地址为0xE000EDFC)控制。

当调试事件发生时,处理器可能进入暂停模式、调试监视器异常,或根据一系列条件忽略该请求。监视器模式调试的处理与暂停模式不同,因为调试监视器异常只是另一种类型的异常,会受到处理器当前优先级的影响。

调试操作后:
- 如果使用暂停模式调试,调试器通过向处理器中的一个调试控制寄存器写入数据来清除暂停请求,从而恢复操作。
- 如果使用监视器调试,调试代理通过执行异常返回来恢复操作。

调试事件与异常类似,分为同步和异步两种类型:
- 同步调试事件包括:执行断点指令、“向量捕获”或程序执行命中断点单元中设置的断点。当断点事件被接受时,处理器停止指令执行,进入暂停模式或调试监视器,调试器可见的程序计数器或监视器模式调试中的堆叠返回地址与断点位置相同,或为向量捕获事件的异常处理程序的第一条指令。
- 异步调试事件包括:数据观察点事件(包括PC匹配和周期计数器匹配)和外部调试请求。这意味着处理器在停止之前可能会继续执行管道中已有的额外指令。

调试器在处理调试操作时必须考虑这些情况。例如,在暂停模式调试中遇到断点后恢复操作,调试器需要:
1. 禁用断点。
2. 使用单步执行功能将程序计数器移动到下一条指令。
3. 重新启用断点,以便处理器随后执行到相同位置时可以再次暂停。
4. 清除暂停请求以恢复操作。

如果不执行上述步骤,软件开发人员在程序因断点暂停后尝试恢复程序执行时,处理器会立即再次命中相同的断点,因为程序计数器仍设置为断点位置(而不是断点后的地址)。

调试事件处理流程如下:

graph TD
    A[调试事件] --> B{暂停模式调试是否启用?}
    B -- 是 --> C{处理器暂停}
    B -- 否 --> D{监视器模式调试是否启用? 且调试监视器异常优先级是否高于当前级别?}
    D -- 是 --> E[执行调试监视器异常处理程序]
    D -- 否 --> F{调试事件是否为断点指令执行?}
    F -- 是 --> G[故障异常]
    F -- 否 --> H[调试事件被忽略]
    I[调试器的暂停请求] --> J{暂停模式调试是否启用?}
    J -- 是 --> C
    J -- 否 --> K{监视器模式调试是否启用? 且调试监视器异常优先级是否高于当前级别?}
    K -- 是 --> E
    K -- 否 --> L[调试监视器保持挂起状态,直到处理器当前级别改变]
    M[外部调试请求(EDBGRQ)] --> B

2. 使用断点指令

编写软件时,可以插入断点指令在感兴趣的点停止程序执行。与硬件断点比较器不同,只要内存空间允许,可以插入任意数量的软件断点。断点指令(BKPT #immed8)是16位Thumb®指令,编码为0xBExx,处理器只解码0xBE的高8位,指令的低8位取决于指令后面给出的立即数。如果调试工具支持半主机模式,立即数可用于请求半主机服务,此时调试器需要从程序内存或编译后的程序映像中提取立即数的值。对于Arm开发工具,半主机服务通常使用BKPT指令中的低8位值0xAB。

在使用CMSIS - CORE驱动的C编程环境中,可以使用CMSIS - CORE定义的函数插入断点指令:

void __BKPT(uint8_t value);

例如:

__BKPT(0x00);

大多数C编译器都有自己的内置函数来生成断点指令。使用半主机功能(如将“printf”消息重定向到半主机通信通道)时,开发工具链会自动插入断点指令和相关的半主机支持代码。需要注意的是,在暂停模式调试中使用半主机功能时,处理器可能会频繁停止,这会显著降低处理器性能,通常不适用于实时应用。

在为生产环境确定软件最终版本时,必须删除代码中为调试而插入的断点指令。如果未删除断点指令且在未启用调试的情况下执行(如未连接调试器),处理器将进入硬故障异常,硬故障状态寄存器(HFSR)中的故障状态位“DEBUGEVT”和调试故障状态寄存器(DFSR)中的“BKPT”位将指示该硬故障的原因。

3. 调试认证与TrustZone

调试认证机制允许系统定义调试和跟踪功能的权限级别。虽然Armv6 - M和Armv7 - M架构中已有基本的调试认证功能,但Armv8 - M架构对其进行了增强,现在支持TrustZone。

为支持调试认证功能,处理器顶层(即处理器设计的边界)有多个输入信号,如下表所示:
| 信号 | 名称 | 描述 |
| ---- | ---- | ---- |
| DBGEN | 侵入式调试使能 | 信号为1时,启用普通(即非安全)世界的侵入式调试功能 |
| NIDEN | 非侵入式调试使能 | 信号为1时,启用普通(即非安全)世界的非侵入式调试功能(如跟踪) |
| SPIDEN | 安全特权侵入式调试使能 | 信号为1时,启用安全世界的侵入式调试功能,仅在实现TrustZone时可用 |
| SPNIDEN | 安全特权非侵入式调试使能 | 信号为1时,启用安全世界的非侵入式调试功能(如跟踪),仅在实现TrustZone时可用 |

并非所有这些信号的组合都是允许的,例如:
- 如果SPIDEN为高电平(即逻辑电平1),则DBGEN也必须为高电平。
- 同样,如果SPNIDEN为高电平,则NIDEN也必须为高电平。
- 如果在安全域中允许侵入式调试,则通常也允许非侵入式调试。

常用的信号组合如下表所示:
| DBGEN | NIDEN | SPIDEN | SPNIDEN | 描述 |
| ---- | ---- | ---- | ---- | ---- |
| 0 | 0 | 0 | 0 | 所有调试和跟踪功能禁用 |
| 0 | 1 | 0 | 0 | 仅允许在非安全世界进行非侵入式调试(如跟踪) |
| 1 | 1 | 0 | 0 | 仅允许在非安全世界使用调试和跟踪功能 |
| 0 | 1 | 0 | 1 | 仅允许在两个世界进行非侵入式调试(如跟踪) |
| 1 | 1 | 0 | 1 | 仅允许在安全世界进行非侵入式调试(如跟踪),允许在非安全世界进行侵入式和非侵入式调试 |
| 1 | 1 | 1 | 1 | 允许在安全和非安全世界使用所有调试和跟踪功能 |

这些调试认证信号由调试认证控制软件控制,该软件可以运行在处理器上,也可以运行在其他系统管理处理器(如安全飞地)上。调试认证过程使用加密函数验证软件开发人员凭证中的信息,并将其与芯片安全存储中的秘密信息进行比较,以确保只有授权的软件开发人员才能获得调试访问权限。由于调试认证配置取决于芯片的生命周期,因此需要非易失性存储器来保存生命周期状态,典型的设备生命周期状态包括设备制造后、安全固件加载后、非安全固件加载后、产品部署和退役等。

安全特权软件可以通过对调试认证控制寄存器(DAUTHCTRL,地址为0xE000EE04)进行编程来覆盖SPIDEN和SPNIDEN的值:
- 当DAUTHCTRL.SPNIDENSEL(位2)设置为1时,DAUTHCTRL.INTSPNIDEN(位3)的值将覆盖SPNIDEN的值。
- 当DAUTHCTRL.SPIDENSEL(位0)设置为1时,DAUTHCTRL.INTSPIDEN(位1)的值将覆盖SPIDEN的值。

当所有调试功能都被禁用时,仍然可以访问某些内存位置,如ID寄存器和ROM表,这是为了让调试器能够检测可用的调试资源和处理器类型。在某些情况下,SoC产品设计人员可能希望禁用所有调试访问,为此,DAP模块上有一个额外的调试权限控制信号,可用于禁用所有调试访问。

使用Armv8 - M架构时,可以在允许非安全世界调试的同时禁用安全调试。此时:
- 软件开发人员无法访问安全内存。尽管调试器可以对DAP进行编程以生成安全或非安全传输,但当安全调试被禁止时,所有调试访问都被视为非安全访问,并被阻止访问安全地址。
- 处理器不能在安全API中间暂停,也不能单步进入安全API。
- 复位向量捕获调试事件将被挂起,以便处理器在分支到非安全程序时立即暂停,由安全异常引起的其他向量捕获事件将被忽略。
- 当处理器处于安全状态时,跟踪源(如ETM、DWT)将停止生成指令/数据跟踪数据包。
- 当处理器处于安全状态时,只要不导致安全信息泄露,跟踪源(如ETM、DWT)仍允许生成其他跟踪数据包。
- 如果调试器在处理器在非安全地址暂停时尝试将程序计数器更改为安全程序地址,处理器在单步执行或恢复时将进入安全故障或安全硬故障。

如果在执行安全API期间收到来自调试器或外部调试请求信号EDBGRQ的暂停请求,该请求将被挂起,直到处理器返回非安全状态才会被接受。在对非安全应用进行单步调试时,如果非安全代码调用安全API,处理器将在安全API的第一条指令(即SG指令)处暂停,但在下一步单步执行时,处理器直到返回非安全世界才会停止。虽然处理器在安全地址位置(即SG指令的地址)暂停,但这种行为不会导致安全信息泄露,因为:
- 安全内存中的安全API代码和安全数据对软件开发人员不可见。
- 此时安全API尚未进行任何处理。

需要注意的是,暂停地址(即安全API的入口点)不被视为安全信息,因为非安全软件开发人员在程序代码中调用安全API时需要知道这些入口点的地址。

调试认证设置定义了调试监视器异常目标是处于安全状态还是非安全状态。如果允许安全调试,调试监视器异常(类型12)的目标是安全状态;否则,目标是非安全状态。

4. CoreSight发现:调试组件的识别

CoreSight调试架构具有很强的扩展性,可用于包含大量调试组件的复杂片上系统设计。为支持广泛的系统配置,CoreSight调试架构提供了一种机制,允许调试器自动识别系统中的调试组件,这涉及使用每个调试组件内的ID寄存器和一个或多个查找ROM表。

当调试器连接到基于CoreSight的调试系统时,会执行以下步骤:
1. 调试器通过JTAG或串行线调试协议,使用检测到的ID值来确定它连接的调试端口组件类型。
2. 调试器使用调试连接向调试系统和(如果需要)系统逻辑发出上电请求,该唤醒请求由调试端口模块上的硬件接口处理,使用握手机制让调试器知道何时可以发出下一个命令。
3. 上电请求握手完成后,调试器扫描DAP的内部调试总线,查看有多少个访问端口组件连接到它。根据CoreSight架构2.0版本,内部调试总线中最多可以有256个AP模块,但对于大多数Cortex - M设备,通常只有一个处理器,且只有一个AP模块连接到DP模块。
4. 调试器通过读取访问端口模块的ID寄存器来确定其类型。对于基于单核Cortex - M的设备,显示为AHB - AP模块。
5. 确认AP模块为AHB - AP后,调试器通过读取AHB - AP内的一个寄存器来确定主ROM表的基地址,该寄存器包含只读的基地址,ROM表用于检测调试组件。
6. 调试器使用步骤5中获得的ROM表基地址,读取主ROM表的ID寄存器,确认它是一个ROM表。
7. 调试器遍历ROM表中的条目,收集调试组件的基地址以及(如果可用)额外的ROM表。ROM表包含一个或多个调试组件条目,每个条目有:
- 一个地址偏移值,指示组件相对于ROM表基地址的地址偏移。由于总线从设备通常按4KB地址边界对齐,32位条目中并非所有位都用于地址。
- 一位(条目的最低位之一),用于指示该条目指向的地址处是否实现了组件。
- 一位(条目的最低位之一),用于指示该条目是否是当前ROM表中的最后一个条目。
8. 调试器使用这些信息构建所有可用设备的树状数据库。

如果调试器在扫描过程中检测到额外的ROM表,也会扫描这些额外ROM表中的条目。调试组件识别过程会持续进行,直到检测到所有ROM表中的所有条目。

CoreSight调试组件识别流程如下:

graph TD
    A[调试器连接到调试系统] --> B[检测调试端口组件类型]
    B --> C[发出上电请求并进行握手]
    C --> D[扫描DAP内部调试总线,检测AP组件数量]
    D --> E[读取AP模块ID寄存器,确定类型]
    E --> F[确认AHB - AP后,确定主ROM表基地址]
    F --> G[读取主ROM表ID寄存器,确认是ROM表]
    G --> H[遍历ROM表条目,收集信息]
    H --> I[构建设备树状数据库]
    H -- 检测到额外ROM表 --> J[扫描额外ROM表条目]
    J --> I

通过以上流程,调试器可以准确识别CoreSight调试系统中的各种组件,为后续的调试工作提供基础。

5. 调试事件处理的关键要点总结

5.1 调试事件触发条件

调试事件的触发条件较为多样,主要包括软件开发人员的请求、单步执行指令以及特定事件的发生。具体如下:
- 软件开发人员请求 :开发人员可主动发出停止(暂停)请求,使处理器进入相应调试状态。
- 单步执行 :每完成一条指令的单步执行后,处理器可能进入调试相关模式。
- 特定事件 :如执行断点指令、命中断点单元指示的断点、数据观察点和跟踪单元发出的事件、外部调试请求以及向量捕获事件等。

5.2 同步与异步调试事件

调试事件分为同步和异步两种类型,它们的特点和影响有所不同:
| 类型 | 特点 | 影响 |
| ---- | ---- | ---- |
| 同步调试事件 | 包括执行断点指令、“向量捕获”或命中预设断点等。处理器会立即停止指令执行,进入相应调试模式,程序计数器或堆叠返回地址与断点位置相关。 | 调试器能准确获取断点位置信息,便于调试操作。 |
| 异步调试事件 | 如数据观察点事件和外部调试请求。处理器在停止前可能继续执行管道中的额外指令。 | 调试器处理时需考虑指令执行的延迟,避免出现调试错误。 |

5.3 调试事件处理流程

调试事件的处理流程较为复杂,涉及多种条件判断,具体流程如下:

graph TD
    A[调试事件] --> B{暂停模式调试是否启用?}
    B -- 是 --> C{处理器暂停}
    B -- 否 --> D{监视器模式调试是否启用? 且调试监视器异常优先级是否高于当前级别?}
    D -- 是 --> E[执行调试监视器异常处理程序]
    D -- 否 --> F{调试事件是否为断点指令执行?}
    F -- 是 --> G[故障异常]
    F -- 否 --> H[调试事件被忽略]
    I[调试器的暂停请求] --> J{暂停模式调试是否启用?}
    J -- 是 --> C
    J -- 否 --> K{监视器模式调试是否启用? 且调试监视器异常优先级是否高于当前级别?}
    K -- 是 --> E
    K -- 否 --> L[调试监视器保持挂起状态,直到处理器当前级别改变]
    M[外部调试请求(EDBGRQ)] --> B

从流程图可以看出,调试事件的处理需要根据不同的条件进行判断,以确定处理器的响应方式。

6. 断点指令使用的注意事项

6.1 断点指令的插入与使用

在软件开发中,断点指令是一种非常有用的调试工具。在使用CMSIS - CORE驱动的C编程环境中,可使用以下函数插入断点指令:

void __BKPT(uint8_t value);

例如:

__BKPT(0x00);

大多数C编译器也有自己的内置函数来生成断点指令。使用半主机功能时,开发工具链会自动插入相关代码。

6.2 半主机功能的影响

在暂停模式调试中使用半主机功能时,处理器可能会频繁停止,这会显著降低处理器性能,通常不适用于实时应用。因此,在实时应用开发中,应谨慎使用半主机功能。

6.3 断点指令的清理

在为生产环境确定软件最终版本时,必须删除代码中为调试而插入的断点指令。如果未删除,在未启用调试的情况下执行,处理器将进入硬故障异常,硬故障状态寄存器(HFSR)中的故障状态位“DEBUGEVT”和调试故障状态寄存器(DFSR)中的“BKPT”位将指示该硬故障的原因。

7. 调试认证与TrustZone的深入理解

7.1 调试认证信号与组合

调试认证机制通过一系列输入信号来定义调试和跟踪功能的权限级别。相关信号及其描述如下表所示:
| 信号 | 名称 | 描述 |
| ---- | ---- | ---- |
| DBGEN | 侵入式调试使能 | 信号为1时,启用普通(即非安全)世界的侵入式调试功能 |
| NIDEN | 非侵入式调试使能 | 信号为1时,启用普通(即非安全)世界的非侵入式调试功能(如跟踪) |
| SPIDEN | 安全特权侵入式调试使能 | 信号为1时,启用安全世界的侵入式调试功能,仅在实现TrustZone时可用 |
| SPNIDEN | 安全特权非侵入式调试使能 | 信号为1时,启用安全世界的非侵入式调试功能(如跟踪),仅在实现TrustZone时可用 |

常用的信号组合也有多种,不同组合对应不同的调试权限:
| DBGEN | NIDEN | SPIDEN | SPNIDEN | 描述 |
| ---- | ---- | ---- | ---- | ---- |
| 0 | 0 | 0 | 0 | 所有调试和跟踪功能禁用 |
| 0 | 1 | 0 | 0 | 仅允许在非安全世界进行非侵入式调试(如跟踪) |
| 1 | 1 | 0 | 0 | 仅允许在非安全世界使用调试和跟踪功能 |
| 0 | 1 | 0 | 1 | 仅允许在两个世界进行非侵入式调试(如跟踪) |
| 1 | 1 | 0 | 1 | 仅允许在安全世界进行非侵入式调试(如跟踪),允许在非安全世界进行侵入式和非侵入式调试 |
| 1 | 1 | 1 | 1 | 允许在安全和非安全世界使用所有调试和跟踪功能 |

7.2 安全特权软件的控制

安全特权软件可以通过对调试认证控制寄存器(DAUTHCTRL,地址为0xE000EE04)进行编程来覆盖SPIDEN和SPNIDEN的值:
- 当DAUTHCTRL.SPNIDENSEL(位2)设置为1时,DAUTHCTRL.INTSPNIDEN(位3)的值将覆盖SPNIDEN的值。
- 当DAUTHCTRL.SPIDENSEL(位0)设置为1时,DAUTHCTRL.INTSPIDEN(位1)的值将覆盖SPIDEN的值。

7.3 安全调试的限制

使用Armv8 - M架构时,在允许非安全世界调试的同时禁用安全调试会有一系列限制:
- 软件开发人员无法访问安全内存。
- 处理器不能在安全API中间暂停,也不能单步进入安全API。
- 复位向量捕获调试事件将被挂起,由安全异常引起的其他向量捕获事件将被忽略。
- 当处理器处于安全状态时,跟踪源的行为受到限制。
- 调试器操作不当可能导致处理器进入安全故障或安全硬故障。

8. CoreSight发现机制的重要性

8.1 识别调试组件的流程

CoreSight调试架构的扩展性使其适用于复杂系统,其发现机制可帮助调试器自动识别调试组件。具体流程如下:
1. 检测调试端口组件类型。
2. 发出上电请求并进行握手。
3. 扫描DAP内部调试总线,检测AP组件数量。
4. 读取AP模块ID寄存器,确定类型。
5. 确认AHB - AP后,确定主ROM表基地址。
6. 读取主ROM表ID寄存器,确认是ROM表。
7. 遍历ROM表条目,收集信息。
8. 构建设备树状数据库。

graph TD
    A[调试器连接到调试系统] --> B[检测调试端口组件类型]
    B --> C[发出上电请求并进行握手]
    C --> D[扫描DAP内部调试总线,检测AP组件数量]
    D --> E[读取AP模块ID寄存器,确定类型]
    E --> F[确认AHB - AP后,确定主ROM表基地址]
    F --> G[读取主ROM表ID寄存器,确认是ROM表]
    G --> H[遍历ROM表条目,收集信息]
    H --> I[构建设备树状数据库]
    H -- 检测到额外ROM表 --> J[扫描额外ROM表条目]
    J --> I

8.2 对调试工作的支持

通过CoreSight发现机制,调试器能够准确识别系统中的各种调试组件,为后续的调试工作提供了基础。调试器可以根据识别结果,对不同的组件进行针对性的调试操作,提高调试效率和准确性。

综上所述,调试与跟踪功能在软件开发和系统调试中具有重要作用。了解调试事件的处理、断点指令的使用、调试认证与TrustZone的机制以及CoreSight发现机制等内容,有助于开发人员更好地进行调试工作,提高软件的质量和稳定性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值