From c82a8e62077ee322f938342dd43e00acc20a96ad Mon Sep 17 00:00:00 2001 From: Alexey Starikovskiy Date: Fri, 1 Sep 2023 20:24:10 +0300 Subject: [PATCH] Make CH347 driver faster Speed up toggleClk Defer write-only USB transactions to better utilize bus --- src/ch347jtag.cpp | 152 +++++++++++++++++++++++++++++----------------- src/ch347jtag.hpp | 17 +++--- 2 files changed, 107 insertions(+), 62 deletions(-) diff --git a/src/ch347jtag.cpp b/src/ch347jtag.cpp index 3f59352..15011f9 100644 --- a/src/ch347jtag.cpp +++ b/src/ch347jtag.cpp @@ -28,7 +28,7 @@ using namespace std; #define CH347JTAG_WRITE_EP 0x06 #define CH347JTAG_READ_EP 0x86 -#define CH347JTAG_TIMEOUT 2000 +#define CH347JTAG_TIMEOUT 200 enum CH347JtagCmd { CMD_BYTES_WO = 0xd3, @@ -43,13 +43,39 @@ enum CH347JtagSig { SIG_TMS = 0b10, SIG_TDI = 0b10000, }; + static void LIBUSB_CALL sync_cb(struct libusb_transfer *transfer) { int *complete = (int *)transfer->user_data; - *complete = true; + *complete = 1; } -int CH347Jtag::usb_xfer(unsigned wlen, unsigned rlen, unsigned *ract) { - wcomplete = 0; +// defer should only be used with rlen == 0 + +int CH347Jtag::usb_xfer(unsigned wlen, unsigned rlen, unsigned *ract, bool defer) { + if (_verbose) { + fprintf(stderr, "usb_xfer: deferred: %ld\n", obuf - _obuf); + } + if (defer && !rlen && obuf - _obuf + wlen < MAX_BUFFER - 12) { + obuf += wlen; + return 0; + } + + // write out whole buffer + + if (obuf - _obuf > MAX_BUFFER) { + throw runtime_error("buffer overflow"); + } + + wlen += obuf - _obuf; + if (wlen > MAX_BUFFER) { + throw runtime_error("buffer overflow"); + } + obuf = _obuf; + + if (wlen == 0) { + return 0; + } + if (_verbose) { fprintf(stderr, "obuf[%d] = {", wlen); for (unsigned i = 0; i < wlen; ++i) { @@ -57,6 +83,8 @@ int CH347Jtag::usb_xfer(unsigned wlen, unsigned rlen, unsigned *ract) { } fprintf(stderr, "}\n\n"); } + + int wcomplete = 0, rcomplete = 0; libusb_fill_bulk_transfer(wtrans, dev_handle, CH347JTAG_WRITE_EP, obuf, wlen, sync_cb, &wcomplete, CH347JTAG_TIMEOUT); int r = libusb_submit_transfer(wtrans); @@ -86,8 +114,6 @@ int CH347Jtag::usb_xfer(unsigned wlen, unsigned rlen, unsigned *ract) { wcomplete = 1; } } - unsigned rdone = 0; -wait_rcompletion: while (!rcomplete) { r = libusb_handle_events_completed(usb_ctx, &rcomplete); if (r < 0) { @@ -104,24 +130,16 @@ wait_rcompletion: return LIBUSB_ERROR_IO; } if (rlen) { - rdone += rtrans->actual_length; if (rtrans->status != LIBUSB_TRANSFER_COMPLETED) { return LIBUSB_ERROR_IO; } - if (rlen > rdone) { - rcomplete = 0; - libusb_fill_bulk_transfer(rtrans, dev_handle, CH347JTAG_READ_EP, - &ibuf[rdone], rlen - rdone, sync_cb, &rcomplete, - CH347JTAG_TIMEOUT); - r = libusb_submit_transfer(rtrans); - if (r < 0) - return r; - goto wait_rcompletion; + if ((int)rlen > rtrans->actual_length) { + throw runtime_error("input buffer underflow"); } - if (ract) *ract = rdone; + if (ract) *ract = rtrans->actual_length; if (_verbose) { - fprintf(stderr, "ibuf[%d] = {", rdone); - for (unsigned i = 0; i < rdone; ++i) { + fprintf(stderr, "ibuf[%d] = {", rtrans->actual_length); + for (int i = 0; i < rtrans->actual_length; ++i) { fprintf(stderr, "%02x ", ibuf[i]); } fprintf(stderr, "}\n\n"); @@ -131,12 +149,14 @@ wait_rcompletion: } int CH347Jtag::setClk(const uint8_t &factor) { + // flush the obuf + usb_xfer(0, 0, 0, false); // is called from constructor, don't replace with virtual flush() memset(obuf, 0, 16); obuf[0] = CMD_CLK; obuf[1] = 6; obuf[4] = factor; unsigned actual = 0; - int rv = usb_xfer(9, 4, &actual); + int rv = usb_xfer(9, 4, &actual, false); if (rv || actual != 4) return -1; if (ibuf[0] != 0xd0 || ibuf[3] != 0) @@ -145,7 +165,7 @@ int CH347Jtag::setClk(const uint8_t &factor) { } CH347Jtag::CH347Jtag(uint32_t clkHZ, int8_t verbose): - _verbose(verbose>1), dev_handle(NULL), usb_ctx(NULL) + _verbose(verbose>1), dev_handle(NULL), usb_ctx(NULL), obuf(_obuf) { int actual_length = 0; struct libusb_device_descriptor desc; @@ -223,47 +243,52 @@ CH347Jtag::~CH347Jtag() int CH347Jtag::_setClkFreq(uint32_t clkHZ) { - unsigned i = 0, sl = 2000000; - if (clkHZ <= 2000000) { - _clkHZ = 2000000; - i = 0; - } else { - for (; i < 5; ++i, sl *= 2) { - if (clkHZ < sl) break; - _clkHZ = sl; - } - i--; +#if 1 + unsigned i = 0, sl = 60000000 >> 5; + for (; i < 6; ++i, sl <<= 1) { + if (clkHZ < sl) + break; } +#else + int i = 6; + unsigned sl = 60000000; + for (; i > 1; --i, sl >>= 1) { + if (clkHZ > sl) + break; + } +#endif + _clkHZ = sl; if (setClk(i)) { printError("failed to set clock rate"); return 0; } char mess[256]; - snprintf(mess, 256, "JTAG TCK frequency set to %d MHz\n\n", _clkHZ/1000000); + snprintf(mess, 256, "JTAG TCK frequency set to %.3f MHz\n\n", _clkHZ/1e6); printInfo(mess); return _clkHZ; } int CH347Jtag::writeTMS(const uint8_t *tms, uint32_t len, bool flush_buffer) { - (void) flush_buffer; - + if (get_obuf_length() < (int)(len * 2 + 4)) { // check if there is enough room left + flush(); + } uint8_t *ptr = obuf; for (uint32_t i = 0; i < len; ++i) { if (ptr == obuf) { *ptr++ = CMD_BITS_WO; ptr += 2; // leave place for length; } - uint8_t x = /*SIG_TDI |*/ ((tms[i >> 3] & (1 << (i & 7))) ? SIG_TMS : 0); + uint8_t x = ((tms[i >> 3] & (1 << (i & 7))) ? SIG_TMS : 0); *ptr++ = x; *ptr++ = x | SIG_TCK; - unsigned wlen = ptr - obuf; - if (wlen > sizeof(obuf) - 3 || i == len - 1) { + int wlen = ptr - obuf; + if (wlen + 1 >= get_obuf_length() || i == len - 1) { *ptr++ = x; // clear TCK - ++wlen; + wlen = ptr - obuf; obuf[1] = wlen - 3; obuf[2] = (wlen - 3) >> 8; - int ret = usb_xfer(wlen, 0, 0); + int ret = usb_xfer(wlen, 0, 0, !flush_buffer); if (ret < 0) { cerr << "writeTMS: usb bulk write failed: " << libusb_strerror(static_cast(ret)) << endl; @@ -280,6 +305,13 @@ int CH347Jtag::toggleClk(uint8_t tms, uint8_t tdi, uint32_t len) uint8_t bits = 0; if (tms) bits |= SIG_TMS; if (tdi) bits |= SIG_TDI; + if (!bits && len > 7) { + return writeTDI(0, 0, len, false); + } + + if (get_obuf_length() < (int)(len * 2 + 4)) { + flush(); + } uint8_t *ptr = obuf; for (uint32_t i = 0; i < len; ++i) { @@ -289,13 +321,13 @@ int CH347Jtag::toggleClk(uint8_t tms, uint8_t tdi, uint32_t len) } *ptr++ = bits; *ptr++ = bits | SIG_TCK; - unsigned wlen = ptr - obuf; - if (wlen > sizeof(obuf) - 3 || i == len - 1) { + int wlen = ptr - obuf; + if (wlen + 1 >= get_obuf_length() || i == len - 1) { *ptr++ = bits; // clear TCK - ++wlen; + wlen = ptr - obuf; obuf[1] = wlen - 3; obuf[2] = (wlen - 3) >> 8; - int ret = usb_xfer(wlen, 0, 0); + int ret = usb_xfer(wlen, 0, 0, true); if (ret < 0) { cerr << "writeCLK: usb bulk write failed: " << libusb_strerror(static_cast(ret)) << endl; @@ -309,25 +341,32 @@ int CH347Jtag::toggleClk(uint8_t tms, uint8_t tdi, uint32_t len) int CH347Jtag::writeTDI(const uint8_t *tx, uint8_t *rx, uint32_t len, bool end) { - if (!tx || !len) + if (len == 0) return 0; - unsigned bytes = (len - ((end)?1:0)) / 8; + unsigned bytes = (len - (end ? 1 : 0)) / 8; unsigned bits = len - bytes * 8; uint8_t *rptr = rx; const uint8_t *tptr = tx; const uint8_t *txend = tx + bytes; - uint8_t cmd = (rx) ? CMD_BYTES_WR : CMD_BYTES_WO; + uint8_t cmd = (rx != nullptr) ? CMD_BYTES_WR : CMD_BYTES_WO; while (tptr < txend) { - unsigned avail = sizeof(obuf) - 3; - unsigned chunk = (txend - tptr < avail)? txend - tptr: avail; - memcpy(&obuf[3], tptr, chunk); + if (get_obuf_length() < 4) { + flush(); + } + int avail = get_obuf_length() - 3; + int chunk = (txend - tptr < avail)? txend - tptr: avail; + if (tx) { + memcpy(&obuf[3], tptr, chunk); + } else { + memset(&obuf[3], 0, chunk); + } tptr += chunk; // write header obuf[0] = cmd; obuf[1] = chunk; obuf[2] = chunk >> 8; unsigned actual_length = 0; - int ret = usb_xfer(chunk + 3, (rx) ? chunk + 3 : 0, &actual_length); + int ret = usb_xfer(chunk + 3, (rx) ? chunk + 3 : 0, &actual_length, rx == 0 && get_obuf_length()); if (ret < 0) { cerr << "writeTDI: usb bulk read failed: " << libusb_strerror(static_cast(ret)) << endl; @@ -347,11 +386,14 @@ int CH347Jtag::writeTDI(const uint8_t *tx, uint8_t *rx, uint32_t len, bool end) if (bits == 0) return EXIT_SUCCESS; cmd = (rx) ? CMD_BITS_WR : CMD_BITS_WO; + if (get_obuf_length() < (int)(4 + bits * 2)) { + flush(); + } uint8_t *ptr = &obuf[3]; uint8_t x = 0; - const uint8_t *bptr = &tx[bytes]; + const uint8_t *bptr = tx + bytes; for (unsigned i = 0; i < bits; ++i) { - uint8_t txb = bptr[i >> 3]; + uint8_t txb = (tx) ? bptr[i >> 3] : 0; uint8_t _tdi = (txb & (1 << (i & 7))) ? SIG_TDI : 0; x = _tdi; if (end && i == bits - 1) { @@ -365,7 +407,7 @@ int CH347Jtag::writeTDI(const uint8_t *tx, uint8_t *rx, uint32_t len, bool end) obuf[0] = cmd; obuf[1] = wlen - 3; obuf[2] = (wlen - 3) >> 8; - int ret = usb_xfer(wlen, (rx) ? (bits + 3) : 0, &actual_length); + int ret = usb_xfer(wlen, (rx) ? (bits + 3) : 0, &actual_length, rx == nullptr); if (ret < 0) { cerr << "writeTDI: usb bulk read failed: " << @@ -381,9 +423,9 @@ int CH347Jtag::writeTDI(const uint8_t *tx, uint8_t *rx, uint32_t len, bool end) cerr << "writeTDI: invalid read data: " << endl; return -EXIT_FAILURE; } - for (unsigned i = 0; i < size / 16; ++i) { + for (unsigned i = 0; i < size / 8; ++i) { uint8_t b = 0; - uint8_t *xb = &ibuf[3 + i * 16 + 1]; + uint8_t *xb = &ibuf[3 + i * 8]; for (unsigned j = 0; j < 8; ++j) b |= (xb[j] & 1) << j; *rptr++ = b; diff --git a/src/ch347jtag.hpp b/src/ch347jtag.hpp index a02fc1e..4abb4de 100644 --- a/src/ch347jtag.hpp +++ b/src/ch347jtag.hpp @@ -8,6 +8,8 @@ #include "jtagInterface.hpp" +constexpr unsigned MAX_BUFFER = 512; + class CH347Jtag : public JtagInterface { public: CH347Jtag(uint32_t clkHZ, int8_t verbose); @@ -22,11 +24,11 @@ class CH347Jtag : public JtagInterface { /* clk */ int toggleClk(uint8_t tms, uint8_t tdo, uint32_t clk_len) override; - int get_buffer_size() override { return 0;} + int get_buffer_size() override {return get_obuf_length();} - bool isFull() override { return false;} + bool isFull() override {return get_obuf_length() == 0;} - int flush() override {return 0;} + int flush() override {return usb_xfer(0, 0, 0, false);} private: bool _verbose; @@ -35,8 +37,9 @@ class CH347Jtag : public JtagInterface { libusb_device_handle *dev_handle; libusb_context *usb_ctx; struct libusb_transfer *wtrans, *rtrans; - int rcomplete, wcomplete; - uint8_t ibuf[512]; - uint8_t obuf[512]; - int usb_xfer(unsigned wlen, unsigned rlen, unsigned *actual); + uint8_t ibuf[MAX_BUFFER]; + uint8_t _obuf[MAX_BUFFER]; + uint8_t *obuf; + int get_obuf_length() const {return MAX_BUFFER - (obuf - _obuf);} + int usb_xfer(unsigned wlen, unsigned rlen, unsigned *actual, bool defer); };