diff --git a/CMakeLists.txt b/CMakeLists.txt index a753d6d..32fe788 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,7 @@ add_definitions(-DVERSION=\"v${PROJECT_VERSION}\") option(BUILD_STATIC "Whether or not to build with static libraries" OFF) option(ENABLE_UDEV "use udev to search JTAG adapter from /dev/xx" ON) +option(ENABLE_FPGALINK "enable fx2 cable support through libfpgalink" OFF) option(USE_PKGCONFIG "Use pkgconfig to find libraries" ON) option(LINK_CMAKE_THREADS "Use CMake find_package to link the threading library" OFF) @@ -139,8 +140,26 @@ else() target_link_libraries(openFPGALoader ${LIBUSB_LIBRARIES} ${LIBFTDI_LIBRARIES} + ) +if (ENABLE_FPGALINK) + find_library(LIBFPGALINK libfpgalink) + if (${LIBFPGALINK} STREQUAL "LIBFPGALINK-NOTFOUND") + message("libfpgalink not found, disabling fpgalink support") + set(ENABLE_FPGALINK OFF) + else() + message("libfpgalink found: ${LIBFPGALINK}") + file(REAL_PATH "../../include" INC_DIR BASE_DIRECTORY ${LIBFPGALINK}) + add_definitions(-DUSE_LIBFPGALINK) + target_include_directories(openFPGALoader PRIVATE ${INC_DIR}) + target_link_libraries(openFPGALoader ${LIBFPGALINK}) + target_sources(openFPGALoader PRIVATE + src/fpgalink.cpp + src/fpgalink.hpp) + endif() +endif() + if (${CMAKE_SYSTEM_NAME} MATCHES "Windows") # winsock provides ntohs target_link_libraries(openFPGALoader ws2_32) diff --git a/INSTALL.md b/INSTALL.md index b582182..58f35d5 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -5,6 +5,16 @@ ``` $ git clone https://github.com/trabucayre/openFPGALoader.git ``` +## For libfpgalink support (fx2 cable and more) add the following steps + +``` +$ git clone https://github.com/makestuff/fpgalink.git +``` + +``` +$ build.sh +``` +Add the install/bin directory to the path (for building and running OpenFPGALoader) ## Compile from source @@ -13,11 +23,14 @@ $ mkdir build $ cd build $ cmake ../ # add -DBUILD_STATIC=ON to build a static version # add -DENABLE_UDEV=OFF to disable udev support and -d /dev/xxx + # add -DENABLE_LIBFPGALINK=ON to enable libfpgalink support $ cmake --build . or $ make -j$(nproc) ``` + + ## Install As root: diff --git a/README.md b/README.md index dbaddf4..b332223 100644 --- a/README.md +++ b/README.md @@ -260,6 +260,15 @@ allowed values are: | DCD | 6 | | RI | 7 | +#### FX2 cables and pins configuration + +FX2 cables need to have their pin mapping provided + +```bash +openFPGALoader [options] -cfx2 --pins=TDI:TDO:TCK:TMS /path/to/bitstream.ext +``` +where: +* TDI:TDO:TCK:TMS may be the pin ID (A0 <= id <= D7) for pins PA0...PD7 ### CYC1000 and de0nano diff --git a/src/cable.hpp b/src/cable.hpp index 02dff5b..336c405 100644 --- a/src/cable.hpp +++ b/src/cable.hpp @@ -14,7 +14,8 @@ enum communication_type { 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 */ + MODE_USBBLASTER = 4, /*! JTAG probe firmware for USBBLASTER */ + MODE_FX2 = 5, /* FX2 support through libFPGAlink */ }; typedef struct { @@ -41,6 +42,9 @@ static std::map cable_list = { {"ft4232", {MODE_FTDI_SERIAL, {0x0403, 0x6011, INTERFACE_A, 0x08, 0x0B, 0x08, 0x0B}}}, {"ecpix5-debug", {MODE_FTDI_SERIAL, {0x0403, 0x6010, INTERFACE_A, 0xF8, 0xFB, 0xFF, 0xFF}}}, {"usb-blaster", {MODE_USBBLASTER, {}}}, +#ifdef USE_LIBFPGALINK + {"fx2", {MODE_FX2, {}}}, +#endif }; #endif diff --git a/src/fpgalink.cpp b/src/fpgalink.cpp new file mode 100644 index 0000000..185a0f8 --- /dev/null +++ b/src/fpgalink.cpp @@ -0,0 +1,191 @@ +/* + * 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 + +#include "display.hpp" +#include "fpgalink.hpp" +#include "makestuff/libfpgalink.h" + + + +using namespace std; + +#undef CHECK_STATUS + +#define CHECK_STATUS(fStatus)\ + if (fStatus) \ + { \ + printError("Error: ", false);\ + printError(error);\ + flFreeError(error);\ + fStatus = progClose(handle, &error);\ + if (error)\ + flFreeError(error);\ + flClose(handle);\ + handle = NULL;\ + throw std::exception();\ + } + +FpgaLink::FpgaLink(bool verbose): + _verbose(verbose) +{ + +} + + +int FpgaLink::writeTMS(uint8_t *tms, int len, bool flush_buffer) +{ + (void)flush_buffer; + FLStatus fStatus; + const char *error = NULL; + uint32_t bitPattern = 0; + int ulen = len; + int i = 0; + while (ulen > 0) + { + {bitPattern = tms[i++];} + ulen-=8; + if (ulen > 0) {bitPattern |= tms[i++] << 8;} + ulen-=8; + if (ulen > 0) {bitPattern |= tms[i++] << 16;} + ulen-=8; + if (ulen > 0) {bitPattern |= tms[i++] << 24;} + ulen-=8; + fStatus = jtagClockFSM(handle, bitPattern, (ulen > 0) ? 32 : 32 + ulen, &error); + CHECK_STATUS(fStatus); + } + + return len; +} + +int FpgaLink::toggleClk(uint8_t tms, uint8_t tdi, uint32_t clk_len) +{ + (void)tms; + (void)tdi; + FLStatus fStatus; + const char *error = NULL; + fStatus = jtagClocks(handle, clk_len, &error); + CHECK_STATUS(fStatus); + return EXIT_SUCCESS; +} + +int FpgaLink::flush() +{ + return 0; +} + +int FpgaLink::writeTDI(uint8_t *tx, uint8_t *rx, uint32_t len, bool end) +{ + FLStatus fStatus; + const char *error = NULL; + if (rx) + fStatus = jtagShiftInOut(handle, len, tx, rx, end, &error); + else + { + fStatus = jtagShiftInOnly(handle, len, tx, end, &error); + } + + CHECK_STATUS(fStatus); + + return EXIT_SUCCESS; +} + + +FX2Cable::FX2Cable(bool verbose, const jtag_pins_conf_t *pin_conf, const char *ivp, const char *vp): + FpgaLink::FpgaLink(verbose) +{ + FLStatus fStatus; + const char *error = NULL; + fStatus = flInitialise(0, &error); + CHECK_STATUS(fStatus); + char portConfig[10]; + + vp = "1D50:602B"; + std::string svp(vp); + + printInfo("Attempting to open connection to FPGALink device " + svp +"...", false); + fStatus = flOpen(vp, &handle, NULL); + if ( fStatus ) { + { + int count = 60; + uint8 flag; + ivp = "04b4:8613"; + std::string sivp(ivp); + printInfo("Loading firmware into " + sivp, true); + fStatus = flLoadStandardFirmware(ivp, vp, &error); + CHECK_STATUS(fStatus); + + printInfo("Awaiting renumeration"); + flSleep(1000); + do { + printInfo(".", false); + fflush(stdout); + fStatus = flIsDeviceAvailable(vp, &flag, &error); + CHECK_STATUS(fStatus); + flSleep(250); + count--; + } while ( !flag && count ); + printf("\n"); + if ( !flag ) { + printError("Error: FPGALink device did not renumerate properly as " + svp, true); + flClose(handle); + handle = NULL; + throw std::exception(); + } + + printInfo("Attempting to open connection to FPGLink device " + svp + " again...", false); + fStatus = flOpen(vp, &handle, &error); + CHECK_STATUS(fStatus); + printInfo("success", true); + } + } else { + printInfo("success", true); + } + + transcode_pin_config(pin_conf, portConfig); + fStatus = progOpen(handle, portConfig, &error); + CHECK_STATUS(fStatus); + +} + +void FX2Cable::transcode_pin_config(const jtag_pins_conf_t *pin_conf, char* buffer) +{ + sprintf(buffer, "%X%X%X%X",pin_conf->tdo_pin, pin_conf->tdi_pin, pin_conf->tms_pin, pin_conf->tck_pin); +} + +FX2Cable::~FX2Cable() +{ + const char *error = NULL; + FLStatus fStatus; + (void)fStatus; + if (handle) + { + fStatus = progClose(handle, &error); + flClose(handle); + handle = NULL; + } +} + diff --git a/src/fpgalink.hpp b/src/fpgalink.hpp new file mode 100644 index 0000000..47a25f4 --- /dev/null +++ b/src/fpgalink.hpp @@ -0,0 +1,72 @@ +/* + * 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_FX2CABLE_HPP_ +#define SRC_FX2CABLE_HPP_ + +#include + +#include "jtagInterface.hpp" +#include "board.hpp" + + +/*! + * \file FX2Cable.hpp + * \class FX2Cable + * \brief concrete class between jtag implementation and FX2 cable + */ + +class FpgaLink : public JtagInterface{ + +public: + FpgaLink(bool verbose); + int setClkFreq(uint32_t clkHZ) override{(void)clkHZ; return 0;} + int setClkFreq(uint32_t clkHZ, char use_divide_by_5) override {(void)clkHZ; (void)use_divide_by_5;return 0;} + + /* 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; + + protected: + bool _verbose; + struct FLContext *handle = NULL; +}; + + +class FX2Cable : public FpgaLink { + public: + FX2Cable(bool verbose, const jtag_pins_conf_t *pin_conf, const char *ivp = NULL, const char *vp = NULL); + virtual ~FX2Cable(); + + private: + void transcode_pin_config(const jtag_pins_conf_t *pin_conf, char* buffer); +}; +#endif // SRC_FX2CABLE_HPP_ diff --git a/src/jtag.cpp b/src/jtag.cpp index 2ba16c2..d539dac 100644 --- a/src/jtag.cpp +++ b/src/jtag.cpp @@ -33,6 +33,10 @@ #include "dirtyJtag.hpp" #include "usbBlaster.hpp" +#ifdef USE_LIBFPGALINK +#include "fpgalink.hpp" +#endif + using namespace std; #define DEBUG 0 @@ -103,6 +107,11 @@ void Jtag::init_internal(cable_t &cable, const string &dev, const string &serial case MODE_USBBLASTER: _jtag = new UsbBlaster(_verbose); break; +#ifdef USE_LIBFPGALINK + case MODE_FX2: + _jtag = new FX2Cable(_verbose, pin_conf); + break; +#endif default: std::cerr << "Jtag: unknown cable type" << std::endl; throw std::exception(); diff --git a/src/main.cpp b/src/main.cpp index 4192704..9daa719 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -383,7 +383,7 @@ int parse_opt(int argc, char **argv, struct arguments *args, jtag_pins_conf_t *p "write bitstream in SRAM (default: true, only for Gowin and ECP5 devices)") ("o,offset", "start offset in EEPROM", cxxopts::value(args->offset)) - ("pins", "pin config (only for ft232R) TDI:TDO:TCK:TMS", + ("pins", "pin config (only for ft232R and fx2) TDI:TDO:TCK:TMS", cxxopts::value>(pins)) ("quiet", "Produce quiet output (no progress bar)", cxxopts::value(quiet)) @@ -467,7 +467,7 @@ int parse_opt(int argc, char **argv, struct arguments *args, jtag_pins_conf_t *p for (int i = 0; i < 4; i++) { int pin_num; try { - pin_num = std::stoi(pins[i], nullptr, 0); + pin_num = std::stoi(pins[i], nullptr, 16); } catch (std::exception &e) { if (pins_list.find(pins[i]) == pins_list.end()) { printError("Invalid pin name"); @@ -475,8 +475,16 @@ int parse_opt(int argc, char **argv, struct arguments *args, jtag_pins_conf_t *p } pin_num = pins_list[pins[i]]; } - +#ifdef USE_LIBFPGALINK + if ((pin_num > 7 || pin_num < 0) && + (pin_num > 0xD7 || pin_num < 0xD0) && + (pin_num > 0xC7 || pin_num < 0xC0) && + (pin_num > 0xB7 || pin_num < 0xB0) && + (pin_num > 0xA7 || pin_num < 0xA0)) { +#else if (pin_num > 7 || pin_num < 0) { +#endif + printError("Invalid pin ID"); throw std::exception(); }