Developer Tools: Debug and Log

Overview

Debugging an embedded system can be done in multiple ways.  For Moto Mod basic logging can be done along with using a Joint Test Action Group (JTAG) or Serial Wire Debug (SWD) debugger.  For convenience and to keep development costs low the MDK comes with an integrated FTDI4232 which can be used for both JTAG and SWD as well as UART logging.  This document will cover how to use this for JTAG to the Motorola High Speed Bridge (HSB) ARM Cortex M3 as well as SWD for the Mods Microcontroller (MuC) ARM Cortex M4.

Using GNU DeBugger (GDB) with the FTDI via OpenOCD is covered in this document.  GDB will use a socket to communicate with OpenOCD, which will communicate with the target through the integrated FTDI4232.  The FTDI4232 will emulate JTAG for the Moto Bridge and emulate SWD for the MuC. 

Connecting to the FTDI for JTAG or SWD requires the following:

In order for the debug Type C connector to power the phone the DIP Switch B4 must be in the on position.  See the MDK User Guide for more information on the hardware including the DIP switches.  The DIP Switch locations are called out in the figure below:


Document Conventions

Commands are presented in text boxes within the document. The prompt within the box indicates which tool the command is to be used with.

  • The following box and command prompt is used for commands executed on the command line (in a terminal emulator):
$
  • The following box is used for command issued within the OpenOCD telnet session:
>
  • The following box is used for commands issued within GDB:
(gdb)
  • A hash mark (‘#’) within any of the command windows indicates a comment and does not need to be issued on the command line.
  • Filenames which need to be filled in when the command is executed are indicated as <filename>.
  • Commands and paths listed within the text will are in a fixed width font.

Debugger Usage

Connecting to the Target

The procedure for connecting to the target is as follows:

  1. Connect your host to debug port USB #1 with a USB Type-C cable
  2. Start OpenOCD to communicate with the target
  3. Start GDB and connect to the OpenOCD target interface of either the MuC or MHB
  4. Configure hardware specifics for breakpoints and watchpoints
  5. Load symbols into GDB
  6. Optionally, load the code onto the target flash or RAM

NOTE: Sleep mode on the MuC complicates the connection procedure because the internal debug hardware may be powered down. The simplest fix is to disable power management mode by disabling CONFIG_PM in the MuC defconfig file. If it cannot be disabled, the MuC will exit sleep mode when receiving characters over the MuC nuttx shell. The MuC nuttx shell is attached to Port C of the FTDI4232. Simply attach to the USB UART for Port C via a terminal emulator and send characters (“enter” works well) during the connection procedures. See the section “Accessing the MuC nuttx Shell” below for more details.

Starting OpenOCD

The following commands are used to start OpenOCD:

Starting OpenOCD Server for the MuC without Resetting

$ openocd -f board/moto_mdk_muc.cfg &

This procedure will attach to an already running MuC. You will need to be sure the MuC is not in low power sleep mode when attaching.

Starting OpenOCD Server for the MuC with Reset

$ openocd -f board/moto_mdk_muc_reset.cfg &

The reset configuration will reset the MuC during the connection phase of OpenOCD. The reset will guarantee the MuC is not in low power mode. The side effect of this is that the current MuC state will be lost.

For the MuC the default OpenOCD port is 3333 for GDB and 4444 for Telnet.

Starting OpenOCD Server for the Motorola High-Speed Bridge

$ openocd -f board/moto_mdk_hsb.cfg &

For the MHB the default OpenOCD port is 3334 for GDB and 4445 for Telnet.

The following scripts are involved in the startup process for OpenOCD:

Script Location Description
moto_mdk_hsb.cfg openocd/tcl/board Main configuration for the Motorola High-Speed Bridge. Includes the dependant files. Changing port numbers can be done in this configuration file.
moto_mdk_muc.cfg openocd/tcl/board Main configuration for the MuC. Includes the dependant files. Changing port numbers can be done in this configuration file.
moto_mdk_muc_reset.cfg openocd/tcl/board Same as moto_mdk_muc.cfg except this script performs a reset of the MDK before SWD attach.
moto_mdk_muc_common.cfg openocd/tcl/board Contains the common code and defines for the normal and reset MuC configurations. This includes the routines needed to enable and disable TSB SPI flash.
hsb.cfg openocd/tcl/chip/motorola Configuration for the Motorola High-Speed Bridge.
moto_mdk_jtag.cfg openocd/tcl/interface/ftdi Configuration of how the onboard FTDI chip communicates over JTAG.
moto_mdk_swd.cfg openocd/tcl/interface/ftdi Configuration of how the onboard FTDI chip communicates over SWD.
moto_mdk_swd_reset.cfg openocd/tcl/interface/ftdi Same as moto_mdk_swd.cfg except this script performs an FTDI reset of the MDK during configuration.
stm32l4x.cfg openocd/tcl/target Configuration file for connection to an STM32L4x series ARM Cortex M4. Includes convenience function definitions.

Starting GDB

Once OpenOCD is up and running, the next step is to start GDB:

$ arm-none-eabi-gdb

After starting GDB you must connect to the remote target and configure hardware specifics for watchpoints and breakpoints. If you are running the debugger interface server (OpenOCD) on the same machine as the target, then you can simply connect to the correct port on the localhost. If you are running it on a different machine then you will have to specify that machine's name or ip address in instead of "localhost" below:

Connecting GDB to the MuC

(gdb) target extended-remote localhost:3333
(gdb) set can-use-hw-watchpoints 1

Connecting GDB to the Motorola High-Speed Bridge

(gdb) target extended-remote localhost:3334
(gdb) set remote hardware-breakpoint-limit 6
(gdb) set remote hardware-watchpoint-limit 4

Loading the Code and Symbols

For systems which have the code in flash memory, the symbol table must be loaded. It is also possible for the debugger to directly load the code into RAM or FLASH.

Loading Symbol Table Only

Use the following command to load the symbols into the debugger manually:

(gdb) file <elf file>

Where <elf file> will be either nuttx, or boot_hdk.elf.

If you are developing your own Moto Mod, you may have created your own target. If so, use those files in place of the ones listed above.

Loading Code

If you have not placed the code into the phone’s file system to be automatically loaded, it may be desirable to have the debugger load the code into the HSB RAM or the MuC flash. To do this in GDB, first run the file command as stated in “Load Symbol Table Only” section. Once symbols load, run the following command to load the code into RAM on the HSB or FLASH on the MuC:

(gdb) load

Additional GDB Setup

Once the code and/or symbols are loaded some additional steps may be required to debug the target. Use the following gdb commands as needed for your setup.

The default OpenOCD configuration will connect to a target which is already running. This can be changed by adding a halt command to the end of the moto_mdk_hsb.cfg. However, in most cases leaving the target running until the problem being debugged happens may be the best option. In this case gdb needs to be told that the target is running after gdb is started:

(gdb) continue

If breakpoints will be used, the watchdog timer on the HSB should be disabled. If it is not the target may reset itself when the watchdog timer expires. This must be done after the all initialization code on the HSB has run. If connecting to an already running target this initialization will most likely be completed once gdb attaches to OpenOCD. It is also recommended that a breakpoint be set at up_assert to allow for debugging of exceptions. To disable the watchdog and set a breakpoint at up_assert use the following sequence:

(gdb) ^C
(gdb) set *(unsigned int  *)0x40002008 = 0x00000000
(gdb) break up_assert
(gdb) continue

Please be aware the watchdog on the MuC will automatically stop when the MuC execution is halted, thus no disable code is needed.

Python Enable GDB

GDB can be built with support for Python scripting. This is useful to add commands which cannot be handled with simple GDB scripting. A few commands to help with nuttx debugging are included in the file $BUILD_TOP/nuttx/nuttx/tools/gdb_nuttx_data.py. See “Useful GDB Commands” or take a look at the file for the commands supported.

To see if Python is enabled in GDB run:

(gdb) show configuration

If the line --with-python= (where is a path to Python) shows up, Python is enabled. To load the Python script run:

(gdb) source -s <hdk_root>/nuttx/nuttx/tools/gdb_nuttx_data.py

Useful GDB Commands

The table below lists useful GDB commands. For the Python based commands to work the gdb_nuttx_data.py script must be loaded. See “Python Enabled GDB for more details.

Function Command Description Location
Dump Memory dump
<hexb|hexh|hexw|hexl>
<address> <size>
Dumps size bytes from the address or symbol provided in both hex and character format.

Where:
  • address is the address or a variable name to be dumped
  • size is the size in bytes to be dumped
  • hexb formats the data in hex bytes (8 bits)
  • hexh formats the data in hex half words (16 bits)
  • hexw formats the data in hex words (32 bits)
  • hexl formats the data in long words (64 bits)
Python
x/<n><f><s> Dump n memory units of size s with format x.

Where:
  • n - Is the number of bytes (prefix with 0x for hex)
  • f - Is the format of the data
  • x - Hex
  • c - Character
  • s - Is the memory unit size
  • b - Byte (8 bits)
  • h - Halfword (16 bit)
  • w - Word (32 bit)
  • l - Long word (64 bit)
GDB
Dump Nuttx Log dump syslog Prints the nuttx system log. Python/Nuttx
Dump reglog dump reglog Prints the register log if it is configured in the nuttx build. Python/Nuttx
Dump HSB I2S Regs dump i2s Prints the contents of the HSB I2S registers. Python/HSB
Dump Registers info regs Dumps all registers, Can specify which ones after the command. GDB
Disassemble disassemble /m <address> Disassembles the code and mixes in the high level source at address.

Where:
  • address is an address or symbol (function name)
GDB
Heap Info print g_mmheap Prints the raw nuttx heap data. Nuttx
Stack Backtrace backtrace Prints the stack backtrace with local variables included. GDB
Dump Locals info locals Prints the known local variables from the stack/registers. GDB
Symbol info symbol <address> Gets the symbol name for the given address. GDB
info address <symbol> Gets the address for a symbol. GDB
Start Execution cont Starts execution at the current program counter. GDB
Set Breakpoint break <address> Sets a breakpoint at the address or symbol provided. GDB
Set Watchpoint watch <address> Sets a watchpoint at the address provided. Please note if can-use-hardware-watchpoint is not set or too many watchpoints are in use this will be very slow. GDB
Print Variable print/<f> <address> Print a high level variable. The / is optional. GDB
Display uC Memory Map info mem Displays the memory map for the remote target. GDB
Turn off press to continue set pagination off Turn off the prompt to press any key to continue. GDB
Array Printing set print array on
set print elements <size>
Print arrays a little nicer.

Where:
  • size - Is the number of array elements to print.
GDB

NOTE: When specifying addresses in gdb they must be prefixed with an ‘*’ for example to set a breakpoint at address 0x10000000 the command ‘break *0x10000000’ must be used.

Standalone OpenOCD

As mentioned previously, OpenOCD can be used in a standalone mode. To communicate with it you must open a telnet session:

# Start openocd for the MuC
$ openocd -f board/moto_mdk_muc.cfg &
$ telnet localhost 4444

# Start openocd for the HSB
$ openocd -f board/moto_mdk_hsb.cfg &
$ telnet localhost 4445

Once attached, a full list of commands can be obtained using the help command. Some useful commands are listed below for quick reference. For a complete list with descriptions, please see the OpenOCD online documentation.

Function Command Description
Memory Display md<size> <address> <count> Display the memory at <address> for <count> units of <size> where <size> is:
  • b - for byte (8 bits)
  • h - for half words (16 bits)
  • w - for words (32 bits)
Memory Write mw<size> <address> <data> Write <data> of <size> to the memory at <address> where <size> is:
  • b - for byte (8 bits)
  • h - for half words (16 bits)
  • w - for words (32 bits)
Memory Modify mm<size> <address> <set> <clear> Set the bits in <set> and clear the bits in <clear> at <address> where <size> is:
  • b - for byte (8 bits)
  • h - for half words (16 bits)
  • w - for words (32 bits)
Erase MuC Flash stm32l4x mass_erase 0 Erase the entire MuC flash including the bootloader. Please note the last 0 is for bank 0, however all banks on the STM parts are mapped to page 0.
Program MuC Bootloader flash write_image erase unlock mods/bin/boot_hdk-p2_mhb.bin 0x08000000 Write the bootloader to the MuC.
Halt Execution halt Stops the execution at it’s current location.
Resume Execution resume [address] Resume execution at the current program counter or address if specified.
Breakpoint bp <address> Set a breakpoint at <address>

The following commands are defined in moto_mdk_muc_common.cfg and can be used when debugging the MuC only:

Function Command Description
Power HSB On hsb_pwr_on Power up the HSB and take it out of reset.
Power HSB Off hsb_pwr_off Power off the HSB and assert the reset line.
Enable HSB Flash hsb_flash_enable Move the HSB SPI flash from HSB control to MuC control.
Disable HSB Flash hsb_flash_disable Move the HSB SPI flash from MuC control to HSB control.
Reset the HSB hsb_reset_assert Assert the HSB reset line.
Run the HSB hsb_reset_deassert Take the HSB out of reset.
Read the HSB flash hsb_flash_read <filename> Enables the HSB flash and reads it into a binary file <filename>
Erase the HSB flash hsb_flash_erase Erases the entire HSB flash.
Program the HSB flash hsb_flash_program <filename> Enable the HSB flash and program it to the contents of the binary file <filename>.

Flashing via OpenOCD

Programming the MuC Bootloader

The Moto version of OpenOCD includes support for programming both the MuC internal flash and the HSB SPI flash. Programming of the bootloader in the MuC flash can be done with the following command run in the OpenOCD telnet session:

> halt
> flash write_image erase unlock out/boot_hdk.bin

Programming the HSB from the MuC

Programming the HSB SPI flash is a bit more complicated. The SPI flash is connected to both the MuC and the HSB. This is done so that it can be reprogrammed through the MuC during normal operation. To complicate matters further JTAG support is disabled when the HSB boots and is enabled by the code in the SPI flash. As a result it cannot be flashed through JTAG on the HSB when the flash is blank or not booting correctly. To program the flash on the HSB through the MuC use the following series of commands from the MuC OpenOCD telnet session:

> halt
> hsb_flash_enable
> flash erase_sector 1 0 2047
> flash write_bank 1 $BUILD_TOP/nuttx/nuttx/nuttx.tftf 0
> hsb_flash_disable

A shortcut command to program the flash is also available in the MuC OpenOCD telnet session:

> halt
> hsb_flash_program $BUILD_TOP/nuttx/nuttx/nuttx.tftf

Programming the SPI flash from the MuC can take 2 minutes or longer. Reading can take 11 minutes or longer. If you are working with the HSB flash on the MuC, and you do not run hsb_flash_enable, the command may not flag an error, but the result will not be what is desired. For example you will be able to read the flash with flash read_bank, but all zeros will be returned.

Programming the HSB from the HSB

To reprogram the HSB SPI flash via HSB JTAG the following commands can be used over the OpenOCD telnet session. As noted above, JTAG is enabled in the HSB SPI flash code, this cannot be done if the HSB has not fully booted or is blank.

> halt
> flash erase_sector 0 0 2047
> flash write_bank 0 $BUILD_TOP/nuttx/nuttx/nuttx.tftf 0

Depending on the firmware loaded into the MuC it may attempt to restart the HSB when debugging or flashing. To avoid this simply start an OpenOCD debugging session on the MuC and halt execution. It can then be resumed when the HSB debugging is completed.


Logging

Accessing the MuC nuttx Shell

Logging done on both the Motorola High-Speed Bridge (MHB) and the MuC can be accessed through the nuttx shell running on the MuC. The MHB InterProcess Communication (IPC) is used between the MuC and the MHB to move log data from the MHB to the MuC. Since the serial port on the MHB is used for IPC, the serial port is not available for nuttx communication.

The Reference Moto Mod includes an FTDI4232 to handle both JTAG/SWD debugging as well as UART to USB translation. The FTDI is attached to the debug USB Type C connector (USB #1) on the HDK. When it connects to a PC, the following will enumerate:

Purpose FTDI Port
MuC Serial Wire Debug A (if00) /dev/ttyUSB<n>
APBE JTAG B (if01) /dev/ttyUSB<n+1>
MuC nuttx Shell C (if02) /dev/ttyUSB<n+2>
APBE-MuC IPC D (if03) /dev/ttyUSB<n+3>

To determine the value of n use ls /dev/ttyUSB* on linux and check for new entries after plugging in the device. The following command can also assist in determining which ttyUSB the FTDI ports are connected. Match the FTDI Port from the above table to the FTDI_Quad_RS232-HS-if??-port0 string produced by the following command:

$ ls /dev/ttyUSB* | xargs -I{} bash -c 'echo -n {}": "; udevadm info --name={} |grep " serial/by-id" |tail --bytes +21'

For the MuC nuttx shell, communications parameters must be set up as:

Parameter Value
Baudrate 115200
Data Bits 8
Parity None
Start Bits 1
Stop Bits 1

Before using a serial console on a Linux environment, you will need to provide your account access to the serial devices on your computer.

$ sudo adduser <username> dialout

You will need to log out and log back in to apply the permissions. You will also need a terminal emulation program. Picocom is available on most Linux distributions and provides a simple interface. It is not installed by default. To install Picocom:

$ sudo apt-get install picocom

After attaching the debug USB-C connector to the USB #1 port, you will have four additional tty devices added to your system. Start Picocom as follows replacing [ttyUSBx] with the appropriate interface from the above steps:

$ picocom  -l -b 115200 /dev/[ttyUSBx]

Enabling Moto High Speed Bridge Debugging

To access the HS Bridge’s debugging capabilities from the MuC Nuttx shell you need to enable the corresponding command-line app in the MuC's defconfig.

To do this, use the menuconfig tool (make menuconfig) and navigate to:

Application Configuration -->
   Mods -->
      [*] Mods MHB Client
      (apbe) Program Name

This will add the following config items to your .config file:

CONFIG_MODS_MHB_CLIENT=y
CONFIG_MODS_MHB_CLIENT_PROGNAME="apbe"

Low Level Logging

The nuttx baseline comes with simple printf-like logging built in. The following functions can be used to log data with this method:

Function Notes
lldbg(fmt_str, …) Logs the string using standard printf formats.
llvdbg(fmt_str, …) Logs the string using standard printf formats only if CONFIG_DEBUG_VERBOSE is defined in your defconfig file.

Please be aware that it takes time to print the string and convert any numbers provided to ASCII. Please refrain for using these in an interrupt context.

The method to retrieve the log depends on which system is in use. The following table outlines the procedure for retriving the logs through the debug connection:

System Log Retrieval Method
Moto Bridge On the MuC in the nuttx shell run:
apbe log
MuC On the MuC in the nuttx shell run:
cat /dev/ramlog
GDB Python Enabled and Loaded (See JTAG/SWD documentation for more details) On the GDB command line run:
dump syslog
GDB Python Disabled (Will print the log out of order, but is better than nothing.) On the GDB command line run:
print g_sysdev.rl_buffer

Advanced reglog Logging

Delays introduced through logging to a serial port can disrupt the realtime requirements of your code. To assist in these cases, the Moto Mods nuttx baseline includes a simple logging referred to as reglog. This was originally developed to log the time, address and value of register reads and writes and is very light-weight in processing. Reglog is also useful for general-purpose logging in time-critical code.

To do this, use the menuconfig tool (make menuconfig) and navigate to:

Device Drivers -->
        [*] Enable register logging to RAM for real time debugging

This will add the following config items to your .config file.

CONFIG_REGLOG=y

Once it is enabled code can be instrumented with:

reglog_log(address, value);

As mentioned previously address and value were originally intended to be register addresses and values, but they can be any uint32_t values. Only 3 uint32_t values will be added to the log:

  1. 48 MHz time value at the time of the call
  2. The address value
  3. The data value.

Depending on which processor is being used the method for retrieving the log differs. This is because the NuttX shell on the Moto Bridge cannot be directly accessed. For the Moto Bridge, MHB must be used to retrieve the values.

System reg log dump method
Moto Bridge In the MuC nuttx shell run:
apbe diag reglog
MuC In nuttx shell run:
cat /dev/reglog
GDB Python Enabled and Loaded (See JTAG/SWD documentation for more details) On the GDB command line run:
dump reglog
GDB Python Disabled (This will output the array, which will not be in since it is a circular buffer.) On the GDB command line run:
print/x g_reglog

The reglog code is capable of running in a Stack mode and a FIFO mode. By default it will startup in FIFO mode. In FIFO mode the code will treat the buffer as a circular buffer, so all calls to reglog_log will result in an entry being written. However, only the latest entries will be retained, due to the limited size of the buffer. If you need data at the start of a run Stack mode should be used. In this mode when the end of the buffer is reached reglog_log will stop adding entries. To switch between modes use the following commands:

System Stack Mode FIFO Mode
Moto Bridge In the MuC nuttx shell run:
   apbe diagreglog_stack
In the MuC nuttx shell run:
   apbe diagreglog_fifo
MuC In the MuC nuttx shell run:
   echo “stack” > /dev/reglog
In the MuC nuttx shell run:
   echo “fifo” > /dev/reglog
GDB set var g_reglog.mode = 1
set var g_relog.head = 0
set var g_reglog.tail = 0
set var g_reglog.mode = 0
set var g_relog.head = 0
set var g_reglog.tail = 0