Thursday, May 25, 2017

VSCode JTAG Debugging of ESP32 - Part 1

(last edited June 2, 2017 - added WSL/Ubuntu info, but not working: JTAG device not recognized in WSL "Error: libusb_init() failed with LIBUSB_ERROR_OTHER"). I opened Bash On Windows github issue #2185 for this.

These instructions are only for Ubuntu in stand-alone (or VM) machine; not (yet) for WSL/Ubuntu on Windows.

To debug in VSCode, I am using the C/C++ for VSCode Extension along with an Olimex ARM-USB-OCD-H JTAG debugger. There are 2 main parts: installing (covered here) and debug setup (covered in part 2). I also show the wiring connections here.

If you already have the toolchain installed and want to simply debug, see step 2!

I've also added a step 3 using ESP WIP OpenOCD.

In order to get VSCode debugging the ESP32, it is probably a good idea to first get regular gdb debugging working. Although I'd like to do this fully in Windows, I start with an Ubuntu VM and use PuTTY SSH and Telnet Client to connect. (see tip below on CR vs CRLF in VSCode)

I started with the esp-idf instructions http://esp-idf.readthedocs.io/en/latest/get-started/linux-setup.html with release 2.1 from https://github.com/espressif/esp-idf. Once the toolchain is installed, there's also a special version of OpenOCD here: https://github.com/espressif/openocd-esp32


Nothing new here not presented in espressif instructions; so in brief... basically for the esp-idf:

# see https://github.com/espressif/esp-idf.git

sudo apt-get install git wget make libncurses-dev flex bison gperf python python-serial

# download the toolchain, in this case the 64-bit linux version 1.22.0-61-gab8375a-5.2.0
mkdir -p ~/Downloads
cd ~/Downloads/
wget https://dl.espressif.com/dl/xtensa-esp32-elf-linux64-1.22.0-61-gab8375a-5.2.0.tar.gz

# put the download into the ~/esp/xtensa-esp32-elf/ directory
mkdir -p ~/esp
cd ~/esp
tar -xzf ~/Downloads/xtensa-esp32-elf-linux64-1.22.0-61-gab8375a-5.2.0.tar.gz

# get the esp-idf from github, don't forget the --recursive !!
mkdir -p ~/esp
cd ~/esp
git clone --recursive https://github.com/espressif/esp-idf.git


export PATH=$PATH:$HOME/esp/xtensa-esp32-elf/bin

if installing on WSL (Ubuntu on Windows), I needed to also install:

sudo apt-get install libtool
sudo apt-get install autotools-dev  
sudo apt-get install automake # needed for aclocal
sudo apt-get install pkg-config # needed to solve error: configure.ac:12: error: possibly undefined macro: AC_MSG_WARN
sudo apt-get install libusb-1.0 # otherwise No package 'libusb-1.0' found

then for the openocd-32; make sure you do not have a regular distro version of openocd installed, if you do:
sudo apt-get remove openocd

Here too, just restating the instructions from https://github.com/espressif/openocd-esp32:
mkdir -p ~/workspace
cd ~/workspace
git clone --recursive https://github.com/espressif/openocd-esp32.git
sudo apt-get install make libtool pkg-config autoconf automake texinfo
sudo apt-get install libusb-1.0
cd openocd-esp32
./bootstrap
./configure

the default configure should show this when done:

libjaylink configuration summary:
 - Package version ................ 0.1.0
 - Library version ................ 0:0:0
 - Installation prefix ............ /usr/local
 - Building on .................... x86_64-pc-linux-gnu
 - Building for ................... x86_64-pc-linux-gnu



OpenOCD configuration summary
--------------------------------------------------
MPSSE mode of FTDI based devices        yes (auto)
Segger J-Link JTAG Programmer           yes (auto)
ST-Link JTAG Programmer                 yes (auto)
TI ICDI JTAG Programmer                 yes (auto)
Keil ULINK JTAG Programmer              yes (auto)
Altera USB-Blaster II Compatible        yes (auto)
Versaloon-Link JTAG Programmer          yes (auto)
OSBDM (JTAG only) Programmer            yes (auto)
eStick/opendous JTAG Programmer         yes (auto)
Andes JTAG Programmer                   yes (auto)
USBProg JTAG Programmer                 no
Raisonance RLink JTAG Programmer        no
Olimex ARM-JTAG-EW Programmer           no
CMSIS-DAP Compliant Debugger            no

if installing in WSL, the only difference I saw:

 - Building on .................... x86_64-unknown-linux-gnu
 - Building for ................... x86_64-unknown-linux-gnu
continue with:

make
sudo make install
openocd
The make takes a rather long time. I did observe a couple of errors/warnings on WSL (didn't notice on linux). Seems to be only related to docs. Note the "makeinfo missing":
Making install in doc
make[1]: Entering directory `/home/gojimmypi/workspace/openocd-esp32/doc'
restore=: && backupdir=".am$$" && \
        am__cwd=`pwd` && CDPATH="${ZSH_VERSION+.}:" && cd . && \
        rm -rf $backupdir && mkdir $backupdir && \
        if (echo makeinfo missing; true --version) >/dev/null 2>&1; then \
          for f in openocd.info openocd.info-[0-9] openocd.info-[0-9][0-9] openocd.i[0-9] openocd.i[0-9][0-9]; do \
            if test -f $f; then mv $f $backupdir; restore=mv; else :; fi; \
          done; \
        else :; fi && \
        cd "$am__cwd"; \
        if echo makeinfo missing; true   -I . \
         -o openocd.info openocd.texi; \
        then \
          rc=0; \
          CDPATH="${ZSH_VERSION+.}:" && cd .; \
        else \
          rc=$?; \
          CDPATH="${ZSH_VERSION+.}:" && cd . && \
          $restore $backupdir/* `echo "./openocd.info" | sed 's|[^/]*$||'`; \
        fi; \
        rm -rf $backupdir; exit $rc
makeinfo missing
make[2]: Entering directory `/home/gojimmypi/workspace/openocd-esp32/doc'
make[2]: Nothing to be done for `install-exec-am'.
restore=: && backupdir=".am$$" && \
        am__cwd=`pwd` && CDPATH="${ZSH_VERSION+.}:" && cd . && \
        rm -rf $backupdir && mkdir $backupdir && \
        if (echo makeinfo missing; true --version) >/dev/null 2>&1; then \
          for f in openocd.info openocd.info-[0-9] openocd.info-[0-9][0-9] openocd.i[0-9] openocd.i[0-9][0-9]; do \
            if test -f $f; then mv $f $backupdir; restore=mv; else :; fi; \
          done; \
        else :; fi && \
        cd "$am__cwd"; \
        if echo makeinfo missing; true   -I . \
         -o openocd.info openocd.texi; \
        then \
          rc=0; \
          CDPATH="${ZSH_VERSION+.}:" && cd .; \
        else \
          rc=$?; \
          CDPATH="${ZSH_VERSION+.}:" && cd . && \
          $restore $backupdir/* `echo "./openocd.info" | sed 's|[^/]*$||'`; \
        fi; \
        rm -rf $backupdir; exit $rc
makeinfo missing
 /bin/mkdir -p '/usr/local/share/info'
 install-info --info-dir='/usr/local/share/info' '/usr/local/share/info/openocd.info'
This is not dpkg install-info anymore, but GNU install-info
See the man page for ginstall-info for command line arguments
install-info: No such file or directory for /usr/local/share/info/openocd.info
 /bin/mkdir -p '/usr/local/share/man/man1'
As we did not previously have apt-get install openocd, we should now see version 0.10 when running it. Open On-Chip Debugger 0.10.0-dev-g372bb59 (2017-05-25-16:24)
copy the hello_world app:

export IDF_PATH=~/esp/esp-idf
cd ~/esp
cp -r $IDF_PATH/examples/get-started/hello_world .
cd ~/esp/hello_world
make menuconfig

if you have a hard time with the config window on WSL, try resizing the [Bash on Ubuntu] Windows size (typically smaller). Hit arrows up & down to screen refresh. If you see the problem you'll understand. If not - then there's no problem! :)

next, edit ~/workspace/openocd-esp32/tcl/interface/ftdi/olimex-arm-usb-ocd-h.cfg
to add the adapter_khz 1000 line (I saved mine to a file called olimex-arm-usb-ocd-h-1MHz.cfg): 
cd ~/workspace/openocd-esp32/tcl/interface/ftdi/
cp olimex-arm-usb-ocd-h.cfg  olimex-arm-usb-ocd-h-1MHz.cfg
nano olimex-arm-usb-ocd-h-1MHz.cfg

#
# Olimex ARM-USB-OCD-H
#
# http://www.olimex.com/dev/arm-usb-ocd-h.html
#
interface ftdi
ftdi_device_desc "Olimex OpenOCD JTAG ARM-USB-OCD-H"
ftdi_vid_pid 0x15ba 0x002b
ftdi_layout_init 0x0908 0x0b1b
ftdi_layout_signal nSRST -oe 0x0200
ftdi_layout_signal nTRST -data 0x0100
ftdi_layout_signal LED -data 0x0800 

adapter_khz 1000
I also slightly modified my esp32.cfg file (thanks @ba0sh1 http://ba0sh1.com/)
cd ~/workspace/openocd-esp32/tcl/target/
cp esp32.cfg ESP32-RTOS-none.cfg
just one change needed to set ESP32_RTOS none, but the whole file shown for reference.

As a reminder, don't try to uncomment a setting and leave a comment at the end of the same line. I saved mine to: ~/workspace/openocd-esp32/tcl/target/ESP32-RTOS-none.cfg

#With no variables set, openocd will configure JTAG for the two cores of the ESP32 and
#will not automatic RTOS detection. This can be be adjusted:

#set ESP32_ONLYCPU 1        # Only configure the PRO CPU
#set ESP32_ONLYCPU 2        # Only configure the APP CPU

# Disable RTOS support
set ESP32_RTOS none        

#set ESP32_RTOS freertos    # Force RTOS to be FreeRTOS

if { [info exists CHIPNAME] } {
 set _CHIPNAME $CHIPNAME
} else {
 set _CHIPNAME esp32
}

if { [info exists CPUTAPID] } {
 set _CPUTAPID $CPUTAPID
} else {
 set _CPUTAPID 0x120034e5
}

if { [info exists ESP32_RTOS] } {
 set _RTOS "$ESP32_RTOS"
} else {
 set _RTOS "auto"
}

if { [info exists ESP32_ONLYCPU] } {
 set _ONLYCPU $ESP32_ONLYCPU
} else {
 set _ONLYCPU 3
}

proc esp_core_halt { tgt } {
 #We need to disable the watchdogs here.
 #TIMG1 WDT
 $tgt mww 0x3FF5F064 0x50D83AA1
 $tgt mww 0x3FF5F048 0x0
 #TIMG2 WDT
 $tgt mww 0x3FF60064 0x50D83AA1
 $tgt mww 0x3FF60048 0x0
 #RTC WDT
 #ToDo: Figure out how to kill the RTC WDT
}

proc configure_esp32_core { TGT } {
 $TGT configure -event halted [list esp_core_halt $TGT]
}

#Change the name of the CPU taps depending on if it's enabled or not. This way, the user
#gets immediate feedback in the openocd logs.
if { $_ONLYCPU == "1" } {
 set _CPU0NAME cpu0
 set _CPU1NAME ignored
} elseif { $_ONLYCPU == "2" } {
 set _CPU0NAME ignored
 set _CPU1NAME cpu1
} else {
 set _CPU0NAME cpu0
 set _CPU1NAME cpu1
}

#Do add both taps, even if one of the CPUs is disabled.
jtag newtap $_CHIPNAME $_CPU0NAME -irlen 5 -expected-id $_CPUTAPID
jtag newtap $_CHIPNAME $_CPU1NAME -irlen 5 -expected-id $_CPUTAPID
set _TARGETNAME1 $_CHIPNAME.cpu1
set _TARGETNAME2 $_CHIPNAME.cpu0

if { $_ONLYCPU != 2 } {
 if { $_RTOS == "none" } {
  target create $_TARGETNAME2 esp108 -endian little -chain-position $_TARGETNAME2
 } else {
  target create $_TARGETNAME2 esp108 -endian little -chain-position $_TARGETNAME2 -rtos $_RTOS
 }
 configure_esp32_core $_TARGETNAME2
}
if { $_ONLYCPU != 1 } {
 if { $_RTOS == "none" } {
 target create $_TARGETNAME1 esp108 -endian little -chain-position $_TARGETNAME1
 } else {
 target create $_TARGETNAME1 esp108 -endian little -chain-position $_TARGETNAME1 -rtos $_RTOS
 }
 if { $_ONLYCPU != 3 } {
  configure_esp32_core $_TARGETNAME1
 }
}


#Force hw breakpoints. Once we have a memory map, we can also allow software bps.
gdb_breakpoint_override hard



Tips and Suggestions:


Note that when moving between Windows and Linux... if you use VSCode to create bash script files from Windows, there's a little issue with the LF vs CRLF settings. (note the little LF in the status bar at the bottom of the screen). You really only want to save files with LF on Linux, not CRLF like on Windows. If you ignore this, you'll may see an err like Not able to execute a .sh file: /bin/bash^M: bad interpreter. The CRLF's are to blame. The linux dosa2unix utility might also be helpful.

Is it working? 

See my gist or use this set of commands:
export PATH=$PATH:$HOME/esp/xtensa-esp32-elf/bin
export IDF_PATH=~/esp/esp-idf

cd  ~/workspace/openocd-esp32/tcl
sudo openocd -f interface/ftdi/olimex-arm-usb-ocd-h-1MHz.cfg -c "transport select jtag"  -f target/ESP32-RTOS-none.cfg

Continued in step 2...

Resources and Inspiration:

Interesting but not really related:



(this is still a work in progress, sorry for spelling / formatting / content)

Continued in step 2....

3 comments:

  1. Do you know why we have to add/uncomment "set ESP32_RTOS none" in esp32.cfg, even if we are using FreeRTOS? I can see that if it's not done then there's no thread with Id="1" listed in the VS Code debug console, and we get "Failed to find thread 1 for break event", but why is this?

    ReplyDelete
  2. Thank you for the great post. Microsoft recently released WSL2 which is based on VM. Have you tried if the USB debugger can be recognized in WSL/Ubuntu? I'm very interested to know.

    ReplyDelete
    Replies
    1. Glad you found the post helpful. :) I've not yet had a chance to try out WSL2

      Delete

comments are welcome, but I prefer not to allow links to promotions or other unrelated services.

Find gojimmypi at gojimmypi.github.io

I'm currently working on my new blog home at  gojimmypi.github.io After implementing a variety of features such as dark mode , syntax hi...