Please note as of Wednesday, August 15th, 2018 this wiki has been set to read only. If you are a TI Employee and require Edit ability please contact x0211426 from the company directory.

Capturing ETB Trace Data With ETBLib/zh

From Texas Instruments Wiki
Jump to: navigation, search

通过 ETBlib 捕获 ETB 跟踪数据

简介

本页将演示如何借助 ETB 编程库 ETBLib 捕获 ETB 跟踪数据并将其从芯片内删除。

所需的工具

  • 具有目标仿真器的 Code Composer Studio v4
  • TCI6488 目标卡

使用的软件

演示应用程序

概述

演示应用程序名为 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 文件的位置,并选择该文件
  • 下一个窗口将要求浏览文件夹。导航至包含源代码的文件夹,并选择该文件夹(可选)
Decoded etb data ta.PNG

在右侧图中,我们可以看到在跟踪分析器中解码之后,跟踪数据的样子。


独立跟踪显示

Code Composer Studio v4 附带独立跟踪显示查看器,可用于查看 .tdf 文件。此应用程序称为 TraceDisplay.exe,可在 <%CCS_INSTALL_DIRECTORY%>/ccsv4/emulation/analysis/bin 目录中找到。打开 TraceDisplay.exe 时,您将看到一个对话框,提示无法连接到跟踪数据服务器并将以独立模式操作。只需确认对话框即可,因为我们希望以独立模式操作。

一旦打开了跟踪显示应用程序,即可以选择“File->Open(文件->打开)”,通过对话框进行导航以选择您希望查看的 .tdf 文件。在一些情况下,一旦您指定了 .tdf,跟踪显示将要求您指定相关的 .out 文件。如果发生这种情况,只需在对话框中进行导航以选择在捕获跟踪数据时使用的 .out 文件即可。(如果要保存这些文件以便日后查看,则还要保存相关的 .out 文件,这点很重要。如果 .out 文件并非在捕获跟踪数据时所使用的同一文件,则跟踪解码器将无法检索数据。)

Decoded etb data.PNG

在右侧图中,我们可以看到解码之后,跟踪数据的样子。顶部框架显示应用程序的原始汇编级别执行情况。我们看到每条指令的程序地址、与每条指令相关的计时周期计数以及每条指令的反汇编。在较低窗格中,我们可以看到 C 源代码。在顶部窗格中滚动时,如果位于指定为源代码目录的目录所含的代码位置时,较低窗格将保持同步。

指定源代码目录

若要为跟踪显示指示包含源代码的文件夹,请右键单击上述显示内容的顶部窗格,然后选择“Source->Set Source Directory(源代码->设置源代码目录)”。为将目录添加到列表中,请选择“New Folder(新建文件夹)”,然后导航到所需的目录并选择“OK(确定)”。必要时重复此操作。可以选择自动包括子目录,但是如果子目录太多,这会显著减慢搜索速度。

常见问题

问:我是否需要 XDS560 跟踪仿真器来使用它?

  • 答:不需要。任何支持 ETB 集合的 JTAG 仿真器均有效。

相关词条