jtag: add esp_usb_jtag driver
authorErhan Kurubas <erhan.kurubas@espressif.com>
Thu, 21 Apr 2022 19:48:28 +0000 (21:48 +0200)
committerAntonio Borneo <borneo.antonio@gmail.com>
Sat, 2 Jul 2022 08:24:01 +0000 (08:24 +0000)
This driver is used with the ESP32 chips which has builtin USB-JTAG
interface. e.g. with ESP32-C3, ESP32-S3

Signed-off-by: Erhan Kurubas <erhan.kurubas@espressif.com>
Change-Id: If966268cb8d26f76540dd5440245a17ed0b72c61
Reviewed-on: https://review.openocd.org/c/openocd/+/6943
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
Tested-by: jenkins
configure.ac
contrib/60-openocd.rules
doc/openocd.texi
src/jtag/drivers/Makefile.am
src/jtag/drivers/esp_usb_jtag.c [new file with mode: 0644]
src/jtag/interfaces.c
tcl/board/esp32s2-bridge.cfg [new file with mode: 0644]
tcl/interface/esp_usb_bridge.cfg [new file with mode: 0644]

index 2d5cb5e29c208910ecb456215f4511b38e0c5d09..66b07b179c308bce92cd0bb743a34dd6175fa525 100644 (file)
@@ -128,7 +128,8 @@ m4_define([USB1_ADAPTERS],
        [[opendous], [eStick/opendous JTAG Programmer], [OPENDOUS]],
        [[armjtagew], [Olimex ARM-JTAG-EW Programmer], [ARMJTAGEW]],
        [[rlink], [Raisonance RLink JTAG Programmer], [RLINK]],
-       [[usbprog], [USBProg JTAG Programmer], [USBPROG]]])
+       [[usbprog], [USBProg JTAG Programmer], [USBPROG]],
+       [[esp_usb_jtag], [Espressif JTAG Programmer], [ESP_USB_JTAG]]])
 
 m4_define([DEPRECATED_USB1_ADAPTERS],
        [[[aice], [Andes JTAG Programmer (deprecated)], [AICE]]])
@@ -720,6 +721,11 @@ AS_IF([test "x$enable_presto" != "xno"], [
   build_bitq=yes
 ])
 
+# esp-usb-jtag also needs the bitq module
+AS_IF([test "x$enable_esp_usb_jtag" != "xno"], [
+  build_bitq=yes
+])
+
 AM_CONDITIONAL([RELEASE], [test "x$build_release" = "xyes"])
 AM_CONDITIONAL([PARPORT], [test "x$build_parport" = "xyes"])
 AM_CONDITIONAL([DUMMY], [test "x$build_dummy" = "xyes"])
index 34defada09672b3534e51e914bf90271fa21f715..43821c26811765c8fddd74c30dea02960b1f782d 100644 (file)
@@ -216,6 +216,10 @@ ATTRS{idVendor}=="2aec", ATTRS{idProduct}=="6010", MODE="660", GROUP="plugdev",
 ATTRS{idVendor}=="2aec", ATTRS{idProduct}=="6011", MODE="660", GROUP="plugdev", TAG+="uaccess"
 ATTRS{idVendor}=="2aec", ATTRS{idProduct}=="1106", MODE="660", GROUP="plugdev", TAG+="uaccess"
 
+# Espressif USB JTAG/serial debug units
+ATTRS{idVendor}=="303a", ATTRS{idProduct}=="1001", MODE="660", GROUP="plugdev", TAG+="uaccess"
+ATTRS{idVendor}=="303a", ATTRS{idProduct}=="1002", MODE="660", GROUP="plugdev", TAG+="uaccess"
+
 # Marvell Sheevaplug
 ATTRS{idVendor}=="9e88", ATTRS{idProduct}=="9e8f", MODE="660", GROUP="plugdev", TAG+="uaccess"
 
index f678621ee00decfbfc313c2ec3e670cc37c410fa..b213798c3172800e94f3087711b784c7e3c536e2 100644 (file)
@@ -613,6 +613,9 @@ emulation model of target hardware.
 @* A bitbang JTAG driver using Linux legacy sysfs GPIO.
 This is deprecated from Linux v5.3; prefer using @b{linuxgpiod}.
 
+@item @b{esp_usb_jtag}
+@* A JTAG driver to communicate with builtin debug modules of Espressif ESP32-C3 and ESP32-S3 chips using OpenOCD.
+
 @end itemize
 
 @node About Jim-Tcl
@@ -3643,6 +3646,44 @@ buspirate led 1
 
 @end deffn
 
+@deffn {Interface Driver} {esp_usb_jtag}
+Espressif JTAG driver to communicate with ESP32-C3, ESP32-S3 chips and ESP USB Bridge board using OpenOCD.
+These chips have built-in JTAG circuitry and can be debugged without any additional hardware.
+Only an USB cable connected to the D+/D- pins is necessary.
+
+@deffn {Config Command} {espusbjtag tdo}
+Returns the current state of the TDO line
+@end deffn
+
+@deffn {Config Command} {espusbjtag setio} setio
+Manually set the status of the output lines with the order of (tdi tms tck trst srst)
+@example
+espusbjtag setio 0 1 0 1 0
+@end example
+@end deffn
+
+@deffn {Config Command} {espusbjtag vid_pid} vid_pid
+Set vendor ID and product ID for the ESP usb jtag driver
+@example
+espusbjtag vid_pid 0x303a 0x1001
+@end example
+@end deffn
+
+@deffn {Config Command} {espusbjtag caps_descriptor} caps_descriptor
+Set the jtag descriptor to read capabilities of ESP usb jtag driver
+@example
+espusbjtag caps_descriptor 0x2000
+@end example
+@end deffn
+
+@deffn {Config Command} {espusbjtag chip_id} chip_id
+Set chip id to transfer to the ESP USB bridge board
+@example
+espusbjtag chip_id 1
+@end example
+@end deffn
+
+@end deffn
 
 @section Transport Configuration
 @cindex Transport
index d05b7b976482972545aaa6ea5fad8796cb202be8..1440eb5a68511f735eb47ef16b87660fd8ea0498 100644 (file)
@@ -106,6 +106,9 @@ endif
 if PRESTO
 DRIVERFILES += %D%/presto.c
 endif
+if ESP_USB_JTAG
+DRIVERFILES += %D%/esp_usb_jtag.c
+endif
 if USBPROG
 DRIVERFILES += %D%/usbprog.c
 endif
diff --git a/src/jtag/drivers/esp_usb_jtag.c b/src/jtag/drivers/esp_usb_jtag.c
new file mode 100644 (file)
index 0000000..a73984a
--- /dev/null
@@ -0,0 +1,808 @@
+/***************************************************************************
+ *   Espressif USB to Jtag adapter                                         *
+ *   Copyright (C) 2020 Espressif Systems (Shanghai) Co. Ltd.              *
+ *   Author: Jeroen Domburg <jeroen@espressif.com>                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <jtag/adapter.h>
+#include <jtag/interface.h>
+#include <helper/time_support.h>
+#include <helper/bits.h>
+#include "bitq.h"
+#include "libusb_helper.h"
+
+#define __packed __attribute__((packed))
+
+/*
+Holy Crap, it's protocol documentation, and it's even vendor-provided!
+
+A device that speaks this protocol has two endpoints intended for JTAG debugging: one
+OUT for the host to send encoded commands to, one IN from which the host can read any read
+TDO bits. The device will also respond to vendor-defined interface requests on ep0.
+
+The main communication method is over the IN/OUT endpoints. The commands that are expected
+on the OUT endpoint are one nibble wide and are processed high-nibble-first, low-nibble-second,
+and in the order the bytes come in. Commands are defined as follows:
+
+    bit     3   2    1    0
+CMD_CLK   [ 0   cap  tdi  tms  ]
+CMD_RST   [ 1   0    0    srst ]
+CMD_FLUSH [ 1   0    1    0    ]
+CMD_RSV   [ 1   0    1    1    ]
+CMD_REP   [ 1   1    R1   R0   ]
+
+CMD_CLK sets the TDI and TMS lines to the value of `tdi` and `tms` and lowers, then raises, TCK. If
+`cap` is 1, the value of TDO is captured and can be retrieved over the IN endpoint. The bytes read from
+the IN endpoint specifically are these bits, with the lowest it in every byte captured first and the
+bytes returned in the order the data in them was captured. The durations of TCK being high / low can
+be set using the VEND_JTAG_SETDIV vendor-specific interface request.
+
+CMD_RST controls the SRST line; as soon as the command is processed, the SRST line will be set
+to the value of `srst`.
+
+CMD_FLUSH flushes the IN endpoint; zeroes will be added to the amount of bits in the endpoint until
+the payload is a multiple of bytes, and the data is offered to the host. If the IN endpoint has
+no data, this effectively becomes a no-op; the endpoint won't send any 0-byte payloads.
+
+CMD_RSV is reserved for future use.
+
+CMD_REP repeats the last command that is not CMD_REP. The amount of times a CMD_REP command will
+re-execute this command is (r1*2+r0)<<(2*n), where n is the amount of previous repeat commands executed
+since the command to be repeated.
+
+An example for CMD_REP: Say the host queues:
+1. CMD_CLK - This will execute one CMD_CLK.
+2. CMD_REP with r1=0 and r0=1 - This will execute 1. another (0*2+1)<<(2*0)=1 time.
+3. CMD_REP with r1=1 and r0=0 - This will execute 1. another (1*2+0)<<(2*1)=4 times.
+4. CMD_REP with r1=0 and r0=1 - This will execute 1. another (0*2+1)<<(2*2)=8 time.
+5. CMD_FLUSH - This will flush the IN pipeline.
+6. CMD_CLK - This will execute one CMD_CLK
+7. CMD_REP with r1=1 and r0=0 - This will execute 6. another (1*2+0)<<(2*0)=2 times.
+8. CMD_FLUSH - This will flush the IN pipeline.
+
+Note that the net effect of the repetitions is that command 1 is executed (1+1+4+8=) 14 times and
+command 6 is executed (1+2=) 3 times.
+
+Note that the device only has a fairly limited amount of endpoint RAM. It's probably best to keep
+an eye on the amount of bytes that are supposed to be in the IN endpoint and grab those before stuffing
+more commands into the OUT endpoint: the OUT endpoint will not accept any more commands (writes will
+time out) when the IN endpoint buffers are all filled up.
+
+The device also supports some vendor-specific interface requests. These requests are sent as control
+transfers on endpoint 0 to the JTAG endpoint. Note that these commands bypass the data in the OUT
+endpoint; if timing is important, it's important that this endpoint is empty. This can be done by
+e.g sending one CMD_CLK capturing TDI, then one CMD_FLUSH, then waiting until the bit appears on the
+IN endpoint.
+
+bmRequestType bRequest         wValue   wIndex    wLength Data
+01000000b     VEND_JTAG_SETDIV [divide] interface 0       None
+01000000b     VEND_JTAG_SETIO  [iobits] interface 0       None
+11000000b     VEND_JTAG_GETTDO  0       interface 1       [iostate]
+10000000b     GET_DESCRIPTOR(6) 0x2000  0         256     [jtag cap desc]
+
+VEND_JTAG_SETDIV indirectly controls the speed of the TCK clock. The value written here is the length
+of a TCK cycle, in ticks of the adapters base clock. Both the base clock value as well as the
+minimum and maximum divider can be read from the jtag capabilities descriptor, as explained
+below. Note that this should not be set to a value outside of the range described there,
+otherwise results are undefined.
+
+VEND_JTAG_SETIO can be controlled to directly set the IO pins. The format of [iobits] normally is
+{11'b0, srst, trst, tck, tms, tdi}
+Note that the first 11 0 bits are reserved for future use, current hardware ignores them.
+
+VEND_JTAG_GETTDO returns one byte, of which bit 0 indicates the current state of the TDO input.
+Note that other bits are reserved for future use and should be ignored.
+
+To describe the capabilities of the JTAG adapter, a specific descriptor (0x20) can be retrieved.
+The format of the descriptor documented below. The descriptor works in the same fashion as USB
+descriptors: a header indicating the version and total length followed by descriptors with a
+specific type and size. Forward compatibility is guaranteed as software can skip over an unknown
+descriptor.
+
+*/
+
+#define JTAG_PROTO_CAPS_VER 1  /* Version field. At the moment, only version 1 is defined. */
+struct jtag_proto_caps_hdr {
+       uint8_t proto_ver;      /* Protocol version. Expects JTAG_PROTO_CAPS_VER for now. */
+       uint8_t length; /* of this plus any following descriptors */
+} __packed;
+
+/* start of the descriptor headers */
+#define JTAG_BUILTIN_DESCR_START_OFF            0      /* Devices with builtin usb jtag */
+/*
+* ESP USB Bridge https://github.com/espressif/esp-usb-bridge uses string descriptor.
+* Skip 1 byte length and 1 byte descriptor type
+*/
+#define JTAG_EUB_DESCR_START_OFF                2      /* ESP USB Bridge */
+
+/*
+Note: At the moment, there is only a speed_caps version indicating the base speed of the JTAG
+hardware is derived from the APB bus speed of the SoC. If later on, there are standalone
+converters using the protocol, we should define e.g. JTAG_PROTO_CAPS_SPEED_FIXED_TYPE to distinguish
+between the two.
+
+Note: If the JTAG device has larger buffers than endpoint-size-plus-a-bit, we should have some kind
+of caps header to assume this. If no such caps exist, assume a minimum (in) buffer of endpoint size + 4.
+*/
+
+struct jtag_gen_hdr {
+       uint8_t type;
+       uint8_t length;
+} __packed;
+
+struct jtag_proto_caps_speed_apb {
+       uint8_t type;                                   /* Type, always JTAG_PROTO_CAPS_SPEED_APB_TYPE */
+       uint8_t length;                                 /* Length of this */
+       uint8_t apb_speed_10khz[2];             /* ABP bus speed, in 10KHz increments. Base speed is half this. */
+       uint8_t div_min[2];                             /* minimum divisor (to base speed), inclusive */
+       uint8_t div_max[2];                             /* maximum divisor (to base speed), inclusive */
+} __packed;
+
+#define JTAG_PROTO_CAPS_DATA_LEN                255
+#define JTAG_PROTO_CAPS_SPEED_APB_TYPE          1
+
+#define VEND_DESCR_BUILTIN_JTAG_CAPS            0x2000
+
+#define VEND_JTAG_SETDIV        0
+#define VEND_JTAG_SETIO         1
+#define VEND_JTAG_GETTDO        2
+#define VEND_JTAG_SET_CHIPID    3
+
+#define VEND_JTAG_SETIO_TDI     BIT(0)
+#define VEND_JTAG_SETIO_TMS     BIT(1)
+#define VEND_JTAG_SETIO_TCK     BIT(2)
+#define VEND_JTAG_SETIO_TRST    BIT(3)
+#define VEND_JTAG_SETIO_SRST    BIT(4)
+
+#define CMD_CLK(cap, tdi, tms) ((cap ? BIT(2) : 0) | (tms ? BIT(1) : 0) | (tdi ? BIT(0) : 0))
+#define CMD_RST(srst)   (0x8 | (srst ? BIT(0) : 0))
+#define CMD_FLUSH       0xA
+#define CMD_RSVD        0xB
+#define CMD_REP(r)      (0xC + ((r) & 3))
+
+/* The internal repeats register is 10 bits, which means we can have 5 repeat commands in a
+ *row at max. This translates to ('b1111111111+1=)1024 reps max. */
+#define CMD_REP_MAX_REPS 1024
+
+/* Currently we only support one USB device. */
+#define USB_CONFIGURATION 0
+
+/* Buffer size; is equal to the endpoint size. In bytes
+ * TODO for future adapters: read from device configuration? */
+#define OUT_EP_SZ 64
+/* Out data can be buffered for longer without issues (as long as the in buffer does not overflow),
+ * so we'll use an out buffer that is much larger than the out ep size. */
+#define OUT_BUF_SZ (OUT_EP_SZ * 32)
+/* The in buffer cannot be larger than the device can offer, though. */
+#define IN_BUF_SZ 64
+
+/* Because a series of out commands can lead to a multitude of IN_BUF_SZ-sized in packets
+ *to be read, we have multiple buffers to store those before the bitq interface reads them out. */
+#define IN_BUF_CT 8
+
+#define ESP_USB_INTERFACE       1
+
+/* Private data */
+struct esp_usb_jtag {
+       struct libusb_device_handle *usb_device;
+       uint32_t base_speed_khz;
+       uint16_t div_min;
+       uint16_t div_max;
+       uint8_t out_buf[OUT_BUF_SZ];
+       unsigned int out_buf_pos_nibbles;                       /* write position in out_buf */
+
+       uint8_t in_buf[IN_BUF_CT][IN_BUF_SZ];
+       unsigned int in_buf_size_bits[IN_BUF_CT];       /* size in bits of the data stored in an in_buf */
+       unsigned int cur_in_buf_rd, cur_in_buf_wr;      /* read/write index */
+       unsigned int in_buf_pos_bits;   /* which bit in the in buf needs to be returned to bitq next */
+
+       unsigned int read_ep;
+       unsigned int write_ep;
+
+       unsigned int prev_cmd;          /* previous command, stored here for RLEing. */
+       int prev_cmd_repct;                     /* Amount of repetitions of that command we have seen until now */
+
+       /* This is the total number of in bits we need to read, including in unsent commands */
+       unsigned int pending_in_bits;
+
+       unsigned int hw_in_fifo_len;
+
+       struct bitq_interface bitq_interface;
+};
+
+/* For now, we only use one static private struct. Technically, we can re-work this, but I don't think
+ * OpenOCD supports multiple JTAG adapters anyway. */
+static struct esp_usb_jtag esp_usb_jtag_priv;
+static struct esp_usb_jtag *priv = &esp_usb_jtag_priv;
+
+static int esp_usb_vid;
+static int esp_usb_pid;
+static int esp_usb_jtag_caps;
+static int esp_usb_target_chip_id;
+
+static int esp_usb_jtag_init(void);
+static int esp_usb_jtag_quit(void);
+
+/* Try to receive from USB endpoint into the current priv->in_buf */
+static int esp_usb_jtag_recv_buf(void)
+{
+       if (priv->in_buf_size_bits[priv->cur_in_buf_wr] != 0)
+               LOG_ERROR("esp_usb_jtag: IN buffer overflow! (%d, size %d)",
+                       priv->cur_in_buf_wr,
+                       priv->in_buf_size_bits[priv->cur_in_buf_wr]);
+
+       unsigned int recvd = 0, ct = (priv->pending_in_bits + 7) / 8;
+       if (ct > IN_BUF_SZ)
+               ct = IN_BUF_SZ;
+       if (ct == 0) {
+               /* Note that the adapters IN EP specifically does *not* usually generate 0-byte in
+                * packets if there has been no data since the last flush.
+                * As such, we don't need (and shouldn't) try to read it. */
+               return ERROR_OK;
+       }
+
+       priv->in_buf_size_bits[priv->cur_in_buf_wr] = 0;
+       while (recvd < ct) {
+               unsigned int tr;
+               int ret = jtag_libusb_bulk_read(priv->usb_device,
+                       priv->read_ep,
+                       (char *)priv->in_buf[priv->cur_in_buf_wr] + recvd,
+                       ct,
+                       LIBUSB_TIMEOUT_MS,      /*ms*/
+                       (int *)&tr);
+               if (ret != ERROR_OK || tr == 0) {
+                       /* Sometimes the hardware returns 0 bytes instead of NAKking the transaction. Ignore this. */
+                       return ERROR_FAIL;
+               }
+
+               if (tr != ct) {
+                       /* Huh, short read? */
+                       LOG_DEBUG("esp_usb_jtag: usb received only %d out of %d bytes.", tr, ct);
+               }
+               /* Adjust the amount of bits we still expect to read from the USB device after this. */
+               unsigned int bits_in_buf = priv->pending_in_bits;       /* initially assume we read
+                                                                       * everything that was pending */
+               if (bits_in_buf > tr * 8)
+                       bits_in_buf = tr * 8;   /* ...but correct that if that was not the case. */
+               priv->pending_in_bits -= bits_in_buf;
+               priv->in_buf_size_bits[priv->cur_in_buf_wr] += bits_in_buf;
+               recvd += tr;
+       }
+       /* next in buffer for the next time. */
+       priv->cur_in_buf_wr++;
+       if (priv->cur_in_buf_wr == IN_BUF_CT)
+               priv->cur_in_buf_wr = 0;
+       LOG_DEBUG_IO("esp_usb_jtag: In ep: received %d bytes; %d bytes (%d bits) left.", recvd,
+               (priv->pending_in_bits + 7) / 8, priv->pending_in_bits);
+       return ERROR_OK;
+}
+
+/* Sends priv->out_buf to the USB device. */
+static int esp_usb_jtag_send_buf(void)
+{
+       unsigned int ct = priv->out_buf_pos_nibbles / 2;
+       unsigned int written = 0;
+
+       while (written < ct) {
+               int tr = 0, ret = jtag_libusb_bulk_write(priv->usb_device,
+                       priv->write_ep,
+                       (char *)priv->out_buf + written,
+                       ct - written,
+                       LIBUSB_TIMEOUT_MS,      /*ms*/
+                       &tr);
+               LOG_DEBUG_IO("esp_usb_jtag: sent %d bytes.", tr);
+               if (written + tr != ct) {
+                       LOG_DEBUG("esp_usb_jtag: usb sent only %d out of %d bytes.",
+                               written + tr,
+                               ct);
+               }
+               if (ret != ERROR_OK)
+                       return ret;
+               written += tr;
+       }
+       priv->out_buf_pos_nibbles = 0;
+
+       /* If there's more than a bufferful of data queuing up in the jtag adapters IN endpoint, empty
+        * all but one buffer. */
+       while (priv->pending_in_bits > (IN_BUF_SZ + priv->hw_in_fifo_len - 1) * 8)
+               esp_usb_jtag_recv_buf();
+
+       return ERROR_OK;
+}
+
+/* Simply adds a command to the buffer. Is called by the RLE encoding mechanism.
+ *Also sends the intermediate buffer if there's enough to go into one USB packet. */
+static int esp_usb_jtag_command_add_raw(unsigned int cmd)
+{
+       int ret = ERROR_OK;
+
+       if ((priv->out_buf_pos_nibbles & 1) == 0)
+               priv->out_buf[priv->out_buf_pos_nibbles / 2] = (cmd << 4);
+       else
+               priv->out_buf[priv->out_buf_pos_nibbles / 2] |= cmd;
+       priv->out_buf_pos_nibbles++;
+
+       if (priv->out_buf_pos_nibbles == OUT_BUF_SZ * 2)
+               ret = esp_usb_jtag_send_buf();
+       if (ret == ERROR_OK && priv->out_buf_pos_nibbles % (OUT_EP_SZ * 2) == 0) {
+               if (priv->pending_in_bits > (IN_BUF_SZ + priv->hw_in_fifo_len - 1) * 8)
+                       ret = esp_usb_jtag_send_buf();
+       }
+       return ret;
+}
+
+/* Writes a command stream equivalent to writing `cmd` `ct` times. */
+static int esp_usb_jtag_write_rlestream(unsigned int cmd, int ct)
+{
+       /* Special case: stacking flush commands does not make sense (and may not make the hardware very happy) */
+       if (cmd == CMD_FLUSH)
+               ct = 1;
+       /* Output previous command and repeat commands */
+       int ret = esp_usb_jtag_command_add_raw(cmd);
+       if (ret != ERROR_OK)
+               return ret;
+       ct--;   /* as the previous line already executes the command one time */
+       while (ct > 0) {
+               ret = esp_usb_jtag_command_add_raw(CMD_REP(ct & 3));
+               if (ret != ERROR_OK)
+                       return ret;
+               ct >>= 2;
+       }
+       return ERROR_OK;
+}
+
+/* Adds a command to the buffer of things to be sent. Transparently handles RLE compression using
+ * the CMD_REP_x commands */
+static int esp_usb_jtag_command_add(unsigned int cmd)
+{
+       if (cmd == priv->prev_cmd && priv->prev_cmd_repct < CMD_REP_MAX_REPS) {
+               priv->prev_cmd_repct++;
+       } else {
+               /* We can now write out the previous command plus repeat count. */
+               if (priv->prev_cmd_repct) {
+                       int ret = esp_usb_jtag_write_rlestream(priv->prev_cmd, priv->prev_cmd_repct);
+                       if (ret != ERROR_OK)
+                               return ret;
+               }
+               /* Ready for new command. */
+               priv->prev_cmd = cmd;
+               priv->prev_cmd_repct = 1;
+       }
+       return ERROR_OK;
+}
+
+/* Called by bitq interface to output a bit on tdi and perhaps read a bit from tdo */
+static int esp_usb_jtag_out(int tms, int tdi, int tdo_req)
+{
+       int ret = esp_usb_jtag_command_add(CMD_CLK(tdo_req, tdi, tms));
+       if (ret != ERROR_OK)
+               return ret;
+       if (tdo_req)
+               priv->pending_in_bits++;
+       return ERROR_OK;
+}
+
+/* Called by bitq interface to flush all output commands and get returned data ready to read */
+static int esp_usb_jtag_flush(void)
+{
+       int ret;
+       /*Make sure last command is written */
+       if (priv->prev_cmd_repct) {
+               ret = esp_usb_jtag_write_rlestream(priv->prev_cmd, priv->prev_cmd_repct);
+               if (ret != ERROR_OK)
+                       return ret;
+       }
+       priv->prev_cmd_repct = 0;
+       /* Flush in buffer */
+       ret = esp_usb_jtag_command_add_raw(CMD_FLUSH);
+       if (ret != ERROR_OK)
+               return ret;
+       /* Make sure we have an even amount of commands, as we can't write a nibble by itself. */
+       if (priv->out_buf_pos_nibbles & 1) {
+               /*If not, pad with an extra FLUSH */
+               ret = esp_usb_jtag_command_add_raw(CMD_FLUSH);
+               if (ret != ERROR_OK)
+                       return ret;
+       }
+       LOG_DEBUG_IO("esp_usb_jtag: Flush!");
+       /* Send off the buffer. */
+       ret = esp_usb_jtag_send_buf();
+       if (ret != ERROR_OK)
+               return ret;
+
+       /* Immediately fetch the response bits. */
+       while (priv->pending_in_bits > 0)
+               esp_usb_jtag_recv_buf();
+
+       return ERROR_OK;
+}
+
+/* Called by bitq interface to sleep for a determined amount of time */
+static int esp_usb_jtag_sleep(unsigned long us)
+{
+       esp_usb_jtag_flush();
+       /* TODO: we can sleep more precisely (for small amounts of sleep at least) by sending dummy
+        * commands to the adapter. */
+       jtag_sleep(us);
+       return 0;
+}
+
+/* Called by the bitq interface to set the various resets */
+static int esp_usb_jtag_reset(int trst, int srst)
+{
+       /* TODO: handle trst using setup commands. Kind-of superfluous, however, as we can also do
+        * a tap reset using tms, and it's also not implemented on other ESP32 chips with external JTAG. */
+       return esp_usb_jtag_command_add(CMD_RST(srst));
+}
+
+/* Called by bitq to see if the IN data already is returned to the host. */
+static int esp_usb_jtag_in_rdy(void)
+{
+       /* We read all bits in the flush() routine, so if we're here, we have bits or are at EOF. */
+       return 1;
+}
+
+/* Read one bit from the IN data */
+static int esp_usb_jtag_in(void)
+{
+       if (!esp_usb_jtag_in_rdy()) {
+               LOG_ERROR("esp_usb_jtag: Eeek! bitq asked us for in data while not ready!");
+               return -1;
+       }
+       if (priv->cur_in_buf_rd == priv->cur_in_buf_wr &&
+               priv->in_buf_size_bits[priv->cur_in_buf_rd] == 0)
+               return -1;
+
+       /* Extract the bit */
+       int r = (priv->in_buf[priv->cur_in_buf_rd][priv->in_buf_pos_bits / 8] &
+               BIT(priv->in_buf_pos_bits & 7)) ? 1 : 0;
+       /* Move to next bit. */
+       priv->in_buf_pos_bits++;
+       if (priv->in_buf_pos_bits == priv->in_buf_size_bits[priv->cur_in_buf_rd]) {
+               /* No more bits in this buffer; mark as re-usable and move to next buffer. */
+               priv->in_buf_pos_bits = 0;
+               priv->in_buf_size_bits[priv->cur_in_buf_rd] = 0;/*indicate it is free again */
+               priv->cur_in_buf_rd++;
+               if (priv->cur_in_buf_rd == IN_BUF_CT)
+                       priv->cur_in_buf_rd = 0;
+       }
+       return r;
+}
+
+static int esp_usb_jtag_init(void)
+{
+       memset(priv, 0, sizeof(struct esp_usb_jtag));
+
+       const uint16_t vids[] = { esp_usb_vid, 0 };             /* must be null terminated */
+       const uint16_t pids[] = { esp_usb_pid, 0 };             /* must be null terminated */
+
+       bitq_interface = &priv->bitq_interface;
+       bitq_interface->out = esp_usb_jtag_out;
+       bitq_interface->flush = esp_usb_jtag_flush;
+       bitq_interface->sleep = esp_usb_jtag_sleep;
+       bitq_interface->reset = esp_usb_jtag_reset;
+       bitq_interface->in_rdy = esp_usb_jtag_in_rdy;
+       bitq_interface->in = esp_usb_jtag_in;
+
+       int r = jtag_libusb_open(vids, pids, &priv->usb_device, NULL);
+       if (r != ERROR_OK) {
+               LOG_ERROR("esp_usb_jtag: could not find or open device!");
+               goto out;
+       }
+
+       jtag_libusb_set_configuration(priv->usb_device, USB_CONFIGURATION);
+
+       r = jtag_libusb_choose_interface(priv->usb_device, &priv->read_ep, &priv->write_ep,
+               LIBUSB_CLASS_VENDOR_SPEC, LIBUSB_CLASS_VENDOR_SPEC, ESP_USB_INTERFACE, LIBUSB_TRANSFER_TYPE_BULK);
+       if (r != ERROR_OK) {
+               LOG_ERROR("esp_usb_jtag: error finding/claiming JTAG interface on device!");
+               goto out;
+       }
+
+       /* TODO: This is not proper way to get caps data. Two requests can be done.
+        * 1- With the minimum size required to get to know the total length of that struct,
+        * 2- Then exactly the length of that struct. */
+       uint8_t jtag_caps_desc[JTAG_PROTO_CAPS_DATA_LEN];
+       int jtag_caps_read_len = jtag_libusb_control_transfer(priv->usb_device,
+               LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_STANDARD | LIBUSB_RECIPIENT_DEVICE,
+               LIBUSB_REQUEST_GET_DESCRIPTOR, esp_usb_jtag_caps, 0,
+               (char *)jtag_caps_desc, JTAG_PROTO_CAPS_DATA_LEN, LIBUSB_TIMEOUT_MS);
+       if (jtag_caps_read_len <= 0) {
+               LOG_ERROR("esp_usb_jtag: could not retrieve jtag_caps descriptor!");
+               goto out;
+       }
+
+       /* defaults for values we normally get from the jtag caps descriptor */
+       priv->base_speed_khz = UINT32_MAX;
+       priv->div_min = 1;
+       priv->div_max = 1;
+
+       int p = esp_usb_jtag_caps ==
+               VEND_DESCR_BUILTIN_JTAG_CAPS ? JTAG_BUILTIN_DESCR_START_OFF : JTAG_EUB_DESCR_START_OFF;
+
+       if (p + sizeof(struct jtag_proto_caps_hdr) > (unsigned int)jtag_caps_read_len) {
+               LOG_ERROR("esp_usb_jtag: not enough data to get header");
+               goto out;
+       }
+
+       struct jtag_proto_caps_hdr *hdr = (struct jtag_proto_caps_hdr *)&jtag_caps_desc[p];
+       if (hdr->proto_ver != JTAG_PROTO_CAPS_VER) {
+               LOG_ERROR("esp_usb_jtag: unknown jtag_caps descriptor version 0x%X!",
+                       hdr->proto_ver);
+               goto out;
+       }
+       if (hdr->length > jtag_caps_read_len) {
+               LOG_ERROR("esp_usb_jtag: header length (%d) bigger then max read bytes (%d)",
+                       hdr->length, jtag_caps_read_len);
+               goto out;
+       }
+
+       p += sizeof(struct jtag_proto_caps_hdr);
+
+       while (p + sizeof(struct jtag_gen_hdr) < hdr->length) {
+               struct jtag_gen_hdr *dhdr = (struct jtag_gen_hdr *)&jtag_caps_desc[p];
+               if (dhdr->type == JTAG_PROTO_CAPS_SPEED_APB_TYPE) {
+                       if (p + sizeof(struct jtag_proto_caps_speed_apb) < hdr->length) {
+                               LOG_ERROR("esp_usb_jtag: not enough data to get caps speed");
+                               goto out;
+                       }
+                       struct jtag_proto_caps_speed_apb *spcap = (struct jtag_proto_caps_speed_apb *)dhdr;
+                       /* base speed always is half APB speed */
+                       priv->base_speed_khz = le_to_h_u16(spcap->apb_speed_10khz) * 10 / 2;
+                       priv->div_min = le_to_h_u16(spcap->div_min);
+                       priv->div_max = le_to_h_u16(spcap->div_max);
+                       /* TODO: mark in priv that this is apb-derived and as such may change if apb
+                        * ever changes? */
+               } else {
+                       LOG_WARNING("esp_usb_jtag: unknown caps type 0x%X", dhdr->type);
+               }
+               p += dhdr->length;
+       }
+       if (priv->base_speed_khz == UINT32_MAX) {
+               LOG_WARNING("esp_usb_jtag: No speed caps found... using sane-ish defaults.");
+               priv->base_speed_khz = 1000;
+       }
+       LOG_INFO("esp_usb_jtag: Device found. Base speed %dKHz, div range %d to %d",
+               priv->base_speed_khz, priv->div_min, priv->div_max);
+
+       /* TODO: grab from (future) descriptor if we ever have a device with larger IN buffers */
+       priv->hw_in_fifo_len = 4;
+
+       /* inform bridge board about the connected target chip for the specific operations
+        * it is also safe to send this info to chips that have builtin usb jtag */
+       jtag_libusb_control_transfer(priv->usb_device,
+               LIBUSB_REQUEST_TYPE_VENDOR,
+               VEND_JTAG_SET_CHIPID,
+               esp_usb_target_chip_id,
+               0,
+               NULL,
+               0,
+               LIBUSB_TIMEOUT_MS);
+
+       return ERROR_OK;
+
+out:
+       if (priv->usb_device)
+               jtag_libusb_close(priv->usb_device);
+       bitq_interface = NULL;
+       priv->usb_device = NULL;
+       return ERROR_FAIL;
+}
+
+static int esp_usb_jtag_quit(void)
+{
+       if (!priv->usb_device)
+               return ERROR_OK;
+       jtag_libusb_close(priv->usb_device);
+       bitq_cleanup();
+       bitq_interface = NULL;
+       return ERROR_OK;
+}
+
+static int esp_usb_jtag_speed_div(int divisor, int *khz)
+{
+       *khz = priv->base_speed_khz / divisor;
+       return ERROR_OK;
+}
+
+static int esp_usb_jtag_khz(int khz, int *divisor)
+{
+       if (khz == 0) {
+               LOG_WARNING("esp_usb_jtag: RCLK not supported");
+               return ERROR_FAIL;
+       }
+
+       *divisor = priv->base_speed_khz / khz;
+       LOG_DEBUG("Divisor for %d KHz with base clock of %d khz is %d",
+               khz,
+               priv->base_speed_khz,
+               *divisor);
+       if (*divisor < priv->div_min)
+               *divisor = priv->div_min;
+       if (*divisor > priv->div_max)
+               *divisor = priv->div_max;
+
+       return ERROR_OK;
+}
+
+static int esp_usb_jtag_speed(int divisor)
+{
+       if (divisor == 0) {
+               LOG_ERROR("esp_usb_jtag: Adaptive clocking is not supported.");
+               return ERROR_JTAG_NOT_IMPLEMENTED;
+       }
+
+       LOG_DEBUG("esp_usb_jtag: setting divisor %d", divisor);
+       jtag_libusb_control_transfer(priv->usb_device,
+               LIBUSB_REQUEST_TYPE_VENDOR, VEND_JTAG_SETDIV, divisor, 0, NULL, 0, LIBUSB_TIMEOUT_MS);
+
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(esp_usb_jtag_tdo_cmd)
+{
+       char tdo;
+       if (!priv->usb_device)
+               return ERROR_FAIL;
+       int r = jtag_libusb_control_transfer(priv->usb_device,
+               LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR, VEND_JTAG_GETTDO, 0, 0, &tdo, 1, LIBUSB_TIMEOUT_MS);
+       if (r < 1)
+               return r;
+
+       command_print(CMD, "%d", tdo);
+
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(esp_usb_jtag_setio_cmd)
+{
+       uint32_t tdi, tms, tck, trst, srst;
+       uint16_t d = 0;
+
+       if (!priv->usb_device)
+               return ERROR_FAIL;
+
+       if (CMD_ARGC != 5)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], tdi);
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], tms);
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], tck);
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[3], trst);
+       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[4], srst);
+       if (tdi)
+               d |= VEND_JTAG_SETIO_TDI;
+       if (tms)
+               d |= VEND_JTAG_SETIO_TMS;
+       if (tck)
+               d |= VEND_JTAG_SETIO_TCK;
+       if (trst)
+               d |= VEND_JTAG_SETIO_TRST;
+       if (srst)
+               d |= VEND_JTAG_SETIO_SRST;
+
+       jtag_libusb_control_transfer(priv->usb_device,
+               0x40, VEND_JTAG_SETIO, d, 0, NULL, 0, LIBUSB_TIMEOUT_MS);
+
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(esp_usb_jtag_vid_pid)
+{
+       if (CMD_ARGC != 2)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], esp_usb_vid);
+       COMMAND_PARSE_NUMBER(int, CMD_ARGV[1], esp_usb_pid);
+       LOG_INFO("esp_usb_jtag: VID set to 0x%x and PID to 0x%x", esp_usb_vid, esp_usb_pid);
+
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(esp_usb_jtag_caps_descriptor)
+{
+       if (CMD_ARGC != 1)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], esp_usb_jtag_caps);
+       LOG_INFO("esp_usb_jtag: capabilities descriptor set to 0x%x", esp_usb_jtag_caps);
+
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(esp_usb_jtag_chip_id)
+{
+       if (CMD_ARGC != 1)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], esp_usb_target_chip_id);
+       LOG_INFO("esp_usb_jtag: target chip id set to %d", esp_usb_target_chip_id);
+
+       return ERROR_OK;
+}
+
+static const struct command_registration esp_usb_jtag_subcommands[] = {
+       {
+               .name = "tdo",
+               .handler = &esp_usb_jtag_tdo_cmd,
+               .mode = COMMAND_EXEC,
+               .help = "Returns the current state of the TDO line",
+               .usage = "",
+       },
+       {
+               .name = "setio",
+               .handler = &esp_usb_jtag_setio_cmd,
+               .mode = COMMAND_EXEC,
+               .help = "Manually set the status of the output lines",
+               .usage = "tdi tms tck trst srst"
+       },
+       {
+               .name = "vid_pid",
+               .handler = &esp_usb_jtag_vid_pid,
+               .mode = COMMAND_CONFIG,
+               .help = "set vendor ID and product ID for ESP usb jtag driver",
+               .usage = "vid pid",
+       },
+       {
+               .name = "caps_descriptor",
+               .handler = &esp_usb_jtag_caps_descriptor,
+               .mode = COMMAND_CONFIG,
+               .help = "set jtag descriptor to read capabilities of ESP usb jtag driver",
+               .usage = "descriptor",
+       },
+       {
+               .name = "chip_id",
+               .handler = &esp_usb_jtag_chip_id,
+               .mode = COMMAND_CONFIG,
+               .help = "set chip_id to transfer to the bridge",
+               .usage = "chip_id",
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+static const struct command_registration esp_usb_jtag_commands[] = {
+       {
+               .name = "espusbjtag",
+               .mode = COMMAND_ANY,
+               .help = "ESP-USB-JTAG commands",
+               .chain = esp_usb_jtag_subcommands,
+               .usage = "",
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+static struct jtag_interface esp_usb_jtag_interface = {
+       .supported = DEBUG_CAP_TMS_SEQ,
+       .execute_queue = bitq_execute_queue,
+};
+
+struct adapter_driver esp_usb_adapter_driver = {
+       .name = "esp_usb_jtag",
+       .transports = jtag_only,
+       .commands = esp_usb_jtag_commands,
+
+       .init = esp_usb_jtag_init,
+       .quit = esp_usb_jtag_quit,
+       .speed_div = esp_usb_jtag_speed_div,
+       .speed = esp_usb_jtag_speed,
+       .khz = esp_usb_jtag_khz,
+
+       .jtag_ops = &esp_usb_jtag_interface,
+};
index 9afd69dff33dd366ebaf4859a72121fd12830e44..4cc197b50258396cbf25321fcdab1216268c3027 100644 (file)
@@ -54,6 +54,9 @@ extern struct adapter_driver ftdi_adapter_driver;
 #if BUILD_USB_BLASTER == 1 || BUILD_USB_BLASTER_2 == 1
 extern struct adapter_driver usb_blaster_adapter_driver;
 #endif
+#if BUILD_ESP_USB_JTAG == 1
+extern struct adapter_driver esp_usb_adapter_driver;
+#endif
 #if BUILD_JTAG_VPI == 1
 extern struct adapter_driver jtag_vpi_adapter_driver;
 #endif
@@ -171,6 +174,9 @@ struct adapter_driver *adapter_drivers[] = {
 #if BUILD_USB_BLASTER || BUILD_USB_BLASTER_2 == 1
                &usb_blaster_adapter_driver,
 #endif
+#if BUILD_ESP_USB_JTAG == 1
+               &esp_usb_adapter_driver,
+#endif
 #if BUILD_JTAG_VPI == 1
                &jtag_vpi_adapter_driver,
 #endif
diff --git a/tcl/board/esp32s2-bridge.cfg b/tcl/board/esp32s2-bridge.cfg
new file mode 100644 (file)
index 0000000..b87be8b
--- /dev/null
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Example OpenOCD configuration file for ESP32-S2 connected via ESP USB Bridge board
+#
+# For example, OpenOCD can be started for ESP32-S2 debugging on
+#
+#   openocd -f board/esp32s2-bridge.cfg
+#
+
+# Source the JTAG interface configuration file
+source [find interface/esp_usb_bridge.cfg]
+# ESP32S2 chip id defined in the idf esp_chip_model_t
+espusbjtag chip_id 2
+# Source the ESP32-S2 configuration file
+source [find target/esp32s2.cfg]
diff --git a/tcl/interface/esp_usb_bridge.cfg b/tcl/interface/esp_usb_bridge.cfg
new file mode 100644 (file)
index 0000000..9342239
--- /dev/null
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# ESP USB Bridge jtag adapter
+#
+
+adapter driver esp_usb_jtag
+
+espusbjtag vid_pid 0x303a 0x1002
+espusbjtag caps_descriptor 0x030A  # string descriptor index:10