diff --git a/CMakeLists.txt b/CMakeLists.txt index 81f9fcf..e5386fe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,6 +26,7 @@ set(OPENFPGALOADER_SOURCE src/jedParser.cpp src/display.cpp src/jtag.cpp + src/ftdiJtagBitbang.cpp src/ftdiJtagMPSSE.cpp src/configBitstreamParser.cpp src/ftdipp_mpsse.cpp @@ -47,6 +48,7 @@ set(OPENFPGALOADER_HEADERS src/altera.hpp src/progressBar.hpp src/bitparser.hpp + src/ftdiJtagBitbang.hpp src/ftdiJtagMPSSE.hpp src/jtag.hpp src/jtagInterface.hpp diff --git a/src/ftdiJtagBitbang.cpp b/src/ftdiJtagBitbang.cpp new file mode 100644 index 0000000..e16d1bb --- /dev/null +++ b/src/ftdiJtagBitbang.cpp @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2020 Gwenhael Goavec-Merou + * + * 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 3 of the License, or + * (at your option) any later version. + * + * 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 . + */ + +#include +#include +#include + +#include +#include +#include +#include + +#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 FTDIpp_MPSSE::mpsse_bit_config &cable, + const jtag_pins_conf_t *pin_conf, string dev, uint32_t clkHZ, bool verbose): + FTDIpp_MPSSE(dev, cable.interface, clkHZ, verbose), _bitmode(0), _nb_bit(0) +{ + init_internal(cable, pin_conf); +} + +FtdiJtagBitBang::FtdiJtagBitBang(const FTDIpp_MPSSE::mpsse_bit_config &cable, + const jtag_pins_conf_t *pin_conf, uint32_t clkHZ, bool verbose): + FTDIpp_MPSSE(cable.vid, cable.pid, cable.interface, clkHZ, verbose), + _bitmode(0), _nb_bit(0) +{ + init_internal(cable, pin_conf); +} + +FtdiJtagBitBang::~FtdiJtagBitBang() +{ + free(_in_buf); +} + +void FtdiJtagBitBang::init_internal(const FTDIpp_MPSSE::mpsse_bit_config &cable, + const jtag_pins_conf_t *pin_conf) +{ + _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); + + _buffer_size = 128; // TX Fifo size + + _in_buf = (unsigned char *)malloc(sizeof(unsigned char) * _buffer_size); + bzero(_in_buf, _buffer_size); + init(5, _tck_pin | _tms_pin | _tdi_pin, BITMODE_BITBANG, + (FTDIpp_MPSSE::mpsse_bit_config &)cable); + setBitmode(BITMODE_BITBANG); +} + +int FtdiJtagBitBang::setBitmode(uint8_t mode) +{ + if (_bitmode == mode) + return 0; + _bitmode = mode; + return ftdi_set_bitmode(_ftdi, _tck_pin | _tms_pin | _tdi_pin, _bitmode); +} + +/** + * store tms in + * internal buffer + */ +int FtdiJtagBitBang::storeTMS(uint8_t *tms, int nb_bit, uint8_t tdi, bool read) +{ + (void) read; + int xfer_len = nb_bit; + /* need to check for available space in buffer */ + if (nb_bit == 0) + return 0; + + while (xfer_len > 0) { + int xfer = xfer_len; + if ((_nb_bit + 2*xfer) > _buffer_size) + xfer = (_buffer_size - _nb_bit) >> 1; + + for (int i = 0; i < xfer; i++, _nb_bit += 2) { + _in_buf[_nb_bit] = ((tdi)?_tdi_pin:0) | + (((tms[i >> 3] >> (i & 0x7)) & 0x01)? _tms_pin:0); + _in_buf[_nb_bit + 1] = _in_buf[_nb_bit] | _tck_pin; + } + + xfer_len -= xfer; + if (xfer_len != 0) + write(NULL); + } + return nb_bit; +} + +int FtdiJtagBitBang::writeTMS(uint8_t *tdo, int len) +{ + (void) len; + return write(tdo); +} + +/** + * store tdi in + * internal buffer with tms + * size must be <= 8 + */ +int FtdiJtagBitBang::storeTDI(uint8_t tdi, int nb_bit, bool read) +{ + for (int i = 0; i < nb_bit; i++, _nb_bit += 2) { + _in_buf[_nb_bit] = + ((tdi & (1 << (i & 0x7)))?_tdi_pin:0); + _in_buf[_nb_bit + 1] = _in_buf[_nb_bit] | _tck_pin; + } + return nb_bit; +} + +/** + * store tdi in + * internal buffer + * since TDI is used in shiftDR and shiftIR, tms is always set to 0 + */ +int FtdiJtagBitBang::storeTDI(uint8_t *tdi, int nb_byte, bool read) +{ + /* need to check for available space in buffer */ + for (int i = 0; i < nb_byte * 8; i++, _nb_bit += 2) { + _in_buf[_nb_bit] = + ((tdi[i >> 3] & (1 << (i & 0x7)))?_tdi_pin:0); + _in_buf[_nb_bit + 1] = _in_buf[_nb_bit] | _tck_pin; + } + return nb_byte; +} + +int FtdiJtagBitBang::writeTDI(uint8_t *tdo, int nb_bit) +{ + (void) nb_bit; + return write(tdo); +} + + +int FtdiJtagBitBang::write(uint8_t *tdo) +{ + int ret = 0; + if (_nb_bit == 0) + return 0; + + setBitmode((tdo) ? BITMODE_SYNCBB : BITMODE_BITBANG); + + ret = ftdi_write_data(_ftdi, _in_buf, _nb_bit); + if (ret != _nb_bit) + printf("problem %d written\n", ret); + + if (tdo) { + ret = ftdi_read_data(_ftdi, _in_buf, _nb_bit); + if (ret != _nb_bit) + printf("problem %d read\n", 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 + * */ + for (int i = 1, offset=0; i < _nb_bit; i+=2, offset++) { + tdo[offset >> 3] = (((_in_buf[i] & _tdo_pin) ? 0x80 : 0x00) | + (tdo[offset >> 3] >> 1)); + } + } + _nb_bit = 0; + return ret; +} diff --git a/src/ftdiJtagBitbang.hpp b/src/ftdiJtagBitbang.hpp new file mode 100644 index 0000000..41ff9d2 --- /dev/null +++ b/src/ftdiJtagBitbang.hpp @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2020 Gwenhael Goavec-Merou + * + * 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 3 of the License, or + * (at your option) any later version. + * + * 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 . + */ + +#ifndef FTDIJTAGBITBANG_H +#define FTDIJTAGBITBANG_H +#include +#include +#include +#include + +#include "board.hpp" +#include "jtagInterface.hpp" +#include "ftdipp_mpsse.hpp" + +/*! + * \file FtdiJtagBitBang.hpp + * \class FtdiJtagBitBang + * \brief concrete class between jtag implementation and FTDI capable bitbang mode + * \author Gwenhael Goavec-Merou + */ + +class FtdiJtagBitBang : public JtagInterface, private FTDIpp_MPSSE { + public: + FtdiJtagBitBang(const FTDIpp_MPSSE::mpsse_bit_config &cable, + const jtag_pins_conf_t *pin_conf, std::string dev, + uint32_t clkHZ, bool verbose = false); + FtdiJtagBitBang(const FTDIpp_MPSSE::mpsse_bit_config &cable, + const jtag_pins_conf_t *pin_conf, uint32_t clkHZ, bool verbose); + virtual ~FtdiJtagBitBang(); + + int setClkFreq(uint32_t clkHZ) override { + return FTDIpp_MPSSE::setClkFreq(clkHZ); + } + int setClkFreq(uint32_t clkHZ, char use_divide_by_5) override { + return FTDIpp_MPSSE::setClkFreq(clkHZ, use_divide_by_5);} + + /* TMS */ + int storeTMS(uint8_t *tms, int _bit_len, uint8_t tdi = 1, + bool read = false) override; + int writeTMS(uint8_t *tdo, int len = 0) override; + + /* TDI */ + int storeTDI(uint8_t tdi, int nb_bit, bool read) override; + int storeTDI(uint8_t *tdi, int nb_byte, bool read) override; + int writeTDI(uint8_t *tdo, int nb_bit) override; + + /*! + * \brief return internal buffer size (in byte). + * \return _buffer_size divided by 2 (two byte for clk) and divided by 8 (one + * state == one byte) + */ + int get_buffer_size() override { return _buffer_size/8/2; } + + bool isFull() override { return _nb_bit == 8*get_buffer_size();} + + private: + void init_internal(const FTDIpp_MPSSE::mpsse_bit_config &cable, + const jtag_pins_conf_t *pin_conf); + int write(uint8_t *tdo); + int setBitmode(uint8_t mode); + uint8_t *_in_buf; + + uint8_t _bitmode; + uint8_t _tck_pin; /*!< tck pin: 1 << pin id */ + uint8_t _tms_pin; /*!< tms pin: 1 << pin id */ + uint8_t _tdo_pin; /*!< tdo pin: 1 << pin id */ + uint8_t _tdi_pin; /*!< tdi pin: 1 << pin id */ + int _nb_bit; +}; +#endif