diff --git a/doc/vendors/gowin.rst b/doc/vendors/gowin.rst index 63f4ac18ce..103c3e38c2 100644 --- a/doc/vendors/gowin.rst +++ b/doc/vendors/gowin.rst @@ -62,3 +62,17 @@ It's possible to flash external SPI Flash (connected to MSPI) in bscan mode by u Gowin's FPGA may fails to be detected if **JTAGSEL_N** (pin 08 for *GW1N-4K*) is used as a GPIO. To recover you have to pull down this pin (before power up) to recover JTAG interface (*UG292 - JTAGSELL_N section*). + +User Flash +---------- + +.. ATTENTION:: + User Flash support is based on reverse engineering of the JTAG protocol. This functionality should be considered + experimental as it hasn't been thoroughly tested, and may in some circumstances destroy your device. + +Gowin FPGA come with extra flash space that can be read and written from the programmable logic ("User Flash"). This +flash section can also be programmed via the JTAG interface: + +.. code-block:: bash + + openFPGALoader --write-flash /path/to/bitstream.fs --user-flash /path/to/flash.bin diff --git a/src/gowin.cpp b/src/gowin.cpp index fef77240e5..2e8029d3fb 100644 --- a/src/gowin.cpp +++ b/src/gowin.cpp @@ -77,15 +77,15 @@ using namespace std; Gowin::Gowin(Jtag *jtag, const string filename, const string &file_type, std::string mcufw, Device::prog_type_t prg_type, bool external_flash, - bool verify, int8_t verbose): Device(jtag, filename, file_type, - verify, verbose), + bool verify, int8_t verbose, const std::string& user_flash) + : 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), + _idcode(0), is_gw1n1(false), is_gw1n4(false), is_gw1n9(false), + is_gw2a(false), is_gw5a(false), + _external_flash(external_flash), _spi_sck(BSCAN_SPI_SCK), _spi_cs(BSCAN_SPI_CS), _spi_di(BSCAN_SPI_DI), _spi_do(BSCAN_SPI_DO), - _spi_msk(BSCAN_SPI_MSK), - _mcufw(NULL) + _spi_msk(BSCAN_SPI_MSK) { detectFamily(); @@ -100,7 +100,7 @@ Gowin::Gowin(Jtag *jtag, const string filename, const string &file_type, std::st if (!_file_extension.empty() && prg_type != Device::RD_FLASH) { if (_file_extension == "fs") { try { - _fs = new FsParser(_filename, _mode == Device::MEM_MODE, _verbose); + _fs = std::unique_ptr(new FsParser(_filename, _mode == Device::MEM_MODE, _verbose)); } catch (std::exception &e) { throw std::runtime_error(e.what()); } @@ -109,7 +109,7 @@ Gowin::Gowin(Jtag *jtag, const string filename, const string &file_type, std::st if (!_external_flash) throw std::runtime_error("incompatible file format"); try { - _fs = new RawParser(_filename, false); + _fs = std::unique_ptr(new RawParser(_filename, false)); } catch (std::exception &e) { throw std::runtime_error(e.what()); } @@ -118,7 +118,6 @@ Gowin::Gowin(Jtag *jtag, const string filename, const string &file_type, std::st printInfo("Parse file ", false); if (_fs->parse() == EXIT_FAILURE) { printError("FAIL"); - delete _fs; throw std::runtime_error("can't parse file"); } else { printSuccess("DONE"); @@ -144,11 +143,26 @@ Gowin::Gowin(Jtag *jtag, const string filename, const string &file_type, std::st if (_idcode != 0x0100981b) throw std::runtime_error("Microcontroller firmware flashing only supported on GW1NSR-4C"); - _mcufw = new RawParser(mcufw, false); + _mcufw = std::unique_ptr(new RawParser(mcufw, false)); if (_mcufw->parse() == EXIT_FAILURE) { printError("FAIL"); - delete _mcufw; + throw std::runtime_error("can't parse file"); + } else { + printSuccess("DONE"); + } + } + + if (user_flash.size() > 0) { + if (!is_gw1n9) + throw std::runtime_error("Unsupported FPGA model (only GW1N(R)-9(C) is supported at the moment)"); + if (mcufw.size() > 0) + throw std::runtime_error("Microcontroller firmware and user flash can't be specified simultaneously"); + + _userflash = std::unique_ptr(new RawParser(user_flash, false)); + + if (_userflash->parse() == EXIT_FAILURE) { + printError("FAIL"); throw std::runtime_error("can't parse file"); } else { printSuccess("DONE"); @@ -167,25 +181,10 @@ Gowin::Gowin(Jtag *jtag, const string filename, const string &file_type, std::st } } -Gowin::~Gowin() -{ - if (_fs) - delete _fs; - if (_mcufw) - delete _mcufw; -} - bool Gowin::detectFamily() { _idcode = _jtag->get_target_device_id(); - /* erase and program flash differ for GW1N1 */ - if (_idcode == 0x0900281B) - is_gw1n1 = true; - /* erase and program flash differ for GW1N4, GW1N1Z-1 */ - if (_idcode == 0x0100381B || _idcode == 0x100681b) - is_gw1n4 = true; - /* bscan spi external flash differ for GW1NSR-4C */ if (_idcode == 0x0100981b) { _spi_sck = BSCAN_GW1NSR_4C_SPI_SCK; @@ -200,6 +199,16 @@ bool Gowin::detectFamily() * algorithm that is not yet supported. */ switch (_idcode) { + case 0x0900281B: /* GW1N-1 */ + is_gw1n1 = true; + break; + case 0x0100381B: /* GW1N-4B */ + case 0x0100681b: /* GW1NZ-1 */ + is_gw1n4 = true; + break; + case 0x0100481B: /* GW1N(R)-9, although documentation says otherwise */ + is_gw1n9 = true; + break; case 0x0000081b: /* GW2A(R)-18(C) */ case 0x0000281b: /* GW2A(R)-55(C) */ _external_flash = true; @@ -300,6 +309,13 @@ void Gowin::programFlash() return; } + if (_userflash) { + const uint8_t *userflash_data = _userflash->getData(); + int userflash_length = _userflash->getLength(); + if (!writeFLASH(0x6D0, userflash_data, userflash_length, true)) + return; + } + if (_verify) printWarn("writing verification not supported"); @@ -415,7 +431,7 @@ void Gowin::program(unsigned int offset, bool unprotect_flash) void Gowin::checkCRC() { uint32_t ucode = readUserCode(); - uint16_t checksum = static_cast(_fs)->checksum(); + uint16_t checksum = static_cast(_fs.get())->checksum(); if (static_cast(0xffff & ucode) == checksum) goto success; /* no match: @@ -574,7 +590,7 @@ inline uint32_t bswap_32(uint32_t x) } /* TN653 p. 17-21 */ -bool Gowin::writeFLASH(uint32_t page, const uint8_t *data, int length) +bool Gowin::writeFLASH(uint32_t page, const uint8_t *data, int length, bool invert_bits) { #if 1 @@ -659,6 +675,13 @@ bool Gowin::writeFLASH(uint32_t page, const uint8_t *data, int length) else tx[x] = t[x]; } + + if (invert_bits) { + for (int x = 0; x < 4; x++) { + tx[x] ^= 0xFF; + } + } + _jtag->shiftDR(tx, NULL, 32); if (!is_gw1n1) @@ -788,7 +811,7 @@ bool Gowin::writeSRAM(const uint8_t *data, int length) } progress.done(); send_command(0x0a); - uint32_t checksum = static_cast(_fs)->checksum(); + uint32_t checksum = static_cast(_fs.get())->checksum(); checksum = htole32(checksum); _jtag->shiftDR((uint8_t *)&checksum, NULL, 32); send_command(0x08); diff --git a/src/gowin.hpp b/src/gowin.hpp index 6abb382099..6f49521439 100644 --- a/src/gowin.hpp +++ b/src/gowin.hpp @@ -10,6 +10,7 @@ #include #include #include +#include #include "configBitstreamParser.hpp" #include "device.hpp" @@ -21,8 +22,8 @@ class Gowin: public Device, SPIInterface { public: Gowin(Jtag *jtag, std::string filename, const std::string &file_type, std::string mcufw, Device::prog_type_t prg_type, - bool external_flash, bool verify, int8_t verbose); - ~Gowin(); + bool external_flash, bool verify, int8_t verbose, + const std::string& user_flash); uint32_t idCode() override; void reset() override; void program(unsigned int offset, bool unprotect_flash) override; @@ -104,7 +105,7 @@ class Gowin: public Device, SPIInterface { void programExtFlash(unsigned int offset, bool unprotect_flash); void programSRAM(); bool writeSRAM(const uint8_t *data, int length); - bool writeFLASH(uint32_t page, const uint8_t *data, int length); + bool writeFLASH(uint32_t page, const uint8_t *data, int length, bool invert_bits = false); void displayReadReg(const char *, uint32_t dev); uint32_t readStatusReg(); uint32_t readUserCode(); @@ -128,11 +129,14 @@ class Gowin: public Device, SPIInterface { */ bool gw5a_enable_spi(); - ConfigBitstreamParser *_fs; + std::unique_ptr _fs; + std::unique_ptr _mcufw; + std::unique_ptr _userflash; uint32_t _idcode; bool is_gw1n1; - bool is_gw2a; bool is_gw1n4; + bool is_gw1n9; + bool is_gw2a; bool is_gw5a; bool skip_checksum; /**< bypass checksum verification (GW2A) */ bool _external_flash; /**< select between int or ext flash */ @@ -141,7 +145,6 @@ class Gowin: public Device, SPIInterface { uint8_t _spi_di; /**< di signal (mosi) offset in bscan SPI */ 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 */ }; diff --git a/src/main.cpp b/src/main.cpp index 9c1b138a6c..bb174455c8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -97,6 +97,7 @@ struct arguments { bool read_dna; bool read_xadc; string read_register; + string user_flash; }; int run_xvc_server(const struct arguments &args, const cable_t &cable, @@ -582,7 +583,7 @@ int main(int argc, char **argv) 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); + args.prg_type, args.external_flash, args.verify, args.verbose, args.user_flash); } else if (fab == "lattice") { fpga = new Lattice(jtag, args.bit_file, args.file_type, args.prg_type, args.flash_sector, args.verify, args.verbose, args.skip_load_bridge, args.skip_reset); @@ -871,6 +872,8 @@ int parse_opt(int argc, char **argv, struct arguments *args, cxxopts::value(args->read_xadc)) ("read-register", "Read Status Register(Xilinx FPGA only)", cxxopts::value(rd_reg)) + ("user-flash", "User flash file (Gowin LittleBee FPGA only)", + cxxopts::value(args->user_flash)) ("V,Version", "Print program version"); options.parse_positional({"bitstream"});