// SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2020 Gwenhael Goavec-Merou */ #include #include #include #include #include #include #include #include "display.hpp" #include "ftdipp_mpsse.hpp" #include "fx2_ll.hpp" #include "usbBlaster.hpp" using namespace std; #define DO_READ (1 << 6) #define DO_WRITE (0 << 6) #define DO_RDWR (1 << 6) #define DO_SHIFT (1 << 7) #define DO_BITBB (0 << 7) #define DEFAULT ((1<<2) | (1<<3) | (1 << 5)) #define DEBUG 0 #ifdef DEBUG #define display(...) \ do { \ if (_verbose) fprintf(stdout, __VA_ARGS__); \ }while(0) #else #define display(...) do {}while(0) #endif UsbBlaster::UsbBlaster(const cable_t &cable, const std::string &firmware_path, int8_t verbose): _verbose(verbose > 1), _nb_bit(0), _curr_tms(0), _buffer_size(64) { if (cable.pid == 0x6001) ll_driver = new UsbBlasterI(); else if (cable.pid == 0x6810) ll_driver = new UsbBlasterII(firmware_path); else throw std::runtime_error("usb-blaster: unknown VID/PID"); _tck_pin = (1 << 0); _tms_pin = (1 << 1); _tdi_pin = (1 << 4); _in_buf = (unsigned char *)malloc(sizeof(unsigned char) * _buffer_size); _nb_bit = 0; memset(_in_buf, 0, _buffer_size); /* Force flush internal FT245 internal buffer */ if (cable.pid == 0x6001) { uint8_t val = DEFAULT | DO_WRITE | DO_BITBB | _tms_pin; uint8_t tmp_buf[4096]; for (_nb_bit = 0; _nb_bit < 4096; _nb_bit += 2) { tmp_buf[_nb_bit ] = val; tmp_buf[_nb_bit + 1] = val | _tck_pin; } ll_driver->write(tmp_buf, _nb_bit, NULL, 0); _nb_bit = 0; } } UsbBlaster::~UsbBlaster() { _in_buf[_nb_bit++] = 0; flush(); free(_in_buf); } int UsbBlaster::setClkFreq(uint32_t clkHZ) { return ll_driver->setClkFreq(clkHZ); } uint32_t UsbBlaster::getClkFreq() { return ll_driver->getClkFreq(); } int UsbBlaster::writeTMS(const uint8_t *tms, uint32_t len, bool flush_buffer, __attribute__((unused)) const uint8_t tdi) { int ret; /* nothing to send * but maybe need to flush internal buffer */ if (len == 0) { if (flush_buffer) { ret = flush(); return ret; } return 0; } /* check for at least one bit space in buffer */ if (_nb_bit+2 > _buffer_size) { ret = flush(); if (ret < 0) return ret; } /* fill buffer to reduce USB transaction */ for (uint32_t i = 0; i < len; i++) { _curr_tms = ((tms[i >> 3] & (1 << (i & 0x07)))? _tms_pin : 0); uint8_t val = DEFAULT | DO_WRITE | DO_BITBB | _tdi_pin | _curr_tms; _in_buf[_nb_bit++] = val; _in_buf[_nb_bit++] = val | _tck_pin; if (_nb_bit + 2 > _buffer_size) { ret = flush(); if (ret < 0) return ret; } } _in_buf[_nb_bit++] = DEFAULT | DO_WRITE | DO_BITBB | _curr_tms; /* security check: try to flush buffer */ if (flush_buffer) { ret = flush(); if (ret < 0) return ret; } return len; } #include "configBitstreamParser.hpp" int UsbBlaster::writeTDI(const uint8_t *tx, uint8_t *rx, uint32_t len, bool end) { uint32_t real_len = (end) ? len -1 : len; uint32_t nb_byte = real_len >> 3; uint32_t nb_bit = (real_len & 0x07); uint8_t mode = (rx != NULL)? DO_RDWR : DO_WRITE; const uint8_t *tx_ptr = tx; uint8_t *rx_ptr = rx; /* security: send residual * it's possible since all functions do a flush at the end * okay it's maybe less efficient */ _in_buf[_nb_bit++] = DEFAULT | DO_BITBB | DO_WRITE | _curr_tms; flush(); if (_curr_tms == 0 && nb_byte != 0) { uint8_t mask = DO_SHIFT | mode; while (nb_byte != 0) { uint32_t tx_len = nb_byte; if (tx_len > 63) tx_len = 63; /* if not enough space flush */ if (_nb_bit + tx_len + 1 > 64) { int num_read = _nb_bit -1; if (writeByte((rx)? rx_ptr:NULL, num_read) < 0) return -EXIT_FAILURE; if (rx) rx_ptr += num_read; } _in_buf[_nb_bit++] = mask | (tx_len & 0x3f); if (tx) { memcpy(&_in_buf[_nb_bit], tx_ptr, tx_len); tx_ptr += tx_len; } else { memset(&_in_buf[_nb_bit], 0, tx_len); } _nb_bit += tx_len; nb_byte -= tx_len; } if (_nb_bit != 0) { int num_read = _nb_bit-1; if (writeByte((rx)? rx_ptr:NULL, num_read) < 0) return -EXIT_FAILURE; if (rx) rx_ptr += num_read; } } if (nb_bit != 0) { uint8_t mask = DEFAULT | DO_BITBB; if (_nb_bit + 2 > _buffer_size) { int num_read = _nb_bit; if (writeBit((rx)? rx_ptr:NULL, num_read/2) < 0) return -EXIT_FAILURE; if (rx) rx_ptr += num_read; } for (uint32_t i = 0; i < nb_bit; i++) { uint8_t val = 0; if (tx) val |= ((tx_ptr[i >> 3] & (1 << (i & 0x07)))? _tdi_pin : 0); _in_buf[_nb_bit++] = mask | val; _in_buf[_nb_bit++] = mask | mode | val | _tck_pin; } int num_read = _nb_bit; if (writeBit((rx)? rx_ptr:NULL, num_read/2) < 0) return -EXIT_FAILURE; if (rx) // realign bits *rx_ptr >>= (8 - nb_bit); } /* set TMS high */ if (end) { _curr_tms = _tms_pin; uint8_t mask = DEFAULT | DO_BITBB | _curr_tms; if (tx && *tx_ptr & (1 << nb_bit)) mask |= _tdi_pin; _in_buf[_nb_bit++] = mask; _in_buf[_nb_bit++] = mask | mode | _tck_pin; uint8_t tmp = 0; if (writeBit((rx)? &tmp:NULL, 1) < 0) return -EXIT_FAILURE; if (rx) *rx_ptr |= (tmp & 0x80) >> (7 - nb_bit); _in_buf[_nb_bit++] = mask; if (writeBit(NULL, 0) < 0) return -EXIT_FAILURE; } return len; } int UsbBlaster::toggleClk(uint8_t tms, uint8_t tdi, uint32_t clk_len) { int xfer_len = clk_len; int mask = DO_SHIFT | DO_WRITE; /* try to use shift mode but only when * xfer > 1Byte and tms is low */ if (tms == 0 && xfer_len >= 8) { if (_nb_bit == 64) flush(); _in_buf[_nb_bit++] = DEFAULT | DO_WRITE | DO_BITBB; flush(); /* fill a byte with all 1 or all 0 */ uint8_t content = (tdi)?0xff:0; while (xfer_len >= 8) { uint32_t tx_len = (xfer_len >> 3); if (tx_len > 63) tx_len = 63; /* if not enough space flush */ if (_nb_bit + tx_len + 1 > 64) if (flush() < 0) return -EXIT_FAILURE; _in_buf[_nb_bit++] = mask | static_cast(tx_len); for (uint32_t i = 0; i < tx_len; i++) _in_buf[_nb_bit++] = content; xfer_len -= (tx_len << 3); } } mask = DEFAULT | DO_BITBB | DO_WRITE | ((tms) ? _tms_pin : 0) | ((tdi) ? _tdi_pin : 0); while (xfer_len > 0) { if (_nb_bit + 2 > _buffer_size) if (flush() < 0) return -EXIT_FAILURE; _in_buf[_nb_bit++] = mask; _in_buf[_nb_bit++] = mask | _tck_pin; xfer_len--; } /* flush */ if (_nb_bit == 64) flush(); _in_buf[_nb_bit++] = mask; flush(); return clk_len; } int UsbBlaster::flush() { return write(false, 0); } /* simply call write and return buffer */ int UsbBlaster::writeByte(uint8_t *tdo, int nb_byte) { int ret = write(tdo != NULL, nb_byte); if (tdo && ret > 0) memcpy(tdo, _in_buf, nb_byte); return ret; } /* call write with a temporary buffer * if tdo reconstruct message */ int UsbBlaster::writeBit(uint8_t *tdo, int nb_bit) { int ret = write(tdo != NULL, nb_bit); if (tdo && ret > 0) { /* need to reconstruct received word * since jtag is LSB first we need to shift right content by 1 * and add 0x80 (1 << 7) or 0 * the buffer may contains some tms bit, so start with i * equal to fill exactly nb_bit bits * */ for (int i = 0, offset=0; i < nb_bit; i++, offset++) { tdo[offset >> 3] = (((_in_buf[i] & (1<<0)) ? 0x80 : 0x00) | (tdo[offset >> 3] >> 1)); } } return ret; } int UsbBlaster::write(bool read, int rd_len) { if (_nb_bit == 0) return 0; int ret = ll_driver->write(_in_buf, _nb_bit, (read)?_in_buf:NULL, rd_len); _nb_bit = 0; return ret; } /* * USB Blash I specific implementation */ UsbBlasterI::UsbBlasterI() { int ret; _ftdi = ftdi_new(); if (_ftdi == NULL) { cout << "open_device: failed to initialize ftdi" << endl; throw std::exception(); } ret = ftdi_usb_open(_ftdi, 0x09fb, 0x6001); if (ret < 0) { fprintf(stderr, "unable to open ftdi device: %d (%s)\n", ret, ftdi_get_error_string(_ftdi)); ftdi_free(_ftdi); throw std::exception(); } ret = ftdi_usb_reset(_ftdi); if (ret < 0) { fprintf(stderr, "Error reset: %d (%s)\n", ret, ftdi_get_error_string(_ftdi)); ftdi_free(_ftdi); throw std::exception(); } ret = ftdi_set_latency_timer(_ftdi, 2); if (ret < 0) { fprintf(stderr, "Error set latency timer: %d (%s)\n", ret, ftdi_get_error_string(_ftdi)); ftdi_free(_ftdi); throw std::exception(); } } UsbBlasterI::~UsbBlasterI() { } int UsbBlasterI::setClkFreq(uint32_t clkHZ) { (void) clkHZ; printWarn("USB-BlasterI has a 24MHz fixed frequency"); return 1; } uint32_t UsbBlasterI::getClkFreq() { return 24e6; } int UsbBlasterI::write(uint8_t *wr_buf, int wr_len, uint8_t *rd_buf, int rd_len) { int ret = 0; ret = ftdi_write_data(_ftdi, wr_buf, wr_len); if (ret != wr_len) { printf("problem %d written %d\n", ret, wr_len); return ret; } if (rd_buf) { int timeout = 100; uint8_t byte_read = 0; while (byte_read < rd_len && timeout != 0) { timeout--; ret = ftdi_read_data(_ftdi, rd_buf + byte_read, rd_len - byte_read); if (ret < 0) { printError("Read error: " + std::to_string(ret)); return ret; } byte_read += ret; } if (timeout == 0) { printError("Error: timeout " + std::to_string(byte_read) + " " + std::to_string(rd_len)); for (int i=0; i < byte_read; i++) printf("%02x ", rd_buf[i]); printf("\n"); return 0; } } return ret; } /* * USB Blash II specific implementation */ UsbBlasterII::UsbBlasterII(const string &firmware_path) { std::string fpath; uint8_t buf[5]; if (firmware_path.empty() && strlen(BLASTERII_DIR) == 0) { printError("missing FX2 firmware"); printError("use --probe-firmware with something"); printError("like /opt/intelFPGA/VERSION/quartus/linux64/blaster_6810.hex"); printError("Or use -DBLASTERII_PATH=/opt/intelFPGA/VERSION/quartus/linux64"); throw std::runtime_error("usbBlasterII: missing firmware"); } if (firmware_path.empty()) fpath = BLASTERII_DIR "/blaster_6810.hex"; else fpath = firmware_path; fx2 = new FX2_ll(0x09fb, 0x6810, 0x09fb, 0x6010, fpath); if (!fx2->read_ctrl(0x94, 0, buf, 5)) { throw std::runtime_error("Unable to read firmware version."); } printInfo("USB-Blaster II firmware version: " + std::string((char *)buf)); } UsbBlasterII::~UsbBlasterII() { delete fx2; } int UsbBlasterII::setClkFreq(uint32_t clkHZ) { (void) clkHZ; printWarn("USB-BlasterII has a 24MHz fixed frequency"); return 1; } uint32_t UsbBlasterII::getClkFreq() { return 24e6; } int UsbBlasterII::write(uint8_t *wr_buf, int wr_len, uint8_t *rd_buf, int rd_len) { int ret = 0; ret = fx2->write(4, wr_buf, wr_len); if (ret != wr_len) { printf("problem %d written %d\n", ret, wr_len); return ret; } if (rd_buf) { uint8_t c = 0x5f; ret = fx2->write(4, &c, 1); if (ret != 1) { printf("problem %d written %d\n", ret, wr_len); return ret; } int timeout = 100; uint8_t byte_read = 0; while (byte_read < rd_len && timeout != 0) { timeout--; ret = fx2->read(8, rd_buf + byte_read, rd_len - byte_read); if (ret < 0) { printError("Read error: " + std::to_string(ret)); return ret; } byte_read += ret; } if (timeout == 0) { printError("Error: timeout " + std::to_string(byte_read) + " " + std::to_string(rd_len)); for (int i=0; i < byte_read; i++) printf("%02x ", rd_buf[i]); printf("\n"); return 0; } } return ret; }