ftdiJtagBitbang: wrapper for bitbanging with ftdi devices

This commit is contained in:
Gwenhael Goavec-Merou 2020-03-07 11:43:18 +01:00
parent 84298839ea
commit e8bff2d2f2
3 changed files with 276 additions and 0 deletions

View File

@ -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

190
src/ftdiJtagBitbang.cpp Normal file
View File

@ -0,0 +1,190 @@
/*
* Copyright (C) 2020 Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#include <libusb.h>
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <map>
#include <vector>
#include <string>
#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;
}

84
src/ftdiJtagBitbang.hpp Normal file
View File

@ -0,0 +1,84 @@
/*
* Copyright (C) 2020 Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#ifndef FTDIJTAGBITBANG_H
#define FTDIJTAGBITBANG_H
#include <ftdi.h>
#include <iostream>
#include <string>
#include <vector>
#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