diff --git a/doc/board-compatibility-list.md b/doc/board-compatibility-list.md index a73ef63..5c8405a 100644 --- a/doc/board-compatibility-list.md +++ b/doc/board-compatibility-list.md @@ -50,6 +50,7 @@ openFPGALoader -b arty -f bitstream.bit # Writing in flash (non-volatile) | **tec0117** | [Trenz Gowin LittleBee (TEC0117)](https://shop.trenz-electronic.de/en/TEC0117-01-FPGA-Module-with-GOWIN-LittleBee-and-8-MByte-internal-SDRAM) | littleBee
GW1NR-9 | OK | IF | | **xtrx** | [FairWaves XTRXPro](https://www.crowdsupply.com/fairwaves/xtrx) | Artix
xc7a50tcpg236 | OK | OK | | **xyloni_spi** | [Efinix Xyloni](https://www.efinixinc.com/products-devkits-xyloni.html) | Trion
T8F81 | NA | AS | +| **xmf3** | [PLDkit XMF3](https://pldkit.com/xilinx/xmf3) | Xilinx
xc3s200ft256, xcf01s | OK | OK | | **zedboard** | [Avnet ZedBoard](https://www.avnet.com/wps/portal/us/products/avnet-boards/avnet-board-families/zedboard/) | zynq7000
xc7z020clg484 | OK | NA | - *IF* Internal Flash diff --git a/doc/fpga-compatibility-list.md b/doc/fpga-compatibility-list.md index 8c231e7..29b3a80 100644 --- a/doc/fpga-compatibility-list.md +++ b/doc/fpga-compatibility-list.md @@ -17,9 +17,11 @@ | | [MachXO3LF](http://www.latticesemi.com/en/Products/FPGAandCPLD/MachXO3.aspx) | OK | OK | | Xilinx | Artix 7 [xc7a35ti, xc7a50t, xc7a75t, xc7a100t, xc7a200t](https://www.xilinx.com/products/silicon-devices/fpga/artix-7.html) | OK | OK | | | Kintex 7 [xc7k325t](https://www.xilinx.com/products/silicon-devices/fpga/kintex-7.html#productTable) | OK | NT | +| | Spartan 3 [xc3s200](https://www.xilinx.com/products/silicon-devices/fpga/spartan-3.html) | OK | NA | | | Spartan 6 [xc6slx9, xc6slx16, xc6slx25, xc6slx45](https://www.xilinx.com/products/silicon-devices/fpga/spartan-6.html) | OK | OK | | | Spartan 7 [xc7s15, xc7s25, xc7s50](https://www.xilinx.com/products/silicon-devices/fpga/spartan-7.html) | OK | OK | | | XC9500XL [xc9536xl, xc9572xl, xc95144xl, xc95188xl](https://www.xilinx.com/support/documentation/data_sheets/ds054.pdf) | NA | OK | +| | XCF [xcf01s, xcf02s, xcf04s](https://www.xilinx.com/products/silicon-devices/configuration-memory/platform-flash.html) | NA | OK | - *IF* Internal Flash - *AS* Active Serial flash mode diff --git a/doc/xilinx.md b/doc/xilinx.md index 5849e93..13d41a0 100644 --- a/doc/xilinx.md +++ b/doc/xilinx.md @@ -10,6 +10,8 @@ current directory. 3. board provides the device/package model, but if the targeted board is not officially supported but the FPGA yes, you can use --fpga-part to provides model +4. with spartan3 the flash is an independent JTAG device. User has to use + `--index-chain` to access FPGA (RAM only) or flash (write/read only) **Warning** *.bin* may be loaded in memory or in flash, but this extension is a classic extension for CPU firmware and, by default, *openFPGALoader* load file in memory, double check diff --git a/src/part.hpp b/src/part.hpp index 55e6076..bb6dbe8 100644 --- a/src/part.hpp +++ b/src/part.hpp @@ -28,6 +28,8 @@ static std::map fpga_list = { {0x03651093, {"xilinx", "kintex7", "xc7k325t", 6}}, + {0x01414093, {"xilinx", "spartan3", "xc3s200", 6}}, + {0x04001093, {"xilinx", "spartan6", "xc6slx9", 6}}, {0x04002093, {"xilinx", "spartan6", "xc6slx16", 6}}, {0x04004093, {"xilinx", "spartan6", "xc6slx25", 6}}, @@ -42,6 +44,10 @@ static std::map fpga_list = { {0x09608093, {"xilinx", "xc9500xl", "xc95144xl", 8}}, {0x09616093, {"xilinx", "xc9500xl", "xc95188xl", 8}}, + {0x05044093, {"xilinx", "xcf", "xcf01s", 8}}, + {0x05045093, {"xilinx", "xcf", "xcf02s", 8}}, + {0x05046093, {"xilinx", "xcf", "xcf04s", 8}}, + {0x03727093, {"xilinx", "zynq", "xc7z020", 6}}, {0x020f20dd, {"altera", "cyclone III", "EP3C16", 10}}, diff --git a/src/xilinx.cpp b/src/xilinx.cpp index 321668f..52bb98d 100644 --- a/src/xilinx.cpp +++ b/src/xilinx.cpp @@ -57,6 +57,16 @@ Xilinx::Xilinx(Jtag *jtag, const std::string &filename, _fpga_family = ZYNQ_FAMILY; } else if (family == "kintex7") { _fpga_family = KINTEX_FAMILY; + } else if (family == "spartan3") { + _fpga_family = SPARTAN3_FAMILY; + if (_mode != Device::MEM_MODE) { + throw std::runtime_error("Error: Only load to mem is supported"); + } + } else if (family == "xcf") { + _fpga_family = XCF_FAMILY; + if (_mode == Device::MEM_MODE) { + throw std::runtime_error("Error: Only write or read is supported"); + } } else if (family == "spartan6") { _fpga_family = SPARTAN6_FAMILY; } else if (family == "xc9500xl") { @@ -89,6 +99,7 @@ Xilinx::~Xilinx() {} #define JPROGRAM 0x0B #define JSTART 0x0C #define JSHUTDOWN 0x0D +#define ISC_PROGRAM 0x11 #define ISC_DISABLE 0x16 #define BYPASS 0xff @@ -164,7 +175,7 @@ void Xilinx::program(unsigned int offset) return; } - if (_mode == Device::MEM_MODE) + if (_mode == Device::MEM_MODE || _fpga_family == XCF_FAMILY) reverse = true; printInfo("Open file ", false); @@ -194,6 +205,11 @@ void Xilinx::program(unsigned int offset) if (_verbose) bit->displayHeader(); + if (_fpga_family == XCF_FAMILY) { + xcf_program(bit); + return; + } + if (_mode == Device::SPI_MODE) { program_spi(bit, offset); reset(); @@ -361,10 +377,21 @@ void Xilinx::program_mem(ConfigBitstreamParser *bitfile) bool Xilinx::dumpFlash(const std::string &filename, uint32_t base_addr, uint32_t len) { - if (_fpga_family == XC95_FAMILY) { - /* enable ISC */ - flow_enable(); - std::string buffer = flow_read(); + if (_fpga_family == XC95_FAMILY || _fpga_family == XCF_FAMILY) { + std::string buffer; + if (_fpga_family == XC95_FAMILY) { + /* enable ISC */ + flow_enable(); + buffer = flow_read(); + /* disable ISC */ + flow_disable(); + } else { + /* enable ISC */ + xcf_flow_enable(0x34); + buffer = xcf_read(); + /* disable ISC */ + xcf_flow_disable(); + } printInfo("Open dump file ", false); FILE *fd = fopen(filename.c_str(), "wb"); if (!fd) { @@ -379,8 +406,6 @@ bool Xilinx::dumpFlash(const std::string &filename, printSuccess("DONE"); fclose(fd); - /* disable ISC */ - flow_disable(); return true; } @@ -610,6 +635,226 @@ std::string Xilinx::flow_read() return buffer; } +/* */ +/* XCF Prom */ +/* */ + +#define XCF_FVFY3 0xE2 +#define XCF_ISCTESTSTATUS 0xE3 +#define XCF_ISC_ENABLE 0xE8 +#define XCF_ISC_PROGRAM 0xEA +#define XCF_ISC_ADDR_SHIFT 0xEB +#define XCF_ISC_ERASE 0xEC +#define XCF_ISC_DATA_SHIFT 0xED +#define XCF_ISC_READ 0xeF +#define XCF_ISC_DISABLE 0xF0 + +void Xilinx::xcf_flow_enable(uint8_t mode) +{ + _jtag->shiftIR(XCF_ISC_ENABLE, 8); + _jtag->shiftDR(&mode, NULL, 6); + _jtag->toggleClk(1); +} + +void Xilinx::xcf_flow_disable() +{ + _jtag->shiftIR(XCF_ISC_DISABLE, 8); + usleep(110000); + _jtag->shiftIR(BYPASS, 8); + _jtag->toggleClk(1); +} + +bool Xilinx::xcf_flow_erase() +{ + uint8_t xfer_buf[2] = {0x01, 0x00}; + + printInfo("Erase flash ", false); + xcf_flow_enable(); + + _jtag->shiftIR(XCF_ISC_ADDR_SHIFT, 8); + _jtag->shiftDR(xfer_buf, NULL, 16); + _jtag->toggleClk(1); + + _jtag->shiftIR(XCF_ISC_ERASE, 8); + usleep(500000); + + int i; + for (i = 0; i < 32; i++) { + _jtag->shiftIR(XCF_ISCTESTSTATUS, 8); + usleep(500000); + _jtag->shiftDR(NULL, xfer_buf, 8); + if ((xfer_buf[0] & 0x04)) + break; + } + + if (i == 32) { + printError("FAIL"); + return false; + } + + printSuccess("DONE"); + + xcf_flow_disable(); + + return true; +} + +bool Xilinx::xcf_program(ConfigBitstreamParser *bitfile) +{ + uint8_t tx_buf[4096 / 8]; + uint16_t pkt_len = + ((_jtag->get_target_device_id() == 0x05044093) ? 2048 : 4096) / 8; + uint8_t *data = bitfile->getData(); + uint32_t data_len = bitfile->getLength() / 8; + uint32_t xfer_len, offset = 0; + uint32_t addr = 0; + int xfer_end; + + /* limit JTAG clock frequency to 15MHz */ + if (_jtag->getClkFreq() > 15e6) + _jtag->setClkFreq(15e6); + + if (!xcf_flow_erase()) { + printError("flow erase failed"); + return false; + } + + xcf_flow_enable(); + + int blk_id = 0; + + ProgressBar progress("Write PROM", (data_len / pkt_len), 50, _quiet); + + while (data_len > 0) { + if (data_len < pkt_len) { + xfer_len = data_len; + xfer_end = Jtag::SHIFT_DR; + } else { + xfer_len = pkt_len; + xfer_end = Jtag::RUN_TEST_IDLE; + } + + /* send data to PROM */ + _jtag->shiftIR(XCF_ISC_DATA_SHIFT, 8); + _jtag->shiftDR(data+offset, NULL, xfer_len * 8, xfer_end); + if (xfer_len != pkt_len) { + uint32_t res = pkt_len - xfer_len; + memset(tx_buf, 0xff, res); + _jtag->shiftDR(tx_buf, NULL, res * 8); + } + + _jtag->toggleClk(1); + + /* send address */ + tx_buf[0] = (addr >> 0) & 0x00ff; + tx_buf[1] = (addr >> 8) & 0x00ff; + _jtag->shiftIR(XCF_ISC_ADDR_SHIFT, 8); + _jtag->shiftDR(tx_buf, NULL, 16); + _jtag->toggleClk(1); + + /* send program instruction */ + _jtag->shiftIR(XCF_ISC_PROGRAM, 8); + usleep((addr == 0) ? 14000: 500); + + /* wait until bit 3 != 1 */ + int i; + for (i = 0; i < 29; i++) { + _jtag->shiftIR(XCF_ISCTESTSTATUS, 8); + usleep(500); + _jtag->shiftDR(NULL, tx_buf, 8); + if ((tx_buf[0] & 0x04)) + break; + } + + if (i == 29) { + progress.fail(); + return false; + } + + blk_id++; + offset += xfer_len; + addr += 32; + data_len -= xfer_len; + progress.display(blk_id); + } + progress.done(); + + /* program done */ + _jtag->shiftIR(BYPASS, 8); + _jtag->toggleClk(1); + + if (_verify) { + std::string flash = xcf_read(); + uint32_t file_size = bitfile->getLength() / 8; + uint32_t prom_size = (uint32_t)flash.size(); + + uint32_t nb_bytes = (file_size > prom_size) ? prom_size : file_size; + ProgressBar progress2("Verify Flash", nb_bytes, 50, _quiet); + + for (uint32_t pos = 0; pos < nb_bytes; pos++) { + if (data[pos] != (uint8_t)flash[pos]) { + progress2.fail(); + char error[64]; + snprintf(error, sizeof(error), + "Error: wrong value: read %02x instead of %02x", + (uint8_t)flash[pos], (uint8_t)data[pos]); + printError(error); + xcf_flow_disable(); + return false; + } + progress.display(pos); + } + progress2.done(); + } + + _jtag->go_test_logic_reset(); + + xcf_flow_disable(); + + return true; +} + +std::string Xilinx::xcf_read() +{ + uint32_t addr = 0; + uint8_t rx_buf[4096 / 8]; + uint16_t pkt_len = + ((_jtag->get_target_device_id() == 0x05044093) ? 2048 : 4096) / 8; + uint16_t nb_section = + ((_jtag->get_target_device_id() == 0x05046093) ? 1024 : 512); + + std::string buffer; + + /* limit JTAG clock frequency to 15MHz */ + if (_jtag->getClkFreq() > 15e6) + _jtag->setClkFreq(15e6); + + ProgressBar progress("Read PROM", nb_section, 50, _quiet); + + for (size_t section = 0; section < nb_section; section++) { + /* send address */ + rx_buf[0] = (addr >> 0) & 0x00ff; + rx_buf[1] = (addr >> 8) & 0x00ff; + _jtag->shiftIR(XCF_ISC_ADDR_SHIFT, 8); + _jtag->shiftDR(rx_buf, NULL, 16); + _jtag->toggleClk(1); + + /* send data to PROM */ + _jtag->shiftIR(XCF_ISC_READ, 8); + usleep(50); + _jtag->shiftDR(NULL, rx_buf, pkt_len * 8); + + for (int i = 0; i < pkt_len; i++) + buffer += rx_buf[i]; + + progress.display(section); + addr += 32; + } + progress.done(); + + return buffer; +} + /* */ /* SPI interface */ /* */ diff --git a/src/xilinx.hpp b/src/xilinx.hpp index 6b017dd..7a894a2 100644 --- a/src/xilinx.hpp +++ b/src/xilinx.hpp @@ -60,6 +60,15 @@ class Xilinx: public Device, SPIInterface { */ std::string flow_read(); + /* ------------------- */ + /* XCF JTAG Flash PROM */ + /* ------------------- */ + void xcf_flow_enable(uint8_t mode = 0x37); + void xcf_flow_disable(); + bool xcf_flow_erase(); + bool xcf_program(ConfigBitstreamParser *bitfile); + std::string xcf_read(); + /* spi interface */ int spi_put(uint8_t cmd, uint8_t *tx, uint8_t *rx, uint32_t len) override; @@ -71,11 +80,13 @@ class Xilinx: public Device, SPIInterface { /* list of xilinx family devices */ enum xilinx_family_t { XC95_FAMILY = 0, - SPARTAN6_FAMILY = 1, - SPARTAN7_FAMILY = 2, - ARTIX_FAMILY = 3, - KINTEX_FAMILY = 4, - ZYNQ_FAMILY = 5, + SPARTAN3_FAMILY, + SPARTAN6_FAMILY, + SPARTAN7_FAMILY, + ARTIX_FAMILY, + KINTEX_FAMILY, + ZYNQ_FAMILY, + XCF_FAMILY, UNKNOWN_FAMILY = 999 };