2024-05-20 21:10:29 +02:00
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
/*
|
|
|
|
|
* Copyright (C) 2024 Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <libusb.h>
|
|
|
|
|
|
|
|
|
|
#include <cstdint>
|
|
|
|
|
#include <cstdio>
|
|
|
|
|
#include <cstring>
|
|
|
|
|
#include <stdexcept>
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
|
|
#include "cable.hpp"
|
|
|
|
|
#include "display.hpp"
|
|
|
|
|
#include "gwu2x_jtag.hpp"
|
|
|
|
|
#include "libusb_ll.hpp"
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* TCK -> GPIOL0
|
|
|
|
|
* TMS -> GPIOL1
|
|
|
|
|
* TDI -> GPIOL2
|
|
|
|
|
* TDO -> GPIOL3
|
|
|
|
|
*/
|
|
|
|
|
enum {
|
|
|
|
|
GWU2X_TMS_LSB_WRO = 0x5B,
|
|
|
|
|
GWU2X_TMS_LSB_RDWR = 0x5C,
|
|
|
|
|
GWU2X_TCK = 0x9b,
|
|
|
|
|
GWU2X_TDI_LSB_BIT_WRO = 0x6B,
|
|
|
|
|
GWU2X_TDI_LSB_BIT_RDWR = 0x6C,
|
|
|
|
|
GWU2X_TDI_LSB_BYTE_WRO = 0x7B,
|
|
|
|
|
GWU2X_TDI_LSB_BYTE_RDWR = 0x7C,
|
|
|
|
|
GWU2X_SET_FREQ_FAST = 0xAB,
|
|
|
|
|
GWU2X_SET_FREQ_SLOW = 0xAC,
|
|
|
|
|
GWU2X_READBACK_BUFFER_FORCED = 0x8B,
|
|
|
|
|
GWU2X_READBACK_BUFFER = 0xDB, /* 0x11: LSB, 0xff: MSB */
|
|
|
|
|
GWU2X_GPIO_CONF_LOW = 0x20, /* GPIO0-7 */
|
|
|
|
|
GWU2X_GPIO_CONF_HIGH = 0x21, /* GPIO8-15 */
|
|
|
|
|
GWU2X_GPIO_READ_LOW = 0x22, /* GPIO0-7 */
|
|
|
|
|
GWU2X_GPIO_READ_HIGH = 0x23, /* GPIO8-15 */
|
|
|
|
|
GWU2X_CPOL_SETTING = 0xCB,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
enum {
|
|
|
|
|
READBACK_LSB = 0x11,
|
|
|
|
|
READBACK_MSB = 0xff,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
GowinGWU2x::GowinGWU2x(cable_t *cable, uint32_t clkHz, int8_t verbose):
|
|
|
|
|
libusb_ll(0, 0, verbose), _verbose(verbose > 1), _cable(cable),
|
|
|
|
|
_usb_dev(nullptr), _dev(nullptr), _xfer_buf(nullptr), _xfer_pos(0),
|
|
|
|
|
_buffer_len(256 + 2 + 1)
|
|
|
|
|
{
|
|
|
|
|
const int found = get_devices_list(_cable);
|
|
|
|
|
if (found == 0)
|
|
|
|
|
throw std::runtime_error("No cable found");
|
|
|
|
|
if (found > 1)
|
|
|
|
|
throw std::runtime_error("More than one cable found");
|
|
|
|
|
std::vector<struct libusb_device *> dev_list = usb_dev_list();
|
|
|
|
|
|
|
|
|
|
/* here we have only one device present */
|
|
|
|
|
_usb_dev = dev_list[0];
|
|
|
|
|
|
|
|
|
|
int ret = libusb_open(_usb_dev, &_dev);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
throw std::runtime_error("Failed to open device");
|
|
|
|
|
|
|
|
|
|
ret = libusb_claim_interface(_dev, 0);
|
|
|
|
|
if (ret < 0) {
|
|
|
|
|
char mess[256];
|
|
|
|
|
snprintf(mess, 256, "Error claiming interface with error %s", libusb_error_name(ret));
|
|
|
|
|
throw std::runtime_error(mess);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_xfer_buf = new uint8_t[_buffer_len]; // one full TDI packet + readback cmd
|
|
|
|
|
|
|
|
|
|
/* cable configuration */
|
|
|
|
|
if (!store_seq(GWU2X_GPIO_CONF_LOW, // gpio0-7
|
|
|
|
|
cable->config.bit_low_dir, // direction
|
|
|
|
|
cable->config.bit_low_val)) // value
|
|
|
|
|
throw std::runtime_error("Error: low pins configuration failed");
|
|
|
|
|
if (!store_seq(GWU2X_GPIO_CONF_HIGH, // gpio8-15
|
|
|
|
|
cable->config.bit_high_dir, // direction
|
|
|
|
|
cable->config.bit_high_val)) // value
|
|
|
|
|
throw std::runtime_error("Error: high pins configuration failed");
|
|
|
|
|
if (!xfer(nullptr, 0))
|
|
|
|
|
throw std::runtime_error("Error: pin configuration failed");
|
|
|
|
|
|
|
|
|
|
if (setClkFreq(clkHz) < 0)
|
|
|
|
|
throw std::runtime_error("Error: clock frequency configuration failed");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GowinGWU2x::~GowinGWU2x()
|
|
|
|
|
{
|
|
|
|
|
flush();
|
|
|
|
|
delete _xfer_buf;
|
|
|
|
|
/* nothing about interface ? */
|
|
|
|
|
libusb_close(_dev);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int GowinGWU2x::writeTMS(const uint8_t *tms, uint32_t len, bool flush_buffer,
|
|
|
|
|
const uint8_t tdi)
|
|
|
|
|
{
|
|
|
|
|
const uint8_t tdi_bit = (tdi) ? 0x80 : 0x00;
|
|
|
|
|
uint8_t tms_buf = tdi_bit;
|
|
|
|
|
uint8_t idx = 0; // bit index in tms_buf
|
|
|
|
|
|
|
|
|
|
/* As FTDI devices TMS instruction is 3 bytes long and can send up to
|
|
|
|
|
* 7bits
|
|
|
|
|
* 2nd byte tells number of TMS bits to send (0: 1bit, 6: 7bits)
|
|
|
|
|
* 3rd byte is the sequence of TMS values LSB first. Offset 7 is the TDI
|
|
|
|
|
* value for all TMS bits
|
|
|
|
|
*/
|
|
|
|
|
for (uint32_t pos = 0; pos < len; pos++) {
|
|
|
|
|
const uint8_t tms_byte = tms[pos >> 3];
|
|
|
|
|
const uint8_t bit_shift = pos & 0x07;
|
|
|
|
|
const uint8_t tms_bit = (tms_byte >> bit_shift) & 0x01;
|
|
|
|
|
if (tms_bit)
|
|
|
|
|
tms_buf |= (1 << idx);
|
|
|
|
|
idx += 1;
|
|
|
|
|
/* if we have 7bits or if it's the last iteration
|
|
|
|
|
* flush the buffer, and restart for the next sequence
|
|
|
|
|
*/
|
|
|
|
|
if (idx == 7 || pos == len - 1) {
|
|
|
|
|
if (!store_seq(GWU2X_TMS_LSB_WRO, idx - 1, tms_buf))
|
|
|
|
|
return -1;
|
|
|
|
|
idx = 0;
|
|
|
|
|
tms_buf = tdi_bit;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (flush_buffer) {
|
|
|
|
|
if (!xfer(nullptr, 0)) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return static_cast<int>(len);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Write / Read data in one to three steps
|
|
|
|
|
* a sequence of up to 256 Bytes per packet
|
|
|
|
|
* a sequence of up to 8 bits
|
|
|
|
|
* the final bit (MSB) using TMS transition when end is true
|
|
|
|
|
*/
|
|
|
|
|
int GowinGWU2x::writeTDI(const uint8_t *tx, uint8_t *rx, uint32_t len, bool end)
|
|
|
|
|
{
|
|
|
|
|
const uint32_t real_len = len - (end ? 1 : 0); // if end: last bit is sent with TMS
|
|
|
|
|
const uint32_t byte_len = real_len >> 3; // convert bit len to byte len (floor)
|
|
|
|
|
const uint32_t bit_len = real_len & 0x07; // extract remaining bits
|
|
|
|
|
uint8_t *tx_ptr = (uint8_t *)tx;
|
|
|
|
|
uint8_t *rx_ptr = rx;
|
|
|
|
|
|
|
|
|
|
/* if the buffer is not empty, some tms bits are present
|
|
|
|
|
* flush to keep maximum size available
|
|
|
|
|
*/
|
|
|
|
|
if (_xfer_pos != 0) {
|
|
|
|
|
if (!xfer(nullptr, 0))
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 1. Byte sequence with up to 256 Bytes
|
|
|
|
|
if (byte_len > 0) {
|
|
|
|
|
uint32_t xfer_len = 256;
|
|
|
|
|
for (uint32_t pos = 0; pos < byte_len; pos += xfer_len) {
|
|
|
|
|
if (pos + 256 > byte_len)
|
|
|
|
|
xfer_len = byte_len - pos;
|
|
|
|
|
_xfer_buf[_xfer_pos++] = (rx) ? GWU2X_TDI_LSB_BYTE_RDWR : GWU2X_TDI_LSB_BYTE_WRO;
|
|
|
|
|
_xfer_buf[_xfer_pos++] = xfer_len - 1;
|
|
|
|
|
memcpy(&_xfer_buf[_xfer_pos], tx_ptr, xfer_len);
|
|
|
|
|
_xfer_pos += xfer_len;
|
|
|
|
|
if (rx)
|
|
|
|
|
_xfer_buf[_xfer_pos++] = GWU2X_READBACK_BUFFER_FORCED;
|
|
|
|
|
if (!xfer((rx) ? rx_ptr : nullptr, xfer_len))
|
|
|
|
|
return -1;
|
|
|
|
|
tx_ptr += xfer_len;
|
|
|
|
|
if (rx_ptr)
|
|
|
|
|
rx_ptr += xfer_len;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* when end with a sequence of bits
|
|
|
|
|
* don't do two usb transfers and
|
|
|
|
|
* postpone it to end sequence
|
|
|
|
|
*/
|
|
|
|
|
const bool postponed_read = (bit_len != 0 && end);
|
|
|
|
|
|
|
|
|
|
// 2. Remaining bits between 1 and 7.
|
|
|
|
|
if (bit_len != 0) {
|
|
|
|
|
/* write up to 8 bits
|
|
|
|
|
* may be less if end, but not more
|
|
|
|
|
* the buffer read must be correctly aligned according to bit_len
|
|
|
|
|
*/
|
|
|
|
|
if (!store_seq(GWU2X_TDI_LSB_BIT_WRO + (rx != nullptr),
|
|
|
|
|
static_cast<uint8_t>(bit_len - 1), *tx_ptr, !postponed_read && rx != nullptr))
|
|
|
|
|
return -1;
|
|
|
|
|
// When not end, flush buffer
|
|
|
|
|
// NOTE: with a more clever global system why not
|
|
|
|
|
// postning it for the next call ?
|
|
|
|
|
if (!postponed_read) {
|
|
|
|
|
/* unlike FTDI bits are filed LSB to MSB ie
|
|
|
|
|
* for 2 bits: 0b000000XX
|
|
|
|
|
* for 7 bits: 0b0XXXXXXX
|
|
|
|
|
* no needs to shift here
|
|
|
|
|
*/
|
|
|
|
|
if (!xfer((rx) ? rx_ptr : nullptr, 1))
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 3. End using TMS instruction and last bit.
|
|
|
|
|
if (end) {
|
|
|
|
|
uint8_t rx_byte;
|
|
|
|
|
/* we are in SHIFTDR or SHIFTIR -> move to next state */
|
|
|
|
|
const uint8_t last_bit = (*tx_ptr >> bit_len) & 0x01;
|
|
|
|
|
if (!store_seq(GWU2X_TMS_LSB_WRO + (rx != nullptr), 0,
|
|
|
|
|
static_cast<uint8_t>(((last_bit) ? 0x80 : 0x00) | 0x01),
|
|
|
|
|
rx != nullptr))
|
|
|
|
|
return -1;
|
|
|
|
|
if (!xfer((rx)? &rx_byte : nullptr, 1))
|
|
|
|
|
return -1;
|
|
|
|
|
if (rx) {
|
|
|
|
|
if (postponed_read) {
|
|
|
|
|
*rx_ptr = rx_byte;
|
|
|
|
|
} else {
|
|
|
|
|
const uint8_t tdo_bit = 1 << (bit_len);
|
|
|
|
|
if (rx_byte & 0x01) // here only one bit is read (always LSB)
|
|
|
|
|
*rx_ptr |= tdo_bit;
|
|
|
|
|
else
|
|
|
|
|
*rx_ptr &= ~tdo_bit;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return static_cast<int>(len);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int GowinGWU2x::toggleClk(uint8_t tms, uint8_t tdi, uint32_t clk_len)
|
|
|
|
|
{
|
|
|
|
|
/* Gowin GWU2X has a specific command to produces
|
|
|
|
|
* a sequence sequence to up to 65536 cycles
|
|
|
|
|
* 0: 1 clk cycle, 0xffff: 65535 clk cycles
|
|
|
|
|
*/
|
|
|
|
|
/* No need to check/flush buffer will be done by store_seq */
|
2024-05-22 20:54:14 +02:00
|
|
|
uint32_t len = 0;
|
2024-05-20 21:10:29 +02:00
|
|
|
if (_verbose)
|
|
|
|
|
printf("toggleClk : %02x %02x %u\n", tms, tdi, clk_len);
|
|
|
|
|
|
|
|
|
|
for (uint32_t length = clk_len; length > 0; length -= len) {
|
|
|
|
|
len = ((length > 65536) ? 65536 : length) - 1;
|
|
|
|
|
if (!store_seq(GWU2X_TCK,
|
|
|
|
|
static_cast<uint8_t>((len >> 0) & 0xff),
|
|
|
|
|
static_cast<uint8_t>((len >> 8) & 0xff)))
|
|
|
|
|
return -1;
|
|
|
|
|
len += 1;
|
|
|
|
|
}
|
|
|
|
|
/* flush before return */
|
|
|
|
|
if (_xfer_pos != 0) {
|
|
|
|
|
if (!xfer(nullptr, 0))
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
return static_cast<int>(clk_len);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum {
|
|
|
|
|
EP_IN = 0x81,
|
|
|
|
|
EP_OUT = 0x02,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* when readback we consider it's for the current sequence, not
|
|
|
|
|
* for current content
|
|
|
|
|
*/
|
|
|
|
|
bool GowinGWU2x::store_seq(const uint8_t &opcode, const uint8_t &len,
|
|
|
|
|
const uint8_t &data, const bool readback)
|
|
|
|
|
{
|
|
|
|
|
if (_verbose) {
|
|
|
|
|
char message[256];
|
|
|
|
|
snprintf(message, 256, "store seq %02x %02x %02x %d",
|
|
|
|
|
opcode, len, data, readback);
|
|
|
|
|
printInfo(message);
|
|
|
|
|
}
|
|
|
|
|
const size_t xfer_len = 3 + readback;
|
|
|
|
|
if (_xfer_pos + xfer_len > _buffer_len) {
|
|
|
|
|
if (!xfer(nullptr, 0))
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
_xfer_buf[_xfer_pos++] = opcode;
|
|
|
|
|
_xfer_buf[_xfer_pos++] = len;
|
|
|
|
|
_xfer_buf[_xfer_pos++] = data;
|
|
|
|
|
if (readback)
|
|
|
|
|
_xfer_buf[_xfer_pos++] = GWU2X_READBACK_BUFFER_FORCED;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool GowinGWU2x::xfer(uint8_t *rx, uint16_t rx_len, const uint16_t timeout)
|
|
|
|
|
{
|
|
|
|
|
int actual_length;
|
|
|
|
|
int ret;
|
|
|
|
|
if (_xfer_pos == 0) // nothing to do
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
if (_verbose) {
|
|
|
|
|
printInfo("Write " + std::to_string(_xfer_pos) + " Bytes");
|
|
|
|
|
printf("\t");
|
|
|
|
|
for (uint32_t i = 0; i < _xfer_pos; i++) {
|
|
|
|
|
char message[6];
|
|
|
|
|
snprintf(message, 6, "0x%02x ", _xfer_buf[i]);
|
|
|
|
|
printSuccess(message, false);
|
|
|
|
|
}
|
|
|
|
|
printf("\n");
|
|
|
|
|
displayCmd();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret = libusb_bulk_transfer(_dev, EP_OUT,
|
|
|
|
|
_xfer_buf, _xfer_pos, &actual_length, timeout);
|
|
|
|
|
if (ret < 0) {
|
|
|
|
|
printError("Write failed with error " + std::to_string(ret));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
_xfer_pos = 0;
|
|
|
|
|
if (!rx) // message sent, nothing to read -> quit
|
|
|
|
|
return true;
|
|
|
|
|
uint8_t *rx_ptr = rx;
|
|
|
|
|
uint32_t dummy;
|
|
|
|
|
ret = libusb_bulk_transfer(_dev, 0x83, (uint8_t*)&dummy, 4, &actual_length, timeout);
|
|
|
|
|
if (_verbose)
|
|
|
|
|
printf("ret: %d %u %d\n", ret, dummy, actual_length);
|
|
|
|
|
do {
|
|
|
|
|
ret = libusb_bulk_transfer(_dev, EP_IN, rx_ptr, rx_len,
|
|
|
|
|
&actual_length, timeout);
|
|
|
|
|
if (ret < 0) {
|
|
|
|
|
char message[256];
|
|
|
|
|
snprintf(message, 256, "Failed to read: %d %s\n", ret,
|
|
|
|
|
libusb_strerror(static_cast<libusb_error>(ret)));
|
|
|
|
|
printError(message);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (_verbose) {
|
|
|
|
|
printf("%d %d %d\n", ret, rx_len, actual_length);
|
|
|
|
|
for (int ii = 0; ii < rx_len; ii++)
|
|
|
|
|
printf("%02x ", rx_ptr[ii]);
|
|
|
|
|
printf("\n");
|
|
|
|
|
}
|
|
|
|
|
rx_ptr += actual_length;
|
|
|
|
|
rx_len -= actual_length;
|
|
|
|
|
} while (rx_len > 0);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int GowinGWU2x::setClkFreq(uint32_t freqHz)
|
|
|
|
|
{
|
|
|
|
|
if (freqHz < 120e3 || freqHz > 30e6) {
|
|
|
|
|
printError("clk Frequency must be between 120kHz and 30MHz");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
const uint16_t div = static_cast<uint16_t>(60e6 / freqHz);
|
|
|
|
|
const int real_freq = static_cast<int>(60e6 / div);
|
|
|
|
|
printInfo("User requested: " + std::to_string(freqHz) + " real frequency is "
|
|
|
|
|
+ std::to_string(real_freq));
|
|
|
|
|
_xfer_buf[_xfer_pos++] = GWU2X_SET_FREQ_FAST + ((freqHz < 240e3) ? 1 : 0);
|
|
|
|
|
_xfer_buf[_xfer_pos++] = static_cast<uint8_t>(div - ((freqHz < 240e3) ? 256 : 0));
|
|
|
|
|
if (!xfer(nullptr, 0, 1000))
|
|
|
|
|
return -1;
|
|
|
|
|
_clkHZ = real_freq;
|
|
|
|
|
return static_cast<int>(_clkHZ);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GowinGWU2x::displayCmd()
|
|
|
|
|
{
|
|
|
|
|
uint32_t len = 0;
|
|
|
|
|
while (len < _xfer_pos) {
|
|
|
|
|
const uint8_t opcode = _xfer_buf[len++];
|
|
|
|
|
const uint8_t b_len = _xfer_buf[len++];
|
|
|
|
|
uint8_t tdi;
|
|
|
|
|
uint8_t tms;
|
|
|
|
|
char message[256];
|
|
|
|
|
uint8_t bytes[256];
|
|
|
|
|
switch(opcode) {
|
|
|
|
|
case GWU2X_TMS_LSB_WRO:
|
|
|
|
|
tdi = (_xfer_buf[len] >> 7) & 0x01;
|
|
|
|
|
tms = (_xfer_buf[len++] & 0x7f);
|
|
|
|
|
snprintf(message, 256,
|
|
|
|
|
"TMS Write Only len %d TDI: %x TMS: %02x",
|
|
|
|
|
b_len + 1, tdi, tms);
|
|
|
|
|
break;
|
|
|
|
|
case GWU2X_TMS_LSB_RDWR:
|
|
|
|
|
tdi = (_xfer_buf[len] >> 7) & 0x01;
|
|
|
|
|
tms = (_xfer_buf[len++] & 0x7f);
|
|
|
|
|
snprintf(message, 256,
|
|
|
|
|
"TMS Read/Write len %d TDI: %x TMS: %02x",
|
|
|
|
|
b_len + 1, tdi, tms);
|
|
|
|
|
break;
|
|
|
|
|
case GWU2X_TDI_LSB_BIT_WRO:
|
|
|
|
|
tdi = _xfer_buf[len++];
|
|
|
|
|
snprintf(message, 256,
|
|
|
|
|
"TDI bit Write Only len %d TDI: %2x",
|
|
|
|
|
b_len + 1, tdi);
|
|
|
|
|
break;
|
|
|
|
|
case GWU2X_TDI_LSB_BIT_RDWR:
|
|
|
|
|
tdi = _xfer_buf[len++];
|
|
|
|
|
snprintf(message, 256,
|
|
|
|
|
"TDI bit Read/Write len %d TDI: %2x",
|
|
|
|
|
b_len + 1, tdi);
|
|
|
|
|
break;
|
|
|
|
|
case GWU2X_TDI_LSB_BYTE_WRO:
|
|
|
|
|
memcpy(bytes, _xfer_buf, b_len + 1);
|
|
|
|
|
len += b_len + 1;
|
|
|
|
|
snprintf(message, 256,
|
|
|
|
|
"TDI Byte Write Only len %d TDI: %2x",
|
|
|
|
|
b_len + 1, bytes[0]);
|
|
|
|
|
break;
|
|
|
|
|
case GWU2X_TDI_LSB_BYTE_RDWR:
|
|
|
|
|
memcpy(bytes, _xfer_buf, b_len + 1);
|
|
|
|
|
len += b_len + 1;
|
|
|
|
|
snprintf(message, 256,
|
|
|
|
|
"TDI Byte Read/Write len %d TDI: %2x",
|
|
|
|
|
b_len + 1, bytes[0]);
|
|
|
|
|
break;
|
|
|
|
|
case GWU2X_TCK:
|
|
|
|
|
tdi = _xfer_buf[len++];
|
|
|
|
|
snprintf(message, 256,
|
|
|
|
|
"toggle Clock len %d",
|
|
|
|
|
(b_len | (tdi << 8)) + 1);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case GWU2X_GPIO_CONF_HIGH:
|
|
|
|
|
tms = _xfer_buf[len++];
|
|
|
|
|
snprintf(message, 256,
|
|
|
|
|
"GPIO conf high: direction: %x value: %02x",
|
|
|
|
|
b_len, tms);
|
|
|
|
|
break;
|
|
|
|
|
case GWU2X_GPIO_CONF_LOW:
|
|
|
|
|
tms = _xfer_buf[len++];
|
|
|
|
|
snprintf(message, 256,
|
|
|
|
|
"GPIO conf low: direction: %x value: %02x",
|
|
|
|
|
b_len, tms);
|
|
|
|
|
break;
|
|
|
|
|
case GWU2X_SET_FREQ_FAST:
|
|
|
|
|
case GWU2X_SET_FREQ_SLOW:
|
|
|
|
|
snprintf(message, 256,
|
|
|
|
|
"Set clk freq prescaler: %02x",
|
|
|
|
|
b_len);
|
|
|
|
|
break;
|
|
|
|
|
case GWU2X_READBACK_BUFFER_FORCED:
|
|
|
|
|
len--;
|
|
|
|
|
snprintf(message, 256, "readback buffer");
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
snprintf(message, 256, "Unknown");
|
|
|
|
|
printError(message);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
printInfo(message);
|
|
|
|
|
}
|
|
|
|
|
}
|