ch552_jtag: driver for ch552_jtag firmware (ft2232c clone)
This commit is contained in:
parent
fb6a3b9f03
commit
f843dc2eb2
|
|
@ -57,6 +57,7 @@ set(OPENFPGALOADER_SOURCE
|
||||||
src/anlogic.cpp
|
src/anlogic.cpp
|
||||||
src/anlogicBitParser.cpp
|
src/anlogicBitParser.cpp
|
||||||
src/anlogicCable.cpp
|
src/anlogicCable.cpp
|
||||||
|
src/ch552_jtag.cpp
|
||||||
src/dfu.cpp
|
src/dfu.cpp
|
||||||
src/dfuFileParser.cpp
|
src/dfuFileParser.cpp
|
||||||
src/dirtyJtag.cpp
|
src/dirtyJtag.cpp
|
||||||
|
|
@ -97,6 +98,7 @@ set(OPENFPGALOADER_HEADERS
|
||||||
src/anlogic.hpp
|
src/anlogic.hpp
|
||||||
src/anlogicBitParser.hpp
|
src/anlogicBitParser.hpp
|
||||||
src/anlogicCable.hpp
|
src/anlogicCable.hpp
|
||||||
|
src/ch552_jtag.hpp
|
||||||
src/cxxopts.hpp
|
src/cxxopts.hpp
|
||||||
src/dfu.hpp
|
src/dfu.hpp
|
||||||
src/dfuFileParser.hpp
|
src/dfuFileParser.hpp
|
||||||
|
|
|
||||||
|
|
@ -138,7 +138,7 @@ static std::map <std::string, target_board_t> board_list = {
|
||||||
DFU_BOARD("orangeCrab", "", "dfu", 0x1209, 0x5af0, 0),
|
DFU_BOARD("orangeCrab", "", "dfu", 0x1209, 0x5af0, 0),
|
||||||
JTAG_BOARD("qmtechCycloneV", "5ce223", "", 0, 0, CABLE_DEFAULT),
|
JTAG_BOARD("qmtechCycloneV", "5ce223", "", 0, 0, CABLE_DEFAULT),
|
||||||
JTAG_BOARD("runber", "", "ft232", 0, 0, CABLE_DEFAULT),
|
JTAG_BOARD("runber", "", "ft232", 0, 0, CABLE_DEFAULT),
|
||||||
JTAG_BOARD("tangnano", "", "ft2232", 0, 0, CABLE_DEFAULT),
|
JTAG_BOARD("tangnano", "", "ch552_jtag", 0, 0, CABLE_DEFAULT),
|
||||||
JTAG_BOARD("tangnano4k", "", "ft2232", 0, 0, CABLE_DEFAULT),
|
JTAG_BOARD("tangnano4k", "", "ft2232", 0, 0, CABLE_DEFAULT),
|
||||||
JTAG_BOARD("tec0117", "", "ft2232", 0, 0, CABLE_DEFAULT),
|
JTAG_BOARD("tec0117", "", "ft2232", 0, 0, CABLE_DEFAULT),
|
||||||
JTAG_BITBANG_BOARD("ulx2s", "", "ft232RL", 0, 0,
|
JTAG_BITBANG_BOARD("ulx2s", "", "ft232RL", 0, 0,
|
||||||
|
|
|
||||||
|
|
@ -16,12 +16,13 @@
|
||||||
*/
|
*/
|
||||||
enum communication_type {
|
enum communication_type {
|
||||||
MODE_ANLOGICCABLE = 0, /*! JTAG probe from Anlogic */
|
MODE_ANLOGICCABLE = 0, /*! JTAG probe from Anlogic */
|
||||||
MODE_FTDI_BITBANG = 1, /*! used with ft232RL/ft231x */
|
MODE_CH552_JTAG, /*! ch552_jtag firmware */
|
||||||
MODE_FTDI_SERIAL = 2, /*! ft2232, ft232H */
|
MODE_FTDI_BITBANG, /*! used with ft232RL/ft231x */
|
||||||
MODE_DIRTYJTAG = 3, /*! JTAG probe firmware for STM32F1 */
|
MODE_FTDI_SERIAL, /*! ft2232, ft232H */
|
||||||
MODE_USBBLASTER = 4, /*! JTAG probe firmware for USBBLASTER */
|
MODE_DIRTYJTAG, /*! JTAG probe firmware for STM32F1 */
|
||||||
MODE_CMSISDAP = 5, /*! CMSIS-DAP JTAG probe */
|
MODE_USBBLASTER, /*! JTAG probe firmware for USBBLASTER */
|
||||||
MODE_DFU = 6, /*! DFU based probe */
|
MODE_CMSISDAP, /*! CMSIS-DAP JTAG probe */
|
||||||
|
MODE_DFU, /*! DFU based probe */
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
@ -35,6 +36,7 @@ static std::map <std::string, cable_t> cable_list = {
|
||||||
{"anlogicCable", {MODE_ANLOGICCABLE, {}}},
|
{"anlogicCable", {MODE_ANLOGICCABLE, {}}},
|
||||||
{"bus_blaster", {MODE_FTDI_SERIAL, {0x0403, 0x6010, INTERFACE_A, 0x08, 0x1B, 0x08, 0x0B}}},
|
{"bus_blaster", {MODE_FTDI_SERIAL, {0x0403, 0x6010, INTERFACE_A, 0x08, 0x1B, 0x08, 0x0B}}},
|
||||||
{"bus_blaster_b", {MODE_FTDI_SERIAL, {0x0403, 0x6010, INTERFACE_B, 0x08, 0x0B, 0x08, 0x0B}}},
|
{"bus_blaster_b", {MODE_FTDI_SERIAL, {0x0403, 0x6010, INTERFACE_B, 0x08, 0x0B, 0x08, 0x0B}}},
|
||||||
|
{"ch552_jtag", {MODE_FTDI_SERIAL, {0x0403, 0x6010, INTERFACE_A, 0x08, 0x0B, 0x08, 0x0B}}},
|
||||||
{"cmsisdap", {MODE_CMSISDAP, {0x0d28, 0x0204, 0, 0, 0, 0, 0 }}},
|
{"cmsisdap", {MODE_CMSISDAP, {0x0d28, 0x0204, 0, 0, 0, 0, 0 }}},
|
||||||
{"dfu", {MODE_DFU, {}}},
|
{"dfu", {MODE_DFU, {}}},
|
||||||
{"digilent", {MODE_FTDI_SERIAL, {0x0403, 0x6010, INTERFACE_A, 0xe8, 0xeb, 0x00, 0x60}}},
|
{"digilent", {MODE_FTDI_SERIAL, {0x0403, 0x6010, INTERFACE_A, 0xe8, 0xeb, 0x00, 0x60}}},
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,366 @@
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <libusb.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "display.hpp"
|
||||||
|
#include "ch552_jtag.hpp"
|
||||||
|
#include "ftdipp_mpsse.hpp"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
#define DEBUG 0
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
#define display(...) \
|
||||||
|
do { \
|
||||||
|
if (_verbose) fprintf(stdout, __VA_ARGS__); \
|
||||||
|
}while(0)
|
||||||
|
#else
|
||||||
|
#define display(...) do {}while(0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
CH552_jtag::CH552_jtag(const FTDIpp_MPSSE::mpsse_bit_config &cable,
|
||||||
|
string dev, const string &serial, uint32_t clkHZ, bool verbose):
|
||||||
|
FTDIpp_MPSSE(cable, dev, serial, clkHZ, verbose), _ch552WA(false),
|
||||||
|
_write_mode(0), _read_mode(0), _to_read(0)
|
||||||
|
{
|
||||||
|
init_internal(cable);
|
||||||
|
}
|
||||||
|
|
||||||
|
CH552_jtag::~CH552_jtag()
|
||||||
|
{
|
||||||
|
int read;
|
||||||
|
/* Before shutdown, we must wait until everything is shifted out
|
||||||
|
* Do this by temporary enabling loopback mode, write something
|
||||||
|
* and wait until we can read it back
|
||||||
|
*/
|
||||||
|
static unsigned char tbuf[16] = { SET_BITS_LOW, 0xff, 0x00,
|
||||||
|
SET_BITS_HIGH, 0xff, 0x00,
|
||||||
|
LOOPBACK_START,
|
||||||
|
static_cast<unsigned char>(MPSSE_DO_READ | _read_mode |
|
||||||
|
MPSSE_DO_WRITE | _write_mode | MPSSE_LSB),
|
||||||
|
0x04, 0x00,
|
||||||
|
0xaa, 0x55, 0x00, 0xff, 0xaa,
|
||||||
|
LOOPBACK_END
|
||||||
|
};
|
||||||
|
mpsse_store(tbuf, 16);
|
||||||
|
read = mpsse_read(tbuf, 5);
|
||||||
|
if (read != 5)
|
||||||
|
fprintf(stderr,
|
||||||
|
"Loopback failed, expect problems on later runs %d\n", read);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CH552_jtag::init_internal(const FTDIpp_MPSSE::mpsse_bit_config &cable)
|
||||||
|
{
|
||||||
|
display("iProduct : %s\n", _iproduct);
|
||||||
|
|
||||||
|
if (!strncmp((const char *)_iproduct, "Sipeed-Debug", 12)) {
|
||||||
|
_ch552WA = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
display("%x\n", cable.bit_low_val);
|
||||||
|
display("%x\n", cable.bit_low_dir);
|
||||||
|
display("%x\n", cable.bit_high_val);
|
||||||
|
display("%x\n", cable.bit_high_dir);
|
||||||
|
|
||||||
|
init(5, 0xfb, BITMODE_MPSSE);
|
||||||
|
config_edge();
|
||||||
|
ftdi_set_event_char(_ftdi, 0, 0);
|
||||||
|
ftdi_set_error_char(_ftdi, 0, 0);
|
||||||
|
ftdi_set_latency_timer(_ftdi, 5);
|
||||||
|
ftdi_tciflush(_ftdi);
|
||||||
|
ftdi_tcoflush(_ftdi);
|
||||||
|
printInfo("fin");
|
||||||
|
}
|
||||||
|
|
||||||
|
int CH552_jtag::setClkFreq(uint32_t clkHZ) {
|
||||||
|
|
||||||
|
int ret = FTDIpp_MPSSE::setClkFreq(clkHZ);
|
||||||
|
config_edge();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CH552_jtag::config_edge()
|
||||||
|
{
|
||||||
|
/* at high (>15MHz) with digilent cable (arty)
|
||||||
|
* opposite edges must be used.
|
||||||
|
* Not required with classic FT2232
|
||||||
|
*/
|
||||||
|
if (!strncmp((const char *)_iproduct, "Digilent USB Device", 19)) {
|
||||||
|
if (FTDIpp_MPSSE::getClkFreq() < 15000000) {
|
||||||
|
_write_mode = MPSSE_WRITE_NEG;
|
||||||
|
_read_mode = 0;
|
||||||
|
} else {
|
||||||
|
_write_mode = 0;
|
||||||
|
_read_mode = MPSSE_READ_NEG;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_write_mode = MPSSE_WRITE_NEG;
|
||||||
|
_read_mode = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int CH552_jtag::writeTMS(uint8_t *tms, uint32_t len, bool flush_buffer)
|
||||||
|
{
|
||||||
|
(void) flush_buffer;
|
||||||
|
display("%s %d %d\n", __func__, len, (len/8)+1);
|
||||||
|
|
||||||
|
if (len == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
uint32_t xfer = len;
|
||||||
|
uint32_t iter = (_buffer_size -8) / 4;
|
||||||
|
iter = (_buffer_size / 3);
|
||||||
|
uint32_t offset = 0, pos = 0;
|
||||||
|
flush_buffer = true;
|
||||||
|
|
||||||
|
uint8_t buf[3]= {static_cast<unsigned char>(MPSSE_WRITE_TMS | MPSSE_LSB |
|
||||||
|
MPSSE_BITMODE | _write_mode |
|
||||||
|
MPSSE_DO_READ | _read_mode),
|
||||||
|
0, 0};
|
||||||
|
while (xfer > 0) {
|
||||||
|
int bit_to_send = (xfer > 6) ? 6 : xfer;
|
||||||
|
buf[1] = bit_to_send-1;
|
||||||
|
buf[2] = 0x80;
|
||||||
|
|
||||||
|
for (int i = 0; i < bit_to_send; i++, offset++) {
|
||||||
|
buf[2] |=
|
||||||
|
(((tms[offset >> 3] & (1 << (offset & 0x07))) ? 1 : 0) << i);
|
||||||
|
}
|
||||||
|
pos+=3;
|
||||||
|
_to_read++;
|
||||||
|
|
||||||
|
mpsse_store(buf, 3);
|
||||||
|
if (pos >= iter) {
|
||||||
|
uint8_t tmp[_to_read];
|
||||||
|
pos = 0;
|
||||||
|
if (-1 == mpsse_read(tmp, _to_read))
|
||||||
|
printError("writeTMS: Fail to read/write");
|
||||||
|
_to_read = 0;
|
||||||
|
}
|
||||||
|
xfer -= bit_to_send;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flush_buffer) {
|
||||||
|
if (_to_read > 0) {
|
||||||
|
uint8_t tmp[_to_read];
|
||||||
|
if (mpsse_read(tmp, _to_read) == -1)
|
||||||
|
printError("writeTMS: fail to flush");
|
||||||
|
_to_read = 0;
|
||||||
|
}
|
||||||
|
if (_num > 0)
|
||||||
|
if (mpsse_write() == -1)
|
||||||
|
printError("writeTMS: fail to flush in write mode");
|
||||||
|
}
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CH552_jtag::toggleClk(uint8_t tms, uint8_t tdi, uint32_t clk_len)
|
||||||
|
{
|
||||||
|
(void) tdi;
|
||||||
|
|
||||||
|
int byteLen = (clk_len+7)/8;
|
||||||
|
uint8_t buf_tms[byteLen];
|
||||||
|
|
||||||
|
memset(buf_tms, (tms) ? 0xff : 0x00, byteLen);
|
||||||
|
return writeTMS(buf_tms, clk_len, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
int CH552_jtag::flush()
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
if (_to_read == 0) {
|
||||||
|
ret = mpsse_write();
|
||||||
|
if (ret == -1)
|
||||||
|
printError("flush: fails to write");
|
||||||
|
} else {
|
||||||
|
uint8_t tmp[_to_read];
|
||||||
|
ret = mpsse_read(tmp, _to_read);
|
||||||
|
if (ret == -1)
|
||||||
|
printError("flush: fails to read/write");
|
||||||
|
_to_read = 0;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CH552_jtag::writeTDI(uint8_t *tdi, uint8_t *tdo, uint32_t len, bool last)
|
||||||
|
{
|
||||||
|
bool rd_mode = (tdo) ? true : false;
|
||||||
|
/* 3 possible case :
|
||||||
|
* - n * 8bits to send -> use byte command
|
||||||
|
* - less than 8bits -> use bit command
|
||||||
|
* - last bit to send -> sent in conjunction with TMS
|
||||||
|
*/
|
||||||
|
int tx_buff_size = mpsse_get_buffer_size();
|
||||||
|
int real_len = (last) ? len - 1 : len; // if its a buffer in a big send send len
|
||||||
|
// else supress last bit -> with TMS
|
||||||
|
int nb_byte = real_len >> 3; // number of byte to send
|
||||||
|
int nb_bit = (real_len & 0x07); // residual bits
|
||||||
|
int xfer = tx_buff_size - 7; // 2 byte for opcode and size 2 time
|
||||||
|
unsigned char *rx_ptr = (unsigned char *)tdo;
|
||||||
|
unsigned char *tx_ptr = (unsigned char *)tdi;
|
||||||
|
unsigned char tx_buf[3] = {(unsigned char)(MPSSE_LSB |
|
||||||
|
((tdi) ? (MPSSE_DO_WRITE | _write_mode) : 0) |
|
||||||
|
((rd_mode) ? (MPSSE_DO_READ | _read_mode) : 0)),
|
||||||
|
static_cast<unsigned char>((xfer - 1) & 0xff), // low
|
||||||
|
static_cast<unsigned char>((((xfer - 1) >> 8) & 0xff))}; // high
|
||||||
|
unsigned char def_cmd = tx_buf[0];
|
||||||
|
unsigned char rd_cmd = def_cmd | (MPSSE_DO_READ | _read_mode);
|
||||||
|
/* read (and write) 1Byte */
|
||||||
|
uint8_t oneshot_buf[3] = {rd_cmd, 0, 0};
|
||||||
|
|
||||||
|
if (_to_read != 0) {
|
||||||
|
uint8_t tmp_[_to_read];
|
||||||
|
if (mpsse_read(tmp_, _to_read) == -1)
|
||||||
|
printError("writeTDI: fails to flush read");
|
||||||
|
_to_read = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
display("%s len : %d %d %d %d\n", __func__, len, real_len, nb_byte,
|
||||||
|
nb_bit);
|
||||||
|
|
||||||
|
if (_num != 0 && ((nb_byte + _num + 3) > _buffer_size))
|
||||||
|
if (mpsse_write() == -1)
|
||||||
|
printError("writeTDI: fails to flush write");
|
||||||
|
|
||||||
|
if ((nb_byte * 8) + nb_bit != real_len) {
|
||||||
|
printf("pas cool\n");
|
||||||
|
throw std::exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* first case: 1 full byte (and maybe up to 7bit) to send
|
||||||
|
* direct write (and read)
|
||||||
|
*/
|
||||||
|
if (nb_byte == 1) {
|
||||||
|
uint8_t tmp_;
|
||||||
|
mpsse_store(oneshot_buf, 3);
|
||||||
|
if (tdi) {
|
||||||
|
mpsse_store(tx_ptr, 1);
|
||||||
|
tx_ptr++;
|
||||||
|
}
|
||||||
|
if (mpsse_read(&tmp_, 1) == -1)
|
||||||
|
printError("writeTDI: fails to read/write with nb_byte == 1");
|
||||||
|
if (rd_mode) {
|
||||||
|
*rx_ptr = tmp_;
|
||||||
|
rx_ptr++;
|
||||||
|
}
|
||||||
|
nb_byte--;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (nb_byte != 0) {
|
||||||
|
uint8_t tmp_;
|
||||||
|
int xfer_len = (nb_byte > xfer) ? xfer : nb_byte;
|
||||||
|
if (!rd_mode) {
|
||||||
|
xfer_len--;
|
||||||
|
}
|
||||||
|
if (xfer_len != 0) {
|
||||||
|
tx_buf[0] = def_cmd;
|
||||||
|
tx_buf[1] = (((xfer_len - 1) ) & 0xff); // low
|
||||||
|
tx_buf[2] = (((xfer_len - 1) >> 8) & 0xff); // high
|
||||||
|
mpsse_store(tx_buf, 3);
|
||||||
|
if (tdi) {
|
||||||
|
mpsse_store(tx_ptr, xfer_len);
|
||||||
|
tx_ptr += xfer_len;
|
||||||
|
}
|
||||||
|
if (rd_mode) {
|
||||||
|
if (mpsse_read(rx_ptr, xfer_len) == -1)
|
||||||
|
printError("writeTDI: fails to read with nb_byte > 1");
|
||||||
|
rx_ptr += xfer_len;
|
||||||
|
} else {
|
||||||
|
mpsse_store(oneshot_buf, 3);
|
||||||
|
if (tdi) {
|
||||||
|
mpsse_store(tx_ptr, 1);
|
||||||
|
tx_ptr++;
|
||||||
|
}
|
||||||
|
if (mpsse_read(&tmp_, 1) == -1)
|
||||||
|
printError("writeTDI: fails to read/write with nb_byte > 1");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tx_buf[0] = rd_cmd;
|
||||||
|
tx_buf[1] = 0;
|
||||||
|
tx_buf[2] = 0;
|
||||||
|
mpsse_store(tx_buf, 3);
|
||||||
|
if (tdi) {
|
||||||
|
mpsse_store(tx_ptr, 1);
|
||||||
|
tx_ptr++;
|
||||||
|
}
|
||||||
|
if (mpsse_read(&tmp_, 1) == -1)
|
||||||
|
printError("writeTDI: fails to read/write with nb_byte == 0");
|
||||||
|
if (rd_mode) {
|
||||||
|
*rx_ptr = tmp_;
|
||||||
|
rx_ptr++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!rd_mode)
|
||||||
|
xfer_len++;
|
||||||
|
nb_byte -= xfer_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char last_bit = (tdi) ? *tx_ptr : 0;
|
||||||
|
|
||||||
|
/* next: serie of bit to send: inconditionnaly write AND read
|
||||||
|
*/
|
||||||
|
if (nb_bit != 0) {
|
||||||
|
display("%s read/write %d bit\n", __func__, nb_bit);
|
||||||
|
tx_buf[0] = rd_cmd | MPSSE_BITMODE;
|
||||||
|
tx_buf[1] = nb_bit - 1;
|
||||||
|
mpsse_store(tx_buf, 2);
|
||||||
|
if (tdi) {
|
||||||
|
display("%s last_bit %x size %d\n", __func__, last_bit, nb_bit-1);
|
||||||
|
mpsse_store(last_bit);
|
||||||
|
}
|
||||||
|
uint8_t tmp_;
|
||||||
|
if (mpsse_read(&tmp_, 1) == -1)
|
||||||
|
printError("writeTDI: fails to read/write serie of bits");
|
||||||
|
if (rd_mode) {
|
||||||
|
*rx_ptr = tmp_;
|
||||||
|
/* realign we have read nb_bit
|
||||||
|
* since LSB add bit by the left and shift
|
||||||
|
* we need to complete shift
|
||||||
|
*/
|
||||||
|
*rx_ptr >>= (8 - nb_bit);
|
||||||
|
display("%s %x\n", __func__, *rx_ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* display : must be dropped */
|
||||||
|
if (_verbose && tdo) {
|
||||||
|
display("\n");
|
||||||
|
for (int i = (len / 8) - 1; i >= 0; i--)
|
||||||
|
display("%x ", (unsigned char)tdo[i]);
|
||||||
|
display("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last == 1) {
|
||||||
|
last_bit = (tdi)? (*tx_ptr & (1 << nb_bit)) : 0;
|
||||||
|
|
||||||
|
display("%s move to EXIT1_xx and send last bit %x\n", __func__, (last_bit?0x81:0x01));
|
||||||
|
/* write the last bit in conjunction with TMS */
|
||||||
|
tx_buf[0] = MPSSE_WRITE_TMS | MPSSE_LSB | MPSSE_BITMODE | _write_mode |
|
||||||
|
(MPSSE_DO_READ | _read_mode);
|
||||||
|
tx_buf[1] = 0x0; // send 1bit
|
||||||
|
tx_buf[2] = ((last_bit) ? 0x81 : 0x01); // we know in TMS tdi is bit 7
|
||||||
|
// and to move to EXIT_XR TMS = 1
|
||||||
|
mpsse_store(tx_buf, 3);
|
||||||
|
uint8_t c;
|
||||||
|
if (mpsse_read(&c, 1) == -1)
|
||||||
|
printError("writeTDI: fails to read/write last transaction");
|
||||||
|
if (rd_mode) {
|
||||||
|
/* in this case for 1 one it's always bit 7 */
|
||||||
|
*rx_ptr |= ((c & 0x80) << (7 - nb_bit));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2021 Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SRC_CH552_JTAG_HPP_
|
||||||
|
#define SRC_CH552_JTAG_HPP_
|
||||||
|
#include <ftdi.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "jtagInterface.hpp"
|
||||||
|
#include "ftdipp_mpsse.hpp"
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \file ch552_jtag.hpp
|
||||||
|
* \class ch552_jtag
|
||||||
|
* \brief ch552_jtag firmware (ft2232C clone) implementation
|
||||||
|
* \author Gwenhael Goavec-Merou
|
||||||
|
*/
|
||||||
|
|
||||||
|
class CH552_jtag : public JtagInterface, private FTDIpp_MPSSE {
|
||||||
|
public:
|
||||||
|
CH552_jtag(const FTDIpp_MPSSE::mpsse_bit_config &cable, std::string dev,
|
||||||
|
const std::string &serial, uint32_t clkHZ, bool verbose = false);
|
||||||
|
virtual ~CH552_jtag();
|
||||||
|
|
||||||
|
int setClkFreq(uint32_t clkHZ) override;
|
||||||
|
|
||||||
|
uint32_t getClkFreq() override {return FTDIpp_MPSSE::getClkFreq();}
|
||||||
|
|
||||||
|
/* TMS */
|
||||||
|
int writeTMS(uint8_t *tms, uint32_t len, bool flush_buffer) override;
|
||||||
|
/* clock */
|
||||||
|
int toggleClk(uint8_t tms, uint8_t tdi, uint32_t clk_len) override;
|
||||||
|
/* TDI */
|
||||||
|
int writeTDI(uint8_t *tx, uint8_t *rx, uint32_t len, bool end) override;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief return internal buffer size (in byte).
|
||||||
|
* \return _buffer_size -3 for mpsse cmd + size, -1 for potential SEND_IMMEDIATE
|
||||||
|
*/
|
||||||
|
int get_buffer_size() override { return _buffer_size-3; }
|
||||||
|
|
||||||
|
bool isFull() override { return false;}
|
||||||
|
|
||||||
|
int flush() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void init_internal(const FTDIpp_MPSSE::mpsse_bit_config &cable);
|
||||||
|
/*!
|
||||||
|
* \brief configure read and write edge (pos or neg), with freq < 15MHz
|
||||||
|
* neg is used for write and pos to sample. with freq >= 15MHz
|
||||||
|
* pos is used for write and neg to sample
|
||||||
|
*/
|
||||||
|
void config_edge();
|
||||||
|
bool _ch552WA; /* avoid errors with SiPeed tangNano */
|
||||||
|
uint8_t _write_mode; /**< write edge configuration */
|
||||||
|
uint8_t _read_mode; /**< read edge configuration */
|
||||||
|
uint32_t _to_read; /*!< amount of byte to read */
|
||||||
|
};
|
||||||
|
#endif // SRC_CH552_JTAG_HPP_
|
||||||
|
|
@ -13,6 +13,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "anlogicCable.hpp"
|
#include "anlogicCable.hpp"
|
||||||
|
#include "ch552_jtag.hpp"
|
||||||
#include "display.hpp"
|
#include "display.hpp"
|
||||||
#include "jtag.hpp"
|
#include "jtag.hpp"
|
||||||
#include "ftdipp_mpsse.hpp"
|
#include "ftdipp_mpsse.hpp"
|
||||||
|
|
@ -91,6 +92,9 @@ void Jtag::init_internal(cable_t &cable, const string &dev, const string &serial
|
||||||
case MODE_FTDI_SERIAL:
|
case MODE_FTDI_SERIAL:
|
||||||
_jtag = new FtdiJtagMPSSE(cable.config, dev, serial, clkHZ, _verbose);
|
_jtag = new FtdiJtagMPSSE(cable.config, dev, serial, clkHZ, _verbose);
|
||||||
break;
|
break;
|
||||||
|
case MODE_CH552_JTAG:
|
||||||
|
_jtag = new CH552_jtag(cable.config, dev, serial, clkHZ, _verbose);
|
||||||
|
break;
|
||||||
case MODE_DIRTYJTAG:
|
case MODE_DIRTYJTAG:
|
||||||
_jtag = new DirtyJtag(clkHZ, _verbose);
|
_jtag = new DirtyJtag(clkHZ, _verbose);
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue