This is an automated email from Gerrit. "Erhan Kurubas <erhan.kuru...@espressif.com>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/6943
-- gerrit commit d38f260a51fbec028083771648864983e2c1c588 Author: Erhan Kurubas <erhan.kuru...@espressif.com> Date: Thu Apr 21 21:48:28 2022 +0200 jtag: add esp_usb_jtag driver 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.kuru...@espressif.com> Change-Id: If966268cb8d26f76540dd5440245a17ed0b72c61 diff --git a/configure.ac b/configure.ac index 15d7229a4f..6f9e727acf 100644 --- a/configure.ac +++ b/configure.ac @@ -123,7 +123,8 @@ m4_define([USB1_ADAPTERS], [[armjtagew], [Olimex ARM-JTAG-EW Programmer], [ARMJTAGEW]], [[rlink], [Raisonance RLink JTAG Programmer], [RLINK]], [[usbprog], [USBProg JTAG Programmer], [USBPROG]], - [[aice], [Andes JTAG Programmer], [AICE]]]) + [[aice], [Andes JTAG Programmer], [AICE]], + [[esp_usb_jtag], [Espressif JTAG Programmer], [ESP_USB_JTAG]]]) m4_define([HIDAPI_ADAPTERS], [[[cmsis_dap], [CMSIS-DAP Compliant Debugger], [CMSIS_DAP_HID]], @@ -698,6 +699,12 @@ 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"]) diff --git a/src/jtag/drivers/Makefile.am b/src/jtag/drivers/Makefile.am index 887f99bcd2..5bc6c245f9 100644 --- a/src/jtag/drivers/Makefile.am +++ b/src/jtag/drivers/Makefile.am @@ -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 index 0000000000..aaa54ec776 --- /dev/null +++ b/src/jtag/drivers/esp_usb_jtag.c @@ -0,0 +1,902 @@ +/*************************************************************************** + * Espressif USB to Jtag adapter * + * Copyright (C) 2020 Espressif Systems (Shanghai) Co. Ltd. * + * Author: Jeroen Domburg <jer...@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 + +#if IS_CYGWIN == 1 +#include "windows.h" +#endif + +#include <jtag/adapter.h> +#include <jtag/interface.h> +#include <helper/time_support.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 1. 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; + +/* +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. +*/ + +#define JTAG_PROTO_CAPS_SPEED_APB_TYPE 1 +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 */ + uint16_t apb_speed_10khz; /*ABP bus speed, in 10KHz increments. Base speed is half + * this. */ + uint16_t div_min; /*minimum divisor (to base speed), inclusive */ + uint16_t div_max; /*maximum divisor (to base speed), inclusive */ +} __packed; + +#define VEND_DESCRIPTOR_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 (1 << 0) +#define VEND_JTAG_SETIO_TMS (1 << 1) +#define VEND_JTAG_SETIO_TCK (1 << 2) +#define VEND_JTAG_SETIO_TRST (1 << 3) +#define VEND_JTAG_SETIO_SRST (1 << 4) + +#define CMD_CLK(cap, tdi, tms) ((cap ? 4 : 0) | (tms ? 2 : 0) | (tdi ? 1 : 0)) +#define CMD_RST(srst) (8 | (srst ? 1 : 0)) +#define CMD_FLUSH 0xA +#define CMD_RSVD 0xB +#define CMD_REP(r) (0xC + (r)) + +/*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 + +/* + * comment from libusb: + * As per the USB 3.0 specs, the current maximum limit for the depth is 7. + */ +#define MAX_USB_PORTS 7 + +/*Private data */ +struct esp_usb_jtag { + struct libusb_device_handle *usb_device; + int base_speed_khz; + int div_min; + int div_max; + uint8_t out_buf[OUT_BUF_SZ]; + int out_buf_pos_nibbles; /*write position in out_buf */ + + uint8_t in_buf[IN_BUF_CT][IN_BUF_SZ]; + int in_buf_size_bits[IN_BUF_CT]; /*size in bits of the data stored in an in_buf */ + int cur_in_buf_rd, cur_in_buf_wr; /*read/write index */ + 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; + + 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 */ + int pending_in_bits; + FILE *logfile; /*If non-NULL, we log communication + * traces here. */ + + int hw_in_fifo_len; + char *serial[256 + 1]; /* device serial */ +}; + +/*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 const char *esp_usb_jtag_serial; + +static int esp_usb_vid; +static int esp_usb_pid; +static int esp_usb_jtag_caps; +static int esp_usb_target_chip_id; + +/*The JTAG adapter can drop a logfile detailing all low-level USB transactions that are done. + *If this is defined, the log will have entries that allow replay on a testbed. */ +#define LOG_REPLAYABLE + +const char *cmds_string[] = {"000", "001", "010", "011", "100", "101", "110", "111", + "srst0", "srst1", "flush", "rsv", "rep0", "rep1", "rep2", "rep3"}; + +static int esp_usb_jtag_init(void); +static int esp_usb_jtag_quit(void); + +static void log_cmds(uint8_t *buf, int ct, int wr) +{ + fprintf(priv->logfile, "Wrote %d of %d bytes:%s\n", wr, ct, wr != ct ? "***ERROR***" : ""); +#ifdef LOG_REPLAYABLE + fprintf(priv->logfile, ":w %02X ", ct); + for (int n = 0; n < ct; n++) + fprintf(priv->logfile, "%02X ", buf[n]); + fprintf(priv->logfile, "\n"); +#endif + for (int n = 0; n < ct * 2; n++) { + int c = (n & 1) ? (buf[n / 2] & 0xf) : (buf[n / 2] >> 4); + fprintf(priv->logfile, "%s ", cmds_string[c]); + } + fprintf(priv->logfile, "\n"); + fflush(priv->logfile); +} + +static void log_resp(uint8_t *buf, int ct, int recvd) +{ + fprintf(priv->logfile, + "Read %d of %d bytes (%d (%d bits) were pending): %s\n", + recvd, + ct, + (priv->pending_in_bits+7)/8, + priv->pending_in_bits, + recvd != ct ? "***ERROR***" : ""); +#ifdef LOG_REPLAYABLE + fprintf(priv->logfile, "%cr %02X\n", recvd == 0 ? 'X' : ':', ct); +#endif + for (int n = 0; n < recvd; n++) + fprintf(priv->logfile, "%02X ", buf[n]); + fprintf(priv->logfile, "\n"); + fflush(priv->logfile); +} + +/*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]); + + 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) { + int ret, tr; + ret = jtag_libusb_bulk_read(priv->usb_device, + priv->read_ep, + (char *)priv->in_buf[priv->cur_in_buf_wr] + recvd, + ct, + 500, /*ms*/ + &tr); + if (priv->logfile) + log_resp(priv->in_buf[priv->cur_in_buf_wr], ct, 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. + **/ + 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) +{ + int ct = priv->out_buf_pos_nibbles / 2; + 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, + 500, /*ms*/ + &tr); + LOG_DEBUG_IO("esp_usb_jtag: sent %d bytes.", tr); + if (priv->logfile) + log_cmds(priv->out_buf, ct, 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 queing 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(int cmd) +{ + int ret; + + 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) + return ret; + } + if (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(); + if (ret != ERROR_OK) + return ret; + } + } + return ERROR_OK; +} + +/*Writes a command stream equivalent to writing `cmd` `ct` times. */ +static int esp_usb_jtag_write_rlestream(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(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 reser 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] & + (1 << (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 = calloc(sizeof(struct bitq_interface), 1); + 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; + } + + /* serial number may have been set by `adapter serial` command */ + if (adapter_get_required_serial()) { + free((void *)esp_usb_jtag_serial); + esp_usb_jtag_serial = strdup(adapter_get_required_serial()); + } + + jtag_libusb_set_configuration(priv->usb_device, USB_CONFIGURATION); + + r = jtag_libusb_choose_interface(priv->usb_device, &priv->read_ep, &priv->write_ep, + 0xff, 0xff, 0x01, 0x2); + if (r != ERROR_OK) { + LOG_ERROR("esp_usb_jtag: error finding/claiming JTAG interface on device!"); + goto out; + } + + char jtag_caps_desc[256]; + r = 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, + jtag_caps_desc, 255, 1000); + if (r <= 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 = -1; + priv->div_min = 1; + priv->div_max = 1; + + /* start of the descriptor header * + * eub uses string descriptor. Skip 1 byte length and 1 byte descriptor type */ + int p = esp_usb_jtag_caps == VEND_DESCRIPTOR_BUILTIN_JTAG_CAPS ? 0 : 2; + + 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; + } + + p += sizeof(struct jtag_proto_caps_hdr); + + while (p < hdr->length) { + struct jtag_gen_hdr *dhdr = (struct jtag_gen_hdr *)&jtag_caps_desc[p]; + if (dhdr->type == JTAG_PROTO_CAPS_SPEED_APB_TYPE) { + 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 = (spcap->apb_speed_10khz*10)/2; + priv->div_min = spcap->div_min; + priv->div_max = 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 == -1) { + 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, + 0x40, + VEND_JTAG_SET_CHIPID, + esp_usb_target_chip_id, + 0, + NULL, + 0, + 1000); + + return ERROR_OK; + +out: + free((void *)esp_usb_jtag_serial); + esp_usb_jtag_serial = NULL; + if (priv->usb_device) + jtag_libusb_close(priv->usb_device); + free(bitq_interface); + bitq_interface = NULL; + priv->usb_device = NULL; + return ERROR_FAIL; +} + +static int esp_usb_jtag_quit(void) +{ + if (priv->usb_device == NULL) + return ERROR_OK; + jtag_libusb_close(priv->usb_device); + bitq_cleanup(); + free(bitq_interface); + bitq_interface = NULL; + if (priv->logfile) + fclose(priv->logfile); + 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); + if (priv->logfile) + fprintf(priv->logfile, "Setting divisor to %d\n.", divisor); + jtag_libusb_control_transfer(priv->usb_device, + 0x40, VEND_JTAG_SETDIV, divisor, 0, NULL, 0, 1000); + + 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, + 0xC0, VEND_JTAG_GETTDO, 0, 0, &tdo, 1, 1000); + if (r < 1) + return r; + + command_print(CMD, "%d", tdo); + if (priv->logfile) + fprintf(priv->logfile, "Retrieved TDO: %d\n.", 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; + + if (priv->logfile) + fprintf(priv->logfile, "SetIO command 0x%X\n.", d); + jtag_libusb_control_transfer(priv->usb_device, + 0x40, VEND_JTAG_SETIO, d, 0, NULL, 0, 1000); + + return ERROR_OK; +} + +COMMAND_HANDLER(esp_usb_jtag_log_cmd) +{ + if (!priv->usb_device) + return ERROR_FAIL; + + if (CMD_ARGC != 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + priv->logfile = fopen(CMD_ARGV[0], "w"); + if (!priv->logfile) + command_print(CMD, "Could not open log file: %s.", strerror(errno)); + + return ERROR_OK; +} + +COMMAND_HANDLER(esp_usb_jtag_vid_pid) +{ + if (CMD_ARGC < 2) { + LOG_ERROR("You need to supply the vendor and product IDs"); + return ERROR_FAIL; + } + + 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) { + LOG_ERROR("You need to supply the jtag capabilities descriptor"); + return ERROR_FAIL; + } + + 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) { + LOG_ERROR("You need to supply the chip id"); + return ERROR_FAIL; + } + + 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 = "log", + .handler = &esp_usb_jtag_log_cmd, + .mode = COMMAND_EXEC, + .help = "Log USB comms to file", + .usage = "logfile.txt" + }, + { + .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 = "", + }, + { + .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 = "", + }, + { + .name = "chip_id", + .handler = &esp_usb_jtag_chip_id, + .mode = COMMAND_CONFIG, + .help = "set chip_id to transfer to the bridge", + .usage = "", + }, + 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, +}; diff --git a/src/jtag/interfaces.c b/src/jtag/interfaces.c index ddf70cc240..d98dac2c13 100644 --- a/src/jtag/interfaces.c +++ b/src/jtag/interfaces.c @@ -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 @@ -168,6 +171,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 --