嵌入式系统中的硬件断点和软件断点

5

我的理解是,插入软件断点涉及将要执行的代码中的下一条指令替换为软件中断指令,当CPU到达该指令时会导致CPU停止运行。

硬件断点则涉及将断点后面要执行的下一条指令地址放入寄存器中,当地址匹配时,通过使用硬件比较器,这会导致CPU停止运行(如有错误请纠正)。

我所困惑的是,我们只在使用硬件调试器通过JTAG等调试板时才使用硬件断点吗?还是JTAG也可以使用软件断点?

GDB只能与软件断点一起使用吗,还是它也可以与JTAG一起使用?如果问题太广泛,请原谅。

2个回答

8

断点定义

在本文的语境中,我们一起确认一下断点的统一定义。这里讨论的断点是程序位置,在这些位置我们希望处理器停止运行,以便我们进行某种形式的调试。虽然可能存在其他类型的断点,例如由数据访问触发的断点,但是在本文中,我们仅讨论程序断点,即我们希望在应用程序代码中遇到每个代码时都停止。

硬件断点与软件断点

硬件断点和软件断点的区别是什么?显而易见的答案是“硬件断点是通过硬件实现的”,“软件断点是通过软件实现的”。但这具体意味着什么,有什么影响?我为什么要选择一个而不是另一个?

硬件断点

硬件断点是通过集成到设备中的特殊逻辑实现的。您可以将硬件断点视为连接到程序地址总线的可编程比较器集合。这些比较器以特定地址值进行编程。当代码执行时,如果程序地址总线上的所有位与比较器中编程的位匹配,则硬件断点逻辑会向CPU生成一个停止信号。 使用硬件断点的优点是它可以在任何类型的内存中使用。这可能在讨论软件断点之后更有意义。当我们讨论软件断点时,我们将发现它们只能在易失性内存中使用。硬件断点可以在RAM或ROM中执行代码时使用,因为对于硬件断点逻辑来说,没有区别。它只是在PAB上匹配一个地址,并在找到一个地址时停止CPU。 硬件断点的缺点是,由于它们是硬件实现的,可用数量有限。不同架构的HWBP可用数量不同,但在大多数情况下只有2-8个可用。确定设备有多少个最简单的方法是在CCS中连接到它并不断设置HWBP,直到收到没有可用的错误消息。
软件断点
正如提到的那样,软件断点是在软件中实现的。但是它是如何实现的呢?实际上有两种不同的实现方式。 一些设备在其操作码定义中保留了指示软件断点的特定位。例如,在C6000系列的某个体系结构中,所有指令都是32位长,第28位保留为指示软件断点的位,因此该指令集中的所有指令都将第28位作为零。在这种情况下,当在CCS中设置软件断点时,它实际上会修改该位置处指令的操作码,并将第28位设置为1。仿真逻辑随后监视程序操作码,每当第28位为1时便会停止CPU。需要注意的是,这是少数情况。大多数体系结构都不是这样做的。原因是它限制了指令集的灵活性。此外,对于具有可变长度指令的体系结构,它也无法工作,因此还限制了代码密度。 更流行的实现软件断点的方法也要复杂得多。在这种情况下,有一个指定的断点操作码。通常,操作码为8位。每当设置断点时,该位置处指令的前导8位将被删除并替换为这个8位断点操作码。然后,指令的原始8位将存储在断点表中。每当遇到断点时,CPU就会停止,CCS会将断点操作码替换为指令的原始8位。当重新启动执行时,CCS必须进行一些技巧性处理,因为实际CPU流水线中的指令不正确。它仍然具有断点操作码。因此,CCS会清除CPU流水线,然后将待处理的指令重新获取到其原始状态,下一个要执行的函数是设置断点的函数。同时,CCS会使用断点操作码重新加载该位置处的指令,以便下次再遇到这段代码时,它将再次停止。 SWBP的优点是可以有无限数量,因此您可以将它们放置在任意位置。缺点是您不能将它们放置在非易失性内存中,例如ROM / FLASH等,因为CCS无法将操作码写入该位置。

http://processors.wiki.ti.com/index.php/How_Do_Breakpoints_Work


1
这是逐字从您提供的链接复制的。 - ItamarG3

5

"如果我错了,请纠正我"

断点是设置在指令上而不是指令之后 - 断点发生在指令执行之前而非之后 - 否则在跳转、调用或分支指令上设置断点将会失败。

"当使用硬件调试器通过JTAG来调试板子时,我们只能使用硬件断点吗?"

JTAG是芯片内部调试的简单通信接口(也用于其他用途,例如电路中的存储器和FPGA编程以及边界扫描等)。

虽然在不同架构上可能有所不同,例如在ARM Cortex-M上,您可以从目标代码访问芯片内部调试寄存器并设置硬件断点。同时,您还可以使用BKPT指令(而不是SWI)在代码中放置软件断点。

或者JTAG也可以使用软件断点吗?

正如我所说,JTAG只是芯片内部调试的通信接口,但是通过芯片内部调试,您可以直接设置任何RAM内容,因此开发主机上运行的JTAG连接的调试器软件可以临时修改RAM中的代码以设置软件断点(通过用BKPT替换目标指令,然后当断点被触发时,恢复回原来的指令以便执行)。对于从ROM运行的代码,软件断点并不是那么简单,尽管一些调试器支持无限制的ROM断点(需要付费)- 这类硬件的制造商不一定会公布他们所使用的方法。

在JTAG和芯片内部调试变得广泛可用之前,采用了In-Circuit Emulation和ROM仿真等技术。这些通常是昂贵且复杂的解决方案。

"GDB只能与软件断点一起使用,还是可以与JTAG一起使用?"

GDB可以用多种方式。它需要一个“调试 stub” - 一个将调试器软件映射到可用硬件的软件层 - 其性质取决于所使用的调试接口和目标设备。例如,当使用UART或Ethernet端口时,stub实际上是在目标本身上运行的代码(例如Linux的gdbserver)。在这种情况下,由于软件错误可能会阻止调试端口驱动程序实际运行,因此它不太可靠,特别是在缺乏MMU保护的目标中。更简单的JTAG设备通过在开发主机上运行的stub与GDB进行接口,例如常用的OpenOCD软件。更昂贵的JTAG调试硬件可能会在JTAG硬件本身上运行stub - 例如Abatron的BDI2000。无论哪种方式,调试 stub都将能够使用硬件和软件断点,具体取决于目标的功能。


谢谢。就单步调试代码而言,这该怎么做呢? - Engineer999
@Engineer999 单步执行指令级别是芯片内置调试单元的功能。跨越和跳出功能使用临时设置断点。源代码级别的单步执行将根据需要使用一系列指令步骤和断点。 - Clifford

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接