diff --git a/src/gowin.cpp b/src/gowin.cpp index 778aa7768c..81cfa4815d 100644 --- a/src/gowin.cpp +++ b/src/gowin.cpp @@ -94,7 +94,7 @@ Gowin::Gowin(Jtag *jtag, const string filename, const string &file_type, std::st else _mode = Device::MEM_MODE; - if (!_file_extension.empty()) { + if (!_file_extension.empty() && prg_type != Device::RD_FLASH) { if (_file_extension == "fs") { try { _fs = new FsParser(_filename, _mode == Device::MEM_MODE, _verbose); @@ -279,18 +279,18 @@ void Gowin::programExtFlash(unsigned int offset, bool unprotect_flash) { _jtag->setClkFreq(10000000); - 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); + if (is_gw5a) { + if (!prepare_flash_access()) { + throw std::runtime_error("Error: fail to switch GW5A to SPI mode"); + return; + } } else { - disableCfg(); - send_command(NOOP); + if (!eraseSRAM()) + throw std::runtime_error("Error: fail to erase SRAM"); + + if (!is_gw2a) { + send_command(0x3D); + } } SPIFlash spiFlash(this, unprotect_flash, @@ -301,12 +301,17 @@ 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 (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_gw5a) { + if (!post_flash_access()) + throw std::runtime_error("Error: fail to disable SPI mode"); + + } else if (!is_gw2a) { if (!disableCfg()) throw std::runtime_error("Error: fail to disable configuration"); } @@ -346,8 +351,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 @@ -728,6 +731,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) @@ -736,12 +742,25 @@ int Gowin::spi_put(uint8_t cmd, const uint8_t *tx, uint8_t *rx, uint32_t len) memset(jtx+1, 0, len); int ret = spi_put(jtx, (rx)? jrx : NULL, len+1); if (rx) - memcpy(rx, jrx+1, len); + memcpy(rx, jrx + 1, len); return ret; } 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], &tx[1], jrx, len); + /* 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]; @@ -796,9 +815,58 @@ int Gowin::spi_put(const uint8_t *tx, uint8_t *rx, uint32_t len) return 0; } +/* + * Specific implementation for Arora V GW5A FPGAs + * interface mode is already configured to mimic SPI (mode 0) + * it possible to also configure to MSB but not all interfaces + * can do that. + */ +int Gowin::spi_put_gw5a(const uint8_t cmd, const uint8_t *tx, uint8_t *rx, uint32_t len) +{ + uint32_t kLen = len; // cppcheck/lint happy + uint8_t jtx[kLen]; + uint8_t jrx[kLen]; + uint8_t _cmd[2]; + int ret = 0; + + _cmd[0] = FsParser::reverseByte(cmd); // extract first byte. + + if (tx != NULL) { + // SPI: MSB, JTAG: LSB -> reverse Bytes + for (uint32_t i = 0; i < kLen; i++) + jtx[i] = FsParser::reverseByte(tx[i + 1]); + } else { + memset(jtx, 0, kLen); + } + // set TMS/CS low by moving to a state where loop is + // with TMS == 0, first cmd bit is also sent here (ie + // before first writeTDI clk rise + _jtag->set_state(Jtag::RUN_TEST_IDLE, _cmd[0] & 0x01); + _cmd[0] >>= 1; + // complete with 7 remaining cmd bits + when read adds 3 extra + // clk cycles (interface GW5A delay ?) + if (0 != _jtag->read_write(_cmd, NULL, 7 + (rx ? 3 : 0), 0)) + return -1; + // write/read the sequence. Force set to 0 to manage state here + if (0 != _jtag->read_write(jtx, (rx) ? jrx : NULL, 8 * kLen, 0)) + return -1; + // set TMS/CS high by moving to a state where loop is + // with TMS == 1 + _jtag->set_state(Jtag::TEST_LOGIC_RESET); + _jtag->flushTMS(true); + if (rx) { + for (uint32_t i=0; i < kLen; i++) + rx[i] = FsParser::reverseByte(jrx[i]); + } + return 0; +} + 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; @@ -881,3 +949,116 @@ int Gowin::spi_wait(uint8_t cmd, uint8_t mask, uint8_t cond, return 0; } + +int Gowin::spi_wait_gw5a(uint8_t cmd, uint8_t mask, uint8_t cond, + uint32_t timeout, bool verbose) +{ + uint8_t tmp; + int ret; + uint32_t count = 0; + + do { + // sent command and read flash answer + if (0 != spi_put_gw5a(cmd, NULL, &tmp, 1)) + 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); + + /* set TMS/CS high by moving to a state where loop is + * with TMS == 1 + */ + _jtag->set_state(Jtag::TEST_LOGIC_RESET); + + return (count == timeout) ? -1 : 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, 0); + } catch (std::exception &e) { + printError(e.what()); + ret = false; + } + + /* reload bitstream */ + return post_flash_access() && ret; +} + +bool Gowin::prepare_flash_access() +{ + if (!is_gw5a) + return false; + + /*if (!EnableCfg()) { + printError("Error: fail to enable configuration"); + return false; + }*/ + + eraseSRAM(); + //wr_rd(XFER_DONE, NULL, 0, NULL, 0); + //wr_rd(NOOP, NULL, 0, NULL, 0); + + /* UG704 3.4.3 'ExtFlash Programming -> Program External Flash via JTAG-SPI' */ + //DisableCfg(); + //wr_rd(NOOP, NULL, 0, NULL, 0); + send_command(0x16); + send_command(0x00); + _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::post_flash_access() +{ + if (!is_gw5a) + return false; + + /* reconfigure WR/RD edge and sent sequence to + * disable SPI mode + */ + _jtag->setWriteEdge(_prev_wr_edge); + _jtag->setReadEdge(_prev_rd_edge); + //_jtag->setEndianness(JtagInterface::ENDIAN_LSB); + _jtag->flushTMS(true); + _jtag->flush(); + // 1. sent 15 TMS pulse + // TEST_LOGIC_RESET to EXIT2_DR: 010101 6 pulses + _jtag->set_state(Jtag::CAPTURE_DR); + // EXIT2_DR to PAUSE_DR: 010 3 pulses + _jtag->set_state(Jtag::PAUSE_DR); + // PAUSE_DR to EXIT1_DR: 101 3 pulses + _jtag->set_state(Jtag::EXIT1_DR); + // EXIT1_DR to SHIFT_DR: 010 3 pulses + _jtag->set_state(Jtag::SHIFT_DR); + // SHIFT_DR to EXIT2_DR: 101 3 pulses + _jtag->set_state(Jtag::EXIT2_DR); + _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 4bfa7392d7..bb9b634248 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 { @@ -35,11 +36,28 @@ class Gowin: public Device, SPIInterface { printError("unprotect flash not supported"); return false;} bool bulk_erase_flash() override { printError("bulk erase flash not supported"); return false;} + virtual 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; + /* Specific implementation for Arora V GW5A FPGAs. */ + int spi_put_gw5a(const uint8_t cmd, const uint8_t *tx, uint8_t *rx, + uint32_t len); int spi_wait(uint8_t cmd, uint8_t mask, uint8_t cond, uint32_t timeout, bool verbose) override; + /* Specific implementation for Arora V GW5A FPGAs. */ + int spi_wait_gw5a(uint8_t cmd, uint8_t mask, uint8_t cond, + uint32_t timeout, bool verbose); + + protected: + /*! + * \brief prepare SPI flash access + */ + virtual bool prepare_flash_access() override; + /*! + * \brief end of SPI flash access + */ + virtual bool post_flash_access() override; private: bool send_command(uint8_t cmd); @@ -77,5 +95,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_