From c41911d0395e712c94117f5ff3b1395994a45fb4 Mon Sep 17 00:00:00 2001 From: Gwenhael Goavec-Merou Date: Thu, 20 Aug 2020 16:56:29 +0200 Subject: [PATCH] introduce anlogic JTAG adapter (tested with sipeed lichee tang --- CMakeLists.txt | 2 + README.md | 1 + src/anlogicCable.cpp | 294 +++++++++++++++++++++++++++++++++++++++++++ src/anlogicCable.hpp | 69 ++++++++++ src/cable.hpp | 10 +- src/jtag.cpp | 4 + 6 files changed, 376 insertions(+), 4 deletions(-) create mode 100644 src/anlogicCable.cpp create mode 100644 src/anlogicCable.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index f7932cd..9dc2820 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,6 +40,7 @@ if(USE_PKGCONFIG) endif() set(OPENFPGALOADER_SOURCE + src/anlogicCable.cpp src/dirtyJtag.cpp src/spiFlash.cpp src/usbBlaster.cpp @@ -68,6 +69,7 @@ set(OPENFPGALOADER_SOURCE set(OPENFPGALOADER_HEADERS src/altera.hpp + src/anlogicCable.hpp src/cxxopts.hpp src/dirtyJtag.hpp src/progressBar.hpp diff --git a/README.md b/README.md index 001a791..8f07c1e 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ detect correct model for flash programming. __Supported cables:__ +* anlogic JTAG adapter * [digilent_hs2](https://store.digilentinc.com/jtag-hs2-programming-cable/): jtag programmer cable from digilent * [DirtyJTAG](https://github.com/jeanthom/DirtyJTAG): JTAG probe firmware for STM32F1 * Intel USB Blaster: jtag programmer cable from intel/altera diff --git a/src/anlogicCable.cpp b/src/anlogicCable.cpp new file mode 100644 index 0000000..2bf63de --- /dev/null +++ b/src/anlogicCable.cpp @@ -0,0 +1,294 @@ +/* + * Copyright (C) 2020 Gwenhael Goavec-Merou + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include "anlogicCable.hpp" +#include "display.hpp" + +using namespace std; + +#define ANLOGICCABLE_VID 0x0547 +#define ANLOGICCABLE_PID 0x1002 + +#define ANLOGICCABLE_CONF_EP 0x08 +#define ANLOGICCABLE_WRITE_EP 0x06 +#define ANLOGICCABLE_READ_EP 0x82 + +#define ANLOGICCABLE_FREQ_CMD 0x01 + +enum analogicCablePin { + ANLOGICCABLE_TCK_PIN = (1 << 2), + ANLOGICCABLE_TDI_PIN = (1 << 1), + ANLOGICCABLE_TMS_PIN = (1 << 0) +}; + +enum analogicCableFreq { + ANLOGICCABLE_FREQ_6M = 0, + ANLOGICCABLE_FREQ_3M = 0x4, + ANLOGICCABLE_FREQ_2M = 0x8, + ANLOGICCABLE_FREQ_1M = 0x14, + ANLOGICCABLE_FREQ_600K = 0x24, + ANLOGICCABLE_FREQ_400K = 0x38, + ANLOGICCABLE_FREQ_200K = 0x70, + ANLOGICCABLE_FREQ_100K = 0xe8, + ANLOGICCABLE_FREQ_90K = 0xff +}; + +AnlogicCable::AnlogicCable(uint32_t clkHZ, bool verbose): + _verbose(verbose), + dev_handle(NULL), usb_ctx(NULL), _tdi(0), _tms(0) +{ + int ret; + + if (libusb_init(&usb_ctx) < 0) { + cerr << "libusb init failed" << endl; + throw std::exception(); + } + + dev_handle = libusb_open_device_with_vid_pid(usb_ctx, + ANLOGICCABLE_VID, ANLOGICCABLE_PID); + if (!dev_handle) { + cerr << "fails to open device" << endl; + libusb_exit(usb_ctx); + throw std::exception(); + } + + ret = libusb_claim_interface(dev_handle, 0); + if (ret) { + cerr << "libusb error while claiming DirtyJTAG interface #0" << endl; + libusb_close(dev_handle); + libusb_exit(usb_ctx); + throw std::exception(); + } + + if (clkHZ > 6000000) { + printInfo("Anlogic JTAG probe limited to 6MHz"); + clkHZ = 6000000; + } + if (setClkFreq(clkHZ) < 0) { + cerr << "Fail to set frequency" << endl; + throw std::exception(); + } +} + +AnlogicCable::~AnlogicCable() +{ + if (dev_handle) + libusb_close(dev_handle); + if (usb_ctx) + libusb_exit(usb_ctx); +} + +int AnlogicCable::setClkFreq(uint32_t clkHZ) +{ + int actual_length; + int ret; + uint8_t buf[] = {ANLOGICCABLE_FREQ_CMD, 0}; + if (clkHZ >= 6000000) + buf[1] = ANLOGICCABLE_FREQ_6M; + else if (clkHZ >= 3000000) + buf[1] = ANLOGICCABLE_FREQ_3M; + else if (clkHZ >= 1000000) + buf[1] = ANLOGICCABLE_FREQ_1M; + else if (clkHZ >= 600000) + buf[1] = ANLOGICCABLE_FREQ_600K; + else if (clkHZ >= 400000) + buf[1] = ANLOGICCABLE_FREQ_400K; + else if (clkHZ >= 200000) + buf[1] = ANLOGICCABLE_FREQ_200K; + else if (clkHZ >= 100000) + buf[1] = ANLOGICCABLE_FREQ_100K; + else if (clkHZ >= 90000) + buf[1] = ANLOGICCABLE_FREQ_90K; + ret = libusb_bulk_transfer(dev_handle, ANLOGICCABLE_CONF_EP, + buf, 2, &actual_length, 1000); + if (ret < 0) { + cerr << "setClkFreq: usb bulk write failed " << ret << endl; + return -EXIT_FAILURE; + } + + return clkHZ; +} + +int AnlogicCable::writeTMS(uint8_t *tms, int len, bool flush_buffer) +{ + (void) flush_buffer; + + if (len == 0) + return 0; + + uint8_t buf[512]; + uint8_t mask = (ANLOGICCABLE_TCK_PIN << 4); + + int full_len = len; + uint8_t *tx_ptr = tms; + + while (full_len > 0) { + /* when len > buffer capacity -> limit to capacity + * else use len + */ + int xfer_len = (full_len > 512) ? 512 : full_len; + + for (int i = 0; i < xfer_len; i++) { + buf[i] = mask; + if (tx_ptr[i >> 3] & (1 << (i & 0x07))) + buf[i] |= (ANLOGICCABLE_TMS_PIN | + (ANLOGICCABLE_TMS_PIN << 4)); + } + + /* when last burst, complite rest of buffer with + * fixed state + */ + if (xfer_len < 512) { + memset(&buf[xfer_len], buf[xfer_len-1] | ANLOGICCABLE_TCK_PIN, + 512-xfer_len); + } + + if (write(buf, NULL, 512, 0) < 0) + return -EXIT_FAILURE; + + full_len -= xfer_len; + tx_ptr += xfer_len; + } + + return len; +} + +int AnlogicCable::toggleClk(uint8_t tms, uint8_t tdi, uint32_t clk_len) +{ + uint8_t buf[512]; + uint8_t mask = ((tms) ? ANLOGICCABLE_TMS_PIN : 0) | + ((tdi) ? ANLOGICCABLE_TDI_PIN : 0); + mask |= (((mask & 0x0f) << 4) | ((ANLOGICCABLE_TCK_PIN << 4))); + uint8_t last = mask | ANLOGICCABLE_TCK_PIN; + + int len = clk_len; + while (len > 0) { + /* when len > buffer capacity -> limit to capacity + * else use len + */ + int xfer_len = (len > 512) ? 512 : len; + /* since value is always the same + * fill xfer_len byte with the same value + */ + memset(buf, mask, xfer_len); + /* when last burst, complite rest of buffer with + * fixed state + */ + if (xfer_len < 512) + memset(&buf[xfer_len], last, 512-xfer_len); + + if (write(buf, NULL, 512, 0) < 0) + return -EXIT_FAILURE; + + len -= xfer_len; + } + + return EXIT_SUCCESS; +} + +int AnlogicCable::flush() +{ + return 0; +} + +int AnlogicCable::writeTDI(uint8_t *tx, uint8_t *rx, uint32_t len, bool end) +{ + uint8_t buf[512]; + uint8_t mask = (ANLOGICCABLE_TCK_PIN << 4); + + int full_len = len; + uint8_t *tx_ptr = tx; + uint8_t *rx_ptr = rx; + + while (full_len > 0) { + /* when len > buffer capacity -> limit to capacity + * else use len + */ + int xfer_len = (full_len > 512) ? 512 : full_len; + + if (!tx) { + memset(buf, mask, xfer_len); + } else { + for (int i = 0; i < xfer_len; i++) { + buf[i] = mask; + if (tx_ptr[i >> 3] & (1 << (i & 0x07))) + buf[i] |= (ANLOGICCABLE_TDI_PIN | + (ANLOGICCABLE_TDI_PIN << 4)); + } + tx_ptr += (xfer_len >> 3); + } + + /* when last burst, complite rest of buffer with + * fixed state + */ + if (xfer_len < 512) { + if (end) { /* set TMS high with the last bit */ + buf[xfer_len-1] |= ((ANLOGICCABLE_TMS_PIN << 4) | + (ANLOGICCABLE_TMS_PIN)); + } + memset(&buf[xfer_len], buf[xfer_len-1] | ANLOGICCABLE_TCK_PIN, + 512-xfer_len); + } + + if (write(buf, (rx)?rx_ptr:NULL, 512, xfer_len) < 0) + return -EXIT_FAILURE; + + if (rx) { + rx_ptr += (xfer_len >> 3); + } + + full_len -= xfer_len; + } + + return EXIT_SUCCESS; +} + +int AnlogicCable::write(uint8_t *in_buf, uint8_t *out_buf, int len, int rd_len) +{ + int actual_length; + int ret = libusb_bulk_transfer(dev_handle, ANLOGICCABLE_WRITE_EP, + in_buf, len, &actual_length, 1000); + if (ret < 0) { + cerr << "write: usb bulk write failed " << ret << endl; + return -EXIT_FAILURE; + } + /* all write must be followed by a read */ + ret = libusb_bulk_transfer(dev_handle, ANLOGICCABLE_READ_EP, + in_buf, len, &actual_length, 1000); + if (ret < 0) { + cerr << "write: usb bulk read failed " << ret << endl; + return -EXIT_FAILURE; + } + + if (out_buf) { + for (int i = 0; i < rd_len; i++) { + out_buf[i >> 3] >>= 1; + if ((in_buf[i] >> 4) & 0x01) + out_buf[i >> 3] |= 0x80; + } + } + return len; +} diff --git a/src/anlogicCable.hpp b/src/anlogicCable.hpp new file mode 100644 index 0000000..e0ab806 --- /dev/null +++ b/src/anlogicCable.hpp @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2020 Gwenhael Goavec-Merou + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef SRC_ANLOGICCABLE_HPP_ +#define SRC_ANLOGICCABLE_HPP_ + +#include + +#include "jtagInterface.hpp" + +/*! + * \file AnlogicCable.hpp + * \class AnlogicCable + * \brief concrete class between jtag implementation and anlogic cable + * \author Gwenhael Goavec-Merou + */ + +class AnlogicCable : public JtagInterface { + public: + AnlogicCable(uint32_t clkHZ, bool verbose); + virtual ~AnlogicCable(); + + int setClkFreq(uint32_t clkHZ) override; + int setClkFreq(uint32_t clkHZ, char use_divide_by_5) override { + (void)use_divide_by_5; return setClkFreq(clkHZ);} + + /* TMS */ + int writeTMS(uint8_t *tms, int len, bool flush_buffer) override; + /* TDI */ + int writeTDI(uint8_t *tx, uint8_t *rx, uint32_t len, bool end) override; + /* clk */ + int toggleClk(uint8_t tms, uint8_t tdi, uint32_t clk_len) override; + + /*! + * \brief return internal buffer size (in byte). + * \return _buffer_size divided by 2 (two byte for clk) and divided by 8 (one + * state == one byte) + */ + int get_buffer_size() override { return 0;} + + bool isFull() override { return false;} + + int flush() override; + + private: + bool _verbose; + + int write(uint8_t *in_buf, uint8_t *out_buf, int len, int rd_len); + + libusb_device_handle *dev_handle; + libusb_context *usb_ctx; + uint8_t _tdi; + uint8_t _tms; +}; +#endif // SRC_ANLOGICCABLE_HPP_ diff --git a/src/cable.hpp b/src/cable.hpp index 953a6c7..0d9d1d9 100644 --- a/src/cable.hpp +++ b/src/cable.hpp @@ -10,10 +10,11 @@ * \brief define type of communication */ enum { - MODE_FTDI_BITBANG = 0, /*! used with ft232RL/ft231x */ - MODE_FTDI_SERIAL = 1, /*! ft2232, ft232H */ - MODE_DIRTYJTAG = 2, /*! JTAG probe firmware for STM32F1 */ - MODE_USBBLASTER = 3 /*! JTAG probe firmware for USBBLASTER */ + MODE_ANLOGICCABLE = 0, /*! JTAG probe from Anlogic */ + MODE_FTDI_BITBANG = 1, /*! used with ft232RL/ft231x */ + MODE_FTDI_SERIAL = 2, /*! ft2232, ft232H */ + MODE_DIRTYJTAG = 3, /*! JTAG probe firmware for STM32F1 */ + MODE_USBBLASTER = 4 /*! JTAG probe firmware for USBBLASTER */ } communication_type_t; typedef struct { @@ -24,6 +25,7 @@ typedef struct { static std::map cable_list = { // last 4 bytes are ADBUS7-0 value, ADBUS7-0 direction, ACBUS7-0 value, ACBUS7-0 direction // some cables requires explicit values on some of the I/Os + {"anlogicCable", {MODE_ANLOGICCABLE, {}}}, {"bus_blaster", {MODE_FTDI_SERIAL, {0x0403, 0x6010, INTERFACE_A, 0x08, 0x1B, 0x08, 0x0B}}}, {"bus_blaster_b",{MODE_FTDI_SERIAL, {0x0403, 0x6010, INTERFACE_B, 0x08, 0x0B, 0x08, 0x0B}}}, {"digilent", {MODE_FTDI_SERIAL, {0x0403, 0x6010, INTERFACE_A, 0xe8, 0xeb, 0x00, 0x60}}}, diff --git a/src/jtag.cpp b/src/jtag.cpp index 6c41dae..0cf4cff 100644 --- a/src/jtag.cpp +++ b/src/jtag.cpp @@ -24,6 +24,7 @@ #include #include +#include "anlogicCable.hpp" #include "display.hpp" #include "jtag.hpp" #include "ftdipp_mpsse.hpp" @@ -95,6 +96,9 @@ void Jtag::init_internal(cable_t &cable, const string &dev, const jtag_pins_conf_t *pin_conf, uint32_t clkHZ) { switch (cable.type) { + case MODE_ANLOGICCABLE: + _jtag = new AnlogicCable(clkHZ, _verbose); + break; case MODE_FTDI_BITBANG: if (pin_conf == NULL) throw std::exception();