diff --git a/src/ftdiJtagMPSSE.cpp b/src/ftdiJtagMPSSE.cpp index 61d7a0d..ed4c88b 100644 --- a/src/ftdiJtagMPSSE.cpp +++ b/src/ftdiJtagMPSSE.cpp @@ -13,6 +13,7 @@ #include #include +#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(MPSSE_WRITE_TMS | MPSSE_LSB | + MPSSE_BITMODE | _write_mode | + MPSSE_DO_READ | _read_mode + ), + static_cast(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; +} diff --git a/src/ftdiJtagMPSSE.hpp b/src/ftdiJtagMPSSE.hpp index d0f997d..99f2527 100644 --- a/src/ftdiJtagMPSSE.hpp +++ b/src/ftdiJtagMPSSE.hpp @@ -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 diff --git a/src/jlink.hpp b/src/jlink.hpp index 7415d1b..152f78a 100644 --- a/src/jlink.hpp +++ b/src/jlink.hpp @@ -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 diff --git a/src/jtagInterface.hpp b/src/jtagInterface.hpp index 37a3a63..adc10f3 100644 --- a/src/jtagInterface.hpp +++ b/src/jtagInterface.hpp @@ -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