Capturing ETB Trace Data With ETBLib

From Texas Instruments Wiki
Jump to: navigation, search

Chinese (中文) translation

Introduction

This page will walk through a demo on how to capture ETB Trace data and moving it off chip with assistance from ETBLib, an ETB programming library.

Tools Needed

  • Code Composer Studio v4 with Target Emulator
  • TCI6488 Target Card

Software Used

  • ETBLib - Embedded Trace Buffer Programming Library
  • DSPTraceLib - DSP Trace Programming Library
  • AETLib - Advanced Event Triggering Library

Demo Application

Overview

The demo application is called etblib_demo. It is a DSP/Bios based sample application. There are three tasks that get called in a round robin fashion, each with a priority level of 2. Each of these tasks will pend on a semaphore. When all three tasks have pended, a lower level task will get executed, which will post the semaphores to re-enable the higher level tasks. In addition, there is one more task with a priority level of three. Upon execution, it immediately pends on an additional semaphore.

In order to simulate an application crash, which would cause the application to end up in the SYS_ABORT routine, we create a PRD function that will get called after 15 seconds of execution. This function will post the semaphore that allows the high priority task to execute. This high priority task then calls SYS_ABORT to simulate a crash.

GOAL

The Goal of this exercise is to demonstrate the ability to look at the resulting trace data and trace back to the point of the crash. In this case, it will involve simply tracing back to the call to SYS_ABORT. In a more realistic scenario, there would be more detailed analysis needed to find the result of the crash. But the objective of this exercise is more in the capturing of the data, rather than in the analysis of it.

Application Source Code Description

Note: Many of these variables are declared global so that they are visible within any of the application functions. Depending on the situation, sometimes internal memory buffer that will hold the ETB contents can be declared dynamically. However, in this case, we are declaring a static buffer. The reason for this is because we won't be able to dynamically allocate once SYS_abort() has been called. So we allocate statically beforehand, and then copy the values into that buffer after SYS_abort() has been called. We have created our own function myExit, which replaces UTL_halt, which is called from the abort routine. This function will copy the ETB into internal memory, set a status bitfield recording progress, and then turn off hardware interrupts and spin in an infinite loop. This same routine could be used on an ordinary application to dump the ETB values to memory before shutting down.

Downloading the Example Prroject

You can download the example project from the following locations. Note that in order to build the projects, you will need to have the AETLib, ETBLib, and DSPTraceLib/ETMLib libraries available. They can be downloaded from here. Refer associated Examples  delivered as part of the library package for integration hints for your application.

main()

The following code, implemented in main, merely sets up ths DSP?Bios objects that are needed to execute the demo. We create each of the tasks, set the appropriate arguments, and also create the PRD one-shot function. If any of this fails, we notify the user through the Bios message log.

/* Initialize the TSK_Attrs structure for all tasks */
	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");
 
	/* Start the PRD Clock */
	PRD_start(&PRD0);
 
 
	/* Create 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");
	}
 
	/* Create 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");
	}
 
	/* Create 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");
	}
 
	/* Create Low Level Task */
	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");
	}
 
	/* Create Clean Up 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");
	}

We also want to initialize the ETB and the DSP trace hardware. We also need to program AET to turn trace on and off at the appropriate points. The code comments will give you an idea of what's going on. We are configuring AET to trigger PC and Timing trace to start when we execute task0, and to end when we execute our exit function myExit. ETB will capture in a looping fashion so that when trace has ended, we will have a group of the last instructions executed.

/* Set Up the ETB Receiver */
	etbError = ETB_open(pETBErrCallBack, eETB_Circular, coreID, &pETBHandle, &etbSize);
	if(etbError != eETB_Success)
	{
		SYS_abort("Error opening ETB");
	} else {
		LOG_printf(&trace, "ETB Opened");
	}
 
		/* Enable ETB receiver */
	etbError = ETB_enable(pETBHandle, 0);
	if(etbError != eETB_Success)
	{
		SYS_abort("Error enabling ETB");
	} else {
		LOG_printf(&trace, "ETB Enabled");
	}
 
 
	/* AETLIB Programming should be done here */
 
	/* Configure AET to start at the task0 function
	   and to end at the custom myExit function
	*/
 
	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");
	}
 
	/* Set Up the Trace Start Job */
	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);
 
	/* Set Up the Trace End Job */
	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);
 
	/* Enable AET */
	if (AET_enable()){
		SYS_abort("Error Enabling AET");
	}else{
		LOG_printf(&trace, "AET Enabled");
	}
 
 
	/*** Setup Trace Export ***/
	/* Open DSP Trace export module */
	dspTraceError = DSPTrace_open( pDSPErrorCallBack, &pDSPHandle);
	if(dspTraceError != eDSPTrace_Success)
	{
		SYS_abort("Error opening DSP Trace Export block");
	}else{
		LOG_printf(&trace, "Trace Export Opened");
	}
 
	/* Setup trace export clock to 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");
	}
 
	/* Enable DSP Trace */
	dspTraceError= DSPTrace_enable(pDSPHandle, 0, testPattern);
 
	if(dspTraceError != eDSPTrace_Success)
	{
		SYS_abort("Error enabling DSP trace export");
	} else {
		LOG_printf(&trace, "DSP Trace Enabled");
	}
 
	/* Start the PRD Clock */
	PRD_start(&PRD0);

dumpEtb()

Once the application has executed for ~15 seconds, the PRD function will be executed, posting the semaphore that allows the high priority task to call SYS_abort() to simulate the application crash. The custom exit function calls dumpEtb(), which reclaims the resources used by AET, moves the ETB Data from the ETB to processor memory, disables and closes the DSP Trace interface, and disables and closes the ETB interface. Through each of these steps, we monitor sucess by updating the bits in the global variable statusCode. When we end up in the infinite loop after SYS_abort(), the value signaling total success in the transport is one of either 0x1F7 (ETB has not wrapped) or 0x1EF (ETB Wrapped). In a typical case, rather than using these bit fields, we'd just use a printf to tell the user where the error occurred. We can't do that here because we can't call printf after we've called SYS_abort.

void dumpEtb(){
 
 
	// Release all of the AETLIB Resources
	AET_releaseJob(startJob);
	AET_releaseJob(endJob);
	AET_release();
 
	// Disable Trace export
	dspTraceError = DSPTrace_disable(pDSPHandle);
	if (dspTraceError == eDSPTrace_Success) {
		statusCode |= STS_TRACE_EXPORT_DISABLED;
	}
 
	/* Disable Trace Capture - ETB Receiver */
	etbError = ETB_disable(pETBHandle);
	if (etbError == eETB_Success){
		statusCode |= STS_ETB_DISABLED;
	}
 
	/* Check the ETB status */
	etbError = ETB_status(pETBHandle, &etbStatus);
	if (etbError == eETB_Success){
		statusCode |= STS_ETB_STATUS;
	}
 
	/* Move the ETB data into memory */
	if (etbStatus.canRead == 1)
	{
		etbWordsAvailable = etbStatus.availableWords;
		if (etbStatus.isWrapped == 1){
			statusCode |= STS_ETB_WRAPPED;
		}else{
			statusCode |= STS_ETB_NOT_WRAPPED;
		}
 
		/*
		 * Allocate the buffer for copying the ETB to
		 * Set the memPtr to the array that we've statically allocated
		 */
		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;
		}
	}
 
 
	/* Close the handle to the ETB */
	etbError = ETB_close(pETBHandle);
 
	if (etbError == eETB_Success)
	{
		statusCode |= STS_ETB_CLOSED;
	}
 
	/* Close the handle to DSPTrace */
	dspTraceError = DSPTrace_close(pDSPHandle);
	if (dspTraceError == eDSPTrace_Success)
	{
		statusCode |= STS_TRACE_EXPORT_CLOSED;
	}
 
}

Exporting the Data

Typically, I set a breakpoint on the asm(" nop") instruction in the myExit() function. The breakpoint will get hit as soon as the application has _crashed_. You can also potentially just wait for the application to execute a sufficient amount of time, and then manually halt it. In both cases, the PC should be in the same location.

When we have halted, the ETB contents will have been copied to CPU memory at the location of the array etb_buf. You should now put the following three values in the CCS watch window

  • etb_buf - Location of the ETB buffer values
  • etbWordsAvilable - Number of words in the buffer
  • statusCode - Error Status code.

If the error status code is either 0x1F7 or 0x1EF, it is safe to proceed. You then want to open the CCS Scripting Console window and enter a command to export the data to a bin file. In this case, we will use the name "etb_test.bin". You can use whatever name you choose. We will assume that the .out file is stored in c:\temp\, and will use the same path for the bin file.

In the command window, enter the following command, replacing the values as necessary. Replace startAddr with the value in etb_buf in the watch window Replace length with the value in etbWordsAvailable in the watch window.
saveRaw(startAddr, PAGE_PROGRAM, "C:\\temp\\etb_test.bin", length , 32, true)


Notes

Alternatively, the example could use CIO file operation to move the ETB buffer to the host (assuming you are connected to the target via CCS).


In real world scenario and where CCS is not connected, you would need to identify your mechanism (Ethernet ot something else) to transfer ETB content to the host in a binary file format. 

Converting and Viewing the Data

To this point, the data that we have captured is simply raw etb data in binary form. We need to convert this data to human readble format for analysis . There are tools supplied with Code Composer Studio that will accomplish this task.

bin2tdf - Converts field captured hardware trace data into a format (.tdf) that can be imported in standalone CCS Trace Visualizer.

td - This is a command line utility that is typically used in a standalone or scripting environment. The trace output can be in a text or CSV format.  

FAQ

Q: Do I need an XDS560 Trace emulator to use this?

  • A: No. Any JTAG emulator which supports ETB collection will work.

Related


CN Capturing ETB Trace Data With ETBLib