diff --git a/spiOverJtag/efinix_spiOverJtag.v b/spiOverJtag/efinix_spiOverJtag.v new file mode 100644 index 0000000..dd9a2e0 --- /dev/null +++ b/spiOverJtag/efinix_spiOverJtag.v @@ -0,0 +1,59 @@ +module spiOverJtag ( + input jtag_1_CAPTURE, + input jtag_1_DRCK, + input jtag_1_RESET, + input jtag_1_RUNTEST, + input jtag_1_SEL, + input jtag_1_SHIFT, + input jtag_1_TCK, + input jtag_1_TDI, + input jtag_1_TMS, + input jtag_1_UPDATE, + output jtag_1_TDO, + + output csn, + output sck, + output sdi_dq0, + input sdo_dq1, + output wpn_dq2, + output hldn_dq3 +); + + wire capture, drck, sel, update; + wire runtest; + wire tdi; + reg fsm_csn; + + assign wpn_dq2 = 1'b1; + assign hldn_dq3 = 1'b1; + // jtag -> spi flash + assign sdi_dq0 = tdi; + wire tdo = (sel) ? sdo_dq1 : tdi; + assign csn = fsm_csn; + + wire tmp_cap_s = capture && sel; + wire tmp_up_s = update && sel; + + always @(posedge drck, posedge runtest) begin + if (runtest) begin + fsm_csn <= 1'b1; + end else begin + if (tmp_cap_s) begin + fsm_csn <= 1'b0; + end else if (tmp_up_s) begin + fsm_csn <= 1'b1; + end else begin + fsm_csn <= fsm_csn; + end + end + end + + assign sck = drck; + assign capture = jtag_1_CAPTURE; + assign drck = jtag_1_DRCK; + assign runtest = jtag_1_RUNTEST; + assign sel = jtag_1_SEL; + assign tdi = jtag_1_TDI; + assign update = jtag_1_UPDATE; + assign jtag_1_TDO = tdo; +endmodule diff --git a/spiOverJtag/spiOverJtag_efinix_t8f81.bit.gz b/spiOverJtag/spiOverJtag_efinix_t8f81.bit.gz new file mode 100644 index 0000000..26a86e2 Binary files /dev/null and b/spiOverJtag/spiOverJtag_efinix_t8f81.bit.gz differ diff --git a/src/board.hpp b/src/board.hpp index 3e0a5e4..b40c5b0 100644 --- a/src/board.hpp +++ b/src/board.hpp @@ -207,7 +207,7 @@ static std::map board_list = { JTAG_BOARD("vcu118", "xcvu9p-flga2104", "jtag-smt2-nc", 0, 0, CABLE_DEFAULT), JTAG_BOARD("vcu128", "xcvu37p-fsvh2892", "ft4232", 0, 0, CABLE_DEFAULT), JTAG_BOARD("xem8320", "xcau25p-2ffvb676", "" , 0, 0, CABLE_DEFAULT), - JTAG_BOARD("xyloni_jtag", "", "efinix_jtag_ft4232" , 0, 0, CABLE_DEFAULT), + JTAG_BOARD("xyloni_jtag", "t8f81", "efinix_jtag_ft4232", 0, 0, CABLE_DEFAULT), SPI_BOARD("xyloni_spi", "efinix", "efinix_spi_ft4232", DBUS4, DBUS5, DBUS7, DBUS3, DBUS0, DBUS1, DBUS2, DBUS6, 0, CABLE_DEFAULT), JTAG_BOARD("xtrx", "xc7a50tcpg236", "" , 0, 0, CABLE_DEFAULT), diff --git a/src/efinix.cpp b/src/efinix.cpp index 4627b30..b7ceabf 100644 --- a/src/efinix.cpp +++ b/src/efinix.cpp @@ -12,15 +12,19 @@ #include #include +#include "common.hpp" +#include "device.hpp" #include "display.hpp" #include "efinixHexParser.hpp" -#include "ftdispi.hpp" -#include "device.hpp" #include "ftdiJtagMPSSE.hpp" +#include "ftdispi.hpp" #include "jtag.hpp" #include "part.hpp" #include "progressBar.hpp" #include "rawParser.hpp" +#if defined (_WIN64) || defined (_WIN32) +#include "pathHelper.hpp" +#endif #include "spiFlash.hpp" Efinix::Efinix(FtdiSpi* spi, const std::string &filename, @@ -30,27 +34,29 @@ Efinix::Efinix(FtdiSpi* spi, const std::string &filename, bool verify, int8_t verbose): Device(NULL, filename, file_type, verify, verbose), _ftdi_jtag(NULL), _rst_pin(rst_pin), _done_pin(done_pin), _cs_pin(0), _oe_pin(oe_pin), - _fpga_family(UNKNOWN_FAMILY), _irlen(0) + _fpga_family(UNKNOWN_FAMILY), _irlen(0), _device_package(""), + _spiOverJtagPath("") { _spi = spi; - _spi->gpio_set_input(_done_pin); - _spi->gpio_set_output(_rst_pin | _oe_pin); + init_common(Device::WR_FLASH); } Efinix::Efinix(Jtag* jtag, const std::string &filename, - const std::string &file_type, - const std::string &board_name, - bool verify, int8_t verbose): + const std::string &file_type, Device::prog_type_t prg_type, + const std::string &board_name, const std::string &device_package, + const std::string &spiOverJtagPath, bool verify, int8_t verbose): Device(jtag, filename, file_type, verify, verbose), + SPIInterface(filename, verbose, 256, false, false, false), _spi(NULL), _rst_pin(0), _done_pin(0), _cs_pin(0), - _oe_pin(0), _fpga_family(UNKNOWN_FAMILY), _irlen(0) + _oe_pin(0), _fpga_family(UNKNOWN_FAMILY), _irlen(0), + _device_package(device_package), _spiOverJtagPath(spiOverJtagPath) { _ftdi_jtag = reinterpret_cast(jtag->get_ll_class()); /* detect FPGA type (Trion or Titanium) */ - uint32_t idcode = _jtag->get_target_device_id(); - std::string family = fpga_list[idcode].family; + const uint32_t idcode = _jtag->get_target_device_id(); + const std::string family = fpga_list[idcode].family; if (family == "Titanium") { if (_file_extension == "hex") { throw std::runtime_error("Error: loading hex file is not allowed " @@ -84,7 +90,7 @@ Efinix::Efinix(Jtag* jtag, const std::string &filename, } /* 2: retrieve spi board */ - target_board_t *spi_board = &(board_list[spi_board_name]); + const target_board_t *spi_board = &(board_list[spi_board_name]); /* 3: SPI cable */ cable_t spi_cable = (cable_list[spi_board->cable_name]); @@ -95,31 +101,56 @@ Efinix::Efinix(Jtag* jtag, const std::string &filename, _cs_pin = spi_board->spi_pins_config.cs_pin; _rst_pin = spi_board->reset_pin; _oe_pin = spi_board->oe_pin; + _done_pin = spi_board->done_pin; /* 5: open SPI interface */ _spi = new FtdiSpi(spi_cable, spi_board->spi_pins_config, jtag->getClkFreq(), verbose > 0); /* 6: configure pins direction and default state */ - _spi->gpio_set_output(_oe_pin | _rst_pin | _cs_pin); + init_common(prg_type); +} + +void Efinix::init_common(const Device::prog_type_t &prg_type) +{ + _spi->gpio_set_input(_done_pin); + _spi->gpio_set_output(_rst_pin | _oe_pin); + + switch (prg_type) { + case Device::WR_FLASH: + _mode = (_jtag) ? Device::FLASH_MODE : Device::SPI_MODE; + break; + case Device::WR_SRAM: + if (!_jtag) { + throw std::runtime_error("Efinix: SRAM load requires jtag"); + } + _mode = MEM_MODE; + break; + default: + _mode = NONE_MODE; + } } Efinix::~Efinix() -{} +{ + if (_jtag && _spi) + delete _spi; +} void Efinix::reset() { - if (_ftdi_jtag) // not supported + if (!_spi) // not supported return; uint32_t timeout = 1000; _spi->gpio_clear(_rst_pin | _oe_pin); usleep(1000); _spi->gpio_set(_rst_pin | _oe_pin); + printInfo("Reset ", false); do { timeout--; usleep(12000); - } while (((_spi->gpio_get(true) & _done_pin) == 0) || timeout > 0); + } while (((_spi->gpio_get(true) & _done_pin) == 0) && timeout > 0); if (timeout == 0) printError("FAIL"); else @@ -130,6 +161,8 @@ void Efinix::program(unsigned int offset, bool unprotect_flash) { if (_file_extension.empty()) return; + if (_mode == Device::NONE_MODE) + return; ConfigBitstreamParser *bit; try { @@ -156,16 +189,28 @@ void Efinix::program(unsigned int offset, bool unprotect_flash) return; } - unsigned char *data = bit->getData(); - int length = bit->getLength() / 8; + const uint8_t *data = bit->getData(); + const int length = bit->getLength() / 8; if (_verbose) bit->displayHeader(); - if (_ftdi_jtag) - programJTAG(data, length); - else - programSPI(offset, data, length, unprotect_flash); + switch (_mode) { + case MEM_MODE: + programJTAG(data, length); + break; + case FLASH_MODE: + if (_jtag) + SPIInterface::write(offset, const_cast(data), + length, unprotect_flash); + else + programSPI(offset, data, length, unprotect_flash); + break; + default: + return; + } + + delete bit; } bool Efinix::dumpFlash(uint32_t base_addr, uint32_t len) @@ -203,11 +248,9 @@ bool Efinix::dumpFlash(uint32_t base_addr, uint32_t len) return false; } -void Efinix::programSPI(unsigned int offset, uint8_t *data, int length, - bool unprotect_flash) +void Efinix::programSPI(unsigned int offset, const uint8_t *data, + const int length, const bool unprotect_flash) { - uint32_t timeout = 1000; - _spi->gpio_clear(_rst_pin | _oe_pin); SPIFlash flash(reinterpret_cast(_spi), unprotect_flash, @@ -217,24 +260,13 @@ void Efinix::programSPI(unsigned int offset, uint8_t *data, int length, printf("%02x\n", flash.read_status_reg()); flash.read_id(); - flash.erase_and_prog(offset, data, length); + flash.erase_and_prog(offset, const_cast(data), length); /* verify write if required */ if (_verify) flash.verify(offset, data, length); - _spi->gpio_set(_rst_pin | _oe_pin); - usleep(12000); - - printInfo("Wait for CDONE ", false); - do { - timeout--; - usleep(12000); - } while (((_spi->gpio_get(true) & _done_pin) == 0) && timeout > 0); - if (timeout == 0) - printError("FAIL"); - else - printSuccess("DONE"); + reset(); } #define SAMPLE_PRELOAD 0x02 @@ -243,8 +275,9 @@ void Efinix::programSPI(unsigned int offset, uint8_t *data, int length, #define IDCODE 0x03 #define PROGRAM 0x04 #define ENTERUSER 0x07 +#define USER1 0x08 -void Efinix::programJTAG(uint8_t *data, int length) +void Efinix::programJTAG(const uint8_t *data, const int length) { int xfer_len = 512, tx_end; uint8_t tx[512]; @@ -299,4 +332,155 @@ void Efinix::programJTAG(uint8_t *data, int length) memset(tx, 0, 512); _jtag->shiftDR(tx, NULL, 100); _jtag->shiftIR(IDCODE, _irlen); + uint8_t idc[4]; + _jtag->shiftDR(NULL, idc, 4); + printf("%02x%02x%02x%02x\n", + idc[0], idc[1], idc[2], idc[3]); +} + +bool Efinix::post_flash_access() +{ + if (_skip_reset) + printInfo("Skip resetting device"); + else + reset(); + return true; +} + +bool Efinix::prepare_flash_access() +{ + if (_skip_load_bridge) { + printInfo("Skip loading bridge for spiOverjtag"); + return true; + } + + std::string bitname; + if (!_spiOverJtagPath.empty()) { + bitname = _spiOverJtagPath; + } else { + if (_device_package.empty()) { + printError("Can't program SPI flash: missing device-package information"); + return false; + } + + bitname = get_shell_env_var("OPENFPGALOADER_SOJ_DIR", + DATA_DIR "/openFPGALoader"); + bitname += "/spiOverJtag_efinix_" + _device_package + ".bit.gz"; + } + +#if defined (_WIN64) || defined (_WIN32) + /* Convert relative path embedded at compile time to an absolute path */ + bitname = PathHelper::absolutePath(bitname); +#endif + + std::cout << "use: " << bitname << std::endl; + + /* first: load spi over jtag */ + try { + EfinixHexParser bridge(bitname); + bridge.parse(); + const uint8_t *data = bridge.getData(); + const int length = bridge.getLength() / 8; + programJTAG(data, length); + } catch (std::exception &e) { + printError(e.what()); + return false; + } + + return true; +} + +/* */ +/* SPI interface */ +/* */ + +/* + * jtag : jtag interface + * cmd : opcode for SPI flash + * tx : buffer to send + * rx : buffer to fill + * len : number of byte to send/receive (cmd not comprise) + * so to send only a cmd set len to 0 (or omit this param) + */ +int Efinix::spi_put(uint8_t cmd, + uint8_t *tx, uint8_t *rx, uint32_t len) +{ + int kXferLen = len + 1 + ((rx == NULL) ? 0 : 1); + uint8_t jtx[kXferLen]; + jtx[0] = EfinixHexParser::reverseByte(cmd); + uint8_t jrx[kXferLen]; + if (tx != NULL) { + for (uint32_t i=0; i < len; i++) + jtx[i+1] = EfinixHexParser::reverseByte(tx[i]); + } + /* addr BSCAN user1 */ + _jtag->shiftIR(USER1, _irlen); + /* send first already stored cmd, + * in the same time store each byte + * to next + */ + _jtag->shiftDR(jtx, (rx == NULL)? NULL: jrx, 8*kXferLen); + + if (rx != NULL) { + for (uint32_t i=0; i < len; i++) + rx[i] = EfinixHexParser::reverseByte(jrx[i+1] >> 1) | (jrx[i+2] & 0x01); + } + return 0; +} + +int Efinix::spi_put(uint8_t *tx, uint8_t *rx, uint32_t len) +{ + int kXferLen = len + ((rx == NULL) ? 0 : 1); + uint8_t jtx[kXferLen]; + uint8_t jrx[kXferLen]; + if (tx != NULL) { + for (uint32_t i=0; i < len; i++) + jtx[i] = EfinixHexParser::reverseByte(tx[i]); + } + /* addr BSCAN user1 */ + _jtag->shiftIR(USER1, _irlen); + /* send first already stored cmd, + * in the same time store each byte + * to next + */ + _jtag->shiftDR(jtx, (rx == NULL)? NULL: jrx, 8*kXferLen); + + if (rx != NULL) { + for (uint32_t i=0; i < len; i++) + rx[i] = EfinixHexParser::reverseByte(jrx[i] >> 1) | (jrx[i+1] & 0x01); + } + return 0; +} + +int Efinix::spi_wait(uint8_t cmd, uint8_t mask, uint8_t cond, + uint32_t timeout, bool verbose) +{ + uint8_t rx[2], dummy[2], tmp; + uint8_t tx = EfinixHexParser::reverseByte(cmd); + uint32_t count = 0; + + _jtag->shiftIR(USER1, _irlen, Jtag::UPDATE_IR); + _jtag->shiftDR(&tx, NULL, 8, Jtag::SHIFT_DR); + + do { + _jtag->shiftDR(dummy, rx, 8*2, Jtag::SHIFT_DR); + tmp = (EfinixHexParser::reverseByte(rx[0] >> 1)) | (0x01 & rx[1]); + count++; + if (count == timeout){ + printf("timeout: %x %x %x\n", tmp, rx[0], rx[1]); + break; + } + if (verbose) { + printf("%x %x %x %u\n", tmp, mask, cond, count); + } + } while ((tmp & mask) != cond); + _jtag->shiftDR(dummy, rx, 8*2, Jtag::EXIT1_DR); + _jtag->go_test_logic_reset(); + + if (count == timeout) { + printf("%x\n", tmp); + std::cout << "wait: Error" << std::endl; + return -ETIME; + } + return 0; } diff --git a/src/efinix.hpp b/src/efinix.hpp index 7f71aae..0854bbe 100644 --- a/src/efinix.hpp +++ b/src/efinix.hpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 /* - * Copyright (C) 2020 Gwenhael Goavec-Merou + * Copyright (C) 2020-2023 Gwenhael Goavec-Merou */ #ifndef SRC_EFINIX_HPP_ @@ -12,32 +12,41 @@ #include "ftdiJtagMPSSE.hpp" #include "ftdispi.hpp" #include "jtag.hpp" +#include "spiInterface.hpp" -class Efinix: public Device { +class Efinix: public Device, SPIInterface { public: Efinix(FtdiSpi *spi, const std::string &filename, const std::string &file_type, uint16_t rst_pin, uint16_t done_pin, uint16_t oe_pin, bool verify, int8_t verbose); Efinix(Jtag* jtag, const std::string &filename, - const std::string &file_type, - const std::string &board_name, + const std::string &file_type, Device::prog_type_t prg_type, + const std::string &board_name, const std::string &device_package, + const std::string &spiOverJtagPath, bool verify, int8_t verbose); ~Efinix(); void program(unsigned int offset, bool unprotect_flash) override; bool dumpFlash(uint32_t base_addr, uint32_t len) override; - virtual bool protect_flash(uint32_t len) override { + bool protect_flash(uint32_t len) override { (void) len; printError("protect flash not supported"); return false;} - virtual bool unprotect_flash() override { + bool unprotect_flash() override { printError("unprotect flash not supported"); return false;} - virtual bool bulk_erase_flash() override { + bool bulk_erase_flash() override { printError("bulk erase flash not supported"); return false;} /* not supported in SPI Active mode */ int idCode() override {return 0;} void reset() override; + /* spi interface */ + 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; + private: /* list of efinix family devices */ enum efinix_family_t { @@ -45,9 +54,12 @@ class Efinix: public Device { TRION_FAMILY, UNKNOWN_FAMILY = 999 }; - void programSPI(unsigned int offset, uint8_t *data, int length, - bool unprotect_flash); - void programJTAG(uint8_t *data, int length); + void init_common(const Device::prog_type_t &prg_type); + void programSPI(unsigned int offset, const uint8_t *data, + const int length, const bool unprotect_flash); + void programJTAG(const uint8_t *data, const int length); + bool post_flash_access() override; + bool prepare_flash_access() override; FtdiSpi *_spi; FtdiJtagMPSSE *_ftdi_jtag; uint16_t _rst_pin; @@ -56,6 +68,8 @@ class Efinix: public Device { uint16_t _oe_pin; efinix_family_t _fpga_family; int _irlen; + std::string _device_package; + std::string _spiOverJtagPath; }; #endif // SRC_EFINIX_HPP_ diff --git a/src/main.cpp b/src/main.cpp index e1ad65d..56bb5dd 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -556,7 +556,8 @@ int main(int argc, char **argv) args.prg_type, args.verify, args.verbose); } else if (fab == "efinix") { fpga = new Efinix(jtag, args.bit_file, args.file_type, - /*DBUS4 | DBUS7, DBUS5*/args.board, args.verify, args.verbose); + args.prg_type, args.board, args.fpga_part, args.bridge_path, + args.verify, args.verbose); } else if (fab == "Gowin") { fpga = new Gowin(jtag, args.bit_file, args.file_type, args.mcufw, args.prg_type, args.external_flash, args.verify, args.verbose);