This is an automated email from Gerrit. "ZhiYuanNJ <871238...@qq.com>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/8612
-- gerrit commit 27b3defa48d1bb52103fe9322c113caec7b9b92b Author: Henrik Mau <henrik....@analog.com> Date: Wed Nov 13 15:43:23 2024 +0000 jtag/drivers: Add support for WCH CH347T/F interfaces CH347 is a high-speed USB bus converter chip. Can provides JTAG/SWD/UART/SPI/I2C interface through USB bus. This driver comes from: https://github.com/WCHSoftGroup/ch347 Change-Id: Id19b6ba270f8bd19943dde3bbd986c99553b82c8 Signed-off-by: ZhiYuanNJ <871238...@qq.com> diff --git a/configure.ac b/configure.ac index 567152b0a6..8feec92b06 100644 --- a/configure.ac +++ b/configure.ac @@ -121,6 +121,7 @@ m4_define([ADAPTER_OPT], [m4_translit(ADAPTER_ARG($1), [_], [-])]) m4_define([USB1_ADAPTERS], [[[ftdi], [MPSSE mode of FTDI based devices], [FTDI]], + [[ch347], [Mode 3 of CH347 based devices], [CH347]], [[stlink], [ST-Link Programmer], [HLADAPTER_STLINK]], [[ti_icdi], [TI ICDI JTAG Programmer], [HLADAPTER_ICDI]], [[ulink], [Keil ULINK JTAG Programmer], [ULINK]], @@ -405,6 +406,10 @@ AC_ARG_ENABLE([remote-bitbang], AS_HELP_STRING([--enable-remote-bitbang], [Enable building support for the Remote Bitbang driver]), [build_remote_bitbang=$enableval], [build_remote_bitbang=no]) +AC_ARG_ENABLE([ch347], + AS_HELP_STRING([--enable-ch347], [Enable building support for CH347]), + [build_ch347=$enableval], [build_ch347=no]) + AS_CASE(["${host_cpu}"], [i?86|x86*], [], [ @@ -632,6 +637,12 @@ AS_IF([test "x$build_sysfsgpio" = "xyes"], [ AC_DEFINE([BUILD_SYSFSGPIO], [0], [0 if you don't want SysfsGPIO driver.]) ]) +AS_IF([test "x$build_ch347" = "xyes"], [ + AC_DEFINE([BUILD_CH347], [1], [1 if you want CH347.]) +], [ + AC_DEFINE([BUILD_CH347], [0], [0 if you don't want CH347.]) +]) + PKG_CHECK_MODULES([LIBUSB1], [libusb-1.0], [ use_libusb1=yes AC_DEFINE([HAVE_LIBUSB1], [1], [Define if you have libusb-1.x]) @@ -795,6 +806,7 @@ AM_CONDITIONAL([USE_LIBJAYLINK], [test "x$use_libjaylink" = "xyes"]) AM_CONDITIONAL([RSHIM], [test "x$build_rshim" = "xyes"]) AM_CONDITIONAL([DMEM], [test "x$build_dmem" = "xyes"]) AM_CONDITIONAL([HAVE_CAPSTONE], [test "x$enable_capstone" != "xno"]) +AM_CONDITIONAL([BUILD_CH347], [test "x$build_ch347" = "xyes"]) AM_CONDITIONAL([INTERNAL_JIMTCL], [test "x$use_internal_jimtcl" = "xyes"]) AM_CONDITIONAL([HAVE_JIMTCL_PKG_CONFIG], [test "x$have_jimtcl_pkg_config" = "xyes"]) diff --git a/contrib/60-openocd.rules b/contrib/60-openocd.rules index 29f8d7a6d4..5ff0dc22be 100644 --- a/contrib/60-openocd.rules +++ b/contrib/60-openocd.rules @@ -216,6 +216,10 @@ ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="06ad", MODE="660", GROUP="plugdev", # USBprog with OpenOCD firmware ATTRS{idVendor}=="1781", ATTRS{idProduct}=="0c63", MODE="660", GROUP="plugdev", TAG+="uaccess" +# WCH CH347 chip +ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="55dd", MODE="660", GROUP="plugdev", TAG+="uaccess" +ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="55de", MODE="660", GROUP="plugdev", TAG+="uaccess" + # TI/Luminary Stellaris In-Circuit Debug Interface (ICDI) Board ATTRS{idVendor}=="1cbe", ATTRS{idProduct}=="00fd", MODE="660", GROUP="plugdev", TAG+="uaccess" diff --git a/doc/openocd.texi b/doc/openocd.texi index 9b5cfbb83e..a3bed045c3 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -624,6 +624,9 @@ 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. +@item @b{ch347T/F} +@* A JTAG driver which uses the WCH CH347T/F JTAG chip. + @end itemize @node About Jim-Tcl diff --git a/src/jtag/drivers/ch347.c b/src/jtag/drivers/ch347.c new file mode 100644 index 0000000000..eb003fa711 --- /dev/null +++ b/src/jtag/drivers/ch347.c @@ -0,0 +1,1515 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/*************************************************************************** + * Driver for CH347-JTAG interface V1.1 * + * * + * Copyright (C) 2024 by oidcat <oidcat...@163.com> * + * * + * CH347 is a high-speed USB bus converter chip that provides UART, I2C * + * and SPI synchronous serial ports and JTAG interface through USB bus. * + * * + * The Jtag interface by CH347 can supports transmission frequency * + * configuration up to 60MHz. * + * * + * The USB2.0 to JTAG scheme based on CH347 can be used to build * + * customized USB high-speed JTAG debugger and other products. * + * * + * 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. * + * * + * _____________ * + * | |____JTAG/SWD (TDO,TDI,TMS,TCK,TRST) * + * USB__| CH347T/F | * + * |_____________|____UART(TXD1,RXD1,RTS1,CTS1,DTR1) * + * ______|______ * + * | | * + * | 8 MHz XTAL | * + * |_____________| * + * * + * 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" +#undef LOG_ERROR +#endif +/* project specific includes */ +#include <jtag/interface.h> +#include <jtag/commands.h> +#include <jtag/swd.h> +#include <helper/time_support.h> +#include <helper/replacements.h> +#include <helper/list.h> +#include <helper/binarybuffer.h> +#include "libusb_helper.h" +/* system includes */ +#include <stdlib.h> +#include <string.h> +#include <sys/time.h> +#include <time.h> +#include <unistd.h> +#define JTAGIO_STA_OUT_TDI (0x10) +#define JTAGIO_STA_OUT_TMS (0x02) +#define JTAGIO_STA_OUT_TCK (0x01) +#define JTAGIO_STA_OUT_TRST (0x20) +#define TDI_H JTAGIO_STA_OUT_TDI +#define TDI_L 0 +#define TMS_H JTAGIO_STA_OUT_TMS +#define TMS_L 0 +#define TCK_H JTAGIO_STA_OUT_TCK +#define TCK_L 0 +#define TRST_H JTAGIO_STA_OUT_TRST +#define TRST_L 0 +#define KHZ(n) ((n) * UINT64_C(1000)) +#define MHZ(n) ((n) * UINT64_C(1000000)) +#define GHZ(n) ((n) * UINT64_C(1000000000)) +#define CH347T_MPHSI_INTERFACE 2 // the JTAG interface is number 2 +#define CH347F_MPHSI_INTERFACE 4 // the JTAG interface is number 4 +#define DEFAULT_VENDOR_ID 0x1a86 // if no vendor id is set use this CH347 default +#define DEFAULT_PRODUCT_ID 0x55dd // if no product id is set use this CH347 default +#define HW_TDO_BUF_SIZE 4096 +#define SF_PACKET_BUF_SIZE 51200 /* Command packet length */ +#define UCMDPKT_DATA_MAX_BYTES_USBHS 507 /* The data length contained in each + command packet during USB + high-speed operation */ +#define USBC_PACKET_USBHS 512 /* Maximum data length per packet at + USB high speed */ +#define USBC_PACKET_USBHS_SINGLE 510 /* usb high speed max + package length */ +#define CH347_CMD_HEADER 3 /* Protocol header length */ +/* Protocol transmission format: CMD (1 byte)+Length (2 bytes)+Data */ +#define CH347_CMD_INFO_RD 0xCA /* Parameter acquisition, used to + obtain firmware version, + JTAG interface related parameters, + etc */ +#define CH347_CMD_JTAG_INIT 0xD0 /* JTAG Interface Initialization + Command */ +#define CH347_CMD_JTAG_BIT_OP 0xD1 /* JTAG interface pin bit control + command */ +#define CH347_CMD_JTAG_BIT_OP_RD 0xD2 /* JTAG interface pin bit control and + read commands */ +#define CH347_CMD_JTAG_DATA_SHIFT 0xD3 /* JTAG interface data shift + command */ +#define CH347_CMD_JTAG_DATA_SHIFT_RD 0xD4 /* JTAG interface data shift and read + command */ +/* SWD */ +#define CH347_CMD_SWD_INIT 0xE5 /* SWD Interface Initialization Command */ +#define CH347_CMD_SWD 0xE8 +#define CH347_CMD_SWD_REG_W 0xA0 /* SWD Interface write reg */ +#define CH347_CMD_SWD_SEQ_W 0xA1 /* SWD Interface write spec seq */ +#define CH347_CMD_SWD_REG_R 0xA2 /* SWD Interface read reg */ +#define CH347_MAX_SEND_CMD 0X20 /* max send cmd number */ +#define CH347_MAX_SEND_BUF 0X200 +#define CH347_MAX_RECV_BUF 0X200 +#define BUILD_UINT16(lo_byte, hi_byte) \ + ((unsigned short)(((lo_byte) & 0x00FF) + (((hi_byte) & 0x00FF) << 8))) +#pragma pack(1) +enum pack_size { + STANDARD_PACK = 0, + LARGER_PACK = 1, +}; +struct ch347_info /* Record the CH347 pin status */ +{ + int TMS; + int TDI; + int TCK; + int TRST; + int buffer_idx; + unsigned char buffer[SF_PACKET_BUF_SIZE]; + int len_idx; + int len_value; + unsigned char last_cmd; + unsigned char read_buffer[SF_PACKET_BUF_SIZE]; + unsigned int read_idx; + unsigned int read_count; + struct bit_copy_queue read_queue; + enum pack_size pack_size; +}; +typedef struct _CH347_SWD_IO { + unsigned char usbcmd; /* 0xA0、0xA1、0xA2 */ + unsigned char cmd; + unsigned int *dst; + unsigned int value; + struct list_head list_entry; +} CH347_SWD_IO, *PCH347_SWD_IO; +typedef struct _CH347_SWD_CONTEXT { + unsigned char send_buf[CH347_MAX_SEND_BUF]; + unsigned char recv_buf[CH347_MAX_RECV_BUF]; + unsigned int send_len; + unsigned int recv_len; + unsigned int need_recv_len; + int queued_retval; + unsigned char sent_cmd_count; + struct list_head send_cmd_head; + struct list_head free_cmd_head; + unsigned char *ch347_cmd_buf; +} CH347_SWD_CONTEXT; +#pragma pack() +int dev_is_opened; /* Whether the device is turned on */ +unsigned long USBC_PACKET; +static CH347_SWD_CONTEXT ch347_swd_context; +static bool swd_mode; +#include <jtag/drivers/libusb_helper.h> +#define CH347_EPOUT 0x06u +#define CH347_EPIN 0x86u +struct libusb_device_handle *ch347_handle; +static char *ch347_device_desc; +static unsigned short ch347_vids[] = {DEFAULT_VENDOR_ID, 0}; +static unsigned short ch347_pids[] = {DEFAULT_PRODUCT_ID, 0}; +static unsigned int ch347_open_device(void) +{ + if (jtag_libusb_open(ch347_vids, ch347_pids, ch347_device_desc, &ch347_handle, NULL) != ERROR_OK) + return false; + else + return true; +} +static bool ch347_write_data(unsigned char *data, unsigned long *length) +{ + int ret, tmp = 0; + ret = jtag_libusb_bulk_write(ch347_handle, + CH347_EPOUT, + (char *)data, + *length, + 1000, &tmp); + *length = tmp; + if (!ret) + return true; + else + return false; +} +static bool ch347_read_data(unsigned char *data, unsigned long *length) +{ + int ret, tmp = 0; + int size = *length; + ret = jtag_libusb_bulk_read(ch347_handle, + CH347_EPIN, + (char *)data, + size, + 1000, &tmp); + *length = tmp; + if (!ret) + return true; + else + return false; +} +static bool ch347_close_device(void) +{ + jtag_libusb_close(ch347_handle); + return true; +} +struct ch347_info ch347; +static int ch347_swd_run_queue(void); +/* swd init func */ +static bool ch347swd_init(unsigned char clock_rate) +{ + unsigned char cmd_buf[128] = ""; + unsigned long i = 0; + cmd_buf[i++] = CH347_CMD_SWD_INIT; + cmd_buf[i++] = 8; /* Data length is 6 */ + cmd_buf[i++] = 0; + cmd_buf[i++] = 0x40; + cmd_buf[i++] = 0x42; + cmd_buf[i++] = 0x0f; /* Reserved Bytes */ + cmd_buf[i++] = 0x00; /* Reserved Bytes */ + cmd_buf[i++] = clock_rate; /* JTAG clock speed */ + i += 3; /* Reserved Bytes */ + unsigned long length = i; + if (!ch347_write_data(cmd_buf, &length) || length != i) + return false; + length = 4; + memset(cmd_buf, 0, sizeof(cmd_buf)); + if (!ch347_read_data(cmd_buf, &length) || length != 4) + return false; + return true; +} +/** + * hex_to_string - Hex Conversion String Function + * @param buf Point to a buffer to place the Hex data to be converted + * @param size Pointing to the length unit where data needs to be converted + * + * @return Returns a converted string + */ +static char *hex_to_string(unsigned char *buf, unsigned int size) +{ + unsigned int i; + if (!buf) + return "NULL"; + char *str = calloc(size * 2 + 1, 1); + for (i = 0; i < size; i++) + sprintf(str + 2 * i, "%02x ", buf[i]); + return str; +} +/** + * ch347_write - CH347 Write + * @param out_buffer Point to a buffer to place the data to be written out + * @param out_length Pointing to the length unit, the input is the length to be + * written out, and the return is the actual written length + * + * @return Write success returns 1, failure returns 0 + */ +static int ch347_write(void *out_buffer, unsigned long *out_length) +{ + int ret = -1; + unsigned long length = *out_length, WI; + if (*out_length >= HW_TDO_BUF_SIZE) + length = HW_TDO_BUF_SIZE; + WI = 0; + while (1) { + ret = ch347_write_data((unsigned char *)out_buffer + WI, + &length); + if (!ret) { + *out_length = 0; + return false; + } + LOG_DEBUG_IO("(size=%lu, buf=[%s]) -> %" PRIu32, length, + hex_to_string((unsigned char *)out_buffer, length), + (unsigned int)length); + WI += length; + if (*out_length <= WI) + break; + if ((*out_length - WI) > HW_TDO_BUF_SIZE) + length = HW_TDO_BUF_SIZE; + else + length = *out_length - WI; + } + *out_length = WI; + return true; +} +/** + * ch347_read - CH347 Read + * @param out_buffer Point to a buffer to place the data to be read in + * @param out_length Pointing to the length unit, the input is the length to + * be read, and the return is the actual read length + * + * @return Write success returns 1, failure returns 0 + */ +static int ch347_read(void *out_buffer, unsigned long *out_length) +{ + unsigned long rlength = *out_length, WI; + /* The maximum allowable reading for a single read is 4096B of data. + If it exceeds the allowable reading limit, it will be calculated as + 4096B */ + if (rlength > HW_TDO_BUF_SIZE) + rlength = HW_TDO_BUF_SIZE; + WI = 0; + while (1) { + if (!ch347_read_data((unsigned char *)out_buffer + WI, + &rlength)) { + LOG_ERROR("read data failure."); + return false; + } + WI += rlength; + if (*out_length <= WI) + break; + if ((*out_length - WI) > HW_TDO_BUF_SIZE) + rlength = HW_TDO_BUF_SIZE; + else + rlength = *out_length - WI; + } + LOG_DEBUG_IO("(size=%lu, buf=[%s]) -> %" PRIu32, WI, + hex_to_string((unsigned char *)out_buffer, WI), (unsigned int)WI); + *out_length = WI; + return true; +} +static void ch347_read_scan(unsigned char *buffer_ptr, unsigned int length) +{ + unsigned long read_size = 0; + unsigned long rx_len = 0; + unsigned long index = 0; + unsigned long read_buf_index = 0; + unsigned char *read_buf = NULL; + int data_len = 0, i = 0; /*, this_bits = 0; */ + read_size = length; + rx_len = read_size; + index = 0; + read_buf_index = 0; + read_buf = calloc(sizeof(unsigned char), read_size); + if (!ch347_read(read_buf, &rx_len)) { + LOG_ERROR("ch347_read read data failure."); + return; + } + while (index < read_size) { /* deal with the CH347_CMD_JTAG_BIT_OP_RD or CH347_CMD_JTAG_DATA_SHIFT_RD */ + if (read_buf[index] == CH347_CMD_JTAG_DATA_SHIFT_RD) { + data_len = read_buf[++index] & 0xFF; + data_len += (read_buf[++index] & 0xFF) << 8; + memcpy(buffer_ptr + read_buf_index, &read_buf[index + 1], + data_len); + read_buf_index += data_len; + index += data_len + 1; + } else if (read_buf[index] == CH347_CMD_JTAG_BIT_OP_RD) { + data_len = read_buf[++index] & 0xFF; + data_len += (read_buf[++index] & 0xFF) << 8; + for (i = 0; i < data_len; i++) { + if (read_buf[index + 1 + i] == 0x01) + *(buffer_ptr + read_buf_index) |= (1 << i); + else + *(buffer_ptr + read_buf_index) &= ~(1 << i); + } + read_buf_index += 1; + index += data_len + 1; + } else { + // LOG_ERROR("readbuf read_commend error"); + *(buffer_ptr + read_buf_index) = read_buf[index]; + read_buf_index++; + index++; + } + } + if (read_buf) { + free(read_buf); + read_buf = NULL; + } +} +static void ch347_flush_buffer(void) +{ + unsigned long retlen = ch347.buffer_idx; + int nb = ch347.buffer_idx, ret = ERROR_OK; + while (ret == ERROR_OK && nb > 0) { + ret = ch347_write(ch347.buffer, &retlen); + nb -= retlen; + } + memset(&ch347.buffer, 0, sizeof(ch347.buffer)); + ch347.buffer_idx = 0; + ch347.last_cmd = 0; + ch347.len_idx = 0; + ch347.len_value = 0; + if (ch347.read_count == 0) + return; + if (ch347.pack_size == LARGER_PACK) { + ch347_read_scan(&ch347.read_buffer[0], ch347.read_count); + bit_copy_execute(&ch347.read_queue); + memset(ch347.read_buffer, 0, SF_PACKET_BUF_SIZE); + ch347.read_count = 0; + ch347.read_idx = 0; + } +} +static void ch347_in_buffer(unsigned char byte) +{ + if ((SF_PACKET_BUF_SIZE - ch347.buffer_idx) < 1) + ch347_flush_buffer(); + ch347.buffer[ch347.buffer_idx] = byte; + ch347.buffer_idx++; + if ((SF_PACKET_BUF_SIZE - ch347.buffer_idx) == 0) + ch347_flush_buffer(); +} +static void ch347_in_buffer_bytes(unsigned char *bytes, unsigned long bytes_length) +{ + if ((ch347.buffer_idx + bytes_length) > SF_PACKET_BUF_SIZE) + ch347_flush_buffer(); + memcpy(&ch347.buffer[ch347.buffer_idx], bytes, bytes_length); + ch347.buffer_idx += bytes_length; + if ((SF_PACKET_BUF_SIZE - ch347.buffer_idx) < 1) + ch347_flush_buffer(); +} +static void combine_packets(unsigned char cmd, int cur_idx, unsigned long len) +{ + if (cmd != ch347.last_cmd) { + ch347.buffer[cur_idx] = cmd; + ch347.buffer[cur_idx + 1] = + (unsigned char)(((len - CH347_CMD_HEADER) >> 0) & 0xFF); + ch347.buffer[cur_idx + 2] = + (unsigned char)(((len - CH347_CMD_HEADER) >> 8) & 0xFF); + /* update the ch347 struct */ + ch347.last_cmd = cmd; + ch347.len_idx = cur_idx + 1; + ch347.len_value = (len - CH347_CMD_HEADER); + } else { + /* update the ch347 struct cmd data leng */ + ch347.len_value += (len - CH347_CMD_HEADER); + /* update the cmd packet valid leng */ + ch347.buffer[ch347.len_idx] = (unsigned char)((ch347.len_value >> 0) & 0xFF); + ch347.buffer[ch347.len_idx + 1] = (unsigned char)((ch347.len_value >> 8) & 0xFF); + /* update the buffer data leng */ + memcpy(&ch347.buffer[cur_idx], + &ch347.buffer[cur_idx + CH347_CMD_HEADER], + (len - CH347_CMD_HEADER)); + /* update the ch347 buffer index */ + ch347.buffer_idx -= CH347_CMD_HEADER; + } +} +/** + * ch347_clock_tims - Function used to change the TMS value at the + * rising edge of TCK to switch its Tap state + * @param bit_bangPkt Protocol package + * @param tms TMS value to be changed + * @param BI Protocol packet length + * + * @return Return protocol packet length + */ +static unsigned long ch347_clock_tims(int tms, unsigned long BI) +{ + unsigned char data = 0; + unsigned char cmd = 0; + if (tms == 1) + cmd = TMS_H; + else + cmd = TMS_L; + BI += 2; + data = cmd | TDI_L | TCK_L | TRST_H; + ch347_in_buffer(data); + data = cmd | TDI_L | TCK_H | TRST_H; + ch347_in_buffer(data); + ch347.TMS = cmd; + ch347.TDI = TDI_L; + ch347.TCK = TCK_H; + ch347.TRST = TRST_H; + return BI; +} +/** + * ch347_idle_clock - Function to ensure that the clock is in a low state + * @param bit_bangPkt Protocol package + * @param BI Protocol packet length + * + * @return Return protocol packet length + */ +static unsigned long ch347_idle_clock(unsigned long BI) +{ + unsigned char byte = 0; + byte |= ch347.TMS ? TMS_H : TMS_L; + byte |= ch347.TDI ? TDI_H : TDI_L; + byte |= ch347.TRST ? TRST_H : TRST_L; + BI++; + ch347_in_buffer(byte); + return BI; +} +/** + * ch347_tms_change - Function that performs state switching by changing the value of TMS + * @param tms_value The TMS values that need to be switched form one byte of data in the switching order + * @param step The number of bit values that need to be read from the tms_value value + * @param skip Count from the skip bit of tms_value to step + * + */ +static void ch347_tms_change(const unsigned char *tms_value, int step, int skip) +{ + int i; + int index = ch347.buffer_idx; + unsigned long BI, retlen, cmdlen; + BI = CH347_CMD_HEADER; + retlen = CH347_CMD_HEADER; + LOG_DEBUG_IO("(TMS Value: %02x..., step = %d, skip = %d)", tms_value[0], + step, skip); + for (i = 0; i < 3; i++) + ch347_in_buffer(0); + for (i = skip; i < step; i++) { + retlen = ch347_clock_tims((tms_value[i / 8] >> (i % 8)) & 0x01, BI); + BI = retlen; + } + cmdlen = ch347_idle_clock(BI); + combine_packets(CH347_CMD_JTAG_BIT_OP, index, cmdlen); +} +/** + * ch347_tms - By ch347_ execute_ Queue call + * @param cmd Upper layer transfer command parameters + * + */ +static void ch347_tms(struct tms_command *cmd) +{ + LOG_DEBUG_IO("(step: %d)", cmd->num_bits); + ch347_tms_change(cmd->bits, cmd->num_bits, 0); +} +/** + * CH347_Reset - CH347 Reset Tap Status Function + * @brief If there are more than six consecutive TCKs and TMS is high, the state + * machine can be set to a Test-Logic-Reset state + * + */ +static int ch347_reset(int trst, int srst) +{ + LOG_DEBUG_IO("reset trst: %i srst %i", trst, srst); + unsigned char bit_bang[512] = "", BII, i; + unsigned long tx_len; + if (!swd_mode) { + BII = CH347_CMD_HEADER; + for (i = 0; i < 7; i++) { + bit_bang[BII++] = TMS_H | TDI_L | TCK_L; + bit_bang[BII++] = TMS_H | TDI_L | TCK_H; + } + bit_bang[BII++] = TMS_H | TDI_L | TCK_L; + ch347.TCK = TCK_L; + ch347.TDI = TDI_L; + ch347.TMS = 0; + bit_bang[0] = CH347_CMD_JTAG_BIT_OP; + bit_bang[1] = BII - CH347_CMD_HEADER; + bit_bang[2] = 0; + tx_len = BII; + if (!ch347_write(bit_bang, &tx_len) && tx_len != BII) { + LOG_ERROR("JTAG_Init send usb data failure."); + return false; + } + } + return ERROR_OK; +} +/** + * ch347_movepath - Obtain the current Tap status and switch to the status TMS + * value passed down by cmd + * @param cmd Upper layer transfer command parameters + * + */ +static void ch347_movepath(struct pathmove_command *cmd) +{ + int i; + int index = ch347.buffer_idx; + unsigned long BI, retlen = 0, cmdlen; + BI = CH347_CMD_HEADER; + for (i = 0; i < 3; i++) + ch347_in_buffer(0); + LOG_DEBUG_IO("(num_states=%d, last_state=%d)", + cmd->num_states, cmd->path[cmd->num_states - 1]); + for (i = 0; i < cmd->num_states; i++) { + if (tap_state_transition(tap_get_state(), false) == + cmd->path[i]) + retlen = ch347_clock_tims(0, BI); + BI = retlen; + if (tap_state_transition(tap_get_state(), true) == cmd->path[i]) + retlen = ch347_clock_tims(1, BI); + BI = retlen; + tap_set_state(cmd->path[i]); + } + cmdlen = ch347_idle_clock(BI); + combine_packets(CH347_CMD_JTAG_BIT_OP, index, cmdlen); +} +/** + * ch347_movestate - Toggle the Tap state to the Target state stat + * @param stat Pre switch target path + * @param skip Number of digits to skip + * + */ +static void ch347_movestate(tap_state_t state, int skip) +{ + unsigned char tms_scan; + int tms_len; + LOG_DEBUG_IO("(from %s to %s)", tap_state_name(tap_get_state()), + tap_state_name(state)); + if (tap_get_state() == state) + return; + tms_scan = tap_get_tms_path(tap_get_state(), state); + tms_len = tap_get_tms_path_len(tap_get_state(), state); + ch347_tms_change(&tms_scan, tms_len, skip); + tap_set_state(state); +} +/** + * ch347_writeread - CH347 Batch read/write function + * @param bits Read and write data this time + * @param nb_bits Incoming data length + * @param scan The transmission method of incoming data to determine whether + * to perform data reading + */ +static void ch347_writeread(struct scan_command *cmd, unsigned char *bits, + int nb_bits, enum scan_type scan) +{ + int nb8 = nb_bits / 8; + int nb1 = nb_bits % 8; + int i, num_bits = 0; + bool is_read = false; /*, isLastByte = false */ + unsigned char tms_bit = 0, tdi_bit = 0, cmd_bit; + static unsigned char byte0[SF_PACKET_BUF_SIZE]; + unsigned char *read_data = calloc(SF_PACKET_BUF_SIZE, 1); + unsigned long read_len = 0; + unsigned long BI = 0, DI, DII, pkt_data_len, data_len = 0, temp_index, + total_read_length = 0, temp_length = 0; + if (ch347.pack_size == LARGER_PACK) { + if ((ch347.read_count >= (510 * 1))) + ch347_flush_buffer(); + } else { + ch347_flush_buffer(); + } + if (nb8 > 0 && nb1 == 0) { + nb8--; + nb1 = 8; + } + is_read = (scan == SCAN_IN || scan == SCAN_IO); + DI = 0; + BI = 0; + while (DI < (unsigned long)nb8) { + if ((nb8 - DI) > UCMDPKT_DATA_MAX_BYTES_USBHS) + pkt_data_len = UCMDPKT_DATA_MAX_BYTES_USBHS; + else + pkt_data_len = nb8 - DI; + DII = pkt_data_len; + if (is_read) + ch347_in_buffer(CH347_CMD_JTAG_DATA_SHIFT_RD); + else + ch347_in_buffer(CH347_CMD_JTAG_DATA_SHIFT); + /* packet data don't deal D3 & D4 */ + if (ch347.last_cmd != CH347_CMD_JTAG_DATA_SHIFT_RD || + ch347.last_cmd != CH347_CMD_JTAG_DATA_SHIFT) { + /* update the ch347 struct */ + ch347.last_cmd = 0; + ch347.len_idx = 0; + ch347.len_value = 0; + } + ch347_in_buffer((unsigned char)(pkt_data_len >> 0) & 0xFF); + ch347_in_buffer((unsigned char)(pkt_data_len >> 8) & 0xFF); + if (bits) + ch347_in_buffer_bytes(&bits[DI], pkt_data_len); + else + ch347_in_buffer_bytes(byte0, pkt_data_len); + DI += DII; + temp_length += (DII + CH347_CMD_HEADER); + } + total_read_length += temp_length; + if (is_read) { + ch347.read_count += temp_length; + read_len += temp_length; + } + if (bits) { + cmd_bit = is_read ? CH347_CMD_JTAG_BIT_OP_RD : CH347_CMD_JTAG_BIT_OP; + data_len = (nb1 * 2) + 1; + if (cmd_bit != ch347.last_cmd) { + ch347_in_buffer(cmd_bit); + ch347_in_buffer((unsigned char)(data_len >> 0) & 0xFF); + ch347_in_buffer((unsigned char)(data_len >> 8) & 0xFF); + ch347.last_cmd = cmd_bit; + ch347.len_idx = ch347.buffer_idx - 2; + ch347.len_value = data_len; + } else { + /* update the ch347 struct cmd data leng */ + ch347.len_value += data_len; + /* update the cmd packet valid leng */ + ch347.buffer[ch347.len_idx] = + (unsigned char)(ch347.len_value >> 0) & 0xFF; + ch347.buffer[ch347.len_idx + 1] = + (unsigned char)(ch347.len_value >> 8) & 0xFF; + } + tms_bit = TMS_L; + for (i = 0; i < nb1; i++) { + if ((bits[nb8] >> i) & 0x01) + tdi_bit = TDI_H; + else + tdi_bit = TDI_L; + if ((i + 1) == nb1) + tms_bit = TMS_H; + ch347_in_buffer(tms_bit | tdi_bit | TCK_L | TRST_H); + ch347_in_buffer(tms_bit | tdi_bit | TCK_H | TRST_H); + } + ch347_in_buffer(tms_bit | tdi_bit | TCK_L | TRST_H); + } + ch347.TMS = tms_bit; + ch347.TDI = tdi_bit; + ch347.TCK = TCK_L; + if (is_read) { + temp_length = ((data_len / 2) + CH347_CMD_HEADER); + total_read_length += temp_length; + ch347.read_count += temp_length; + read_len += temp_length; + DI = 0; + BI = 0; + } + int offset = 0, bit_count = 0; + if (is_read && total_read_length > 0) { + if (ch347.pack_size == STANDARD_PACK && bits && cmd) { + ch347_flush_buffer(); + ch347_read_scan(read_data, read_len); + } + for (i = 0; i < cmd->num_fields; i++) { + /* if neither in_value nor in_handler + * are specified we don't have to examine this field + */ + LOG_DEBUG("fields[%i].in_value[%i], offset: %d", + i, cmd->fields[i].num_bits, offset); + num_bits = cmd->fields[i].num_bits; + if (ch347.pack_size == LARGER_PACK) + bit_count += num_bits; + if (cmd->fields[i].in_value) { + if (ch347.pack_size == LARGER_PACK) { + bit_copy_queued(&ch347.read_queue, + cmd->fields[i].in_value, + 0, + &ch347.read_buffer[ch347.read_idx], + offset, num_bits); + if (num_bits > 7) + ch347.read_idx += DIV_ROUND_UP(bit_count, 8); + } else { + num_bits = cmd->fields[i].num_bits; + unsigned char *captured = buf_set_buf(read_data, bit_count, + malloc(DIV_ROUND_UP(num_bits, + 8)), 0, + num_bits); + if (LOG_LEVEL_IS(LOG_LVL_DEBUG_IO)) { + char *char_buf = + buf_to_hex_str(captured, + (num_bits > + DEBUG_JTAG_IOZ) + ? DEBUG_JTAG_IOZ + : num_bits); + free(char_buf); + } + buf_cpy(captured, + cmd->fields[i].in_value, + num_bits); + free(captured); + } + } else { + LOG_DEBUG_IO("cmd->fields with no data"); + } + if (ch347.pack_size == LARGER_PACK) + offset += num_bits; + else + bit_count += cmd->fields[i].num_bits; + } + } + temp_index = ch347.buffer_idx; + for (i = 0; i < CH347_CMD_HEADER; i++) + ch347_in_buffer(0); + BI = CH347_CMD_HEADER; + BI = ch347_idle_clock(BI); + combine_packets(CH347_CMD_JTAG_BIT_OP, temp_index, BI); + if (read_data) { + free(read_data); + read_data = NULL; + } +} +static void ch347_runtest(int cycles, tap_state_t state) +{ + LOG_DEBUG_IO("%s(cycles=%i, end_state=%d)", __func__, cycles, state); + if (tap_get_state() != TAP_IDLE) + ch347_movestate(TAP_IDLE, 0); + unsigned char tms_value = 0; + ch347_tms_change(&tms_value, 7, 1); + ch347_writeread(NULL, NULL, cycles, SCAN_OUT); + ch347_movestate(state, 0); +} +static void ch347_tableclocks(int cycles) +{ + LOG_DEBUG_IO("%s(cycles=%i)", __func__, cycles); + ch347_writeread(NULL, NULL, cycles, SCAN_OUT); +} +/** + * ch347_scan - Switch to SHIFT-DR or SHIFT-IR status for scanning + * @param cmd Upper layer transfer command parameters + * + * @return Success returns ERROR_OK + */ +static int ch347_scan(struct scan_command *cmd) +{ + int scan_bits; + unsigned char *buf = NULL; + enum scan_type type; + int ret = ERROR_OK; + static const char *const type2str[] = { + "", "SCAN_IN", "SCAN_OUT", "SCAN_IO" + }; + char *log_buf = NULL; + type = jtag_scan_type(cmd); + scan_bits = jtag_build_buffer(cmd, &buf); + if (cmd->ir_scan) + ch347_movestate(TAP_IRSHIFT, 0); + else + ch347_movestate(TAP_DRSHIFT, 0); + log_buf = hex_to_string(buf, DIV_ROUND_UP(scan_bits, 8)); + LOG_DEBUG_IO("Scan"); + LOG_DEBUG_IO("%s(scan=%s, type=%s, bits=%d, buf=[%s], end_state=%d)", + __func__, + cmd->ir_scan ? "IRSCAN" : "DRSCAN", + type2str[type], + scan_bits, log_buf, cmd->end_state); + free(log_buf); + ch347_writeread(cmd, buf, scan_bits, type); + free(buf); + ch347_movestate(cmd->end_state, 1); + return ret; +} +static void ch347_sleep(int us) +{ + LOG_DEBUG_IO("%s(us=%d)", __func__, us); + jtag_sleep(us); +} +static int ch347_execute_queue(struct jtag_command *cmd) +{ + int ret = ERROR_OK; + for (cmd = jtag_command_queue_get(); ret == ERROR_OK && cmd; + cmd = cmd->next) { + switch (cmd->type) { + case JTAG_RESET: + LOG_DEBUG_IO("JTAG_RESET : %d %d.\n", + cmd->cmd.reset->trst, + cmd->cmd.reset->srst); + ch347_reset(cmd->cmd.reset->trst, cmd->cmd.reset->srst); + break; + case JTAG_RUNTEST: + ch347_runtest(cmd->cmd.runtest->num_cycles, + cmd->cmd.runtest->end_state); + break; + case JTAG_STABLECLOCKS: + ch347_tableclocks(cmd->cmd.stableclocks->num_cycles); + break; + case JTAG_TLR_RESET: + ch347_movestate(cmd->cmd.statemove->end_state, 0); + break; + case JTAG_PATHMOVE: + ch347_movepath(cmd->cmd.pathmove); + break; + case JTAG_TMS: + ch347_tms(cmd->cmd.tms); + break; + case JTAG_SLEEP: + ch347_sleep(cmd->cmd.sleep->us); + break; + case JTAG_SCAN: + ret = ch347_scan(cmd->cmd.scan); + break; + default: + LOG_ERROR("BUG: unknown JTAG command type 0x%X", + cmd->type); + ret = ERROR_FAIL; + break; + } + } + ch347_flush_buffer(); + return ret; +} +/** + * ch347_init - CH347 Initialization function + * + * Todo: + * Initialize dynamic library functions + * Open Device + * @return Success returns 0, failure returns ERROR_FAIL + */ +static int ch347_init(void) +{ + int retval; + dev_is_opened = ch347_open_device(); + if (dev_is_opened) { + LOG_INFO("CH347 Open Succ."); + } else { + LOG_ERROR("CH347 Open Error."); + return ERROR_FAIL; + } + if (ch347_pids[0] == DEFAULT_PRODUCT_ID) + retval = libusb_claim_interface(ch347_handle, CH347T_MPHSI_INTERFACE); + else + retval = libusb_claim_interface(ch347_handle, CH347F_MPHSI_INTERFACE); + if (retval != ERROR_OK) { + LOG_ERROR("CH347 unable to claim interface: %s", libusb_error_name(retval)); + jtag_libusb_close(ch347_handle); + return retval; + } + if (!swd_mode) { + USBC_PACKET = USBC_PACKET_USBHS; + /* ch347 init */ + ch347.TCK = TCK_L; + ch347.TMS = TMS_H; + ch347.TDI = TDI_L; + ch347.TRST = TRST_H; + ch347.buffer_idx = 0; + memset(&ch347.buffer, 0, SF_PACKET_BUF_SIZE); + ch347.len_idx = 0; + ch347.len_value = 0; + ch347.last_cmd = 0; + memset(&ch347.read_buffer, 0, SF_PACKET_BUF_SIZE); + ch347.read_count = 0; + ch347.read_idx = 0; + bit_copy_queue_init(&ch347.read_queue); + tap_set_state(TAP_RESET); + } else { /* swd init */ + ch347swd_init(1); + } + return 0; +} +/** + * ch347_quit - CH347 Device Release Function + * + * Todo: + * Reset JTAG pin signal + * Close + * @return always returns 0 + */ +static int ch347_quit(void) +{ + unsigned long retlen = 4; + unsigned char byte[4] = {CH347_CMD_JTAG_BIT_OP, 0x01, 0x00, ch347.TRST}; + if (!swd_mode) { + ch347_write(byte, &retlen); + bit_copy_discard(&ch347.read_queue); + } + if (dev_is_opened) { + ch347_close_device(); + LOG_INFO("Close the CH347."); + dev_is_opened = false; + } + return 0; +} +static bool check_speed(unsigned char clock_rate) +{ + unsigned long i = 0, j; + bool ret; + unsigned char cmd_buf[32] = ""; + cmd_buf[i++] = CH347_CMD_JTAG_INIT; + cmd_buf[i++] = 6; + cmd_buf[i++] = 0; + cmd_buf[i++] = 0; + cmd_buf[i++] = clock_rate; + for (j = 0; j < 4; j++) + cmd_buf[i++] = ch347.TCK | ch347.TDI | ch347.TMS | ch347.TRST; + unsigned long length = i; + if (!ch347_write_data(cmd_buf, &length) || length != i) + return false; + length = 4; + memset(cmd_buf, 0, sizeof(cmd_buf)); + if (!ch347_read_data(cmd_buf, &length) || length != 4) { + LOG_ERROR("LINE == %d FALSE", __LINE__); + return false; + } + ret = ((cmd_buf[0] == CH347_CMD_JTAG_INIT) && (cmd_buf[CH347_CMD_HEADER] == 0)); + return ret; +} +static bool ch347_jtag_init(unsigned char clock_rate) +{ + ch347.pack_size = (check_speed(0x09) == true) ? STANDARD_PACK : LARGER_PACK; + if (ch347.pack_size == STANDARD_PACK) { + if (clock_rate - 2 < 0) + return check_speed(0); + else + return check_speed(clock_rate - 2); + } + return check_speed(clock_rate); +} +/** + * ch347_speed - CH347 TCK frequency setting + * @param speed Frequency size set + * @return Success returns ERROR_OK,failed returns FALSE + */ +static int ch347_speed(int speed) +{ + unsigned long i = 0; + unsigned char clock_rate; + int ret = -1; + int speed_clock[8] = { + KHZ(468.75), KHZ(937.5), MHZ(1.875), + MHZ(3.75), MHZ(7.5), MHZ(15), MHZ(30), MHZ(60) + }; + if (!swd_mode) { + for (i = 0; i < ARRAY_SIZE(speed_clock); i++) { + if (speed >= speed_clock[i] && + speed <= speed_clock[i + 1]) { + clock_rate = i + 1; + ret = ch347_jtag_init(clock_rate); + if (!ret) + LOG_ERROR("Couldn't set CH347 TCK speed"); + return ERROR_OK; + } else if (speed < speed_clock[0]) { + ret = ch347_jtag_init(0); + if (!ret) { + LOG_ERROR("Couldn't set CH347 TCK speed"); + return ret; + } else { + return ERROR_OK; + } + } + } + } + return ERROR_OK; +} +static int ch347_speed_div(int speed, int *khz) +{ + *khz = speed / 1000; + return ERROR_OK; +} +static int ch347_khz(int khz, int *jtag_speed) +{ + if (khz == 0) { + LOG_ERROR("Couldn't support the adapter speed"); + return ERROR_FAIL; + } + *jtag_speed = khz * 1000; + return ERROR_OK; +} +static int ch347_trst_out(unsigned char status) +{ + unsigned long BI = 0; + unsigned char byte = 0; + unsigned char cmd_packet[4] = ""; + cmd_packet[BI++] = CH347_CMD_JTAG_BIT_OP; + cmd_packet[BI++] = 0x01; + cmd_packet[BI++] = 0; + byte = ch347.TCK | ch347.TDI | ch347.TMS | (ch347.TRST = + (status ? TRST_H : TRST_L)); + cmd_packet[BI++] = byte; + if (!ch347_write(cmd_packet, &BI)) { + LOG_ERROR("TRST set failure."); + return ERROR_FAIL; + } + return ERROR_OK; +} +COMMAND_HANDLER(ch347_handle_vid_pid_command) +{ + if (CMD_ARGC != 2) + return ERROR_COMMAND_SYNTAX_ERROR; + COMMAND_PARSE_NUMBER(u16, CMD_ARGV[0], ch347_vids[0]); + COMMAND_PARSE_NUMBER(u16, CMD_ARGV[1], ch347_pids[0]); + return ERROR_OK; +} +COMMAND_HANDLER(ch347_trst) +{ + ch347_trst_out(TRST_L); + jtag_sleep(atoi(CMD_ARGV[0]) * 1000); + ch347_trst_out(TRST_H); + return ERROR_OK; +} +static const struct command_registration ch347_subcommand_handlers[] = { + { + .name = "vid_pid", + .handler = &ch347_handle_vid_pid_command, + .mode = COMMAND_CONFIG, + .help = "", + .usage = "", + }, + { + .name = "jtag_ntrst_delay", + .handler = &ch347_trst, + .mode = COMMAND_ANY, + .help = "set the trst of the CH347 device that is used as JTAG", + .usage = "[milliseconds]", + }, + COMMAND_REGISTRATION_DONE}; +static const struct command_registration ch347_command_handlers[] = { + { + .name = "ch347", + .mode = COMMAND_ANY, + .help = "perform ch347 management", + .chain = ch347_subcommand_handlers, + .usage = "", + }, + COMMAND_REGISTRATION_DONE}; +static int ch347_swd_init(void) +{ + PCH347_SWD_IO pswd_io; + LOG_INFO("CH347 SWD mode enabled"); + swd_mode = true; + memset(&ch347_swd_context, 0, sizeof(ch347_swd_context)); + INIT_LIST_HEAD(&ch347_swd_context.send_cmd_head); + INIT_LIST_HEAD(&ch347_swd_context.free_cmd_head); + ch347_swd_context.queued_retval = ERROR_OK; + /* 0XE8 + 2byte len + N byte cmds */ + ch347_swd_context.send_len = CH347_CMD_HEADER; + /* 0XE8 + 2byte len + N byte ack + data */ + ch347_swd_context.need_recv_len = CH347_CMD_HEADER; + ch347_swd_context.ch347_cmd_buf = malloc(128 * sizeof(CH347_SWD_IO)); + if (ch347_swd_context.ch347_cmd_buf) { + pswd_io = (PCH347_SWD_IO)ch347_swd_context.ch347_cmd_buf; + for (int i = 0; i < 128; i++, pswd_io++) { + INIT_LIST_HEAD(&pswd_io->list_entry); + list_add_tail(&pswd_io->list_entry, + &ch347_swd_context.free_cmd_head); + } + } + return ch347_swd_context.ch347_cmd_buf ? ERROR_OK : ERROR_FAIL; +} +static PCH347_SWD_IO ch347_get_one_swd_io(void) +{ + PCH347_SWD_IO pswd_io; + if (list_empty(&ch347_swd_context.free_cmd_head)) + return NULL; + pswd_io = list_first_entry(&ch347_swd_context.free_cmd_head, CH347_SWD_IO, list_entry); + list_del_init(&pswd_io->list_entry); + pswd_io->cmd = 0; + pswd_io->usbcmd = CH347_CMD_SWD_SEQ_W; + pswd_io->dst = NULL; + return pswd_io; +} +static void ch347_swd_queue_flush(void) +{ + unsigned long length = ch347_swd_context.send_len; + ch347_swd_context.send_buf[0] = (unsigned char)CH347_CMD_SWD; + ch347_swd_context.send_buf[1] = (unsigned char)(ch347_swd_context.send_len - + CH347_CMD_HEADER); + ch347_swd_context.send_buf[2] = (unsigned char)((ch347_swd_context.send_len - + CH347_CMD_HEADER) >> 8); + if (!ch347_write_data(ch347_swd_context.send_buf, &length) || + length != ch347_swd_context.send_len) { + ch347_swd_context.queued_retval = ERROR_FAIL; + LOG_DEBUG("ch347_write_data error "); + return; + } + ch347_swd_context.recv_len = 0; + do { + length = CH347_MAX_RECV_BUF - ch347_swd_context.recv_len; + if (!ch347_read_data(&ch347_swd_context.recv_buf[ch347_swd_context.recv_len], + &length)) { + ch347_swd_context.queued_retval = ERROR_FAIL; + LOG_DEBUG("ch347_read_data error "); + return; + } + ch347_swd_context.recv_len += length; + } while (ch347_swd_context.recv_len < ch347_swd_context.need_recv_len); + if (ch347_swd_context.need_recv_len > ch347_swd_context.recv_len) { + LOG_ERROR("%d : write/read failed %d %d", + __LINE__, + ch347_swd_context.recv_len, + ch347_swd_context.need_recv_len); + } +} +static void ch347_wrtie_swd_reg(unsigned char cmd, const unsigned char *out, unsigned char parity) +{ + ch347_swd_context.send_buf[ch347_swd_context.send_len++] = + CH347_CMD_SWD_REG_W; + /* 8bit + 32bit +1bit */ + ch347_swd_context.send_buf[ch347_swd_context.send_len++] = 0x29; + ch347_swd_context.send_buf[ch347_swd_context.send_len++] = 0x00; + ch347_swd_context.send_buf[ch347_swd_context.send_len++] = cmd; + ch347_swd_context.send_buf[ch347_swd_context.send_len++] = out[0]; + ch347_swd_context.send_buf[ch347_swd_context.send_len++] = out[1]; + ch347_swd_context.send_buf[ch347_swd_context.send_len++] = out[2]; + ch347_swd_context.send_buf[ch347_swd_context.send_len++] = out[3]; + ch347_swd_context.send_buf[ch347_swd_context.send_len++] = parity; + /* 0xA0 + 1 byte(3bit ACK) */ + ch347_swd_context.need_recv_len += (1 + 1); +} +static void ch347_wrtie_spec_seq(const unsigned char *out, unsigned char out_len) +{ + ch347_swd_context.send_buf[ch347_swd_context.send_len++] = + CH347_CMD_SWD_SEQ_W; + ch347_swd_context.send_buf[ch347_swd_context.send_len++] = out_len; + ch347_swd_context.send_buf[ch347_swd_context.send_len++] = 0x00; + for (unsigned char i = 0; i < DIV_ROUND_UP(out_len, 8); i++) { + if (out) + ch347_swd_context.send_buf[ch347_swd_context.send_len++] = out[i]; + else + ch347_swd_context.send_buf[ch347_swd_context.send_len++] = 0x00; + } + ch347_swd_context.need_recv_len += 1; /* 0xA1 */ +} +static void ch347_read_swd_reg(unsigned char cmd) +{ + ch347_swd_context.send_buf[ch347_swd_context.send_len++] = + CH347_CMD_SWD_REG_R; + ch347_swd_context.send_buf[ch347_swd_context.send_len++] = 0x22; + ch347_swd_context.send_buf[ch347_swd_context.send_len++] = 0x00; + ch347_swd_context.send_buf[ch347_swd_context.send_len++] = cmd; + /* 0xA2 + 1 byte(3bit ACK) + 4 byte(data) + + 1 byte(1bit parity+1bit trn) */ + ch347_swd_context.need_recv_len += 1 + 1 + 4 + 1; +} +static int ch347_swd_switch_out(enum swd_special_seq seq, const unsigned char *out, + unsigned int out_len) +{ + PCH347_SWD_IO pswd_io; + if ((ch347_swd_context.send_len + + (1 + 2 + DIV_ROUND_UP(out_len, 8))) > CH347_MAX_SEND_BUF) { + return ERROR_FAIL; + } + if ((ch347_swd_context.need_recv_len + 2) > CH347_MAX_RECV_BUF) + return ERROR_FAIL; + pswd_io = ch347_get_one_swd_io(); + if (pswd_io) { + ch347_wrtie_spec_seq(out, out_len); + list_add_tail(&pswd_io->list_entry, + &ch347_swd_context.send_cmd_head); + return ERROR_OK; + } else { + return ERROR_FAIL; + } +} +/* check read/write REG can fill in remaining buff */ +static bool ch347_chk_buf_size(unsigned char cmd, unsigned int ap_delay_clk) +{ + bool bflush; + unsigned int send_len, recv_len, len; + bflush = false; + send_len = ch347_swd_context.send_len; + recv_len = ch347_swd_context.need_recv_len; + do { + if (cmd & SWD_CMD_RNW) { + len = 1 + 1 + 1 + 1; /* 0xA2 + len + rev + cmd */ + if (send_len + len > CH347_MAX_SEND_BUF) + break; + send_len += len; + len = 1 + 1 + 4 + 1; + /* 0xA2 + 1byte(3bit ack) + 4byte(data) + + 1byte(1bit parity+1bit trn) */ + if (recv_len + len > CH347_MAX_RECV_BUF) + break; + recv_len += len; + } else { /* write reg */ + len = 1 + 1 + 1 + 1 + 4 + 1; + /* 0xA0 + len + rev + cmd +data + parity */ + if (send_len + len > CH347_MAX_SEND_BUF) + break; + send_len += len; + len = 1 + 1; /* 0xA0 + 1byte(3bit ack) */ + if (recv_len + len > CH347_MAX_RECV_BUF) + break; + recv_len += len; + } + if (cmd & SWD_CMD_APNDP) { + len = 1 + 1 + 1 + DIV_ROUND_UP(ap_delay_clk, 8); + /* 0xA1 + Len + rev + n byte(delay) */ + if (send_len + len > CH347_MAX_SEND_BUF) + break; + len = 1; /* 0xA1 */ + if ((recv_len + len) > CH347_MAX_RECV_BUF) + break; + } + /* swd packet requests */ + bflush = true; + } while (false); + return bflush; +} +static void ch347_swd_send_idle(unsigned int ap_delay_clk) +{ + PCH347_SWD_IO pswd_io; + pswd_io = ch347_get_one_swd_io(); + if (!pswd_io) { + ch347_swd_run_queue(); + pswd_io = ch347_get_one_swd_io(); + if (!pswd_io) { + LOG_DEBUG("ch347_swd_queue_cmd error "); + ch347_swd_context.queued_retval = ERROR_FAIL; + return; + } + } + ch347_wrtie_spec_seq(NULL, ap_delay_clk); + list_add_tail(&pswd_io->list_entry, &ch347_swd_context.send_cmd_head); +} +static void ch347_swd_queue_cmd(unsigned char cmd, unsigned int *dst, unsigned int data, + unsigned int ap_delay_clk) +{ + PCH347_SWD_IO pswd_io; + if (ap_delay_clk > 255) + printf("%s ap_delay_clk = %d\r\n", "ch347_swd_queue_cmd", ap_delay_clk); + if (ch347_swd_context.sent_cmd_count >= CH347_MAX_SEND_CMD) + ch347_swd_run_queue(); + if (!ch347_chk_buf_size(cmd, ap_delay_clk)) + ch347_swd_run_queue(); + pswd_io = ch347_get_one_swd_io(); + if (!pswd_io) { + ch347_swd_run_queue(); + pswd_io = ch347_get_one_swd_io(); + if (!pswd_io) { + LOG_DEBUG("%s error ", "ch347_swd_queue_cmd"); + ch347_swd_context.queued_retval = ERROR_FAIL; + return; + } + } + pswd_io->cmd = cmd | SWD_CMD_START | SWD_CMD_PARK; + if (pswd_io->cmd & SWD_CMD_RNW) { + pswd_io->usbcmd = CH347_CMD_SWD_REG_R; + pswd_io->dst = dst; + ch347_read_swd_reg(pswd_io->cmd); + } else { + pswd_io->usbcmd = CH347_CMD_SWD_REG_W; + pswd_io->value = data; + ch347_wrtie_swd_reg(pswd_io->cmd, (unsigned char *)&data, + parity_u32(data)); + } + ch347_swd_context.sent_cmd_count++; + list_add_tail(&pswd_io->list_entry, &ch347_swd_context.send_cmd_head); + /* Insert idle cycles after AP accesses to avoid WAIT */ + if (cmd & SWD_CMD_APNDP) { + if (ap_delay_clk == 0) + printf("ap_delay_clk == 0"); + ch347_swd_send_idle(ap_delay_clk); + } +} +static int ch347_swd_switch_seq(enum swd_special_seq seq) +{ + printf("%s %d \r\n", "ch347_swd_switch_seq", seq); + switch (seq) { + case LINE_RESET: + LOG_DEBUG("SWD line reset"); + return ch347_swd_switch_out(seq, swd_seq_line_reset, + swd_seq_line_reset_len); + case JTAG_TO_SWD: + LOG_DEBUG("JTAG-to-SWD"); + return ch347_swd_switch_out(seq, swd_seq_jtag_to_swd, + swd_seq_jtag_to_swd_len); + case JTAG_TO_DORMANT: + LOG_DEBUG("JTAG-to-DORMANT"); + return ch347_swd_switch_out(seq, swd_seq_jtag_to_dormant, + swd_seq_jtag_to_dormant_len); + case SWD_TO_JTAG: + LOG_DEBUG("SWD-to-JTAG"); + return ch347_swd_switch_out(seq, swd_seq_swd_to_jtag, + swd_seq_swd_to_jtag_len); + case SWD_TO_DORMANT: + LOG_DEBUG("SWD-to-DORMANT"); + return ch347_swd_switch_out(seq, swd_seq_swd_to_dormant, + swd_seq_swd_to_dormant_len); + case DORMANT_TO_SWD: + LOG_DEBUG("DORMANT-to-SWD"); + return ch347_swd_switch_out(seq, swd_seq_dormant_to_swd, + swd_seq_dormant_to_swd_len); + case DORMANT_TO_JTAG: + LOG_DEBUG("DORMANT-to-JTAG"); + return ch347_swd_switch_out(seq, swd_seq_dormant_to_jtag, + swd_seq_dormant_to_jtag_len); + default: + LOG_ERROR("Sequence %d not supported", seq); + return ERROR_FAIL; + } +} +static void ch347_swd_read_reg(unsigned char cmd, unsigned int *value, + unsigned int ap_delay_clk) +{ + assert(cmd & SWD_CMD_RNW); + ch347_swd_queue_cmd(cmd, value, 0, ap_delay_clk); +} +static void ch347_swd_write_reg(unsigned char cmd, unsigned int value, + unsigned int ap_delay_clk) +{ + assert(!(cmd & SWD_CMD_RNW)); + ch347_swd_queue_cmd(cmd, NULL, value, ap_delay_clk); +} +static int ch347_swd_run_queue(void) +{ + LOG_DEBUG_IO("Executing %u queued transactions", + ch347_swd_context.sent_cmd_count); + int retval, parity; + struct list_head *tmp, *pos; + unsigned char *recv_buf; + unsigned int recv_len, cmds_len, data; + PCH347_SWD_IO pswd_io; + if (ch347_swd_context.queued_retval != ERROR_OK) { + LOG_DEBUG_IO("Skipping due to previous errors: %d", + ch347_swd_context.queued_retval); + goto skip; + } + /* A transaction must be followed by another transaction or at least 8 + idle cycles to ensure that data is clocked through the AP. */ + if ((ch347_swd_context.send_len + (1 + 2 + 1)) > CH347_MAX_SEND_BUF) + goto skip_idle; + if ((ch347_swd_context.need_recv_len + 1) > CH347_MAX_RECV_BUF) + goto skip_idle; + ch347_swd_send_idle(8); +skip_idle: + ch347_swd_queue_flush(); + if (ch347_swd_context.queued_retval != ERROR_OK) { + LOG_ERROR("CH347 usb write/read failed %d", __LINE__); + goto skip; + } + recv_buf = ch347_swd_context.recv_buf; + recv_len = 0; + if (recv_buf[recv_len++] != CH347_CMD_SWD) { /* 0XE8 */ + ch347_swd_context.queued_retval = ERROR_FAIL; + LOG_ERROR("CH347 usb write/read failed %d", __LINE__); + goto skip; + } + cmds_len = BUILD_UINT16(recv_buf[recv_len], recv_buf[recv_len + 1]); + recv_len += 2; /* cmds_len */ + if ((cmds_len + CH347_CMD_HEADER) > ch347_swd_context.recv_len) { + ch347_swd_context.queued_retval = ERROR_FAIL; + LOG_ERROR("CH347 usb write/read failed %d", __LINE__); + goto skip; + } + list_for_each_safe(pos, tmp, &ch347_swd_context.send_cmd_head) { + pswd_io = list_entry(pos, CH347_SWD_IO, list_entry); + if (pswd_io->usbcmd == CH347_CMD_SWD_SEQ_W) { + if (recv_buf[recv_len++] != CH347_CMD_SWD_SEQ_W) { + ch347_swd_context.queued_retval = ERROR_FAIL; + LOG_ERROR("CH347 usb write/read failed %d", + __LINE__); + goto skip; + } + } else { /* read/write Reg */ + int ack; + bool check_ack; + /* read Reg */ + if (recv_buf[recv_len] == CH347_CMD_SWD_REG_R) { + recv_len++; + ack = buf_get_u32(&recv_buf[recv_len++], 0, 3); + /* Devices do not reply to DP_TARGETSEL write + cmd, ignore received ack */ + check_ack = swd_cmd_returns_ack(pswd_io->cmd); + if (ack != SWD_ACK_OK && check_ack) { + ch347_swd_context.queued_retval = + swd_ack_to_error_code(ack); + LOG_ERROR("ack != SWD_ACK_OK %d", + __LINE__); + goto skip; + } + if (pswd_io->cmd & SWD_CMD_RNW) { + data = buf_get_u32(&recv_buf[recv_len], + 0, 32); + parity = buf_get_u32(&recv_buf[recv_len], 32, 1); + if (parity != parity_u32(data)) { + LOG_ERROR("SWD Read data parity mismatch %d", + __LINE__); + ch347_swd_context.queued_retval = ERROR_FAIL; + goto skip; + } + if (pswd_io->dst) + *pswd_io->dst = data; + } else { + ch347_swd_context.queued_retval = + ERROR_FAIL; + LOG_ERROR("CH347 usb write/read failed %d", __LINE__); + goto skip; + } + recv_len += 5; + } else if (recv_buf[recv_len] == CH347_CMD_SWD_REG_W) { + recv_len++; + ack = buf_get_u32(&recv_buf[recv_len++], 0, 3); + /* Devices do not reply to DP_TARGETSEL write + cmd, ignore received ack */ + check_ack = swd_cmd_returns_ack(pswd_io->cmd); + if (ack != SWD_ACK_OK && check_ack) { + ch347_swd_context.queued_retval = + swd_ack_to_error_code(ack); + LOG_ERROR("SWD Read data parity mismatch%d", __LINE__); + goto skip; + } + } else { + ch347_swd_context.queued_retval = ERROR_FAIL; + LOG_ERROR("CH347 usb write/read failed %d recv_len = %d", + __LINE__, recv_len); + goto skip; + } + } + list_del_init(&pswd_io->list_entry); + list_add_tail(&pswd_io->list_entry, + &ch347_swd_context.free_cmd_head); + } +skip: + if (!list_empty(&ch347_swd_context.send_cmd_head)) { + list_for_each_safe(pos, tmp, &ch347_swd_context.send_cmd_head) { + pswd_io = list_entry(pos, CH347_SWD_IO, list_entry); + list_del_init(&pswd_io->list_entry); + list_add_tail(&pswd_io->list_entry, + &ch347_swd_context.free_cmd_head); + } + } + /* 0xE8 + 2byte len */ + ch347_swd_context.send_len = CH347_CMD_HEADER; + /* 0xE8 + 2byte len */ + ch347_swd_context.need_recv_len = CH347_CMD_HEADER; + ch347_swd_context.recv_len = 0; + ch347_swd_context.sent_cmd_count = 0; + retval = ch347_swd_context.queued_retval; + ch347_swd_context.queued_retval = ERROR_OK; + return retval; +} +static const struct swd_driver ch347_swd = { + .init = ch347_swd_init, + .switch_seq = ch347_swd_switch_seq, + .read_reg = ch347_swd_read_reg, + .write_reg = ch347_swd_write_reg, + .run = ch347_swd_run_queue, +}; +static const char *const ch347_transports[] = {"jtag", "swd", NULL}; +static struct jtag_interface ch347_interface = { + .supported = DEBUG_CAP_TMS_SEQ, + .execute_queue = ch347_execute_queue, +}; +struct adapter_driver ch347_adapter_driver = { + .name = "ch347", + .transports = ch347_transports, + .commands = ch347_command_handlers, + .init = ch347_init, + .quit = ch347_quit, + .reset = ch347_reset, + .speed = ch347_speed, + .khz = ch347_khz, + .speed_div = ch347_speed_div, + .jtag_ops = &ch347_interface, + .swd_ops = &ch347_swd, +}; diff --git a/src/jtag/interface.h b/src/jtag/interface.h index b448851dc4..ba5c5eb2db 100644 --- a/src/jtag/interface.h +++ b/src/jtag/interface.h @@ -378,6 +378,7 @@ extern struct adapter_driver ep93xx_adapter_driver; extern struct adapter_driver esp_usb_adapter_driver; extern struct adapter_driver ft232r_adapter_driver; extern struct adapter_driver ftdi_adapter_driver; +extern struct adapter_driver ch347_adapter_driver; extern struct adapter_driver gw16012_adapter_driver; extern struct adapter_driver hl_adapter_driver; extern struct adapter_driver imx_gpio_adapter_driver; diff --git a/src/jtag/interfaces.c b/src/jtag/interfaces.c index 67f0838e39..e0cfd660cf 100644 --- a/src/jtag/interfaces.c +++ b/src/jtag/interfaces.c @@ -152,6 +152,9 @@ struct adapter_driver *adapter_drivers[] = { #endif #if BUILD_AM335XGPIO == 1 &am335xgpio_adapter_driver, +#endif +#if BUILD_CH347 == 1 + &ch347_adapter_driver, #endif NULL, }; diff --git a/src/target/xtensa/xtensa.c b/src/target/xtensa/xtensa.c index 284bfc9c62..fe1b83bc3e 100644 --- a/src/target/xtensa/xtensa.c +++ b/src/target/xtensa/xtensa.c @@ -1727,7 +1727,7 @@ int xtensa_do_step(struct target *target, int current, target_addr_t address, in xtensa_reg_val_t dbreakc[XT_WATCHPOINTS_NUM_MAX]; xtensa_reg_val_t icountlvl, cause; xtensa_reg_val_t oldps, oldpc, cur_pc; - bool ps_lowered = false; + bool ps_modified = false; LOG_TARGET_DEBUG(target, "current=%d, address=" TARGET_ADDR_FMT ", handle_breakpoints=%i", current, address, handle_breakpoints); @@ -1783,14 +1783,23 @@ int xtensa_do_step(struct target *target, int current, target_addr_t address, in * RFI >= DBGLEVEL. */ if (xtensa->stepping_isr_mode == XT_STEPPING_ISR_OFF) { - if (!xtensa->core_config->high_irq.enabled) { - LOG_TARGET_WARNING( - target, - "disabling IRQs while stepping is not implemented w/o high prio IRQs option!"); - return ERROR_FAIL; + if (xtensa->core_config->core_type == XT_LX) { + if (!xtensa->core_config->high_irq.enabled) { + LOG_TARGET_WARNING(target, + "disabling IRQs while stepping is not implemented w/o high prio IRQs option!"); + return ERROR_FAIL; + } + /* Update ICOUNTLEVEL accordingly */ + icountlvl = MIN((oldps & 0xF) + 1, xtensa->core_config->debug.irq_level); + } else { + /* Xtensa NX does not have the ICOUNTLEVEL feature present in Xtensa LX + * and instead disable interrupts while stepping. This could change + * the timing of the system while under debug */ + xtensa_reg_val_t newps = oldps | XT_PS_DI_MSK; + xtensa_reg_set(target, XT_REG_IDX_PS, newps); + icountlvl = xtensa->core_config->debug.irq_level; + ps_modified = true; } - /* Update ICOUNTLEVEL accordingly */ - icountlvl = MIN((oldps & 0xF) + 1, xtensa->core_config->debug.irq_level); } else { icountlvl = xtensa->core_config->debug.irq_level; } @@ -1815,7 +1824,7 @@ int xtensa_do_step(struct target *target, int current, target_addr_t address, in xtensa_cause_clear(target); /* so we don't recurse into the same routine */ if (xtensa->core_config->core_type == XT_LX && ((oldps & 0xf) >= icountlvl)) { /* Lower interrupt level to allow stepping, but flag eps[dbglvl] to be restored */ - ps_lowered = true; + ps_modified = true; uint32_t newps = (oldps & ~0xf) | (icountlvl - 1); xtensa_reg_set(target, xtensa->eps_dbglevel_idx, newps); LOG_TARGET_DEBUG(target, @@ -1916,11 +1925,12 @@ int xtensa_do_step(struct target *target, int current, target_addr_t address, in } /* Restore int level */ - if (ps_lowered) { + if (ps_modified) { LOG_DEBUG("Restoring %s after stepping: 0x%08" PRIx32, - xtensa->core_cache->reg_list[xtensa->eps_dbglevel_idx].name, - oldps); - xtensa_reg_set(target, xtensa->eps_dbglevel_idx, oldps); + xtensa->core_cache->reg_list[(xtensa->core_config->core_type == XT_LX) ? + xtensa->eps_dbglevel_idx : XT_REG_IDX_PS].name, oldps); + xtensa_reg_set(target, (xtensa->core_config->core_type == XT_LX) ? + xtensa->eps_dbglevel_idx : XT_REG_IDX_PS, oldps); } /* write ICOUNTLEVEL back to zero */ @@ -4191,11 +4201,6 @@ COMMAND_HELPER(xtensa_cmd_mask_interrupts_do, struct xtensa *xtensa) return ERROR_OK; } - if (xtensa->core_config->core_type == XT_NX) { - command_print(CMD, "ERROR: ISR step mode only supported on Xtensa LX"); - return ERROR_FAIL; - } - /* Masking is ON -> interrupts during stepping are OFF, and vice versa */ if (!strcasecmp(CMD_ARGV[0], "off")) state = XT_STEPPING_ISR_ON; diff --git a/src/target/xtensa/xtensa.h b/src/target/xtensa/xtensa.h index 1d56f83682..419277675c 100644 --- a/src/target/xtensa/xtensa.h +++ b/src/target/xtensa/xtensa.h @@ -45,6 +45,7 @@ /* PS register bits (NX) */ #define XT_PS_DIEXC_MSK BIT(2) +#define XT_PS_DI_MSK BIT(3) /* MS register bits (NX) */ #define XT_MS_DE_MSK BIT(5) --