Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
23cbdbf0eThis file is pinned to the selected release. Download the original to open it in your design tool.
// 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 <stdexcept>
#include "display.hpp"
#include "ftdiJtagBitbang.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
FtdiJtagBitBang::FtdiJtagBitBang(const cable_t &cable,
const jtag_pins_conf_t *pin_conf, const string &dev,
const std::string &serial, uint32_t clkHZ, int8_t verbose):
FTDIpp_MPSSE(cable, dev, serial, clkHZ, verbose), _bitmode(0),
_curr_tms(0), _rx_size(0)
{
unsigned char *ptr;
/* Validate pins */
uint8_t pins[] = {pin_conf->tck_pin, pin_conf->tms_pin,
pin_conf->tdi_pin, pin_conf->tdo_pin};
for (uint32_t i = 0; i < sizeof(pins) / sizeof(pins[0]); i++) {
if (pins[i] > FT232RL_RI || pins[i] < FT232RL_TXD) {
printf("%d\n", pins[i]);
printError("Invalid pin ID");
throw std::exception();
}
}
_tck_pin = 1 << pin_conf->tck_pin;
_tms_pin = 1 << pin_conf->tms_pin;
_tdi_pin = 1 << pin_conf->tdi_pin;
_tdo_pin = 1 << pin_conf->tdo_pin;
/* store FTDI TX Fifo size */
if (_pid == 0x6001) // FT232R
_rx_size = 256;
else if (_pid == 0x6015) // FT231X
_rx_size = 512;
else
_rx_size = _buffer_size;
/* RX Fifo size (rx: USB -> FTDI)
* is 128 or 256 Byte and MaxPacketSize ~= 64Byte
* but we let subsystem (libftdi, libusb, linux)
* sending with the correct size -> this reduce hierarchical calls
*/
_buffer_size = 4096;
/* _buffer_size has changed -> resize buffer */
ptr = (unsigned char *)realloc(_buffer, sizeof(char) * _buffer_size);
if (!ptr)
throw std::runtime_error("_buffer realloc failed\n");
_buffer = ptr;
setClkFreq(clkHZ);
if (init(1, _tck_pin | _tms_pin | _tdi_pin, BITMODE_BITBANG) != 0)
throw std::runtime_error("low level FTDI init failed");
setBitmode(BITMODE_BITBANG);
}
FtdiJtagBitBang::~FtdiJtagBitBang()
{
}
int FtdiJtagBitBang::setClkFreq(uint32_t clkHZ)
{
uint32_t user_clk = clkHZ;
if (clkHZ > 3000000) {
printWarn("Jtag probe limited to 3MHz");
clkHZ = 3000000;
}
printInfo("Jtag frequency : requested " + std::to_string(user_clk) +
"Hz -> real " + std::to_string(clkHZ) + "Hz");
int ret = ftdi_set_baudrate(_ftdi, clkHZ);
printf("ret %d\n", ret);
return ret;
}
int FtdiJtagBitBang::setBitmode(uint8_t mode)
{
if (_bitmode == mode)
return 0;
_bitmode = mode;
int ret = ftdi_set_bitmode(_ftdi, _tck_pin | _tms_pin | _tdi_pin, _bitmode);
#if (FTDI_VERSION < 105)
ftdi_usb_purge_rx_buffer(_ftdi);
ftdi_usb_purge_tx_buffer(_ftdi);
#else
ftdi_tcioflush(_ftdi);
#endif
return ret;
}
int FtdiJtagBitBang::writeTMS(const uint8_t *tms, uint32_t len, bool flush_buffer,
__attribute__((unused)) const uint8_t tdi)
{
int ret;
/* nothing to send
* but maybe need to flush internal buffer
*/
if (len == 0) {
if (flush_buffer) {
ret = flush();
return ret;
}
return 0;
}
/* check for at least one bit space in buffer */
if (_num+2 > _buffer_size) {
ret = flush();
if (ret < 0)
return ret;
}
/* fill buffer to reduce USB transaction */
for (uint32_t i = 0; i < len; i++) {
_curr_tms = ((tms[i >> 3] & (1 << (i & 0x07)))? _tms_pin : 0);
uint8_t val = _tdi_pin | _curr_tms;
_buffer[_num++] = val;
_buffer[_num++] = val | _tck_pin;
if (_num + 2 > _buffer_size) {
ret = write(NULL, 0);
if (ret < 0)
return ret;
}
}
/* security check: try to flush buffer */
if (flush_buffer) {
ret = write(NULL, 0);
if (ret < 0)
return ret;
}
return len;
}
int FtdiJtagBitBang::writeTDI(const uint8_t *tx, uint8_t *rx, uint32_t len, bool end)
{
uint32_t iter;
uint32_t xfer_size = (rx) ? _rx_size : _buffer_size;
if (len * 2 + 1 < xfer_size) {
iter = len;
} else {
iter = xfer_size >> 1; // two tx / bit
iter = (iter / 8) * 8;
}
uint8_t *rx_ptr = rx;
if (len == 0)
return 0;
if (rx)
memset(rx, 0, len/8);
/* quick fix: use an empty buffer */
if (_num != 0)
flush();
for (uint32_t i = 0, pos = 0; i < len; i++) {
/* keep tms or
* set tms high if it's last bit and end true */
if (end && (i == len -1))
_curr_tms = _tms_pin;
uint8_t val = _curr_tms;
if (tx)
val |= ((tx[i >> 3] & (1 << (i & 0x07)))? _tdi_pin : 0);
_buffer[_num ] = val;
_buffer[_num + 1] = val | _tck_pin;
_num += 2;
pos++;
/* flush buffer */
if (pos == iter) {
pos = 0;
write((rx) ? rx_ptr : NULL, iter);
if (rx)
rx_ptr += (iter/8);
}
}
/* security check: try to flush buffer */
if (_num != 0) {
write((rx && _num > 1) ? rx_ptr : NULL, _num / 2);
}
return len;
}
int FtdiJtagBitBang::toggleClk(uint8_t tms, uint8_t tdi, uint32_t clk_len)
{
int xfer_len = clk_len;
int val = ((tms) ? _tms_pin : 0) | ((tdi) ? _tdi_pin : 0);
while (xfer_len > 0) {
if (_num + 2 > _buffer_size)
if (write(NULL, 0) < 0)
return -EXIT_FAILURE;
_buffer[_num++] = val | _tck_pin;
_buffer[_num++] = val;
xfer_len--;
}
/* flush */
write(NULL, 0);
return clk_len;
}
int FtdiJtagBitBang::flush()
{
return write(NULL, 0);
}
int FtdiJtagBitBang::write(uint8_t *tdo, int nb_bit)
{
int ret = 0;
if (_num == 0)
return 0;
setBitmode((tdo) ? BITMODE_SYNCBB : BITMODE_BITBANG);
ret = ftdi_write_data(_ftdi, _buffer, _num);
if (ret != _num) {
printf("problem %d written\n", ret);
return ret;
}
if (tdo) {
ret = ftdi_read_data(_ftdi, _buffer, _num);
if (ret != _num) {
printf("problem %d read\n", ret);
return ret;
}
/* need to reconstruct received word
* even bit are discarded since JTAG read in rising edge
* since jtag is LSB first we need to shift right content by 1
* and add 0x80 (1 << 7) or 0
* the buffer may contains some tms bit, so start with i
* equal to fill exactly nb_bit bits
* */
for (int i = (_num-(nb_bit *2) + 1), offset=0; i < _num; i+=2, offset++) {
tdo[offset >> 3] = (((_buffer[i] & _tdo_pin) ? 0x80 : 0x00) |
(tdo[offset >> 3] >> 1));
}
}
_num = 0;
return ret;
}