From d09e5da0ba67cd61d20e30da32defe63ca04dff9 Mon Sep 17 00:00:00 2001 From: Patrick Urban Date: Fri, 10 Dec 2021 12:12:32 +0100 Subject: [PATCH 01/10] colognechip integration: initial commit This commit adds support for the Cologne Chip GateMate FPGA series. Both Evaluation Board and Programmer Cable are supported. Configurations can be loaded into the FPGA with both devices via JTAG or SPI. In addition to reading/writing data from/to flashes directly via SPI, this can also be done via the built-in JTAG-SPI-bypass. A direct wiring between programming hardware and flash is no longer necessary in this case. Signed-off-by: Patrick Urban --- CMakeLists.txt | 4 + doc/vendors/colognechip.rst | 75 ++++++ src/board.hpp | 5 + src/cable.hpp | 3 + src/colognechip.cpp | 445 +++++++++++++++++++++++++++++++++++ src/colognechip.hpp | 61 +++++ src/colognechipCfgParser.cpp | 31 +++ src/colognechipCfgParser.hpp | 25 ++ src/ftdiJtagMPSSE.hpp | 2 +- src/jtag.cpp | 2 +- src/jtag.hpp | 3 +- src/main.cpp | 17 ++ src/part.hpp | 2 + 13 files changed, 672 insertions(+), 3 deletions(-) create mode 100644 doc/vendors/colognechip.rst create mode 100644 src/colognechip.cpp create mode 100644 src/colognechip.hpp create mode 100644 src/colognechipCfgParser.cpp create mode 100644 src/colognechipCfgParser.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index cfb5a18..185ab2b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -103,6 +103,8 @@ set(OPENFPGALOADER_SOURCE src/bitparser.cpp src/xilinx.cpp src/xilinxMapParser.cpp + src/colognechip.cpp + src/colognechipCfgParser.cpp ) set(OPENFPGALOADER_HEADERS @@ -150,6 +152,8 @@ set(OPENFPGALOADER_HEADERS src/latticeBitParser.hpp src/xilinx.hpp src/xilinxMapParser.hpp + src/colognechip.hpp + src/colognechipCfgParser.hpp ) add_executable(openFPGALoader diff --git a/doc/vendors/colognechip.rst b/doc/vendors/colognechip.rst new file mode 100644 index 0000000..b7f50b5 --- /dev/null +++ b/doc/vendors/colognechip.rst @@ -0,0 +1,75 @@ +.. _colognechip: + +Cologne Chip notes +################## + +Supported Boards/Cables +======================= + +* GateMate Evaluation Board using board parameters `-b gatemate_evb_jtag` or `-b gatemate_evb_spi` +* GateMate Programmer using cable parameter `-c gatemate_pgm` + +Programming Modes +================= + +Supported configuration files are bitfiles `*.bit` and it's ASCII equivalents `*.cfg`. + +JTAG Configuration +------------------ + +Performs an active hardware reset and writes the configuration into the FPGA latches via JTAG. The configuration mode pins CFG_MD[3:0] must be set to 0xF0 (JTAG). + +* Program using Evaluation Board: + + openFPGALoader -b gatemate_evb_jtag .cfg.bit + +* Program using Programmer Cable: + + openFPGALoader -c gatemate_pgm .cfg.bit + +SPI Configuration +----------------- + +Performs an active hardware reset and writes the configuration into the FPGA latches via SPI. The configuration mode pins CFG_MD[3:0] must be set to 0x40 (SPI passive). + +* Program using Evaluation Board: + + openFPGALoader -b gatemate_evb_spi .cfg.bit + +* Program using Programmer Cable: + + openFPGALoader -b gatemate_pgm_spi .cfg.bit + +JTAG Flash Access +----------------- + +It is possible to access external flashes via the internal JTAG-SPI-bypass. The configuration mode pins CFG_MD[3:0] must be set to 0xF0 (JTAG). Note that the FPGA will not start automatically. + +* Write to flash using Evaluation Board: + + openFPGALoader -b gatemate_evb_jtag .cfg.bit + +* Write to flash using Programmer Cable: + + openFPGALoader -c gatemate_pgm -f .cfg.bit + +The `offset` parameter can be used to store data at any point in the flash, e.g.: + + openFPGALoader -b gatemate_evb_jtag -o .cfg.bit + +SPI Flash Access +---------------- + +If the programming device and FPGA share the same SPI signals, it is possible to hold the FPGA in reset and write data to the flash. The configuration mode can be set as desired. If the FPGA should start from the external memory after reset, the configuration mode pins CFG_MD[3:0] set to 0x00 (SPI active). + +* Write to flash using Evaluation Board: + + openFPGALoader -b gatemate_evb_spi -f .cfg.bit + +* Write to flash using Programmer Cable: + + openFPGALoader -b gatemate_pgm_spi -f .cfg.bit + +The `offset` parameter can be used to store data at any point in the flash, e.g.: + + openFPGALoader -b gatemate_evb_spi -o .cfg.bit \ No newline at end of file diff --git a/src/board.hpp b/src/board.hpp index 551ebee..4fdd041 100644 --- a/src/board.hpp +++ b/src/board.hpp @@ -120,6 +120,11 @@ static std::map board_list = { SPI_BOARD("fireant", "efinix", "ft232", DBUS4, DBUS5, 0, DBUS3, DBUS0, DBUS1, DBUS2, DBUS6, 0, CABLE_DEFAULT), DFU_BOARD("fomu", "", "dfu", 0x1209, 0x5bf0, 0), + SPI_BOARD("gatemate_pgm_spi", "colognechip", "gatemate_pgm", + DBUS4, DBUS5, CBUS0, DBUS3, DBUS0, DBUS1, DBUS2, 0, 0, CABLE_DEFAULT), + JTAG_BOARD("gatemate_evb_jtag", "", "gatemate_evb_jtag", 0, 0, CABLE_DEFAULT), + SPI_BOARD("gatemate_evb_spi", "colognechip", "gatemate_evb_spi", + DBUS4, DBUS5, CBUS0, DBUS3, DBUS0, DBUS1, DBUS2, 0, 0, CABLE_DEFAULT), /* most ice40 boards uses the same pinout */ SPI_BOARD("ice40_generic", "lattice", "ft2232", DBUS7, DBUS6, 0, diff --git a/src/cable.hpp b/src/cable.hpp index 5be98b0..b72682a 100644 --- a/src/cable.hpp +++ b/src/cable.hpp @@ -38,6 +38,9 @@ static std::map cable_list = { {"bus_blaster_b", {MODE_FTDI_SERIAL, {0x0403, 0x6010, INTERFACE_B, 0x08, 0x0B, 0x08, 0x0B}}}, {"ch552_jtag", {MODE_FTDI_SERIAL, {0x0403, 0x6010, INTERFACE_A, 0x08, 0x0B, 0x08, 0x0B}}}, {"cmsisdap", {MODE_CMSISDAP, {0x0d28, 0x0204, 0, 0, 0, 0, 0 }}}, + {"gatemate_pgm", {MODE_FTDI_SERIAL, {0x0403, 0x6014, INTERFACE_A, 0x10, 0x9B, 0x14, 0x17}}}, + {"gatemate_evb_jtag", {MODE_FTDI_SERIAL, {0x0403, 0x6010, INTERFACE_A, 0x10, 0x1B, 0x00, 0x01}}}, + {"gatemate_evb_spi", {MODE_FTDI_SERIAL, {0x0403, 0x6010, INTERFACE_B, 0x00, 0x1B, 0x00, 0x01}}}, {"dfu", {MODE_DFU, {}}}, {"digilent", {MODE_FTDI_SERIAL, {0x0403, 0x6010, INTERFACE_A, 0xe8, 0xeb, 0x00, 0x60}}}, {"digilent_b", {MODE_FTDI_SERIAL, {0x0403, 0x6010, INTERFACE_B, 0xe8, 0xeb, 0x00, 0x60}}}, diff --git a/src/colognechip.cpp b/src/colognechip.cpp new file mode 100644 index 0000000..18ba091 --- /dev/null +++ b/src/colognechip.cpp @@ -0,0 +1,445 @@ +// SPDX-License-Identifier: Apache-2.0 +/* + * Copyright (C) 2021 Gwenhael Goavec-Merou + * Copyright (C) 2021 Cologne Chip AG + */ + +#include "colognechip.hpp" + +#define JTAG_CONFIGURE 0x06 +#define JTAG_SPI_BYPASS 0x05 +#define SLEEP_US 500 + +CologneChip::CologneChip(FtdiSpi *spi, const std::string &filename, + const std::string &file_type, Device::prog_type_t prg_type, + uint16_t rstn_pin, uint16_t done_pin, uint16_t failn_pin, uint16_t oen_pin, + bool verify, int8_t verbose) : + Device(NULL, filename, file_type, verify, verbose), _rstn_pin(rstn_pin), + _done_pin(done_pin), _failn_pin(failn_pin), _oen_pin(oen_pin) +{ + _spi = spi; + _spi->gpio_set_input(_done_pin | _failn_pin); + _spi->gpio_set_output(_rstn_pin | _oen_pin); + + if (prg_type == Device::WR_SRAM) { + _mode = Device::MEM_MODE; + } + else { + _mode = Device::FLASH_MODE; + } +} + +CologneChip::CologneChip(Jtag* jtag, const std::string &filename, + const std::string &file_type, Device::prog_type_t prg_type, + const std::string &board_name, const std::string &cable_name, + bool verify, int8_t verbose) : + Device(jtag, filename, file_type, verify, verbose) +{ + /* check which cable/board we're using in order to select pin definitions */ + std::string spi_board_name; + if (board_name != "-") { + spi_board_name = std::regex_replace(board_name, std::regex("jtag"), "spi"); + } + else if (cable_name == "gatemate_pgm") { + spi_board_name = "gatemate_pgm_spi"; + } + + target_board_t *spi_board = &(board_list[spi_board_name]); + cable_t *spi_cable = &(cable_list[spi_board->cable_name]); + + /* pin configurations valid for both evaluation board and programer */ + _rstn_pin = spi_board->reset_pin; + _done_pin = spi_board->done_pin; + _failn_pin = DBUS6; + _oen_pin = spi_board->oe_pin; + + /* cast _jtag->_jtag from JtagInterface to FtdiJtagMPSSE to access GPIO */ + _ftdi_jtag = reinterpret_cast(_jtag->_jtag); + + _ftdi_jtag->gpio_set_input(_done_pin | _failn_pin); + _ftdi_jtag->gpio_set_output(_rstn_pin | _oen_pin); + + if (prg_type == Device::WR_SRAM) { + _mode = Device::MEM_MODE; + } + else { + _mode = Device::FLASH_MODE; + } +} + +/** + * Enable outputs and hold FPGA in active hardware reset for SLEEP_US. + */ +void CologneChip::reset() +{ + if (_spi) + { + _spi->gpio_clear(_rstn_pin | _oen_pin); + usleep(SLEEP_US); + _spi->gpio_set(_rstn_pin); + } + else if (_ftdi_jtag) + { + _ftdi_jtag->gpio_clear(_rstn_pin | _oen_pin); + usleep(SLEEP_US); + _ftdi_jtag->gpio_set(_rstn_pin); + } +} + +/** + * Obtain CFG_DONE and ~CFG_FAILED signals. Configuration is successfull iff + * CFG_DONE=true and ~CFG_FAILED=false. + */ +bool CologneChip::cfgDone() +{ + uint16_t status = 0; + if (_spi) { + status = _spi->gpio_get(true); + } + else if (_ftdi_jtag) { + status = _ftdi_jtag->gpio_get(true); + } + bool done = (status & _done_pin) > 0; + bool fail = (status & _failn_pin) == 0; + return (done && !fail); +} + +/** + * Dump flash contents to file. Works in both SPI and JTAG-SPI-bypass mode. + */ +bool CologneChip::dumpFlash(const std::string &filename, uint32_t base_addr, + uint32_t len) +{ + if (_spi) { + /* enable output and hold reset */ + _spi->gpio_clear(_rstn_pin | _oen_pin); + } + else if (_ftdi_jtag) { + /* enable output and disable reset */ + _ftdi_jtag->gpio_clear(_oen_pin); + _ftdi_jtag->gpio_set(_rstn_pin); + } + + /* prepare SPI access */ + printInfo("Read Flash ", false); + try { + SPIFlash *flash; + if (_spi) { + flash = new SPIFlash(reinterpret_cast(_spi), _verbose); + } + else if (_ftdi_jtag) { + flash = new SPIFlash(this, _verbose); + } + flash->reset(); + flash->power_up(); + flash->dump(filename, base_addr, len); + } catch (std::exception &e) { + printError("Fail"); + printError(std::string(e.what())); + return false; + } + + if (_spi) { + /* disable output and release reset */ + _spi->gpio_set(_rstn_pin | _oen_pin); + } + else if (_ftdi_jtag) { + /* disable output */ + _ftdi_jtag->gpio_set(_oen_pin); + } + usleep(SLEEP_US); + + return true; +} + +/** + * Parse bitstream from *.bit or *.cfg and program FPGA in SPI or JTAG mode + * or write configuration to external flash via SPI or JTAG-SPI-bypass. + */ +void CologneChip::program(unsigned int offset) +{ + ConfigBitstreamParser *cfg; + if (_file_extension == "bit") { + cfg = new RawParser(_filename, false); + } + else if (_file_extension == "cfg") { + cfg = new CologneChipCfgParser(_filename, false); + } + + cfg->parse(); + + uint8_t *data = cfg->getData(); + int length = cfg->getLength() / 8; + + switch (_mode) { + case Device::FLASH_MODE: + if (_jtag != NULL) { + programJTAG_flash(offset, data, length); + } + else if (_jtag == NULL) { + programSPI_flash(offset, data, length); + } + break; + case Device::MEM_MODE: + if (_jtag != NULL) { + programJTAG_sram(data, length); + } + else if (_jtag == NULL) { + programSPI_sram(data, length); + } + break; + } +} + +/** + * Write configuration into FPGA latches via SPI after active reset. + * CFG_MD[3:0] must be set to 0x40 (SPI passive). + */ +void CologneChip::programSPI_sram(uint8_t *data, int length) +{ + uint32_t timeout = 1000; + + /* hold device in reset for a moment */ + reset(); + + uint8_t *recv = new uint8_t[length]; + _spi->gpio_set(_rstn_pin); + _spi->spi_put(data, recv, length); // TODO _spi->spi_put(data, null, length) does not work? + + printInfo("Wait for CFG_DONE ", false); + do { + timeout--; + usleep(SLEEP_US); + } while (!cfgDone() && timeout > 0); + if (timeout == 0) { + printError("FAIL"); + } + else { + printSuccess("DONE"); + } + + _spi->gpio_set(_oen_pin); + delete [] recv; +} + +/** + * Write configuration to flash via SPI while FPGA is in active reset. When + * done, release reset to start FPGA in active SPI mode (load from flash). + * CFG_MD[3:0] must be set to 0x00 (SPI active). + */ +void CologneChip::programSPI_flash(unsigned int offset, uint8_t *data, int length) +{ + uint32_t timeout = 1000; + + /* hold device in reset during flash write access */ + _spi->gpio_clear(_rstn_pin | _oen_pin); + usleep(SLEEP_US); + + SPIFlash flash(reinterpret_cast(_spi), _verbose); + flash.reset(); + flash.power_up(); + + printf("%02x\n", flash.read_status_reg()); + flash.read_id(); + flash.erase_and_prog(offset, data, length); + + /* verify write if required */ + if (_verify) + flash.verify(offset, data, length); + + _spi->gpio_set(_rstn_pin); + usleep(SLEEP_US); + + printInfo("Wait for CFG_DONE ", false); + do { + timeout--; + usleep(SLEEP_US); + } while (!cfgDone() && timeout > 0); + if (timeout == 0) { + printError("FAIL"); + } + else { + printSuccess("DONE"); + } + + _spi->gpio_set(_oen_pin); +} + +/** + * Write configuration into FPGA latches via JTAG after active reset. + * CFG_MD[3:0] must be set to 0xF0 (JTAG). + */ +void CologneChip::programJTAG_sram(uint8_t *data, int length) +{ + uint32_t timeout = 1000; + + /* hold device in reset for a moment */ + reset(); + + _jtag->set_state(Jtag::RUN_TEST_IDLE); + + uint8_t tmp[1024]; + int size = 1024; + + _jtag->shiftIR(JTAG_CONFIGURE, 6, Jtag::SELECT_DR_SCAN); + + //ProgressBar progress("Loading", length, 50, _quiet); + + for (int i = 0; i < length; i += size) { + //progress.display(i); + + if (length < i + size) + size = length-i; + + for (int ii = 0; ii < size; ii++) + tmp[ii] = data[i+ii]; + + _jtag->shiftDR(tmp, NULL, size*8, Jtag::SHIFT_DR); + } + + //progress.done(); + _jtag->set_state(Jtag::RUN_TEST_IDLE); + + printInfo("Wait for CFG_DONE ", false); + do { + timeout--; + usleep(SLEEP_US); + } while (!cfgDone() && timeout > 0); + if (timeout == 0) { + printError("FAIL"); + } + else { + printSuccess("DONE"); + } + + _ftdi_jtag->gpio_set(_oen_pin); +} + +/** + * Write configuration to flash via JTAG-SPI-bypass. The FPGA will not start + * as it is in JTAG mode with CFG_MD[3:0] set to 0xF0 (JTAG). + */ +void CologneChip::programJTAG_flash(unsigned int offset, uint8_t *data, int length) +{ + /* hold device in reset for a moment */ + reset(); + + SPIFlash flash(this, _verbose); + flash.reset(); + flash.power_up(); + + printf("%02x\n", flash.read_status_reg()); + flash.read_id(); + flash.erase_and_prog(offset, data, length); + + /* verify write if required */ + if (_verify) + flash.verify(offset, data, length); + + _ftdi_jtag->gpio_set(_oen_pin); +} + +/** + * Overrides spi_put() to access SPI components via JTAG-SPI-bypass. + */ +int CologneChip::spi_put(uint8_t cmd, uint8_t *tx, uint8_t *rx, uint32_t len) +{ + int xfer_len = len + 1; + uint8_t jtx[xfer_len+2]; + uint8_t jrx[xfer_len+2]; + + jtx[0] = ConfigBitstreamParser::reverseByte(cmd); + + if (tx != NULL) { + for (uint32_t i=0; i < len; i++) + jtx[i+1] = ConfigBitstreamParser::reverseByte(tx[i]); + } + + _jtag->shiftIR(JTAG_SPI_BYPASS, 6, Jtag::SELECT_DR_SCAN); + + int test = (rx == NULL) ? 8*xfer_len+1 : 8*xfer_len+2; + _jtag->shiftDR(jtx, (rx == NULL)? NULL: jrx, test, Jtag::SELECT_DR_SCAN); + + if (rx != NULL) { + for (uint32_t i=0; i < len; i++) { + uint8_t b0 = ConfigBitstreamParser::reverseByte(jrx[i+1]); + uint8_t b1 = ConfigBitstreamParser::reverseByte(jrx[i+2]); + rx[i] = (b0 << 1) | ((b1 >> 7) & 0x01); + } + } + return 0; +} + +/** + * Overrides spi_put() to access SPI components via JTAG-SPI-bypass. + */ +int CologneChip::spi_put(uint8_t *tx, uint8_t *rx, uint32_t len) +{ + int xfer_len = len; + uint8_t jtx[xfer_len+2]; + uint8_t jrx[xfer_len+2]; + + if (tx != NULL) { + for (uint32_t i=0; i < len; i++) + jtx[i] = ConfigBitstreamParser::reverseByte(tx[i]); + } + + _jtag->shiftIR(JTAG_SPI_BYPASS, 6, Jtag::SELECT_DR_SCAN); + _jtag->shiftDR(jtx, (rx == NULL)? NULL: jrx, 8*xfer_len+1, Jtag::SELECT_DR_SCAN); + + if (rx != NULL) { + for (uint32_t i=0; i < len; i++) { + uint8_t b0 = ConfigBitstreamParser::reverseByte(jrx[i]); + uint8_t b1 = ConfigBitstreamParser::reverseByte(jrx[i+1]); + rx[i] = (b0 << 1) | ((b1 >> 7) & 0x01); + } + } + return 0; +} + +/** + * Overrides spi_put() to access SPI components via JTAG-SPI-bypass. + */ +int CologneChip::spi_wait(uint8_t cmd, uint8_t mask, uint8_t cond, + uint32_t timeout, bool verbose) +{ + uint8_t rx[2]; + uint8_t dummy[2]; + uint8_t tmp; + uint8_t tx = ConfigBitstreamParser::reverseByte(cmd); + uint32_t count = 0; + + _jtag->shiftIR(JTAG_SPI_BYPASS, 6, Jtag::SHIFT_DR); + _jtag->read_write(&tx, NULL, 8, 0); + + do { + if (count == 0) { + _jtag->read_write(dummy, rx, 9, 0); + uint8_t b0 = ConfigBitstreamParser::reverseByte(rx[0]); + uint8_t b1 = ConfigBitstreamParser::reverseByte(rx[1]); + tmp = (b0 << 1) | ((b1 >> 7) & 0x01); + } + else { + _jtag->read_write(dummy, rx, 8, 0); + tmp = ConfigBitstreamParser::reverseByte(rx[0]); + } + + count++; + if (count == timeout) { + printf("timeout: %x %u\n", tmp, count); + break; + } + + if (verbose) { + printf("%x %x %x %u\n", tmp, mask, cond, count); + } + } while ((tmp & mask) != cond); + _jtag->set_state(Jtag::RUN_TEST_IDLE); + if (count == timeout) { + printf("%x\n", tmp); + std::cout << "wait: Error" << std::endl; + return -ETIME; + } else { + return 0; + } +} diff --git a/src/colognechip.hpp b/src/colognechip.hpp new file mode 100644 index 0000000..ddd4878 --- /dev/null +++ b/src/colognechip.hpp @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: Apache-2.0 +/* + * Copyright (C) 2021 Gwenhael Goavec-Merou + * Copyright (C) 2021 Cologne Chip AG + */ + +#ifndef SRC_COLOGNECHIP_HPP_ +#define SRC_COLOGNECHIP_HPP_ + +#include +#include + +#include "device.hpp" +#include "jtag.hpp" +#include "ftdispi.hpp" +#include "ftdiJtagMPSSE.hpp" +#include "rawParser.hpp" +#include "colognechipCfgParser.hpp" +#include "spiFlash.hpp" + +class CologneChip: public Device, SPIInterface { + public: + CologneChip(FtdiSpi *spi, const std::string &filename, + const std::string &file_type, Device::prog_type_t prg_type, + uint16_t rstn_pin, uint16_t done_pin, uint16_t failn_pin, uint16_t oen_pin, + bool verify, int8_t verbose); + CologneChip(Jtag* jtag, const std::string &filename, + const std::string &file_type, Device::prog_type_t prg_type, + const std::string &board_name, const std::string &cable_name, + bool verify, int8_t verbose); + ~CologneChip() {} + + bool cfgDone(); + bool dumpFlash(const std::string &filename, uint32_t base_addr, uint32_t len); + void program(unsigned int offset = 0) override; + + int idCode() override {return 0;} + void reset() override; + + private: + void programSPI_sram(uint8_t *data, int length); + void programSPI_flash(unsigned int offset, uint8_t *data, int length); + void programJTAG_sram(uint8_t *data, int length); + void programJTAG_flash(unsigned int offset, uint8_t *data, int length); + + /* spi interface via jtag */ + int spi_put(uint8_t cmd, uint8_t *tx, uint8_t *rx, + uint32_t len) override; + int spi_put(uint8_t *tx, uint8_t *rx, uint32_t len) override; + int spi_wait(uint8_t cmd, uint8_t mask, uint8_t cond, uint32_t timeout, + bool verbose=false) override; + + FtdiSpi *_spi = NULL; + FtdiJtagMPSSE *_ftdi_jtag = NULL; + uint16_t _rstn_pin; + uint16_t _done_pin; + uint16_t _failn_pin; + uint16_t _oen_pin; +}; + +#endif // SRC_COLOGNECHIP_HPP_ diff --git a/src/colognechipCfgParser.cpp b/src/colognechipCfgParser.cpp new file mode 100644 index 0000000..3e867bc --- /dev/null +++ b/src/colognechipCfgParser.cpp @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: Apache-2.0 +/* + * Copyright (C) 2021 Gwenhael Goavec-Merou + * Copyright (C) 2021 Cologne Chip AG + */ + +#include + +#include "colognechipCfgParser.hpp" + +CologneChipCfgParser::CologneChipCfgParser(const std::string &filename, bool reverseOrder): + ConfigBitstreamParser(filename, ConfigBitstreamParser::ASCII_MODE, + false), _reverseOrder(reverseOrder) +{} + +int CologneChipCfgParser::parse() +{ + std::string buffer; + std::istringstream lineStream(_raw_data); + + while (std::getline(lineStream, buffer, '\n')) { + std::string val = buffer.substr(0, buffer.find("//")); + val.erase(std::remove_if(val.begin(), val.end(), ::isspace), val.end()); + if (val != "") { + _bit_data += std::stol(val, nullptr, 16); + } + } + _bit_length = _bit_data.size() * 8; + + return EXIT_SUCCESS; +} diff --git a/src/colognechipCfgParser.hpp b/src/colognechipCfgParser.hpp new file mode 100644 index 0000000..8725c41 --- /dev/null +++ b/src/colognechipCfgParser.hpp @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: Apache-2.0 +/* + * Copyright (C) 2021 Gwenhael Goavec-Merou + * Copyright (C) 2021 Cologne Chip AG + */ + +#ifndef SRC_COLOGNECHIPCFGPARSER_HPP_ +#define SRC_COLOGNECHIPCFGPARSER_HPP_ + +#include +#include + +#include "configBitstreamParser.hpp" + +class CologneChipCfgParser: public ConfigBitstreamParser { + public: + CologneChipCfgParser(const std::string &filename, bool reverseOrder); + + int parse() override; + + private: + bool _reverseOrder; +}; + +#endif // SRC_COLOGNECHIPCFGPARSER_HPP_ diff --git a/src/ftdiJtagMPSSE.hpp b/src/ftdiJtagMPSSE.hpp index 5b6b9ed..ebb7947 100644 --- a/src/ftdiJtagMPSSE.hpp +++ b/src/ftdiJtagMPSSE.hpp @@ -20,7 +20,7 @@ * \author Gwenhael Goavec-Merou */ -class FtdiJtagMPSSE : public JtagInterface, private FTDIpp_MPSSE { +class FtdiJtagMPSSE : public JtagInterface, public FTDIpp_MPSSE { public: FtdiJtagMPSSE(const FTDIpp_MPSSE::mpsse_bit_config &cable, std::string dev, const std::string &serial, uint32_t clkHZ, uint8_t verbose = 0); diff --git a/src/jtag.cpp b/src/jtag.cpp index 5a55b79..cbfe6aa 100644 --- a/src/jtag.cpp +++ b/src/jtag.cpp @@ -136,7 +136,7 @@ int Jtag::detectChain(int max_dev) for (int ii=0; ii < 4; ii++) tmp |= (rx_buff[ii] << (8*ii)); if (tmp != 0 && tmp != 0xffffffff) { - tmp &= 0x0fffffff; + tmp &= 0xffffffff; _devices_list.insert(_devices_list.begin(), tmp); /* search for irlength in fpga_list or misc_dev_list */ diff --git a/src/jtag.hpp b/src/jtag.hpp index 73ab548..90bc7ef 100644 --- a/src/jtag.hpp +++ b/src/jtag.hpp @@ -92,6 +92,8 @@ class Jtag { /* utilities */ void setVerbose(int8_t verbose){_verbose = verbose;} + JtagInterface *_jtag; + private: void init_internal(cable_t &cable, const std::string &dev, const std::string &serial, const jtag_pins_conf_t *pin_conf, uint32_t clkHZ, @@ -102,7 +104,6 @@ class Jtag { int _num_tms; unsigned char *_tms_buffer; std::string _board_name; - JtagInterface *_jtag; int device_index; /*!< index for targeted FPGA */ std::vector _devices_list; /*!< ordered list of devices idcode */ diff --git a/src/main.cpp b/src/main.cpp index 5fe6b92..a3342e3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -16,6 +16,7 @@ #include "anlogic.hpp" #include "board.hpp" #include "cable.hpp" +#include "colognechip.hpp" #include "device.hpp" #include "dfu.hpp" #include "display.hpp" @@ -227,6 +228,19 @@ int main(int argc, char **argv) } else { target.program(args.offset); } + } else if (board->manufacturer == "colognechip") { + CologneChip target(spi, args.bit_file, args.file_type, args.prg_type, + board->reset_pin, board->done_pin, DBUS6, board->oe_pin, + args.verify, args.verbose); + if (args.prg_type == Device::RD_FLASH) { + if (args.file_size == 0) { + printError("Error: 0 size for dump"); + } else { + target.dumpFlash(args.bit_file, args.offset, args.file_size); + } + } else { + target.program(args.offset); + } } else { RawParser *bit = NULL; if (board->reset_pin) { @@ -443,6 +457,9 @@ int main(int argc, char **argv) } else if (fab == "lattice") { fpga = new Lattice(jtag, args.bit_file, args.file_type, args.prg_type, args.flash_sector, args.verify, args.verbose); + } else if (fab == "colognechip") { + fpga = new CologneChip(jtag, args.bit_file, args.file_type, + args.prg_type, args.board, args.cable, args.verify, args.verbose); } else { printError("Error: manufacturer " + fab + " not supported"); delete(jtag); diff --git a/src/part.hpp b/src/part.hpp index ed8b565..be411d0 100644 --- a/src/part.hpp +++ b/src/part.hpp @@ -107,6 +107,8 @@ static std::map fpga_list = { {0x0100381B, {"Gowin", "GW1N", "GW1N-4", 8}}, {0x0300181b, {"Gowin", "GW1NS", "GW1NS-2C", 8}}, {0x0100981b, {"Gowin", "GW1NSR", "GW1NSR-4C", 8}}, + + {0x20000001, {"colognechip", "GateMate Series", "GM1Ax", 6}}, }; /* device potentially in JTAG chain but not handled */ From 49e6602b9f2117304ec52fe0d082d779707280d4 Mon Sep 17 00:00:00 2001 From: Patrick Urban Date: Fri, 10 Dec 2021 12:41:33 +0100 Subject: [PATCH 02/10] colognechip integration: update vendor documentation --- doc/vendors/colognechip.rst | 50 ++++++++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/doc/vendors/colognechip.rst b/doc/vendors/colognechip.rst index b7f50b5..dd52fc8 100644 --- a/doc/vendors/colognechip.rst +++ b/doc/vendors/colognechip.rst @@ -6,70 +6,90 @@ Cologne Chip notes Supported Boards/Cables ======================= -* GateMate Evaluation Board using board parameters `-b gatemate_evb_jtag` or `-b gatemate_evb_spi` -* GateMate Programmer using cable parameter `-c gatemate_pgm` +* GateMate Evaluation Board using board parameters ``-b gatemate_evb_jtag`` or ``-b gatemate_evb_spi`` +* GateMate Programmer using cable parameter ``-c gatemate_pgm`` Programming Modes ================= -Supported configuration files are bitfiles `*.bit` and it's ASCII equivalents `*.cfg`. +Supported configuration files are bitfiles ``*.bit`` and it's ASCII equivalents ``*.cfg``. JTAG Configuration ------------------ -Performs an active hardware reset and writes the configuration into the FPGA latches via JTAG. The configuration mode pins CFG_MD[3:0] must be set to 0xF0 (JTAG). +Performs an active hardware reset and writes the configuration into the FPGA latches via JTAG. The configuration mode pins ``CFG_MD[3:0]`` must be set to 0xF0 (JTAG). -* Program using Evaluation Board: +1. Program using Evaluation Board: + +.. code-block:: bash openFPGALoader -b gatemate_evb_jtag .cfg.bit -* Program using Programmer Cable: +2. Program using Programmer Cable: + +.. code-block:: bash openFPGALoader -c gatemate_pgm .cfg.bit SPI Configuration ----------------- -Performs an active hardware reset and writes the configuration into the FPGA latches via SPI. The configuration mode pins CFG_MD[3:0] must be set to 0x40 (SPI passive). +Performs an active hardware reset and writes the configuration into the FPGA latches via SPI. The configuration mode pins ``CFG_MD[3:0]`` must be set to 0x40 (SPI passive). -* Program using Evaluation Board: +1. Program using Evaluation Board: + +.. code-block:: bash openFPGALoader -b gatemate_evb_spi .cfg.bit -* Program using Programmer Cable: +2. Program using Programmer Cable: + +.. code-block:: bash openFPGALoader -b gatemate_pgm_spi .cfg.bit JTAG Flash Access ----------------- -It is possible to access external flashes via the internal JTAG-SPI-bypass. The configuration mode pins CFG_MD[3:0] must be set to 0xF0 (JTAG). Note that the FPGA will not start automatically. +It is possible to access external flashes via the internal JTAG-SPI-bypass. The configuration mode pins ``CFG_MD[3:0]`` must be set to 0xF0 (JTAG). Note that the FPGA will not start automatically. -* Write to flash using Evaluation Board: +1. Write to flash using Evaluation Board: + +.. code-block:: bash openFPGALoader -b gatemate_evb_jtag .cfg.bit -* Write to flash using Programmer Cable: +2. Write to flash using Programmer Cable: + +.. code-block:: bash openFPGALoader -c gatemate_pgm -f .cfg.bit The `offset` parameter can be used to store data at any point in the flash, e.g.: +.. code-block:: bash + openFPGALoader -b gatemate_evb_jtag -o .cfg.bit SPI Flash Access ---------------- -If the programming device and FPGA share the same SPI signals, it is possible to hold the FPGA in reset and write data to the flash. The configuration mode can be set as desired. If the FPGA should start from the external memory after reset, the configuration mode pins CFG_MD[3:0] set to 0x00 (SPI active). +If the programming device and FPGA share the same SPI signals, it is possible to hold the FPGA in reset and write data to the flash. The configuration mode can be set as desired. If the FPGA should start from the external memory after reset, the configuration mode pins ``CFG_MD[3:0]`` set to 0x00 (SPI active). -* Write to flash using Evaluation Board: +1. Write to flash using Evaluation Board: + +.. code-block:: bash openFPGALoader -b gatemate_evb_spi -f .cfg.bit -* Write to flash using Programmer Cable: +2. Write to flash using Programmer Cable: + +.. code-block:: bash openFPGALoader -b gatemate_pgm_spi -f .cfg.bit The `offset` parameter can be used to store data at any point in the flash, e.g.: +.. code-block:: bash + openFPGALoader -b gatemate_evb_spi -o .cfg.bit \ No newline at end of file From e4971897d7f9186446926a55255bbe27b8ea389e Mon Sep 17 00:00:00 2001 From: Patrick Urban Date: Sat, 11 Dec 2021 16:35:30 +0100 Subject: [PATCH 03/10] jtag: ckeck highest nibble to prevent confusion between Cologne Chip GateMate and Efinix Trion T4/T8 devices --- src/jtag.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/jtag.cpp b/src/jtag.cpp index cbfe6aa..c95577e 100644 --- a/src/jtag.cpp +++ b/src/jtag.cpp @@ -136,7 +136,13 @@ int Jtag::detectChain(int max_dev) for (int ii=0; ii < 4; ii++) tmp |= (rx_buff[ii] << (8*ii)); if (tmp != 0 && tmp != 0xffffffff) { - tmp &= 0xffffffff; + /* ckeck highest nibble to prevent confusion between Cologne Chip + * GateMate and Efinix Trion T4/T8 devices + */ + if (tmp != 0x20000001) + tmp &= 0x0fffffff; + else + tmp &= 0xffffffff; _devices_list.insert(_devices_list.begin(), tmp); /* search for irlength in fpga_list or misc_dev_list */ From dd0d668d96cab239d0b59355caf643ca5a826091 Mon Sep 17 00:00:00 2001 From: Patrick Urban Date: Sat, 11 Dec 2021 16:36:29 +0100 Subject: [PATCH 04/10] colognechip integration: update code style --- src/colognechip.cpp | 56 +++++++++++++++------------------------------ src/colognechip.hpp | 1 + 2 files changed, 20 insertions(+), 37 deletions(-) diff --git a/src/colognechip.cpp b/src/colognechip.cpp index 18ba091..a4e1e44 100644 --- a/src/colognechip.cpp +++ b/src/colognechip.cpp @@ -23,8 +23,7 @@ CologneChip::CologneChip(FtdiSpi *spi, const std::string &filename, if (prg_type == Device::WR_SRAM) { _mode = Device::MEM_MODE; - } - else { + } else { _mode = Device::FLASH_MODE; } } @@ -39,8 +38,7 @@ CologneChip::CologneChip(Jtag* jtag, const std::string &filename, std::string spi_board_name; if (board_name != "-") { spi_board_name = std::regex_replace(board_name, std::regex("jtag"), "spi"); - } - else if (cable_name == "gatemate_pgm") { + } else if (cable_name == "gatemate_pgm") { spi_board_name = "gatemate_pgm_spi"; } @@ -61,8 +59,7 @@ CologneChip::CologneChip(Jtag* jtag, const std::string &filename, if (prg_type == Device::WR_SRAM) { _mode = Device::MEM_MODE; - } - else { + } else { _mode = Device::FLASH_MODE; } } @@ -72,14 +69,11 @@ CologneChip::CologneChip(Jtag* jtag, const std::string &filename, */ void CologneChip::reset() { - if (_spi) - { + if (_spi) { _spi->gpio_clear(_rstn_pin | _oen_pin); usleep(SLEEP_US); _spi->gpio_set(_rstn_pin); - } - else if (_ftdi_jtag) - { + } else if (_ftdi_jtag) { _ftdi_jtag->gpio_clear(_rstn_pin | _oen_pin); usleep(SLEEP_US); _ftdi_jtag->gpio_set(_rstn_pin); @@ -95,8 +89,7 @@ bool CologneChip::cfgDone() uint16_t status = 0; if (_spi) { status = _spi->gpio_get(true); - } - else if (_ftdi_jtag) { + } else if (_ftdi_jtag) { status = _ftdi_jtag->gpio_get(true); } bool done = (status & _done_pin) > 0; @@ -113,8 +106,7 @@ bool CologneChip::dumpFlash(const std::string &filename, uint32_t base_addr, if (_spi) { /* enable output and hold reset */ _spi->gpio_clear(_rstn_pin | _oen_pin); - } - else if (_ftdi_jtag) { + } else if (_ftdi_jtag) { /* enable output and disable reset */ _ftdi_jtag->gpio_clear(_oen_pin); _ftdi_jtag->gpio_set(_rstn_pin); @@ -126,8 +118,7 @@ bool CologneChip::dumpFlash(const std::string &filename, uint32_t base_addr, SPIFlash *flash; if (_spi) { flash = new SPIFlash(reinterpret_cast(_spi), _verbose); - } - else if (_ftdi_jtag) { + } else if (_ftdi_jtag) { flash = new SPIFlash(this, _verbose); } flash->reset(); @@ -142,8 +133,7 @@ bool CologneChip::dumpFlash(const std::string &filename, uint32_t base_addr, if (_spi) { /* disable output and release reset */ _spi->gpio_set(_rstn_pin | _oen_pin); - } - else if (_ftdi_jtag) { + } else if (_ftdi_jtag) { /* disable output */ _ftdi_jtag->gpio_set(_oen_pin); } @@ -161,8 +151,7 @@ void CologneChip::program(unsigned int offset) ConfigBitstreamParser *cfg; if (_file_extension == "bit") { cfg = new RawParser(_filename, false); - } - else if (_file_extension == "cfg") { + } else if (_file_extension == "cfg") { cfg = new CologneChipCfgParser(_filename, false); } @@ -175,16 +164,14 @@ void CologneChip::program(unsigned int offset) case Device::FLASH_MODE: if (_jtag != NULL) { programJTAG_flash(offset, data, length); - } - else if (_jtag == NULL) { + } else if (_jtag == NULL) { programSPI_flash(offset, data, length); } break; case Device::MEM_MODE: if (_jtag != NULL) { programJTAG_sram(data, length); - } - else if (_jtag == NULL) { + } else if (_jtag == NULL) { programSPI_sram(data, length); } break; @@ -213,8 +200,7 @@ void CologneChip::programSPI_sram(uint8_t *data, int length) } while (!cfgDone() && timeout > 0); if (timeout == 0) { printError("FAIL"); - } - else { + } else { printSuccess("DONE"); } @@ -257,8 +243,7 @@ void CologneChip::programSPI_flash(unsigned int offset, uint8_t *data, int lengt } while (!cfgDone() && timeout > 0); if (timeout == 0) { printError("FAIL"); - } - else { + } else { printSuccess("DONE"); } @@ -283,11 +268,9 @@ void CologneChip::programJTAG_sram(uint8_t *data, int length) _jtag->shiftIR(JTAG_CONFIGURE, 6, Jtag::SELECT_DR_SCAN); - //ProgressBar progress("Loading", length, 50, _quiet); + ProgressBar progress("Load SRAM via JTAG", length, 50, _quiet); for (int i = 0; i < length; i += size) { - //progress.display(i); - if (length < i + size) size = length-i; @@ -295,9 +278,10 @@ void CologneChip::programJTAG_sram(uint8_t *data, int length) tmp[ii] = data[i+ii]; _jtag->shiftDR(tmp, NULL, size*8, Jtag::SHIFT_DR); + progress.display(i); } - //progress.done(); + progress.done(); _jtag->set_state(Jtag::RUN_TEST_IDLE); printInfo("Wait for CFG_DONE ", false); @@ -307,8 +291,7 @@ void CologneChip::programJTAG_sram(uint8_t *data, int length) } while (!cfgDone() && timeout > 0); if (timeout == 0) { printError("FAIL"); - } - else { + } else { printSuccess("DONE"); } @@ -418,8 +401,7 @@ int CologneChip::spi_wait(uint8_t cmd, uint8_t mask, uint8_t cond, uint8_t b0 = ConfigBitstreamParser::reverseByte(rx[0]); uint8_t b1 = ConfigBitstreamParser::reverseByte(rx[1]); tmp = (b0 << 1) | ((b1 >> 7) & 0x01); - } - else { + } else { _jtag->read_write(dummy, rx, 8, 0); tmp = ConfigBitstreamParser::reverseByte(rx[0]); } diff --git a/src/colognechip.hpp b/src/colognechip.hpp index ddd4878..b148a9b 100644 --- a/src/colognechip.hpp +++ b/src/colognechip.hpp @@ -17,6 +17,7 @@ #include "rawParser.hpp" #include "colognechipCfgParser.hpp" #include "spiFlash.hpp" +#include "progressBar.hpp" class CologneChip: public Device, SPIInterface { public: From e252e713dd51ca8479756700c42b1d0f40bda8d1 Mon Sep 17 00:00:00 2001 From: Patrick Urban Date: Sun, 12 Dec 2021 11:55:10 +0100 Subject: [PATCH 05/10] colognechip integration: apply review remarks * add missing #include * add comment to part.hpp why highest nibble should be kept * remove _reverseOrder variable from colognechipCfgParser.{hpp,cpp} * rename cfgDone() to to a more meaningfull waitCfgDone() --- src/colognechip.cpp | 10 +++++----- src/colognechip.hpp | 3 ++- src/colognechipCfgParser.cpp | 4 ++-- src/colognechipCfgParser.hpp | 5 +---- src/part.hpp | 1 + 5 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/colognechip.cpp b/src/colognechip.cpp index a4e1e44..f6a3928 100644 --- a/src/colognechip.cpp +++ b/src/colognechip.cpp @@ -84,7 +84,7 @@ void CologneChip::reset() * Obtain CFG_DONE and ~CFG_FAILED signals. Configuration is successfull iff * CFG_DONE=true and ~CFG_FAILED=false. */ -bool CologneChip::cfgDone() +bool CologneChip::waitCfgDone() { uint16_t status = 0; if (_spi) { @@ -152,7 +152,7 @@ void CologneChip::program(unsigned int offset) if (_file_extension == "bit") { cfg = new RawParser(_filename, false); } else if (_file_extension == "cfg") { - cfg = new CologneChipCfgParser(_filename, false); + cfg = new CologneChipCfgParser(_filename); } cfg->parse(); @@ -197,7 +197,7 @@ void CologneChip::programSPI_sram(uint8_t *data, int length) do { timeout--; usleep(SLEEP_US); - } while (!cfgDone() && timeout > 0); + } while (!waitCfgDone() && timeout > 0); if (timeout == 0) { printError("FAIL"); } else { @@ -240,7 +240,7 @@ void CologneChip::programSPI_flash(unsigned int offset, uint8_t *data, int lengt do { timeout--; usleep(SLEEP_US); - } while (!cfgDone() && timeout > 0); + } while (!waitCfgDone() && timeout > 0); if (timeout == 0) { printError("FAIL"); } else { @@ -288,7 +288,7 @@ void CologneChip::programJTAG_sram(uint8_t *data, int length) do { timeout--; usleep(SLEEP_US); - } while (!cfgDone() && timeout > 0); + } while (!waitCfgDone() && timeout > 0); if (timeout == 0) { printError("FAIL"); } else { diff --git a/src/colognechip.hpp b/src/colognechip.hpp index b148a9b..5c6225d 100644 --- a/src/colognechip.hpp +++ b/src/colognechip.hpp @@ -9,6 +9,7 @@ #include #include +#include #include "device.hpp" #include "jtag.hpp" @@ -31,7 +32,7 @@ class CologneChip: public Device, SPIInterface { bool verify, int8_t verbose); ~CologneChip() {} - bool cfgDone(); + bool waitCfgDone(); bool dumpFlash(const std::string &filename, uint32_t base_addr, uint32_t len); void program(unsigned int offset = 0) override; diff --git a/src/colognechipCfgParser.cpp b/src/colognechipCfgParser.cpp index 3e867bc..0d4faad 100644 --- a/src/colognechipCfgParser.cpp +++ b/src/colognechipCfgParser.cpp @@ -8,9 +8,9 @@ #include "colognechipCfgParser.hpp" -CologneChipCfgParser::CologneChipCfgParser(const std::string &filename, bool reverseOrder): +CologneChipCfgParser::CologneChipCfgParser(const std::string &filename): ConfigBitstreamParser(filename, ConfigBitstreamParser::ASCII_MODE, - false), _reverseOrder(reverseOrder) + false) {} int CologneChipCfgParser::parse() diff --git a/src/colognechipCfgParser.hpp b/src/colognechipCfgParser.hpp index 8725c41..d4375f8 100644 --- a/src/colognechipCfgParser.hpp +++ b/src/colognechipCfgParser.hpp @@ -14,12 +14,9 @@ class CologneChipCfgParser: public ConfigBitstreamParser { public: - CologneChipCfgParser(const std::string &filename, bool reverseOrder); + CologneChipCfgParser(const std::string &filename); int parse() override; - - private: - bool _reverseOrder; }; #endif // SRC_COLOGNECHIPCFGPARSER_HPP_ diff --git a/src/part.hpp b/src/part.hpp index be411d0..16c91be 100644 --- a/src/part.hpp +++ b/src/part.hpp @@ -108,6 +108,7 @@ static std::map fpga_list = { {0x0300181b, {"Gowin", "GW1NS", "GW1NS-2C", 8}}, {0x0100981b, {"Gowin", "GW1NSR", "GW1NSR-4C", 8}}, + /* keep highest nibble to prevent confusion with Efinix T4/T8 IDCODE */ {0x20000001, {"colognechip", "GateMate Series", "GM1Ax", 6}}, }; From 1e1b93c27a6f92aaf9aecf572e9cca9cc7fcef7b Mon Sep 17 00:00:00 2001 From: Patrick Urban Date: Sun, 12 Dec 2021 15:34:25 +0100 Subject: [PATCH 06/10] colognechip integration: introduce waitCfgDone() and check for raw data --- src/colognechip.cpp | 72 ++++++++++++++++++++------------------------- src/colognechip.hpp | 3 +- 2 files changed, 34 insertions(+), 41 deletions(-) diff --git a/src/colognechip.cpp b/src/colognechip.cpp index f6a3928..3d2ca88 100644 --- a/src/colognechip.cpp +++ b/src/colognechip.cpp @@ -84,7 +84,7 @@ void CologneChip::reset() * Obtain CFG_DONE and ~CFG_FAILED signals. Configuration is successfull iff * CFG_DONE=true and ~CFG_FAILED=false. */ -bool CologneChip::waitCfgDone() +bool CologneChip::cfgDone() { uint16_t status = 0; if (_spi) { @@ -97,6 +97,25 @@ bool CologneChip::waitCfgDone() return (done && !fail); } +/** + * Prints information if configuration was successfull. + */ +void CologneChip::waitCfgDone() +{ + uint32_t timeout = 1000; + + printInfo("Wait for CFG_DONE ", false); + do { + timeout--; + usleep(SLEEP_US); + } while (!cfgDone() && timeout > 0); + if (timeout == 0) { + printError("FAIL"); + } else { + printSuccess("DONE"); + } +} + /** * Dump flash contents to file. Works in both SPI and JTAG-SPI-bypass mode. */ @@ -149,10 +168,16 @@ bool CologneChip::dumpFlash(const std::string &filename, uint32_t base_addr, void CologneChip::program(unsigned int offset) { ConfigBitstreamParser *cfg; - if (_file_extension == "bit") { - cfg = new RawParser(_filename, false); - } else if (_file_extension == "cfg") { + if (_file_extension == "cfg") { cfg = new CologneChipCfgParser(_filename); + } else if (_file_extension == "bit") { + cfg = new RawParser(_filename, false); + } else { /* unknown type: */ + if (_mode == Device::FLASH_MODE) { + cfg = new RawParser(_filename, false); + } else { + throw std::runtime_error("incompatible file format"); + } } cfg->parse(); @@ -184,8 +209,6 @@ void CologneChip::program(unsigned int offset) */ void CologneChip::programSPI_sram(uint8_t *data, int length) { - uint32_t timeout = 1000; - /* hold device in reset for a moment */ reset(); @@ -193,16 +216,7 @@ void CologneChip::programSPI_sram(uint8_t *data, int length) _spi->gpio_set(_rstn_pin); _spi->spi_put(data, recv, length); // TODO _spi->spi_put(data, null, length) does not work? - printInfo("Wait for CFG_DONE ", false); - do { - timeout--; - usleep(SLEEP_US); - } while (!waitCfgDone() && timeout > 0); - if (timeout == 0) { - printError("FAIL"); - } else { - printSuccess("DONE"); - } + waitCfgDone(); _spi->gpio_set(_oen_pin); delete [] recv; @@ -215,8 +229,6 @@ void CologneChip::programSPI_sram(uint8_t *data, int length) */ void CologneChip::programSPI_flash(unsigned int offset, uint8_t *data, int length) { - uint32_t timeout = 1000; - /* hold device in reset during flash write access */ _spi->gpio_clear(_rstn_pin | _oen_pin); usleep(SLEEP_US); @@ -236,16 +248,7 @@ void CologneChip::programSPI_flash(unsigned int offset, uint8_t *data, int lengt _spi->gpio_set(_rstn_pin); usleep(SLEEP_US); - printInfo("Wait for CFG_DONE ", false); - do { - timeout--; - usleep(SLEEP_US); - } while (!waitCfgDone() && timeout > 0); - if (timeout == 0) { - printError("FAIL"); - } else { - printSuccess("DONE"); - } + waitCfgDone(); _spi->gpio_set(_oen_pin); } @@ -256,8 +259,6 @@ void CologneChip::programSPI_flash(unsigned int offset, uint8_t *data, int lengt */ void CologneChip::programJTAG_sram(uint8_t *data, int length) { - uint32_t timeout = 1000; - /* hold device in reset for a moment */ reset(); @@ -284,16 +285,7 @@ void CologneChip::programJTAG_sram(uint8_t *data, int length) progress.done(); _jtag->set_state(Jtag::RUN_TEST_IDLE); - printInfo("Wait for CFG_DONE ", false); - do { - timeout--; - usleep(SLEEP_US); - } while (!waitCfgDone() && timeout > 0); - if (timeout == 0) { - printError("FAIL"); - } else { - printSuccess("DONE"); - } + waitCfgDone(); _ftdi_jtag->gpio_set(_oen_pin); } diff --git a/src/colognechip.hpp b/src/colognechip.hpp index 5c6225d..a5f860e 100644 --- a/src/colognechip.hpp +++ b/src/colognechip.hpp @@ -32,7 +32,8 @@ class CologneChip: public Device, SPIInterface { bool verify, int8_t verbose); ~CologneChip() {} - bool waitCfgDone(); + bool cfgDone(); + void waitCfgDone(); bool dumpFlash(const std::string &filename, uint32_t base_addr, uint32_t len); void program(unsigned int offset = 0) override; From d4f17481d5429ab64b21199bc77cae01d70e5f43 Mon Sep 17 00:00:00 2001 From: Patrick Urban Date: Sun, 12 Dec 2021 18:26:14 +0100 Subject: [PATCH 07/10] colognechip integration: update documentation * compatibility/board.rst, compatibility/cable.rst, compatibility/fpga.rst * README.md and doc/index.rst to add new doc/vendors/colognechip.rst entry --- README.md | 3 ++- doc/compatibility/board.rst | 3 +++ doc/compatibility/cable.rst | 1 + doc/compatibility/fpga.rst | 51 +++++++++++++++++++------------------ doc/index.rst | 4 ++- 5 files changed, 35 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 2e67742..d047adf 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ First stepsInstallTroubleshootingAdvanced usage

-Universal utility for programming FPGAs. Compatible with many boards, cables and FPGA from major manufacturers (Xilinx, Altera/Intel, Lattice, Gowin, Efinix, Anlogic). openFPGALoader works on Linux, Windows and macOS. +Universal utility for programming FPGAs. Compatible with many boards, cables and FPGA from major manufacturers (Xilinx, Altera/Intel, Lattice, Gowin, Efinix, Anlogic, Cologne Chip). openFPGALoader works on Linux, Windows and macOS. Not sure if your hardware is supported? Check the hardware compatibility lists: @@ -22,6 +22,7 @@ Not sure if your hardware is supported? Check the hardware compatibility lists: Also checkout the vendor-specific documentation: [Anlogic](https://trabucayre.github.io/openFPGALoader/vendors/anlogic.html), +[Cologne Chip](https://trabucayre.github.io/openFPGALoader/vendors/colognechip.html), [Efinix](https://trabucayre.github.io/openFPGALoader/vendors/efinix.html), [Gowin](https://trabucayre.github.io/openFPGALoader/vendors/gowin.html), [Intel/Altera](https://trabucayre.github.io/openFPGALoader/vendors/intel.html), diff --git a/doc/compatibility/board.rst b/doc/compatibility/board.rst index 50db9ce..dd578df 100644 --- a/doc/compatibility/board.rst +++ b/doc/compatibility/board.rst @@ -21,6 +21,9 @@ Boards arty `Digilent Analog Discovery 2 `__ Spartan6 xc6slx25 OK NT arty `Digilent Digital Discovery `__ Spartan6 xc6slx25 OK NT basys3 `Digilent Basys3 `__ Artix xc7a35tcpg236 OK OK + gatemate_evb_jtag `Cologne Chip GateMate FPGA Evaluation Boad (JTAG mode) `__ Cologne Chip GateMate Series OK OK + gatemate_evb_spi `Cologne Chip GateMate FPGA Evaluation Boad (SPI mode) `__ Cologne Chip GateMate Series OK OK + gatemate_pgm_spi `Cologne Chip GateMate FPGA Programmer (SPI mode) `__ Cologne Chip GateMate Series OK OK colorlight `Colorlight 5A-75B (version 7) `__ ECP5 LFE5U-25F-6BG256C OK OK colorlight_i5 `Colorlight I5 `__ ECP5 LFE5U-25F-6BG381C OK OK crosslinknx_evn `Lattice CrossLink-NX Evaluation Board `__ Nexus LIFCL-40 OK OK diff --git a/doc/compatibility/cable.rst b/doc/compatibility/cable.rst index 37fe25f..8fed6e0 100644 --- a/doc/compatibility/cable.rst +++ b/doc/compatibility/cable.rst @@ -20,3 +20,4 @@ Cables * `Tang Nano 4k USB-JTAG interface `__: USB-JTAG/UART debugger based on BL702 microcontroler. * `Tigard `__: SWD/JTAG/UART/SPI programmer based on Ftdi FT2232HQ * `honeycomb USB-JTAG interface `__: FT2232C clone based on STM32F042 microcontroler +* `Cologne Chip GateMate FPGA Programmer `__ FT232H-based JTAG/SPI programmer cable diff --git a/doc/compatibility/fpga.rst b/doc/compatibility/fpga.rst index 2db3d20..fc325f0 100644 --- a/doc/compatibility/fpga.rst +++ b/doc/compatibility/fpga.rst @@ -3,31 +3,32 @@ FPGAs ##### -======== =================================================================================================================================== ====== ===== - Vendor Model Memory Flash -======== =================================================================================================================================== ====== ===== -Anlogic `EG4S20 `__ OK AS - Efinix `Trion T8 `__ NA OK - Gowin `GW1N (GW1N-1, GW1N-4, GW1NR-9, GW1NS-2C, GW1NSR-4C) `__ OK IF - Intel Cyclone III `EP3C16 `__ OK OK - Intel Cyclone IV CE `EP4CE22 `__ OK OK - Intel Cyclone V E `5CEA2, 5CEBA4 `__ OK OK - Intel Cyclone 10 LP `10CL025 `__ OK OK -Lattice `CrossLink-NX (LIFCL-40) `__ OK OK -Lattice `ECP5 (25F, 5G 85F) `__ OK OK -Lattice `iCE40 (HX1K, HX4K, HX8K, UP5K) `__ NA AS -Lattice `MachXO2 `__ OK OK -Lattice `MachXO3D `__ OK OK -Lattice `MachXO3LF `__ OK OK - Xilinx Artix 7 `xc7a35ti, xc7a50t, xc7a75t, xc7a100t, xc7a200t `__ OK OK - Xilinx Kintex 7 `xc7k325t `__ OK NT - Xilinx Spartan 3 `xc3s200 `__ OK NA - Xilinx Spartan 6 `xc6slx9, xc6slx16, xc6slx25, xc6slx45 `__ OK OK - Xilinx Spartan 7 `xc7s15, xc7s25, xc7s50 `__ OK OK - Xilinx XC9500XL `xc9536xl, xc9572xl, xc95144xl, xc95188xl `__ NA OK - Xilinx XC2C (coolrunner II) `xc2c32a `__ TBD OK - Xilinx XCF `xcf01s, xcf02s, xcf04s `__ NA OK -======== =================================================================================================================================== ====== ===== +============= =================================================================================================================================== ====== ===== + Vendor Model Memory Flash +============= =================================================================================================================================== ====== ===== + Anlogic `EG4S20 `__ OK AS +Cologne Chip `GateMate Series` __ OK OK + Efinix `Trion T8 `__ NA OK + Gowin `GW1N (GW1N-1, GW1N-4, GW1NR-9, GW1NS-2C, GW1NSR-4C) `__ OK IF + Intel Cyclone III `EP3C16 `__ OK OK + Intel Cyclone IV CE `EP4CE22 `__ OK OK + Intel Cyclone V E `5CEA2, 5CEBA4 `__ OK OK + Intel Cyclone 10 LP `10CL025 `__ OK OK + Lattice `CrossLink-NX (LIFCL-40) `__ OK OK + Lattice `ECP5 (25F, 5G 85F) `__ OK OK + Lattice `iCE40 (HX1K, HX4K, HX8K, UP5K) `__ NA AS + Lattice `MachXO2 `__ OK OK + Lattice `MachXO3D `__ OK OK + Lattice `MachXO3LF `__ OK OK + Xilinx Artix 7 `xc7a35ti, xc7a50t, xc7a75t, xc7a100t, xc7a200t `__ OK OK + Xilinx Kintex 7 `xc7k325t `__ OK NT + Xilinx Spartan 3 `xc3s200 `__ OK NA + Xilinx Spartan 6 `xc6slx9, xc6slx16, xc6slx25, xc6slx45 `__ OK OK + Xilinx Spartan 7 `xc7s15, xc7s25, xc7s50 `__ OK OK + Xilinx XC9500XL `xc9536xl, xc9572xl, xc95144xl, xc95188xl `__ NA OK + Xilinx XC2C (coolrunner II) `xc2c32a `__ TBD OK + Xilinx XCF `xcf01s, xcf02s, xcf04s `__ NA OK +============= =================================================================================================================================== ====== ===== * IF: Internal Flash * AS: Active Serial flash mode diff --git a/doc/index.rst b/doc/index.rst index f206180..7a93723 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -4,7 +4,7 @@ openFPGALoader: universal utility for programming FPGA Welcome to the documentation of openFPGALoader! openFPGALoader is a universal utility for programming FPGAs. -Compatible with many boards, cables and FPGA from major manufacturers (Xilinx, Altera/Intel, Lattice, Gowin, Efinix, Anlogic). +Compatible with many boards, cables and FPGA from major manufacturers (Xilinx, Altera/Intel, Lattice, Gowin, Efinix, Anlogic, Cologne Chip). openFPGALoader works on Linux, Windows and macOS. Not sure if your hardware is supported? Check the hardware compatibility lists: @@ -16,6 +16,7 @@ Not sure if your hardware is supported? Check the hardware compatibility lists: Also checkout the vendor-specific documentation: * `Anlogic `__ +* `Cologne Chip `__ * `Efinix `__ * `Gowin `__ * `Intel/Altera `__ @@ -44,6 +45,7 @@ Also checkout the vendor-specific documentation: :hidden: vendors/anlogic + vendors/colognechip vendors/efinix vendors/gowin vendors/intel From c78aef0d0796a9b4b35e7529b0842727c7f56859 Mon Sep 17 00:00:00 2001 From: Patrick Urban Date: Sun, 12 Dec 2021 18:31:05 +0100 Subject: [PATCH 08/10] colognechip integration: fix broken documentation --- doc/compatibility/cable.rst | 2 +- doc/compatibility/fpga.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/compatibility/cable.rst b/doc/compatibility/cable.rst index 8fed6e0..8656034 100644 --- a/doc/compatibility/cable.rst +++ b/doc/compatibility/cable.rst @@ -20,4 +20,4 @@ Cables * `Tang Nano 4k USB-JTAG interface `__: USB-JTAG/UART debugger based on BL702 microcontroler. * `Tigard `__: SWD/JTAG/UART/SPI programmer based on Ftdi FT2232HQ * `honeycomb USB-JTAG interface `__: FT2232C clone based on STM32F042 microcontroler -* `Cologne Chip GateMate FPGA Programmer `__ FT232H-based JTAG/SPI programmer cable +* `Cologne Chip GateMate FPGA Programmer `__: FT232H-based JTAG/SPI programmer cable diff --git a/doc/compatibility/fpga.rst b/doc/compatibility/fpga.rst index fc325f0..454e672 100644 --- a/doc/compatibility/fpga.rst +++ b/doc/compatibility/fpga.rst @@ -7,7 +7,7 @@ FPGAs Vendor Model Memory Flash ============= =================================================================================================================================== ====== ===== Anlogic `EG4S20 `__ OK AS -Cologne Chip `GateMate Series` __ OK OK +Cologne Chip `GateMate Series` `__ OK OK Efinix `Trion T8 `__ NA OK Gowin `GW1N (GW1N-1, GW1N-4, GW1NR-9, GW1NS-2C, GW1NSR-4C) `__ OK IF Intel Cyclone III `EP3C16 `__ OK OK From 5e0262a7dbef50c8387707c6b8c4e2f918cfe841 Mon Sep 17 00:00:00 2001 From: Patrick Urban Date: Sun, 12 Dec 2021 19:10:17 +0100 Subject: [PATCH 09/10] colognechip integration: fix typo --- doc/compatibility/board.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/compatibility/board.rst b/doc/compatibility/board.rst index dd578df..470b024 100644 --- a/doc/compatibility/board.rst +++ b/doc/compatibility/board.rst @@ -21,8 +21,8 @@ Boards arty `Digilent Analog Discovery 2 `__ Spartan6 xc6slx25 OK NT arty `Digilent Digital Discovery `__ Spartan6 xc6slx25 OK NT basys3 `Digilent Basys3 `__ Artix xc7a35tcpg236 OK OK - gatemate_evb_jtag `Cologne Chip GateMate FPGA Evaluation Boad (JTAG mode) `__ Cologne Chip GateMate Series OK OK - gatemate_evb_spi `Cologne Chip GateMate FPGA Evaluation Boad (SPI mode) `__ Cologne Chip GateMate Series OK OK + gatemate_evb_jtag `Cologne Chip GateMate FPGA Evaluation Board (JTAG mode) `__ Cologne Chip GateMate Series OK OK + gatemate_evb_spi `Cologne Chip GateMate FPGA Evaluation Board (SPI mode) `__ Cologne Chip GateMate Series OK OK gatemate_pgm_spi `Cologne Chip GateMate FPGA Programmer (SPI mode) `__ Cologne Chip GateMate Series OK OK colorlight `Colorlight 5A-75B (version 7) `__ ECP5 LFE5U-25F-6BG256C OK OK colorlight_i5 `Colorlight I5 `__ ECP5 LFE5U-25F-6BG381C OK OK From 9f530227c42a2ccf4d32fab2bcd976491a13e8b2 Mon Sep 17 00:00:00 2001 From: Patrick Urban Date: Mon, 13 Dec 2021 10:20:20 +0100 Subject: [PATCH 10/10] colognechip integration: fix fpga.rst entry --- doc/compatibility/fpga.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/compatibility/fpga.rst b/doc/compatibility/fpga.rst index c19b31c..0cc825b 100644 --- a/doc/compatibility/fpga.rst +++ b/doc/compatibility/fpga.rst @@ -8,7 +8,7 @@ FPGAs ============= =================================================================================================================================== ====== ===== Anlogic `EG4S20 `__ OK AS Anlogic `EF2M45 `__ OK OK -Cologne Chip `GateMate Series` `__ OK OK +Cologne Chip `GateMate Series `__ OK OK Efinix `Trion T8 `__ NA OK Gowin `GW1N (GW1N-1, GW1N-4, GW1NR-9, GW1NS-2C, GW1NSR-4C) `__ OK IF Intel Cyclone III `EP3C16 `__ OK OK