Debug Module
- 版本:V2R2
- 状态:OK
- 日期:2025/01/20
- commit:xxx
术语说明
| 缩写 | 全称 | 描述 |
|---|---|---|
| DM | Debug Module | 调试模块 |
| DTM | Debug Transport Module | 调试转换模块 |
| DMI | Debug Module Interface | 调试模块接口 |
参数设计
| 参数 | 默认值 | 描述 |
|---|---|---|
| baseAddress | 0x38020800 | debug Module MMIO 基地址 |
| nDMIAddrSize | 7 | DMI 地址宽度 |
| nProgramBufferWords | 16 | Program Buffer 数量 |
| nAbstractDataWords | 4 | Abstract Commands 数量 |
| hasBusMaster | true | system bus master |
| maxSupportedSBAccess | 64 | sysbus 最大访存宽度 |
| supportQuickAccess | false | QuickAccess 支持 |
| supportHartArray | true | hart array 支持 |
| nHaltGroups | 1 | halt group 数量 |
| nExtTriggers | 0 | external triggers 数量 |
| hasHartResets | true | reset 选中的 harts |
| hasImplicitEbreak | false | 隐式 ebreak 支持 |
总体设计
整体框图
如 此图 所示:
多时钟域
如 此图 所示:
Debug MMIO
如 此表 所示:
| 地址(基地址0x3802_0000) | 名称 | 描述 | 该地址里存放的内容 |
|---|---|---|---|
| 0x800 | debugEntry | debug 入口地址 /debug rom 的基地址 | |
| 0x808 | debugException | 在 dmode 执行时出现异常入口地址 | |
| 0x100 | HALTED | 进入 dmode 的 hart 对应的 hartid,debugmodule 会拿到 | |
| 0x104 | GOING | whereto,最终跳转到 ABSTRACT 去执行 | |
| 0x108 | RESUMING | 执行 dret | |
| 0x10c | EXCEPTION | ||
| 0x300 | WHERETO | 该地址里存放的指令 | dm 生成的跳转到ABSTRACT 的跳转指令 |
| 0x380 | DATA | DATA 的基地址(for ld/st) | 数据交换 |
| DATA-4*nProgBuf | PROGBUF | progbuf0 的地址 | dm 生成的指令(go 之前准备好) |
| DATA-4 | IMPEBREAK | 隐式 ebreak 指令 | |
| PROGBUF - 4* nAbstractInst | ABSTRACT | AbstractInstructions | dm 生成的指令(go 之前准备好) |
| 0x400 | FLAGS | hartid 对应 flag 的基地址,每个 flag 是 8bits, 0x400 代表的是 hartid=0 时的 flag 的地址 | 这个 8bits 只有低两位有效,次低位指的是 resume,最低位指的 go, 地址空间是 1k 即 0x400->(0x500-0x1) |
模块设计
Debug Module
当前昆明湖 debug 实现的情况如下:
- 支持从第一条指令开始的调试,在 cpu 复位之后进入调试模式。
- 支持单核、多核(选中的核)调试的运行控制,包括 halt, resume, reset。
- 支持单步调试。
- 支持 stopcount,stoptime。
- 支持软断点( ebreak 指令)、硬断点( trigger )和内存断点( trigger )。
- 支持GPR,CSR 和内存访问,支持 progbuf 和 sysbus 两种访存方式。
- 支持通过 debug interrupt(haltreq, haltgroup, halt-on-reset), trigger fire, ebreak, singlestep, critical error 等方式进入 debug mode。
Trigger Module
当前昆明湖 trigger module 的实现情况如下:
- 昆明湖 trigger module 当前实现的 debug 相关的 CSR 如下表所示。
- trigger 的默认配置数量是 4 (支持用户自定义配置)。
- 支持 mcontrol6 类型的指令、访存的 trigger。
- match 支持相等,大于等于,小于三种类型(向量访存当前只支持相等类型匹配)。
- 仅支持 address 匹配,不支持 data 匹配。
- 仅支持 timing = before。
- 仅支持一对 trigger 的 chain。
- 为了防止 trigger 的二次产生 breakpoint 的异常,支持通过 xSTATUS.xIE 控制。
- 支持H扩展的软硬件断点,watchpoint 调试手段。
- 支持原子指令的访存 trigger。
以下表格描述的是当前昆明湖支持的访存指令在微架构里的访存粒度和trigger匹配粒度:对于标量指令和以元素为粒度进行访存的向量指令来说,match type 支持 >=, =, <;对于其余向量指令来说,仅支持match type为 = 的匹配。另外对于向量访存指令来说,处理元素索引较小的指令触发的trigger fire(不管其 trigger action 是 breakpoint 还是 debug )。
| 指令类型 | 访存粒度 | trigger匹配粒度 |
|---|---|---|
| 标量访存指令 | 指令(元素) | 检查元素小端地址,支持>=, =, < |
| 原子访存指令(lr/sc) | 指令(元素) | 检查元素小端地址,支持>=, =, <; lr 视为load,sc 视为 store(不管成功与否) |
| 原子访存指令(amo) | 指令(元素) | 检查元素小端地址,支持>=, =, <;在拿到 vaddr 同时检查 load 和 store |
| 向量访存指令(unit-stride) | 向量寄存器宽度(128bit) | 支持该指令访存地址范围内任意地址的检查(以 8bit 为粒度),但仅仅支持 = 匹配 |
| 向量访存指令(whole) | 向量寄存器宽度(128bit) | 支持该指令访存地址范围内任意地址的检查(以 8bit 为粒度),但仅仅支持 = 匹配 |
| 向量访存指令(fof unit-stride) | 向量寄存器宽度(128bit) | 支持该指令访存地址范围内任意地址的检查(以 8bit 为粒度),但仅仅支持0号元素 = 匹配 |
| 向量访存指令(segment) | 元素 | 检查每个元素小端地址,但仅仅支持 = 匹配 |
| 其他类型的向量访存指令 | 元素 | 检查每个元素小端地址,支持 >=, =, < |
| 名称 | 地址 | 读写 | 介绍 | 复位值 |
|---|---|---|---|---|
| Tselect | 0x7A0 | RW | trigger 选择寄存器 | 0X0 |
| Tdata1(Mcontrol6) | 0x7A1 | RW | trigger data1 | 0xF0000000000000000 |
| Tdata2 | 0x7A2 | RW | trigger data2 | 0x0 |
| Tinfo | 0x7A4 | RO | trigger info | 0x40 |
| Dcsr | 0x7B0 | RW | Debug Control and Status | 0x40000003 |
| Dpc | 0x7B1 | RW | Debug PC | 0x0 |
| Dscratch0 | 0x7B2 | RW | Debug Scratch Register 0 | - |
| Dscratch1 | 0x7B3 | RW | Debug Scratch Register 1 | |
| mcontext | 0x7A8 | RW | Machine Context | - |
| hcontext | 0x6A8 | RW | Hypervisor Context | - |
| scontext | 0x5A8 | RW | Supervisor Context | - |
调试流程举例
CSR 访问:
debug module CSR 访问是 abstract command 和 progbuff 配合完成的,根据 abstract command 会在 ABSTRACT 和 PROGBUFF 地址处分别生成相应的指令(这两块地址空间是连续的),让 CPU 去执行,达到访问 CSR 的目的。ABSTRAT 处生成的是 lw/st 指令,做的是 MMIO 地址与 GPR s0/s1 之间的数据交换,PROGBUFF 处生成的是 CSR 的读写指令)。 下面以访问 mstatus 寄存器为例说明 debug module 是如何访问 CSR 的:
- 假设软件发出一个写 mstatus CSR 的命令然后该命令会依次经过 JtagProbe,JtagDTM,DMI 转化为 DMI 操作;
- Dmi 操作经过 dmi2tl 去修改 DebugModule 内部控制信号,修改 DMI_COMMAND 为写 mstatus 寄存器的 command;
- openocd 首先会把 s0/fp 的值读出来存着,然后往 progbuffer 写 csr 的写指令;
- 执行 ABSTRAT(ld指令),把 DATA 写到s0;
- 执行 progbuffer(CRS写指令),progbuff 以 ebreak 指令结尾,重新进入 parking loop;
如果是读 csr 的话: 6. 执行 progbuffer(CRS读指令),把 csr 的值读出来给 s0; 7. 执行 ABSTRAT(st指令),把 s0 写到 DATA;
硬件断点:
以下内容以打断点为例,说明在调试过程中,软硬件的协同工作流程:
- 首先软件发出一个 halte d的命令,然后该命令会依次经过 JtagProbe,JtagDTM,DMI 转化为 DMI 操作;
- Dmi 操作经过 dmi2tl 去修改 DebugModule 内部控制信号,向 hart 发出一个外部的 debug 中断,该中断最终会传到 hart 内部的 CSR 模块中;
- CSR 处理来自外部的 debug 中断:hart 将 Trap 到 debugModule 的入口地址去执行(见 Debug Module MMIO ),进入 DMode;
- Hart 进入到 DMode 之后,执行 debug ROM 里的指令,会把自己的 hartid 写到 HALTED(见第8节 Debug ROM ),告知 Debug module 自己(hart)进入了 dmode,Debugger 在 Dmode 下可以对 hart 进行调试;
- 当软件发出一个打硬件断点的命令时,hart 会跳到 whereto,abstract 和 progbuff 相互配合,控制 hart 执行 CSR 指令(progbuffer)配置 trigger CSR 寄存器,把断点的信息写入 trigger CSR;progbuff 以 ebreak 指令结尾,执行该 ebreak 会再次跳到 debugModule 的入口地址;
- 软件发出一个 resume 的命令,hart 会跳到 _resume 去执行 dret 指令,退出 dMode,回到1处 halted 前去执行;( resume 之前有一个准备工作,需要先执行一次 step ,只提交一条指令,然后通过 single step 异常 trap 到 debugMode,这块可以去看 openocd 的源码)
- 当 hart 执行程序到打断点的位置时,指令的 pc 匹配上 trigger CSR 里配置的断点地址,trigger fire,hart 会再次进入 dmode(Trap 到 debugModule 的入口地址)去执行 debug rom 里的指令,等待 debugger 调试。