Tuesday, July 4, 2017

Converting a Blue Pill STM32F103 board to a Black Magic Probe

Recently, I learned about the really awesome Black Magic Probe - an interesting JTAG and SWD adapter that essentially has its own, built-in OpenOCD server... so you can use only the GDB client to connect directly to this device for debugging! I've been meaning to place an order for one, but then I ran across this article that converts an STM32F103 "Blue Pill" board to a Black Magic Probe!

Edit: I also tried to use the Maple Mini clone, like this one unsuccessfully; Although I was able to flash the bootloader, my computer did not recognized the device when plugged into the mini-USB connector. (apparently I'm not the only one that was unsuccessful with this).

The instructions there appear very straightforward, however as soon as I saw the some of the text, I realized the guy was using a slightly different distro of linux. So I'll add my notes on using Ubuntu. My Ubuntu is in a VMWare Workstation Virtual Machine running on Windows 10. If you have a similar setup - just don't forget to connect the device as needed from host in VM - Removable Devices. The WSL Ubuntu on Windows that I have still does not yet recognize all USB devices.

Another significant issue for me turned out to the fact that the STM32F103C8T6 is (debatable: see my comment here and noted by someone else here) only a 64K device (8K for bootloader leaving 56K for app)... and the Black Magic Probe now compiles to 57K. If it was easy, it would be no fun, eh?

To get started, ensure the tool chain is installed:


sudo apt-get update
sudo apt-get install git --assume-yes
sudo apt-get install gcc-arm-none-eabi --assume-yes
sudo apt-get install dfu-util --assume-yes

cd ~/
mkdir -p workspace
cd ~/workspace/
git clone https://github.com/jsnyder/stm32loader.git # download stm32loader
git clone --recursive https://github.com/blacksphere/blackmagic.git # download Black Magic source
#git clone --recursive https://github.com/rogerclarkmelbourne/Arduino_STM32.git # optional, has stm32flash
# optional stm32flash (64 bit version)
mkdir -p tools
cd tools
wget https://github.com/rogerclarkmelbourne/Arduino_STM32/raw/master/tools/linux64/stm32flash/stm32flash
chmod +x stm32flash
cd ~/workspace/blackmagic
make
cd src
make clean && make PROBE_HOST=stlink


when all looks good, see if you can talk to your board:

sudo apt-get install python-serial
sudo chmod 777  /dev/ttyUSB0
cd ~/workspace/blackmagic/src/
python ~/workspace/stm32loader/stm32loader.py -p /dev/ttyUSB0

it should return a result like this:
Bootloader version 22
Chip id: 0x410 (STM32 Medium-density)

Ready to flash! However - note that this is where my instructions are different.

cd ~/workspace/blackmagic/src/
python ~/workspace/stm32loader/stm32loader.py -p /dev/ttyUSB0 -e -w -v blackmagic_dfu.bin

The first time I tried, there was an error (also not the jumper setting needed for BOOT0=1:
Can't init. Ensure that BOOT0 is enabled and reset device
Traceback (most recent call last):
  File "/home/gojimmypi/workspace/stm32loader/stm32loader.py", line 436, in 
    bootversion = cmd.cmdGet()
  File "/home/gojimmypi/workspace/stm32loader/stm32loader.py", line 118, in cmdGet
    if self.cmdGeneric(0x00):
  File "/home/gojimmypi/workspace/stm32loader/stm32loader.py", line 115, in cmdGeneric
    return self._wait_for_ask(hex(cmd))
  File "/home/gojimmypi/workspace/stm32loader/stm32loader.py", line 88, in _wait_for_ask
    raise CmdException("NACK "+info)
__main__.CmdException: NACK 0x0
So just as the error hint said, I pressed the reset button and tried again. Success! Output looks like this:
Bootloader version 22
Chip id: 0x410 (STM32 Medium-density)
Write 256 bytes at 0x8000000
Write 256 bytes at 0x8000100
  [..snip..]
Write 256 bytes at 0x8001900
Write 256 bytes at 0x8001A00
Read 256 bytes at 0x8000000
Read 256 bytes at 0x8000100
  [..snip..]
Read 256 bytes at 0x8001900
Read 256 bytes at 0x8001A00
Verification OK
DFU loader done! Be sure to move jumpers back to default of "0" and press reset again. The instructions that I followed indicated that the USB device should be disconnected and reconnected. Not much interesting or different there upon re-insertion. Device is still a cp2104 on /dev/ttyUSB0
[927124.098974] usb 2-2.1: new full-speed USB device number 18 using uhci_hcd
[927124.418782] usb 2-2.1: New USB device found, idVendor=10c4, idProduct=ea60
[927124.418783] usb 2-2.1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[927124.418784] usb 2-2.1: Product: CP2104 USB to UART Bridge Controller
[927124.418785] usb 2-2.1: Manufacturer: Silicon Labs
[927124.418786] usb 2-2.1: SerialNumber: 012345678
[927124.424936] cp210x 2-2.1:1.0: cp210x converter detected
[927124.433135] usb 2-2.1: cp210x converter now attached to ttyUSB0 
The first time I ran dfu-util:
dfu-util -d 1d50:6018,:6017 -s 0x08002000:leave -D blackmagic.bin
I received an error:
dfu-util 0.8

Copyright 2005-2009 Weston Schmidt, Harald Welte and OpenMoko Inc.
Copyright 2010-2014 Tormod Volden and Stefan Schmidt
This program is Free Software and has ABSOLUTELY NO WARRANTY
Please report bugs to dfu-util@lists.gnumonks.org

dfu-util: Invalid DFU suffix signature
dfu-util: A valid DFU suffix will be required in a future dfu-util release!!!
dfu-util: No DFU capable USB device available
I had 2 new devices: /dev/serial and /dev/ttyUSB0 when plugged in.

The reason for the error? I missed the MOST IMPORTANT PART:

Disconnect everything and use the USB to connect.

The Black Magic probe uses the micro USB connector, NOT the Serial TTL! (oops) Sure enough, the device is now listed in Device Manager:


A word of caution here: if you happen to use different sources of power, beware of ground loops. I highly recommend only 1 source of power (either the USB TTY/UART or onboard Micro USB. This could be particularly important if they are on different physical computers, using a USB hub, etc.

When you remove the TTY/UART and connect the USB port on the Blue Pill, you should see something like this with dmesg:

[937218.226791] usb 2-2.1: USB disconnect, device number 22
[937218.227053] cp210x ttyUSB0: cp210x converter now disconnected from ttyUSB0
[937218.227064] cp210x 2-2.1:1.0: device disconnected
[937223.643635] usb 2-2.1: new full-speed USB device number 23 using uhci_hcd
[937223.959267] usb 2-2.1: New USB device found, idVendor=10c4, idProduct=ea60
[937223.959269] usb 2-2.1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[937223.959271] usb 2-2.1: Product: CP2104 USB to UART Bridge Controller
[937223.959272] usb 2-2.1: Manufacturer: Silicon Labs
[937223.959273] usb 2-2.1: SerialNumber: 01234567
[937223.967154] cp210x 2-2.1:1.0: cp210x converter detected
[937223.973711] usb 2-2.1: cp210x converter now attached to ttyUSB0
[937258.140246] usb 2-2.1: USB disconnect, device number 23
[937258.144289] cp210x ttyUSB0: cp210x converter now disconnected from ttyUSB0
[937258.144327] cp210x 2-2.1:1.0: device disconnected

[938012.539892] usb 2-2.1: new full-speed USB device number 24 using uhci_hcd
[938012.886453] usb 2-2.1: New USB device found, idVendor=1d50, idProduct=6017
[938012.886455] usb 2-2.1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[938012.886472] usb 2-2.1: Product: Black Magic (Upgrade) for STLink/Discovery, (Firmware v1.6.1-43-g984f8b3)
[938012.886474] usb 2-2.1: Manufacturer: Black Sphere Technologies
[938012.886474] usb 2-2.1: SerialNumber: 76543210
Unfortunately dfu-util -l is not happy:

dfu-util 0.8

Copyright 2005-2009 Weston Schmidt, Harald Welte and OpenMoko Inc.
Copyright 2010-2014 Tormod Volden and Stefan Schmidt
This program is Free Software and has ABSOLUTELY NO WARRANTY
Please report bugs to dfu-util@lists.gnumonks.org

dfu-util: Cannot open DFU device 1d50:6017
To confirm the device is there,lsusb shows the device as "OpenMoko":

Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 025: ID 1d50:6017 OpenMoko, Inc.
Bus 002 Device 003: ID 0e0f:0002 VMware, Inc. Virtual USB Hub
Bus 002 Device 002: ID 0e0f:0003 VMware, Inc. Virtual Mouse
Bus 002 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
You can also view just the device you are looking for with the -d parameter:
lsusb -d 1d50:
(don't forget the trailing colon ":"!

Ok, not obvious - but the problem here is one again permissions. So run the command with sudo:

sudo dfu-util -l
If successful, the output should look like this:

dfu-util 0.8

Copyright 2005-2009 Weston Schmidt, Harald Welte and OpenMoko Inc.
Copyright 2010-2014 Tormod Volden and Stefan Schmidt
This program is Free Software and has ABSOLUTELY NO WARRANTY
Please report bugs to dfu-util@lists.gnumonks.org

Found DFU: [1d50:6017] ver=0100, devnum=27, cfg=1, intf=0, alt=0, name="@Internal Flash /0x08000000/8*001Ka,056*001Kg", serial="76543210"




Once you can see the device, run dfu-util with sudo:


sudo dfu-util -d 1d50:6018,:6017 -s 0x08002000:leave -D blackmagic.bin
I received an error, that sure looks like an out of memory issue:

dfu-util 0.8

Copyright 2005-2009 Weston Schmidt, Harald Welte and OpenMoko Inc.
Copyright 2010-2014 Tormod Volden and Stefan Schmidt
This program is Free Software and has ABSOLUTELY NO WARRANTY
Please report bugs to dfu-util@lists.gnumonks.org

dfu-util: Invalid DFU suffix signature
dfu-util: A valid DFU suffix will be required in a future dfu-util release!!!
Opening DFU capable USB device...
ID 1d50:6017
Run-time device DFU version 011a
Claiming USB DFU Interface...
Setting Alternate Setting #0 ...
Determining device status: state = dfuIDLE, status = 0
dfuIDLE, continuing
DFU mode device DFU version 011a
Device returned transfer size 1024
DfuSe interface name: "Internal Flash   "
Downloading to address = 0x08002000, size = 58364
dfu-util: Last page at 0x080103fb is not writeable
So I found other instructions relating to STM Discovery as a Black Magic Probe that indicated dfu_upgrade.bin needed to be loaded (I was willing to try anything!)

sudo dfu-util -d 1d50:6018,:6017 -s 0x08002000:leave -D dfu_upgrade.bin
The results are a bit more promising:

dfu-util 0.8

Copyright 2005-2009 Weston Schmidt, Harald Welte and OpenMoko Inc.
Copyright 2010-2014 Tormod Volden and Stefan Schmidt
This program is Free Software and has ABSOLUTELY NO WARRANTY
Please report bugs to dfu-util@lists.gnumonks.org

dfu-util: Invalid DFU suffix signature
dfu-util: A valid DFU suffix will be required in a future dfu-util release!!!
Opening DFU capable USB device...
ID 1d50:6017
Run-time device DFU version 011a
Claiming USB DFU Interface...
Setting Alternate Setting #0 ...
Determining device status: state = dfuIDLE, status = 0
dfuIDLE, continuing
DFU mode device DFU version 011a
Device returned transfer size 1024
DfuSe interface name: "Internal Flash   "
Downloading to address = 0x08002000, size = 6764
Download        [=========================] 100%         6764 bytes
Download done.
File downloaded successfully
Transitioning to dfuMANIFEST state
But even with that - I was completely unable to use dfu-util to program the Blue Pill with a 57K blackmagic.bin file. Even after power cycle, reset, etc. So I put the board back into USART programming mode (BOOT0 jumper=1, BOOT1 jumper=0) and used the ST Flash Loader app on Windows. The device is initially detected as a 65K device:


However on the next screen, apparently you can force it into 128K mode:


The select the blackmagic.bin file, click the "Erase necessary pages", select an address of 0x8002000 and check the "Verify after download" checkbox:


If successful, you should see:

I should point out that several times I received a verify error at this last verify step (I was really convinced I had a 64K-only device). After trying again the next day - things started working immediately and I never again saw the verify error, even after doing a full erase and starting over. YMMV.

Keep in mind that the fact that we are using 128K on a 64K device is rather dangerous. In all likelihood, the manufacturer does an exhaustive memory test: throws away the ones with an error in the first 64K, market the ones with an error in the second 64K as only a 64K STM32f103C8T6 device, and label the ones with no error as 128K STM32f103CBT6 devices. Although this may be a fun Saturday afternoon project, if you are serious about actually using and depending on a BlackMagic probe for real debugging, consider buying one. (besides, always good to support the developer).  When you are elbows-deep in debugging your own project, you really don't want a questionable debugger.

So at this point, I'm not even sure the dfu bootloader is needed, if we are loading the blackmagic.bin file this way.

My particular debugging experiment uses the STM Smart V2 board as a target, and using my STM32 to ST7735 TFT LCD display example with the source on github. This is somewhat of a convoluted setup, as the project is in Visual Studio 2017 (on Windows 10) with the VisualGDB Extension from Sysprogs.

As previously mentioned, I'm running my Ubuntu in a VM, and I have samba installed so that on Windows I can map a drive  (C$ is root  /):

net use z: \\192.168.1.30\c$ /user:gojimmypi

Debugging connections from Blue Pill board configured as a Black Magic Probe (micro usb connected to Ubuntu):

Connecting the Blue Pill Black Magic Probe to an STM Smart V2 board:





cd  ~/workspace
git clone https://github.com/gojimmypi/STM32-ST7735.git

then open the solution Z:\home\gojimmypi\workspace\STM32-ST7735\STM32-ST7735.sln in Visual Studio and compile.


cd  ~/workspace/STM32-ST7735/STM32-ST7735
arm-none-eabi-gdb -d ./ -f ~/workspace/STM32-ST7735/VisualGDB/Debug/STM32-ST7735 -tui
ls /dev/ttyACM* -al

then in GDB, connect with the first ttyACM[n] device (the second one is a serial port). Mine were originally ttyACM0 and ttyACM1, but then later changed to ttyACM1 and ttyACM2 (no idea how/why). I even saw an instance while debugging that ttyACM1 changed to ttyACM0 yes leaving me with (ttyACM0 and ttyACM2).Weird.
Anyhow, here are some useful GDB commands to get started:

target extended-remote /dev/ttyACM1
monitor swdp_scan
attach 1


If successful you should see something like this:

GNU gdb (7.10-1ubuntu3+9) 7.10
Copyright (C) 2015 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later 
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "--host=x86_64-linux-gnu --target=arm-none-eabi".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
.
Find the GDB manual and other documentation resources online at:
.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from /home/gojimmypi/workspace/STM32-ST7735/VisualGDB/Debug/STM32-ST7735...done.
(gdb) target extended-remote /dev/ttyACM1
Remote debugging using /dev/ttyACM1
(gdb) monitor swdp_scan
Target voltage: unknown
Available Targets:
No. Att Driver
 1      STM32F1 medium density
(gdb) attach 1
Attaching to program: /home/gojimmypi/workspace/STM32-ST7735/VisualGDB/Debug/STM32-ST7735, Remote target
0x08001514 in SetSysClockTo72 () at system_stm32f10x.c:993
(gdb)

sample


Resources, Inspiration, Credits, and Other Links:





Friday, June 9, 2017

VSCode JTAG Debugging of ESP32 - Part 3

(see also hardware setup, part 1, and part 2)

Test drive of OpenOCD WIP branch with VSCode. See: https://github.com/espressif/openocd-esp32/wiki/Work-in-progress-ESP32-dual-core-target

If there's already an openocd-esp32 checked out, save it:
cd ~/workspace
mv openocd-esp32 openocd-esp32-master

Fetch the WIP branch
cd ~/workspace
git clone -b feature/esp32_dualcore  --recursive https://github.com/espressif/openocd-esp32.git
cd openocd-esp32
git status
then follow instructions https://github.com/espressif/openocd-esp32
./bootstrap 
./configure 
make
sudo make install

Modified esp32_dc.cfg to esp32_dcx.cfg

cp  /./home/gojimmypi/workspace/openocd-esp32/esp32_dc.cfg /./home/gojimmypi/workspace/openocd-esp32/esp32_dcx.cfg
Mine looks like this:

# Example configuration file to hook up an ESP32 module or board to a JTAG
# adapter. Please modify this file to your local setup.

# Include the configuration for the JTAG adapter.
# By default, DevKit-J is used.
# Later versions of DevKit-J are known as ESP-WROVER-KIT,
# they can work with the same configuration file.
# If you use a different interface, please edit this to include the
# configuration file of yours.
# source [find interface/ftdi/esp32_devkitj_v1.cfg]

# The ESP32 only supports JTAG.
transport select jtag

# The speed of the JTAG interface, in KHz. If you get DSR/DIR errors (and they
# do not relate to OpenOCD trying to read from a memory range without physical
# memory being present there), you can try lowering this.
# On DevKit-J, this can go as high as 20MHz if CPU frequency is 80MHz, or 26MHz
# if CPU frequency is 160MHz or 240MHz.
adapter_khz 1000

#Source the ESP32 configuration file
source [find target/esp32_dc.cfg]

# The TDI pin of ESP32 is also a bootstrap pin that selects the voltage the SPI flash
# chip runs at. When a hard reset happens (e.g. because someone switches the board off
# and on) the ESP32 will use the current TDI value as the bootstrap value because the
# JTAG adapter overrides the pull-up or pull-down resistor that is supposed to do the
# bootstrapping. These lines basically set the idle value of the TDO line to a
# specified value, therefore reducing the chance of a bad bootup due to a bad flash
# voltage greatly.

# Enable this for 1.8V SPI flash
#esp32 flashbootstrap 1.8
# Enable this for 3.3V SPI flash
esp32 flashbootstrap 3.3

Note there's a discussion for openocd-esp32/issues/6 that mentions the flash voltage in config file:
That depends on the module which is used on the dev board. Most WROOM modules use 3.3V flash, while WROVER modules use 1.8V flash.

For a JTAG debugger, I'm using the Olimex (default file, see esp32 config for some settings). I could not get the default 20MHz to work, so lower this setting if there are problems. Sample above is using 1MHz, but I have been able to get 10MHz to work reliably. Actual max speed will depend on JTAG cable type characteristics (length, capacitance, shielding, etc)
#
# Olimex ARM-USB-OCD-H
#
# http://www.olimex.com/dev/arm-usb-ocd.html
#

interface ft2232
ft2232_device_desc "Olimex OpenOCD JTAG ARM-USB-OCD-H"
ft2232_layout olimex-jtag
ft2232_vid_pid 0x15ba 0x002b
Modified launch.json in VSCode (different from prior blog). Note changes as specified here. But I omitted last line with single "c".
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "OpenOCD Debug",
            "type": "cppdbg",
            "request": "launch",
            "miDebuggerPath": "C:/msys32/opt/xtensa-esp32-elf/bin/xtensa-esp32-elf-gdb.exe",
            "program": "Y:/home/gojimmypi/esp/hello_world/build/hello-world.elf",
            "setupCommands": [
                {
                    "description": "Enable pretty-printing for gdb",
                    "text": "-enable-pretty-printing",
                    "ignoreFailures": true
                },
                {
                    "text": "file 'Y:/home/gojimmypi/esp/hello_world/build/hello-world.elf'"
                },
                {
                    "text": "target remote 192.168.174.130:3333"
                },
                {
                    "text": "monitor reset halt"
                },
                {
                    "text": "thb app_main"
                },
                {
                    "text": "x $a1=0"
                }
            ],
            "externalConsole": false,
            "cwd": "Y:/home/gojimmypi/esp/hello_world/build/",
            "logging": {
                "trace": true,
                "traceResponse": true,
                "engineLogging": true
            }
        }
    ]
}
then launch OpenOCD
cd ~/workspace/openocd-esp32/tcl
sudo openocd -f interface/ftdi/olimex-arm-usb-ocd-h.cfg  -f ~/workspace/openocd-esp32/esp32_dcx.cfg
Output looks like this:
Open On-Chip Debugger 0.10.0-dev-gc83a89c (2017-06-09-11:42)
Licensed under GNU GPL v2
For bug reports, read
        http://openocd.org/doc/doxygen/bugs.html
adapter speed: 1000 kHz
force hard breakpoints
Info : clock speed 1000 kHz
Info : JTAG tap: esp32.cpu0 tap/device found: 0x120034e5 (mfg: 0x272 (Tensilica), part: 0x2003, ver: 0x1)
Info : JTAG tap: esp32.slave tap/device found: 0x120034e5 (mfg: 0x272 (Tensilica), part: 0x2003, ver: 0x1)
Info : Target halted. PRO_CPU: PC=0x4008495D (active)    APP_CPU: PC=0x00000000

Note FreeRTOS change for app: Disable "Stop program on scheduler start..."
cd ~/esp/hello_world
export PATH=$PATH:$HOME/esp/xtensa-esp32-elf/bin
export IDF_PATH=~/esp/esp-idf
make menuconfig

Rebuild and flash. Note I edited my Hello World to loop and not reset.
make clean
make
make flash
Ensure it is working:

make monitor
Output like this:

MONITOR
--- idf_monitor on /dev/ttyUSB0 115200 ---
--- Quit: Ctrl+] | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---
ets Jun  8 2016 00:22:57

rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
ets Jun  8 2016 00:22:57

rst:0x10 (RTCWDT_RTC_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0x00
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0008,len:8
load:0x3fff0010,len:4400
load:0x40078000,len:11092
ho 0 tail 12 room 4
load:0x40080000,len:252
0x40080000: _iram_start at ??:?

entry 0x40080034
0x40080034: _iram_start at ??:?

I (49) boot: ESP-IDF v2.0-rc1-854-gba55461 2nd stage bootloader
I (49) boot: compile time 13:14:19
I (49) boot: Enabling RNG early entropy source...
I (67) boot: SPI Speed      : 40MHz
I (79) boot: SPI Mode       : DIO
I (92) boot: SPI Flash Size : 4MB
I (104) boot: Partition Table:
I (115) boot: ## Label            Usage          Type ST Offset   Length
I (138) boot:  0 nvs              WiFi data        01 02 00009000 00006000
I (161) boot:  1 phy_init         RF data          01 01 0000f000 00001000
I (185) boot:  2 factory          factory app      00 00 00010000 00100000
I (208) boot: End of partition table
I (221) boot: Disabling RNG early entropy source...
I (238) boot: Loading app partition at offset 00010000
I (753) boot: segment 0: paddr=0x00010018 vaddr=0x00000000 size=0x0ffe8 ( 65512)
I (753) boot: segment 1: paddr=0x00020008 vaddr=0x3f400010 size=0x070ac ( 28844) map
I (769) boot: segment 2: paddr=0x000270bc vaddr=0x3ffb0000 size=0x02b00 ( 11008) load
I (800) boot: segment 3: paddr=0x00029bc4 vaddr=0x40080000 size=0x00400 (  1024) load
0x40080000: _iram_start at ??:?

I (822) boot: segment 4: paddr=0x00029fcc vaddr=0x40080400 size=0x12cdc ( 77020) load
I (885) boot: segment 5: paddr=0x0003ccb0 vaddr=0x400c0000 size=0x00000 (     0) load
I (886) boot: segment 6: paddr=0x0003ccb8 vaddr=0x00000000 size=0x03350 ( 13136)
I (906) boot: segment 7: paddr=0x00040010 vaddr=0x400d0018 size=0x26ae8 (158440) map
0x400d0018: _flash_cache_start at ??:?

I (932) cpu_start: Pro cpu up.
I (943) cpu_start: Starting app cpu, entry point is 0x40080ec0
0x40080ec0: call_start_cpu1 at /home/gojimmypi/esp/esp-idf/components/esp32/./cpu_start.c:174

I (0) cpu_start: App cpu up.
I (976) heap_alloc_caps: Initializing. RAM available for dynamic allocation:
I (998) heap_alloc_caps: At 3FFAE2A0 len 00001D60 (7 KiB): DRAM
I (1018) heap_alloc_caps: At 3FFB6418 len 00029BE8 (166 KiB): DRAM
I (1039) heap_alloc_caps: At 3FFE0440 len 00003BC0 (14 KiB): D/IRAM
I (1061) heap_alloc_caps: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM
I (1082) heap_alloc_caps: At 400930DC len 0000CF24 (51 KiB): IRAM
I (1103) cpu_start: Pro cpu start user code
I (1161) cpu_start: Starting scheduler on PRO CPU.
Hello world!
I (201) cpu_start: Starting scheduler on APP CPU.
This is ESP32 chip with 2 CPU cores, WiFi/BT/BLE, silicon revision 0, 4MB external flash
Looping in 10 seconds...
Looping in 9 seconds...
Looping in 8 seconds...
Looping in 7 seconds...
Looping in 6 seconds...


On Windows machine, my Y: drive is mapped to the root of the Ubuntu machine (using samba):

net use y: \\192.168.174.130\c$
Then in Windows Explorer, open VSCode from
Y:\home\gojimmypi\esp\hello_world\build

Press F5 to debug! Tada!





VSCode immediately fails with this error if the last line is included in launch.json as noted for gdbinit: (so don't add it that last "c" command)



Unable to start debugging. Unexpected GDB output from command "-interpreter-exec console "c"".


1: (138) LaunchOptions
1: (143) LaunchOptions    
1: (143) LaunchOptions        -enable-pretty-printing
1: (144) LaunchOptions        file 'Y:/home/gojimmypi/esp/hello_world/build/hello-world.elf'
1: (144) LaunchOptions        target remote 192.168.174.130:3333
1: (144) LaunchOptions        monitor reset halt
1: (144) LaunchOptions        thb app_main
1: (144) LaunchOptions        x $a1=0
1: (144) LaunchOptions        c
1: (144) LaunchOptions    
1: (144) LaunchOptions
1: (176) Starting: "C:/msys32/opt/xtensa-esp32-elf/bin/xtensa-esp32-elf-gdb.exe" --interpreter=mi
1: (189) DebuggerPid=22492
1: (216) ->=thread-group-added,id="i1"
1: (217) ->~"GNU gdb (crosstool-NG crosstool-ng-1.22.0-61-gab8375a) 7.10\n"
1: (217) ->~"Copyright (C) 2015 Free Software Foundation, Inc.\n"
1: (217) ->~"License GPLv3+: GNU GPL version 3 or later \nThis is free software: you are free to change and redistribute it.\nThere is NO WARRANTY, to the extent permitted by law.  Type \"show copying\"\nand \"show warranty\" for details.\n"
1: (217) ->~"This GDB was configured as \"--host=i686-host_pc-mingw32 --target=xtensa-esp32-elf\".\nType \"show configuration\" for configuration details."
1: (217) ->~"\nFor bug reporting instructions, please see:\n"
1: (217) ->~".\n"
1: (217) ->~"Find the GDB manual and other documentation resources online at:\n.\n"
1: (217) ->~"For help, type \"help\".\n"
1: (217) ->~"Type \"apropos word\" to search for commands related to \"word\".\n"
1: (217) ->(gdb)
1: (222) <-1001-gdb-set -="" 1:="" on="" target-async="">1001^done
1: (228) ->(gdb)
1: (229) 1001: elapsed time 6
1: (234) <-1002-enable-pretty-printing -="" 1:="">1002^done
1: (239) ->(gdb)
1: (239) 1002: elapsed time 5
1: (241) <-1003-interpreter-exec -="" 1:="" build="" console="" esp="" file="" gojimmypi="" hello-world.elf="" hello_world="" home="">~"Reading symbols from Y:/home/gojimmypi/esp/hello_world/build/hello-world.elf..."
1: (274) ->~"done.\n"
1: (275) ->1003^done
1: (275) ->(gdb)
1: (275) 1003: elapsed time 34
1: (275) <-1004-interpreter-exec -="" 192.168.174.130:3333="" 1:="" console="" remote="" target="">~"Remote debugging using 192.168.174.130:3333\n"
1: (305) ->=thread-group-started,id="i1",pid="42000"
1: (305) ->=thread-created,id="1",group-id="i1"
1: (307) ->~"0x00000000 in ?? ()\n"
1: (307) ->*stopped,frame={addr="0x00000000",func="??",args=[]},thread-id="1",stopped-threads="all"
1: (309) ->1004^done
1: (309) ->(gdb)
1: (318) 1004: elapsed time 42
1: (319) <-1005-thread-info -="" 1:="" 1="">1005^done,threads=[{id="1",target-id="Remote target",frame={level="0",addr="0x00000000",func="??",args=[]},state="stopped"}]
1: (329) ->(gdb)
1: (330) 1005: elapsed time 11
1: (330) <-1006-interpreter-exec -="" 1:="" console="" e="" halt="" monitor="" reason="" reset="" started="" thread:="" thread="" threadid="" type="">@"JTAG tap: esp32.cpu0 tap/device found: 0x120034e5 (mfg: 0x272 (Tensilica), part: 0x2003, ver: 0x1)\n"
1: (366) ->@"JTAG tap: esp32.slave tap/device found: 0x120034e5 (mfg: 0x272 (Tensilica), part: 0x2003, ver: 0x1)\n"
1: (490) ->@"Target halted. PRO_CPU: PC=0x400D0E1C (active)    APP_CPU: PC=0x00000000 \n"
1: (500) ->@"esp32: target state: halted\n"
1: (542) ->@"esp32: Debug controller was reset (pwrstat=0x5F, after clear 0x0F).\n"
1: (542) ->@"esp32: Core was reset (pwrstat=0x5F, after clear 0x0F).\n"
1: (676) ->@"Target halted. PRO_CPU: PC=0x5000004B (active)    APP_CPU: PC=0x00000000 \n"
1: (691) ->@"esp32: target state: halted\n"
1: (740) ->@"esp32: Core was reset (pwrstat=0x1F, after clear 0x0F).\n"

Even when things seem to be going well, eventually some errors are encountered. If you try to debug and nothing happens, it may be because there are too many breakpoints. The limit is TWO! But perhaps you don't know / remember you even have breakpoints, but see this in the View - Debug Console

1: (1048) <-1020-interpreter-exec -="" 1:="" console="" info="" sharedlibrary="">~"No shared libraries loaded at this time.\n"
1: (1051) ->1020^done
1: (1051) ->(gdb)
1: (1051) 1020: elapsed time 3
E output: {"category":"console","output":"Loaded 'shared libraries loaded at this time.'. Cannot find or open the symbol file.\r\n","data":null,"type":"output"}
Loaded 'shared libraries loaded at this time.'. Cannot find or open the symbol file.
1: (1061) <-1021-thread-info -="" 1:="">1021^done,threads=[{id="1",target-id="Remote target",frame={level="0",addr="0x40000400",func="??",args=[]},state="stopped"}],current-thread-id="1"
1: (1072) ->(gdb)
1: (1072) 1021: elapsed time 11
1: (1078) <-1022-stack-list-frames -="" 0="" 1000="" 1:="">1022^done,stack=[frame={level="0",addr="0x40000400",func="??"}]
1: (1083) ->(gdb)
1: (1083) 1022: elapsed time 4
1: (1086) <--exec-continue -="" 1:="">^error,msg="Warning:\nCannot insert breakpoint 3.\nCannot access memory at address 0x400f4d55\nCannot insert breakpoint 4.\nCannot access memory at address 0x400f4d75\n"
1: (1104) ->(gdb)
E output: {"category":"stderr","output":"ERROR: Warning:\nCannot insert breakpoint 3.\nCannot access memory at address 0x400f4d55\nCannot insert breakpoint 4.\nCannot access memory at address 0x400f4d75\n\r\n","data":null,"type":"output"}
ERROR: Warning:
Cannot insert breakpoint 3.
Cannot access memory at address 0x400f4d55
Cannot insert breakpoint 4.
Cannot access memory at address 0x400f4d75
To resolve this, click on Debug - Remove All Breakpoints in VSCode.

Thursday, May 25, 2017

VSCode JTAG Debugging of ESP32 - Part 2

This in a continuation from part 1. If you need help connecting the ESP32 to JTAG, see this post.

After setting up the ESP32 toolchain and confirming regular GDB debugging is working as described in my previous post, now it is time to configure VSCode for remote debugging of the ESP32.

I've also added Part 3 - using ESP WIP OpenOCD.

Everything is pretty straightforward here, well except for the details of actually debugging in VSCode. I had quite an ordeal with that, as described in github issue  #763: Remote Debugging of ESP32 causes EXCEPTION: System.ArgumentOutOfRangeException.

In the end, it seems that somehow VSCode was confused on the initial *state* of the ESP32 when debugging is first initiated. The first MI commands would get the ESP32 into some sort of weird state "internal to GDB":

C next: {"threadId":-2}
1: (812785) <-1022-exec-next
1: (812793) ->1022^running
1: (812793) ->*running,thread-id="all"
1: (812793) ->(gdb)
1: (812793) 1022: elapsed time 8
1: (812794) ->(gdb)
 R: {"success":true,"message":null,"request_seq":11,"command":"next","body":null,"running":false,"refs":null,"seq":0,"type":"response"}
1: (812903) ->@"esp32.cpu0: Target halted, pc=0x40080E77\n"
E output: {"category":"stdout","output":"@\"esp32.cpu0: Target halted, pc=0x40080E77\\n\"\n","data":null,"type":"output"}
@"esp32.cpu0: Target halted, pc=0x40080E77\n"
1: (812932) ->~"/home/ivan/e/crosstool-NG/.build/src/gdb-7.10/gdb/inline-frame.c:171: internal-error: inline_frame_this_id: Assertion `!frame_id_eq (*this_id, outer_frame_id)' failed.\nA problem internal to GDB has been detected,\nfurther debugging may prove unreliable.\nQuit this debugging session? "
1: (812932) ->~"(y or n) [answered Y; input not from terminal]\n"
E output: {"category":"stdout","output":"/home/ivan/e/crosstool-NG/.build/src/gdb-7.10/gdb/inline-frame.c:171: internal-error: inline_frame_this_id: Assertion `!frame_id_eq (*this_id, outer_frame_id)' failed.\nA problem internal to GDB has been detected,\nfurther debugging may prove unreliable.\nQuit this debugging session? ","data":null,"type":"output"}
1: (812932) ->&"\nThis is a bug, please report it."
/home/ivan/e/crosstool-NG/.build/src/gdb-7.10/gdb/inline-frame.c:171: internal-error: inline_frame_this_id: Assertion `!frame_id_eq (*this_id, outer_frame_id)' failed.
A problem internal to GDB has been detected,
further debugging may prove unreliable.
Quit this debugging session? 

I opened an espressif issue on giuthub for this one: [TW#12876] VSCode Debugging via MI causes internal GDB error  #645. Fortunately - with the help of @pieandcakes - I was able to determine a (yes, somewhat wonky) sequence of steps to persuade VSCode to debug the ESP32!

I have my openocd running on Ubuntu with samba, and have mapped my windows Z: drive to the root of the ubuntu file system. For that, my smb.conf looks like this:

## edit the file /etc/samba/smb.conf and put in these lines at the end (without single # comment markers!)
##*******************************************************************************************************
[c$]
   comment= root
   path=/
   browseable=Yes
   writeable=Yes
   only guest=no
   create mask=0777
   directory mask=0777
   public=no

## also, make sure the authentication line is not commented out
####### Authentication #######
#   security = user
##*******************************************************************************************************

# try to keep away WannaCry
[global]
min protocol = SMB2

First, browse to the Hello World "Build" directory in Windows File Explorer. Right click and click "Open with Code".  The select Debug - Open Configurations. This should create a file called launch.json - in my case, in:

Z:\home\gojimmypi\esp\hello_world\build\.vscode\

Here's my launch.json for debugging the ESP32:


{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "OpenOCD Debug",
            "type": "cppdbg",
            "request": "launch",
            "miDebuggerPath": "C:/msys32/opt/xtensa-esp32-elf/bin/xtensa-esp32-elf-gdb.exe",
            "program": "Z:/home/gojimmypi/esp/hello_world/build/hello-world.elf",
            "setupCommands": [
                {
                    "description": "Enable pretty-printing for gdb",
                    "text": "-enable-pretty-printing",
                    "ignoreFailures": true
                },
                {
                    "text": "file 'Z:/home/gojimmypi/esp/hello_world/build/hello-world.elf'"
                },
                {
                    "text": "target remote 192.168.174.129:3333"
                },
                {
                    "text": "monitor reset init"
                }
            ],
            "externalConsole": false,
            "cwd": "Z:/home/gojimmypi/esp/hello_world/build/",
            "logging": {
                "trace": true,
                "traceResponse": true,
                "engineLogging": true
            }
        }
    ]
}
Note that you need to choose between
"miDebuggerServerAddress": "192.168.174.129:3333"
and
"text": "target remote 192.168.174.129:3333"
- but not both! (see that first question in github issue 763). You will, of course - need to edit the IP address to point to your own OpenOCD server, otherwise you will see this error in VSCode:

Unable to start debugging. Unexpected GDB output from command "-interpreter-exec console "target remote 192.168.174.129:3333""


If you don't have a VM or a Linux box, perhaps consider a Raspberry Pi.  :)


The line

            "logging": {
                "trace": true,
                "traceResponse": true,
                "engineLogging": true

is optional, but tremendously helpful in actually seeing what's going on between VSCode and the ESP32.

I created a few gists that can either be put into script files, or copy/pasted for some basic operations. I run each of these in a separate putty shell connection. First, the compile & flash:

#!/bin/bash
export PATH=$PATH:$HOME/esp/xtensa-esp32-elf/bin
export IDF_PATH=~/esp/esp-idf

sudo chmod 777 /dev/ttyUSB0

cd ~/esp/hello_world
make flash

In this same "compile window", it can be helpful to run the serial monitor. I like keeping it in the same window as make/building, so that I am sure to stop the monitor when reflashing.

make monitor
Next, run openocd server in yet another putty connection (when successful, the openocd will not exit, rather it continues running as a server for your VSCode GDB connection as a foreground task):

#!/bin/bash
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

A successful launch of openocd will look something like this:

0 $ sudo openocd -f interface/ftdi/olimex-arm-usb-ocd-h-1MHz.cfg -c "transport select jtag"  -f target/ESP32-RTOS-none.cfg
[sudo] password for gojimmypi:
Open On-Chip Debugger 0.10.0-dev-g372bb59 (2017-05-19-10:35)
Licensed under GNU GPL v2
For bug reports, read
        http://openocd.org/doc/doxygen/bugs.html
adapter speed: 1000 kHz
jtag
force hard breakpoints
Info : clock speed 1000 kHz
Info : JTAG tap: esp32.cpu0 tap/device found: 0x120034e5 (mfg: 0x272 (Tensilica), part: 0x2003, ver: 0x1)
Info : JTAG tap: esp32.cpu1 tap/device found: 0x120034e5 (mfg: 0x272 (Tensilica), part: 0x2003, ver: 0x1)
Info : esp32.cpu0: Target halted, pc=0x400D1158
then the xtensa-esp32-elf-gdb debug client (the find is of course optional) this one with gdb commands in hello_world,gdb:

#!/bin/bash
export PATH=$PATH:$HOME/esp/xtensa-esp32-elf/bin
export IDF_PATH=~/esp/esp-idf
#find /. -name xtensa-esp32-elf-gdb 2>/dev/null

cd ~/esp/hello_world/build
xtensa-esp32-elf-gdb -tui -x hello_world.gdb hello-world.elf


Alternatively launch the xtensa gdb client simply like this (assume paths properly set as shown above):


xtensa-esp32-elf-gdb  hello-world.elf

A successful launch of gdb will look something like this:

gojimmypi@ubuntu : ~/esp/hello_world/build
0 $ xtensa-esp32-elf-gdb  hello-world.elf
GNU gdb (crosstool-NG crosstool-ng-1.22.0-61-gab8375a) 7.10
Copyright (C) 2015 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later &lthttp://gnu.org/licenses/gpl.html&gt
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "--host=x86_64-build_pc-linux-gnu --target=xtensa-esp32-elf".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
          &lthttp://www.gnu.org/software/gdb/bugs/&gt.
Find the GDB manual and other documentation resources online at:
          &lthttp://www.gnu.org/software/gdb/documentation/&gt.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from hello-world.elf...done.
(gdb) 

target remote localhost:3333
Remote debugging using localhost:3333
0x400d1158 in esp_vApplicationIdleHook () at /home/gojimmypi/esp/esp-idf/components/esp32/./freertos_hooks.c:52
52              asm("waiti 0");
(gdb)


We just wanted to confirm OpenOCD was working and that we could connect with linux GDB, so exit/quit.

The Windows 10 executable gdb is needed to debug in VSCode on Windows, so as shown on: http://esp-idf.readthedocs.io/en/latest/get-started/windows-setup.html it can be downloaded from here

(mine took HOURS to download)

When ready to start debugging in Windows, locate your xtensa-esp32-elf-gdb.exe, Mine is located in:

C:\msys32\opt\xtensa-esp32-elf\bin

Note the line in the VSCode launch.json:

"miDebuggerPath": "C:/msys32/opt/xtensa-esp32-elf/bin/xtensa-esp32-elf-gdb.exe",


That's the one that calls the Windows GDB client to talk to the Ubuntu OpenOCD server. This gives us the best of both worlds: assimilation! Here we can have all the reliable compile of ESP32 code in linux, yet the ability to edit and debug from Windows!

Pay attention to the forward and back-slashes in the launch.json file. See vscode-cpptools issue #706.

The details of these steps are shown in my github issue (in particular the 8th comment after the question) if you want to see more details, but in summary:

Be sure the OpenOCD server is running as described above. Press F5 to start debugging. You should see "Exception has occurred.Trace/breakpoint trap." It appears we are debugging, but we are not. Target (appears to be) running. VSCode thinks it is paused.

If you try to single step here, all bets are off; xtensa GDB crashes, VSCode debugging stops. Game over.

If instead VSCode immediately stops debugging and you see something in the debug log like:

E output: {"category":"stderr","output":"ERROR: Unable to start debugging. Failed to find thread 1 for break event\r\n","data":null,"type":"output"}
ERROR: Unable to start debugging. Failed to find thread 1 for break event
...then go back to part 1 and read about how I slightly modified my esp32.cfg

Otherwise if all is still going according to plan: type -exec continue at the VSCode > prompt.



Observe debug output. Note that despite -exec continue we are now actually in halt state. One of the lines should say:

(16086) ->@"esp32.cpu0: Target halted, pc=0x400F4AB3\n"

Press the VSCode Play button (F5). Observe error:


ERROR: Cannot execute this command while the selected thread is running.


Only after the above steps are completed, now press the pause button. Observe output. There should be no errors.

Press a code-stepping button, such as Shift-F11.... tada! Single step debugging!



For an even better experience - see FHFS comment on espressif/esp-idf issue 303,
"if esp-idf is in the project folder symbols get found automatically. for windows you can also add the msys2, mingw32 and xtensa-esp32-elf bin folders to the path in tasks.json. make monitor has some trouble starting a console, you can edit the idf_monitor.py to output to stderr(task output window), or make a .cmd file with the python command in it and run that command from tasks.
You can see how I work with it esp-idf-VSCode-template"


I'm still working on polishing these instructions. Hopefully you've found this useful. Feedback is welcome and appreciated. Send me a message on twitter, github pull request  or gmail.


Various tips:


GDB tip: Ctrl-x Ctrl-a in gdb to enter TUI mode.

You can press the EN button on the ESP32; it acts like reset and resolves many problems without having to power cycle (which will likely also need permissions re-applied,etc)

Note that as of May 2017, there's an issue in VSCode regarding the direction and quantity of path slashes as note in gitbub vscode-cpptools issue #706.

When unplugging usb devices such as your debugger and the ESP32 itself, be sure to note the order when re-inserting. Sometimes there's an unexpected recognition delay on the VM; you'll want to be consistent (e.g. having ESP32 on /dev/ttyUSB0). Trust me, you'll wait all day long trying to flash ESP32 firmware onto your JTAG debugger if it somehow ends up as /dev/ttyUSB0

If you see something like this there's a good change the JTAG adapter ended up on the default ttyUSB0 port. Edit the config or change the plug order.
>
gojimmypi@ubuntu : ~/esp/hello_world
0 $ sudo chmod 777 /dev/ttyUSB0
[sudo] password for gojimmypi:
gojimmypi@ubuntu : ~/esp/hello_world
0 $ make flash
Flashing binaries to serial port /dev/ttyUSB0 (app at offset 0x10000)...
esptool.py v2.0-beta3
Connecting........_____....._____....._____....._____....._____....._____....._____....._____....._____....._____

A fatal error occurred: Failed to connect to ESP32: Timed out waiting for packet header
/home/gojimmypi/esp/esp-idf/components/esptool_py/Makefile.projbuild:52: recipe for target 'flash' failed
make: *** [flash] Error 2


Debugging the ESP32 can be rather finicky; I spent a ton of time on the thread problem https://github.com/espressif/openocd-esp32/issues/10

If you don't see the debug console in VSCode: see View - Debug Console (Ctryl-Shift-Y)

Somehow over the course of time, my old friend "ifconfig" fell out of vogue, It is now simply "ip a". I don't really like it; harder to read. So you may need to install net-tools if ifconfig does nto work for you to determine ip addy.

Don't leave OpenOCD / GDB running when recompiling.

Don't leave OpenOCD running when disconnecting JTAG or ESP32. When the process gets stuck, closing the putty window does NOT terminate the process. Regular kill does not work. You'll need -SIGKILL


gojimmypi@ubuntu : ~/esp
1 $ ps aux | grep openocd
root      25099  0.0  0.3  54796  3964 pts/8    S+   10:30   0:00 sudo ../src/openocd -f interface/ftdi/olimex-arm-usb-ocd-h.cfg -f target/esp32-single-thread.cfg
root      25100  0.5  0.4  28660  4252 pts/8    Sl+  10:30   0:16 ../src/openocd -f interface/ftdi/olimex-arm-usb-ocd-h.cfg -f target/esp32-single-thread.cfg
gojimmy+  25176  0.0  0.0  14224   936 pts/9    S+   11:23   0:00 grep --color=auto openocd
gojimmypi@ubuntu : ~/esp
0 $ sudo kill -SIGKILL 25100
[sudo] password for gojimmypi:
gojimmypi@ubuntu : ~/esp
0 $ ps aux | grep openocd
gojimmy+  25180  0.0  0.0  14224   984 pts/9    S+   11:24   0:00 grep --color=auto openocd
gojimmypi@ubuntu : ~/esp


Resources, Inspiration, and Other Links: 


Completely unrelated, but cool:






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....

Tuesday, May 9, 2017

VSCode Remote Debugging of Embedded Devices

The developers for Visual Studio Code continue to amaze me by implementing really awesome features. This latest feature is remote OpenOCD / GDB debugging!

As mentioned in a previous post, I've been really enjoying the  OpenDPS Programmable Power Supply project introduced by Johan. This has been a really cool and fun learning opportunity.

Recently, I was able to get an OpenOCD / GDB session running on Ubuntu to do single-step debugging. If you've ever used gdb on a linux machine, even with the tui ... well, it is really cool, but when coming from the world of Visual Studio, there are a lot of cool IDE features missing.

I've tried Visual Micro and really like that environment. The drawback is that (at least at the time) there was no JTAG debugging. I noticed a recent update mentions that, so I need to revisit. Another drawback is that it is *very* Arduino specific. So embedded apps that use other libraries - say FreeRTOS - are practically impossible to load up, compile, and debug.

Another major player in the Visual Studio IDE world is the SysProgs VisualGDB add-in app. It is a considerably more serious debugger supporting JTAG and more... alas there's *no* current or even planned Arduino library support. I really like the Arduino world. I have several ESP8266 and soon ESP32 projects that use Arduino-style code libraries. Adafruit in particular has a ton of really cool open source ESP8266 libraries and sample code.

Enter VSCode. WOW! It supports gdb debugging! And shockingly easily!

First, make sure you have the VSCode ms-vscode.cpptools add-in installed.

Browse to the source code directory of your embedded app. In my case, I have my "Z:" drive mapped to a VM hosting Ubuntu (I have samba installed there).

net use z: \\192.168.8.130\c$\

It seems to be very important to use the same full path on Windows as Linux, as this process seems to see the full path on the Linux side for debugging, so Windows needs to have the same respective path starting at the root  (e.g. in my case /home/gojimmypi/ ).

I keep my projects in a directory called ~/workspace. My OpenDPS project directory is thus:

Z:/home/gojimmypi/workspace/opendps/opendps/

Right click on the directory in Windows File Explorer and click "Open with Code". From VSCode, click on Debug - Add Configuration. Then select "C++ (GDB/LLDB)". A template comes up, ready to be filled in, but perhaps not the most intuitive the first time.

Here's my complete launch.json that you could purge / edit & replace the default template:
{
    "version": "0.2.0",
    "configurations": [

        {
            "name": "Debug",
            "type": "cppdbg",
            "request": "launch",
            "miDebuggerPath": "C:/workspace/opendps/opendps/arm-none-eabi-gdb.exe",
            "targetArchitecture": "arm",
            "program": "Z:/home/gojimmypi/workspace/opendps/opendps/opendps.elf",

            "setupCommands": [
            {
                "text": "file Z:/home/gojimmypi/workspace/opendps/opendps/opendps.elf"
            },
            {
                "text": "target remote 192.168.8.130:3333"
            },
            {
                "text": "monitor reset init"
            }
                
            ],
            "externalConsole": false,
            "cwd": "Z:/home/gojimmypi/workspace/opendps/opendps/"
            }
    ]
}

I had some initial difficulties as noted in issue 706 on gitthub, so be sure to take note of the forward slashes (not back-slashes!) when editing the file paths.

Note that I have OpenOCD running on the Ubuntu VM (192.168.8.130) and I am using an ST-Link V2 JTAG debugger interface, launched in a putty terminal window like this:

  cd ~/workspace/opendps/openocd/scripts
  sudo openocd -f interface/stlink-v2.cfg -f target/stm32f1x.cfg


I have more details on Ubuntu OpenOCD in my prior post here.

Additionally, I also have Atmel Studio 7 installed, and I am using their arm-none-eabi-gdb.exe file located here:

C:/Program Files (x86)/Atmel/Studio/7.0/toolchain/arm/arm-gnu-toolchain/bin/arm-none-eabi-gdb.exe

Note that one of the things I did was copy the arm-none-eabi-gdb.exe executable to the project directory (so as to have no spaces in path). There's no problem with spaces in the path, just the forward vs back slash issue.

If you don't have (and don't want) Atmel Studio installed, you could try to install the Windows GNU ARM Embedded Toolchain located here:

https://developer.arm.com/open-source/gnu-toolchain/gnu-rm/downloads

Once everything is step, simply press "F5" to start debugging in VSCode! Some gdb commands result in errors, including important ones like "n" and "s" for stepping. So be sure and use the icons at the top of the screen. You'll probably want to press pause first if there are no break points.


Even more cool - is just like Visual Studio - you can hover over many variables while paused, and SEE THE VALUES in the hover text! No real big deal in Visual Studio, but VSCode via GDB and OpenOCD to a remote embedded device is really quite cool.  :)


This is simply amazing!

See also: "Configuring launch.json for C/C++ debugging" here:

https://github.com/Microsoft/vscode-cpptools/blob/master/launch.md


Additional resources:

https://code.visualstudio.com/docs/editor/debugging

https://code.visualstudio.com/docs/languages/cpp#_debugging

https://developer.arm.com/open-source/gnu-toolchain/gnu-rm/downloads

https://code.visualstudio.com/docs/editor/tasks

https://github.com/Microsoft/vscode-cpptools/issues/682


Using OpenOCD and GDB

Reverse engineering an existing STM32 hardware device.

I first started reverse engineering the STM32 based programmable power supply as described in a prior blog post. The more I dug into Johan's code, the more impressed I became. There's a real gem stashed away in his project: the ocd-client.py utility. This is a work of genius!

Rather than play on my only DPS, I instead did some experimenting using the STM32_Smart V2 device, with a simple blink app installed.

I learned how to use OpenOCD with GDB with the UDemy class by Peter Dalmaris called Advanced Arduino Boards and Tools. Although this was an Arduino-specific class - the instructions are the same for the STM32 devices.

First, the OpenOCD server needs to be running (in this case with the Segger J-Link):


cd ~/workspace/opendps/openocd/scripts
sudo openocd -f interface/jlink.cfg -f target/stm32f1x.cfg

# or

sudo openocd -f interface/stlink-v2.cfg -f target/stm32f1x.cfg


With no other OpenOCD client connected (e.g. gdb not running!), we can inspect the GPIO Port C  like this:

cd ~/workspace/opendps
./ocd-client.py gpioc

That command should return something like this:

GPIOC settings
CRL      : 0x44444444  [0x40011000]
CRH      : 0x44344444  [0x40011004]
IDR      : 0x0000e000  [0x40011008]
ODR      : 0x00002000  [0x4001100c]
BSRR     : 0x00000000  [0x40011010]
BRR      : 0x00000000  [0x40011014]
LCKR     : 0x00000000  [0x40011018]

launch gdb. Note that a special one called "arm-none-eabi-gdb" is needed. I think gdb is really best with the "-tui" (text user interface) option turned on. Although not as cool as a full featured IDE such as Visual Studio, it is an impressive little text-based debugger.


cd ~/workspace/opendps/opendps
arm-none-eabi-gdb -d ./ -f opendps.elf -tui

then from the gdb prompt:


target remote localhost:3333
monitor reset init

If more source code is desired (beyond that in the app itself), in Ubunto go to "System settings - Software and Updates"


and be sure "source" is selected:

without the "Source code" checkbox, you'll see an error like:

E: You must put some 'source' URIs in your sources.list

WITH that setting, then you can do things like:


sudo apt-get build-dep gcc
sudo apt-get build-dep libusb-1.0-0-dev
sudo apt-get build-dep  gcc-arm-none-eabi

# this next command will typically install source code in your
# $USER\gcc-arm-none-eabi-4.9.3+svn231177
sudo apt-get source  gcc-arm-none-eabi


then in gdb:


directory  gcc-arm-none-eabi-4.9.3+svn231177/src/libgcc/config/arm


I used that include directory as "ctrl-c" would typically break in this file:

../../../../src/libgcc/config/arm/ieee754-df.S


MYLOG=~/toolchain.log
sudo apt-get update  --assume-yes | tee -a $MYLOG

sudo apt-get install ssh  --assume-yes
sudo apt-get install xrdp  --assume-yes
sudo apt-get install samba samba-common-bin  --assume-yes
sudo apt-get install fail2ban  --assume-yes

sudo apt-get install binutils  --assume-yes
sudo apt-get install gcc  --assume-yes
sudo apt-get install gdb  --assume-yes
sudo apt-get install make  --assume-yes

sudo apt-get install build-essential  --assume-yes

sudo apt-get install python  --assume-yes

sudo apt-get install gawk  --assume-yes

(TODO - I plan to put the whole toolchain script on github)

Additional resources and inspiration:

Friday, May 5, 2017

Converting OpenDPS to Visual Studio with VisualGDB

minimal description supporting thread at sysprogs forum.

see: https://sysprogs.com/w/forums/topic/visualgdb-manual-import-failure-2-separate-compiles/

come back soon. I'll post more details when I have this working :)

Compile on Ubuntu VM...


(compiles successfully)
 
(also compiles successfully)


Starting OpenOCD


Separate telnet session... flashing to hardware via OpenOCD


Windows drive map to VM








If instead following the same instructions and using VS 2015:

make: *** No targets specified and no makefile found. Stop.

 1>C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V140\Microsoft.MakeFile.Targets(46,5): error MSB3073: The command ""C:\Program Files (x86)\Sysprogs\VisualGDB\\VisualGDB.exe" /rebuild "c:\workspace\opendps-import15\opendps-import15\opendps-import15.vcxproj" "/solution:c:\workspace\opendps-import15\opendps-import15.sln" "/config:Debug" "/platform:Win32"" exited with code 1.

 
 
 
 
 

also tried "ARM" and "VisualGDB" platforms, same result:
 
 
 

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...