{"id":1208,"date":"2023-03-19T01:12:52","date_gmt":"2023-03-19T00:12:52","guid":{"rendered":"https:\/\/www.netquote.it\/nqmain\/?p=1208"},"modified":"2026-06-25T12:08:32","modified_gmt":"2026-06-25T11:08:32","slug":"debugging-esp32-with-jtag-adapter-on-linux","status":"publish","type":"post","link":"https:\/\/www.netquote.it\/nqmain\/2023\/03\/debugging-esp32-with-jtag-adapter-on-linux\/","title":{"rendered":"Debugging ESP32 with JTAG Adapter on Linux"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Although Espressif&#8217;s website provides detailed instructions, this post offers a quick overview on steps required to setup a JTAG debugger adapter for ESP32 NodeMCU development board on Linux platform.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Why JTAG<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">JTAG (Joint Test Action Group) is a useful interface for debugging and programming electronic devices such as microcontrollers and digital signal processors for several reasons:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Non-Intrusive: JTAG is a non-intrusive interface, meaning it can be used to test and debug a device without affecting its normal operation. It provides a way to access the internal circuitry of a device, which is typically not accessible through other interfaces.<\/li>\n\n\n\n<li>Boundary Scan: JTAG includes a feature called boundary scan that allows designers to test and debug connections on printed circuit boards (PCBs). The boundary scan feature allows to test and diagnose problems with individual components or connection.<\/li>\n\n\n\n<li>In-System Programming: JTAG can be used to program a device while it is still mounted on a PCB or other system, which is known as in-system programming. This can be useful for updating firmware or debug device without needing to remove it from the system.<\/li>\n\n\n\n<li>Standardized Protocol: JTAG uses a standardized protocol defined by the IEEE 1149.1 standard, which ensures compatibility between devices and tools from different manufacturers.<\/li>\n\n\n\n<li>Semi-hosting: Provides a access to Host Resources, redirect &#8220;printf&#8221; or access to files on the host system as if they were on the local device, making it easier to test and debug software that relies on file input\/output.<\/li>\n\n\n\n<li>Low-Cost: JTAG is a low-cost interface that is widely available on many microcontrollers and other electronic devices. This makes it a popular choice for developers and engineers who need to test and debug electronic systems.<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Prerequisites<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Linux host machine, in my case Linux Mint 21.1 distribution is adopted, but any modern Linux distribution should work.<\/li>\n\n\n\n<li>OpenOCD compatible JTAG debugger hadware, in my case FT2232D that was left over from a previous plug computer project. <a href=\"https:\/\/globalscaletechnologies.com\/product\/gti-jtag-probe\/\">https:\/\/globalscaletechnologies.com\/product\/gti-jtag-probe\/<\/a><br \/>Reference design for this JTAG interface and connection schematics: <a href=\"https:\/\/www.netquote.it\/nqmain\/wp-content\/uploads\/SheevaPlug.pdf\">https:\/\/www.netquote.it\/nqmain\/wp-content\/uploads\/SheevaPlug.pdf<\/a><\/li>\n\n\n\n<li>Esp32 NodeMCU development board: <a href=\"https:\/\/wiki.geekworm.com\/NodeMCU-32S\">https:\/\/wiki.geekworm.com\/NodeMCU-32S<\/a><br \/><a href=\"https:\/\/www.espressif.com\/sites\/default\/files\/documentation\/esp32-wroom-32_datasheet_en.pdf\">https:\/\/www.espressif.com\/sites\/default\/files\/documentation\/esp32-wroom-32_datasheet_en.pdf<\/a><\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Setup OpenOCD<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Because the version of OpenOCD available in this Linux release is outdated and lacks Xtensa support, install the latest release from the official repository: <a href=\"https:\/\/openocd.org\/pages\/getting-openocd.html\">https:\/\/openocd.org\/pages\/getting-openocd.html<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For detailed instruction on howto build and install OpenOCD: <a href=\"https:\/\/openocd.org\/doc-release\/README\">https:\/\/openocd.org\/doc-release\/README<\/a><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>~\/esp32$ git clone https:\/\/git.code.sf.net\/p\/openocd\/code openocd-code\nCloning into 'openocd-code'...\nremote: Enumerating objects: 77625, done.\n...\nResolving deltas: 100% (64682\/64682), done. <\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Run &#8220;.\/bootstrap&#8221; when building from the git repository<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>~\/esp32$ cd openocd-code\/\n~\/esp32\/openocd-code$ .\/bootstrap\n+ aclocal --warnings=all\n+ libtoolize --automake --copy\n+ autoconf --warnings=all\n...\nlibjaylink\/Makefile.am: installing 'build-aux\/depcomp'\nBootstrap complete. Quick build instructions:\n.\/configure ....<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Now we can set some build options like hardware interface support, verbosity, installation directories and disable\/enable other features.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>~\/esp32\/openocd-code$ .\/configure --enable-ftdi\nchecking for makeinfo... no\nconfigure: WARNING: Info documentation will not be built.\nchecking for a BSD-compatible install... \/usr\/bin\/install -c\nchecking whether build environment is sane... yes\n...\nOpenOCD configuration summary\n--------------------------------------------------\nMPSSE mode of FTDI based devices        yes\nST-Link Programmer                      yes (auto)\nTI ICDI JTAG Programmer                 yes (auto)\nKeil ULINK JTAG Programmer              yes (auto)\nAltera USB-Blaster II Compatible        yes (auto)\nBitbang mode of FT232R based devices    yes (auto)\nVersaloon-Link JTAG Programmer          yes (auto)\nTI XDS110 Debug Probe                   yes (auto)\nCMSIS-DAP v2 Compliant Debugger         yes (auto)\nOSBDM (JTAG only) Programmer            yes (auto)\neStick\/opendous JTAG Programmer         yes (auto)\nOlimex ARM-JTAG-EW Programmer           yes (auto)\nRaisonance RLink JTAG Programmer        yes (auto)\nUSBProg JTAG Programmer                 yes (auto)\nEspressif JTAG Programmer               yes (auto)\nCMSIS-DAP Compliant Debugger            no\nNu-Link Programmer                      no\nCypress KitProg Programmer              no\nAltera USB-Blaster Compatible           yes (auto)\nASIX Presto Adapter                     yes (auto)\nOpenJTAG Adapter                        yes (auto)\nLinux GPIO bitbang through libgpiod     no\nSEGGER J-Link Programmer                no\nBus Pirate                              yes (auto)\nUse Capstone disassembly framework      yes (auto)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then make and install as usual, be careful about conflicts with existing OpenOCD installation, remove or add prefix\/suffix to the new OpenOCD executable.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>~\/esp32\/openocd-code$ make -j16\n...\n~\/esp32\/openocd-code$ sudo make install\n...<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Add udev rules:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>~\/esp32\/openocd-code$ nano \/etc\/udev\/rules.d\/10-ft2322D_jtag.rules\nUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"9e88\", ATTRS{idProduct}==\"9e8f\", MODE=\"0666\"<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Add user to dialout group: <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>~\/esp32\/openocd-code$ sudo usermod -aG dialout $USER<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">JTAG connections:<\/h3>\n\n\n\n<div class=\"wp-block-group\"><div class=\"wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained\">\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-8f761849 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:100%\">\n<figure class=\"wp-block-table alignleft is-style-stripes\"><table><thead><tr><th>ESP32 pin<\/th><th>JTAG pin<\/th><th>#J6<\/th><\/tr><\/thead><tbody><tr><td>3V3<\/td><td>VTRef (3.3V)<\/td><td>1<\/td><\/tr><tr><td>EN<\/td><td>JT_SRSTn<\/td><td>3<\/td><\/tr><tr><td>GPIO12<\/td><td>JT_TDI<\/td><td>5<\/td><\/tr><tr><td>GPIO14<\/td><td>JT_TMS_CPU<\/td><td>7<\/td><\/tr><tr><td>GPIO13<\/td><td>JT_CLK<\/td><td>9<\/td><\/tr><tr><td>GPIO15<\/td><td>JT_TDO<\/td><td>13<\/td><\/tr><tr><td>GND<\/td><td>GND<\/td><td>20<\/td><\/tr><\/tbody><\/table><figcaption class=\"wp-element-caption\">Esp32 to JTAG connections<\/figcaption><\/figure>\n\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-8f761849 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:100%\"><div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.netquote.it\/nqmain\/wp-content\/uploads\/sheevaplug_jtag.jpg\" alt=\"\" class=\"wp-image-1213\" width=\"382\" height=\"330\" srcset=\"https:\/\/www.netquote.it\/nqmain\/wp-content\/uploads\/sheevaplug_jtag.jpg 542w, https:\/\/www.netquote.it\/nqmain\/wp-content\/uploads\/sheevaplug_jtag-300x258.jpg 300w, https:\/\/www.netquote.it\/nqmain\/wp-content\/uploads\/sheevaplug_jtag-150x129.jpg 150w\" sizes=\"auto, (max-width: 382px) 100vw, 382px\" \/><figcaption class=\"wp-element-caption\">Sheevaplug JTAG pinout<\/figcaption><\/figure>\n<\/div><\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Detailed description for JTAG configuration can be found on this page: <a href=\"https:\/\/docs.espressif.com\/projects\/esp-idf\/en\/latest\/esp32\/api-guides\/jtag-debugging\/configure-other-jtag.html\">https:\/\/docs.espressif.com\/projects\/esp-idf\/en\/latest\/esp32\/api-guides\/jtag-debugging\/configure-other-jtag.html<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For ESP32 pinout refer to this page: <a href=\"https:\/\/www.studiopieters.nl\/esp32-pinout\/\">https:\/\/www.studiopieters.nl\/esp32-pinout\/<\/a><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">OpenOCD Configuration and Testing<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Now if we run openocd binary we will se somethink like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>~\/esp32\/bare_v1$ openocd \nOpen On-Chip Debugger v0.11.0-esp32-20221026 (2022-10-26-14:47)\nLicensed under GNU GPL v2\nFor bug reports, read\n        http:&#47;&#47;openocd.org\/doc\/doxygen\/bugs.html\nembedded:startup.tcl:28: Error: Can't find openocd.cfg\nin procedure 'script' \nat file \"embedded:startup.tcl\", line 28\nInfo : Listening on port 6666 for tcl connections\nInfo : Listening on port 4444 for telnet connections\nError: Debug Adapter has to be specified, see \"adapter driver\" command\nembedded:startup.tcl:28: Error: \nin procedure 'script' \nat file \"embedded:startup.tcl\", line 28<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This error, because of unknown adapter. We need to instruct OpenOCD about our hardware interface and target. This can be done by creating a configuration file named &#8216;esp32_jtag.cfg&#8217; with next settings:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># FT2232D JTAG Adapter Configuration \nadapter driver ftdi\nftdi device_desc \"SheevaPlug JTAGKey FT2232D B\"\nftdi vid_pid 0x9e88 0x9e8f\nftdi channel 0\nftdi layout_init 0x0608 0x0f1b\nftdi layout_signal nTRST -data 0x0200 -noe 0x0100\nftdi layout_signal nSRST -data 0x0800 -noe 0x0400\n\n# The speed of the JTAG interface, in kHz. If you get DSR\/DIR errors (and they\n# do not relate to OpenOCD trying to read from a memory range without physical\n# memory being present there), you can try lowering this.\n#\n# On DevKit-J, this can go as high as 20MHz if CPU frequency is 80MHz, or 26MHz\n# if CPU frequency is 160MHz or 240MHz.\nadapter speed 1000\n\n# flash 3.3 V for ESP32-WROOM-32 \nset ESP32_FLASH_VOLTAGE 3.3\n\n# Load esp32 target configuration\nsource &#91;find target\/esp32.cfg]<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">You can find a list of available interfaces and targets in the &#8216;openocd-code\/tcl&#8217; folder. <\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Running JTAG debugger<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">After launching the debugger, we should see something like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>~\/esp32$ openocd -f esp32_jtag.cfg\nOpen On-Chip Debugger 0.12.0+dev-00078-gfc30feb51 (2023-03-11-15:26)\nLicensed under GNU GPL v2\nFor bug reports, read\n\thttp:&#47;&#47;openocd.org\/doc\/doxygen\/bugs.html\nforce hard breakpoints\nInfo : Listening on port 6666 for tcl connections\nInfo : Listening on port 4444 for telnet connections\nInfo : clock speed 1000 kHz\nInfo : JTAG tap: esp32.cpu0 tap\/device found: 0x120034e5 (mfg: 0x272 (Tensilica), part: 0x2003, ver: 0x1)\nInfo : JTAG tap: esp32.cpu1 tap\/device found: 0x120034e5 (mfg: 0x272 (Tensilica), part: 0x2003, ver: 0x1)\nInfo : starting gdb server for esp32.cpu0 on 3333\nInfo : Listening on port 3333 for gdb connections\nInfo : Set GDB target to 'esp32.cpu0'<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Open your favorite telnet console on localhost at port 4444<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>~$ telnet localhost 4444\nTrying 127.0.0.1...\nConnected to localhost.\nEscape character is '^]'.\nOpen On-Chip Debugger\n&gt; help\n...<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Brief command list: <\/p>\n\n\n\n<figure class=\"wp-block-table is-style-stripes\" style=\"font-size:14px\"><table><tbody><tr><td><strong>Command<\/strong><\/td><td><strong>Description<\/strong><\/td><\/tr><tr><td>help [command_name]<\/td><td>Show full command help; command can be multiple tokens. (command valid any time)<\/td><\/tr><tr><td>version<\/td><td>show program version (command valid any time)<\/td><\/tr><tr><td>reset [run|halt|init]<\/td><td>Reset all targets into the specified mode. Default reset mode is run, if not given.<\/td><\/tr><tr><td>halt [milliseconds]<\/td><td>request target to halt, then wait up to the specified number of milliseconds (default 5000) for it to complete<\/td><\/tr><tr><td>targets [target]<\/td><td>change current default target (one parameter) or prints table of all targets (no parameters) (command valid any time)<\/td><\/tr><tr><td>flash banks<\/td><td>Display table with information about flash banks. (command valid any time)<\/td><\/tr><tr><td>reg [(number|name)]<br \/>reg [(value|&#8217;force&#8217;)]<\/td><td>display (reread from target with &#8220;force&#8221;) or set a register; with no arguments, displays all registers and their values<\/td><\/tr><tr><td>resume [address]<\/td><td>resume target execution from current PC or address<\/td><\/tr><tr><td>scan_chain<\/td><td>print current scan chain configuration (command valid any time) script<\/td><\/tr><tr><td>shutdown<\/td><td>Close the OpenOCD server, disconnecting all clients (GDB, telnet, other). If option<br \/>error is used, OpenOCD will return a non-zero exit code to the parent process.<br \/>If user types CTRL-C or kills OpenOCD, the command shutdown will be automati-<br \/>cally executed to cause OpenOCD to exit.<\/td><\/tr><tr><td>arm semihosting [&#8216;enable&#8217;|&#8217;disable&#8217;]<\/td><td>Set the base directory for semihosting I\/O.DEPRECATED! use arm activate support for semihosting operations<\/td><\/tr><tr><td>arm semihosting_basedir [dir]<\/td><td>set the base directory for semihosting I\/O operations<\/td><\/tr><tr><td>arm semihosting_fileio [&#8216;enable&#8217;|&#8217;disable&#8217;]<\/td><td>activate support for semihosting fileio operations<\/td><\/tr><tr><td>arm semihosting_redirect (disable | tcp [&#8216;debug&#8217;|&#8217;stdio&#8217;|&#8217;all&#8217;])<\/td><td>redirect semihosting IO<\/td><\/tr><tr><td>init<\/td><td>Initializes configured targets and servers. Changes command mode from CONFIG to EXEC. Unless &#8216;noinit&#8217; is called, this command is called automatically at the end of startup. (command valid any time)<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Although Espressif&#8217;s website provides detailed instructions, this post offers a quick overview on steps required to setup a JTAG debugger<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[8,4,1],"tags":[16,17,15],"class_list":["post-1208","post","type-post","status-publish","format-standard","hentry","category-hardware","category-linux","category-senza-categoria","tag-jtag","tag-linux","tag-openocd","comments-off"],"_links":{"self":[{"href":"https:\/\/www.netquote.it\/nqmain\/wp-json\/wp\/v2\/posts\/1208","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.netquote.it\/nqmain\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.netquote.it\/nqmain\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.netquote.it\/nqmain\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.netquote.it\/nqmain\/wp-json\/wp\/v2\/comments?post=1208"}],"version-history":[{"count":12,"href":"https:\/\/www.netquote.it\/nqmain\/wp-json\/wp\/v2\/posts\/1208\/revisions"}],"predecessor-version":[{"id":1280,"href":"https:\/\/www.netquote.it\/nqmain\/wp-json\/wp\/v2\/posts\/1208\/revisions\/1280"}],"wp:attachment":[{"href":"https:\/\/www.netquote.it\/nqmain\/wp-json\/wp\/v2\/media?parent=1208"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.netquote.it\/nqmain\/wp-json\/wp\/v2\/categories?post=1208"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.netquote.it\/nqmain\/wp-json\/wp\/v2\/tags?post=1208"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}