From 07a0708eb8c85c48fbcffd961b6296826b217d34 Mon Sep 17 00:00:00 2001 From: Gwenhael Goavec-Merou Date: Wed, 19 Apr 2023 06:58:39 +0200 Subject: [PATCH] efinix: - jtag mode: added spiOverJtag support; - spiOverJtag: added efinix support: verilog file and t8f81 bitstream - xyloni: part code for spiOverJtag flash access/load bridge --- spiOverJtag/efinix_spiOverJtag.v | 59 +++++ spiOverJtag/spiOverJtag_efinix_t8f81.bit.gz | Bin 0 -> 5526 bytes src/board.hpp | 2 +- src/efinix.cpp | 264 +++++++++++++++++--- src/efinix.hpp | 34 ++- src/main.cpp | 3 +- 6 files changed, 310 insertions(+), 52 deletions(-) create mode 100644 spiOverJtag/efinix_spiOverJtag.v create mode 100644 spiOverJtag/spiOverJtag_efinix_t8f81.bit.gz 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 0000000000000000000000000000000000000000..26a86e21a2e7f73954fefa5b9ecd7db1c50eab34 GIT binary patch literal 5526 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 CPRrx~ literal 0 HcmV?d00001 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);