ftdiJtagBitbang: wrapper for bitbanging with ftdi devices
This commit is contained in:
parent
84298839ea
commit
e8bff2d2f2
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
Loading…
Reference in New Issue