2 仿真环境中的嵌入式软件调试
2.1 固件调试方法概述
目前的EDA环境提供了各种固件调试方法。通常可以使用以下方法之一:
- 使用硬件的SystemC模型进行仿真
这可以在不接触硬件的情况下尽早开始固件开发,并在假设模型准确的情况下测试代码的功能。主要局限是缺乏系统视图和(取决于模型的准确性)缺乏硬件时序准确性(行为模型)。
- 在模拟器CPU上执行固件的硬件模拟。
这是结合实际RTL的最简单方法,可实现代码原型。它需要一些SystemC封装器来访问寄存器和中断。它缺乏系统视图,因此无法验证固件在其他系统元素存在时的行为。
-
回放(可双向播放)录制的系统仿真会话。
-
带有完整系统模型的硬件仿真。
这是一种同步混合仿真,RTL和软件在同一仿真过程中运行。可分为使用CPU的快速模型–这允许非常快速地执行代码(例如,在1分钟内启动Linux),但由于TLM到RTL的转换缺乏周期精度。当开始进行完整的 RTL 仿真(启用所有时钟)时,速度也会明显减慢。
使用全系统RTL仿真通常非常缓慢,只能在合理的时间内测试简单的操作(低于10kCPU指令)。上图TLM部分包括CPU快速模型,RTL部分包括接口IP核。
- 完整系统的硬件仿真
同样可分为由CPU快速模型和仿真RTL组成的混合模式。在接口IP核的情况下,执行速度非常快,但需要对快速模型和仿真设备之间的内存进行良好管理,以确保有效仿真数据传输(通常是CPU写入数据,然后发送到接口)。注意:在这种模式下,软件与RTL异步执行,两者在跨域事务和设定的时间间隔内同步。实际上,软件时序与硬件的周期并不精确,而且根据设置会移除高速缓存事务和高速缓存缺失内存事务。
全RTL模式下,所有系统的周期都是精确的。这种模式速度较慢(Linux启动可能需要10分钟),但在仿真过程中可以保持稳定的性能。该模式允许测试通用系统用例或复制FPGA 测试过程中发现的问题。
无CPU仿真–PCIeSpeedBridge®适配器可用于将任意接口IP核设备连接到PC,并在PCIe空间开发驱动程序。仿真环境允许访问IP核的所有内部信号(在运行时捕获,甚至使用非常复杂的基于条件的触发器),以调试问题(无论是来自软件、硬件还是连接在接口另一端的设备)。
- 使用FPGA进行硬件原型开发
处理器的运行速度可达10-100MHz(如果是与FPGA逻辑相连的硅内核,运行速度甚至可达GHz)。这些环境并不能很好地调用新的(未经验证的)硬件,但它们非常适合以下用途:实时或接近实时的系统测试;性能测试;浸泡测试。
也可以在FPGA中制作简单的SoC原型。
- 硅测试
这通常被视为系统级芯片(SoC)的调试,所有硬件问题都应在此得到解决,但这并不妨碍在固件中进行一些调整。系统以全速运行,通常无法访问实际硬件(信号),但应该有一个处理器调试接口,可以逐步检查代码。、
2.2 固件可调试性
在许多情况下,管理固件的工程师可能不是代码的创建者,因此提供尽可能多的有关代码功能的信息非常重要。最佳做法包括
- 自解释的寄存器名称结构或宏
- 自解释的变量名(而不是 a、b、c、d)
- 自解释函数名
- 特别有用的是有关指针用法的信息,因为固件通常使用指针来存储例程地址,因此必须提供足够的信息,以便调试工程师能够理解代码应该做什么。
目前市场上出现了一些新系统,其中大部分IP核驱动程序代码都可以通过高级(行为)语言自动生成。在这种情况下,重要的是能够重新生成代码,并将所有更改追溯到源代码(元代码),以便传播所有调试信息。
生成的代码:
如上所述,使用寄存器抽象可以将代码从寄存器和归档地址中分离出来,即使结构中的任何寄存器发生移动,代码也不会受到影响。
2.3 接口IP核的测试驱动固件开发
在创建初始固件时,新的IP核通常刚刚被开发出来,这就需要一种持续的闭环工作模式,在这种模式下,新版本的代码可以根据新版本的RTL进行测试。接口IP固件支持团队的典型开发流程如下:
- 硬件团队设计IP核
- 固件工程师参与设计,为寄存器接口设计决策提供信息
- 一旦设计出寄存器模型,并完成具有功能总线连接的首个RTL实现,就可以立即将此类IP核集成到早期固件开发中。
在固件开发中,测试驱动开发是一种有趣的方法。这可以被视为硬件升级过程的正规化,在这一过程中,对功能的某些预期始终是明确定义的,而开发/升级的目的则是启用/支持该功能。
测试驱动开发的步骤是
- 设计一个失败的测试
- 实现通过的功能
- 完善以便于集成。
2.3.1 开始开发
第一个开发周期通常以硬件为重点。最初的测试一般是对IP核的地址空间进行寄存器读/写操作,以确认IP核已与系统正确集成。
一旦这些代码就位(由于它们总是相同的,因此很容易重复使用),就可以开始准备测试平台了。在当前的硬件仿真环境中,可以使用虚拟平台环境 (VPE Virtual Platform VPE) 类型的环境(如图所示),其中CPU和所有系统外设都作为SystemC快速模型存在,而正在开发的IP核则通过TLM到RTL封装器连接。在典型的使用案例中,这种连接需要以下步骤:
- 为新IP准备系统级封装器
- 将封装器与现有系统集成
- 选择基地址
- 提供该IP核所需但现有从属总线接口无法提供的任何控制信号
- 在接口IP核的情况下,将IP的实际”外部世界”接口(图)连接到合理的交易器至关重要。这些接口可以是
- 验证IP核实例
- 与接口兼容的IP核实例。
在理想情况下,这种集成应该是无缝的,因为IP核自带标准总线接口,只需将这些接口连接到系统即可。实际上,测试环境的”事务处理”部分可能是测试环境准备工作中最费力的部分。在早期阶段,这可以推迟到首次接触IP核寄存器之后,但这一阶段通常可以很快通过。
一旦所有设备连接完毕并将二进制文件加载到系统中,就需要进行几个软件/硬件联合调试周期,包括
- 检查从属总线端口访问
- 检查中断生成
最终形成可正常工作的固件调试环境。寄存器读写操作的C代码示例:
裸机读写:
在这一阶段,固件调试过程通常开始与硬件验证团队交叉进行。如果IP核附带了机器可读的寄存器描述(如 IP-XACT 或 SysRDL),则可以直接自动生成系统级测试序列,这些序列可以立即在新设计上执行,测试代码完全由寄存器设计数据驱动。这构成了一套非常基本的系统正常性测试用例,例如
- 检查寄存器复位值
- 确认寄存器可写位是否可写
- 确认寄存器只读位为只读
读写地址8的寄存器(在APB上)
这些一般由硬件验证小组负责,但在实际系统中执行这些操作可以解决总线访问问题和任何细小的寄存器库问题。
在此阶段,固件开发人员可以开始在IP核上运行任何功能测试用例。
2.3.2 首次功能测试
在为某个IP核开发底层驱动程序的典型情况下,需要支持一些 “关键功能”,以便进行初始系统级集成。这些功能通常包括:
- 初始化IP核
- 使用寄存器接口通过接口发送数据
- 使用寄存器接口通过接口接收数据
- 使用主总线接口/触发DMA
2.3.2.1 初始化IP核
一旦验证环境就绪,就可以设置简单的例程来执行IP核初始化,并确认初始化是否正确。确认初始化的测试可以是
- 接收中断,说明系统已准备就绪
- 轮询寄存器,说明系统已就绪
- 在极端情况下,可能需要窥探实际的IP核信号,以确定其已初始化。
IP核初始化可能涉及设置与IP连接的元素,以建立工作数据通道,如物理层初始化。在这一阶段,系统的所有组件都必须正确连接,在某些情况下,固件在系统环境中执行的初始化程序可能会发现硬件连接问题。使用自动IP核组装工具可以轻松避免这些集成问题。在这种情况下,还需要在”外部接口 “的另一端有一个正常工作的”交易器”,以确保连接已经建立。如果转换器是支持相同协议的另一个IP核实例,则最好有单元测试环境,在该环境中,每个IP核都可以根据已知模型(如验证IP核实例)进行隔离测试。
2.3.2.2 发送和接收数据
IP核初始化和数据通道就位后,就可以开始传输数据以确认系统的连接性。在接口IP核的情况下,需要有一个能满足通信协议要求的”交易者”实例,以确保IP核能完成数据传输。让我们以闪存控制器为例。调试步骤的目的是确认我们可以创建一个工作代码,通过寄存器接口逐字发送数据,从而在NAND存储器中写入一页数据。要做到这一点,必须具备以下先决条件:
- 控制器已初始化
- 存储器已初始化
需要了解存储器控制器使用的协议(对于 NAND 存储器,它是一串与所需数据交错的标准化命令(如 ONFI)。
让我们考虑一个操作示例:使用寄存器接口写入内存页,然后读取以确认一致性。在仿真中调试该代码可实现以下功能: - 逐步浏览固件代码
- 查看寄存器接口,确认向闪存控制器发送了正确的数据
- 查看存储器接口,确认向闪存发送和从闪存接收的数据正确无误(模型)
- 查看寄存器接口,确认从存储器读取的数据正确(模型)
- 访问存储器(或调试信息),以确认数据的完整性。
代码经过仿真调试和验证后,就可以在FPGA设置上进行测试。一旦在仿真中解决了所有基本问题,FPGA平台就可以使用真实存储器部件对运行进行性能分析和剖析。
2.3.2.3测试DMA
仿真环境是测试DMA传输和执行这些传输的固件原型的绝佳平台。DMA传输会显示系统问题(如内存缓存),因此最好能看到传输启动时的系统内存以及主总线接口上的数据流,以确认传输数据的完整性。下图显示了与上一节相同的内存写入和读取过程,但这次建立了一个请求每次操作的描述符,在初始触发后,IP核预计将
- 读取描述符
- 开始读取存储器(使用主总线接口)
- 开始向设备写入数据
- 完成读取存储器
- 完成数据写入
- 中断处理器,通知操作已完成
- 执行与上述类似的步骤,但方向相反。
在确认单个DMA可以正常工作后,可能需要测试链式DMA操作,即获取多个描述符(按顺序,可能使用部分硬件缓冲),以确保固件能够创建和处理这些描述符。比如在APB总线上执行初始化序列:
另一种方法是测试多通道DMA,其中接口IP核在多个连接设备上创建并执行多条命令链。
2.3.2.4 在FPGA上发送和接收数据
FPGA的调试能力大大降低,可跟踪的IP核信号数量有限,在仿真中检查代码可大大减少调试过程。将FPGA中的嵌入式处理器连接到调试器上,仍然可以逐步检查代码,但查看接口信号可能比较困难,可能需要使用示波器或逻辑分析仪。
可以在FPGA上重复先前定义的步骤(已在仿真中得到验证),以实现硬件的基本功能。
FPGA 测试的优势在于速度,例如在NAND闪存测试中,可以运行比模拟(16 MB多2个数量级的数据(500 MB)。这也有助于发现驱动程序在处理大量数据块时存在的问题。此外由于测试是在真实存储器上进行的,因此可以发现所使用的存储器模型是默认擦除的,而真实存储器芯片并非如此,因此需要更新存储器擦除程序。
下图在QSPI IP核上启用接口:
带有图像处理装置和摄像头/显示器MIPI接口的示例系统
2.3.3 调试系统
一旦系统的所有组件都已调试完毕(至少在正向路径上),就可以开始调试系统以及同时 使用多个组件的应用程序。此时的假设应该是
- 有一个可调试的中央处理器(允许逐步检查代码)
- 所有组件都能执行基本操作
- CPU都能访问内存。
一旦确定了这些步骤,就可以进行系统调试。在大多数情况下,系统调试会从一些基本的裸机测试开始,其中会同时使用多个组件。例如,在调试摄像头接口(MIPI CSI)IP核时,可能需要将UART用作调试输出(甚至输入)。此外,在使用 MIPI接口获取图像并将其存入系统存储器的同时,可能还需要使用另一个简单接口(如 I2C)对摄像头进行初始化。在这种情况下,了解上图所示的完整系统架构非常重要,因为多个组件会相互影响,CPU 也会与所有组件相互影响。
上例中的调试步骤如下:
- 为调试信息创建串行端口连接(运行UART)
- 创建与摄像头的I2C连接并记录连接结果
- 初始化摄像头并记录结果
- 初始化MIPI CSI接口
- 请求MIPI CSI接口开始从摄像机向系统内存传输数据。
- 使用调试器访问内存,或使用 UART 转储内存内容。
在此示例中,最好有一个可控环境,在此环境中,摄像机将发送可验证(如测试模式)的已知数据,以确定是否已将正确的内容写入系统内存。一旦确认系统按照正确的顺序执行了所有操作,正确的数据被复制到内存中,还需要确认数据传输与系统的其他部分同步良好,即一旦摄像机开始以每秒多帧的速度发送数据,就会有足够的缓冲,帧数据不会损坏。这是测试中断的好机会,因为多个设备(UART、I2C、MIPI CSI)都可能触发中断,因此设置正确的中断处理方式非常重要。优先执行中断的示例如图所示。
需要适当设置中断优先级,以确保在这种情况下不会中断数据流和用户交互,因为这只是为了设置和调试目的。
系统内存传输(DMA)示意图:
中断处理不应影响系统功能,因此可能需要人为创造条件,同时触发多个中断。
2.3.4 系统性能
一旦所有组件都能协同工作,就可以考虑系统性能,并对代码进行优化,以达到所需的基准。让我们在上述系统的基础上增加一个屏幕界面和一个图像处理程序。在这种情况下,图像
- 从摄像头获取图像并存储到内存中
- 由图像/信号处理组件从内存中读取、
- 由图像/信号处理组件存储回内存
- 由显示组件(如 MIPI DSI)从内存中读取。
在RGB模式下处理VGA测试图像时,系统需要为每幅图像读取2MB和写入2MB的数据。如果图像以30帧/秒的速度传输,则读取和写入速度分别为60MB/秒和60MB/秒。我们必须考虑到,系统可能需要同时为其他目的访问内存(例如,记录/维护/读取程序或与图像处理相关的其他数据)。如果系统运行在FPGA上,则需要确保系统架构能够提供所需的带宽。FPGA平台本身就是证明这一点的理想工具。
一旦系统的性能在FPGA中得到验证,那么扩展到ASIC速度就会简单得多,可能只需要进行少量的软件优化。这里需要考虑的问题包括
- 总线访问
- 内存访问
- 内存带宽(总线、控制器、模块)
- 内存缓存。
“在我们的一个演示设计中,团队使用了一块现有的FPGA板,其中主(也是最大的FPGA芯片与DDR存储器没有直接连接。DDR控制器是在单独的(较小的 FPGA)上合成的,它可以通过芯片间连接与主芯片相连。上述系统(包括摄像头、图像处理器和显示器)就是在这块电路板上实现的,结果发现内存带宽成为图像分辨率和每秒帧数的限制因素。为了达到最佳性能,采用了Xilinx Chip2 AXI结构的特殊设计。这需要在硬件和软件层面进行进一步优化。在硬件方面,重要的调整因素包括
- 芯片2的时钟速度(以及相对于系统总线的同步/异步实现)
- 图像处理器缓存大小
- 图像处理器缓存行大小(根据DDR行大小调整)。
在软件方面,重要的是要限制内存访问,并确保图像块(在四条数据路径中的每一条路径上)将使用’链式’DMA 操作序列发送,以充分利用结构带宽”。
2.3.5 全功能操作系统案例中的接口IP核性能
另一个有趣的例子是,以太网MAC实现可用作带有PCIe端点的NIC(网络接口卡)。两个内核连接在一起并插入PC。有两种镜像设置:
- 仿真(使用 PCIe SpeedBridge® 适配器和以太网 SpeedBridge® 适配器)
- 带有标准PCIe和以太网连接器的FPGA。
其中任何一种实现方式都可以连接到运行Linux操作系统的实验室PC上,并在Linux中作为PCIe设备为接口IP核实施驱动程序,以进行性能测试和调试(同样的设置用于合规性测试)。
在一个特殊的案例中,以太网控制器增加了一项功能,可以卸载软件TCP/IP协议栈,使其不必将接收到的数据包”聚合”成一个大数据包。Linux 驱动程序中增加了对该功能的支持,网卡平台的两种实现方式都用于在系统环境中测试该功能。FPGA实现用于对卸载功能节省CPU的情况进行基准测试,在运行过程中发现了卸载的潜在问题。采用了以下步骤来跟踪 FPGA 的问题:)
- Linux驱动程序中的附加日志记录
- 在Linux中启用额外的内核日志记录
- 使用TCP嗅探器(wireshark)确认数据传输。
然而,所有这些方法都无法找到问题的原因,因此需要在仿真环境中复制该问题,以便全面了解问题。此外,事实证明,有些问题在不同的PC上是不一致的。在可以完全访问所有硬件设计信号的仿真环境中复制问题后,结果发现
- PCIe 根端口(主机)可能会对数据包重新排序
- 某些PCIe总线参数会影响该问题的发生
除此之外,通过进一步完善测试,还发现了一个与PCIe总线无关的实际硬件问题。
在执行此类实验室测试时,对测试环境进行严格控制是非常重要的,这样才能确保随机事件或设置变化不会导致系统以不同的方式运行。最佳做法是建立一个干净的沙盒,通过完全脚本化的路径进行测试,并对外部事件进行完全控制(例如,避免连接到实时本地网络)。
2.3.6 最新嵌入式系统中的低级固件调试
硬件设计人员在实验室调试用于合规性测试的新SoC系统时,创建了一个由64位CPU内核和当代中断控制器组成的系统,两者都用于测试嵌入式xHCI 控制器的消息信号中断(MSI)能力,xHCI控制器作为IP和USB SuperSpeed设备VIP实例。MSI 功能是基于PCIe设备的典型功能,支持良好,但随着新型中断控制器的推出,嵌入式系统也可以接收这些中断。
在嵌入式MSI中断中,内存位置被中断控制器劫持,为CPU生成中断信号。这就需要设置正确的总线配置(包括受保护的访问线路),以便与中断控制器协同工作,并允许总线上的设备访问给定的内存位置以产生中断。支持这一功能的设备示例是xHCI接口内核。Linux驱动程序支持MSI模式,通过在仿真环境中运行完整系统,可以确认IP会产生MSI中断。在对地址进行适当配置后,将xHCI内核切换到MSI模式,并配置GIC以接收这些信息。
2.4 作为硬件验证工具的固件启动
由于目前可以在全系统环境的模拟/仿真中快速调用IP核,因此可以使用现成的系统测试对新开发的IP核进行早期验证。
最近,我们在实验室环境中对两个不同的内核进行了验证。
2.4.1 NAND闪存
我们为一个新的NAND闪存控制器内核创建了MTD(内存技术设备 Memory Technology Device)驱动程序,以便在 Linux中使用MTD测试框架,对仍在开发中的IP和执行多个(现有)系统测试。
社区中可用的MTD测试列表:
- mtd_speedtest:测量并报告MTD设备的读/写/擦除速度。
- mtd_stresstest:执行随机读/写/擦除操作,验证MTD设备的 I/O 能力。
- mtd_readtest:该测试读取整个MTD设备,每次读取一个NAND页,包括OOB(或在NOR等闪存情况下每次读取512字节),并检查读取是否正常。
- mtd_pagetest:仅适用于NAND闪存,测试不同大小和顺序的NAND页写入和读取;该测试最初是为测试 OneNAND 驱动程序而开发的,因此可能有点面向 OneNAND,但必须适用于任何NAND闪存。
- mtd_oobtest:仅适用于NAND闪存,通过向不同偏移量写入数据并验证,测试OOB区域I/O是否正常工作。
- mtd_subpagetest:仅适用于NAND闪存,测试I/O。
- mtd_torturetest:该测试旨在耗尽闪存擦除锁。它会重复写入和擦除同一组擦除锁,直到发生I/O错误,因此要小心!该测试支持许多选项(请参阅 modinfo mtd_torturetest),允许你设置要折磨的擦除块数量以及折磨的方式。你可以使用 cycles_count 模块参数来限制折磨周期的数量。如果有备用设备,最好能运行该测试一段时间,并对闪存驱动程序和硬件进行验证。例如,我们在一块使用OneNAND闪存的OMAP2板上发现了一个相当罕见且令人讨厌的 DMA 问题,但只需运行该测试几个小时即可解决。
- mtd_nandecctest:这是一个简单的测试,用于检查256和512字节缓冲区内置软件 ECC 的正确性;该测试并非针对特定驱动程序,而是测试一般的NAND支持代码。
这有助于发现Linux驱动程序中的许多问题,因为它是一套现成的”测试过的测试”,因此是一个完美的驱动程序开发环境,具有明确的验收标准(通过所有测试)。
MTD 框架允许在现有堆栈上运行文件系统(如 JFFS2),从而进一步提高系统和压力测试能力。
2.4.2 xHCI
对于具有标准寄存器接口的IP核,只要能将IP核集成到仿真/模拟环境中,Linux本身就可用作系统测试的第一阶段。此外,USB-test Linux框架还可用于执行多个系统测试,以确认IP核的系统级稳定性(该IP核同时经历了设计更新和标准硬件验证过程)。
其中:
- iterations-迭代次数
- length-数据包大小
- sglen-收集/汇集数据包条目
- vary-数据包大小的变化。
- i-当前迭代次数
通过这些测试,我们发现了驱动程序的一些问题,特别是
- 停滞支持
- 设备配置
测试套件还被用作IP核的压力测试,使用了多种系统配置(32/64 位、传统/MSI 中断),以确保驱动程序的系统质量。
参考资料
- 软件测试精品书籍文档下载持续更新 https://github.com/china-testing/python-testing-examples 请点赞,谢谢!
- 本文涉及的python测试开发库 谢谢点赞! https://github.com/china-testing/python_cn_resouce
- python精品书籍下载 https://github.com/china-testing/python_cn_resouce/blob/main/python_good_books.md
- Linux精品书籍下载 https://www.cnblogs.com/testing-/p/17438558.html
2.5 使用 Cadence® Indago™ 嵌入式软件调试器进行回放调试
随着时间的推移,数字系统变得越来越复杂。在保留向后兼容性的同时,对新功能的要求使得每一次新系统的发布都比上一次复杂。要对这样的项目进行全面验证,就需要一种能对固件和硬件进行共同验证的工具。硬件测试(无论是UVM还是功能/定向测试)可能还不够,为了创建更真实的用例,需要固件驱动的测试用例。这样就可以在单个测试用例中建立完整的用例,例如,向SoundWire设备添加新的声音通道需要重新配置IP核并同时运行所有传输。因此,将带有微控制器的系统作为验证平台的一部分非常重要。
另一方面,市场竞争日趋激烈,迫使人们更多地考虑设计新版本设备的成本。其中,上市时间的考虑尤为重要。关键是要确保工作不被难以分析的意外错误所耽搁。这些问题的出现可能会导致项目延期和 “紧要关头”的出现,对团队造成重大影响,并可能引发更多问题。在很多情况下,错误发生后是可以观察到的,而且很容易在随后的执行时间设置断点以查看其影响,但可能很难跟踪根本原因(如意外输入值或代码中的错误)。在这种情况下,能够观察系统的过去(硬件和软件,包括内存内容)以追踪实际根源的发生是非常重要的。
Indago™ 嵌入式软件调试器是一种可以调试整个系统(硬件和软件)的解决方案。它是一个完整的解决方案,只需使用一个工具即可访问项目的所有组件。它可以轻松分析硬件和软件之间的合作。通过 Indago™ 嵌入式软件调试器,可以跟踪所有信号、CPU 状态和内存内容,随时观察整个系统的状态。
2.5.1 示例
为SoundWire主设备和从设备创建的验证系统示例如图。该系统基于 SoC,SystemC 和 RTL 均在 Incisive® Enterprise 仿真器中执行。
该系统由以下组件组成:
- SystemC:
- CPU 快速模型
- 虚拟结构模型
- 内存模型
- UART 模型
- RTL (Verilog):
- 声导线主站
- 声导线从站
- VIP:
- 声导线从站
要使用 Indago™ 嵌入式软件调试器进行回放调试,需要ELF文件和有关软件执行流程的信息。可以是跟踪文本文件,允许使用 Indago™ 嵌入式软件调试器的所有功能、
波形上的PC计数器跟踪,仅提供基本调试功能。
还可将包含硬件信号信息的兼容波形数据库加载到应用程序中,从而实现硬件/软件协同调试。
下图显示了 Indago™ 嵌入式软件调试器的主界面,它包含两个主要部分:代码调试器和可显示软硬件信息的波形窗口。
下图展示了一个中断处理程序调用的函数示例。调用堆栈和波形可用于快速确定调用原因。
下图显示了对硬件的写操作。CPU将数据写入FIFO,在硬件级显示为AHB写事务。
离线调试功能允许在代码执行过程中向前或向后移动,并可设置无限断点。当代码在中断处理程序中失败时,这一点尤其有用。Indago™ 嵌入式软件调试器允许跳回最后一次执行,查看出错的地方,而无需步进成功的运行。
离线调试允许使用非侵入式调试方法。使用打印来调试代码很常见,但它会影响代码的执行时间,因此在代码中添加一些打印后,有问题的代码可能会开始工作。Indago™嵌入式软件调试器支持SmartPrints。在执行的任何一行代码中,都可以添加包含自定义信息和可用变量的打印信息。由于它使用的是软件执行的离线数据库,因此不会增加任何延迟,尤其是在中断期间,延迟可能会影响执行。这也使调试变得更加容易。
2.5.2 覆盖范围测量
Indago™ 嵌入式软件调试器与 Incisive® Enterprise Simulator Metrics Center 一起使用,可以测量代码覆盖率。
下图显示了应用程序的主窗口。通过该窗口可快速了解代码测试质量,并有助于识别和修复代码验证中的问题。如图 2.18 所示,还可以查看代码源代码,并显示执行了哪一行。多个测试运行的结果可以合并在一起。
上图为仿真器指标中心显示代码覆盖率结果示例
上图为函数源代码覆盖视图
2.5.3 缺点
其局限性在于无法对修改进行现场测试。因此,必须使用多种调试工具,以便能够重新评估新代码,因为记录新系统运行可能会耗费大量时间。
2.6 小结
本章介绍了多个采用不同调试方法的嵌入式系统实例。EDA工具的最新发展以及计算机速度和容量的不断提高,使我们能够模拟或仿真非常复杂的片上系统平台,并立即访问软件调试符号、代码步进和硬件信号跟踪,从而确保在系统环境中轻松调用新设备(正在设计中),并根据行业标准验证IP核或与实际设备的连接进行测试。这反过来又缩短了新器件的上市时间,因为软件开发人员可以在IP和合理稳定或TLM模型可用后立即开始开发驱动程序和系统软件。
利用新的回放系统调试方法,还可以在不影响运行时的情况下跟踪系统中的问题。
嵌入式软件调试中最重要的要素是:
- 设置最接近目标系统的平台。
- 在开发过程中尽早开始使用设备或其模型的能力。
- 硬件和软件工程师的合作(尤其是在早期启动阶段)。
- 适当选择工具。