当代码在硅片上奔跑,我们如何洞悉其一举一动?当系统陷入僵局,又该如何揪出那只捣蛋的“虫子”?答案,就隐藏在 RISC-V 架构的心脏地带——调试模块(Debug Module, DM)之中。
Debug Module 并非处理器核心的直接组成部分,却像一位拥有“上帝视角”的贴身外科医生,赋予开发者透视、暂停、干预乃至修复系统运行状态的超能力。无论是芯片设计阶段的硬件验证、固件开发中的单步跟踪,还是操作系统内核的崩溃分析,都离不开这个强大而神秘的模块。
内容概况
本报告系统梳理了 RISC-V Debug Module 的完整技术体系,共分4章:
第一章:Debug Module 的角色与分层架构
RISC-V Debug Module(DM)是调试体系的核心枢纽,处于“承上启下”的关键位置:
向上:对接调试器(OpenOCD/GDB),通过抽象命令和程序缓冲区提供统一接口; 向下:直接控制 Hart 的暂停(Halt)、恢复(Resume)和单步(Single Step)行为。
RISC-V 调试架构采用 三层分层设计:
第二章:架构总览
Debug Module 的内部由三大核心组件构成,它们各司其职,协同工作。首先是负责整体控制与状态管理的控制状态模块,它掌管着DM的整体启停、运行模式切换,记录当前调试任务的状态,同时承载各类核心控制与状态寄存器,是整个模块的指挥中枢;其次是抽象命令模块,负责解析调试器发来的各类抽象命令,完成对Hart寄存器、系统内存的访问操作,是对接上层调试请求的核心处理单元;最后是系统接口模块,负责对接本端SoC的总线系统,完成对内存、外设等资源的访问操作,打通调试命令到目标资源的通路。
第三章:规范要求
Debug Module(调试模块,DM)是连接调试器与 RISC-V 处理器核的关键桥梁。本文从寄存器体系、核心功能机制、调试传输三个维度梳理 DM 技术全貌。
一、寄存器体系:DM 寄存器与调试模式 CSR 的双层架构
RISC-V 调试架构的寄存器分属两个独立地址空间。
DM 寄存器通过 DMI 总线寻址。调试器常见路径是经 JTAG DTM 将 JTAG 扫描转换为 DMI 访问。实现也可以采用其它 Debug Transport。dmcontrol 负责全局控制(DM 使能、Hart 暂停/恢复、Hart 选择),dmstatus 反映 Hart 实时状态;abstractcs 记录抽象命令执行状态与错误码;command 是抽象命令写入端口。data0-data11 用于参数传递,progbuf0-progbuf15 构成程序缓冲区,sbcs 系列寄存器服务于系统总线访问。dmstatus.version 需要单独确认:version=2 表示实现符合 Debug Spec 0.13,version=3 表示实现符合 Debug Spec 1.0,version=15 表示 custom。当前主流软件栈通常以 0.13/1.0 兼容为基础,服务器平台说明中建议明确写出实测或设计目标版本。
调试模式 CSR 仅在 Hart 进入 Debug Mode(DMode)后可见。dcsr(Debug Control and Status Register)是核心控制寄存器,涵盖各特权级 ebreak 行为控制(ebreakm/ebreaks/ebreaku,及 H 扩展的 ebreakvs/ebreakvu)、单步使能(step)及进入调试模式的原因码(cause)。dpc 保存进入 DMode 时的 PC,dscratch0/dscratch1 是暂存寄存器。
触发器 CSR 是第三组关键寄存器:tselect 选择触发器编号,tdata1 存放配置,tdata2 存放匹配值,Server Platform 对应用处理器 Hart 给出了最低能力要求,具体实现数量应以 tinfo 或实际调试枚举结果为准。
与 Intel x86 对比,RISC-V 设计更具扩展性:x86 固定使用 DR0-DR3 四个地址断点,而 RISC-V 将触发器设计为可配置数量的 CSR 组,通过 tselect 动态切换。单步机制由 dcsr.step 控制,对应 x86 的 EFLAGS.TF。
二、核心功能机制:从抽象命令到触发器匹配
调试模式进入与退出是调试操作的基础。Hart 进入 DMode 的常见原因包括外部调试请求(haltreq)、配置为进入 Debug Mode 的 trigger、ebreak 指令、单步执行(step)、复位即暂停(halt-on-reset)以及 halt group 等;其它实现相关原因可归入 other/extcause,不宜笼统表述为“严重错误”。退出则执行 dret 指令返回原特权级。
抽象命令(Abstract Commands)是 DM 的高层调试接口。调试器向 command 写入命令字,DM 引擎解析执行,通过 data0-data11 传递参数和结果。大部分命令可选,若未实现可通过 abstractcs.cmderr 判断失败。Access Register 命令用于读写 GPR 和 CSR,是日常调试中最频繁使用的命令。
程序缓冲区(Program Buffer)提供更灵活的调试手段:调试器向 progbuf 注入自定义指令序列,DM 控制 Hart 在 DMode 下执行。以写 mstatus 为例,OpenOCD 先保存 s0,向 progbuf 写入 CSR 写指令,再执行 ld 将 DATA 加载到 s0,最后执行 CSR 写指令,随后通过 ebreak 或实现提供的 implicit ebreak 重新回到调试控制流。更准确地说,若 dmstatus.impebreak=1,实现可在 Program Buffer 之后提供隐式 ebreak;否则调试器通常需要显式放置 ebreak。
系统总线访问(System Bus Access, SBA)允许调试器不经 Hart 直接用物理地址访问系统总线。规范层面并不把 SBA 限定为只能访问普通 memory;它能否访问 MMIO 取决于 SoC 总线连接、权限隔离和具体实现策略。昆明湖当前工程中若 SBA 主要覆盖 memory,访问部分外设寄存器时可改用 Program Buffer,由 Hart 视角完成访问。
RISC-V Debug Module 构建了一套层次分明、可扩展性强的调试体系。
三、Hart 级调试能力规范要求
前两节梳理了 DM 寄存器体系与核心功能机制,刻画的是“DM 提供什么调试接口”。但调试的最终执行者是 Hart 本身——若 Hart 不具备相应的硬件能力,DM 的接口再丰富也无处着力。RISC-V 服务器规范(server platform)在 SEE(Software Execution Environment)章节中,以 SEE_010 和 SEE_020 两条规则对应用处理器 Hart 的必选规范”。
SEE_010:单步执行能力
SEE_010 要求,RISC-V 应用处理器 Hart 必须支持通过 ·dcsr(Debug Control and Status Register)中的 ·step 位实现单步执行(single stepping)。置位 ·dcsr.step 后,Hart 在恢复执行时仅执行恰好一条指令便自动重新进入 Debug Mode(DMode),调试器据此读取 ·dpc(Debug PC Register)与通用寄存器,完成逐指令跟踪。
是第4章“二、单步调试:dcsr.step 的硬件语义”所描述实战流程的规范根基。没有·step 位的硬件支持,调试器无法实现逐指令跟踪,核间时序对齐也无从谈起。规范在前、实战在后,二者前后印证。
SEE_020:Trigger 资源与过滤能力
如果说 SEE_010 规定了 Hart 的“单步能力底线”, SEE_020 则对 Trigger 硬件资源给出了明确的量化约束,与前面提到的“核心功能机制”中的 Trigger 模块形成“机制→规范量化”的逻辑自然递进, RISC-V 服务器规范要求对 Trigger 资源的硬件配置下限,以及必须实现的过滤能力结合。
触发器资源量化要求。 SEE_020 规定,应用处理器 Hart 必须支持:
至少 4 个指令地址匹配触发器(instruction address match trigger)
至少 4 个 load/store 地址匹配触发器(load/store address match trigger)
至少 1 个 ·icount 触发器,用于支持单步执行
至少 1 个中断触发器(interrupt trigger)
至少 1 个异常触发器(exception trigger)
过滤能力的要求仅有Trigger数量还不够,SEE_020 进一步规定了过滤能力,使Trigger能够精确控制触发命中范围。所有Trigger必须支持:
特权模式过滤(privilege mode filtering),覆盖 VS、VU、S、U 四种模式,允许调试器按特权层级精确命中——在虚拟化多核场景下,可仅命中 Guest 内核代码而不误触发 Host 或其他 VM。
第 4 章 典型工作流程
从调试器接入的第一条 JTAG 命令,到多核场景下的 halt group 协调,每一步背后都有明确的寄存器交互和状态转移。
一、连接与枚举:调试链路的第一步
调试器通过 JTAG 接入目标设备时,并非直接读写 DM 寄存器,而是要经过明确的初始化链路。首先驱动 TAP(Test Access Port)控制器进入 Test-Logic-Reset 状态——这是 IEEE 1149.1 标准状态机的已知复位点,确保 JTAG 逻辑回到初始状态。随后读取 IDCODE 确认连接,通过 IR(Instruction Register)选择 DMI(Debug Module Interface)访问指令。需要注意,JTAG DTM 是最常见传输方式之一,但 DMI 访问并不从体系上限定只能经 JTAG。
DMI 是 DM 与外部世界之间的地址空间桥梁。调试器通过 DR(Data Register)扫描链向 DMI 写入目标地址和数据,DM 内部根据地址译码访问对应寄存器。链路建立后,读取 dmstatus 确认 DM 当前状态,置位 dmcontrol.dmactive 激活 DM。dmactive 是 DM 的使能开关——置 0 复位整个 DM 内部状态,置 1 后 DM 才能正常响应调试请求。随后通过 hartinfo 获取 Hart 基础信息(trigger 数量、数据宽度等),结合 dmstatus 中的 allhalted/allrunning 位枚举所有可用 Hart。
二、单步调试:dcsr.step 的硬件语义
单步调试的核心是 dcsr(Debug Control and Status Register)中的 step 位。流程分三步:暂停 Hart、配置单步、恢复执行。
调试器先通过 dmcontrol.haltreq 暂停 Hart,轮询 dmstatus.allhalted 确认进入 Debug Mode(DMode)后,置位 dcsr.step 为 1,再发送 dmcontrol.resumereq。此时 Hart 仅执行恰好一条指令便自动重新进入 DMode,而非继续运行。调试器读取 dpc(Debug PC Register)获取当前 PC,读取 GPR 获取寄存器状态,完成一次单步迭代。
三、硬件断点设置:抽象命令与 Program Buffer 的协同
硬件断点设置是 DM 最复杂的工作流程之一,涉及抽象命令(Abstract Command)和程序缓冲区(Program Buffer)的紧密配合。以 KMH 为例,完整流程分四步:
第一步,OpenOCD 发出 halt 命令,经 JTAG Probe → JTAG DTM → DMI 传递到 DM,向 Hart 发出外部 debug 中断。Hart 响应中断后 Trap 到 Debug Module 入口地址,进入 DMode。
第二步,Hart 在 DMode 下执行 Debug ROM 指令,将自身 hartid 写入 HALTED 地址通知 DM “我已暂停”。DM 据此更新 dmstatus 的 allhalted 位。
第三步,调试器配置 trigger 寄存器。由于 trigger CSR(tselect/tdata1/tdata2)属于调试模式 CSR,无法通过 DMI 直接访问,必须借助抽象命令和 Program Buffer。调试器向 command 写入 Access Register 命令,配合 progbuf 注入 csrw 指令——将断点地址写入 tdata2,匹配模式写入 tdata1。progbuf 必须以 ebreak 结尾,执行后 Hart 重新进入 parking loop(Debug ROM 中的等待循环)。
第四步,Hart 恢复执行。当 PC 匹配 trigger CSR 中配置的断点地址时,trigger fire,Hart 再次进入 DMode,断点命中流程完成。
四、内存读写:Hart 视角与总线视角的双通路
DM 提供两条内存访问通路,适用于不同场景。
第一条是 Program Buffer 访问,本质是让 Hart 在 DMode 下代为执行访存指令。调试器向 progbuf 写入 load/store 指令序列,通过 data0-data11 传参和获取结果。例如读取内存时,先将目标地址写入 data0,progbuf 中注入 ld 指令(地址来自 data0),执行后通过抽象命令将结果读回 data 寄存器。这条通路的优势是 Hart 视角访问——使用虚拟地址,经过 MMU 翻译,与软件执行时的内存视图一致。
第二条是 System Bus Access(SBA),调试器配置 sbcs 设置访问参数(数据宽度、地址自增等),写 sbaddress 设置目标地址,读写 sbdata 传输数据。SBA 直接以物理地址访问系统总线,不需要 Hart 参与,适用于 Hart 无法暂停或需大批量读取的场景。
五、多核调试协调:halt group 与 resume group
多核 SoC 中,调试单个 Hart 时往往需要同时暂停或恢复其他 Hart,否则核间交互会导致调试状态混乱。RISC-V 通过 halt group 和 resume group 机制解决此问题。
halt group 的语义是:组内任意 Hart 暂停时,组内其余 Hart 同时暂停。resume group 对称:组内任意 Hart 恢复时,组内其余 Hart 同时恢复。调试器通过 dmcontrol 中的 hasel/hartsel 字段选择目标 Hart,通过配置寄存器将多个 Hart 划入同一 group。
值得关注的是,RISC-V 规范未定义类似 ARM CoreSight 的 cross trigger matrix,通过专门互联矩阵协调 CPU、DSP 等多模块的同时启停。RISC-V 的思路更简洁,通过在group中增加extrigger,通过ext trigger信号是实现 halt group/resume group 在 DM 层面实现多核协同,不引入额外硬件互联,降低了实现复杂度,但是跨子系统调试灵活性不及 CoreSight。
欢迎您关注开芯院公众号
▼
