ftdiJtagMPSSE: add writeTMSTDI method with full tms & tdi buffer. Always return TDO buffer
This commit is contained in:
parent
f364ea2c8f
commit
de42f8b239
|
|
@ -13,6 +13,7 @@
|
|||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include "display.hpp"
|
||||
#include "ftdiJtagMPSSE.hpp"
|
||||
#include "ftdipp_mpsse.hpp"
|
||||
|
||||
|
|
@ -35,7 +36,8 @@ FtdiJtagMPSSE::FtdiJtagMPSSE(const FTDIpp_MPSSE::mpsse_bit_config &cable,
|
|||
FTDIpp_MPSSE(cable, dev, serial, clkHZ, verbose), _ch552WA(false),
|
||||
_write_mode(MPSSE_WRITE_NEG), // always write on neg edge
|
||||
_read_mode(0),
|
||||
_invert_read_edge(invert_read_edge) // false: pos, true: neg
|
||||
_invert_read_edge(invert_read_edge), // false: pos, true: neg
|
||||
_tdo_pos(0)
|
||||
{
|
||||
init_internal(cable);
|
||||
}
|
||||
|
|
@ -79,6 +81,9 @@ void FtdiJtagMPSSE::init_internal(const FTDIpp_MPSSE::mpsse_bit_config &cable)
|
|||
if (init(5, 0xfb, BITMODE_MPSSE) != 0)
|
||||
throw std::runtime_error("low level FTDI init failed");
|
||||
config_edge();
|
||||
|
||||
_curr_tms = (cable.bit_low_val >> 3) & 0x01;
|
||||
_curr_tdi = (cable.bit_low_val >> 1) & 0x01;
|
||||
}
|
||||
|
||||
int FtdiJtagMPSSE::setClkFreq(uint32_t clkHZ) {
|
||||
|
|
@ -339,3 +344,220 @@ int FtdiJtagMPSSE::writeTDI(uint8_t *tdi, uint8_t *tdo, uint32_t len, bool last)
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t FtdiJtagMPSSE::update_tms_buff(uint8_t *buffer, uint8_t bit,
|
||||
uint32_t offset, uint8_t tdi, uint8_t *tdo, bool end)
|
||||
{
|
||||
int32_t ret;
|
||||
if (_verbose)
|
||||
printf("%s %d %02x %d\n", __func__, offset, buffer[0], end);
|
||||
if (!end) {
|
||||
uint8_t bit_shift = (1 << (offset));
|
||||
if (bit)
|
||||
buffer[0] |= bit_shift;
|
||||
else
|
||||
buffer[0] &= ~bit_shift;
|
||||
offset++;
|
||||
}
|
||||
if (offset == 6 || end) {
|
||||
if (tdi)
|
||||
buffer[0] |= (0x01 << 7);
|
||||
else
|
||||
buffer[0] &= ~(0x01 << 7);
|
||||
uint8_t mp[3] = {
|
||||
static_cast<unsigned char>(MPSSE_WRITE_TMS | MPSSE_LSB |
|
||||
MPSSE_BITMODE | _write_mode |
|
||||
MPSSE_DO_READ | _read_mode
|
||||
),
|
||||
static_cast<uint8_t>(offset - 1),
|
||||
buffer[0]
|
||||
};
|
||||
// force a write
|
||||
if (_verbose)
|
||||
printf("\t%02x %02d %02x\n", mp[0], mp[1], mp[2]);
|
||||
if ((ret = mpsse_store(mp, 3)) < 0)
|
||||
return ret;
|
||||
uint8_t tdo_tmp;
|
||||
if ((ret = mpsse_read(&tdo_tmp, 1)) < 0)
|
||||
return ret;
|
||||
update_tdo_buff(&tdo_tmp, tdo, offset);
|
||||
offset = 0;
|
||||
buffer[0] = 0;
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
uint32_t FtdiJtagMPSSE::update_tdo_buff(uint8_t *buffer, uint8_t *tdo, uint32_t len)
|
||||
{
|
||||
if (_verbose) {
|
||||
printError("update tdo " + std::to_string(_tdo_pos) + " " + std::to_string(len) + " ", false);
|
||||
uint32_t tt = (len + 7) / 8;
|
||||
for (uint32_t i = 0; i < tt; i++)
|
||||
printf("%02x ", buffer[i]);
|
||||
}
|
||||
for (uint32_t i = 0; i < len; i++, _tdo_pos++) {
|
||||
uint8_t bit = (buffer[i >> 3] >> (i & 0x07)) & 0x01;
|
||||
uint8_t mask = 1 << (_tdo_pos & 0x07);
|
||||
if (bit)
|
||||
tdo[_tdo_pos >> 3] |= mask;
|
||||
else
|
||||
tdo[_tdo_pos >> 3] &= ~mask;
|
||||
}
|
||||
if (_verbose)
|
||||
printf("\n");
|
||||
return _tdo_pos;
|
||||
}
|
||||
|
||||
bool FtdiJtagMPSSE::writeTMSTDI(const uint8_t *tms, const uint8_t *tdi,
|
||||
uint8_t *tdo, uint32_t len)
|
||||
{
|
||||
int32_t ret;
|
||||
uint32_t max_len = 1024;
|
||||
uint8_t mode = 0; // current state: 0 none, 1 TDI, 2 TMS
|
||||
uint8_t tdi_buf[max_len]; // buffer to store TDI sequence
|
||||
uint8_t tms_tmp = 0; // buffer to store TMS sequence (limited to 6bits per cmd)
|
||||
uint8_t tdo_tmp[max_len]; // local TDO sequence
|
||||
uint32_t buff_len = 0; // current bits stored
|
||||
memset(tdi_buf, 0, max_len);
|
||||
memset(tdo_tmp, 0, max_len);
|
||||
_tdo_pos = 0; // current bits read
|
||||
|
||||
if (_verbose)
|
||||
printSuccess("begin: " + std::to_string(len));
|
||||
|
||||
for (uint32_t buf_pos = 0; buf_pos < len; buf_pos++) {
|
||||
/* extract bit from TMS and TDI sequence */
|
||||
uint8_t tms_bit = (tms[buf_pos >> 3] >> (buf_pos & 0x07) & 0x01);
|
||||
uint8_t tdi_bit = (tdi[buf_pos >> 3] >> (buf_pos & 0x07) & 0x01);
|
||||
|
||||
if (_verbose) {
|
||||
char mess[256];
|
||||
snprintf(mess, 256, "tms %d -> %d tdi %d -> %d mode %d %d/%d (%d)",
|
||||
_curr_tms, tms_bit, _curr_tdi, tdi_bit, mode, buf_pos, len, buff_len);
|
||||
printInfo(mess);
|
||||
}
|
||||
|
||||
/* possible case:
|
||||
* tdi & tms not changed -> write tdi
|
||||
* tdi change but tms not -> write tdi
|
||||
* tdi idem but tms changed -> write tms
|
||||
* tdi & tms changed -> serie of write tms
|
||||
* but finally:
|
||||
* if tms is not changed -> write tdi
|
||||
* otherwise -> write tms
|
||||
*/
|
||||
/* tms unchanged -> try to use TDI/TDO transaction */
|
||||
if (tms_bit == _curr_tms) {
|
||||
/* a tms transaction exist but not flushed or full
|
||||
* if tdi is not changed -> update tms sequence
|
||||
*/
|
||||
if (mode == 2 && buff_len != 0 && tdi_bit == _curr_tdi) {
|
||||
ret = update_tms_buff(&tms_tmp, tms_bit, buff_len,
|
||||
tdi_bit, tdo);
|
||||
if (ret < 0)
|
||||
return false;
|
||||
else
|
||||
buff_len = ret;
|
||||
} else {
|
||||
/* tdi sequence
|
||||
* first flush tms sequence if required */
|
||||
if (mode != 1 && buff_len != 0) {
|
||||
ret = update_tms_buff(&tms_tmp, 0, buff_len, _curr_tdi,
|
||||
tdo, true);
|
||||
if (ret < 0)
|
||||
return false;
|
||||
else
|
||||
buff_len = ret;
|
||||
}
|
||||
/* update tdi buffer with one more bit */
|
||||
uint8_t mask = (1 << (buff_len & 0x07));
|
||||
if (tdi_bit)
|
||||
tdi_buf[buff_len >> 3] |= mask;
|
||||
else
|
||||
tdi_buf[buff_len >> 3] &= ~mask;
|
||||
buff_len++;
|
||||
mode = 1;
|
||||
}
|
||||
/* TMS is changed -> TMS transaction */
|
||||
} else {
|
||||
/* flush */
|
||||
/* previous write TDI -> flush */
|
||||
if (mode == 1 && buff_len > 0) {
|
||||
bool is_end = false;
|
||||
/* tms 0 -> 1: it's handled by writeTDI:
|
||||
* append bit to avoid another transaction */
|
||||
if (_curr_tms == 0 && tms_bit == 1) {
|
||||
uint8_t mask = (1 << (buff_len & 0x07));
|
||||
if (tdi_bit)
|
||||
tdi_buf[buff_len >> 3] |= mask;
|
||||
else
|
||||
tdi_buf[buff_len >> 3] &= ~mask;
|
||||
buff_len++;
|
||||
is_end = true;
|
||||
}
|
||||
writeTDI(tdi_buf, tdo_tmp, buff_len, is_end);
|
||||
update_tdo_buff(tdo_tmp, tdo, buff_len);
|
||||
memset(tdi_buf, 0, max_len);
|
||||
buff_len = 0;
|
||||
if (is_end) {
|
||||
_curr_tdi = tdi_bit;
|
||||
mode = 1;
|
||||
continue;
|
||||
}
|
||||
/* same state -> simply flush */
|
||||
} else if (tdi_bit != _curr_tdi && mode == 2 && buff_len > 0) {
|
||||
ret = update_tms_buff(&tms_tmp, 0, buff_len, _curr_tdi, tdo, true);
|
||||
if (ret < 0)
|
||||
return false;
|
||||
else
|
||||
buff_len = ret;
|
||||
tms_tmp = 0;
|
||||
}
|
||||
/* update */
|
||||
ret = update_tms_buff(&tms_tmp, tms_bit, buff_len, tdi_bit, tdo);
|
||||
if (ret < 0)
|
||||
return false;
|
||||
else
|
||||
buff_len = ret;
|
||||
mode = 2;
|
||||
}
|
||||
|
||||
/* buffer full? */
|
||||
if (buff_len == 8*max_len && mode == 1) {
|
||||
writeTDI(tdi_buf, tdo_tmp, buff_len, false);
|
||||
update_tdo_buff(tdo_tmp, tdo, buff_len);
|
||||
memset(tdi_buf, 0, max_len);
|
||||
buff_len = 0;
|
||||
} else if (buff_len == 6 && mode == 2) {
|
||||
ret = update_tms_buff(&tms_tmp, 0, buff_len, _curr_tdi, tdo, true);
|
||||
if (ret < 0)
|
||||
return false;
|
||||
else
|
||||
buff_len = ret;
|
||||
tms_tmp = 0;
|
||||
}
|
||||
_curr_tdi = tdi_bit;
|
||||
_curr_tms = tms_bit;
|
||||
}
|
||||
|
||||
/* end -> force flush buffers */
|
||||
if (buff_len > 0) {
|
||||
switch (mode) {
|
||||
case 1:
|
||||
writeTDI(tdi_buf, tdo_tmp, buff_len, false);
|
||||
update_tdo_buff(tdo_tmp, tdo, buff_len);
|
||||
break;
|
||||
case 2:
|
||||
if (update_tms_buff(&tms_tmp, 0, buff_len, _curr_tdi,
|
||||
tdo, true) < 0)
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (_verbose) {
|
||||
printSuccess("end state: tdi " + std::to_string(_curr_tdi) +
|
||||
" tms " + std::to_string(_curr_tms));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,16 @@ class FtdiJtagMPSSE : public JtagInterface, public FTDIpp_MPSSE {
|
|||
/* TDI */
|
||||
int writeTDI(uint8_t *tx, uint8_t *rx, uint32_t len, bool end) override;
|
||||
|
||||
/*!
|
||||
* \brief send TMD and TDI and receive tdo bits;
|
||||
* \param tms: array of TMS values (used to write)
|
||||
* \param tdi: array of TDI values (used to write)
|
||||
* \param tdo: array of TDO values (used when read)
|
||||
* \param len: number of bit to send/receive
|
||||
* \return true with full buffers are sent, false otherwise
|
||||
*/
|
||||
virtual bool writeTMSTDI(const uint8_t *tms, const uint8_t *tdi, uint8_t *tdo,
|
||||
uint32_t len) override;
|
||||
/*!
|
||||
* \brief return internal buffer size (in byte).
|
||||
* \return _buffer_size -3 for mpsse cmd + size, -1 for potential SEND_IMMEDIATE
|
||||
|
|
@ -50,6 +60,19 @@ class FtdiJtagMPSSE : public JtagInterface, public FTDIpp_MPSSE {
|
|||
|
||||
private:
|
||||
void init_internal(const FTDIpp_MPSSE::mpsse_bit_config &cable);
|
||||
/* writeTMSTDI specifics */
|
||||
/*!
|
||||
* \brief try to append tms buffer, flush content if > 6
|
||||
* \param buffer: current tms buffer
|
||||
* \param bit: bit to append
|
||||
* \param offset: bits already stored
|
||||
* \param tdo: buffer used when reading after flush
|
||||
* \param len: length
|
||||
* \return < 0 if transaction fails, offset + 1 when append and 0 when flush
|
||||
*/
|
||||
int32_t update_tms_buff(uint8_t *buffer, uint8_t bit,
|
||||
uint32_t offset, uint8_t tdi, uint8_t *tdo, bool end=false);
|
||||
uint32_t update_tdo_buff(uint8_t *buffer, uint8_t *tdo, uint32_t len);
|
||||
/*!
|
||||
* \brief configure read and write edge (pos or neg), with freq < 15MHz
|
||||
* neg is used for write and pos to sample. with freq >= 15MHz
|
||||
|
|
@ -60,5 +83,9 @@ class FtdiJtagMPSSE : public JtagInterface, public FTDIpp_MPSSE {
|
|||
uint8_t _write_mode; /**< write edge configuration */
|
||||
uint8_t _read_mode; /**< read edge configuration */
|
||||
bool _invert_read_edge; /**< read edge selection (false: pos, true: neg) */
|
||||
/* writeTMSTDI specifics */
|
||||
uint32_t _tdo_pos;
|
||||
uint8_t _curr_tdi;
|
||||
uint8_t _curr_tms;
|
||||
};
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -64,8 +64,8 @@ class Jlink: public JtagInterface {
|
|||
* \param[out] tdo: tdo buffer
|
||||
* \param[in] numbits: tms/tdi/tdo buffer size (in bit)
|
||||
*/
|
||||
bool writeTMSTDI(const uint8_t *tms, const uint8_t *tdi, uint8_t *tdo,
|
||||
uint32_t numbits);
|
||||
virtual bool writeTMSTDI(const uint8_t *tms, const uint8_t *tdi,
|
||||
uint8_t *tdo, uint32_t numbits) override;
|
||||
|
||||
/*!
|
||||
* \brief send a serie of clock cycle with constant TMS and TDI
|
||||
|
|
|
|||
|
|
@ -41,7 +41,17 @@ class JtagInterface {
|
|||
* \return number of bit written and/or read
|
||||
*/
|
||||
virtual int writeTDI(uint8_t *tx, uint8_t *rx, uint32_t len, bool end) = 0;
|
||||
|
||||
/*!
|
||||
* \brief send TMD and TDI and receive tdo bits;
|
||||
* \param tms: array of TMS values (used to write)
|
||||
* \param tdi: array of TDI values (used to write)
|
||||
* \param tdo: array of TDO values (used when read)
|
||||
* \param len: number of bit to send/receive
|
||||
* \return true with full buffers are sent, false otherwise
|
||||
*/
|
||||
virtual bool writeTMSTDI(const uint8_t *tms, const uint8_t *tdi,
|
||||
uint8_t *tdo, uint32_t len)
|
||||
{ (void)tms; (void)tdi; (void)tdo; (void)len; return false;}
|
||||
/*!
|
||||
* \brief toggle clk without touch of TDI/TMS
|
||||
* \param tms: state of tms signal
|
||||
|
|
|
|||
Loading…
Reference in New Issue