diff --git a/src/gowin.cpp b/src/gowin.cpp index 823d231..c9b7461 100644 --- a/src/gowin.cpp +++ b/src/gowin.cpp @@ -79,6 +79,7 @@ Gowin::Gowin(Jtag *jtag, const string filename, const string &file_type, std::st Device::prog_type_t prg_type, bool external_flash, bool verify, int8_t verbose): Device(jtag, filename, file_type, verify, verbose), + SPIInterface(filename, verbose, 0, verify, false, false), _fs(NULL), _idcode(0), is_gw1n1(false), is_gw2a(false), is_gw1n4(false), is_gw5a(false), _external_flash(external_flash), _spi_sck(BSCAN_SPI_SCK), _spi_cs(BSCAN_SPI_CS), @@ -88,6 +89,9 @@ Gowin::Gowin(Jtag *jtag, const string filename, const string &file_type, std::st { detectFamily(); + _prev_wr_edge = _jtag->getWriteEdge(); + _prev_rd_edge = _jtag->getReadEdge(); + if (prg_type == Device::WR_FLASH) _mode = Device::FLASH_MODE; else @@ -150,6 +154,17 @@ Gowin::Gowin(Jtag *jtag, const string filename, const string &file_type, std::st printSuccess("DONE"); } } + + if (is_gw5a && _mode == Device::FLASH_MODE) { + _jtag->setClkFreq(2500000); + _jtag->set_state(Jtag::TEST_LOGIC_RESET); + if (_verbose) + displayReadReg("Before disable SPI mode", readStatusReg()); + disableCfg(); + send_command(0); // BYPASS ? + _jtag->set_state(Jtag::TEST_LOGIC_RESET); + gw5a_disable_spi(); + } } Gowin::~Gowin() @@ -283,20 +298,10 @@ void Gowin::programFlash() void Gowin::programExtFlash(unsigned int offset, bool unprotect_flash) { - _jtag->setClkFreq(10000000); + displayReadReg("after program flash", readStatusReg()); - if (!enableCfg()) - throw std::runtime_error("Error: fail to enable configuration"); - - eraseSRAM(); - send_command(XFER_DONE); - send_command(NOOP); - - if (!is_gw2a) { - send_command(0x3D); - } else { - disableCfg(); - send_command(NOOP); + if (!prepare_flash_access()) { + throw std::runtime_error("Error: fail to prepare flash access"); } SPIFlash spiFlash(this, unprotect_flash, @@ -307,17 +312,30 @@ void Gowin::programExtFlash(unsigned int offset, bool unprotect_flash) const uint8_t *data = _fs->getData(); int length = _fs->getLength(); - if (spiFlash.erase_and_prog(offset, data, length / 8) != 0) - throw std::runtime_error("Error: write to flash failed"); - if (_verify) - if (!spiFlash.verify(offset, data, length / 8, 256)) - throw std::runtime_error("Error: flash vefication failed"); - if (!is_gw2a) { - if (!disableCfg()) - throw std::runtime_error("Error: fail to disable configuration"); + char mess[256]; + bool ret = true; + + if (spiFlash.erase_and_prog(offset, data, length / 8) != 0) { + snprintf(mess, 256, "Error: write to flash failed"); + printError(mess); + ret = false; + } + if (ret && _verify) + if (!spiFlash.verify(offset, data, length / 8, 256)) { + snprintf(mess, 256, "Error: flash vefication failed"); + printError(mess); + ret = false; + } + + if (!post_flash_access()) { + snprintf(mess, 256, "Error: fail to disable flash access"); + printError(mess); + ret = false; } - reset(); + if (!ret) { + throw std::runtime_error(mess); + } } void Gowin::programSRAM() @@ -352,8 +370,6 @@ void Gowin::program(unsigned int offset, bool unprotect_flash) return; if (_mode == FLASH_MODE) { - if (is_gw5a) - throw std::runtime_error("Error: write to flash on GW5A is not yet supported"); if (_external_flash) programExtFlash(offset, unprotect_flash); else @@ -787,6 +803,9 @@ inline void Gowin::spi_gowin_write(const uint8_t *wr, uint8_t *rd, unsigned len) int Gowin::spi_put(uint8_t cmd, const uint8_t *tx, uint8_t *rx, uint32_t len) { + if (is_gw5a) + return spi_put_gw5a(cmd, tx, rx, len); + uint8_t jrx[len+1], jtx[len+1]; jtx[0] = cmd; if (tx) @@ -801,6 +820,18 @@ int Gowin::spi_put(uint8_t cmd, const uint8_t *tx, uint8_t *rx, uint32_t len) int Gowin::spi_put(const uint8_t *tx, uint8_t *rx, uint32_t len) { + if (is_gw5a) { + uint8_t jrx[len]; + int ret = spi_put_gw5a(tx[0], (len > 1) ? &tx[1] : NULL, + (rx) ? jrx : NULL, len - 1); + // FIXME: first byte is never read (but in most call it's not an issue + if (rx) { + rx[0] = 0; + memcpy(&rx[1], jrx, len - 1); + } + return ret; + } + if (is_gw2a) { uint8_t jtx[len]; uint8_t jrx[len]; @@ -858,6 +889,9 @@ int Gowin::spi_put(const uint8_t *tx, uint8_t *rx, uint32_t len) int Gowin::spi_wait(uint8_t cmd, uint8_t mask, uint8_t cond, uint32_t timeout, bool verbose) { + if (is_gw5a) + return spi_wait_gw5a(cmd, mask, cond, timeout, verbose); + uint8_t tmp; uint32_t count = 0; @@ -940,3 +974,203 @@ int Gowin::spi_wait(uint8_t cmd, uint8_t mask, uint8_t cond, return 0; } + +bool Gowin::dumpFlash(uint32_t base_addr, uint32_t len) +{ + bool ret = true; + /* enable SPI flash access */ + if (!prepare_flash_access()) + return false; + + try { + SPIFlash flash(this, false, _verbose); + ret = flash.dump(_filename, base_addr, len, 256); + } catch (std::exception &e) { + printError(e.what()); + ret = false; + } + + /* reload bitstream */ + return post_flash_access() && ret; +} + +bool Gowin::prepare_flash_access() +{ + _jtag->setClkFreq(10000000); + + if (!eraseSRAM()) { + printError("Error: fail to erase SRAM"); + return false; + } + + if (is_gw5a) { + if (!gw5a_enable_spi()) { + printError("Error: fail to switch GW5A to SPI mode"); + return false; + } + } else if (!is_gw2a) { + send_command(0x3D); + } + + return true; +} + +bool Gowin::post_flash_access() +{ + bool ret = true; + + if (is_gw5a) { + if (!gw5a_disable_spi()) { + printError("Error: fail to disable GW5A SPI mode"); + ret = false; + } + } else if (!is_gw2a) { + if (!disableCfg()) { + printError("Error: fail to disable configuration"); + ret = false; + } + } + + reset(); + + return ret; +} + +/* + * Specific implementation for Arora V GW5A FPGAs + */ + +/* interface mode is already configured to mimic SPI (mode 0). + * JTAG is LSB, SPI is MSB -> all byte must be reversed. + */ +int Gowin::spi_put_gw5a(const uint8_t cmd, const uint8_t *tx, uint8_t *rx, + uint32_t len) +{ + uint32_t kLen = len + (rx ? 1 : 0); // cppcheck/lint happy + uint32_t bit_len = len * 8 + (rx ? 3 : 0); // 3bits delay when read + uint8_t jtx[kLen], jrx[kLen]; + uint8_t _cmd = FsParser::reverseByte(cmd); // reverse cmd. + uint8_t curr_tdi = cmd & 0x01; + + if (tx != NULL) { // SPI: MSB, JTAG: LSB -> reverse Bytes + for (uint32_t i = 0; i < len; i++) + jtx[i] = FsParser::reverseByte(tx[i]); + curr_tdi = tx[len-1] & 0x01; + } else { + memset(jtx, curr_tdi, kLen); + } + + // set TMS/CS low by moving to a state where TMS == 0, + // first cmd bit is also sent here (ie before next TCK rise). + _jtag->set_state(Jtag::RUN_TEST_IDLE, _cmd & 0x01); + _cmd >>= 1; + + // complete with 7 remaining cmd bits + if (0 != _jtag->read_write(&_cmd, NULL, 7, 0)) + return -1; + + // write/read the sequence. Force set to 0 to manage state here + // (with jtag last bit is sent with tms rise) + if (0 != _jtag->read_write(jtx, (rx) ? jrx : NULL, bit_len, 0)) + return -1; + // set TMS/CS high by moving to a state where TMS == 1 + _jtag->set_state(Jtag::TEST_LOGIC_RESET, curr_tdi); + _jtag->toggleClk(5); // Required ? + _jtag->flushTMS(true); + if (rx) { // Reconstruct read sequence and drop first 3bits. + for (uint32_t i = 0; i < len; i++) + rx[i] = FsParser::reverseByte((jrx[i] >> 3) | + (((jrx[i+1]) & 0x07) << 5)); + } + + return 0; +} + +int Gowin::spi_wait_gw5a(uint8_t cmd, uint8_t mask, uint8_t cond, + uint32_t timeout, bool verbose) +{ + uint8_t tmp; + uint32_t count = 0; + + do { + // sent command and read flash answer + if (0 != spi_put_gw5a(cmd, NULL, &tmp, 1)) { + printError("Error: cant write/read status"); + return -1; + } + + count++; + if (count == timeout) { + printf("timeout: %x\n", tmp); + break; + } + if (verbose) { + printf("%x %x %x %u\n", tmp, mask, cond, count); + } + } while ((tmp & mask) != cond); + + return (count == timeout) ? -1 : 0; +} + +bool Gowin::gw5a_enable_spi() +{ + enableCfg(); + send_command(0x3F); + disableCfg(); + if (_verbose) + displayReadReg("toto", readStatusReg()); + + /* UG704 3.4.3 'ExtFlash Programming -> Program External Flash via JTAG-SPI' */ + send_command(NOOP); + _jtag->set_state(Jtag::RUN_TEST_IDLE); + _jtag->toggleClk(126*8); + _jtag->set_state(Jtag::RUN_TEST_IDLE); + send_command(0x16); + send_command(0x00); + _jtag->set_state(Jtag::RUN_TEST_IDLE); + _jtag->toggleClk(625*8); + _jtag->set_state(Jtag::TEST_LOGIC_RESET); + /* save current read/write edge cfg before switching to SPI mode0 + * (rising edge: read / falling edge: write) + */ + _prev_wr_edge = _jtag->getWriteEdge(); + _prev_rd_edge = _jtag->getReadEdge(); + _jtag->setWriteEdge(JtagInterface::FALLING_EDGE); + _jtag->setReadEdge(JtagInterface::RISING_EDGE); + + return true; +} + +bool Gowin::gw5a_disable_spi() +{ + /* reconfigure WR/RD edge and sent sequence to + * disable SPI mode + */ + _jtag->setWriteEdge(_prev_wr_edge); + _jtag->setReadEdge(_prev_rd_edge); + _jtag->flushTMS(true); + _jtag->flush(); + // 1. sent 15 TMS pulse + // TEST_LOGIC_RESET to SELECT_DR_SCAN: 01 + _jtag->set_state(Jtag::SELECT_DR_SCAN); + // SELECT_DR_SCAN to CAPTURE_DR: 0 + _jtag->set_state(Jtag::CAPTURE_DR); + // CAPTURE_DR to EXIT1_DR: 1 + _jtag->set_state(Jtag::EXIT1_DR); + // EXIT1_DR to EXIT2_DR: 01 + _jtag->set_state(Jtag::EXIT2_DR); + // Now we have 3 pulses + for (int i = 0; i < 6; i++) { // 2 each loop: 12 pulses + 3 before + _jtag->set_state(Jtag::PAUSE_DR); // 010 + _jtag->set_state(Jtag::EXIT2_DR); // 1 + } + _jtag->set_state(Jtag::EXIT1_DR); // 01 : 16 + _jtag->set_state(Jtag::PAUSE_DR); // 0 + + _jtag->flushTMS(true); + _jtag->flush(); + // 2. 8 TCK clock cycle with TMS=1 + _jtag->set_state(Jtag::TEST_LOGIC_RESET); // 5 cycles + _jtag->toggleClk(5); + return true; +} diff --git a/src/gowin.hpp b/src/gowin.hpp index 878f66f..94649f0 100644 --- a/src/gowin.hpp +++ b/src/gowin.hpp @@ -14,6 +14,7 @@ #include "configBitstreamParser.hpp" #include "device.hpp" #include "jtag.hpp" +#include "jtagInterface.hpp" #include "spiInterface.hpp" class Gowin: public Device, SPIInterface { @@ -32,15 +33,58 @@ class Gowin: public Device, SPIInterface { (void) len; printError("protect flash not supported"); return false;} bool unprotect_flash() override { + if (is_gw5a) + return SPIInterface::unprotect_flash(); printError("unprotect flash not supported"); return false;} bool bulk_erase_flash() override { + if (is_gw5a) + return SPIInterface::bulk_erase_flash(); printError("bulk erase flash not supported"); return false;} + bool dumpFlash(uint32_t base_addr, uint32_t len) override; int spi_put(uint8_t cmd, const uint8_t *tx, uint8_t *rx, uint32_t len) override; int spi_put(const 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) override; + /* ---------------- */ + /* Arora V specific */ + /* ---------------- */ + + /*! + * \brief Send cmd, followed by (optional) tx sequence and fill + * rx if not null + * \param[in] cmd: SPI command + * \param[in] rx: Byte sequence to write (may be null) + * \param[out] tx: Byte buffer when read is requested + * \param[in] len: number of Byte to read/write (0 when no read/write) + * \return 0 on success, -1 otherwise + */ + int spi_put_gw5a(const uint8_t cmd, const uint8_t *tx, uint8_t *rx, + uint32_t len); + /*! + * \brief poll on cmd register until timeout or SPI reg content match + * cond with mask + * \param[in] cmd: SPI command + * \param[in] mask: mask to apply on SPI rx + * \param[in] cond: SPI rx value value + * \param[in] timeout: number of try before fail + * \param[in] verbose: display try + * \return 0 on success, -1 otherwise + */ + int spi_wait_gw5a(uint8_t cmd, uint8_t mask, uint8_t cond, + uint32_t timeout, bool verbose); + + protected: + /*! + * \brief prepare SPI flash access + */ + bool prepare_flash_access() override; + /*! + * \brief end of SPI flash access + */ + bool post_flash_access() override; + private: bool detectFamily(); bool send_command(uint8_t cmd); @@ -65,6 +109,21 @@ class Gowin: public Device, SPIInterface { * .fs usercode field */ void checkCRC(); + + /* ---------------- */ + /* Arora V specific */ + /* ---------------- */ + /*! + * \brief Send the sequence to pass GW5A to SPI mode. + * \return true on success, false otherwise + */ + bool gw5a_disable_spi(); + /*! + * \brief Send the sequence to disable SPI mode for GW5A. + * \return true on success, false otherwise + */ + bool gw5a_enable_spi(); + ConfigBitstreamParser *_fs; uint32_t _idcode; bool is_gw1n1; @@ -79,5 +138,7 @@ class Gowin: public Device, SPIInterface { uint8_t _spi_do; /**< do signal (miso) offset in bscan SPI */ uint8_t _spi_msk; /** default spi msk with only do out */ ConfigBitstreamParser *_mcufw; + JtagInterface::tck_edge_t _prev_rd_edge; /**< default probe rd edge cfg */ + JtagInterface::tck_edge_t _prev_wr_edge; /**< default probe wr edge cfg */ }; #endif // SRC_GOWIN_HPP_