554 lines
12 KiB
C++
554 lines
12 KiB
C++
|
|
#include <iostream>
|
|
#include <map>
|
|
#include <vector>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <string>
|
|
|
|
#include "ftdijtag.hpp"
|
|
#include "ftdipp_mpsse.hpp"
|
|
|
|
using namespace std;
|
|
|
|
#define DEBUG 1
|
|
|
|
#ifdef DEBUG
|
|
#define display(...) \
|
|
do { if (_verbose) fprintf(stdout, __VA_ARGS__);}while(0)
|
|
#else
|
|
#define display(...) do {}while(0)
|
|
#endif
|
|
|
|
/*
|
|
* AD0 -> TCK
|
|
* AD1 -> TDI
|
|
* AD2 -> TD0
|
|
* AD3 -> TMS
|
|
*/
|
|
|
|
/* Rmq:
|
|
* pour TMS: l'envoi de n necessite de mettre n-1 comme longueur
|
|
* mais le bit n+1 est utilise pour l'etat suivant le dernier
|
|
* front. Donc il faut envoyer 6bits ([5:0]) pertinents pour
|
|
* utiliser le bit 6 comme etat apres la commande,
|
|
* le bit 7 corresponds a l'etat de TDI (donc si on fait 7 cycles
|
|
* l'etat de TDI va donner l'etat de TMS...)
|
|
* transfert/lecture: le dernier bit de IR ou DR doit etre envoye en
|
|
* meme temps que le TMS qui fait sortir de l'etat donc il faut
|
|
* pour n bits a transferer :
|
|
* - envoyer 8bits * (n/8)-1
|
|
* - envoyer les 7 bits du dernier octet;
|
|
* - envoyer le dernier avec 0x4B ou 0x6B
|
|
*/
|
|
|
|
FtdiJtag::FtdiJtag(FTDIpp_MPSSE::mpsse_bit_config &cable,
|
|
unsigned char interface, uint32_t clkHZ):
|
|
FTDIpp_MPSSE(cable.vid, cable.pid, interface, clkHZ),
|
|
_state(RUN_TEST_IDLE),
|
|
_tms_buffer_size(128), _num_tms(0),
|
|
_board_name("nope"), _verbose(false)
|
|
{
|
|
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);
|
|
_verbose = false;
|
|
|
|
_tms_buffer = (unsigned char *)malloc(sizeof(unsigned char) * _tms_buffer_size);
|
|
bzero(_tms_buffer, _tms_buffer_size);
|
|
init(1, 0xfb, cable);
|
|
}
|
|
|
|
FtdiJtag::~FtdiJtag()
|
|
{
|
|
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_READ_NEG |
|
|
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);
|
|
}
|
|
|
|
int FtdiJtag::detectChain(vector<int> &devices, int max_dev)
|
|
{
|
|
unsigned char rx_buff[4];
|
|
unsigned int tmp;
|
|
|
|
devices.clear();
|
|
go_test_logic_reset();
|
|
set_state(SHIFT_DR);
|
|
|
|
for (int i = 0; i < max_dev; i++) {
|
|
read_write(NULL, rx_buff, 32, (i == max_dev-1)?1:0);
|
|
tmp = 0;
|
|
for (int ii=0; ii < 4; ii++)
|
|
tmp |= (rx_buff[ii] << (8*ii));
|
|
if (tmp != 0 && tmp != 0xffffffff)
|
|
devices.push_back(tmp);
|
|
}
|
|
go_test_logic_reset();
|
|
return devices.size();
|
|
}
|
|
|
|
void FtdiJtag::setTMS(unsigned char tms)
|
|
{
|
|
display("%s %d %d\n", __func__, _num_tms, (_num_tms >> 3));
|
|
if (_num_tms+1 == _tms_buffer_size * 8)
|
|
flushTMS();
|
|
if (tms != 0)
|
|
_tms_buffer[_num_tms>>3] |= (0x1) << (_num_tms & 0x7);
|
|
_num_tms++;
|
|
}
|
|
|
|
/* reconstruct byte sent to TMS pins
|
|
* - use up to 6 bits
|
|
* -since next bit after length is use to
|
|
* fix TMS state after sent we copy last bit
|
|
* to bit after next
|
|
* -bit 7 is TDI state for each clk cycles
|
|
*/
|
|
|
|
int FtdiJtag::flushTMS(bool flush_buffer)
|
|
{
|
|
int xfer, pos = 0;
|
|
unsigned char buf[3]= {MPSSE_WRITE_TMS | MPSSE_LSB | MPSSE_BITMODE |
|
|
MPSSE_WRITE_NEG, 0, 0};
|
|
|
|
if (_num_tms == 0)
|
|
return 0;
|
|
|
|
display("%s: %d %x\n", __func__, _num_tms, _tms_buffer[0]);
|
|
|
|
while (_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)
|
|
return mpsse_write();
|
|
return 0;
|
|
}
|
|
|
|
void FtdiJtag::go_test_logic_reset()
|
|
{
|
|
/* idenpendly to current state 5 clk with TMS high is enough */
|
|
for (int i = 0; i < 6; i++)
|
|
setTMS(0x01);
|
|
flushTMS(true);
|
|
_state = TEST_LOGIC_RESET;
|
|
}
|
|
|
|
/* 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
|
|
*/
|
|
int FtdiJtag::read_write(unsigned char *tdi, unsigned char *tdo, int len, char last)
|
|
{
|
|
/* 3 possible case :
|
|
* - n * 8bits to send -> use byte command
|
|
* - less than 8bits -> use bit command
|
|
* - last bit to send -> sent in conjunction with TMS
|
|
*/
|
|
int tx_buff_size = mpsse_get_buffer_size();
|
|
int real_len = (last) ? len - 1 : len; // if its a buffer in a big send send len
|
|
// else supress last bit -> with TMS
|
|
int nb_byte = real_len >> 3; // number of byte to send
|
|
int nb_bit = (real_len & 0x07); // residual bits
|
|
int xfer = tx_buff_size - 3;
|
|
unsigned char *rx_ptr = (unsigned char *)tdo;
|
|
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 | MPSSE_READ_NEG) : 0)),
|
|
((xfer - 1) & 0xff), // low
|
|
(((xfer - 1) >> 8) & 0xff)}; // high
|
|
|
|
flushTMS(true);
|
|
|
|
display("%s len : %d %d %d %d\n", __func__, len, real_len, nb_byte,
|
|
nb_bit);
|
|
while (nb_byte > xfer) {
|
|
mpsse_store(tx_buf, 3);
|
|
if (tdi) {
|
|
mpsse_store(tx_ptr, xfer);
|
|
tx_ptr += xfer;
|
|
}
|
|
if (tdo) {
|
|
mpsse_read(rx_ptr, xfer);
|
|
rx_ptr += xfer;
|
|
}
|
|
nb_byte -= xfer;
|
|
}
|
|
|
|
|
|
/* 1/ send serie of byte */
|
|
if (nb_byte > 0) {
|
|
display("%s read/write %d byte\n", __func__, nb_byte);
|
|
tx_buf[1] = ((nb_byte - 1) & 0xff); // low
|
|
tx_buf[2] = (((nb_byte - 1) >> 8) & 0xff); // high
|
|
mpsse_store(tx_buf, 3);
|
|
if (tdi) {
|
|
mpsse_store(tx_ptr, nb_byte);
|
|
tx_ptr += nb_byte;
|
|
}
|
|
if (tdo) {
|
|
mpsse_read(rx_ptr, nb_byte);
|
|
rx_ptr += nb_byte;
|
|
}
|
|
}
|
|
|
|
unsigned char last_bit = (tdi) ? *tx_ptr : 0;
|
|
|
|
if (nb_bit != 0) {
|
|
display("%s read/write %d bit\n", __func__, nb_bit);
|
|
tx_buf[0] |= MPSSE_BITMODE;
|
|
tx_buf[1] = nb_bit - 1;
|
|
mpsse_store(tx_buf, 2);
|
|
if (tdi) {
|
|
display("%s last_bit %x size %d\n", __func__, last_bit, nb_bit-1);
|
|
mpsse_store(last_bit);
|
|
}
|
|
mpsse_write();
|
|
if (tdo) {
|
|
mpsse_read(rx_ptr, 1);
|
|
/* realign we have read nb_bit
|
|
* since LSB add bit by the left and shift
|
|
* we need to complete shift
|
|
*/
|
|
*rx_ptr >>= (8 - nb_bit);
|
|
display("%s %x\n", __func__, *rx_ptr);
|
|
}
|
|
}
|
|
|
|
/* display : must be dropped */
|
|
if (_verbose && tdo) {
|
|
display("\n");
|
|
for (int i = (len / 8) - 1; i >= 0; i--)
|
|
display("%x ", (unsigned char)tdo[i]);
|
|
display("\n");
|
|
}
|
|
|
|
if (last == 1) {
|
|
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));
|
|
/* write the last bit in conjunction with TMS */
|
|
tx_buf[0] = MPSSE_WRITE_TMS | MPSSE_LSB | MPSSE_BITMODE | MPSSE_WRITE_NEG |
|
|
((tdo) ? MPSSE_DO_READ | MPSSE_READ_NEG : 0);
|
|
tx_buf[1] = 0x0 ; // send 1bit
|
|
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) {
|
|
unsigned char c;
|
|
mpsse_read(&c, 1);
|
|
/* in this case for 1 one it's always bit 7 */
|
|
*rx_ptr |= ((c & 0x80) << (7 - nb_bit));
|
|
display("%s %x\n", __func__, c);
|
|
}
|
|
_state = (_state == SHIFT_DR) ? EXIT1_DR : EXIT1_IR;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void FtdiJtag::toggleClk(int nb)
|
|
{
|
|
unsigned char c = (TEST_LOGIC_RESET == _state) ? 1 : 0;
|
|
for (int i = 0; i < nb; i++)
|
|
setTMS(c);
|
|
flushTMS(true);
|
|
}
|
|
|
|
int FtdiJtag::shiftDR(unsigned char *tdi, unsigned char *tdo, int drlen, int end_state)
|
|
{
|
|
set_state(SHIFT_DR);
|
|
// force transmit tms state
|
|
flushTMS(true);
|
|
// currently don't care about multiple device in the chain
|
|
read_write(tdi, tdo, drlen, 1);// 1 since only one device
|
|
|
|
set_state(end_state);
|
|
return 0;
|
|
}
|
|
|
|
int FtdiJtag::shiftIR(unsigned char tdi, int irlen, int end_state)
|
|
{
|
|
if (irlen > 8) {
|
|
cerr << "Error: this method this direct char don't support more than 1 byte" << endl;
|
|
return -1;
|
|
}
|
|
return shiftIR(&tdi, NULL, irlen, end_state);
|
|
}
|
|
|
|
int FtdiJtag::shiftIR(unsigned char *tdi, unsigned char *tdo, int irlen, int end_state)
|
|
{
|
|
display("%s: avant shiftIR\n", __func__);
|
|
set_state(SHIFT_IR);
|
|
flushTMS(true);
|
|
// currently don't care about multiple device in the chain
|
|
|
|
display("%s: envoi ircode\n", __func__);
|
|
read_write(tdi, tdo, irlen, 1);// 1 since only one device
|
|
|
|
set_state(end_state);
|
|
return 0;
|
|
}
|
|
|
|
void FtdiJtag::set_state(int newState)
|
|
{
|
|
unsigned char tms;
|
|
while (newState != _state) {
|
|
display("_state : %16s(%02d) -> %s(%02d) ",
|
|
getStateName((tapState_t)_state),
|
|
_state,
|
|
getStateName((tapState_t)newState), newState);
|
|
switch (_state) {
|
|
case TEST_LOGIC_RESET:
|
|
if (newState == TEST_LOGIC_RESET) {
|
|
tms = 1;
|
|
} else {
|
|
tms = 0;
|
|
_state = RUN_TEST_IDLE;
|
|
}
|
|
break;
|
|
case RUN_TEST_IDLE:
|
|
if (newState == RUN_TEST_IDLE) {
|
|
tms = 0;
|
|
} else {
|
|
tms = 1;
|
|
_state = SELECT_DR_SCAN;
|
|
}
|
|
break;
|
|
case SELECT_DR_SCAN:
|
|
switch (newState) {
|
|
case CAPTURE_DR:
|
|
case SHIFT_DR:
|
|
case EXIT1_DR:
|
|
case PAUSE_DR:
|
|
case EXIT2_DR:
|
|
case UPDATE_DR:
|
|
tms = 0;
|
|
_state = CAPTURE_DR;
|
|
break;
|
|
default:
|
|
tms = 1;
|
|
_state = SELECT_IR_SCAN;
|
|
}
|
|
break;
|
|
case SELECT_IR_SCAN:
|
|
switch (newState) {
|
|
case CAPTURE_IR:
|
|
case SHIFT_IR:
|
|
case EXIT1_IR:
|
|
case PAUSE_IR:
|
|
case EXIT2_IR:
|
|
case UPDATE_IR:
|
|
tms = 0;
|
|
_state = CAPTURE_IR;
|
|
break;
|
|
default:
|
|
tms = 1;
|
|
_state = TEST_LOGIC_RESET;
|
|
}
|
|
break;
|
|
/* DR column */
|
|
case CAPTURE_DR:
|
|
if (newState == SHIFT_DR) {
|
|
tms = 0;
|
|
_state = SHIFT_DR;
|
|
} else {
|
|
tms = 1;
|
|
_state = EXIT1_DR;
|
|
}
|
|
break;
|
|
case SHIFT_DR:
|
|
if (newState == SHIFT_DR) {
|
|
tms = 0;
|
|
} else {
|
|
tms = 1;
|
|
_state = EXIT1_DR;
|
|
}
|
|
break;
|
|
case EXIT1_DR:
|
|
switch (newState) {
|
|
case PAUSE_DR:
|
|
case EXIT2_DR:
|
|
case SHIFT_DR:
|
|
case EXIT1_DR:
|
|
tms = 0;
|
|
_state = PAUSE_DR;
|
|
break;
|
|
default:
|
|
tms = 1;
|
|
_state = UPDATE_DR;
|
|
}
|
|
break;
|
|
case PAUSE_DR:
|
|
if (newState == PAUSE_DR) {
|
|
tms = 0;
|
|
} else {
|
|
tms = 1;
|
|
_state = EXIT2_DR;
|
|
}
|
|
break;
|
|
case EXIT2_DR:
|
|
switch (newState) {
|
|
case SHIFT_DR:
|
|
case EXIT1_DR:
|
|
case PAUSE_DR:
|
|
tms = 0;
|
|
_state = SHIFT_DR;
|
|
break;
|
|
default:
|
|
tms = 1;
|
|
_state = UPDATE_DR;
|
|
}
|
|
break;
|
|
case UPDATE_DR:
|
|
if (newState == RUN_TEST_IDLE) {
|
|
tms = 0;
|
|
_state = RUN_TEST_IDLE;
|
|
} else {
|
|
tms = 1;
|
|
_state = SELECT_DR_SCAN;
|
|
}
|
|
break;
|
|
/* IR column */
|
|
case CAPTURE_IR:
|
|
if (newState == SHIFT_IR) {
|
|
tms = 0;
|
|
_state = SHIFT_IR;
|
|
} else {
|
|
tms = 1;
|
|
_state = EXIT1_IR;
|
|
}
|
|
break;
|
|
case SHIFT_IR:
|
|
if (newState == SHIFT_IR) {
|
|
tms = 0;
|
|
} else {
|
|
tms = 1;
|
|
_state = EXIT1_IR;
|
|
}
|
|
break;
|
|
case EXIT1_IR:
|
|
switch (newState) {
|
|
case PAUSE_IR:
|
|
case EXIT2_IR:
|
|
case SHIFT_IR:
|
|
case EXIT1_IR:
|
|
tms = 0;
|
|
_state = PAUSE_IR;
|
|
break;
|
|
default:
|
|
tms = 1;
|
|
_state = UPDATE_IR;
|
|
}
|
|
break;
|
|
case PAUSE_IR:
|
|
if (newState == PAUSE_IR) {
|
|
tms = 0;
|
|
} else {
|
|
tms = 1;
|
|
_state = EXIT2_IR;
|
|
}
|
|
break;
|
|
case EXIT2_IR:
|
|
switch (newState) {
|
|
case SHIFT_IR:
|
|
case EXIT1_IR:
|
|
case PAUSE_IR:
|
|
tms = 0;
|
|
_state = SHIFT_IR;
|
|
break;
|
|
default:
|
|
tms = 1;
|
|
_state = UPDATE_IR;
|
|
}
|
|
break;
|
|
case UPDATE_IR:
|
|
if (newState == RUN_TEST_IDLE) {
|
|
tms = 0;
|
|
_state = RUN_TEST_IDLE;
|
|
} else {
|
|
tms = 1;
|
|
_state = SELECT_DR_SCAN;
|
|
}
|
|
break;
|
|
}
|
|
|
|
setTMS(tms);
|
|
display("%d %d %d %x\n", tms, _num_tms-1, _state, _tms_buffer[(_num_tms-1) / 8]);
|
|
}
|
|
/* force write buffer */
|
|
flushTMS();
|
|
}
|
|
|
|
const char *FtdiJtag::getStateName(tapState_t s)
|
|
{
|
|
switch (s) {
|
|
case TEST_LOGIC_RESET:
|
|
return "TEST_LOGIC_RESET";
|
|
case RUN_TEST_IDLE:
|
|
return "RUN_TEST_IDLE";
|
|
case SELECT_DR_SCAN:
|
|
return "SELECT_DR_SCAN";
|
|
case CAPTURE_DR:
|
|
return "CAPTURE_DR";
|
|
case SHIFT_DR:
|
|
return "SHIFT_DR";
|
|
case EXIT1_DR:
|
|
return "EXIT1_DR";
|
|
case PAUSE_DR:
|
|
return "PAUSE_DR";
|
|
case EXIT2_DR:
|
|
return "EXIT2_DR";
|
|
case UPDATE_DR:
|
|
return "UPDATE_DR";
|
|
case SELECT_IR_SCAN:
|
|
return "SELECT_IR_SCAN";
|
|
case CAPTURE_IR:
|
|
return "CAPTURE_IR";
|
|
case SHIFT_IR:
|
|
return "SHIFT_IR";
|
|
case EXIT1_IR:
|
|
return "EXIT1_IR";
|
|
case PAUSE_IR:
|
|
return "PAUSE_IR";
|
|
case EXIT2_IR:
|
|
return "EXIT2_IR";
|
|
case UPDATE_IR:
|
|
return "UPDATE_IR";
|
|
default:
|
|
return "Unknown";
|
|
}
|
|
}
|