Capturing ETB Trace Data With ETBLib/zh
From Texas Instruments Embedded Processors Wiki
Contents |
通过 ETBlib 捕获 ETB 跟踪数据
简介
本页将演示如何借助 ETB 编程库 ETBLib 捕获 ETB 跟踪数据并将其从芯片内删除。
所需的工具
- 具有目标仿真器的 Code Composer Studio v4
- TCI6488 目标卡
使用的软件
- ETBLib - 嵌入式跟踪缓冲区编程库
- DSPTraceLib - DSP 跟踪编程库
- AETLib - 高级事件触发库
演示应用程序
概述
演示应用程序名为 etblib_demo。它是基于 DSP/Bios 的示例应用程序。有三个任务以循环方式进行调用,每个任务的优先级均为 2。每个任务都将等待一个信号灯。当全部三个任务都处于等待状态时,较低级别的任务会先执行,这会告知信号灯重新启用较高级别的任务。此外,还有一个任务,其优先级为 3。该任务在执行时会紧接地等待另一个信号灯。
为了模拟应用程序崩溃(会导致应用程序以 SYS_ABORT 例程结束),需要创建一个将在执行 15 秒后调用的 PRD 函数。此函数会告知信号灯允许执行高优先级任务。此高优先级任务随后调用 SYS_ABORT,以模拟应用程序崩溃。
目标
本练习的目标是演示如何查看所得到的跟踪数据并追溯至崩溃点。在本例中,只涉及追溯至 SYS_ABORT 调用。在更现实的情形中,将需要更详细的分析才能查明崩溃结果。不过,本练习的目标更侧重于捕获数据,而不是分析数据。
应用程序源代码描述
注意:由于所述变量的大部分都是全局声明的,因此它们在任何应用程序函数中均可见。根据具体情况,有时可以动态声明用于保存 ETB 内容的内部内存缓冲区。然而,在本例中,需要声明一个静态缓冲区。之所以这样做,是因为在调用 SYS_abort() 后将无法进行动态分配。因此,可以事先进行静态分配,然后在调用 SYS_abort() 后将值复制到缓冲区。我们已经创建了自己的函数 myExit,以替换通过 abort 例程调用的 UTL_halt。此函数可将 ETB 复制到内部内存,设置状态位域记录进度,然后关闭硬件中断并进入无限循环。普通应用程序可能会使用此相同的例程,并在关闭之前将 ETB 值转储到内存。
下载示例项目
可以从以下位置下载示例项目。请注意,要生成项目,将需要有可用的 AETLib、ETBLib 和 TraceLib 库。可以从此处下载这些库。
两个项目都存在几个关于包含文件和库位置的假设。您需要调整项目以适应您的配置,或者相应地更新宏变量。下面列出了这些假设。
- 宏 AetLib 指向具有 \include 和 \lib 子目录的目录。AetLib 头文件位于 \include 子目录中,.lib 文件位于 \lib 子目录中。
- 宏 EtbLib 指向具有 \include 和 \lib 子目录的目录。EtbLib 头文件位于 \include 子目录中,.lib 文件位于 \lib 子目录中。
- 宏 TraceLib 指向具有 \include 和 \lib 子目录的目录。TraceLib 头文件位于 \include 子目录中,.lib 文件位于 \lib 子目录中。
| 项目文件描述 | 链接 |
|---|---|
| Code Composer Studio 3.3 项目 | 链接 |
| Code Composer Studio v4 项目 | 链接 |
main()
以下代码在 main 函数内执行,仅设置执行演示所需的 DSP/Bios 对象。需要创建每个任务,设置适当的参数,以及创建 PRD 一次性函数。如果上述任何操作失败,我们会通过 Bios 消息日志通知用户。
/* 初始化所有任务的 TSK_Attrs 结构 */ TSK_Attrs tsk0_attrs = {0}; TSK_Attrs tsk1_attrs = {0}; TSK_Attrs tsk2_attrs = {0}; TSK_Attrs lltask_attrs = {0}; TSK_Attrs cleanup_attrs = {0}; LOG_printf(&trace, "Faraday Test example started.\n"); /* 启动 PRD 时钟 */ PRD_start(&PRD0); /* 创建 Task0 */ tsk0_attrs.name = "Task0"; tsk0_attrs.priority = 2; tsk0_attrs.stacksize = 512; tsk0_attrs.stack = NULL; tsk0_attrs.initstackflag = 1; hTask0 = TSK_create((Fxn)task0, &tsk0_attrs); if (!hTask0) { SYS_abort("Can't create task 0"); } /* 创建 Task1 */ tsk1_attrs.name = "Task1"; tsk1_attrs.priority = 2; tsk1_attrs.stacksize = 512; tsk1_attrs.stack = NULL; tsk1_attrs.initstackflag = 1; hTask1 = TSK_create((Fxn)task1, &tsk1_attrs); if (!hTask1) { SYS_abort("Can't create task 1"); } /* 创建 Task2 */ tsk2_attrs.name = "Task2"; tsk2_attrs.priority = 2; tsk2_attrs.stacksize = 512; tsk2_attrs.stack = NULL; tsk2_attrs.initstackflag = 1; hTask2 = TSK_create((Fxn)task2, &tsk2_attrs); if (!hTask2) { SYS_abort("Can't create task 2"); } /* 创建低级别任务 */ lltask_attrs.name = "LowLevel Task"; lltask_attrs.priority = 1; lltask_attrs.stacksize = 512; lltask_attrs.stack = NULL; lltask_attrs.initstackflag = 1; hllTask = TSK_create((Fxn)lltask, &lltask_attrs); if (!hllTask) { SYS_abort("Can't create low level task"); } /* 创建清除任务 */ cleanup_attrs.name = "Cleanup Task"; cleanup_attrs.priority = 3; cleanup_attrs.stacksize = 512; cleanup_attrs.stack = NULL; cleanup_attrs.initstackflag = 1; cleanupTask = TSK_create((Fxn)cleanup, &cleanup_attrs); if (!hllTask) { SYS_abort("Can't create CleanUp task"); }
另外,还需要初始化 ETB 和 DSP 跟踪硬件,而且还需要对 AET 进行编程,以便在相应的点打开和关闭跟踪。代码注释将为您提供所发生操作的概况。我们将要配置 AET 以触发 PC 和计时跟踪,以便在执行 task0 时启动,并在执行退出函数 myExit 时结束。ETB 将以循环方式捕获,以便在跟踪结束时会获得最后执行的一组指令。
/* 设置 ETB 接收器 */ etbError = ETB_open(pETBErrCallBack, eETB_Circular, coreID, &pETBHandle, &etbSize); if(etbError != eETB_Success) { SYS_abort("Error opening ETB"); } else { LOG_printf(&trace, "ETB Opened"); } /* 启用 ETB 接收器 */ etbError = ETB_enable(pETBHandle, 0); if(etbError != eETB_Success) { SYS_abort("Error enabling ETB"); } else { LOG_printf(&trace, "ETB Enabled"); } /* 在此处应完成 AETLIB 编程 */ /* 配置 AET 以便在 task0 函数处启动, 并在自定义 myExit 函数处结束 */ startTraceParams = AET_JOBPARAMS; endTraceParams = AET_JOBPARAMS; startTraceParams.programAddress = (Uint32)&task0; startTraceParams.traceTriggers = AET_TRACE_TIMING | AET_TRACE_PA; startTraceParams.traceActive = AET_TRACE_ACTIVE; endTraceParams.programAddress = (Uint32) &myExit; endTraceParams.traceTriggers = AET_TRACE_TIMING | AET_TRACE_PA; endTraceParams.traceActive = AET_TRACE_INACTIVE; AET_init(); if (AET_claim()){ SYS_abort("AET not claimed"); }else{ LOG_printf(&trace, "AET Claimed"); } /* 设置跟踪开始任务 */ if (AET_setupJob(AET_JOB_START_STOP_TRACE_ON_PC, &startTraceParams)){ SYS_abort("Start job program failure"); }else{ LOG_printf(&trace, "Start Job Programmed Success"); } startJob = startTraceParams.jobIndex; LOG_printf(&trace, "Start Job #%d", startJob); /* 设置跟踪结束任务 */ if (AET_setupJob(AET_JOB_START_STOP_TRACE_ON_PC, &endTraceParams)){ SYS_abort("End job program failure"); }else{ LOG_printf(&trace, "End Job Programmed Success"); } endJob = endTraceParams.jobIndex; LOG_printf(&trace, "End Job #%d", endJob); /* 启用 AET */ if (AET_enable()){ SYS_abort("Error Enabling AET"); }else{ LOG_printf(&trace, "AET Enabled"); } /*** 设置跟踪导出 ***/ /* 打开 DSP 跟踪导出模块 */ dspTraceError = DSPTrace_open( pDSPErrorCallBack, &pDSPHandle); if(dspTraceError != eDSPTrace_Success) { SYS_abort("Error opening DSP Trace Export block"); }else{ LOG_printf(&trace, "Trace Export Opened"); } /* 将跟踪导出时钟设置为 FCLK/2 */ dspTraceError= DSPTrace_setClock(pDSPHandle, 2); if(dspTraceError != eDSPTrace_Success) { SYS_abort("Error setting up DSP trace export clock"); }else{ LOG_printf(&trace, "Trace Clock Initialized"); } /* 启用 DSP 跟踪 */ dspTraceError= DSPTrace_enable(pDSPHandle, 0, testPattern); if(dspTraceError != eDSPTrace_Success) { SYS_abort("Error enabling DSP trace export"); } else { LOG_printf(&trace, "DSP Trace Enabled"); } /* 启动 PRD 时钟 */ PRD_start(&PRD0);
dumpEtb()
一旦应用程序执行时间到达约 15 秒,就将执行 PRD 函数,并告知信号灯允许高优先级任务调用 SYS_abort(),以模拟应用程序崩溃。自定义退出函数会调用 dumpEtb(),后者将回收 AET 所使用的资源,将 ETB 数据从 ETB 移至处理器内存,禁用并关闭 DSP 跟踪接口,以及禁用并关闭 ETB 接口。借助上述每个步骤,可以通过更新全局变量 statusCode 中的位来监视成功与否。当在 SYS_abort() 之后以无限循环结束时,表示传输安全成功的值为 0x1F7(ETB 尚未包装)或 0x1EF(ETB 已包装)中的一个。在典型情况下,只使用 printf 告知用户发生错误的位置,而不会使用这些位域。我们在此之所以不这么做,是因为在调用 SYS_abort 之后无法调用 printf。
void dumpEtb(){ // 释放所有 AETLIB 资源 AET_releaseJob(startJob); AET_releaseJob(endJob); AET_release(); // 禁用跟踪导出 dspTraceError = DSPTrace_disable(pDSPHandle); if (dspTraceError == eDSPTrace_Success) { statusCode |= STS_TRACE_EXPORT_DISABLED; } /* 禁用跟踪捕获 - ETB 接收器 */ etbError = ETB_disable(pETBHandle); if (etbError == eETB_Success){ statusCode |= STS_ETB_DISABLED; } /* 检查 ETB 状态 */ etbError = ETB_status(pETBHandle, &etbStatus); if (etbError == eETB_Success){ statusCode |= STS_ETB_STATUS; } /* 将 ETB 数据移动到内存 */ if (etbStatus.canRead == 1) { etbWordsAvailable = etbStatus.availableWords; if (etbStatus.isWrapped == 1){ statusCode |= STS_ETB_WRAPPED; }else{ statusCode |= STS_ETB_NOT_WRAPPED; } /* * 分配缓冲区以便将 ETB 复制到其中 * 将 memPtr 设置为之前静态分配的数组 */ memPtr = (uint32_t*)&etb_buf; if (memPtr) { etbError = ETB_read(pETBHandle, memPtr, etbStatus.availableWords, 0, etbStatus.availableWords, &retSize); if (etbError == eETB_Success) { statusCode |= STS_ETB_READ; } statusCode |= STS_BUF_ALLOCATE; } } /* 关闭 ETB 的句柄 */ etbError = ETB_close(pETBHandle); if (etbError == eETB_Success) { statusCode |= STS_ETB_CLOSED; } /* 关闭 DSPTrace 的句柄 */ dspTraceError = DSPTrace_close(pDSPHandle); if (dspTraceError == eDSPTrace_Success) { statusCode |= STS_TRACE_EXPORT_CLOSED; } }
导出数据
通常,我会在 myExit() 函数中的 asm(" nop") 指令上设置断点。断点将在应用程序崩溃后立即被命中。也可以等待应用程序执行足够长的时间,然后手动停止应用程序。在这两种情况下,PC 应处于相同的位置。
停止后,ETB 内容就已经复制到 CPU 内存中 etb_buf 数组的位置。 您现在应该将以下三项值放入 CCS 监视窗口:
- etb_buf - ETB 缓存值的位置
- etbWordsAvilable - 缓存中的字数
- statusCode - 错误状态代码
如果错误状态代码是 0x1F7 或 0x1EF,则可安全地继续进行。然后打开 CCS 脚本控制台窗口,并输入命令以导出数据至 bin 文件。在本例中,我们使用“etb_test.bin”作为文件名。您也可使用您选择的其他任何文件名。我们将假设 .out 文件存储在 c:\temp\ 中,并且使用与该 bin 文件相同的路径。
在命令窗口中,输入以下命令,必要时替换这些值。 使用监视窗口中的 etb_buf 值替换 startAddr 使用监视窗口的 etbWordsAvailable 值替换 length
saveRaw(startAddr, PAGE_PROGRAM, "C:\\temp\\etb_test.bin", length , 32, true)
转换数据
至此,我们已经捕获的数据只是二进制形式的原始 etb 数据。我们需要将此数据转换为跟踪数据格式 (.tdf),以便能够查看它。随 Code Composer Studio v4 提供的一项工具将完成此任务。该工具称为 bin2tdf.exe,位于 <%CCS_INSTALL_DIR%>\CCSv4\emulation]analysis\bin 目录。
使用 bin2tdf 的第一步是创建将需要传递给实用程序的 bin2tdf 配置文件。您可以通过复制以下文本框中的文本到文本文件并用 bin2tdf.cfg 这样的名称进行保存来创建用于此演示的配置文件。
Trace Mode = 0 Encoding Mode = 0 Number of AEG = 0 Memory Mode = 0 CEMU Status = 0
许多参数需要在命令行上指定。
- -bin <二进制 etb 文件>
- -coffname <.out 文件>
- -cpuid <CPU 类型:对于 TCI6488,它为 64x+>
- -rcvrname <跟踪接收器名称:在本例中,它为 ETB>
- -dcmfile <bin2tdf 配置文件名
假设所有必需的文件都位于当前目录,且 .exe 的位置也处于路径中,可将以下命令行用于此示例。
bin2tdf.exe -bin etb_test.bin -coffname etblib_demo.out -cpuid 64x+ -rcvrname ETB -dcmfile bin2tdf.cfg
将创建 .tdf 文件,且文件名与 .bin 文件的文件名相同,因此在本例中,它将为 etb_test.tdf。该文件会在当前目录中创建。
查看数据
Code Composer Studio 4 实际上附带有两个不同的查看器应用程序,以使您可显示捕获的 .tdf 文件。您可自行决定具体选择哪一个查看器。
跟踪分析器显示
Code Composer Studio v4 附带一个内置的跟踪查看器,称为跟踪分析器。选择“Tools->Trace Analyzer(工具->跟踪分析器)”可在 CCS 内打开“Trace Analyzer(跟踪分析器)”窗口。为打开 .tdf 文件,请执行以下步骤。
- 选择“Tools->Trace Analyzer->Open Trace file in New View(工具->跟踪分析器->在新视图中打开跟踪文件)”
- 导航至 .tdf 文件所在的位置,并选择该文件
- 下一个窗口将要求给出程序文件。导航至 .out 文件的位置,并选择该文件
- 下一个窗口将要求浏览文件夹。导航至包含源代码的文件夹,并选择该文件夹(可选)
在右侧图中,我们可以看到在跟踪分析器中解码之后,跟踪数据的样子。
独立跟踪显示
Code Composer Studio v4 附带独立跟踪显示查看器,可用于查看 .tdf 文件。此应用程序称为 TraceDisplay.exe,可在 <%CCS_INSTALL_DIRECTORY%>/ccsv4/emulation/analysis/bin 目录中找到。打开 TraceDisplay.exe 时,您将看到一个对话框,提示无法连接到跟踪数据服务器并将以独立模式操作。只需确认对话框即可,因为我们希望以独立模式操作。
一旦打开了跟踪显示应用程序,即可以选择“File->Open(文件->打开)”,通过对话框进行导航以选择您希望查看的 .tdf 文件。在一些情况下,一旦您指定了 .tdf,跟踪显示将要求您指定相关的 .out 文件。如果发生这种情况,只需在对话框中进行导航以选择在捕获跟踪数据时使用的 .out 文件即可。(如果要保存这些文件以便日后查看,则还要保存相关的 .out 文件,这点很重要。如果 .out 文件并非在捕获跟踪数据时所使用的同一文件,则跟踪解码器将无法检索数据。)
在右侧图中,我们可以看到解码之后,跟踪数据的样子。顶部框架显示应用程序的原始汇编级别执行情况。我们看到每条指令的程序地址、与每条指令相关的计时周期计数以及每条指令的反汇编。在较低窗格中,我们可以看到 C 源代码。在顶部窗格中滚动时,如果位于指定为源代码目录的目录所含的代码位置时,较低窗格将保持同步。
指定源代码目录
若要为跟踪显示指示包含源代码的文件夹,请右键单击上述显示内容的顶部窗格,然后选择“Source->Set Source Directory(源代码->设置源代码目录)”。为将目录添加到列表中,请选择“New Folder(新建文件夹)”,然后导航到所需的目录并选择“OK(确定)”。必要时重复此操作。可以选择自动包括子目录,但是如果子目录太多,这会显著减慢搜索速度。
常见问题
问:我是否需要 XDS560 跟踪仿真器来使用它?
相关词条
- CToolsLib
- XDS560 Trace(XDS560 跟踪)
- Embedded Trace Buffer(嵌入式跟踪缓存)或 ETB
- Advanced Event Triggering(高级事件触发)
- Unified Breakpoint Manager(统一断点管理器)
