Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gowin: Implement User Flash programming for GW1N9 #499

Merged
merged 4 commits into from
Dec 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading