Debugging

External Resources

Debug Interfaces

The CC13xx and CC26xx platform supports both the cJTAG and JTAG debug interfaces. Debug probes that support cJTAG, like the TI XDS110 and XDS100v3, can work natively with the CC13xx or CC26xx. Other debug probes that only support JTAG, like the IAR I-Jet and Segger J-Link, need to inject a cJTAG sequence to enable JTAG functionality. The hardware resources included on the devices for debugging are listed as follows. Not all debugging functionality is available in all combinations of debug probe and IDE.

  • Flash Patch and Breakpoint Unit (FPB) - 6 instruction comparators, 2 literal comparators

  • Data Watchpoint and Trace Unit (DWT) - 5 watchpoints on memory access

  • Instrumentation Trace Macrocell (ITM) - 32 x 32 bit stimulus registers

  • Trace Port Interface Unit (TPIU) - serialization and time-stamping of DWT and ITM events

XDS110 Debug Probe

The CC13xx or CC26xx LaunchPad has an on-board XDS110, and this is the assumed debug probe for most development.

The XDS110 is the latest entry level debug probe (emulators) for TI embedded processors. Designed to be a complete solution that delivers JTAG and SWD connectivity at a low cost, the XDS110 is the debug probe of choice for entry-level debugging of TI microcontrollers, processors and SimpleLink devices. Also, both Core Processor and System Trace are available for all Arm and DSP devices that support Embedded Trace Buffer (ETB).

It is possible to buy the XDS110 as a standalone debugger. See the XDS110 JTAG Debug Probe product page.

Debugging RF Output

As mentioned in the CC13x2 CC26x2 SimpleLink Wireless MCU Technical Reference Manual, the RF output can be mapped to pins on the LaunchPad for RF signal debugging. These pins are intended to be used when connecting an RF range extender. However, they can also help in instances where it is unclear if the device is transmitting or receiving in the right window.

To enable RF output debugging, you will need to map the RF Core LNA enable and PA enable output or Tx start signals to a GPIO. See Enable RF Observables for an example of using DIO6 to output the LNA enable signal (logic high corresponds to an Rx) and DIO7 to output the Tx start (logic high corresponds to a Tx). These pins correspond to the Red (DIO6) and Green (DIO7) CC13xx or CC26xx LaunchPad LEDs and if the jumpers are connected, these LEDs will flicker to indicate RF activity. By connecting a logic analyzer to these pins, the RF activity over time can be recorded. If these pins are unavailable in your project, you can select any available DIO.

Listing 18. Enable RF Observables
/*******************************************************************************
 * INCLUDES
*/
#include <driverlib/ioc.h>

// ...

int main()
{

    /*
     * Add the following after Board_initGeneral();
     * Be sure IOIDs used below are not used by any entries in PIN or
     * GPIO tables from the board files
     */

    // Map LNA enable pin RFC_GPO0 to DIO6
    IOCPortConfigureSet(IOID_6, IOC_PORT_RFC_GPO0,
                        IOC_IOMODE_NORMAL);
    // Map Tx start pin RFC_GPO3 to DIO7
    IOCPortConfigureSet(IOID_7, IOC_PORT_RFC_GPO3,
                        IOC_IOMODE_NORMAL);

    // ...

}

For more details, see the “External Signaling” chapter of the CC13x2 CC26x2 SimpleLink Wireless MCU Technical Reference Manual.

Configuring Debugger in Code Composer Studio

If only one debug probe is attached, CCS will automatically select the connected debug probe when a debug session is started. You can start a debug session by clicking the debug icon img_icon_debug_ccs on the toolbar.

If more than one debug probe is attached when a debug session is started, CCS will prompt you to select a debug probe as seen in Figure 124. CCS will save the selected debug probe in the target configuration for the project.

../_images/ccs_probe_select.png

Figure 124. CCS Probe Selection

To set or change the selected debug probe for a project, it is necessary to set the serial number for the probe in the project’s target configuration.

Find The Serial Number

To find the serial number for XDS110 debug probe, open a command prompt and run the xdsdfu.exe command for your CCS version, e.g. c:\ti\ccs_7_3_0_00019\ccsv7\ccs_base\common\uscif\xds110\xdsdfu.exe -e. This will enumerate all the attached XDS110 debug probes. This should result in output like the following.

C:\>c:\ti\ccs_7_3_0_00019\ccsv7\ccs_base\common\uscif\xds110\xdsdfu.exe -e

USB Device Firmware Upgrade Utility
Copyright (c) 2008-2015 Texas Instruments Incorporated.  All rights reserved.

Scanning USB buses for supported XDS110 devices...


<<<< Device 0 >>>>

VID: 0x0451    PID: 0xbef3
Device Name:   XDS110 Embed with CMSIS-DAP
Version:       2.3.0.9
Manufacturer:  Texas Instruments
Serial Num:    L1100017
Mode:          Runtime

<<<< Device 1 >>>>

VID: 0x0451    PID: 0xbef3
Device Name:   XDS110 Embed with CMSIS-DAP
Version:       2.3.0.9
Manufacturer:  Texas Instruments
Serial Num:    L11000EN
Mode:          Runtime

Found 2 devices.

C:\>

For XDS100 series debug probes, open a command prompt and run the xds100serial.exe command for your CCS version, e.g. c:\ti\ccs_7_3_0_00019 \ccsv7\ccs_base\common\uscif\xds100serial.exe. This should result in output like the following:

C:\>c:\ti\ccs_7_3_0_00019\ccsv7\ccs_base\common\uscif\xds100serial.exe
Scanning for XDS100 emulators...

VID/PID    Type            Serial #    Description
0403/a6d1  XDS100v3        06EB12213144  Texas Instruments XDS100v3

C:\>

Configure Serial Number

It may be necessary to set or unset the selected debug probe. Use the following steps to do this.

  1. Open the target configuration file.

../_images/ccs_targetconfig_file.png
  1. Open the Advanced pane.

  1. Choose the top-level debugger entry.

../_images/ccs_targetconfig_probe.png
  1. Enter the serial number.

../_images/ccs_targetconfig_serial.png

Connecting to the XDS Debugger

If only one debugger is attached, the IDE uses it automatically when you click the img_icon_debug_ccs button in CCS or img_icon_debug_iar in IAR.

If multiple debuggers are connected, you must choose the individual debugger to use. The following steps detail how to select a debugger in CCS and IAR.

Configuring Debugger in IAR

If only one debugger is attached, IAR uses it automatically when you click the img_icon_debug_iar button.

If more than one debug probe is connected, use the following steps to have IAR always prompt to select the connection.

  1. Open the project options (ProjectOptions)

  2. Go to the Debugger entry.

  3. Go to Extra options.

  4. Add the following command line option: --drv_communication=USB:#select

Breakpoints

Comparators in the Flash Patch and Breakpoint Unit (FPB) of the CC13xx or CC26xx LaunchPad are used to break on an instruction fetch. This can be used to patch a function as it is fetched from instruction memory. Or these comparators can be used to supply a Breakpoint (BKPT) instruction to the CPU. These instructions halt the processors operation, waiting for the debug probe.

Considerations

While breakpoints are a useful tool for debugging code online, they have the possibility of altering the execution flow of a piece of code.

Breakpoints and Timing

Synchronous RF protocols are timing sensitive. Breakpoints can easily halt the execution long enough to lose network timing and break the link.

To still be able to debug, place breakpoints as close as possible to where the relevant debug information can be read or step through the relevant code segment to debug.

After you hit a breakpoint and read out the necessary debug information, it is recommended that you reset the device and re-establish the connection.

Breakpoints and Optimization

When compiler optimizations are enabled, toggling a breakpoint on a line of C code may not result in the expected behavior. Some examples include the following.

Code is removed or not compiled in

Toggling a breakpoint in the IDE results in a breakpoint somewhere other than the intended line. Some IDEs disable breakpoints on nonexistent code.

Code block is part of a common subexpression

A breakpoint might be trigged from a function or piece of code near the marked line. This might have been due to the compiler reusing sections.

An if clause is represented by a conditional branch in assembly

A breakpoint inside an if clause always breaks on the conditional statement, even when the condition is not true.

TI recommends selecting an optimization level as low as possible when debugging. See Optimizations for information on modifying optimization levels.

Breakpoints in CCS

Note

CCS reserves one hardware breakpoint for instruction stepping.

To toggle a breakpoint, do any of the following.

  • Double-click the area to the left of the line number.

  • Press Ctrl + Shift + B.

  • Right-click on the line.

A breakpoint set on line 207 looks like the following.

../_images/ccs_breakpoint_ex.png

Figure 125. Breakpoint on line 207. Debugger halted at start of main().

For an overview of the active and inactive breakpoints, click on ViewBreakpoints.

../_images/ccs_breakpoint_list.png

Figure 126. List of breakpoints. Right-click to edit options, or de-select to deactivate.

To set a conditional break, do as follows.

  1. Right-click the breakpoint in the overview.

  2. Choose Properties.

When debugging, Skip Count and Condition can help skip a number of breaks or only break if a variable is a certain value.

Note

Conditional breaks require a debugger response and may halt the processor long enough to break an active RF connection, or otherwise disrupt timing on the debug target.

Breakpoints in IAR

Note

IAR reserves one comparator for instruction stepping.

To toggle a breakpoint, do any of the following.

  • Single-click the area to the left of the line number.

  • Go to the line and press F9 to toggle breakpoint

  • Right-click on the line and select Toggle Breakpoint (Code).

A breakpoint looks like this:

../_images/iar_breakpoint_ex.png

Figure 127. Breakpoint on PIN_init(). Debugger halted at start of main().

For an overview of the active and inactive breakpoints, click ViewBreakpoints.

../_images/iar_breakpoint_list.png

Figure 128. List of breakpoints. Right-click to edit options, or de-select to deactivate.

To set a conditional break, do as follows.

  1. Right-click the breakpoint in the overview.

  2. Choose Edit….

When debugging, Skip Count and Condition can help skip a number of breaks or only break if a variable is a certain value.

Note

Conditional breaks require a debugger response and may halt the processor long enough to break an active RF connection, or otherwise disrupt timing on the debug target.

Watching Variables and Registers

Debuggers offer several ways of viewing the state of a halted program. Global variables are statically placed during link-time and can end up anywhere in the RAM or Flash of the chip. These variables can be viewed when then target is halted by the debugger through the Watch and Expression windows.

Unless removed due to optimizations, global variables are always available in these views. Local variables or variables that are only valid inside a limited scope are only viewable in that scope. Such variables can also be viewed with the Watch or Expression views, and may also be automatically displayed when breaking or stepping through code.

Considerations

Local variables are often placed in CPU registers and not on the stack. These variables also have a limited lifetime even within the scope in which they are valid. Depending on the optimization performed, a variable placed in a register may not have a cohesive view of the current state of the variable. Some possible solutions are:

  • Move the variable to global scope, so it remains accessible in RAM.

  • Make the variable volatile, so the compiler doesn’t place the value in a register.

  • Make a shadow copy of the variable that is global and volatile.

Variables in CCS

You can view Global Variables by doing either of the following.

  • Select ViewExpressions.

  • Select a variable name in code.

  • Right-click and select Add Watch Expression.

../_images/ccs_watch.png

Figure 129. Variable watch window. Note that you can cast values, get address and sizeof, etc.

  • Select ViewVariables to auto-variables that are present at the current location when stepping through code.

../_images/ccs_locals.png

Figure 130. Local variables. This screenshot is taken during execution of an application function.

Variables in IAR

To view Global Variables, do either of the following.

  • Right-click on the variable.

    • Select Add to Watch: varName.

  • Select ViewWatch

    • Enter the name of the variable.

../_images/iar_watch.png

Figure 131. Variable watch window. Note that you can cast values, get address and sizeof, etc.

View –> Locals show the local variables in IAR.

../_images/iar_locals.png

Figure 132. Local variables. This screenshot is taken during execution of the Simple Peripheral init function.

Note

IAR may remove the variable during optimization and inline the usage of the value. If so, add the __root directive in front.

Memory Watchpoints

As mentioned in Debug Interfaces, the Data Watchpoint and Trace Unit (DWT module) contains four memory watchpoints that allow breakpoints on memory access. The hardware match functionality looks only at the address. If intended for use on a variable, the variable must be statically allocated.

Note

If a data watchpoint with value match is used, two of the four watchpoints are used.

Watchpoints in CCS

  1. Right-click on a global variable.

  2. Select BreakpointHardware Watchpoint.

  3. Go to the list of breakpoints (ViewBreakpoints).

  4. Right-click and edit the Breakpoint Properties to configure the watchpoint.

../_images/ccs_watchpoint_add.png

Figure 133. Adding a watchpoint on a variable.

This example configuration ensures that if 0x42 is written to the memory location for Characteristic 1 in the Bluetooth Low Energy simple_peripheral example project the device halts execution.

../_images/ccs_watchpoint_configure.png

Figure 134. Configuring a hardware watchpoint to break on 8-bit write with value 0x42.

Watchpoints in IAR

  1. Right-click a variable (global).

  2. Select Set Data Breakpoint for 'myVar' to add it to the active breakpoints.

  3. Go to the list of breakpoints (View –> Breakpoints)

  4. Choose Edit... to set up whether the watchpoint should match on read, write, or any access.

../_images/iar_watchpoint_configure.png

Figure 135. Configuring a hardware watchpoint to break on 8-bit write with value 0x42.

TI-RTOS Object Viewer

Debuggers may include the RTOS Object Viewer (ROV) plug-in that provides insight into the current state of TI-RTOS, including task states, stacks, and so forth.

This section discusses some ROV views useful for debugging and profiling. More details can be found in the TI-RTOS User’s Guide, including documentation on how to add log events to application code.

Scanning the BIOS for Errors

The BIOS Scan for errors view goes through the available ROV modules and reports on errors. This functionality can be a good point to start if anything has gone wrong. This scan only shows errors related to TI-RTOS modules and only the errors it can catch.

../_images/rov_bios_scan.png

Figure 136. Scan for errors. Here a Task Stack has been overrun.

Viewing the State of Each Task

The Task Detailed view is useful for seeing the state of each task and its related runtime stack usage. This example shows the state the first time the user-thread is called. Figure 137. shows the Bluetooth low energy stack task, represented by its ICall proxy, the Idle task, the simple_peripheral task and the GAPRole task.

../_images/rov_task_detailed.png

Figure 137. Detailed view of the Tasks. Notice the address of the overrun task matches the instance id from Scan for errors.

The following list explains the column in Figure 137.

address

This column shows the memory location of the Task_Struct instance for each task.

priority

This column shows the TI-RTOS priority for the task.

mode

This column shows the current state of the task.

fxn

This column shows the name of the entry function of the task.

arg0, arg1

These columns show arbitrary values that can be given to entry function of the task. In the image, the ICall_taskEntry is given 0xb001, which is the flash location of the entry function of the RF stack image and 0x20003a30 (the location of bleUserCfg_t user0Cfg, defined in main()).

stackPeak

This column shows the maximum run-time stack memory used based on watermark in RAM, where the stacks are prefilled with 0xBE and there is a sentinel word at the end of the run-time stack.

Note

Function calls may push the stack pointer out of the run-time stack, but not actually write to the entire area. A stack peak near stackSize but not exceeding it may indicate stack overflow.

stackSize

This column shows the size of the runtime stack, configured when instantiating a task.

stackBase

This column shows the logical top of the runtime stack of the task (usage starts at stackBase + stackSize and grows down to this address).

Viewing the System Stack

The Hwi Module view allows profiling of the system stack used during boot or for main(), Hwi execution, and Swi execution. See sec-memory-management-system-stack for more information on the system stack.

../_images/rov_hwi_module.png

Figure 138. Viewing the System Stack in Hwi

The hwiStackPeak, hwiStackSize, and hwiStackBase can be used to check for system stack overflow.

ROV in CCS

To access the ROV while in a debug session in CCS:

  • Click the Tools menu.

  • Click RTOS Object View (ROV).

ROV in IAR

To access the ROV while in a debug session in IAR

  • Use the TI-RTOS menu on the menu bar.

  • Select a subview.

Warning

When using autosized heap, the ROV may display errors when accessing modules such as Task and Heap for ROV. In order to use ROV with an autosized heap you can apply the following patch to the function init() in \kernel\tirtos\packages\ti\sysbios\heaps\package.xs. The lines highlited below should be added.

 1  function init()
 2  {
 3    /* Add HeapMem's primaryHeap addresses range to ROV's memory sections */
 4    if (xdc.om.$name == "rov") {
 5            // original code omitted
 6            // .....
 7            // .....
 8
 9            /* Retrieve the MemoryImage java object. */
10            var Model = xdc.module("xdc.rov.Model");
11            var memReader = Model.getMemoryImageInst();
12
13            /* retrieve the sections list and add a new section */
14            var sections = memReader.getSections();
15            sections.addSection(0x20000000, 0x20005000);
16     }
17   }

Using the Memory Browser

Debuggers are able to show a representation of the memory on the CC13xx or CC26xx. In CCS, you can index by address or by symbol name. As an example, consider the stack that was overrun in fig-rov-task-detailed:

Simple Peripheral Task’s stack. Note BE watermark

GAPRole Task’s stack. Note it’s completely filled.

../_images/mem_browser_sbp_stack.png
../_images/mem_browser_gaprole_stack.png

The solution in this case would be to increase the stack size for the failing task and see what the stack peak really is. The stackPeak reported is relying on how many watermark bytes are overwritten, so it can’t know how much the overrun amounts to.

Because stacks are utilized from higher addresses towards lower addressed (upwards in the picture), stacks that overrun will tend to overwrite data at locations immediately before the stack.

Connect the debugger to a running target

Connecting the debugger to a target can help when you want to see the status of your target after it has been running for several hours, or even days; or if you cannot reproduce a crash with the debugger attached. Once the debugger connected to the target, all the usual functionalities (break points, step-by-step, variable view, ROV, memory view…) are available.

This step-by-step guide will help you to configure CCS in order to connect to a running target

  1. Modify the GEL file

    In a very simplistic view, the GEL files describe the way the device’s debugger has to act (more details can be found in the CCS’s help). By default, the GEL files ask the device to reset when the debugger is started up. Fortunately we can modify this:

    1. Identify the GEL file to modify

      • Start a debug session as always

      • (If needed), display the debug view

      • Right-click on the program being, “Open GEL files View”.

      • In the GEL files list, open the corresponding GEL file by double-clicking it. Chose cc26x2.gel for CC26x2 and CC13x2 devices. Chose cc26x0.gel for CC26x0 and CC13x0 devices.

      ../_images/locate_gel_file.png

      Figure 139. Locate the GEL file to modify.

      Figure 139. shows how to find the GEL file to modify.

      Note

      Another possibility consists in looking directly in <CCS directory>\ccs_base\emulation\gel for the GEL file.

      Caution

      The modifications done in a GEL file affect all the devices using the same GEL files. In other words, it affects all the CC26X2 and CC13X2 if you modified cc26x2.gel, and all the CC26x0 and CC13x0 if you modified cc26x0.gel. By default, two different CCS versions do not use the same GEL files.

    2. In the StartUp() function, comment out the code executing the reset. If needed, an explicit comment will help you to identify the code to comment out.

      Listing 19. The StartUp() function after modification.
       1StartUp(int major, int minor, int patch)
       2{
       3    /* Initialize memory map */
       4    memorymap_init();
       5
       6    /* Debugger specific handling */
       7    if(GEL_MatchesConnection(".*TIXDS.*") == 1)
       8    {
       9        GEL_LoadGel("$(GEL_file_dir)/cc26xx_connect_util.gel");
      10        GEL_LoadGel("$(GEL_file_dir)/cc26x2_xds.gel");
      11
      12        DefineResets(0);
      13
      14        // Issue Board Reset to ensure device is in a known state
      15        // Note: If you want to attach to a running target without resetting the
      16        //       device, you must comment out the following 4 lines:
      17    //  if(!GEL_IsConnected())
      18    //  {
      19    //      GEL_AdvancedReset("Board Reset");
      20    //  }
      21    }
      22    else if(GEL_MatchesConnection(".*JLink.*") == 1)
      23    {
      24        GEL_LoadGel("$(GEL_file_dir)/cc26xx_jlink.gel");
      25    }
      26    else
      27    {
      28        GEL_TextOut("Error: Unknown debugger.\n");
      29        return;
      30    }
      31}
      
    3. Save your modification and close the file. Stop your debugging session

    Caution

    The modification of the debug configurations only affects one project.

  2. Modify the Debug Configuration of your project

    Once you have clicked CCS’s debug button (the green bug), CCS is doing a lot of actions for you. For example, CCS loads the program and stops the execution of the code on the target. In our case, we don’t want CCS to load the program (as we already have a running program…). In addition, we don’t necessarily want to stop the execution of the code on the target. Fortunately, the way CCS is running a debug session is highly configurable. So let’s adapt those configurations to our needs.

    1. On the right of the Debug button, there is an arrow. Click this arrow and select Debug Configurations….

    2. Select your project

    3. Prevent CCS from loading the program: in the Program tab, chose the proper Loading options (Load symbols only)

      ../_images/debug_configuration_1.png

      Figure 140. Open the Debug configurations and modify the loading options.

    4. Prevent CCS from stopping the target: in the Target tab, deselect the option Halt the target on a connect

    ../_images/debug_configuration_2.png

    Figure 141. In the Debug configurations, modify the Connection options.

  3. [Optional] Prevent CCS from building the program before load

    As no program will be loaded, it is a bit useless to ask CCS to build an image when you start a debug session. As result, you can disable this option by using the small arrow at the right of the Flash button. Click on Build Project Before Load in order to disable the option.

    ../_images/build_project_before_load.png

    Figure 142. Disable “Build Project Before Load” option.

    Now, the debug button does not anymore load code on the device. So, how can you load a new image on the device? The easiest way is to use the Flash button and select the image to flash. Don’t forget to rebuild your image manually (as we have disabled the option before). Another solution consists in undoing all the configuration changes we did before. A third solution consists in using a different version of CCS.

Tip

You are all set now! Let the code running and, when needed, connect to the running target by using the Debug button as you usually do.

Optimizations

Compiler optimizations are great for saving space or speeding up execution. However, these optimizations can be very difficult to debug around. There are multiple levels at which optimization can be turned on or off.

Project-wide optimization settings are the most general. Sometimes, given the constraints of the device, it is impossible to lower the size optimization level. File-wide optimization settings can be used like project-wide optimizations to turn on or off certain settings. The most granular control is using compiler directives to control optimization at a function level.

Optimizations in CCS

Project-Wide Optimizations

Open the project optimization settings by going to Project PropertiesCCS BuildARM CompilerOptimization

../_images/ccs_optim_level_project.png

Figure 143. Project-level optimization setting in CCS

Single-File Optimizations

Note

Do single-file optimizations with care because this also overrides the project-wide preprocessor symbols.

  1. Right-click on the file in the Workspace pane.

  2. Choose Properties.

  3. Change the optimization level of the file using the same menu in the CCS project-wide optimization menu.

Single-Function Optimizations

Warning

Care must be taken when using pragmas, since they are very specific to the toolchain and may render non-reusable code.

Important

The TI ARM Clang compiler does not support single-function optimizations. Use Single File optimizations instead.

For additional details, check section 3 of the TI ARM Clang User’s Guide.

Listing 20. Function-level optimization setting in gcc
#pragma GCC push_options
#pragma GCC optimize ("O0")
static void myFunction(int number)
{
    // ...
    return yourFunction(other_number);
}
#pragma GCC pop_options

Optimizations in IAR

Project-Wide Optimizations

Project OptionsC/C++ CompilerOptimizations

../_images/iar_optim_level_project.png

Figure 144. Project-level optimization setting in IAR

Single-File Optimizations

  1. Right-click on the file in the Workspace pane.

  2. Choose Options.

  3. Check Override inherited Settings.

  4. Choose the optimization level.

Single-Function Optimizations

Warning

Pragmas are very specific to the toolchain, and may lead to non-reusable code. Be careful where you use these.

Use #pragma optimize=none before the function definition to deoptimize the entire function, that is, as follows.

Listing 21. Function-level optimization setting in IAR
#pragma optimize=none
static void myFunction(int number)
{
    // ...
    return yourFunction(other_number);
}

Deciphering CPU Exceptions

Several possible exception causes exist. If an exception is caught, an exception handler function can be called. Depending on the project settings, this handler may be a default handler in ROM, which is just an infinite loop or a custom function called from this default handler instead of a loop.

When an exception occurs, the exception may be caught and halted in debug mode immediately, depending on the debugger. If the execution halted manually later through the Break debugger, it is then stopped within the exception handler loop.

Exception Cause

With the default setup using TI-RTOS, the exception cause can be found in the System Control Space register group (CPU_SCS) in the register CFSR (Configurable Fault Status Register). The Arm Cortex User Guide describes this register. Most exception causes fall into the following three categories.

  • Stack overflow or corruption leads to arbitrary code execution.

    • Almost any exception is possible.

  • A NULL pointer has been dereferenced and written to.

    • Typically (IM)PRECISERR exceptions

  • A peripheral module (like UART, Timer, and so forth) is accessed without being powered.

    • Typically (IM)PRECISERR exceptions

The CFSR register is available in ViewRegisters.

When an access violation occurs, the exception type is IMPRECISERR because writes to flash and peripheral memory regions are mostly buffered writes.

If the CFSR:BFARVALID flag is set when the exception occurs (typical for PRECISERR), the BFAR register in CPU_SCS can be read out to find which memory address caused the exception.

If the exception is IMPRECISERR, PRECISERR can be forced by manually disabling buffered writes. Set CPU_SCS:ACTRL:DISDEFWBUF to 1, by either manually setting the bit in the register view in the debugger or by including <hw_cpu_scs.h> from Driverlib and calling the following.

#include <ti/devices/cc26x0r2/inc/hw_cpu_scs.h>
//..
int main()
{
    // Disable write-buffering. Note that this negatively affect performance.
    HWREG(CPU_SCS_BASE + CPU_SCS_O_ACTLR) = CPU_SCS_ACTLR_DISDEFWBUF;
    // ..
}

Using TI-RTOS and ROV to Parse Exceptions

To enable exception decoding in the RTOS Object View (ROV) without using too much memory, use the Minimal exception handler in TI-RTOS. The default choice in the TI 15.4-Stack projects is to use no exception handler.

To set this up, open the project’s SysConfig file (.syscfg) and navigate to TI-RTOSHALHwi and select the Enable Exception Decoding at runtime option.

SysConfig sets the default exception handler to Hwi_excHandlerMax, which uses the Error module to pass up errors to a customized function. To customize this, navigate to TI-RTOSRUNTIMEError Handling and enter in an Optional function to call when an error is raised.

When an exception occurs, the device should end up in that infinite loop. Inspect the ROVHwiException information.

../_images/rov_hwi_exception.png

Figure 145. Decoded exception, intentional write to address 0x0013 which is illegal. Note that writebuffering has been disabled to get a precise error location, and that m3Hwi.enableException has been set to false to get the decoding.

In this case, a bus fault was forced in the function writeToAddress by dereferencing address 0x0013 and trying to write to it:

Listing 22. Write to an address in the FLASH memory region.
void writeToAddress(uintptr_t *addr, int val)
{
    *(int *)addr = val;
}

// ..

void taskFxn(...)
{
    // ..
    writeToAddress( (void*)19, 4 ); // Randomly chosen values
}

The write instruction was placed on line 79 of application.c, as indicated. To get a precise location, the write buffer was disabled as described earlier.

It can be instructive to look at the disassembly view for the locations specified by PC (program counter) and LR (link register). PC is the presumed exception location, and LR is normally the location the failing function should have returned to. As an example, the PC at this exception:

../_images/exception_pc_of_write.png

Figure 146. Here the pc from the decoded exception was looked up in the disassembly view.

Some forensics is required here. We have from the Hwi decoding in ROV (and from the exception context in the exception hook) that the program counter was 0x708e when the exception occurred.

At that location there is a store instruction str r0, [r1] meaning, store in R0 the value of what the memory address in R1 points to. The business with SP in the figure above is related to optimization being turned off, so all local variables are stored on the stack, even though in this case R0 and R1 could have been used directly from the caller.

Now we know that the exception occurred because someone called writeToAddress with an invalid address.

Thanks to the exception decoder we can easily find the call site by looking at the call stack, but if the call stack isn’t helpful, we can look at lr, which is seen in the exception decoder to be 0x198f

../_images/exception_lr_of_write.png

Figure 147. Call site as specified in lr. Note that lr is the instruction after the call to writeToAddress because execution would have resumed here.

We can see here that R0 and R1 are initialized with constants. This means that some programmer has intentionally called the write function with an address that causes a busfault.

Most often the reason for a bus-fault is that a pointer is not initialized and a function like writeToAddress gets the pointer, assumes it’s valid and dereferences the pointer and writes to the invalid address.

Debugging guide for serial interfaces

The aim of this chapter is to give some pointers on what to check if some issues are seen when using the UART, SPI, or I2C interfaces.

Note: TI only supports how to use the serial interface modules and the TI supplied drivers for these interfaces. TI are not able to help on the usage of a non-TI device connected to I2C, SPI or UART.

I2C

  • The I2C interface needs an external pull-up resistor. On the LauchPad these are 3.3 kOhm.

  • Measure the waveform on the I2C interface with an oscilloscope to verify the SCL and SDA levels

  • Monitor the interface with a logic analyzer. Check that data on the bus is the same as the wanted data.

  • Check the interface by reading out the device ID of the sensor.

  • Check the slave address. The I2C module uses a 7-bit address. The last 8th bit is the data direction bit. This last bit is appended automatically by the driver/HW. The Slave address given to the driver has therefore to be 7-bit.

  • Ensure that the readCount and writeCount is according to the register size used. If the result register in the sensor is 16-bit, the readCount has to be set to 2.

SPI

  • Monitor the interface with a logic analyzer. Compare the waveform with the waveform given in the datasheet for the device you are communicating with.

  • Check that the CSn is set correctly.

  • Check that the frame format is set correctly. Check if the device you are communicating with uses SPO = 1 or 0 and SPH = 1 or 0.

UART

  • Make sure to connect the TX pin of one device to the RX pin of the other device, and vice versa. If flow control is enabled, then the same applies towards the CTS and RTS pins.

  • Verify all communication settings between devices, including baud rate, data length, stop/parity bits and flow control.

  • Evaluate the interface with a logic analyzer or oscilloscope, using an analyzer tool if possible to convert the waveforms into their numerical counterpart.

Debugging TFM-examples

The Trusted Firmware M examples can be debugged using code composer studio. There are a few details that need to be taken care of which are covered in this section.

Building and Running

These examples need the secure image provided in the SDK to be flashed along with the application executable themselves.

The secure image(tfm_s.axf or tfm_s.hex) is found in the path SDK_ROOT\tfm_s\build\cc26x4\production_full\Release\outputs. This secure image and the generated application .out file can be flashed in any order using UniFlash. They can be flashed in any order.

Debugging

To debug the Trusted Firmware M examples, symbols from both the non secure application and the secure image must be loaded. You can do the following in CCS.

  1. Launch the CCS debug session.

  2. Select Tools -> Debugger Options -> Auto Run and Launch Options. Disable Auto Run Option “On a program load or restart”. Set “Remember My Settings” to allow this setting to persist for subsequent debug sessions.

../_images/autorun_options.png

Figure 148. Autorun and Launch Options

  1. Exit and relaunch the debug session. Execution should be paused at _c_int00(). Steps 2 & 3 can be skipped for subsequent debug sessions.

../_images/execution_paused.png

Figure 149. Execution paused at _c_int00()

  1. Select Run -> Load -> Load Program. Select tfm_s.axf. This will overwrite the non secure application symbols.

../_images/load_secure_image_menu.png
../_images/load_secure_image.png

Figure 150. Load Secure Image

  1. Add the non secure application symbols.

../_images/add_symbols.png
../_images/add_non_secure_symbols.png

Figure 151. Add Non-Secure Image Symbols

  1. Set breakpoints if desired and resume execution to run the example.

Warning

Due to a known issue in the symbol management of CCS, you may be disallowed from placing a breakpoint in the application. In this case, load the non secure application symbols first, then add the secure image symbols. This sequence of loading non secure symbols and adding secure image symbols, forces CCS to use the correct symbol manager.

Debug single-step over non-secure PSA API

When using the CCS Debugger to single-step in your non-secure program, special care must be taken when stepping over the following PSA API functions:

  • psa_connect()

  • psa_call()

  • psa_close()

These functions must be executed with interrupts enabled. They depend on the Secure Partition Environment’s PendSV interrupt handler being invoked. The PendSV interrupt is used by the secure library to context switch into the Secure Partition.

By default, the CCS debugger is configured to:

  • Disable interrupts while single-stepping at the assembly level, causing PSA functions to misbehave if you step over them at the assembly level

  • Not disable interrupts when single-stepping at the C source code level, safely permitting the user to step over PSA functions at the C source level

The single-step behavior of the debugger can be modified by the following steps.

  1. Tools > Debugger Options > Program/Memory Load Options

  2. Disable interrupts

  3. When assembly stepping > Select

  4. When source stepping > Unselect

  5. When running > Unselect

../_images/tfm_program_load_options.png

Figure 152. Disable interrupts when assembly stepping

In many cases, it is simplest to set a breakpoint after the PSA API and then run to the breakpoint.

Runtime Object View (ROV)

Runtime Object View can be used with the Trusted Firmware M examples just like you would use with any other non-secure examples. It must be noted that the ROV session opens up with the latest symbols or program that is loaded. Adding the symbols does not affect the ROV context. For example, loading the secure image symbols after starting the debug session will result in ROV starting up in the secure image context.

../_images/secure_image_ROV.png

Figure 153. ROV when secure image symbols are loaded last

To open the ROV in the context of the non-secure application. Load the non-secure application symbols.

../_images/non_secure_app_ROV.png

Figure 154. ROV when non-secure application symbols are loaded last