1. E203调试架构概览

调试,说白了就是让芯片在运行时能被我们“看穿”。我刚开始接触RISC-V调试时,总觉得这东西离验证很远——直到有一次,一个bug在仿真里死活复现不了,最后靠JTAG单步才抓到问题。从那以后,我对调试子系统的态度就变了。

这一章,我们先搭个框架。把调试子系统在SoC里到底长什么样、怎么切换模式、JTAG和DM是什么关系,一次性说清楚。

1.1 调试子系统在SoC中的位置

蜂鸟E203的调试子系统,不是孤立的模块。它嵌在Core和总线之间,像一个“监听者”。

我习惯把它的位置画成下面这样:

E203调试子系统在SoC中的位置 JTAG 接口 调试模块 (DM) 调试寄存器 + 状态机 E203 Core RISC-V 处理器核 系统总线 ICB / AHB-Lite 外设 / 内存 外设 / 内存 调试子系统位于Core与总线之间 通过JTAG从外部访问

从图上你能看到:JTAG从芯片外部接入,经过DM(调试模块),再分别连到Core和总线。DM是核心枢纽,它决定了调试器能不能“看到”CPU内部。

关键点:DM不直接挂在总线上,而是通过专用调试接口与Core交互。这样做的好处是——调试时不会干扰正常的总线事务。

1.2 调试模式与运行模式切换

E203的Core有两种工作状态:运行模式和调试模式。说白了,就是“正常干活”和“停下来被检查”的区别。

切换过程是这样的:

步骤 动作 说明
1 调试器通过JTAG发送请求 比如写DM的control寄存器
2 DM向Core发出halt信号 Core在指令边界停下
3 Core进入调试模式 PC冻结,流水线暂停
4 调试器读写GPR/CSR/内存 通过DM的抽象命令
5 调试器发送resume请求 Core退出调试模式,继续执行

嗯,这里要注意:Core进入调试模式后,中断响应行为会变化。我记得有一次调试时,发现中断没被响应,查了半天才发现Core停在调试模式里,中断被屏蔽了。所以,调试时如果发现中断异常,先看看Core是不是在调试模式。

我的习惯:在验证环境中,我会在测试用例里显式检查Core的调试状态寄存器。如果预期是运行模式但读出来是调试模式,那大概率是DM状态机出了问题。

1.3 JTAG与调试模块(DM)的基本概念

JTAG是物理层的通信协议,DM是逻辑层的控制单元。两者分工明确:

  • JTAG:负责把调试器的命令“搬”进芯片。它只有4根线(TCK、TMS、TDI、TDO),通过状态机实现串行通信。
  • DM:负责解析JTAG送进来的命令,然后操作Core或总线。它内部有一组寄存器,调试器通过读写这些寄存器来控制调试流程。

我画了个简图,帮你理解它们的关系:

JTAG 与 DM 的关系 调试器 (GDB/OpenOCD) JTAG 接口 调试模块 (DM) E203 Core JTAG:物理层,串行传输 DM:逻辑层,命令解析 两者通过JTAG-DP连接

DM内部有几个关键寄存器,我列个表:

寄存器 地址 作用
dmcontrol 0x10 控制halt/resume、复位等
dmstatus 0x11 反映当前调试状态
abstractcs 0x16 抽象命令状态与控制
command 0x17 发送抽象命令(读写寄存器/内存)
data0~data11 0x04~0x0F 抽象命令的数据缓冲区

避坑指南:我曾经在调试时,连续发送抽象命令没检查abstractcs的busy位,结果命令被丢弃,调试器以为成功了,实际上Core根本没反应。所以,每次发命令前,一定要轮询busy位,等它空闲再发下一条。

JTAG的TAP状态机我就不展开了,你只需要知道:通过TMS信号切换状态,最终进入Shift-DR或Shift-IR,就能读写DM的寄存器。E203的JTAG实现里,IR(指令寄存器)长度是5位,DR(数据寄存器)长度可变。

举个例子,读dmstatus的流程:

// 伪代码:通过JTAG读dmstatus
1. 进入Shift-IR状态,发送5位指令 0x11 (选择dmstatus)
2. 进入Shift-DR状态,移位读出32位数据
3. 返回Run-Test/Idle状态

嗯,这里要注意:JTAG的时钟TCK和Core的主时钟是异步的。所以DM内部有同步处理,避免跨时钟域问题。我在验证时,会特意在TCK和Core时钟之间插入随机相位偏移,确保同步逻辑能覆盖所有边界情况。

总结一下:调试子系统 = JTAG(物理通路)+ DM(控制中枢)+ Core(被调试对象)。三者配合,才能实现单步、断点、寄存器读写等调试功能。