嵌入式系统除错的工作量,可望占了嵌入式软体专案工作量的一半以上。了解硬体除错功能以及它们所能解决的问题,是选择正确的晶片、建立除错系统以及提高生产效率的关键。
很多人都相信"臭虫(bug)"此一用词起源于海军少将 Grace Murray Hopper。但事实上,没人知道这种用法的真正来源,它可能可以回溯到 Thomas A Edison,或甚至更早。美国海军后备役军官 Hopper 在 1945 年 9 月 9 日于哈佛大学对 Mark II Aiken 继电计算器( Relay Calculator)进行测试时,于短路的 Panel F #70 号继电器处发现了一双臭虫。她把这只臭虫拿掉后(就是对电脑进行除臭虫(debugging)),就解决了这台机器不定时关机的故障(参考文献 1)。虽然这个早期的例子指的是从系统硬体中去除臭虫,但今天"debugging"这个词是指发现一个程式的问题,并去掉缺陷代码(defective code)的过程。这些问题包括任何与原始意向的差异,而去掉缺陷代码的做法则远优于增加纠正代码(correction code)。理想情况下,在任何时间都可以获得所有的特性(如系统的匯流排与暂存器值),供监控与修改。但随着 IC 转向 SoC,可存取性(accessibility)就变得更加困难了。
对硬体除错,是要尽可能地可以存取到嵌入式系统内部资源,这可以透过观察系统的特性,如 CPU 状态和 PC 值,或修改系统的参数。在嵌入式系统设计的早期可以用简单技术进行除错,如记录(logging)与监控,或者对多核心 SoC而言,可以采用最新开发的方法,如跟踪、缓衡记忆体除错(cache debugging)和交叉触发(cross-triggering)等。本文的重点不是在如何编写代码或为代码除错,而是描述现在可用的嵌入式硬体硅智财权(intellectual property ,IP),以及该 IP 能解决哪方面的除错问题。
记录与监控
最古老也是最常用的除错方法就是在代码中增加一些列印叙述(print statement),它可以显示软体某个部分的执行资讯,并提供暂存器和变数的实际值。这可能是一年级学生练习"hello world"的一种延续,该程式会在荧幕上显示这两个词,用于表示程式能够运行,以及执行到了某个点。列印叙述(或 printf 语句)只是记录的一种变型,它是用处理器将重要资讯写入一个"管道(pipe)",作为外部跟踪的过程。所使用的管道将视系统而订,在 printf 情况下,管道是标准的输出(荧幕),但它也可以是 UART、USB,甚至是通用 I/O。
当你需要用一种对程式设计工程师有意义的方式,组织各个部分资讯时(例如提供感测器资讯或状态机的转换),采用记录的方法是很有价值的。可以用记录工具来分析记录的资讯,并产生一个后处理资料库。必须小心使用记录功能才能获得高效率。例如,记录资讯应使用关键字开头,如"警告"、"错误"或"除错"等,并应能识别出资讯的建立者。应将记录功能划分为一小组档案,以便于维护,并提供时间戳资讯。不幸的是,记录是一种侵入性的方法,它会修改软体的即时状态,因而不同于最终应用。
除错监控器是另一种常见的除错工具,它与运行在 CPU 记忆体中的目标代码一起工作(参考文献 2 和参考文献 3)。除错器运行在一台主机上,它透过一个专用的埠发送指令和接收响应,从而达到与监控器通信的目的。可以将 Linux 上的 gdbserver 程式当作一个除错监控器,不过它比早期的 ROM 监控器更复杂
(图 1)。
当使用者希望在某条指令处设置一个中断点时,gdbserver 会保存该指令,而用一个系统呼叫(system call)来替代它。Gdbserver 然后用 Linux 的 ptrace 程式获得所有进行系统呼叫的应用程式资讯。接着,当发生系统呼叫而啟动中断点时,gdbserver 可以取得对被除错应用的控制。除错器运行在一台主控机上,透过串列埠或乙太网路连接到目标物件(参考文献 4)。监控器的方法既便宜又实用,但也有一些缺点,例如需要在进行任何除错前载入代码,并可能与应用软体相互影响。如果代码位于快闪记忆体中就不能使用监控器软体,因为要插入软体中断点就需要修补应用软体。
内电路模拟
内电路模拟器(in-circuit emulator ,ICE)是第一种以硬体为基础的除错技术,它是所除错处理器的一个版本。ICE 通常使用一颗现场可编程闸阵列(field-programmable gate array,FPGA)。FPGA 外合(bond out)其内部匯流排和状态信号,而让使用者可以使用它们(图 2)。
ICE 提供的除错功能多于 ROM 监控器。使用 ICE 时,必须用一个连接到 ICE 盒的连接替换待除错电路板上的处理器。一个运行并模拟除错器功能的主机控制这个 ICE 盒。ICE 的主要局限性之一是它的价格昂贵。另外,虽然这个方法很适合简单的处理器,但现代 SoC 有更高的复杂性、整合度和频率,因此 IC 供应商难以为现代处理器提供 ICE 版本。
一些欧洲公司在1985 年成立了联合测试活动组(Joint Test Action Group,JTAG),这个联盟尝试要解决测试半导体 IC 的各种问题。它们为 IC 的边界扫描测试建立了 IEEE 1149.1 标准,并在 1990 年公布了此一标准(参考文献 5 和 图 3)。
JTAG 标准定义了一个有限的 I/O JTAG 埠,有多达五个信号,透过串列通信完成电路的测试与分析:测试时鐘(test clock,TCK)、测试模式选择(test-mode select,TMS)、可选的测试重定(test reset, TRST)、测试资料登录(test-data in ,TDI)和 测试资料输出(test-data out,TDO)。
IEEE 将 JTAG 硬体建立在一个 16 态的有限状态(finite-state)机上,并由 TMS 信号控制。TCK的上升沿时鐘(rising- edge clock)擷取到此一 TMS 信号。资料资讯在TDI 垫(TDI pad)移入,并在TDO 垫移出。最后用 TRST 来对设计重新设定。IC 的每个垫都可以增加扫描暂存器,并将它们内部连接起来构成一个边界扫描链。可以透过 TDI/TDO 和 JTAG 命令将此链移入和移出,以测试电路板上的外部连接,测试 IC 内部的逻辑连接,擷取 IC 垫的值,并将 JTAG 置于旁路模式。JTAG 提供了低成本的製造测试功能,并成为最常用的测试方法。但是,由于它有易于使用、高可用性和低成本实现的特点,设计者经常会把 JTAG 当作除错埠,以存取片上的除错资源(参考文献 6)。
JTAG 是除错通信的传输层,位于运行在主机上的除错器与嵌入式处理器除错资源之间。命令被移入命令暂存器,以存取除错中的硬体 IP。由于 ICE 增加了成本,很多半导体供应商将更多除错硬体整合在晶片上,以解决除错限制的问题,并提供与 ICE 相似的功能。片上除错硬体的一种常见实现是飞思卡尔半导体公司在 68-kbit Coldfire 嵌入式处理器和 PowerPC(现在是 Power 架构)处理器上的背景除错模式(background debugger mode ,BDM)。其他供应商也有相似功能的专有名称。
片上除错硬体增加了一些功能,如硬体中断点、内部暂存器存取、读/写到记忆体,以及观察点(watchpoint)等,这些功能以前只能透过 ICE 使用。在一个多处理器 SoC 中,可以将每块晶片除错硬体连接到主 JTAG 控制器。不同供应商的连接可能有所不同,但典型的作法是在片上除错 JTAG 状态机和主除错器之间建立起一个 TDI-TDO JTAG 链(图 4)。
跟踪
即时系统除错中最大的问题之一就是海森堡的臭虫(Heisenberg bug),或探针效应(probe effect):为除错或监控而增加的任何软体或硬体都可能改变即时系统的行为。当增加用于剖析、除错或监控资讯的软体时,就会发生这种情况。使用除错硬体时也会有类似的影响。例如,片上除错硬体可能将处理器的执行流程修改到插入中断点处,或者用于剖析的硬体可能"偷取(steal)"从处理器到记忆体的部分频宽,以便用来保存剖析的资讯。
除错硬体还可能以 UART 连接来记录资讯,而产品软体也可能使用这个介面。ICE 通常提供非侵入式的跟踪功能,开发者可以在任何时候存取处理器的程式计数器。越来越多的 SoC 供应商正在把跟踪硬体整合到晶片上,以提供类似的功能(图 5)。
透过专用的跟踪硬体、专用的跟踪埠、跟踪资料与处理器资料分离的资料匯流排,以及 JTAG 介面,使跟踪硬体得以实现非侵入式的跟踪。
使用者可以利用一个跟踪埠来擷取跟踪资讯。在这种方式下,将一个跟踪盒或逻辑分析仪连接到跟踪埠,以重建资讯并让它们与原始码建立关联性。另一种擷取跟踪资讯的方法是使用一个虚拟跟踪缓衝记忆体,处理器记忆体将跟踪资讯存入缓衝记忆体,而当测试结束时由主机(例如透过 JTAG 埠)来检索(retrieve)这些资讯。第叁种方法是使用一个专用的跟踪缓衝记忆体,专门用来保存跟踪资讯,这可以让主机在测试结束时检索。
全球嵌入式处理器除错介面(Global Embedded Processor Debug Interface)的IEEE-ISTO 5001TM 2003 Nexus 5001TM 论坛标准可为嵌入式处理器的软体发展和除错提供一个开放而通用的介面(参考文献 7)。Nexus Forum 在 1998 年开始运作,在1999 年发表了第一个 Nexus 标准,并在 2003 年做了更新。其目标是,在嵌入式系统除错与工具领域多个供应商的经验基础上,实现片上除错功能与介面的标准化。由于该论坛的多个供应商已经为片上除错提供了专有产品,因此基本的片上除错要求已经足以轻鬆地达到 Nexus 的符合性。好处是 Nexus 跟踪介面的标准化,例如跟踪功能、信号、消息协定和应用编程介面(application-programming interface,API)。同时,标准化还为供应商的定制化提供充分的空间。Nexus 标准最初是针对汽车应用的,现已快速扩充到无线与网路市场。
多核心除错
缓衡记忆体能提供高性能,但却难以除错,因为它们将 CPU 的执行情况与外部记忆体匯流排隐藏起来,并且难以了解到核心与 DMA 或加速硬体之间的一致性。嵌入式跟踪硬体有助于解决这个问题,因为被跟踪的匯流排通常是虚拟的(在缓衡记忆体以前),而非实体的。跟踪缓衝记忆体前、后的匯流排也非常有助于更完整地了解缓衡记忆体的行为(图 6)。
两个跟踪结果的比较,能够提供快取失败(cache miss)的良好指示,此时缓衡记忆体造成对实体匯流排的存取。这种方法有助于减少快取失败,并提高软体的性能。另一种方法是增加嵌入式的缓衡记忆体除错硬体,在除错模式下读取缓衡记忆体内容,或写入缓衡记忆体。这种方法通常采用缓衡记忆体除错暂存器形式,并透过软体或 JTAG 埠存取。使用者使用除错器可以暂停程式的执行,并检查缓衡记忆体的内容。使用者可以用这个资讯解决各种缓衡记忆体清除问题,如无效、同步或溢出。
现代 SoC 经常在一颗晶片内整合多个处理器,用传统的除错硬体难以对多个核心之间的互通性进行除错。有一种最新出现的除错技术叫交叉触发(Cross-triggering),它成为对复杂多核心 SoC 除错的常用方法(参考文献 8)。该方法的原理是将一个核心域的事件转换到其他核心域或相同核心域的产生触发器(generate trigger)上。典型事件是进入除错模式、发生中断、出现观察点,以及出现中断点。输入触发器一般是除错要求。触发器产生一个除错要求、一个中断,或一个 SoC 垫(SOC pad)的突波杂讯(glitch)。它们都可以啟动或停止处理器上的跟踪。触发器的组合将所有弹性留给了最终使用者,使他们能够设计出复杂的除错序列。可以在 Core B 到达某个程式位址时,用交叉触发器啟动 Core A 上的一个跟踪,或当 Core A 进入除错时,停止 Core B 的活动。
随着对 SoC 尺寸压力不断地增加,低成本除错可能成为嵌入式系统架构的圣杯。但是必须牢记一件重要的事:永远不要在系统的除错能力方面作出妥协。如果不能预先处理好系统的所有臭虫,那么以后就无法获得更大发现问题的机会。在减少嵌入式除错硬体上所节省的成本,其代价可能是在专案后期付出更高昂的软体除错成本。另外还应记得,除错与安全两种要求是相互矛盾的。很多制造商现在交付产品时只是简单地关掉除错功能来防止骇客的攻击。这种做法是不明智的。你永远不可能预期会在现场遇到什么问题。比较好的做法是采用各种安全方式防止进入除错部分,如用密钥或熔丝(fuse),这样才不会危及你的除错功能。