Skip to content

Commit

Permalink
Merge pull request #499 from lambdaconcept/gowin-gw1n9-userflash
Browse files Browse the repository at this point in the history
gowin: Implement User Flash programming for GW1N9
  • Loading branch information
trabucayre authored Dec 13, 2024
2 parents 8f88d22 + 46ff6cb commit cdc4d32
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 36 deletions.
14 changes: 14 additions & 0 deletions doc/vendors/gowin.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
81 changes: 52 additions & 29 deletions src/gowin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand All @@ -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());
}
Expand All @@ -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());
}
Expand All @@ -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");
Expand All @@ -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");
Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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");

Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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);
Expand Down
15 changes: 9 additions & 6 deletions src/gowin.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <iostream>
#include <string>
#include <vector>
#include <memory>

#include "configBitstreamParser.hpp"
#include "device.hpp"
Expand All @@ -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;
Expand Down Expand Up @@ -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();
Expand All @@ -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 */
Expand All @@ -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 */
};
Expand Down
5 changes: 4 additions & 1 deletion src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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"});
Expand Down

0 comments on commit cdc4d32

Please sign in to comment.