add gowin external spi in bscan and --external-flash option

This commit is contained in:
Gwenhael Goavec-Merou 2021-09-15 20:18:49 +02:00
parent f41c1d4d60
commit 85b53b5918
6 changed files with 260 additions and 37 deletions

View File

@ -50,6 +50,8 @@ openFPGALoader -- a program to flash FPGA
--detect detect FPGA
--dfu DFU mode
--dump-flash Dump flash mode
--external-flash select ext flash for device with internal and
external storage
--file-size arg provides size in Byte to dump, must be used with
dump-flash
--file-type arg provides file type instead of let's deduced by

View File

@ -43,11 +43,11 @@ openFPGALoader -b arty -f bitstream.bit # Writing in flash (non-volatile)
| **orangeCrab** | [Orange Crab](https://github.com/gregdavill/OrangeCrab) | ECP5</br>LFE5U-25F-8MG285C | OK (JTAG) | OK (DFU) |
| **pipistrello** | [Saanlima Pipistrello LX45](http://pipistrello.saanlima.com/index.php?title=Welcome_to_Pipistrello) | Spartan6</br>xc6slx45-csg324 | OK | OK |
| **qmtechCycloneV** | [QMTech CycloneV Core Board](https://fr.aliexpress.com/i/1000006622149.html) | Cyclone V</br>5CEFA2F23I7 | OK | OK |
| **runber** | [SeeedStudio Gowin RUNBER](https://www.seeedstudio.com/Gowin-RUNBER-Development-Board-p-4779.html) | littleBee</br>GW1N-4 | OK | IF |
| **runber** | [SeeedStudio Gowin RUNBER](https://www.seeedstudio.com/Gowin-RUNBER-Development-Board-p-4779.html) | littleBee</br>GW1N-4 | OK | IF/EF |
| | [Scarab Hardware MiniSpartan6+](https://www.kickstarter.com/projects/1812459948/minispartan6-a-powerful-fpga-board-and-easy-to-use) | Spartan6</br>xc6slx25-3-ftg256 | OK | NT |
| **spartanEdgeAccelBoard** | [SeeedStudio Spartan Edge Accelerator Board](http://wiki.seeedstudio.com/Spartan-Edge-Accelerator-Board) | Spartan7</br>xc7s15ftgb196 | OK | NA |
| **tangnano** | [Sipeed Tang Nano](https://tangnano.sipeed.com/en/) | littleBee</br>GW1N-1 | OK | |
| **tangnano4k** | [Sipeed Tang Nano 4K](https://tangnano.sipeed.com/en/) | littleBee</br>GW1NSR-4C | OK | |
| **tangnano4k** | [Sipeed Tang Nano 4K](https://tangnano.sipeed.com/en/) | littleBee</br>GW1NSR-4C | OK | IF/EF |
| **tec0117** | [Trenz Gowin LittleBee (TEC0117)](https://shop.trenz-electronic.de/en/TEC0117-01-FPGA-Module-with-GOWIN-LittleBee-and-8-MByte-internal-SDRAM) | littleBee</br>GW1NR-9 | OK | IF |
| **xtrx** | [FairWaves XTRXPro](https://www.crowdsupply.com/fairwaves/xtrx) | Artix</br>xc7a50tcpg236 | OK | OK |
| **xyloni_spi** | [Efinix Xyloni](https://www.efinixinc.com/products-devkits-xyloni.html) | Trion</br>T8F81 | NA | AS |
@ -55,6 +55,7 @@ openFPGALoader -b arty -f bitstream.bit # Writing in flash (non-volatile)
| **zedboard** | [Avnet ZedBoard](https://www.avnet.com/wps/portal/us/products/avnet-boards/avnet-board-families/zedboard/) | zynq7000</br>xc7z020clg484 | OK | NA |
- *IF* Internal Flash
- *EF* External Flash
- *AS* Active Serial flash mode
- *NA* Not Available
- *NT* Not Tested

View File

@ -32,3 +32,6 @@ openFPGALoader -f -b BOARD_NAME impl/pnr/*.fs
where *BOARD_NAME* is:
- **tec0117**
- **runber**
It's possible to flash external SPI Flash (connected to MSPI) in bscan mode
by using `--external-flash` instead of `-f`

View File

@ -18,6 +18,8 @@
#include "progressBar.hpp"
#include "display.hpp"
#include "fsparser.hpp"
#include "rawParser.hpp"
#include "spiFlash.hpp"
using namespace std;
@ -57,44 +59,73 @@ using namespace std;
#define EF_PROGRAM 0x71
#define EFLASH_ERASE 0x75
/* BSCAN spi (external flash) (see below for details) */
/* most common pins def */
#define BSCAN_SPI_SCK (1 << 1)
#define BSCAN_SPI_CS (1 << 3)
#define BSCAN_SPI_DI (1 << 5)
#define BSCAN_SPI_DO (1 << 7)
#define BSCAN_SPI_MSK ((0x01 << 6))
/* GW1NSR-4C pins def */
#define BSCAN_GW1NSR_4C_SPI_SCK (1 << 7)
#define BSCAN_GW1NSR_4C_SPI_CS (1 << 5)
#define BSCAN_GW1NSR_4C_SPI_DI (1 << 3)
#define BSCAN_GW1NSR_4C_SPI_DO (1 << 1)
#define BSCAN_GW1NSR_4C_SPI_MSK ((0x01 << 0))
Gowin::Gowin(Jtag *jtag, const string filename, const string &file_type,
Device::prog_type_t prg_type,
Device::prog_type_t prg_type, bool external_flash,
bool verify, int8_t verbose): Device(jtag, filename, file_type,
verify, verbose), is_gw1n1(false)
verify, verbose), is_gw1n1(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)
{
_fs = NULL;
uint32_t idcode = _jtag->get_target_device_id();;
if (prg_type == Device::WR_FLASH)
_mode = Device::FLASH_MODE;
else
_mode = Device::MEM_MODE;
if (!_file_extension.empty()) {
if (_file_extension == "fs") {
if (prg_type == Device::WR_FLASH)
_mode = Device::FLASH_MODE;
else
_mode = Device::MEM_MODE;
try {
_fs = new FsParser(_filename, _mode == Device::MEM_MODE, _verbose);
} catch (std::exception &e) {
throw std::runtime_error(e.what());
}
printInfo("Parse file ", false);
if (_fs->parse() == EXIT_FAILURE) {
printError("FAIL");
delete _fs;
throw std::runtime_error("can't parse file");
} else {
printSuccess("DONE");
} else {
/* non fs file is only allowed with external flash */
if (!external_flash)
throw std::runtime_error("incompatible file format");
try {
_fs = new RawParser(_filename, false);
} catch (std::exception &e) {
throw std::runtime_error(e.what());
}
}
if (_verbose)
_fs->displayHeader();
printInfo("Parse file ", false);
if (_fs->parse() == EXIT_FAILURE) {
printError("FAIL");
delete _fs;
throw std::runtime_error("can't parse file");
} else {
printSuccess("DONE");
}
if (_verbose)
_fs->displayHeader();
/* for fs file check match with targeted device */
if (_file_extension == "fs") {
string idcode_str = _fs->getHeaderVal("idcode");
uint32_t fs_idcode = std::stoul(idcode_str.c_str(), NULL, 16);
if ((fs_idcode & 0x0fffffff) != idcode) {
throw std::runtime_error("mismatch between target's idcode and fs idcode");
}
} else {
throw std::runtime_error("incompatible file format");
}
}
_jtag->setClkFreq(2500000);
@ -102,6 +133,14 @@ Gowin::Gowin(Jtag *jtag, const string filename, const string &file_type,
/* erase and program flash differ for GW1N1 */
if (idcode == 0x0900281B)
is_gw1n1 = true;
/* bscan spi external flash differ for GW1NSR-4C */
if (idcode == 0x0100981b) {
_spi_sck = BSCAN_GW1NSR_4C_SPI_SCK;
_spi_cs = BSCAN_GW1NSR_4C_SPI_CS;
_spi_di = BSCAN_GW1NSR_4C_SPI_DI;
_spi_do = BSCAN_GW1NSR_4C_SPI_DO;
_spi_msk = BSCAN_GW1NSR_4C_SPI_MSK;
}
}
Gowin::~Gowin()
@ -155,11 +194,13 @@ void Gowin::programFlash()
/* check if file checksum == checksum in FPGA */
status = readUserCode();
if (_fs->checksum() != status) {
int checksum = static_cast<FsParser *>(_fs)->checksum();
if (checksum != status) {
printError("CRC check : FAIL");
printf("%04x %04x\n", _fs->checksum(), status);
} else
printf("%04x %04x\n", checksum, status);
} else {
printSuccess("CRC check: Success");
}
if (_verbose)
displayReadReg(readStatusReg());
@ -176,8 +217,39 @@ void Gowin::program(unsigned int offset)
if (_mode == NONE_MODE || !_fs)
return;
data = _fs->getData();
length = _fs->getLength();
if (_mode == FLASH_MODE) {
programFlash();
if (!_external_flash) { /* write into internal flash */
programFlash();
} else { /* write bitstream into external flash */
_jtag->setClkFreq(10000000);
if (!EnableCfg())
throw std::runtime_error("Error: fail to enable configuration");
eraseSRAM();
wr_rd(XFER_DONE, NULL, 0, NULL, 0);
wr_rd(NOOP, NULL, 0, NULL, 0);
wr_rd(0x3D, NULL, 0, NULL, 0);
SPIFlash spiFlash(this, (_verbose ? 1 : (_quiet ? -1 : 0)));
spiFlash.reset();
spiFlash.read_id();
spiFlash.read_status_reg();
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 (!DisableCfg())
throw std::runtime_error("Error: fail to disable configuration");
reset();
}
return;
}
@ -185,9 +257,6 @@ void Gowin::program(unsigned int offset)
displayReadReg(readStatusReg());
}
data = _fs->getData();
length = _fs->getLength();
wr_rd(READ_IDCODE, NULL, 0, NULL, 0);
/* erase SRAM */
@ -207,11 +276,13 @@ void Gowin::program(unsigned int offset)
/* check if file checksum == checksum in FPGA */
status = readUserCode();
if (_fs->checksum() != status) {
uint32_t checksum = static_cast<FsParser *>(_fs)->checksum();
if (checksum != status) {
printError("SRAM Flash: FAIL");
printf("%04x %04x\n", _fs->checksum(), status);
} else
printf("%04x %04x\n", checksum, status);
} else {
printSuccess("SRAM Flash: Success");
}
if (_verbose)
displayReadReg(readStatusReg());
}
@ -528,3 +599,130 @@ bool Gowin::eraseSRAM()
return false;
}
}
/* SPI wrapper
* extflash access may be done using specific mode or
* boundary scan. But former is only available with mode=[11]
* so use Bscan
*
* it's a bitbanging mode with:
* Pins Name of SPI Flash | SCLK | CS | DI | DO |
* Bscan Chain[7:0] | 7 6 | 5 4 | 3 2 | 1 0 |
* (ctrl & data) | 0 | 0 | 0 | 1 |
* ctrl 0 -> out, 1 -> in
* data 1 -> high, 0 -> low
* but all byte must be bit reversal...
*/
#define spi_gowin_write(_wr, _rd, _len) do { \
_jtag->shiftDR(_wr, _rd, _len); \
_jtag->toggleClk(6); } while (0)
int Gowin::spi_put(uint8_t cmd, uint8_t *tx, uint8_t *rx, uint32_t len)
{
uint8_t jrx[len+1], jtx[len+1];
jtx[0] = cmd;
if (tx)
memcpy(jtx+1, tx, len);
else
memset(jtx+1, 0, len);
int ret = spi_put(jtx, (rx)? jrx : NULL, len+1);
if (rx)
memcpy(rx, jrx+1, len);
return ret;
}
int Gowin::spi_put(uint8_t *tx, uint8_t *rx, uint32_t len)
{
/* set CS/SCK/DI low */
uint8_t t = _spi_msk | _spi_do;
t &= ~_spi_cs;
spi_gowin_write(&t, NULL, 8);
_jtag->flush();
/* send bit/bit full tx content (or set di to 0 when NULL) */
for (uint32_t i = 0; i < len * 8; i++) {
uint8_t r;
t = _spi_msk | _spi_do;
if (tx != NULL && tx[i>>3] & (1 << (7-(i&0x07))))
t |= _spi_di;
spi_gowin_write(&t, NULL, 8);
t |= _spi_sck;
spi_gowin_write(&t, (rx) ? &r : NULL, 8);
_jtag->flush();
/* if read reconstruct bytes */
if (rx) {
if (r & _spi_do)
rx[i >> 3] |= 1 << (7-(i & 0x07));
else
rx[i >> 3] &= ~(1 << (7-(i & 0x07)));
}
}
/* set CS and unset SCK (next xfer) */
t &= ~_spi_sck;
t |= _spi_cs;
spi_gowin_write(&t, NULL, 8);
_jtag->flush();
return 0;
}
int Gowin::spi_wait(uint8_t cmd, uint8_t mask, uint8_t cond,
uint32_t timeout, bool verbose)
{
uint32_t count = 0;
uint8_t rx, t;
/* set CS/SCK/DI low */
t = _spi_msk | _spi_do;
spi_gowin_write(&t, NULL, 8);
/* send command bit/bit */
for (int i = 0; i < 8; i++) {
t = _spi_msk | _spi_do;
if ((cmd & (1 << (7-i))) != 0)
t |= _spi_di;
spi_gowin_write(&t, NULL, 8);
t |= _spi_sck;
spi_gowin_write(&t, NULL, 8);
_jtag->flush();
}
t = _spi_msk | _spi_do;
do {
rx = 0;
/* read status register bit/bit with di == 0 */
for (int i = 0; i < 8; i++) {
uint8_t r;
t &= ~_spi_sck;
spi_gowin_write(&t, NULL, 8);
t |= _spi_sck;
spi_gowin_write(&t, &r, 8);
_jtag->flush();
if ((r & _spi_do) != 0)
rx |= 1 << (7-i);
}
count++;
if (count == timeout) {
printf("timeout: %x\n", rx);
break;
}
if (verbose)
printf("%x %x %x %u\n", rx, mask, cond, count);
} while ((rx & mask) != cond);
/* set CS & unset SCK (next xfer) */
t &= ~_spi_sck;
t |= _spi_cs;
spi_gowin_write(&t, NULL, 8);
_jtag->flush();
if (count == timeout) {
printf("%02x\n", rx);
std::cout << "wait: Error" << std::endl;
return -ETIME;
}
return 0;
}

View File

@ -12,14 +12,14 @@
#include <vector>
#include "device.hpp"
#include "fsparser.hpp"
#include "configBitstreamParser.hpp"
#include "jtag.hpp"
#include "jedParser.hpp"
#include "spiInterface.hpp"
class Gowin: public Device {
class Gowin: public Device, SPIInterface {
public:
Gowin(Jtag *jtag, std::string filename, const std::string &file_type,
Device::prog_type_t prg_type,
Device::prog_type_t prg_type, bool external_flash,
bool verify, int8_t verbose);
~Gowin();
int idCode() override;
@ -27,6 +27,13 @@ class Gowin: public Device {
void program(unsigned int offset) override;
void programFlash();
/* spi interface */
int spi_put(uint8_t cmd, uint8_t *tx, uint8_t *rx,
uint32_t len) override;
int spi_put(uint8_t *tx, uint8_t *rx, uint32_t len) override;
int spi_wait(uint8_t cmd, uint8_t mask, uint8_t cond,
uint32_t timeout, bool verbose) override;
private:
bool wr_rd(uint8_t cmd, uint8_t *tx, int tx_len,
uint8_t *rx, int rx_len, bool verbose = false);
@ -40,7 +47,13 @@ class Gowin: public Device {
void displayReadReg(uint32_t dev);
uint32_t readStatusReg();
uint32_t readUserCode();
FsParser *_fs;
ConfigBitstreamParser *_fs;
bool is_gw1n1;
bool _external_flash; /**< select between int or ext flash */
uint8_t _spi_sck; /**< clk signal offset in bscan SPI */
uint8_t _spi_cs; /**< cs signal offset in bscan SPI */
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 */
};
#endif // GOWIN_HPP_

View File

@ -58,6 +58,7 @@ struct arguments {
string probe_firmware;
int index_chain;
unsigned int file_size;
bool external_flash;
};
int parse_opt(int argc, char **argv, struct arguments *args, jtag_pins_conf_t *pins_config);
@ -73,7 +74,7 @@ int main(int argc, char **argv)
/* command line args. */
struct arguments args = {0, false, false, false, 0, "", "", "-", "", -1,
0, "-", false, false, false, false, Device::WR_SRAM, false,
false, false, "", "", "", -1, 0};
false, false, "", "", "", -1, 0, false};
/* parse arguments */
try {
if (parse_opt(argc, argv, &args, &pins_config))
@ -397,7 +398,7 @@ int main(int argc, char **argv)
args.prg_type, args.verify, args.verbose);
} else if (fab == "Gowin") {
fpga = new Gowin(jtag, args.bit_file, args.file_type,
args.prg_type, args.verify, args.verbose);
args.prg_type, args.external_flash, args.verify, args.verbose);
} else if (fab == "lattice") {
fpga = new Lattice(jtag, args.bit_file, args.file_type,
args.prg_type, args.verify, args.verbose);
@ -499,6 +500,9 @@ int parse_opt(int argc, char **argv, struct arguments *args, jtag_pins_conf_t *p
cxxopts::value<bool>(args->detect))
("dfu", "DFU mode", cxxopts::value<bool>(args->dfu))
("dump-flash", "Dump flash mode")
("external-flash",
"select ext flash for device with internal and external storage",
cxxopts::value<bool>(args->external_flash))
("file-size", "provides size in Byte to dump, must be used with dump-flash",
cxxopts::value<unsigned int>(args->file_size))
("file-type", "provides file type instead of let's deduced by using extension",
@ -569,6 +573,8 @@ int parse_opt(int argc, char **argv, struct arguments *args, jtag_pins_conf_t *p
args->prg_type = Device::WR_SRAM;
else if (result.count("dump-flash"))
args->prg_type = Device::RD_FLASH;
else if (result.count("external-flash"))
args->prg_type = Device::WR_FLASH;
if (result.count("freq")) {
double freq;