openFPGALoader/src/ch552_jtag.cpp

344 lines
8.5 KiB
C++

// 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, uint8_t verbose):
FTDIpp_MPSSE(cable, dev, serial, clkHZ, verbose), _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 |
MPSSE_DO_WRITE | MPSSE_WRITE_NEG | 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);
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);
ftdi_set_event_char(_ftdi, 0, 0);
ftdi_set_error_char(_ftdi, 0, 0);
ftdi_set_latency_timer(_ftdi, 5);
#if (FTDI_VERSION < 105)
ftdi_usb_purge_rx_buffer(_ftdi);
ftdi_usb_purge_tx_buffer(_ftdi);
#else
ftdi_tciflush(_ftdi);
ftdi_tcoflush(_ftdi);
#endif
}
int CH552_jtag::setClkFreq(uint32_t clkHZ) {
int ret = FTDIpp_MPSSE::setClkFreq(clkHZ);
return ret;
}
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 | MPSSE_WRITE_NEG|
MPSSE_DO_READ),
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 | MPSSE_WRITE_NEG) : 0) |
((rd_mode) ? (MPSSE_DO_READ) : 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 (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 | MPSSE_WRITE_NEG |
(MPSSE_DO_READ);
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;
}