多内核和多线程SoC带来新的调试挑战
2005 年, MIPS收购了 FS2,满足客户有关调试支持要求 FS2 作为MIPS 科技的一个部门其先的系统级测试、调试和跟踪能力。FS2的在线仪技术为SoC内部工作提供深入系统级可视性这是保证设计成功和加快上市时间的关键。两公司合开发了 PDtrace?(程序和数据踪)和踪,用于MIPS32 4KE24K24KE以及最新的 34K 多线程处理器系列。除了 PDtrace 技术,FS2为嵌入式系统开发及调试提供系统导航( System Navigator)。FS2还为先处理器总线系统级分析IP 和工具。
有经验的开发人员都知道,好的工具支持对于嵌入式产品的实现、调试和维护的成功至关重要。SoC 设计对好的工具支持提出了一系列重要的挑战。一些问题可以归结为 SoC 设计中使用的元件的可存取性及可视性的本质上的下降;另一些则是因为软硬件不断增加的复杂性。尤其是,SoC 设计中更多使用的并发将带来现有工具无法进行调试的问题。这些并发技术并不新,但是在大多数情况下,相对于台式和企业(超级计算)产品来说,并发技术在嵌入式设计中的应用一直十分有限。
开发人员需要及早了解设计过程中的类似问题,以保证他们拥有足以应对产品开发后期出现的这些挑战的工具。基于 SoC 的设计主要依赖于片上调试支持。大多数处理器内核厂商都不自行开发工具。不过,他们仍需提供必要的片上调试支持,以推动这类调试工具的发展。SoC 设计中的并发用来增加各种多媒体、网络和通信产品的性能及可扩展性。多任务处理和多线程是通过 CPU 分时来模拟并发的技术。分时由一个管理多任务或多线程之间上下文转换的调度程序进行控制。进程、任务和线程三个术语有时可以通用,其实三者之间有着细微的区别。任务是可以暂停或重新开始的一连串指令。多任务可以或不可以共享同一个地址空间(即可进入其他任务的文本、数据和整个区域)。一个任务的状态由其上下文表示,它可以“快照(Snapshot)”处理器状态。
快照可记录程序计数器、堆栈相关的寄存器、各种通用寄存器,以及一些与特权模式和异常处理有关的特定内核寄存器。调度程序利用上下文记录延缓和进行任务恢复。单线程是任务的一种实例模式,线程组是相同任务的多实例模式。同组线程共享相同的程序空间和上下文状态。利用线程的优势在于调度程序能够使用一个线程组内的共享上下文更快地执行该组的上下文转换。也就是说,在线程组内的上下文之间进行转换时,调度程序不必占用很多寄存器。某些RTOS厂商仅支持任务模型,而另一些则支持线程模型。
一个进程是运行于一个操作系统的任务,后者采用MMU以防止其他任务进入存储区域。运行于这种操作系统上的线程称为轻量级进程,而且它可以共享虚拟地址空间。轻量级进程可释放存储器保护限制,其行为更像任务。例如,调度程序能够利用共享程序空间,通过将信息传送给指示器而非复制全部信息来加速进程间通信。而且,共享程序空间可更有效利用存储器并提高高速缓存的命中率。这种进程模型是Linux等“全功能”OS采用的模式,它是嵌入式应用的普遍选择。某些像Java这样的平台可提供其自身应用水平的线程调度程序,但这些平台可以简单地将应用线程映射到OS水平的任务或线程。
图1 采用并发多级设计的典型SoC
用于硬件设计的并发技术通过多指令流水线增强性能,从而可减少共享资源的闲置周期(如流水线、总线等),并通过多内核分配处理负载。通常,增加更多内核可比减少时钟频率获得更多的处理能力。多内核SoC包括通用CPU、DSP和专用内核的组合。某些内核可提供多线程支持以增加CPU的利用率,并提高上下文转换的性能。
内核厂商正在用两类多线程微处理器架构进行实验。第一种是为以多虚拟处理器单元而非物理处理器形式出现的OS提供的额外逻辑。这种想法可在当前的线程运行时,利用第二线程实现闲置单元的利用。第二种类型是在当前的线程停止时,通过发出来自不同线程的指令,使用内部调度算法来减少闲置流水线周期。问题在于选择新线程进行转换时,应该采用什么标准?为避免QoS问题,需要采用基于优先权的方法,而且它必须可以由OS调度程序控制。
任何调试的目的都是找出问题所在。为发现多任务处理、多线程、多内核系统的问题,调试人员必须明确任务和线程意识,以帮助开发人员是甄别出哪种问题是由线程执行引起的。他们也要具有并发的执行控制能力和大量的跟踪信息。所有这些功能都需要片上支持来实现。
任务和线程意识
某些调试器具备足够的智能,可以访问存储器OS核的数据结构,或者利用由OS提供的API找回任务线程状态信息。这种方法限于特定 OS的情况,在执行暂停时,它只能访问状态信息。任务和线程意识非常必要,这样才能支持特定线程和特定任务的断点。大多数具备线程支持(如GDB)的调试器都采用“全或非”的方法。也就是说,当特定线程断点被触发时,其整个线程组都会延缓执行。在选择线程暂停时,这将避免出现共享数据变化的问题。当执行继续时,它可重新启动组内所有线程。紧接组中其他线程的所选线程的单步执行需要OS的支持。要停止特定线程断点的内核是困难的,因为线程组共享一套相同的指令。一个指令断点将引发组内执行相同指令的内核停止所有线程的执行。为了停止特定线程,调试器需要重启断点停止的线程。该内核会提供更复杂的断点,它可以触发特定任务或线程的指令地址。例如,当OS调度程序执行上下文转换时,它可以更改寄存器,以辨别哪个任务或线程是活跃的。某些调试器可通过活跃的虚拟地址执行特定进程断点。但是这对共享地址空间的任务和线程不起作用。
在指令跟踪日志中集成更多任务和线程意识非常有益(见图2)。跟踪日志只显示哪个指令在某个点的及时执行情况。没有与跟踪帧(trace frames)关联的任务id或线程id。而且,如果OS调度程序通知内核现在哪个任务或线程对上下文转换是活跃的,那么跟踪块就可能访问该信息,并在跟踪日志中记录。通常,唯一可选择的方法是利用侵入式而不必提供组装级跟踪信息的仪器技术。
图2 三个线程执行相同片段的代码
调试并发的编程问题
大多数并发的编程问题可以归结为共享资源访问缺乏适当的同步(如:CPU和总线周期、存储器和其他器件)。问题的表现形式是数据毁损、竞态条件、死锁、停顿和资源耗尽。这些问题的发生通常都是不可预知的,也是难以复制的。比如,以相同的输入执行相同代码可产生不同的结果,或在不同的完成时间产生相同的结果。这些是对传统的源代码调试技术的挑战,例如“通过代码单步执行”,这是极具侵入性且专注于单线程执行的技术。
结果,可以解决这类问题的最常见的解决方案是“使用printfs”或基于事件日志技术的软件方法。工具厂商声称,他们的仪器技术可增加大约2-5%的系统开销。在大多数情况下,这些技术是足够的,系统开销也是可接受的。但是,高度优化的架构代码仪器技术对流水线、高速缓存行为做出了不可接受的改变,而且增加了更多非确定性的副作用。不幸的是,随着SoC设计在处理速度和并发程度方面的继续增加,这种情况变得越发普遍。可供选择的方法是采用非侵入式实时跟踪调试技术。此外,在跟踪调试技术中增加上下文转换事件以确定哪个任务或线程是活跃的方法也非常有用。
采用并发技术的目的是增加资源利用率(即减少闲置周期),需要利用由内核提供的不同的计数器和统计表(如高速缓存命中/未命中、CPU/流水线停顿)来测量这些技术的有效性。
多线程内核的调试
大多数开发人员已经意识到多内核可存取性的问题,并利用JTAG扫描链接提供探测器访问所有内核。同样,工具厂商也在修改其IDE以支持多个异型内核的并发调试,因此工程师不必再使用多个调试器。为解决与多内核有关的同步问题,我们需要并发执行控制。例如,如果内核提供可以通知其他内核停止的外部引脚,就可以执行多于一个所选内核的同步断点的停止。当一个内核发生断点时,它将通知该引脚停止其他内核。这时调试器试图通过JTAG接口发出停止命令,仿真同步断点。但是,这又会产生严重的延迟问题。
多内核对跟踪支持是新的挑战。理想的情况是,每个内核都希望有一个片上跟踪缓冲器。不幸的是,这也许代价太高,而且如果是异型内核或以不同时钟频率运行,那么相关的跟踪转储就比较麻烦。相关的跟踪转储对调试SMP等架构很有用。该架构中的一个内核能够由另外等待共享资源的内核来停止。但是相关性需要普通的时基或参考点。普通时基可以通过一个内核定期向其他内核发送的信号复位(同步)内部计数器来实现。另一种选择是,单芯片跟踪缓冲器可以与内核或探测器内部的一个离线跟踪缓冲器共享(见图3)。在后者的情况下,探测器能够为时戳提供时基。
图3 跟踪捕捉的可能性配置
结语
我们已经看到,如何通过增加片上支持来实现更先进的并发调试功能。那么,为什么如今有这么多SoC以很少或没有片上支持来生产呢?答案是成本和上市时间的压力。片上调试支持来之不易。它可增加成本和芯片的复杂性。为解决这些问题,现在一些厂商以IP块的形式提供片上调试支持。最终,制造成本将会下降,利用内部调试支持生产芯片将变得更为实际。目前,开发人员必须通过采用像仪器这样的调试技术。但是,随着SoC设计的复杂性和性能的增加,这些技术最终将变得太富于侵入性。结果是,随着OS调度程序以硬件方式提供的上下文转换支持,调试支持将越来越多地从软件转移到硬件,而硬件将变得更具有任务和线程意识。
评论