有经验的开发人员都知道,要保证嵌入式产品的成功实现、调试和维护,好的工具支持是非常关键的。SoC(系统芯片)设计带来了一系列新的问题,使好的工具支持变得更加关键。部分问题可以归因于 SoC 设计使用的部件,其可访问性和可视性本来就有所下降;其它原因则可归因于在硬件和软件方面复杂性的增加。
另外,SoC 设计中越来越多地使用并行技术也带来许多问题,使当前一代的工具不再适合帮助调试。这些并行技术本身并没有什么新鲜东西,但是,在以往大多数情况下,它们更多地用于桌面或企业级(超级计算)产品中,相对而言在嵌入式设计中的应用有限。
开发人员必须尽可能在设计过程的早期就意识到这些问题,以保证他们能在以后的产品开发周期内,拥有必要的工具去应付这些挑战。基于 SoC 的设计极大地依赖于片上调试的支持。多数处理器内核供应商自己并不开发工具。但是他们仍须提供片上调试支持,以方便这类调试工具的开发。
在 SoC 设计中使用并行技术,可以提高各种多媒体、网络和通信产品的性能与可伸缩性。多任务和多线程是通过 CPU 分时共享(time-sharing)来模拟并行的技术。分时共享受一个调度程序的控制,该程序用于管理多个任务或多个线程之间的上下文切换。进程、任务和线程这三个词有时可以互换使用,但它们之间也有细微的差别。
一个任务是一个指令的序列,它可以被挂起(suspend)或恢复(resume)。多个任务可以共享或可以不共享同一地址空间(例如,其它任务可以访问该文本、数据和全局区域)。任务的状态由其任务环境来表示,它是处理器状态的一个快照。这个快照记录了许多部件的数值,如程序计数器、堆栈相关的寄存器、各个通用寄存器以及一些用于特权方式和例外处理的内核专用寄存器等。调度程序使用上下文记录来完成任务的挂起和恢复操作。
一个线程是某个任务的一个实例。线程组则是同一任务的多个实例。同一组里的所有线程都共享同一个程序空间和某些环境状态。使用线程的好处是:调度程序可以在一个线程组中使用共享的上下文,使该组的上下文切换速度更快。就是说,调度程序不必保存许多寄存器的值,就可以在一个线程组的上下文间进行切换。有些 RTOS 供应商只支持任务模型,而有些则支持线程模型。
一个进程是运行在某个操作系统上的一个任务,它使用一个 MMU 来防止其它任务访问自己的内存区。运行在这个操作系统上的线程被称为轻量进程(或低权进程),它们是共享虚拟地址空间的。轻量进程放松了对内存保护的约束,因而表现得更像任务。例如:调度程序可以采取把指针传给消息,而不是复制整个消息的办法,以利用共享程序空间来提高进程间通信(IPC)的速度。
并且,共享的程序空间提高了内存的使用效率,增加了高速缓存的命中率。进程模型被用在"全功能的"操作系统上,如 Linux,它已成为嵌入应用中的流行选择。某些平台(如 Java)提供自己的应用级线程调度程序,但它们只是简单地把自己的应用线程映射到操作系统级的任务或线程上。
硬件设计中使用诸如多指令流水线、减少共享资源(如流水线、总线等)的空闲周期,以及将处理负荷分配到多个内核等方法的并行技术,增强了产品的性能。一般而言,提升处理能力的更简单方法是增加更多的内核,而不是提高时钟速度。多内核 SoC 由一些通用 CPU、DSP 和专用内核组合而成。有些内核提供多线程支持,以增加 CPU 使用率和提升上下文切换的性能。
内核供应商也正在试验两种多线程微处理器架构。第一种提供了额外的逻辑,它使操作系统看来象运行在多个虚拟的处理器单元上,而不是一个物理处理器。这种构想可以在当前线程处于运行状态时,让空闲单元被第二个线程来使用。第二种类型使用内部调度算法,以减少空闲的流水线周期,其方法是在当前线程停止时,从另一个不同的线程发出指令。问题是应该用什么标准来选择要切换到哪个新线程?为避免 QoS 问题,需要有一个基于优先权的方案,该方案必须能由操作系统的调度程序控制。
图1 三个线程正执行同样的代码片断。如果没有增强的包含上下文切换信息的跟踪支持,很难看出日志里各个框中哪一个线程正在执行。
任何调试工作的目的都是要搞清楚正在发生什么事。为了在一个多任务、多线程、多内核的系统内达到这个目的,调试器必须具有任务感知(task-aware)和线程感知thread-aware)的能力,以帮助开发人员隔离出导致问题的那些执行线程。调试器还应提供对并行执行的控制能力,以及更多有用的跟踪信息。所有这些功能的实现都需要片上支持。
任务和线程感知
有些调试器具有足够的智能,可以访问内存中操作系统的核心数据结构,或者使用操作系统提供的 API 来获取任务和线程状态的信息。这种方法的应用范围有限,因为它是针对专门操作系统的,并且只有当执行被挂起时才能访问状态信息。为了能支持任务特定的断点和线程特定的断点,任务感知和线程感知能力是很有必要的。
多数带线程支持的调试器(如 GDB)使用一种"全有"或"全无"的方法。即,当一个线程特定的断点被触发时,它会把整个线程组的执行都挂起。这可以防止出现当所选线程暂停时共享数据发生改变的问题。当执行恢复时,它将重新起动组中的所有线程。单步运行所选的与组中其它线程一起处于锁步(lockstep)状态的线程需要操作系统的支持。
为一个线程特定的断点而中断内核的运行非常困难,因为整个线程组共享同一个指令集。一个指令断点将使内核为执行同一指令的线程组中的任何线程而停止执行。如果要只对一个特定线程中断运行,调试器必须重新起动禁止了断点的线程。内核要能提供更精密的断点,它只在某个特定的任务或线程的指令地址上才被触发。
例如,当操作系统调度程序进行上下文切换时,它可能要修改一个寄存器,以识别哪个任务或线程处于活动状态。有些调试器通过查看活动的虚拟地址,从而实现进程特定的断点。但这种方法对任务和线程无效,因为它们可以共享地址空间。
把更多的任务感知和线程感知加到指令跟踪日志中也是有益的(见图 1)。跟踪日志只显示某个时间点有哪些指令在执行,没有显示与跟踪框关联的任务标识符和线程标识符。并且,如果操作系统调度程序通知内核哪个任务或线程对于某个上下文切换是活动的,则跟踪块就能够访问到这个信息,并把它写到跟踪日志里。当前唯一的替代办法就是使用测量技术,但它是干扰性的,并且不提供汇编级的跟踪信息。
多数的并行编程问题可以归因于在共享资源(如 CPU、总线周期、内存和各种设备等)的访问上缺乏正确的同步机制。问题表现为数据损坏、竞争状态、死锁、停机和饥饿等。这些问题的发生通常是不可预测的,而且难以重现。
可能的跟踪捕获结构
例如,用相同的输入值,执行相同的代码,可以产生不同的结果;或者结果虽然相同,但完成时间不同。这是对传统的源码调试技术(如"单步跟踪"等)的挑战,单步跟踪技术具有太强的干扰性,并且只专注于单个执行的线程。
因此,对付这种问题的最常用方法就是"使用很多 printf",或者使用基于软件测量技术的事件记录。工具厂商声称他们的测量技术一般会增加 2%-5% 的系统开销。对于多数情况来说,这些技术已可以胜任,并且系统开销也可接受。
但对高度优化的架构来说,代码测量技术会导致流水线和缓存性能出现不可接受的变化,同时还会增加许多难以确定的副作用。不幸的是,随着 SoC 设计在处理速度和并行程度上的持续提升,这种情况将变得越来越普遍。另一个选择是使用非干扰式的实时跟踪调试技术,它将上下文切换事件加入到跟踪中,以确定某个时间哪个任务或线程是活动的,这一技术仍然非常有用。
由于使用并行技术的目的是增加资源的利用率(即减少空闲周期),所以由内核提供各种计数器和统计数据(如缓存命中/未中、CPU/流水线的停止等)来量度这些技术的效果就成为迫切的要求。
对多个内核进行调试
大多数的开发人员已经意识到多内核访问性的问题,他们使用 JTAG 扫描链来为探测器提供对所有内核的访问。同样,工具厂商正在修改他们的集成开发环境(IDE),以支持多个异种内核的并行调试,这样工程师们就不必使用多个调试器。
为了能对多个内核的同步问题进行调试,我们需要并行执行控制。例如,可以实现中止多个选定内核的同步断点,条件是各内核均提供一个外部引脚,用于将停止信号传送给其它内核。当某个内核中发生断点时,它使用这根引脚将停止信号传给其它内核。现在,调试器是通过 JTAG 接口发出停止的指令,从而试图仿真同步断点。但这种方式有很大的延迟时间问题。
多内核结构还给跟踪支持带来了新挑战。理想情况下,你会希望每个内核都有一个片上跟踪缓冲区。不幸的是,这样做的成本太高,而且如果这些内核是异种的,或运行于不同的时钟频率下,则很难使跟踪转储相互关联。
在对一个 SMP 式的架构进行调试时,拥有相关的跟踪转储是非常有用的。在这种架构中,一个内核可以被等待共享资源的另一个内核停止。但这种相关性需要一个公共的时基或参考点。公共时基可以这样实现:一个内核定期向其它内核发信号,对一个内部计数器进行复位(同步)。另外,可以使多个内核共享一个片上跟踪缓冲区,或者可以使用探测器内的一个片外跟踪缓冲区(见图 2)。在后一情况下,探测器将为时间戳提供时基。
我们已经知道了,增加片上支持可以帮助实现更先进的并行调试功能。那么为什么今天还有那么多 SoC 没有或几乎没有片上支持呢?答案是成本和产品上市时间的双重压力。
片上调试支持不会免费实现。它增加了芯片的成本和复杂度。为了解决这些问题,有些厂商现在以 IP 块的形式提供片上调试支持。最终制造成本会得到降低,生产带有内部调试支持的芯片也将变得更实际。
目前,开发人员还必须使用其它调试技术,如仪器测量等。但随着 SoC 设计在复杂度和性能方面的逐步提升,这些技术将最终造成过大的干扰。结果是越来越多的调试支持从软件向硬件迁移,并且随着在硬件中为操作系统调度程序提供上下文切换支持,硬件将会具备更强的任务感知和线程感知能力。
文章评论(0条评论)
登录后参与讨论