Skip to content

Commit

Permalink
Gowin: adding basic GW5A support (draft), prepare to move to spiInter…
Browse files Browse the repository at this point in the history
…face methods
  • Loading branch information
trabucayre committed Oct 29, 2023
1 parent f71858f commit f66f945
Show file tree
Hide file tree
Showing 2 changed files with 222 additions and 21 deletions.
223 changes: 202 additions & 21 deletions src/gowin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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,
Expand All @@ -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");
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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];
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;
}
20 changes: 20 additions & 0 deletions src/gowin.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "configBitstreamParser.hpp"
#include "device.hpp"
#include "jtag.hpp"
#include "jtagInterface.hpp"
#include "spiInterface.hpp"

class Gowin: public Device, SPIInterface {
Expand All @@ -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);
Expand Down Expand Up @@ -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_

0 comments on commit f66f945

Please sign in to comment.