Merge remote-tracking branch 'origin/ftdi_bitbang'
This commit is contained in:
commit
df226e16e9
|
|
@ -25,7 +25,9 @@ set(OPENFPGALOADER_SOURCE
|
||||||
src/svf_jtag.cpp
|
src/svf_jtag.cpp
|
||||||
src/jedParser.cpp
|
src/jedParser.cpp
|
||||||
src/display.cpp
|
src/display.cpp
|
||||||
src/ftdijtag.cpp
|
src/jtag.cpp
|
||||||
|
src/ftdiJtagBitbang.cpp
|
||||||
|
src/ftdiJtagMPSSE.cpp
|
||||||
src/configBitstreamParser.cpp
|
src/configBitstreamParser.cpp
|
||||||
src/ftdipp_mpsse.cpp
|
src/ftdipp_mpsse.cpp
|
||||||
src/xilinx.cpp
|
src/xilinx.cpp
|
||||||
|
|
@ -46,7 +48,10 @@ set(OPENFPGALOADER_HEADERS
|
||||||
src/altera.hpp
|
src/altera.hpp
|
||||||
src/progressBar.hpp
|
src/progressBar.hpp
|
||||||
src/bitparser.hpp
|
src/bitparser.hpp
|
||||||
src/ftdijtag.hpp
|
src/ftdiJtagBitbang.hpp
|
||||||
|
src/ftdiJtagMPSSE.hpp
|
||||||
|
src/jtag.hpp
|
||||||
|
src/jtagInterface.hpp
|
||||||
src/fsparser.hpp
|
src/fsparser.hpp
|
||||||
src/part.hpp
|
src/part.hpp
|
||||||
src/board.hpp
|
src/board.hpp
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
|
#include <string.h>
|
||||||
#include "altera.hpp"
|
#include "altera.hpp"
|
||||||
#include "ftdijtag.hpp"
|
#include "jtag.hpp"
|
||||||
#include "device.hpp"
|
#include "device.hpp"
|
||||||
#include "epcq.hpp"
|
#include "epcq.hpp"
|
||||||
|
|
||||||
|
|
@ -8,7 +9,7 @@
|
||||||
// DATA_DIR is defined at compile time.
|
// DATA_DIR is defined at compile time.
|
||||||
#define BIT_FOR_FLASH (DATA_DIR "/openFPGALoader/test_sfl.svf")
|
#define BIT_FOR_FLASH (DATA_DIR "/openFPGALoader/test_sfl.svf")
|
||||||
|
|
||||||
Altera::Altera(FtdiJtag *jtag, std::string filename, bool verbose):
|
Altera::Altera(Jtag *jtag, std::string filename, bool verbose):
|
||||||
Device(jtag, filename, verbose), _svf(_jtag, _verbose)
|
Device(jtag, filename, verbose), _svf(_jtag, _verbose)
|
||||||
{
|
{
|
||||||
if (_filename != "") {
|
if (_filename != "") {
|
||||||
|
|
@ -24,10 +25,10 @@ void Altera::reset()
|
||||||
{
|
{
|
||||||
/* PULSE_NCONFIG */
|
/* PULSE_NCONFIG */
|
||||||
unsigned char tx_buff[2] = {0x01, 0x00};
|
unsigned char tx_buff[2] = {0x01, 0x00};
|
||||||
_jtag->set_state(FtdiJtag::TEST_LOGIC_RESET);
|
_jtag->set_state(Jtag::TEST_LOGIC_RESET);
|
||||||
_jtag->shiftIR(tx_buff, NULL, IRLENGTH);
|
_jtag->shiftIR(tx_buff, NULL, IRLENGTH);
|
||||||
_jtag->toggleClk(1);
|
_jtag->toggleClk(1);
|
||||||
_jtag->set_state(FtdiJtag::TEST_LOGIC_RESET);
|
_jtag->set_state(Jtag::TEST_LOGIC_RESET);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Altera::program(unsigned int offset)
|
void Altera::program(unsigned int offset)
|
||||||
|
|
@ -45,7 +46,7 @@ void Altera::program(unsigned int offset)
|
||||||
_svf.parse(_filename);
|
_svf.parse(_filename);
|
||||||
} else if (_mode == Device::SPI_MODE) {
|
} else if (_mode == Device::SPI_MODE) {
|
||||||
/* GGM: TODO: fix this issue */
|
/* GGM: TODO: fix this issue */
|
||||||
EPCQ epcq(_jtag->vid(), _jtag->pid(), 2, 6000000);
|
EPCQ epcq(0x403, 0x6010/*_jtag->vid(), _jtag->pid()*/, 2, 6000000);
|
||||||
_svf.parse(BIT_FOR_FLASH);
|
_svf.parse(BIT_FOR_FLASH);
|
||||||
epcq.program(offset, _filename, (_file_extension == "rpd")? true:false);
|
epcq.program(offset, _filename, (_file_extension == "rpd")? true:false);
|
||||||
reset();
|
reset();
|
||||||
|
|
@ -53,11 +54,12 @@ void Altera::program(unsigned int offset)
|
||||||
}
|
}
|
||||||
int Altera::idCode()
|
int Altera::idCode()
|
||||||
{
|
{
|
||||||
unsigned char tx_data = IDCODE;
|
unsigned char tx_data[4] = {IDCODE};
|
||||||
unsigned char rx_data[4];
|
unsigned char rx_data[4];
|
||||||
_jtag->go_test_logic_reset();
|
_jtag->go_test_logic_reset();
|
||||||
_jtag->shiftIR(&tx_data, NULL, IRLENGTH);
|
_jtag->shiftIR(tx_data, NULL, IRLENGTH);
|
||||||
_jtag->shiftDR(NULL, rx_data, 32);
|
bzero(tx_data, 4);
|
||||||
|
_jtag->shiftDR(tx_data, rx_data, 32);
|
||||||
return ((rx_data[0] & 0x000000ff) |
|
return ((rx_data[0] & 0x000000ff) |
|
||||||
((rx_data[1] << 8) & 0x0000ff00) |
|
((rx_data[1] << 8) & 0x0000ff00) |
|
||||||
((rx_data[2] << 16) & 0x00ff0000) |
|
((rx_data[2] << 16) & 0x00ff0000) |
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,12 @@
|
||||||
|
|
||||||
#include "bitparser.hpp"
|
#include "bitparser.hpp"
|
||||||
#include "device.hpp"
|
#include "device.hpp"
|
||||||
#include "ftdijtag.hpp"
|
#include "jtag.hpp"
|
||||||
#include "svf_jtag.hpp"
|
#include "svf_jtag.hpp"
|
||||||
|
|
||||||
class Altera: public Device {
|
class Altera: public Device {
|
||||||
public:
|
public:
|
||||||
Altera(FtdiJtag *jtag, std::string filename, bool verbose);
|
Altera(Jtag *jtag, std::string filename, bool verbose);
|
||||||
~Altera();
|
~Altera();
|
||||||
|
|
||||||
void program(unsigned int offset = 0);
|
void program(unsigned int offset = 0);
|
||||||
|
|
|
||||||
|
|
@ -3,16 +3,51 @@
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
static std::map <std::string, std::string > board_list = {
|
#include "cable.hpp"
|
||||||
{"arty", "digilent"},
|
|
||||||
{"colorlight", ""},
|
/* AN_232R-01_Bit_Bang_Mode_Available_For_FT232R_and_Ft245R */
|
||||||
{"cyc1000", "ft2232"},
|
enum {
|
||||||
{"de0nano", "usbblaster"},
|
FT232RL_TXD = 0,
|
||||||
{"ecp5_evn", "ft2232"},
|
FT232RL_RXD = 1,
|
||||||
{"machXO3SK", "ft2232"},
|
FT232RL_RTS = 2,
|
||||||
{"littleBee", "ft2232"},
|
FT232RL_CTS = 3,
|
||||||
{"spartanEdgeAccelBoard", ""},
|
FT232RL_DTR = 4,
|
||||||
{"tangnano", "ft2232"}
|
FT232RL_DSR = 5,
|
||||||
|
FT232RL_DCD = 6,
|
||||||
|
FT232RL_RI = 7
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief for bitbang mode this structure provide offset for each JTAG signals
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
uint8_t tms_pin; /*! TMS pin offset */
|
||||||
|
uint8_t tck_pin; /*! TCK pin offset */
|
||||||
|
uint8_t tdi_pin; /*! TDI pin offset */
|
||||||
|
uint8_t tdo_pin; /*! TDO pin offset */
|
||||||
|
} jtag_pins_conf_t;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief a board has a target cable and optionnally a pin configuration
|
||||||
|
* (bitbang mode)
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
std::string cable_name; /*! provide name of one entry in cable_list */
|
||||||
|
jtag_pins_conf_t pins_config; /*! for bitbang, provide struct with pins offset */
|
||||||
|
} target_cable_t;
|
||||||
|
|
||||||
|
static std::map <std::string, target_cable_t> board_list = {
|
||||||
|
{"arty", {"digilent", {}}},
|
||||||
|
{"colorlight", {"", {}}},
|
||||||
|
{"cyc1000", {"ft2232", {}}},
|
||||||
|
{"de0nano", {"usbblaster", {}}},
|
||||||
|
{"ecp5_evn", {"ft2232", {}}},
|
||||||
|
{"machXO3SK", {"ft2232", {}}},
|
||||||
|
{"littleBee", {"ft2232", {}}},
|
||||||
|
{"spartanEdgeAccelBoard", {"",{}}},
|
||||||
|
{"tangnano", {"ft2232", {}}},
|
||||||
|
{"ulx2s", {"ft232RL", {FT232RL_RI, FT232RL_DSR, FT232RL_CTS, FT232RL_DCD}}},
|
||||||
|
{"ulx3s", {"ft231X", {FT232RL_DCD, FT232RL_DSR, FT232RL_RI, FT232RL_CTS}}}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -2,16 +2,32 @@
|
||||||
#define CABLE_HPP
|
#define CABLE_HPP
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include "ftdipp_mpsse.hpp"
|
#include "ftdipp_mpsse.hpp"
|
||||||
|
|
||||||
static std::map <std::string, FTDIpp_MPSSE::mpsse_bit_config > cable_list = {
|
/*!
|
||||||
{"digilent", {0x0403, 0x6010, 0xe8, 0xeb, 0x00, 0x60}},
|
* \brief define type of communication
|
||||||
{"digilent_hs2", {0x0403, 0x6014, 0xe8, 0xeb, 0x00, 0x60}},
|
*/
|
||||||
{"digilent_hs3", {0x0403, 0x6014, 0x88, 0x8B, 0x20, 0x30}},
|
enum {
|
||||||
{"ft232", {0x0403, 0x6014, 0x08, 0x0B, 0x08, 0x0B}},
|
MODE_FTDI_BITBANG = 0, /*! used with ft232RL/ft231x */
|
||||||
{"ft2232", {0x0403, 0x6010, 0x08, 0x0B, 0x08, 0x0B}},
|
MODE_FTDI_SERIAL = 1 /*! ft2232, ft232H */
|
||||||
{"ft4232", {0x0403, 0x6011, 0x08, 0x0B, 0x08, 0x0B}},
|
} communication_type_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int type;
|
||||||
|
FTDIpp_MPSSE::mpsse_bit_config config;
|
||||||
|
} cable_t;
|
||||||
|
|
||||||
|
static std::map <std::string, cable_t> cable_list = {
|
||||||
|
{"digilent", {MODE_FTDI_SERIAL, {0x0403, 0x6010, INTERFACE_A, 0xe8, 0xeb, 0x00, 0x60}}},
|
||||||
|
{"digilent_hs2", {MODE_FTDI_SERIAL, {0x0403, 0x6014, INTERFACE_A, 0xe8, 0xeb, 0x00, 0x60}}},
|
||||||
|
{"digilent_hs3", {MODE_FTDI_SERIAL, {0x0403, 0x6014, INTERFACE_A, 0x88, 0x8B, 0x20, 0x30}}},
|
||||||
|
{"ft2232", {MODE_FTDI_SERIAL, {0x0403, 0x6010, INTERFACE_A, 0x08, 0x0B, 0x08, 0x0B}}},
|
||||||
|
{"ft231X", {MODE_FTDI_BITBANG, {0x0403, 0x6015, INTERFACE_A, 0x00, 0x00, 0x00, 0x00}}},
|
||||||
|
{"ft232", {MODE_FTDI_SERIAL, {0x0403, 0x6014, INTERFACE_A, 0x08, 0x0B, 0x08, 0x0B}}},
|
||||||
|
{"ft232RL", {MODE_FTDI_BITBANG, {0x0403, 0x6001, INTERFACE_A, 0x08, 0x0B, 0x08, 0x0B}}},
|
||||||
|
{"ft4232", {MODE_FTDI_SERIAL, {0x0403, 0x6011, INTERFACE_A, 0x08, 0x0B, 0x08, 0x0B}}}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
Device::Device(FtdiJtag *jtag, string filename, bool verbose):
|
Device::Device(Jtag *jtag, string filename, bool verbose):
|
||||||
_filename(filename),
|
_filename(filename),
|
||||||
_file_extension(filename.substr(filename.find_last_of(".") +1)),
|
_file_extension(filename.substr(filename.find_last_of(".") +1)),
|
||||||
_mode(NONE_MODE), _verbose(verbose)
|
_mode(NONE_MODE), _verbose(verbose)
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include "ftdijtag.hpp"
|
#include "jtag.hpp"
|
||||||
|
|
||||||
/* GGM: TODO: program must have an optional
|
/* GGM: TODO: program must have an optional
|
||||||
* offset
|
* offset
|
||||||
|
|
@ -18,13 +18,13 @@ class Device {
|
||||||
FLASH_MODE = 1,
|
FLASH_MODE = 1,
|
||||||
MEM_MODE = 2
|
MEM_MODE = 2
|
||||||
};
|
};
|
||||||
Device(FtdiJtag *jtag, std::string filename, bool verbose = false);
|
Device(Jtag *jtag, std::string filename, bool verbose = false);
|
||||||
virtual ~Device();
|
virtual ~Device();
|
||||||
virtual void program(unsigned int offset = 0) = 0;
|
virtual void program(unsigned int offset = 0) = 0;
|
||||||
virtual int idCode() = 0;
|
virtual int idCode() = 0;
|
||||||
virtual void reset();
|
virtual void reset();
|
||||||
protected:
|
protected:
|
||||||
FtdiJtag *_jtag;
|
Jtag *_jtag;
|
||||||
std::string _filename;
|
std::string _filename;
|
||||||
std::string _file_extension;
|
std::string _file_extension;
|
||||||
enum prog_mode _mode;
|
enum prog_mode _mode;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,195 @@
|
||||||
|
/*
|
||||||
|
* 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(cable, dev, 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, 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(1, _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;
|
||||||
|
|
||||||
|
int ret = ftdi_set_bitmode(_ftdi, _tck_pin | _tms_pin | _tdi_pin, _bitmode);
|
||||||
|
ftdi_usb_purge_rx_buffer(_ftdi);
|
||||||
|
ftdi_usb_purge_tx_buffer(_ftdi);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
|
@ -0,0 +1,212 @@
|
||||||
|
/*
|
||||||
|
* 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 "ftdiJtagMPSSE.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
|
||||||
|
|
||||||
|
FtdiJtagMPSSE::FtdiJtagMPSSE(const FTDIpp_MPSSE::mpsse_bit_config &cable,
|
||||||
|
string dev, uint32_t clkHZ, bool verbose):
|
||||||
|
FTDIpp_MPSSE(cable, dev, clkHZ, verbose), _ch552WA(false)
|
||||||
|
{
|
||||||
|
init_internal(cable);
|
||||||
|
}
|
||||||
|
|
||||||
|
FtdiJtagMPSSE::FtdiJtagMPSSE(const FTDIpp_MPSSE::mpsse_bit_config &cable,
|
||||||
|
uint32_t clkHZ, bool verbose):
|
||||||
|
FTDIpp_MPSSE(cable, clkHZ, verbose),
|
||||||
|
_ch552WA(false)
|
||||||
|
{
|
||||||
|
init_internal(cable);
|
||||||
|
}
|
||||||
|
|
||||||
|
FtdiJtagMPSSE::~FtdiJtagMPSSE()
|
||||||
|
{
|
||||||
|
int read;
|
||||||
|
/* Before shutdown, we must wait until everything is shifted out
|
||||||
|
* Do this by temporary enabling loopback mode, write something
|
||||||
|
* and wait until we can read it back
|
||||||
|
*/
|
||||||
|
static unsigned char tbuf[16] = { SET_BITS_LOW, 0xff, 0x00,
|
||||||
|
SET_BITS_HIGH, 0xff, 0x00,
|
||||||
|
LOOPBACK_START,
|
||||||
|
MPSSE_DO_READ |
|
||||||
|
MPSSE_DO_WRITE | MPSSE_WRITE_NEG | MPSSE_LSB,
|
||||||
|
0x04, 0x00,
|
||||||
|
0xaa, 0x55, 0x00, 0xff, 0xaa,
|
||||||
|
LOOPBACK_END
|
||||||
|
};
|
||||||
|
mpsse_store(tbuf, 16);
|
||||||
|
read = mpsse_read(tbuf, 5);
|
||||||
|
if (read != 5)
|
||||||
|
fprintf(stderr,
|
||||||
|
"Loopback failed, expect problems on later runs %d\n", read);
|
||||||
|
|
||||||
|
free(_in_buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FtdiJtagMPSSE::init_internal(const FTDIpp_MPSSE::mpsse_bit_config &cable)
|
||||||
|
{
|
||||||
|
/* search for iProduct -> need to have
|
||||||
|
* ftdi->usb_dev (libusb_device_handler) -> libusb_device ->
|
||||||
|
* libusb_device_descriptor
|
||||||
|
*/
|
||||||
|
struct libusb_device * usb_dev = libusb_get_device(_ftdi->usb_dev);
|
||||||
|
struct libusb_device_descriptor usb_desc;
|
||||||
|
unsigned char iProduct[200];
|
||||||
|
libusb_get_device_descriptor(usb_dev, &usb_desc);
|
||||||
|
libusb_get_string_descriptor_ascii(_ftdi->usb_dev, usb_desc.iProduct,
|
||||||
|
iProduct, 200);
|
||||||
|
|
||||||
|
display("iProduct : %s\n", iProduct);
|
||||||
|
|
||||||
|
if (!strncmp((const char *)iProduct, "Sipeed-Debug", 12)) {
|
||||||
|
_ch552WA = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
display("%x\n", cable.bit_low_val);
|
||||||
|
display("%x\n", cable.bit_low_dir);
|
||||||
|
display("%x\n", cable.bit_high_val);
|
||||||
|
display("%x\n", cable.bit_high_dir);
|
||||||
|
|
||||||
|
_in_buf = (unsigned char *)malloc(sizeof(unsigned char) * _buffer_size);
|
||||||
|
bzero(_in_buf, _buffer_size);
|
||||||
|
init(5, 0xfb, BITMODE_MPSSE, (FTDIpp_MPSSE::mpsse_bit_config &)cable);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* store tms in
|
||||||
|
* internal buffer
|
||||||
|
* size must be <= 8
|
||||||
|
*/
|
||||||
|
int FtdiJtagMPSSE::storeTMS(uint8_t *tms, int nb_bit, uint8_t tdi, bool read)
|
||||||
|
{
|
||||||
|
int xfer, pos = 0, tx = nb_bit;
|
||||||
|
unsigned char buf[3]= {static_cast<unsigned char>(MPSSE_WRITE_TMS | MPSSE_LSB |
|
||||||
|
MPSSE_BITMODE | MPSSE_WRITE_NEG | ((read) ? MPSSE_DO_READ : 0)),
|
||||||
|
static_cast<unsigned char>(0),
|
||||||
|
static_cast<unsigned char>(0)};
|
||||||
|
|
||||||
|
if (nb_bit == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
display("%s: %d %s %d %d %x\n", __func__, tdi, (read) ? "true" : "false",
|
||||||
|
nb_bit, (nb_bit / 6) * 3, tms[0]);
|
||||||
|
int plop = 0;
|
||||||
|
|
||||||
|
while (nb_bit != 0) {
|
||||||
|
xfer = (nb_bit > 6) ? 6 : nb_bit;
|
||||||
|
buf[1] = xfer - 1;
|
||||||
|
buf[2] = (tdi)?0x80 : 0x00;
|
||||||
|
for (int i = 0; i < xfer; i++, pos++) {
|
||||||
|
buf[2] |=
|
||||||
|
(((tms[pos >> 3] & (1 << (pos & 0x07))) ? 1 : 0) << i);
|
||||||
|
}
|
||||||
|
nb_bit -= xfer;
|
||||||
|
mpsse_store(buf, 3);
|
||||||
|
plop += 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
return tx;
|
||||||
|
}
|
||||||
|
|
||||||
|
int FtdiJtagMPSSE::writeTMS(uint8_t *tdo, int len)
|
||||||
|
{
|
||||||
|
display("%s %s %d %d\n", __func__, (tdo)?"true":"false", len, (len/8)+1);
|
||||||
|
|
||||||
|
if (tdo) {
|
||||||
|
return mpsse_read(tdo, (len/8)+1);
|
||||||
|
} else {
|
||||||
|
int ret = mpsse_write();
|
||||||
|
if (_ch552WA) {
|
||||||
|
uint8_t c[len/8+1];
|
||||||
|
ftdi_read_data(_ftdi, c, len/8+1);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* store tdi in internal buffer
|
||||||
|
* size must be <= 8
|
||||||
|
*/
|
||||||
|
int FtdiJtagMPSSE::storeTDI(uint8_t tdi, int nb_bit, bool read)
|
||||||
|
{
|
||||||
|
unsigned char tx_buf[3] = {(unsigned char)(MPSSE_LSB | MPSSE_WRITE_NEG |
|
||||||
|
MPSSE_DO_WRITE | MPSSE_BITMODE | ((read) ? MPSSE_DO_READ : 0)),
|
||||||
|
static_cast<unsigned char>(nb_bit - 1),
|
||||||
|
tdi};
|
||||||
|
mpsse_store(tx_buf, 3);
|
||||||
|
|
||||||
|
return nb_bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* store tdi in internal buffer
|
||||||
|
*/
|
||||||
|
int FtdiJtagMPSSE::storeTDI(uint8_t *tdi, int nb_byte, bool read)
|
||||||
|
{
|
||||||
|
unsigned char tx_buf[3] = {(unsigned char)(MPSSE_LSB | MPSSE_WRITE_NEG |
|
||||||
|
MPSSE_DO_WRITE |
|
||||||
|
((read) ? MPSSE_DO_READ : 0)),
|
||||||
|
static_cast<unsigned char>((nb_byte - 1) & 0xff),
|
||||||
|
static_cast<unsigned char>(((nb_byte - 1) >> 8) & 0xff)};
|
||||||
|
mpsse_store(tx_buf, 3);
|
||||||
|
mpsse_store(tdi, nb_byte);
|
||||||
|
|
||||||
|
return nb_byte;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* flush buffer
|
||||||
|
* if tdo is not null read nb_bit
|
||||||
|
*/
|
||||||
|
int FtdiJtagMPSSE::writeTDI(uint8_t *tdo, int nb_bit)
|
||||||
|
{
|
||||||
|
int nb_byte = (nb_bit < 8)? 1: (nb_bit >> 3);
|
||||||
|
|
||||||
|
if (tdo) {
|
||||||
|
return mpsse_read(tdo, nb_byte);
|
||||||
|
} else {
|
||||||
|
int ret = mpsse_write();
|
||||||
|
if (_ch552WA) {
|
||||||
|
uint8_t c[nb_byte];
|
||||||
|
ftdi_read_data(_ftdi, c, nb_byte);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
/*
|
||||||
|
* 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 FTDIJTAGMPSSE_H
|
||||||
|
#define FTDIJTAGMPSSE_H
|
||||||
|
#include <ftdi.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "jtagInterface.hpp"
|
||||||
|
#include "ftdipp_mpsse.hpp"
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \file FtdiJtagMPSSE.hpp
|
||||||
|
* \class FtdiJtagMPSSE
|
||||||
|
* \brief concrete class between jtag implementation and FTDI capable bitbang mode
|
||||||
|
* \author Gwenhael Goavec-Merou
|
||||||
|
*/
|
||||||
|
|
||||||
|
class FtdiJtagMPSSE : public JtagInterface, private FTDIpp_MPSSE {
|
||||||
|
public:
|
||||||
|
FtdiJtagMPSSE(const FTDIpp_MPSSE::mpsse_bit_config &cable, std::string dev,
|
||||||
|
uint32_t clkHZ, bool verbose = false);
|
||||||
|
FtdiJtagMPSSE(const FTDIpp_MPSSE::mpsse_bit_config &cable,
|
||||||
|
uint32_t clkHZ, bool verbose);
|
||||||
|
virtual ~FtdiJtagMPSSE();
|
||||||
|
|
||||||
|
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 nb_bit, 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 -3 for mpsse cmd + size, -1 for potential SEND_IMMEDIATE
|
||||||
|
*/
|
||||||
|
int get_buffer_size() override { return _buffer_size-3; }
|
||||||
|
|
||||||
|
bool isFull() override { return false;}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void init_internal(const FTDIpp_MPSSE::mpsse_bit_config &cable);
|
||||||
|
uint8_t *_in_buf;
|
||||||
|
bool _ch552WA; /* avoid errors with SiPeed tangNano */
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
@ -1,65 +0,0 @@
|
||||||
#ifndef FTDIJTAG_H
|
|
||||||
#define FTDIJTAG_H
|
|
||||||
#include <ftdi.h>
|
|
||||||
#include <iostream>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "ftdipp_mpsse.hpp"
|
|
||||||
|
|
||||||
class FtdiJtag : public FTDIpp_MPSSE {
|
|
||||||
public:
|
|
||||||
//FtdiJtag(std::string board_name, int vid, int pid, unsigned char interface, uint32_t clkHZ);
|
|
||||||
FtdiJtag(FTDIpp_MPSSE::mpsse_bit_config &cable, std::string dev,
|
|
||||||
unsigned char interface, uint32_t clkHZ, bool verbose = false);
|
|
||||||
FtdiJtag(FTDIpp_MPSSE::mpsse_bit_config &cable, unsigned char interface, uint32_t clkHZ,
|
|
||||||
bool verbose);
|
|
||||||
~FtdiJtag();
|
|
||||||
|
|
||||||
int detectChain(std::vector<int> &devices, int max_dev);
|
|
||||||
|
|
||||||
int shiftIR(unsigned char *tdi, unsigned char *tdo, int irlen, int end_state = RUN_TEST_IDLE);
|
|
||||||
int shiftIR(unsigned char tdi, int irlen, int end_state = RUN_TEST_IDLE);
|
|
||||||
int shiftDR(unsigned char *tdi, unsigned char *tdo, int drlen, int end_state = RUN_TEST_IDLE);
|
|
||||||
int read_write(unsigned char *tdi, unsigned char *tdo, int len, char last);
|
|
||||||
|
|
||||||
void toggleClk(int nb);
|
|
||||||
void go_test_logic_reset();
|
|
||||||
void set_state(int newState);
|
|
||||||
int flushTMS(bool flush_buffer = false);
|
|
||||||
void flush() {mpsse_write();}
|
|
||||||
void setTMS(unsigned char tms);
|
|
||||||
|
|
||||||
enum tapState_t {
|
|
||||||
TEST_LOGIC_RESET = 0,
|
|
||||||
RUN_TEST_IDLE = 1,
|
|
||||||
SELECT_DR_SCAN = 2,
|
|
||||||
CAPTURE_DR = 3,
|
|
||||||
SHIFT_DR = 4,
|
|
||||||
EXIT1_DR = 5,
|
|
||||||
PAUSE_DR = 6,
|
|
||||||
EXIT2_DR = 7,
|
|
||||||
UPDATE_DR = 8,
|
|
||||||
SELECT_IR_SCAN = 9,
|
|
||||||
CAPTURE_IR = 10,
|
|
||||||
SHIFT_IR = 11,
|
|
||||||
EXIT1_IR = 12,
|
|
||||||
PAUSE_IR = 13,
|
|
||||||
EXIT2_IR = 14,
|
|
||||||
UPDATE_IR = 15,
|
|
||||||
UNKNOWN = 999
|
|
||||||
};
|
|
||||||
const char *getStateName(tapState_t s);
|
|
||||||
|
|
||||||
/* utilities */
|
|
||||||
void setVerbose(bool verbose){_verbose=verbose;}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void init_internal(FTDIpp_MPSSE::mpsse_bit_config &cable);
|
|
||||||
int _state;
|
|
||||||
int _tms_buffer_size;
|
|
||||||
int _num_tms;
|
|
||||||
unsigned char *_tms_buffer;
|
|
||||||
std::string _board_name;
|
|
||||||
bool _ch552WA;
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
@ -18,14 +18,20 @@ using namespace std;
|
||||||
#define display(...) \
|
#define display(...) \
|
||||||
do { if (_verbose) fprintf(stdout, __VA_ARGS__);}while(0)
|
do { if (_verbose) fprintf(stdout, __VA_ARGS__);}while(0)
|
||||||
|
|
||||||
FTDIpp_MPSSE::FTDIpp_MPSSE(const string &dev, unsigned char interface,
|
FTDIpp_MPSSE::FTDIpp_MPSSE(const mpsse_bit_config &cable, const string &dev,
|
||||||
uint32_t clkHZ, bool verbose):_verbose(verbose), _vid(0),
|
uint32_t clkHZ, bool verbose):_verbose(verbose), _vid(0),
|
||||||
_pid(0), _bus(-1), _addr(-1), _product(""), _interface(interface),
|
_pid(0), _bus(-1), _addr(-1), _product(""),
|
||||||
|
_interface(cable.interface),
|
||||||
_clkHZ(clkHZ), _buffer_size(2*32768), _num(0)
|
_clkHZ(clkHZ), _buffer_size(2*32768), _num(0)
|
||||||
{
|
{
|
||||||
if (!search_with_dev(dev)) {
|
if (!dev.empty()) {
|
||||||
cerr << "No cable found" << endl;
|
if (!search_with_dev(dev)) {
|
||||||
throw std::exception();
|
cerr << "No cable found" << endl;
|
||||||
|
throw std::exception();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_vid = cable.vid;
|
||||||
|
_pid = cable.pid;
|
||||||
}
|
}
|
||||||
|
|
||||||
open_device(115200);
|
open_device(115200);
|
||||||
|
|
@ -38,10 +44,10 @@ FTDIpp_MPSSE::FTDIpp_MPSSE(const string &dev, unsigned char interface,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FTDIpp_MPSSE::FTDIpp_MPSSE(int vid, int pid, unsigned char interface,
|
FTDIpp_MPSSE::FTDIpp_MPSSE(const mpsse_bit_config &cable,
|
||||||
uint32_t clkHZ, bool verbose):_verbose(verbose), _vid(vid),
|
uint32_t clkHZ, bool verbose):_verbose(verbose),
|
||||||
_pid(pid), _bus(-1),
|
_vid(cable.vid), _pid(cable.pid), _bus(-1),
|
||||||
_addr(-1), _product(""), _interface(interface),
|
_addr(-1), _product(""), _interface(cable.interface),
|
||||||
_clkHZ(clkHZ), _buffer_size(2*32768), _num(0)
|
_clkHZ(clkHZ), _buffer_size(2*32768), _num(0)
|
||||||
{
|
{
|
||||||
open_device(115200);
|
open_device(115200);
|
||||||
|
|
@ -138,6 +144,7 @@ int FTDIpp_MPSSE::close_device()
|
||||||
|
|
||||||
|
|
||||||
int FTDIpp_MPSSE::init(unsigned char latency, unsigned char bitmask_mode,
|
int FTDIpp_MPSSE::init(unsigned char latency, unsigned char bitmask_mode,
|
||||||
|
unsigned char mode,
|
||||||
mpsse_bit_config & bit_conf)
|
mpsse_bit_config & bit_conf)
|
||||||
{
|
{
|
||||||
unsigned char buf_cmd[6] = { SET_BITS_LOW, 0, 0,
|
unsigned char buf_cmd[6] = { SET_BITS_LOW, 0, 0,
|
||||||
|
|
@ -153,6 +160,7 @@ int FTDIpp_MPSSE::init(unsigned char latency, unsigned char bitmask_mode,
|
||||||
cout << "bitmode_reset error" << endl;
|
cout << "bitmode_reset error" << endl;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ftdi_usb_purge_buffers(_ftdi) != 0) {
|
if (ftdi_usb_purge_buffers(_ftdi) != 0) {
|
||||||
cout << "reset error" << endl;
|
cout << "reset error" << endl;
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -161,25 +169,30 @@ int FTDIpp_MPSSE::init(unsigned char latency, unsigned char bitmask_mode,
|
||||||
cout << "reset error" << endl;
|
cout << "reset error" << endl;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
/* enable MPSSE mode */
|
/* enable mode */
|
||||||
if (ftdi_set_bitmode(_ftdi, bitmask_mode, BITMODE_MPSSE) < 0) {
|
if (ftdi_set_bitmode(_ftdi, bitmask_mode, mode) < 0) {
|
||||||
cout << "bitmode_mpsse error" << endl;
|
cout << "bitmode_mpsse error" << endl;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
if (mode == BITMODE_MPSSE) {
|
||||||
|
|
||||||
unsigned char buf1[5];
|
unsigned char buf1[5];
|
||||||
ftdi_read_data(_ftdi, buf1, 5);
|
ftdi_read_data(_ftdi, buf1, 5);
|
||||||
|
|
||||||
if (setClkFreq(_clkHZ, 0) < 0)
|
if (setClkFreq(_clkHZ, 0) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
buf_cmd[1] = bit_conf.bit_low_val; // 0xe8;
|
buf_cmd[1] = bit_conf.bit_low_val; // 0xe8;
|
||||||
buf_cmd[2] = bit_conf.bit_low_dir; // 0xeb;
|
buf_cmd[2] = bit_conf.bit_low_dir; // 0xeb;
|
||||||
|
|
||||||
buf_cmd[4] = bit_conf.bit_high_val; // 0x00;
|
buf_cmd[4] = bit_conf.bit_high_val; // 0x00;
|
||||||
buf_cmd[5] = bit_conf.bit_high_dir; // 0x60;
|
buf_cmd[5] = bit_conf.bit_high_dir; // 0x60;
|
||||||
mpsse_store(buf_cmd, 6);
|
mpsse_store(buf_cmd, 6);
|
||||||
mpsse_write();
|
mpsse_write();
|
||||||
|
}
|
||||||
|
|
||||||
|
ftdi_read_data_set_chunksize(_ftdi, _buffer_size);
|
||||||
|
ftdi_write_data_set_chunksize(_ftdi, _buffer_size);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -237,6 +250,7 @@ int FTDIpp_MPSSE::setClkFreq(uint32_t clkHZ, char use_divide_by_5)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
ret = ftdi_read_data(_ftdi, buffer, 4);
|
ret = ftdi_read_data(_ftdi, buffer, 4);
|
||||||
|
ftdi_usb_purge_buffers(_ftdi);
|
||||||
|
|
||||||
return real_freq;
|
return real_freq;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,22 +5,24 @@
|
||||||
|
|
||||||
class FTDIpp_MPSSE {
|
class FTDIpp_MPSSE {
|
||||||
public:
|
public:
|
||||||
FTDIpp_MPSSE(const std::string &dev, unsigned char interface,
|
|
||||||
uint32_t clkHZ, bool verbose = false);
|
|
||||||
FTDIpp_MPSSE(int vid, int pid, unsigned char interface,
|
|
||||||
uint32_t clkHZ, bool verbose = false);
|
|
||||||
~FTDIpp_MPSSE();
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int vid;
|
int vid;
|
||||||
int pid;
|
int pid;
|
||||||
|
int interface;
|
||||||
int bit_low_val;
|
int bit_low_val;
|
||||||
int bit_low_dir;
|
int bit_low_dir;
|
||||||
int bit_high_val;
|
int bit_high_val;
|
||||||
int bit_high_dir;
|
int bit_high_dir;
|
||||||
} mpsse_bit_config;
|
} mpsse_bit_config;
|
||||||
|
|
||||||
int init(unsigned char latency, unsigned char bitmask_mode, mpsse_bit_config &bit_conf);
|
FTDIpp_MPSSE(const mpsse_bit_config &cable, const std::string &dev,
|
||||||
|
uint32_t clkHZ, bool verbose = false);
|
||||||
|
FTDIpp_MPSSE(const mpsse_bit_config &cablen,
|
||||||
|
uint32_t clkHZ, bool verbose = false);
|
||||||
|
~FTDIpp_MPSSE();
|
||||||
|
|
||||||
|
int init(unsigned char latency, unsigned char bitmask_mode, unsigned char mode,
|
||||||
|
mpsse_bit_config &bit_conf);
|
||||||
int setClkFreq(uint32_t clkHZ);
|
int setClkFreq(uint32_t clkHZ);
|
||||||
int setClkFreq(uint32_t clkHZ, char use_divide_by_5);
|
int setClkFreq(uint32_t clkHZ, char use_divide_by_5);
|
||||||
|
|
||||||
|
|
@ -39,8 +41,6 @@ class FTDIpp_MPSSE {
|
||||||
unsigned int udevstufftoint(const char *udevstring, int base);
|
unsigned int udevstufftoint(const char *udevstring, int base);
|
||||||
bool search_with_dev(const std::string &device);
|
bool search_with_dev(const std::string &device);
|
||||||
bool _verbose;
|
bool _verbose;
|
||||||
struct ftdi_context *_ftdi;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int _vid;
|
int _vid;
|
||||||
int _pid;
|
int _pid;
|
||||||
|
|
@ -49,7 +49,10 @@ class FTDIpp_MPSSE {
|
||||||
char _product[64];
|
char _product[64];
|
||||||
unsigned char _interface;
|
unsigned char _interface;
|
||||||
int _clkHZ;
|
int _clkHZ;
|
||||||
|
protected:
|
||||||
|
struct ftdi_context *_ftdi;
|
||||||
int _buffer_size;
|
int _buffer_size;
|
||||||
|
private:
|
||||||
int _num;
|
int _num;
|
||||||
unsigned char *_buffer;
|
unsigned char *_buffer;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -62,16 +62,16 @@ void FtdiSpi::setMode(uint8_t mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
static FTDIpp_MPSSE::mpsse_bit_config bit_conf =
|
static FTDIpp_MPSSE::mpsse_bit_config bit_conf =
|
||||||
{0x08, 0x0B, 0x08, 0x0B};
|
{0x403, 0x6010, INTERFACE_B, 0x08, 0x0B, 0x08, 0x0B};
|
||||||
|
|
||||||
FtdiSpi::FtdiSpi(int vid, int pid, unsigned char interface, uint32_t clkHZ,
|
FtdiSpi::FtdiSpi(int vid, int pid, unsigned char interface, uint32_t clkHZ,
|
||||||
bool verbose):
|
bool verbose):
|
||||||
FTDIpp_MPSSE(vid, pid, interface, clkHZ, verbose)
|
FTDIpp_MPSSE(bit_conf, clkHZ, verbose)
|
||||||
{
|
{
|
||||||
setCSmode(SPI_CS_AUTO);
|
setCSmode(SPI_CS_AUTO);
|
||||||
setEndianness(SPI_MSB_FIRST);
|
setEndianness(SPI_MSB_FIRST);
|
||||||
|
|
||||||
init(1, 0x00, bit_conf);
|
init(1, 0x00, BITMODE_MPSSE, bit_conf);
|
||||||
}
|
}
|
||||||
FtdiSpi::~FtdiSpi()
|
FtdiSpi::~FtdiSpi()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include "ftdijtag.hpp"
|
#include "jtag.hpp"
|
||||||
#include "gowin.hpp"
|
#include "gowin.hpp"
|
||||||
#include "progressBar.hpp"
|
#include "progressBar.hpp"
|
||||||
#include "display.hpp"
|
#include "display.hpp"
|
||||||
|
|
@ -63,7 +63,7 @@ using namespace std;
|
||||||
#define EF_PROGRAM 0x71
|
#define EF_PROGRAM 0x71
|
||||||
#define EFLASH_ERASE 0x75
|
#define EFLASH_ERASE 0x75
|
||||||
|
|
||||||
Gowin::Gowin(FtdiJtag *jtag, const string filename, bool flash_wr, bool sram_wr,
|
Gowin::Gowin(Jtag *jtag, const string filename, bool flash_wr, bool sram_wr,
|
||||||
bool verbose): Device(jtag, filename, verbose)
|
bool verbose): Device(jtag, filename, verbose)
|
||||||
{
|
{
|
||||||
_fs = NULL;
|
_fs = NULL;
|
||||||
|
|
@ -372,7 +372,7 @@ bool Gowin::flashFLASH(uint8_t *data, int length)
|
||||||
progress.display(i);
|
progress.display(i);
|
||||||
}
|
}
|
||||||
/* 2.2.6.6 */
|
/* 2.2.6.6 */
|
||||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
||||||
|
|
||||||
progress.done();
|
progress.done();
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -390,25 +390,28 @@ bool Gowin::flashSRAM(uint8_t *data, int length)
|
||||||
wr_rd(XFER_WRITE, NULL, 0, NULL, 0);
|
wr_rd(XFER_WRITE, NULL, 0, NULL, 0);
|
||||||
|
|
||||||
/* 2.2.6.5 */
|
/* 2.2.6.5 */
|
||||||
_jtag->set_state(FtdiJtag::SHIFT_DR);
|
_jtag->set_state(Jtag::SHIFT_DR);
|
||||||
|
|
||||||
for (int i=0; i < byte_length; i+=256) {
|
int xfer_len = 256;
|
||||||
if (i + 256 > byte_length) { // last packet with some size
|
|
||||||
|
for (int i=0; i < byte_length; i+=xfer_len) {
|
||||||
|
if (i + xfer_len > byte_length) { // last packet with some size
|
||||||
tx_len = (byte_length - i) * 8;
|
tx_len = (byte_length - i) * 8;
|
||||||
tx_end = 1; // to move in EXIT1_DR
|
tx_end = 1; // to move in EXIT1_DR
|
||||||
} else {
|
} else {
|
||||||
tx_len = 256 * 8;
|
tx_len = xfer_len * 8;
|
||||||
tx_end = 0;
|
tx_end = 0;
|
||||||
}
|
}
|
||||||
_jtag->read_write(data+i, NULL, tx_len, tx_end);
|
_jtag->read_write(data+i, NULL, tx_len, tx_end);
|
||||||
_jtag->flush();
|
//_jtag->flush();
|
||||||
progress.display(i);
|
progress.display(i);
|
||||||
}
|
}
|
||||||
/* 2.2.6.6 */
|
/* 2.2.6.6 */
|
||||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
||||||
|
|
||||||
/* p.15 fig 2.11 */
|
/* p.15 fig 2.11 */
|
||||||
wr_rd(XFER_DONE, NULL, 0, NULL, 0);
|
wr_rd(XFER_DONE, NULL, 0, NULL, 0);
|
||||||
|
|
||||||
if (pollFlag(STATUS_DONE_FINAL, STATUS_DONE_FINAL)) {
|
if (pollFlag(STATUS_DONE_FINAL, STATUS_DONE_FINAL)) {
|
||||||
progress.done();
|
progress.done();
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -426,7 +429,7 @@ bool Gowin::eraseFLASH()
|
||||||
unsigned char tx[4] = {0, 0, 0, 0};
|
unsigned char tx[4] = {0, 0, 0, 0};
|
||||||
printInfo("erase Flash ", false);
|
printInfo("erase Flash ", false);
|
||||||
wr_rd(EFLASH_ERASE, NULL, 0, NULL, 0);
|
wr_rd(EFLASH_ERASE, NULL, 0, NULL, 0);
|
||||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
||||||
_jtag->shiftDR(tx, NULL, 32);
|
_jtag->shiftDR(tx, NULL, 32);
|
||||||
/* TN653 specifies to wait for 120ms with
|
/* TN653 specifies to wait for 120ms with
|
||||||
* there are no bit in status register to specify
|
* there are no bit in status register to specify
|
||||||
|
|
|
||||||
|
|
@ -25,12 +25,12 @@
|
||||||
|
|
||||||
#include "device.hpp"
|
#include "device.hpp"
|
||||||
#include "fsparser.hpp"
|
#include "fsparser.hpp"
|
||||||
#include "ftdijtag.hpp"
|
#include "jtag.hpp"
|
||||||
#include "jedParser.hpp"
|
#include "jedParser.hpp"
|
||||||
|
|
||||||
class Gowin: public Device {
|
class Gowin: public Device {
|
||||||
public:
|
public:
|
||||||
Gowin(FtdiJtag *jtag, std::string filename, bool flash_wr, bool sram_wr,
|
Gowin(Jtag *jtag, std::string filename, bool flash_wr, bool sram_wr,
|
||||||
bool verbose);
|
bool verbose);
|
||||||
~Gowin();
|
~Gowin();
|
||||||
int idCode() override;
|
int idCode() override;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,20 @@
|
||||||
|
/*
|
||||||
|
* 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 <libusb.h>
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
@ -7,8 +24,11 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "ftdijtag.hpp"
|
#include "display.hpp"
|
||||||
|
#include "jtag.hpp"
|
||||||
#include "ftdipp_mpsse.hpp"
|
#include "ftdipp_mpsse.hpp"
|
||||||
|
#include "ftdiJtagBitbang.hpp"
|
||||||
|
#include "ftdiJtagMPSSE.hpp"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
|
@ -43,81 +63,48 @@ using namespace std;
|
||||||
* - envoyer le dernier avec 0x4B ou 0x6B
|
* - envoyer le dernier avec 0x4B ou 0x6B
|
||||||
*/
|
*/
|
||||||
|
|
||||||
FtdiJtag::FtdiJtag(FTDIpp_MPSSE::mpsse_bit_config &cable, string dev,
|
Jtag::Jtag(cable_t &cable, const jtag_pins_conf_t *pin_conf, string dev,
|
||||||
unsigned char interface, uint32_t clkHZ, bool verbose):
|
uint32_t clkHZ, bool verbose):
|
||||||
FTDIpp_MPSSE(dev, interface, clkHZ, verbose),
|
_verbose(verbose),
|
||||||
_state(RUN_TEST_IDLE),
|
_state(RUN_TEST_IDLE),
|
||||||
_tms_buffer_size(128), _num_tms(0),
|
_tms_buffer_size(128), _num_tms(0),
|
||||||
_board_name("nope"), _ch552WA(false)
|
_board_name("nope")
|
||||||
{
|
{
|
||||||
init_internal(cable);
|
init_internal(cable, dev, pin_conf, clkHZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
FtdiJtag::FtdiJtag(FTDIpp_MPSSE::mpsse_bit_config &cable,
|
Jtag::Jtag(cable_t &cable, const jtag_pins_conf_t *pin_conf,
|
||||||
unsigned char interface, uint32_t clkHZ, bool verbose):
|
uint32_t clkHZ, bool verbose):
|
||||||
FTDIpp_MPSSE(cable.vid, cable.pid, interface, clkHZ, verbose),
|
_verbose(verbose),
|
||||||
_state(RUN_TEST_IDLE),
|
_state(RUN_TEST_IDLE),
|
||||||
_tms_buffer_size(128), _num_tms(0),
|
_tms_buffer_size(128), _num_tms(0),
|
||||||
_board_name("nope"), _ch552WA(false)
|
_board_name("nope")
|
||||||
{
|
{
|
||||||
init_internal(cable);
|
init_internal(cable, "", pin_conf, clkHZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
FtdiJtag::~FtdiJtag()
|
Jtag::~Jtag()
|
||||||
{
|
{
|
||||||
int read;
|
|
||||||
/* Before shutdown, we must wait until everything is shifted out
|
|
||||||
* Do this by temporary enabling loopback mode, write something
|
|
||||||
* and wait until we can read it back
|
|
||||||
* */
|
|
||||||
static unsigned char tbuf[16] = { SET_BITS_LOW, 0xff, 0x00,
|
|
||||||
SET_BITS_HIGH, 0xff, 0x00,
|
|
||||||
LOOPBACK_START,
|
|
||||||
MPSSE_DO_READ |
|
|
||||||
MPSSE_DO_WRITE | MPSSE_WRITE_NEG | MPSSE_LSB,
|
|
||||||
0x04, 0x00,
|
|
||||||
0xaa, 0x55, 0x00, 0xff, 0xaa,
|
|
||||||
LOOPBACK_END
|
|
||||||
};
|
|
||||||
mpsse_store(tbuf, 16);
|
|
||||||
read = mpsse_read(tbuf, 5);
|
|
||||||
if (read != 5)
|
|
||||||
fprintf(stderr,
|
|
||||||
"Loopback failed, expect problems on later runs %d\n", read);
|
|
||||||
|
|
||||||
free(_tms_buffer);
|
free(_tms_buffer);
|
||||||
|
delete _jtag;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FtdiJtag::init_internal(FTDIpp_MPSSE::mpsse_bit_config &cable)
|
void Jtag::init_internal(cable_t &cable, const string &dev,
|
||||||
|
const jtag_pins_conf_t *pin_conf, uint32_t clkHZ)
|
||||||
{
|
{
|
||||||
/* search for iProduct -> need to have
|
if (cable.type == MODE_FTDI_BITBANG) {
|
||||||
* ftdi->usb_dev (libusb_device_handler) -> libusb_device ->
|
if (pin_conf == NULL)
|
||||||
* libusb_device_descriptor
|
throw std::exception();
|
||||||
*/
|
_jtag = new FtdiJtagBitBang(cable.config, pin_conf, dev, clkHZ, _verbose);
|
||||||
struct libusb_device * usb_dev = libusb_get_device(_ftdi->usb_dev);
|
} else {
|
||||||
struct libusb_device_descriptor usb_desc;
|
_jtag = new FtdiJtagMPSSE(cable.config, dev, clkHZ, _verbose);
|
||||||
unsigned char iProduct[200];
|
|
||||||
libusb_get_device_descriptor(usb_dev, &usb_desc);
|
|
||||||
libusb_get_string_descriptor_ascii(_ftdi->usb_dev, usb_desc.iProduct,
|
|
||||||
iProduct, 200);
|
|
||||||
|
|
||||||
display("iProduct : %s\n", iProduct);
|
|
||||||
if (!strncmp((const char *)iProduct, "Sipeed-Debug", 12)) {
|
|
||||||
_ch552WA = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
display("board_name %s\n", _board_name.c_str());
|
|
||||||
display("%x\n", cable.bit_low_val);
|
|
||||||
display("%x\n", cable.bit_low_dir);
|
|
||||||
display("%x\n", cable.bit_high_val);
|
|
||||||
display("%x\n", cable.bit_high_dir);
|
|
||||||
|
|
||||||
_tms_buffer = (unsigned char *)malloc(sizeof(unsigned char) * _tms_buffer_size);
|
_tms_buffer = (unsigned char *)malloc(sizeof(unsigned char) * _tms_buffer_size);
|
||||||
bzero(_tms_buffer, _tms_buffer_size);
|
bzero(_tms_buffer, _tms_buffer_size);
|
||||||
init(5, 0xfb, cable);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int FtdiJtag::detectChain(vector<int> &devices, int max_dev)
|
int Jtag::detectChain(vector<int> &devices, int max_dev)
|
||||||
{
|
{
|
||||||
unsigned char rx_buff[4];
|
unsigned char rx_buff[4];
|
||||||
/* WA for CH552/tangNano: write is always mandatory */
|
/* WA for CH552/tangNano: write is always mandatory */
|
||||||
|
|
@ -140,9 +127,9 @@ int FtdiJtag::detectChain(vector<int> &devices, int max_dev)
|
||||||
return devices.size();
|
return devices.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FtdiJtag::setTMS(unsigned char tms)
|
void Jtag::setTMS(unsigned char tms)
|
||||||
{
|
{
|
||||||
display("%s %d %d\n", __func__, _num_tms, (_num_tms >> 3));
|
display("%s %x %d %d\n", __func__, tms, _num_tms, (_num_tms >> 3));
|
||||||
if (_num_tms+1 == _tms_buffer_size * 8)
|
if (_num_tms+1 == _tms_buffer_size * 8)
|
||||||
flushTMS();
|
flushTMS();
|
||||||
if (tms != 0)
|
if (tms != 0)
|
||||||
|
|
@ -158,38 +145,23 @@ void FtdiJtag::setTMS(unsigned char tms)
|
||||||
* -bit 7 is TDI state for each clk cycles
|
* -bit 7 is TDI state for each clk cycles
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int FtdiJtag::flushTMS(bool flush_buffer)
|
int Jtag::flushTMS(bool flush_buffer)
|
||||||
{
|
{
|
||||||
int xfer, pos = 0;
|
if (_num_tms != 0) {
|
||||||
unsigned char buf[3]= {MPSSE_WRITE_TMS | MPSSE_LSB | MPSSE_BITMODE |
|
display("%s: %d %x\n", __func__, _num_tms, _tms_buffer[0]);
|
||||||
MPSSE_WRITE_NEG, 0, 0};
|
|
||||||
|
|
||||||
if (_num_tms == 0)
|
_jtag->storeTMS(_tms_buffer, _num_tms);
|
||||||
return 0;
|
|
||||||
|
|
||||||
display("%s: %d %x\n", __func__, _num_tms, _tms_buffer[0]);
|
/* reset buffer and number of bits */
|
||||||
|
bzero(_tms_buffer, _tms_buffer_size);
|
||||||
while (_num_tms != 0) {
|
_num_tms = 0;
|
||||||
xfer = (_num_tms > 6) ? 6 : _num_tms;
|
|
||||||
buf[1] = xfer - 1;
|
|
||||||
buf[2] = 0x80;
|
|
||||||
for (int i = 0; i < xfer; i++, pos++) {
|
|
||||||
buf[2] |=
|
|
||||||
(((_tms_buffer[pos >> 3] & (1 << (pos & 0x07))) ? 1 : 0) << i);
|
|
||||||
}
|
|
||||||
_num_tms -= xfer;
|
|
||||||
mpsse_store(buf, 3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* reset buffer and number of bits */
|
|
||||||
bzero(_tms_buffer, _tms_buffer_size);
|
|
||||||
_num_tms = 0;
|
|
||||||
if (flush_buffer)
|
if (flush_buffer)
|
||||||
return mpsse_write();
|
return _jtag->writeTMS(NULL);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FtdiJtag::go_test_logic_reset()
|
void Jtag::go_test_logic_reset()
|
||||||
{
|
{
|
||||||
/* idenpendly to current state 5 clk with TMS high is enough */
|
/* idenpendly to current state 5 clk with TMS high is enough */
|
||||||
for (int i = 0; i < 6; i++)
|
for (int i = 0; i < 6; i++)
|
||||||
|
|
@ -201,44 +173,42 @@ void FtdiJtag::go_test_logic_reset()
|
||||||
/* GGM: faut tenir plus compte de la taille de la fifo interne
|
/* GGM: faut tenir plus compte de la taille de la fifo interne
|
||||||
* du FT2232 pour maximiser l'envoi au lieu de faire de petits envoies
|
* du FT2232 pour maximiser l'envoi au lieu de faire de petits envoies
|
||||||
*/
|
*/
|
||||||
int FtdiJtag::read_write(unsigned char *tdi, unsigned char *tdo, int len, char last)
|
int Jtag::read_write(unsigned char *tdi, unsigned char *tdo, int len, char last)
|
||||||
{
|
{
|
||||||
/* 3 possible case :
|
/* 3 possible case :
|
||||||
* - n * 8bits to send -> use byte command
|
* - n * 8bits to send -> use byte command
|
||||||
* - less than 8bits -> use bit command
|
* - less than 8bits -> use bit command
|
||||||
* - last bit to send -> sent in conjunction with TMS
|
* - last bit to send -> sent in conjunction with TMS
|
||||||
*/
|
*/
|
||||||
int tx_buff_size = mpsse_get_buffer_size();
|
int tx_buff_size = _jtag->get_buffer_size();
|
||||||
int real_len = (last) ? len - 1 : len; // if its a buffer in a big send send len
|
int real_len = (last) ? len - 1 : len; // if its a buffer in a big send send len
|
||||||
// else supress last bit -> with TMS
|
// else supress last bit -> with TMS
|
||||||
int nb_byte = real_len >> 3; // number of byte to send
|
int nb_byte = real_len >> 3; // number of byte to send
|
||||||
int nb_bit = (real_len & 0x07); // residual bits
|
int nb_bit = (real_len & 0x07); // residual bits
|
||||||
int xfer = tx_buff_size - 3;
|
int xfer = tx_buff_size;
|
||||||
unsigned char c[len];
|
|
||||||
unsigned char *rx_ptr = (unsigned char *)tdo;
|
unsigned char *rx_ptr = (unsigned char *)tdo;
|
||||||
unsigned char *tx_ptr = (unsigned char *)tdi;
|
unsigned char *tx_ptr = (unsigned char *)tdi;
|
||||||
unsigned char tx_buf[3] = {(unsigned char)(MPSSE_LSB | MPSSE_WRITE_NEG |
|
|
||||||
((tdi) ? MPSSE_DO_WRITE : 0) |
|
|
||||||
((tdo) ? MPSSE_DO_READ : 0)),
|
|
||||||
static_cast<unsigned char>((xfer - 1) & 0xff), // low
|
|
||||||
static_cast<unsigned char>((((xfer - 1) >> 8) & 0xff))}; // high
|
|
||||||
|
|
||||||
flushTMS(true);
|
flushTMS(true);
|
||||||
|
|
||||||
display("%s len : %d %d %d %d\n", __func__, len, real_len, nb_byte,
|
display("%s len : %d %d %d %d %d\n", __func__, len, real_len, nb_byte,
|
||||||
nb_bit);
|
nb_bit, tx_buff_size);
|
||||||
while (nb_byte > xfer) {
|
while (nb_byte > xfer) {
|
||||||
mpsse_store(tx_buf, 3);
|
display("%s %d %d\n", __func__, nb_byte, xfer);
|
||||||
if (tdi) {
|
|
||||||
mpsse_store(tx_ptr, xfer);
|
if (xfer != _jtag->storeTDI(tx_ptr, xfer, tdo != NULL)) {
|
||||||
|
printError("%s: Fail to store tdi\n", __func__);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (0 > _jtag->writeTDI(rx_ptr, xfer * 8)) {
|
||||||
|
printError("%s: Write errror\n", __func__);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (tdi)
|
||||||
tx_ptr += xfer;
|
tx_ptr += xfer;
|
||||||
}
|
if (tdo)
|
||||||
if (tdo) {
|
|
||||||
mpsse_read(rx_ptr, xfer);
|
|
||||||
rx_ptr += xfer;
|
rx_ptr += xfer;
|
||||||
} else if (_ch552WA) {
|
|
||||||
ftdi_read_data(_ftdi, c, xfer);
|
|
||||||
}
|
|
||||||
nb_byte -= xfer;
|
nb_byte -= xfer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -246,43 +216,40 @@ int FtdiJtag::read_write(unsigned char *tdi, unsigned char *tdo, int len, char l
|
||||||
/* 1/ send serie of byte */
|
/* 1/ send serie of byte */
|
||||||
if (nb_byte > 0) {
|
if (nb_byte > 0) {
|
||||||
display("%s read/write %d byte\n", __func__, nb_byte);
|
display("%s read/write %d byte\n", __func__, nb_byte);
|
||||||
tx_buf[1] = ((nb_byte - 1) & 0xff); // low
|
if (nb_byte != _jtag->storeTDI(tx_ptr, nb_byte, tdo != NULL)) {
|
||||||
tx_buf[2] = (((nb_byte - 1) >> 8) & 0xff); // high
|
printError("%s: Fail to store tdi\n", __func__);
|
||||||
mpsse_store(tx_buf, 3);
|
return -1;
|
||||||
if (tdi) {
|
}
|
||||||
mpsse_store(tx_ptr, nb_byte);
|
if (0 > _jtag->writeTDI(((tdo)?rx_ptr:NULL), nb_byte * 8)) {
|
||||||
|
printError("%s: Write errror\n", __func__);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (tdi)
|
||||||
tx_ptr += nb_byte;
|
tx_ptr += nb_byte;
|
||||||
}
|
if (tdo)
|
||||||
if (tdo) {
|
|
||||||
mpsse_read(rx_ptr, nb_byte);
|
|
||||||
rx_ptr += nb_byte;
|
rx_ptr += nb_byte;
|
||||||
} else if (_ch552WA) {
|
|
||||||
ftdi_read_data(_ftdi, c, nb_byte);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char last_bit = (tdi) ? *tx_ptr : 0;
|
unsigned char last_bit = (tdi) ? *tx_ptr : 0;
|
||||||
|
|
||||||
if (nb_bit != 0) {
|
if (nb_bit != 0) {
|
||||||
display("%s read/write %d bit\n", __func__, nb_bit);
|
display("%s read/write %d bit\n", __func__, nb_bit);
|
||||||
tx_buf[0] |= MPSSE_BITMODE;
|
if (nb_bit != _jtag->storeTDI(last_bit, nb_bit, tdo != NULL)) {
|
||||||
tx_buf[1] = nb_bit - 1;
|
printError("%s: Fail to store tdi\n", __func__);
|
||||||
mpsse_store(tx_buf, 2);
|
return -1;
|
||||||
if (tdi) {
|
|
||||||
display("%s last_bit %x size %d\n", __func__, last_bit, nb_bit-1);
|
|
||||||
mpsse_store(last_bit);
|
|
||||||
}
|
}
|
||||||
mpsse_write();
|
if (0 > _jtag->writeTDI((tdo)?rx_ptr:NULL, nb_bit)) {
|
||||||
|
printError("%s: Write errror\n", __func__);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (tdo) {
|
if (tdo) {
|
||||||
mpsse_read(rx_ptr, 1);
|
|
||||||
/* realign we have read nb_bit
|
/* realign we have read nb_bit
|
||||||
* since LSB add bit by the left and shift
|
* since LSB add bit by the left and shift
|
||||||
* we need to complete shift
|
* we need to complete shift
|
||||||
*/
|
*/
|
||||||
*rx_ptr >>= (8 - nb_bit);
|
*rx_ptr >>= (8 - nb_bit);
|
||||||
display("%s %x\n", __func__, *rx_ptr);
|
display("%s %x\n", __func__, *rx_ptr);
|
||||||
} else if (_ch552WA) {
|
|
||||||
ftdi_read_data(_ftdi, c, nb_bit);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -295,25 +262,18 @@ int FtdiJtag::read_write(unsigned char *tdi, unsigned char *tdo, int len, char l
|
||||||
}
|
}
|
||||||
|
|
||||||
if (last == 1) {
|
if (last == 1) {
|
||||||
|
uint8_t c;
|
||||||
last_bit = (tdi)? (*tx_ptr & (1 << nb_bit)) : 0;
|
last_bit = (tdi)? (*tx_ptr & (1 << nb_bit)) : 0;
|
||||||
|
|
||||||
display("%s move to EXIT1_xx and send last bit %x\n", __func__, (last_bit?0x81:0x01));
|
display("%s move to EXIT1_xx and send last bit %x\n", __func__, (last_bit?0x81:0x01));
|
||||||
/* write the last bit in conjunction with TMS */
|
|
||||||
tx_buf[0] = MPSSE_WRITE_TMS | MPSSE_LSB | MPSSE_BITMODE | MPSSE_WRITE_NEG |
|
c=1;
|
||||||
((tdo) ? MPSSE_DO_READ : 0);
|
_jtag->storeTMS(&c, 1, (last_bit)?1:0, tdo != NULL);
|
||||||
tx_buf[1] = 0x0 ; // send 1bit
|
_jtag->writeTMS((tdo)?&c:NULL, 1);
|
||||||
tx_buf[2] = ((last_bit)?0x81:0x01); // we know in TMS tdi is bit 7
|
|
||||||
// and to move to EXIT_XR TMS = 1
|
|
||||||
mpsse_store(tx_buf, 3);
|
|
||||||
mpsse_write();
|
|
||||||
if (tdo) {
|
if (tdo) {
|
||||||
unsigned char c;
|
|
||||||
mpsse_read(&c, 1);
|
|
||||||
/* in this case for 1 one it's always bit 7 */
|
/* in this case for 1 one it's always bit 7 */
|
||||||
*rx_ptr |= ((c & 0x80) << (7 - nb_bit));
|
*rx_ptr |= ((c & 0x80) << (7 - nb_bit));
|
||||||
display("%s %x\n", __func__, c);
|
display("%s %x\n", __func__, c);
|
||||||
} else if (_ch552WA) {
|
|
||||||
ftdi_read_data(_ftdi, c, 1);
|
|
||||||
}
|
}
|
||||||
_state = (_state == SHIFT_DR) ? EXIT1_DR : EXIT1_IR;
|
_state = (_state == SHIFT_DR) ? EXIT1_DR : EXIT1_IR;
|
||||||
}
|
}
|
||||||
|
|
@ -321,7 +281,7 @@ int FtdiJtag::read_write(unsigned char *tdi, unsigned char *tdo, int len, char l
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FtdiJtag::toggleClk(int nb)
|
void Jtag::toggleClk(int nb)
|
||||||
{
|
{
|
||||||
unsigned char c = (TEST_LOGIC_RESET == _state) ? 1 : 0;
|
unsigned char c = (TEST_LOGIC_RESET == _state) ? 1 : 0;
|
||||||
for (int i = 0; i < nb; i++)
|
for (int i = 0; i < nb; i++)
|
||||||
|
|
@ -329,7 +289,7 @@ void FtdiJtag::toggleClk(int nb)
|
||||||
flushTMS(true);
|
flushTMS(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
int FtdiJtag::shiftDR(unsigned char *tdi, unsigned char *tdo, int drlen, int end_state)
|
int Jtag::shiftDR(unsigned char *tdi, unsigned char *tdo, int drlen, int end_state)
|
||||||
{
|
{
|
||||||
set_state(SHIFT_DR);
|
set_state(SHIFT_DR);
|
||||||
// force transmit tms state
|
// force transmit tms state
|
||||||
|
|
@ -341,7 +301,7 @@ int FtdiJtag::shiftDR(unsigned char *tdi, unsigned char *tdo, int drlen, int end
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int FtdiJtag::shiftIR(unsigned char tdi, int irlen, int end_state)
|
int Jtag::shiftIR(unsigned char tdi, int irlen, int end_state)
|
||||||
{
|
{
|
||||||
if (irlen > 8) {
|
if (irlen > 8) {
|
||||||
cerr << "Error: this method this direct char don't support more than 1 byte" << endl;
|
cerr << "Error: this method this direct char don't support more than 1 byte" << endl;
|
||||||
|
|
@ -350,7 +310,7 @@ int FtdiJtag::shiftIR(unsigned char tdi, int irlen, int end_state)
|
||||||
return shiftIR(&tdi, NULL, irlen, end_state);
|
return shiftIR(&tdi, NULL, irlen, end_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
int FtdiJtag::shiftIR(unsigned char *tdi, unsigned char *tdo, int irlen, int end_state)
|
int Jtag::shiftIR(unsigned char *tdi, unsigned char *tdo, int irlen, int end_state)
|
||||||
{
|
{
|
||||||
display("%s: avant shiftIR\n", __func__);
|
display("%s: avant shiftIR\n", __func__);
|
||||||
set_state(SHIFT_IR);
|
set_state(SHIFT_IR);
|
||||||
|
|
@ -358,13 +318,13 @@ int FtdiJtag::shiftIR(unsigned char *tdi, unsigned char *tdo, int irlen, int end
|
||||||
// currently don't care about multiple device in the chain
|
// currently don't care about multiple device in the chain
|
||||||
|
|
||||||
display("%s: envoi ircode\n", __func__);
|
display("%s: envoi ircode\n", __func__);
|
||||||
read_write(tdi, tdo, irlen, 1);// 1 since only one device
|
read_write(tdi, tdo, irlen, 1); // 1 since only one device
|
||||||
|
|
||||||
set_state(end_state);
|
set_state(end_state);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FtdiJtag::set_state(int newState)
|
void Jtag::set_state(int newState)
|
||||||
{
|
{
|
||||||
unsigned char tms;
|
unsigned char tms;
|
||||||
while (newState != _state) {
|
while (newState != _state) {
|
||||||
|
|
@ -548,13 +508,14 @@ void FtdiJtag::set_state(int newState)
|
||||||
}
|
}
|
||||||
|
|
||||||
setTMS(tms);
|
setTMS(tms);
|
||||||
display("%d %d %d %x\n", tms, _num_tms-1, _state, _tms_buffer[(_num_tms-1) / 8]);
|
display("%d %d %d %x\n", tms, _num_tms-1, _state,
|
||||||
|
_tms_buffer[(_num_tms-1) / 8]);
|
||||||
}
|
}
|
||||||
/* force write buffer */
|
/* force write buffer */
|
||||||
flushTMS();
|
flushTMS();
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *FtdiJtag::getStateName(tapState_t s)
|
const char *Jtag::getStateName(tapState_t s)
|
||||||
{
|
{
|
||||||
switch (s) {
|
switch (s) {
|
||||||
case TEST_LOGIC_RESET:
|
case TEST_LOGIC_RESET:
|
||||||
|
|
@ -0,0 +1,95 @@
|
||||||
|
/*
|
||||||
|
* 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 JTAG_H
|
||||||
|
#define JTAG_H
|
||||||
|
#include <ftdi.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "board.hpp"
|
||||||
|
#include "cable.hpp"
|
||||||
|
#include "ftdipp_mpsse.hpp"
|
||||||
|
#include "jtagInterface.hpp"
|
||||||
|
|
||||||
|
class Jtag {
|
||||||
|
public:
|
||||||
|
Jtag(cable_t &cable, const jtag_pins_conf_t *pin_conf, std::string dev,
|
||||||
|
uint32_t clkHZ, bool verbose = false);
|
||||||
|
Jtag(cable_t &cable, const jtag_pins_conf_t *pin_conf,
|
||||||
|
uint32_t clkHZ, bool verbose);
|
||||||
|
~Jtag();
|
||||||
|
|
||||||
|
/* maybe to update */
|
||||||
|
int setClkFreq(uint32_t clkHZ) { return _jtag->setClkFreq(clkHZ);}
|
||||||
|
int setClkFreq(uint32_t clkHZ, char use_divide_by_5) {
|
||||||
|
return _jtag->setClkFreq(clkHZ, use_divide_by_5);}
|
||||||
|
|
||||||
|
int detectChain(std::vector<int> &devices, int max_dev);
|
||||||
|
|
||||||
|
int shiftIR(unsigned char *tdi, unsigned char *tdo, int irlen,
|
||||||
|
int end_state = RUN_TEST_IDLE);
|
||||||
|
int shiftIR(unsigned char tdi, int irlen,
|
||||||
|
int end_state = RUN_TEST_IDLE);
|
||||||
|
int shiftDR(unsigned char *tdi, unsigned char *tdo, int drlen,
|
||||||
|
int end_state = RUN_TEST_IDLE);
|
||||||
|
int read_write(unsigned char *tdi, unsigned char *tdo, int len, char last);
|
||||||
|
|
||||||
|
void toggleClk(int nb);
|
||||||
|
void go_test_logic_reset();
|
||||||
|
void set_state(int newState);
|
||||||
|
int flushTMS(bool flush_buffer = false);
|
||||||
|
void flush() {_jtag->writeTMS(NULL, 0); _jtag->writeTDI(NULL, 0);}
|
||||||
|
void setTMS(unsigned char tms);
|
||||||
|
|
||||||
|
enum tapState_t {
|
||||||
|
TEST_LOGIC_RESET = 0,
|
||||||
|
RUN_TEST_IDLE = 1,
|
||||||
|
SELECT_DR_SCAN = 2,
|
||||||
|
CAPTURE_DR = 3,
|
||||||
|
SHIFT_DR = 4,
|
||||||
|
EXIT1_DR = 5,
|
||||||
|
PAUSE_DR = 6,
|
||||||
|
EXIT2_DR = 7,
|
||||||
|
UPDATE_DR = 8,
|
||||||
|
SELECT_IR_SCAN = 9,
|
||||||
|
CAPTURE_IR = 10,
|
||||||
|
SHIFT_IR = 11,
|
||||||
|
EXIT1_IR = 12,
|
||||||
|
PAUSE_IR = 13,
|
||||||
|
EXIT2_IR = 14,
|
||||||
|
UPDATE_IR = 15,
|
||||||
|
UNKNOWN = 999
|
||||||
|
};
|
||||||
|
const char *getStateName(tapState_t s);
|
||||||
|
|
||||||
|
/* utilities */
|
||||||
|
void setVerbose(bool verbose){_verbose = verbose;}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void init_internal(cable_t &cable, const std::string &dev,
|
||||||
|
const jtag_pins_conf_t *pin_conf, uint32_t clkHZ);
|
||||||
|
bool _verbose;
|
||||||
|
int _state;
|
||||||
|
int _tms_buffer_size;
|
||||||
|
int _num_tms;
|
||||||
|
unsigned char *_tms_buffer;
|
||||||
|
std::string _board_name;
|
||||||
|
JtagInterface *_jtag;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,89 @@
|
||||||
|
/*
|
||||||
|
* 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 _JTAGINTERFACE_H_
|
||||||
|
#define _JTAGINTERFACE_H_
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \file JtagInterface.hpp
|
||||||
|
* \class JtagInterface
|
||||||
|
* \brief abstract class between jtag implementation and converters
|
||||||
|
* \author Gwenhael Goavec-Merou
|
||||||
|
*/
|
||||||
|
|
||||||
|
class JtagInterface {
|
||||||
|
public:
|
||||||
|
virtual ~JtagInterface() {}
|
||||||
|
|
||||||
|
virtual int setClkFreq(uint32_t clkHZ) = 0;
|
||||||
|
virtual int setClkFreq(uint32_t clkHZ, char use_divide_by_5) = 0;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief store TMS states in internal buffer. Not limited to 8 states
|
||||||
|
* \param tms: array of TMS values
|
||||||
|
* \param nb_bit: number of TMS states to store
|
||||||
|
* \param tdi: state of TDI for all TMS to store
|
||||||
|
* \return number of states stored
|
||||||
|
*/
|
||||||
|
virtual int storeTMS(uint8_t *tms, int nb_bit, uint8_t tdi = 1,
|
||||||
|
bool read = false) = 0;
|
||||||
|
/*!
|
||||||
|
* \brief flush TMS internal buffer (ie. transmit to converter)
|
||||||
|
* \param tdo: pointer for read operation. May be NULL
|
||||||
|
* \param len: number of bit to send
|
||||||
|
* \return number of bit send/received
|
||||||
|
*/
|
||||||
|
virtual int writeTMS(uint8_t *tdo, int len = 0) = 0;
|
||||||
|
/*!
|
||||||
|
* \brief store up to 8 TDI state(s) in internal buffer
|
||||||
|
* \param tdi: TDI value(s)
|
||||||
|
* \param tms: state of TMS for all TDI to store
|
||||||
|
* \param nb_bit: number of TMS states to store
|
||||||
|
* \return number of states stored
|
||||||
|
*/
|
||||||
|
virtual int storeTDI(uint8_t tdi, int nb_bit, bool read) = 0;
|
||||||
|
/*!
|
||||||
|
* \brief store TDI multiple of 8 states in internal buffer.
|
||||||
|
* \param tdi: array of TDI values
|
||||||
|
* \param tms: state of TMS for all TDI to store
|
||||||
|
* \param nb_byte: number of TDI states to store
|
||||||
|
* \return number of states stored
|
||||||
|
*/
|
||||||
|
virtual int storeTDI(uint8_t *tdi, int nb_byte, bool read) = 0;
|
||||||
|
/*!
|
||||||
|
* \brief flush TDI internal buffer (ie. transmit to converter)
|
||||||
|
* \param tdo: pointer for read operation. May be NULL
|
||||||
|
* \return number of bit send/received
|
||||||
|
*/
|
||||||
|
virtual int writeTDI(uint8_t *tdo, int nb_bit) = 0;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief return internal buffer size (in byte)
|
||||||
|
* \return internal buffer size
|
||||||
|
*/
|
||||||
|
virtual int get_buffer_size() = 0;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief return status of internal buffer
|
||||||
|
* \return true when internal buffer is full
|
||||||
|
*/
|
||||||
|
virtual bool isFull() = 0;
|
||||||
|
};
|
||||||
|
#endif // _JTAGINTERFACE_H_
|
||||||
|
|
@ -24,7 +24,7 @@
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include "ftdijtag.hpp"
|
#include "jtag.hpp"
|
||||||
#include "lattice.hpp"
|
#include "lattice.hpp"
|
||||||
#include "progressBar.hpp"
|
#include "progressBar.hpp"
|
||||||
#include "display.hpp"
|
#include "display.hpp"
|
||||||
|
|
@ -61,7 +61,7 @@ using namespace std;
|
||||||
# define REG_STATUS_CNF_CHK_MASK (0x7 << 23)
|
# define REG_STATUS_CNF_CHK_MASK (0x7 << 23)
|
||||||
# define REG_STATUS_EXEC_ERR (1 << 26)
|
# define REG_STATUS_EXEC_ERR (1 << 26)
|
||||||
|
|
||||||
Lattice::Lattice(FtdiJtag *jtag, const string filename, bool verbose):
|
Lattice::Lattice(Jtag *jtag, const string filename, bool verbose):
|
||||||
Device(jtag, filename, verbose)
|
Device(jtag, filename, verbose)
|
||||||
{
|
{
|
||||||
if (_filename != "") {
|
if (_filename != "") {
|
||||||
|
|
@ -182,13 +182,13 @@ bool Lattice::program_mem()
|
||||||
|
|
||||||
/* LSC_INIT_ADDRESS */
|
/* LSC_INIT_ADDRESS */
|
||||||
wr_rd(0x46, NULL, 0, NULL, 0);
|
wr_rd(0x46, NULL, 0, NULL, 0);
|
||||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
||||||
_jtag->toggleClk(1000);
|
_jtag->toggleClk(1000);
|
||||||
|
|
||||||
uint8_t *data = _bit.getData();
|
uint8_t *data = _bit.getData();
|
||||||
int length = _bit.getLength()/8;
|
int length = _bit.getLength()/8;
|
||||||
wr_rd(0x7A, NULL, 0, NULL, 0);
|
wr_rd(0x7A, NULL, 0, NULL, 0);
|
||||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
||||||
_jtag->toggleClk(2);
|
_jtag->toggleClk(2);
|
||||||
|
|
||||||
uint8_t tmp[1024];
|
uint8_t tmp[1024];
|
||||||
|
|
@ -205,10 +205,10 @@ bool Lattice::program_mem()
|
||||||
for (int ii = 0; ii < size; ii++)
|
for (int ii = 0; ii < size; ii++)
|
||||||
tmp[ii] = ConfigBitstreamParser::reverseByte(data[i+ii]);
|
tmp[ii] = ConfigBitstreamParser::reverseByte(data[i+ii]);
|
||||||
|
|
||||||
_jtag->shiftDR(tmp, NULL, size*8, FtdiJtag::SHIFT_DR);
|
_jtag->shiftDR(tmp, NULL, size*8, Jtag::SHIFT_DR);
|
||||||
}
|
}
|
||||||
|
|
||||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
||||||
|
|
||||||
if (checkStatus(0, REG_STATUS_CNF_CHK_MASK))
|
if (checkStatus(0, REG_STATUS_CNF_CHK_MASK))
|
||||||
progress.done();
|
progress.done();
|
||||||
|
|
@ -341,7 +341,7 @@ bool Lattice::program_flash()
|
||||||
|
|
||||||
/* LSC_INIT_ADDRESS */
|
/* LSC_INIT_ADDRESS */
|
||||||
wr_rd(0x46, NULL, 0, NULL, 0);
|
wr_rd(0x46, NULL, 0, NULL, 0);
|
||||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
||||||
_jtag->toggleClk(1000);
|
_jtag->toggleClk(1000);
|
||||||
|
|
||||||
/* flash UFM */
|
/* flash UFM */
|
||||||
|
|
@ -354,7 +354,7 @@ bool Lattice::program_flash()
|
||||||
|
|
||||||
/* LSC_INIT_ADDRESS */
|
/* LSC_INIT_ADDRESS */
|
||||||
wr_rd(0x46, NULL, 0, NULL, 0);
|
wr_rd(0x46, NULL, 0, NULL, 0);
|
||||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
||||||
_jtag->toggleClk(1000);
|
_jtag->toggleClk(1000);
|
||||||
|
|
||||||
if ((eraseMode & FLASH_ERASE_FEATURE) != 0) {
|
if ((eraseMode & FLASH_ERASE_FEATURE) != 0) {
|
||||||
|
|
@ -425,7 +425,7 @@ bool Lattice::EnableISC(uint8_t flash_mode)
|
||||||
{
|
{
|
||||||
wr_rd(ISC_ENABLE, &flash_mode, 1, NULL, 0);
|
wr_rd(ISC_ENABLE, &flash_mode, 1, NULL, 0);
|
||||||
|
|
||||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
||||||
_jtag->toggleClk(1000);
|
_jtag->toggleClk(1000);
|
||||||
if (!pollBusyFlag())
|
if (!pollBusyFlag())
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -437,7 +437,7 @@ bool Lattice::EnableISC(uint8_t flash_mode)
|
||||||
bool Lattice::DisableISC()
|
bool Lattice::DisableISC()
|
||||||
{
|
{
|
||||||
wr_rd(ISC_DISABLE, NULL, 0, NULL, 0);
|
wr_rd(ISC_DISABLE, NULL, 0, NULL, 0);
|
||||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
||||||
_jtag->toggleClk(1000);
|
_jtag->toggleClk(1000);
|
||||||
if (!pollBusyFlag())
|
if (!pollBusyFlag())
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -450,7 +450,7 @@ bool Lattice::EnableCfgIf()
|
||||||
{
|
{
|
||||||
uint8_t tx_buf = 0x08;
|
uint8_t tx_buf = 0x08;
|
||||||
wr_rd(0x74, &tx_buf, 1, NULL, 0);
|
wr_rd(0x74, &tx_buf, 1, NULL, 0);
|
||||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
||||||
_jtag->toggleClk(1000);
|
_jtag->toggleClk(1000);
|
||||||
return pollBusyFlag();
|
return pollBusyFlag();
|
||||||
}
|
}
|
||||||
|
|
@ -459,7 +459,7 @@ bool Lattice::DisableCfg()
|
||||||
{
|
{
|
||||||
uint8_t tx_buf, rx_buf;
|
uint8_t tx_buf, rx_buf;
|
||||||
wr_rd(0x26, &tx_buf, 1, &rx_buf, 1);
|
wr_rd(0x26, &tx_buf, 1, &rx_buf, 1);
|
||||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
||||||
_jtag->toggleClk(1000);
|
_jtag->toggleClk(1000);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -490,7 +490,7 @@ bool Lattice::checkID()
|
||||||
printf("check ID\n");
|
printf("check ID\n");
|
||||||
uint8_t tx[4];
|
uint8_t tx[4];
|
||||||
wr_rd(0xE2, tx, 4, NULL, 0);
|
wr_rd(0xE2, tx, 4, NULL, 0);
|
||||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
||||||
_jtag->toggleClk(1000);
|
_jtag->toggleClk(1000);
|
||||||
|
|
||||||
uint32_t reg = readStatusReg();
|
uint32_t reg = readStatusReg();
|
||||||
|
|
@ -501,7 +501,7 @@ bool Lattice::checkID()
|
||||||
tx[1] = 0xd0;
|
tx[1] = 0xd0;
|
||||||
tx[0] = 0x43;
|
tx[0] = 0x43;
|
||||||
wr_rd(0xE2, tx, 4, NULL, 0);
|
wr_rd(0xE2, tx, 4, NULL, 0);
|
||||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
||||||
_jtag->toggleClk(1000);
|
_jtag->toggleClk(1000);
|
||||||
reg = readStatusReg();
|
reg = readStatusReg();
|
||||||
displayReadReg(reg);
|
displayReadReg(reg);
|
||||||
|
|
@ -519,7 +519,7 @@ uint32_t Lattice::readStatusReg()
|
||||||
uint32_t reg;
|
uint32_t reg;
|
||||||
uint8_t rx[4], tx[4];
|
uint8_t rx[4], tx[4];
|
||||||
wr_rd(0x3C, tx, 4, rx, 4);
|
wr_rd(0x3C, tx, 4, rx, 4);
|
||||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
||||||
_jtag->toggleClk(1000);
|
_jtag->toggleClk(1000);
|
||||||
reg = rx[3] << 24 | rx[2] << 16 | rx[1] << 8 | rx[0];
|
reg = rx[3] << 24 | rx[2] << 16 | rx[1] << 8 | rx[0];
|
||||||
return reg;
|
return reg;
|
||||||
|
|
@ -543,10 +543,10 @@ bool Lattice::wr_rd(uint8_t cmd,
|
||||||
xfer_tx[i] = tx[i];
|
xfer_tx[i] = tx[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
_jtag->shiftIR(&cmd, NULL, 8, FtdiJtag::PAUSE_IR);
|
_jtag->shiftIR(&cmd, NULL, 8, Jtag::PAUSE_IR);
|
||||||
if (rx || tx) {
|
if (rx || tx) {
|
||||||
_jtag->shiftDR(xfer_tx, (rx) ? xfer_rx : NULL, 8 * xfer_len,
|
_jtag->shiftDR(xfer_tx, (rx) ? xfer_rx : NULL, 8 * xfer_len,
|
||||||
FtdiJtag::PAUSE_DR);
|
Jtag::PAUSE_DR);
|
||||||
}
|
}
|
||||||
if (rx) {
|
if (rx) {
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
|
|
@ -652,7 +652,7 @@ bool Lattice::pollBusyFlag(bool verbose)
|
||||||
int timeout = 0;
|
int timeout = 0;
|
||||||
do {
|
do {
|
||||||
wr_rd(CHECK_BUSY_FLAG, NULL, 0, &rx, 1);
|
wr_rd(CHECK_BUSY_FLAG, NULL, 0, &rx, 1);
|
||||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
||||||
_jtag->toggleClk(1000);
|
_jtag->toggleClk(1000);
|
||||||
if (verbose)
|
if (verbose)
|
||||||
printf("pollBusyFlag :%02x\n", rx);
|
printf("pollBusyFlag :%02x\n", rx);
|
||||||
|
|
@ -676,7 +676,7 @@ bool Lattice::flashErase(uint8_t mask)
|
||||||
{
|
{
|
||||||
uint8_t tx[1] = {mask};
|
uint8_t tx[1] = {mask};
|
||||||
wr_rd(FLASH_ERASE, tx, 1, NULL, 0);
|
wr_rd(FLASH_ERASE, tx, 1, NULL, 0);
|
||||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
||||||
_jtag->toggleClk(1000);
|
_jtag->toggleClk(1000);
|
||||||
if (!pollBusyFlag())
|
if (!pollBusyFlag())
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -692,7 +692,7 @@ bool Lattice::flashProg(uint32_t start_addr, std::vector<std::string> data)
|
||||||
for (uint32_t line = 0; line < data.size(); line++) {
|
for (uint32_t line = 0; line < data.size(); line++) {
|
||||||
wr_rd(PROG_CFG_FLASH, (uint8_t *)data[line].c_str(),
|
wr_rd(PROG_CFG_FLASH, (uint8_t *)data[line].c_str(),
|
||||||
16, NULL, 0);
|
16, NULL, 0);
|
||||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
||||||
_jtag->toggleClk(1000);
|
_jtag->toggleClk(1000);
|
||||||
progress.display(line);
|
progress.display(line);
|
||||||
if (pollBusyFlag() == false)
|
if (pollBusyFlag() == false)
|
||||||
|
|
@ -709,20 +709,20 @@ bool Lattice::Verify(JedParser &_jed, bool unlock)
|
||||||
EnableISC(0x08);
|
EnableISC(0x08);
|
||||||
|
|
||||||
wr_rd(0x46, NULL, 0, NULL, 0);
|
wr_rd(0x46, NULL, 0, NULL, 0);
|
||||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
||||||
_jtag->toggleClk(1000);
|
_jtag->toggleClk(1000);
|
||||||
|
|
||||||
tx_buf[0] = 0x73;
|
tx_buf[0] = 0x73;
|
||||||
_jtag->shiftIR(tx_buf, NULL, 8, FtdiJtag::PAUSE_IR);
|
_jtag->shiftIR(tx_buf, NULL, 8, Jtag::PAUSE_IR);
|
||||||
|
|
||||||
bzero(tx_buf, 16);
|
bzero(tx_buf, 16);
|
||||||
bool failure = false;
|
bool failure = false;
|
||||||
vector<string> data = _jed.data_for_section(0);
|
vector<string> data = _jed.data_for_section(0);
|
||||||
ProgressBar progress("Verifying", data.size(), 50);
|
ProgressBar progress("Verifying", data.size(), 50);
|
||||||
for (size_t line = 0; line< data.size(); line++) {
|
for (size_t line = 0; line< data.size(); line++) {
|
||||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
||||||
_jtag->toggleClk(2);
|
_jtag->toggleClk(2);
|
||||||
_jtag->shiftDR(tx_buf, rx_buf, 16*8, FtdiJtag::PAUSE_DR);
|
_jtag->shiftDR(tx_buf, rx_buf, 16*8, Jtag::PAUSE_DR);
|
||||||
for (size_t i = 0; i < data[i].size(); i++) {
|
for (size_t i = 0; i < data[i].size(); i++) {
|
||||||
if (rx_buf[i] != (unsigned char)data[line][i]) {
|
if (rx_buf[i] != (unsigned char)data[line][i]) {
|
||||||
printf("%3ld %3ld %02x -> %02x\n", line, i,
|
printf("%3ld %3ld %02x -> %02x\n", line, i,
|
||||||
|
|
@ -760,7 +760,7 @@ uint16_t Lattice::readFeabits()
|
||||||
{
|
{
|
||||||
uint8_t rx_buf[2];
|
uint8_t rx_buf[2];
|
||||||
wr_rd(READ_FEABITS, NULL, 0, rx_buf, 2);
|
wr_rd(READ_FEABITS, NULL, 0, rx_buf, 2);
|
||||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
||||||
_jtag->toggleClk(1000);
|
_jtag->toggleClk(1000);
|
||||||
|
|
||||||
return rx_buf[0] | (((uint16_t)rx_buf[1]) << 8);
|
return rx_buf[0] | (((uint16_t)rx_buf[1]) << 8);
|
||||||
|
|
@ -772,7 +772,7 @@ bool Lattice::writeFeaturesRow(uint64_t features, bool verify)
|
||||||
for (int i=0; i < 8; i++)
|
for (int i=0; i < 8; i++)
|
||||||
tx_buf[i] = ((features >> (i*8)) & 0x00ff);
|
tx_buf[i] = ((features >> (i*8)) & 0x00ff);
|
||||||
wr_rd(PROG_FEATURE_ROW, tx_buf, 8, NULL, 0);
|
wr_rd(PROG_FEATURE_ROW, tx_buf, 8, NULL, 0);
|
||||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
||||||
_jtag->toggleClk(1000);
|
_jtag->toggleClk(1000);
|
||||||
if (!pollBusyFlag())
|
if (!pollBusyFlag())
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -787,7 +787,7 @@ bool Lattice::writeFeabits(uint16_t feabits, bool verify)
|
||||||
(uint8_t)(0x00ff & (feabits>>8))};
|
(uint8_t)(0x00ff & (feabits>>8))};
|
||||||
|
|
||||||
wr_rd(PROG_FEABITS, tx_buf, 2, NULL, 0);
|
wr_rd(PROG_FEABITS, tx_buf, 2, NULL, 0);
|
||||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
||||||
_jtag->toggleClk(1000);
|
_jtag->toggleClk(1000);
|
||||||
if (!pollBusyFlag())
|
if (!pollBusyFlag())
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -799,7 +799,7 @@ bool Lattice::writeFeabits(uint16_t feabits, bool verify)
|
||||||
bool Lattice::writeProgramDone()
|
bool Lattice::writeProgramDone()
|
||||||
{
|
{
|
||||||
wr_rd(PROG_DONE, NULL, 0, NULL, 0);
|
wr_rd(PROG_DONE, NULL, 0, NULL, 0);
|
||||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
||||||
_jtag->toggleClk(1000);
|
_jtag->toggleClk(1000);
|
||||||
if (!pollBusyFlag())
|
if (!pollBusyFlag())
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -811,7 +811,7 @@ bool Lattice::writeProgramDone()
|
||||||
bool Lattice::loadConfiguration()
|
bool Lattice::loadConfiguration()
|
||||||
{
|
{
|
||||||
wr_rd(REFRESH, NULL, 0, NULL, 0);
|
wr_rd(REFRESH, NULL, 0, NULL, 0);
|
||||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
||||||
_jtag->toggleClk(1000);
|
_jtag->toggleClk(1000);
|
||||||
if (!pollBusyFlag())
|
if (!pollBusyFlag())
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
|
|
@ -23,14 +23,14 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "ftdijtag.hpp"
|
#include "jtag.hpp"
|
||||||
#include "device.hpp"
|
#include "device.hpp"
|
||||||
#include "jedParser.hpp"
|
#include "jedParser.hpp"
|
||||||
#include "latticeBitParser.hpp"
|
#include "latticeBitParser.hpp"
|
||||||
|
|
||||||
class Lattice: public Device {
|
class Lattice: public Device {
|
||||||
public:
|
public:
|
||||||
Lattice(FtdiJtag *jtag, std::string filename, bool verbose);
|
Lattice(Jtag *jtag, std::string filename, bool verbose);
|
||||||
int idCode() override;
|
int idCode() override;
|
||||||
int userCode();
|
int userCode();
|
||||||
void reset() override {}
|
void reset() override {}
|
||||||
|
|
|
||||||
21
src/main.cpp
21
src/main.cpp
|
|
@ -31,7 +31,7 @@
|
||||||
#include "display.hpp"
|
#include "display.hpp"
|
||||||
#include "gowin.hpp"
|
#include "gowin.hpp"
|
||||||
#include "lattice.hpp"
|
#include "lattice.hpp"
|
||||||
#include "ftdijtag.hpp"
|
#include "jtag.hpp"
|
||||||
#include "part.hpp"
|
#include "part.hpp"
|
||||||
#include "xilinx.hpp"
|
#include "xilinx.hpp"
|
||||||
|
|
||||||
|
|
@ -85,7 +85,8 @@ void displaySupported(const struct arguments &args);
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
FTDIpp_MPSSE::mpsse_bit_config cable;
|
cable_t cable;
|
||||||
|
jtag_pins_conf_t *pins_config = NULL;
|
||||||
|
|
||||||
/* command line args. */
|
/* command line args. */
|
||||||
struct arguments args = {false, false, false, 0, "", "-", "-", "-",
|
struct arguments args = {false, false, false, 0, "", "-", "-", "-",
|
||||||
|
|
@ -100,7 +101,10 @@ int main(int argc, char **argv)
|
||||||
|
|
||||||
/* if a board name is specified try to use this to determine cable */
|
/* if a board name is specified try to use this to determine cable */
|
||||||
if (args.board[0] != '-' && board_list.find(args.board) != board_list.end()) {
|
if (args.board[0] != '-' && board_list.find(args.board) != board_list.end()) {
|
||||||
auto t = cable_list.find(board_list[args.board]);
|
/* set pins config */
|
||||||
|
pins_config = &board_list[args.board].pins_config;
|
||||||
|
/* search for cable */
|
||||||
|
auto t = cable_list.find(board_list[args.board].cable_name);
|
||||||
if (t == cable_list.end()) {
|
if (t == cable_list.end()) {
|
||||||
args.cable = "-";
|
args.cable = "-";
|
||||||
cout << "Board " << args.board << " has not default cable" << endl;
|
cout << "Board " << args.board << " has not default cable" << endl;
|
||||||
|
|
@ -122,11 +126,11 @@ int main(int argc, char **argv)
|
||||||
cable = select_cable->second;
|
cable = select_cable->second;
|
||||||
|
|
||||||
/* jtag base */
|
/* jtag base */
|
||||||
FtdiJtag *jtag;
|
Jtag *jtag;
|
||||||
if (args.device == "-")
|
if (args.device == "-")
|
||||||
jtag = new FtdiJtag(cable, 1, 6000000, false);
|
jtag = new Jtag(cable, pins_config, 6000000, false);
|
||||||
else
|
else
|
||||||
jtag = new FtdiJtag(cable, args.device, 1, 6000000, false);
|
jtag = new Jtag(cable, pins_config, args.device, 6000000, false);
|
||||||
|
|
||||||
/* chain detection */
|
/* chain detection */
|
||||||
vector<int> listDev;
|
vector<int> listDev;
|
||||||
|
|
@ -267,7 +271,7 @@ void displaySupported(const struct arguments &args)
|
||||||
t << setw(15) << left << "cable name:" << "vid:pid";
|
t << setw(15) << left << "cable name:" << "vid:pid";
|
||||||
printSuccess(t.str());
|
printSuccess(t.str());
|
||||||
for (auto b = cable_list.begin(); b != cable_list.end(); b++) {
|
for (auto b = cable_list.begin(); b != cable_list.end(); b++) {
|
||||||
FTDIpp_MPSSE::mpsse_bit_config c = (*b).second;
|
FTDIpp_MPSSE::mpsse_bit_config c = (*b).second.config;
|
||||||
stringstream ss;
|
stringstream ss;
|
||||||
ss << setw(15) << left << (*b).first;
|
ss << setw(15) << left << (*b).first;
|
||||||
ss << "0x" << hex << c.vid << ":" << c.pid;
|
ss << "0x" << hex << c.vid << ":" << c.pid;
|
||||||
|
|
@ -282,7 +286,8 @@ void displaySupported(const struct arguments &args)
|
||||||
printSuccess(t.str());
|
printSuccess(t.str());
|
||||||
for (auto b = board_list.begin(); b != board_list.end(); b++) {
|
for (auto b = board_list.begin(); b != board_list.end(); b++) {
|
||||||
stringstream ss;
|
stringstream ss;
|
||||||
ss << setw(15) << left << (*b).first << " " << (*b).second;
|
target_cable_t c = (*b).second;
|
||||||
|
ss << setw(15) << left << (*b).first << " " << c.cable_name;
|
||||||
printInfo(ss.str());
|
printInfo(ss.str());
|
||||||
}
|
}
|
||||||
cout << endl;
|
cout << endl;
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,8 @@ static std::map <int, fpga_model> fpga_list = {
|
||||||
{0x81112043, {"lattice", "ECP5", "LFE5UM5G-45"}},
|
{0x81112043, {"lattice", "ECP5", "LFE5UM5G-45"}},
|
||||||
{0x81113043, {"lattice", "ECP5", "LFE5UM5G-85"}},
|
{0x81113043, {"lattice", "ECP5", "LFE5UM5G-85"}},
|
||||||
|
|
||||||
|
{0x0129a043, {"lattice", "XP2", "LFXP2-8E"}},
|
||||||
|
|
||||||
{0x1100581b, {"Gowin", "GW1N", "GW1NR-9"}},
|
{0x1100581b, {"Gowin", "GW1N", "GW1NR-9"}},
|
||||||
{0x0900281B, {"Gowin", "GW1N", "GW1N-1"}},
|
{0x0900281B, {"Gowin", "GW1N", "GW1N-1"}},
|
||||||
{0x0100381B, {"Gowin", "GW1N", "GW1N-4"}},
|
{0x0100381B, {"Gowin", "GW1N", "GW1N-4"}},
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include "ftdijtag.hpp"
|
#include "jtag.hpp"
|
||||||
#include "ftdipp_mpsse.hpp"
|
#include "ftdipp_mpsse.hpp"
|
||||||
#include "progressBar.hpp"
|
#include "progressBar.hpp"
|
||||||
#include "spiFlash.hpp"
|
#include "spiFlash.hpp"
|
||||||
|
|
@ -72,7 +72,7 @@ static uint8_t reverseByte(uint8_t src)
|
||||||
#define FLASH_WRVECR 0x61
|
#define FLASH_WRVECR 0x61
|
||||||
#define FLASH_RDVECR 0x65
|
#define FLASH_RDVECR 0x65
|
||||||
|
|
||||||
SPIFlash::SPIFlash(FtdiJtag *jtag, bool verbose):_jtag(jtag), _verbose(verbose)
|
SPIFlash::SPIFlash(Jtag *jtag, bool verbose):_jtag(jtag), _verbose(verbose)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -116,8 +116,8 @@ int SPIFlash::wait(uint8_t mask, uint8_t cond, uint32_t timeout, bool verbose)
|
||||||
uint8_t tx = reverseByte(FLASH_RDSR);
|
uint8_t tx = reverseByte(FLASH_RDSR);
|
||||||
uint32_t count = 0;
|
uint32_t count = 0;
|
||||||
|
|
||||||
_jtag->shiftIR(USER1, 6, FtdiJtag::UPDATE_IR);
|
_jtag->shiftIR(USER1, 6, Jtag::UPDATE_IR);
|
||||||
_jtag->set_state(FtdiJtag::SHIFT_DR);
|
_jtag->set_state(Jtag::SHIFT_DR);
|
||||||
_jtag->read_write(&tx, NULL, 8, 0);
|
_jtag->read_write(&tx, NULL, 8, 0);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
|
|
||||||
|
|
@ -18,11 +18,11 @@
|
||||||
#ifndef SPIFLASH_HPP
|
#ifndef SPIFLASH_HPP
|
||||||
#define SPIFLASH_HPP
|
#define SPIFLASH_HPP
|
||||||
|
|
||||||
#include "ftdijtag.hpp"
|
#include "jtag.hpp"
|
||||||
|
|
||||||
class SPIFlash {
|
class SPIFlash {
|
||||||
public:
|
public:
|
||||||
SPIFlash(FtdiJtag *jtag, bool verbose);
|
SPIFlash(Jtag *jtag, bool verbose);
|
||||||
/* power */
|
/* power */
|
||||||
void power_up();
|
void power_up();
|
||||||
void power_down();
|
void power_down();
|
||||||
|
|
@ -46,7 +46,7 @@ class SPIFlash {
|
||||||
void jtag_write_read(uint8_t cmd, uint8_t *tx, uint8_t *rx, uint16_t len = 0);
|
void jtag_write_read(uint8_t cmd, uint8_t *tx, uint8_t *rx, uint16_t len = 0);
|
||||||
int wait(uint8_t mask, uint8_t cond, uint32_t timeout, bool verbose=false);
|
int wait(uint8_t mask, uint8_t cond, uint32_t timeout, bool verbose=false);
|
||||||
|
|
||||||
FtdiJtag *_jtag;
|
Jtag *_jtag;
|
||||||
bool _verbose;
|
bool _verbose;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "ftdijtag.hpp"
|
#include "jtag.hpp"
|
||||||
|
|
||||||
#include "svf_jtag.hpp"
|
#include "svf_jtag.hpp"
|
||||||
|
|
||||||
|
|
@ -231,7 +231,7 @@ void SVF_jtag::handle_instruction(vector<string> const &vstr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SVF_jtag::SVF_jtag(FtdiJtag *jtag, bool verbose):_verbose(verbose), _freq_hz(0),
|
SVF_jtag::SVF_jtag(Jtag *jtag, bool verbose):_verbose(verbose), _freq_hz(0),
|
||||||
_enddr(fsm_state["IDLE"]), _endir(fsm_state["IDLE"]),
|
_enddr(fsm_state["IDLE"]), _endir(fsm_state["IDLE"]),
|
||||||
_run_state(fsm_state["IDLE"]), _end_state(fsm_state["IDLE"])
|
_run_state(fsm_state["IDLE"]), _end_state(fsm_state["IDLE"])
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,13 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
|
#include "jtag.hpp"
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
class SVF_jtag {
|
class SVF_jtag {
|
||||||
public:
|
public:
|
||||||
SVF_jtag(FtdiJtag *jtag, bool verbose);
|
SVF_jtag(Jtag *jtag, bool verbose);
|
||||||
~SVF_jtag();
|
~SVF_jtag();
|
||||||
void parse(string filename);
|
void parse(string filename);
|
||||||
void setVerbose(bool verbose) {_verbose = verbose;}
|
void setVerbose(bool verbose) {_verbose = verbose;}
|
||||||
|
|
@ -46,7 +48,7 @@ class SVF_jtag {
|
||||||
{"IRUPDATE", 15}
|
{"IRUPDATE", 15}
|
||||||
};
|
};
|
||||||
|
|
||||||
FtdiJtag *_jtag;
|
Jtag *_jtag;
|
||||||
bool _verbose;
|
bool _verbose;
|
||||||
|
|
||||||
uint32_t _freq_hz;
|
uint32_t _freq_hz;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
#include "ftdijtag.hpp"
|
#include "jtag.hpp"
|
||||||
#include "bitparser.hpp"
|
#include "bitparser.hpp"
|
||||||
#include "mcsParser.hpp"
|
#include "mcsParser.hpp"
|
||||||
#include "spiFlash.hpp"
|
#include "spiFlash.hpp"
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
#include "part.hpp"
|
#include "part.hpp"
|
||||||
#include "progressBar.hpp"
|
#include "progressBar.hpp"
|
||||||
|
|
||||||
Xilinx::Xilinx(FtdiJtag *jtag, std::string filename, bool verbose):
|
Xilinx::Xilinx(Jtag *jtag, std::string filename, bool verbose):
|
||||||
Device(jtag, filename, verbose)
|
Device(jtag, filename, verbose)
|
||||||
{
|
{
|
||||||
if (_filename != ""){
|
if (_filename != ""){
|
||||||
|
|
@ -36,23 +36,24 @@ void Xilinx::reset()
|
||||||
{
|
{
|
||||||
_jtag->shiftIR(JSHUTDOWN, 6);
|
_jtag->shiftIR(JSHUTDOWN, 6);
|
||||||
_jtag->shiftIR(JPROGRAM, 6);
|
_jtag->shiftIR(JPROGRAM, 6);
|
||||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
||||||
_jtag->toggleClk(10000*12);
|
_jtag->toggleClk(10000*12);
|
||||||
|
|
||||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
||||||
_jtag->toggleClk(2000);
|
_jtag->toggleClk(2000);
|
||||||
|
|
||||||
_jtag->shiftIR(BYPASS, 6);
|
_jtag->shiftIR(BYPASS, 6);
|
||||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
||||||
_jtag->toggleClk(2000);
|
_jtag->toggleClk(2000);
|
||||||
}
|
}
|
||||||
|
|
||||||
int Xilinx::idCode()
|
int Xilinx::idCode()
|
||||||
{
|
{
|
||||||
|
unsigned char tx_data[4]= {0x00, 0x00, 0x00, 0x00};
|
||||||
unsigned char rx_data[4];
|
unsigned char rx_data[4];
|
||||||
_jtag->go_test_logic_reset();
|
_jtag->go_test_logic_reset();
|
||||||
_jtag->shiftIR(IDCODE, 6);
|
_jtag->shiftIR(IDCODE, 6);
|
||||||
_jtag->shiftDR(NULL, rx_data, 32);
|
_jtag->shiftDR(tx_data, rx_data, 32);
|
||||||
return ((rx_data[0] & 0x000000ff) |
|
return ((rx_data[0] & 0x000000ff) |
|
||||||
((rx_data[1] << 8) & 0x0000ff00) |
|
((rx_data[1] << 8) & 0x0000ff00) |
|
||||||
((rx_data[2] << 16) & 0x00ff0000) |
|
((rx_data[2] << 16) & 0x00ff0000) |
|
||||||
|
|
@ -128,7 +129,7 @@ void Xilinx::program_mem(BitParser &bitfile)
|
||||||
/*
|
/*
|
||||||
* 8: Move into the RTI state. X 0 10,000(1)
|
* 8: Move into the RTI state. X 0 10,000(1)
|
||||||
*/
|
*/
|
||||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
||||||
_jtag->toggleClk(10000*12);
|
_jtag->toggleClk(10000*12);
|
||||||
/*
|
/*
|
||||||
* 9: Start loading the CFG_IN instruction,
|
* 9: Start loading the CFG_IN instruction,
|
||||||
|
|
@ -141,11 +142,11 @@ void Xilinx::program_mem(BitParser &bitfile)
|
||||||
/*
|
/*
|
||||||
* 11: Enter the SELECT-DR state. X 1 2
|
* 11: Enter the SELECT-DR state. X 1 2
|
||||||
*/
|
*/
|
||||||
_jtag->set_state(FtdiJtag::SELECT_DR_SCAN);
|
_jtag->set_state(Jtag::SELECT_DR_SCAN);
|
||||||
/*
|
/*
|
||||||
* 12: Enter the SHIFT-DR state. X 0 2
|
* 12: Enter the SHIFT-DR state. X 0 2
|
||||||
*/
|
*/
|
||||||
_jtag->set_state(FtdiJtag::SHIFT_DR);
|
_jtag->set_state(Jtag::SHIFT_DR);
|
||||||
/*
|
/*
|
||||||
* 13: Shift in the FPGA bitstream. Bitn (MSB)
|
* 13: Shift in the FPGA bitstream. Bitn (MSB)
|
||||||
* is the first bit in the bitstream(2). bit1...bitn 0 (bits in bitstream)-1
|
* is the first bit in the bitstream(2). bit1...bitn 0 (bits in bitstream)-1
|
||||||
|
|
@ -177,11 +178,11 @@ void Xilinx::program_mem(BitParser &bitfile)
|
||||||
/*
|
/*
|
||||||
* 15: Enter UPDATE-DR state. X 1 1
|
* 15: Enter UPDATE-DR state. X 1 1
|
||||||
*/
|
*/
|
||||||
_jtag->set_state(FtdiJtag::UPDATE_DR);
|
_jtag->set_state(Jtag::UPDATE_DR);
|
||||||
/*
|
/*
|
||||||
* 16: Move into RTI state. X 0 1
|
* 16: Move into RTI state. X 0 1
|
||||||
*/
|
*/
|
||||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
||||||
/*
|
/*
|
||||||
* 17: Enter the SELECT-IR state. X 1 2
|
* 17: Enter the SELECT-IR state. X 1 2
|
||||||
* 18: Move to the SHIFT-IR state. X 0 2
|
* 18: Move to the SHIFT-IR state. X 0 2
|
||||||
|
|
@ -191,13 +192,13 @@ void Xilinx::program_mem(BitParser &bitfile)
|
||||||
* 20: Load the last bit of the JSTART instruction. 0 1 1
|
* 20: Load the last bit of the JSTART instruction. 0 1 1
|
||||||
* 21: Move to the UPDATE-IR state. X 1 1
|
* 21: Move to the UPDATE-IR state. X 1 1
|
||||||
*/
|
*/
|
||||||
_jtag->shiftIR(JSTART, 6, FtdiJtag::UPDATE_IR);
|
_jtag->shiftIR(JSTART, 6, Jtag::UPDATE_IR);
|
||||||
/*
|
/*
|
||||||
* 22: Move to the RTI state and clock the
|
* 22: Move to the RTI state and clock the
|
||||||
* startup sequence by applying a minimum X 0 2000
|
* startup sequence by applying a minimum X 0 2000
|
||||||
* of 2000 clock cycles to the TCK.
|
* of 2000 clock cycles to the TCK.
|
||||||
*/
|
*/
|
||||||
_jtag->set_state(FtdiJtag::RUN_TEST_IDLE);
|
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
||||||
_jtag->toggleClk(2000);
|
_jtag->toggleClk(2000);
|
||||||
/*
|
/*
|
||||||
* 23: Move to the TLR state. The device is
|
* 23: Move to the TLR state. The device is
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,11 @@
|
||||||
|
|
||||||
#include "bitparser.hpp"
|
#include "bitparser.hpp"
|
||||||
#include "device.hpp"
|
#include "device.hpp"
|
||||||
#include "ftdijtag.hpp"
|
#include "jtag.hpp"
|
||||||
|
|
||||||
class Xilinx: public Device {
|
class Xilinx: public Device {
|
||||||
public:
|
public:
|
||||||
Xilinx(FtdiJtag *jtag, std::string filename, bool verbose);
|
Xilinx(Jtag *jtag, std::string filename, bool verbose);
|
||||||
~Xilinx();
|
~Xilinx();
|
||||||
|
|
||||||
void program(unsigned int offset = 0) override;
|
void program(unsigned int offset = 0) override;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue