解释On-Chip Debug和Off-Chip Debug

本文给一位同事解释一下On-Chip和Off-Chip Debug两个概念的区别。

我们大部分做软件的,比较熟悉的是On-Chip的调试过程。这种模式只需要CPU提供Trap能力,不需要额外的功能,所以,系统做这种功能,实现CPU的工程师不一定会知道。比如我有一个应用程序A需要调试,在某个地方需要一个断点,我只需要在那个地方放一个非法指令,A运行到这个地方就会Trap到内核中,这时CPU并没有停止运行,CPU也不知道现在正在调试状态,这个状态属于软件。内核(软件)发现这是一个自己设置的断点,它可以直接调度给调试器(比如GDB),GDB可以通过内核获得A的相关状态,和用户UI进行交互。如果用户UI要求它单步,它可以让内核在A的内存中修改原来放置非法指令的位置,然后恢复A的运行状态,A就会执行一步,然后trap到下一个指令上,重复上面的过程……

这种调试,调试器和被调试的程序都在同一个CPU上。CPU从来没有停止过,它可能可以增加一些硬件支持,比如增加专门的break指令(这样内核不需要区分这是调试还是真的非法),增加Access break trap之类的,但本质上,CPU其实不知道自己被调试,是软件在控制这个调试的过程。

上面这种模式,就称为On-Chip Debug。它的优点是对硬件没有什么依赖,缺点是这是一种“假调试”,因为被调试的进程并没有真的停下来,相关的时钟寄存器,指令计数,乃至其他配合的进程和内核等等东西都是继续在变化的,而且需要高一层的调试程序(比如这里的内核)去“管理”被调试的程序的。对于很原始的软件逻辑,比如bootloader,硬件初始化等,这种调试常常没法用(因为管理程序的环境可能都无法准备)。

为了解决这个问题,就相应提出Off-Chip的调试模式了。Off-Chip Debug就完全是个硬件功能了。硬件在遇到特定的断点指令的时候,直接进入Debug Mode,会整个核都停下来,然后对外发硬件信号,让一个外部的硬件来完成这个调试过程,之后,只有这个外部的硬件发信号让它继续,这个硬件核才会继续运行下去。

这种情况,那个被调试的核是真的停下来的,调试器不能运行在被调试的核上。你必须有另外一条设备(比如另一个核,或者通过JTag连接的另一台计算机)运行调试器,然后发命令给被调试的核,恢复它的工作状态。

比如下面是RISCV的Off-Chip的原理图(来源:content.riscv.org/wp-co):

很明显,可以看到,它不是定义和软件的接口的,它其实定义的是核的硬件和片上的Debug Module的接口的。

补充一句,虽然这个接口是个硬件接口,但这个调试的目的还是软件,而不是硬件,它提供更容易调试关键代码的能力(比如Bootloader,系统调用,中断等)。

关键在于,对于后面这种模式,我们应该更关注核停下来以后,核被访问的外部接口是怎么设计的——DM可以访问内存,这个部分好办,但由于它不在核上,核需要给它接口让它可以访问Register Files,这是和On-Chip调试在接口描述上最不一样的地方。

编辑于 2019-07-23

文章被以下专栏收录