Merge pull request #499 from lambdaconcept/gowin-gw1n9-userflash

gowin: Implement User Flash programming for GW1N9
This commit is contained in:
Gwenhael Goavec-Merou 2024-12-13 06:50:38 +01:00 committed by GitHub
commit cdc4d32e32
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 79 additions and 36 deletions

14
doc/vendors/gowin.rst vendored
View File

@ -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

View File

@ -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<ConfigBitstreamParser>(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<ConfigBitstreamParser>(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<ConfigBitstreamParser>(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<ConfigBitstreamParser>(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<FsParser *>(_fs)->checksum();
uint16_t checksum = static_cast<FsParser *>(_fs.get())->checksum();
if (static_cast<uint16_t>(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<FsParser *>(_fs)->checksum();
uint32_t checksum = static_cast<FsParser *>(_fs.get())->checksum();
checksum = htole32(checksum);
_jtag->shiftDR((uint8_t *)&checksum, NULL, 32);
send_command(0x08);

View File

@ -10,6 +10,7 @@
#include <iostream>
#include <string>
#include <vector>
#include <memory>
#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<ConfigBitstreamParser> _fs;
std::unique_ptr<ConfigBitstreamParser> _mcufw;
std::unique_ptr<ConfigBitstreamParser> _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 */
};

View File

@ -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<bool>(args->read_xadc))
("read-register", "Read Status Register(Xilinx FPGA only)",
cxxopts::value<string>(rd_reg))
("user-flash", "User flash file (Gowin LittleBee FPGA only)",
cxxopts::value<string>(args->user_flash))
("V,Version", "Print program version");
options.parse_positional({"bitstream"});