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.
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:
- esp-idf - Standard Setup of Toolchain for Windows
- esp-idf - Getting Started
- The GDB/MI Interface
- https://github.com/FHFS/esp-idf-VSCode-template
- https://www.allaboutcircuits.com/technical-articles/getting-started-with-openocd-using-ft2232h-adapter-for-swd-debugging/
- OpenOCD FreeRTOS source
- https://stackoverflow.com/questions/2420813/using-gdb-to-single-step-assembly-code-outside-specified-executable-causes-error
- sparkfun forum - OpenOCD with Olimex ARM-USB-OCD-H
- https://github.com/espressif/openocd-esp32
- http://openocd.org/doc/html/Server-Configuration.html
- olimex forum - Olimex ARM-USB-OCD-H with OpenOCD - libusb_open() failed with LIBUSB_ERROR_NOT_S
- [TW#12206] VSCode works really well. Worth adding setup instructions in to the docs? #303
- allaboutcircuits - Getting Started with OPENOCD Using FT2232H Adapter for SWD Debugging
- Benjamin's robotics - Debugging the STM32F4 using openocd, gdb and Eclipse
- custom board script example (JLINK SWD)
Interesting but not really related:
(this is still a work in progress, sorry for spelling / formatting / content)
Continued in step 2....
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?
ReplyDeleteThank 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.
ReplyDeleteGlad you found the post helpful. :) I've not yet had a chance to try out WSL2
Delete