- jtag mode: added spiOverJtag support;
- spiOverJtag: added efinix support: verilog file and t8f81 bitstream
- xyloni: part code for spiOverJtag flash access/load bridge
This commit is contained in:
Gwenhael Goavec-Merou 2023-04-19 06:58:39 +02:00
parent 6dbc3b6a54
commit 07a0708eb8
6 changed files with 310 additions and 52 deletions

View File

@ -0,0 +1,59 @@
module spiOverJtag (
input jtag_1_CAPTURE,
input jtag_1_DRCK,
input jtag_1_RESET,
input jtag_1_RUNTEST,
input jtag_1_SEL,
input jtag_1_SHIFT,
input jtag_1_TCK,
input jtag_1_TDI,
input jtag_1_TMS,
input jtag_1_UPDATE,
output jtag_1_TDO,
output csn,
output sck,
output sdi_dq0,
input sdo_dq1,
output wpn_dq2,
output hldn_dq3
);
wire capture, drck, sel, update;
wire runtest;
wire tdi;
reg fsm_csn;
assign wpn_dq2 = 1'b1;
assign hldn_dq3 = 1'b1;
// jtag -> spi flash
assign sdi_dq0 = tdi;
wire tdo = (sel) ? sdo_dq1 : tdi;
assign csn = fsm_csn;
wire tmp_cap_s = capture && sel;
wire tmp_up_s = update && sel;
always @(posedge drck, posedge runtest) begin
if (runtest) begin
fsm_csn <= 1'b1;
end else begin
if (tmp_cap_s) begin
fsm_csn <= 1'b0;
end else if (tmp_up_s) begin
fsm_csn <= 1'b1;
end else begin
fsm_csn <= fsm_csn;
end
end
end
assign sck = drck;
assign capture = jtag_1_CAPTURE;
assign drck = jtag_1_DRCK;
assign runtest = jtag_1_RUNTEST;
assign sel = jtag_1_SEL;
assign tdi = jtag_1_TDI;
assign update = jtag_1_UPDATE;
assign jtag_1_TDO = tdo;
endmodule

Binary file not shown.

View File

@ -207,7 +207,7 @@ static std::map <std::string, target_board_t> board_list = {
JTAG_BOARD("vcu118", "xcvu9p-flga2104", "jtag-smt2-nc", 0, 0, CABLE_DEFAULT), JTAG_BOARD("vcu118", "xcvu9p-flga2104", "jtag-smt2-nc", 0, 0, CABLE_DEFAULT),
JTAG_BOARD("vcu128", "xcvu37p-fsvh2892", "ft4232", 0, 0, CABLE_DEFAULT), JTAG_BOARD("vcu128", "xcvu37p-fsvh2892", "ft4232", 0, 0, CABLE_DEFAULT),
JTAG_BOARD("xem8320", "xcau25p-2ffvb676", "" , 0, 0, CABLE_DEFAULT), JTAG_BOARD("xem8320", "xcau25p-2ffvb676", "" , 0, 0, CABLE_DEFAULT),
JTAG_BOARD("xyloni_jtag", "", "efinix_jtag_ft4232" , 0, 0, CABLE_DEFAULT), JTAG_BOARD("xyloni_jtag", "t8f81", "efinix_jtag_ft4232", 0, 0, CABLE_DEFAULT),
SPI_BOARD("xyloni_spi", "efinix", "efinix_spi_ft4232", SPI_BOARD("xyloni_spi", "efinix", "efinix_spi_ft4232",
DBUS4, DBUS5, DBUS7, DBUS3, DBUS0, DBUS1, DBUS2, DBUS6, 0, CABLE_DEFAULT), DBUS4, DBUS5, DBUS7, DBUS3, DBUS0, DBUS1, DBUS2, DBUS6, 0, CABLE_DEFAULT),
JTAG_BOARD("xtrx", "xc7a50tcpg236", "" , 0, 0, CABLE_DEFAULT), JTAG_BOARD("xtrx", "xc7a50tcpg236", "" , 0, 0, CABLE_DEFAULT),

View File

@ -12,15 +12,19 @@
#include <stdexcept> #include <stdexcept>
#include <string> #include <string>
#include "common.hpp"
#include "device.hpp"
#include "display.hpp" #include "display.hpp"
#include "efinixHexParser.hpp" #include "efinixHexParser.hpp"
#include "ftdispi.hpp"
#include "device.hpp"
#include "ftdiJtagMPSSE.hpp" #include "ftdiJtagMPSSE.hpp"
#include "ftdispi.hpp"
#include "jtag.hpp" #include "jtag.hpp"
#include "part.hpp" #include "part.hpp"
#include "progressBar.hpp" #include "progressBar.hpp"
#include "rawParser.hpp" #include "rawParser.hpp"
#if defined (_WIN64) || defined (_WIN32)
#include "pathHelper.hpp"
#endif
#include "spiFlash.hpp" #include "spiFlash.hpp"
Efinix::Efinix(FtdiSpi* spi, const std::string &filename, Efinix::Efinix(FtdiSpi* spi, const std::string &filename,
@ -30,27 +34,29 @@ Efinix::Efinix(FtdiSpi* spi, const std::string &filename,
bool verify, int8_t verbose): bool verify, int8_t verbose):
Device(NULL, filename, file_type, verify, verbose), _ftdi_jtag(NULL), Device(NULL, filename, file_type, verify, verbose), _ftdi_jtag(NULL),
_rst_pin(rst_pin), _done_pin(done_pin), _cs_pin(0), _oe_pin(oe_pin), _rst_pin(rst_pin), _done_pin(done_pin), _cs_pin(0), _oe_pin(oe_pin),
_fpga_family(UNKNOWN_FAMILY), _irlen(0) _fpga_family(UNKNOWN_FAMILY), _irlen(0), _device_package(""),
_spiOverJtagPath("")
{ {
_spi = spi; _spi = spi;
_spi->gpio_set_input(_done_pin); init_common(Device::WR_FLASH);
_spi->gpio_set_output(_rst_pin | _oe_pin);
} }
Efinix::Efinix(Jtag* jtag, const std::string &filename, Efinix::Efinix(Jtag* jtag, const std::string &filename,
const std::string &file_type, const std::string &file_type, Device::prog_type_t prg_type,
const std::string &board_name, const std::string &board_name, const std::string &device_package,
bool verify, int8_t verbose): const std::string &spiOverJtagPath, bool verify, int8_t verbose):
Device(jtag, filename, file_type, verify, verbose), Device(jtag, filename, file_type, verify, verbose),
SPIInterface(filename, verbose, 256, false, false, false),
_spi(NULL), _rst_pin(0), _done_pin(0), _cs_pin(0), _spi(NULL), _rst_pin(0), _done_pin(0), _cs_pin(0),
_oe_pin(0), _fpga_family(UNKNOWN_FAMILY), _irlen(0) _oe_pin(0), _fpga_family(UNKNOWN_FAMILY), _irlen(0),
_device_package(device_package), _spiOverJtagPath(spiOverJtagPath)
{ {
_ftdi_jtag = reinterpret_cast<FtdiJtagMPSSE *>(jtag->get_ll_class()); _ftdi_jtag = reinterpret_cast<FtdiJtagMPSSE *>(jtag->get_ll_class());
/* detect FPGA type (Trion or Titanium) */ /* detect FPGA type (Trion or Titanium) */
uint32_t idcode = _jtag->get_target_device_id(); const uint32_t idcode = _jtag->get_target_device_id();
std::string family = fpga_list[idcode].family; const std::string family = fpga_list[idcode].family;
if (family == "Titanium") { if (family == "Titanium") {
if (_file_extension == "hex") { if (_file_extension == "hex") {
throw std::runtime_error("Error: loading hex file is not allowed " throw std::runtime_error("Error: loading hex file is not allowed "
@ -84,7 +90,7 @@ Efinix::Efinix(Jtag* jtag, const std::string &filename,
} }
/* 2: retrieve spi board */ /* 2: retrieve spi board */
target_board_t *spi_board = &(board_list[spi_board_name]); const target_board_t *spi_board = &(board_list[spi_board_name]);
/* 3: SPI cable */ /* 3: SPI cable */
cable_t spi_cable = (cable_list[spi_board->cable_name]); cable_t spi_cable = (cable_list[spi_board->cable_name]);
@ -95,31 +101,56 @@ Efinix::Efinix(Jtag* jtag, const std::string &filename,
_cs_pin = spi_board->spi_pins_config.cs_pin; _cs_pin = spi_board->spi_pins_config.cs_pin;
_rst_pin = spi_board->reset_pin; _rst_pin = spi_board->reset_pin;
_oe_pin = spi_board->oe_pin; _oe_pin = spi_board->oe_pin;
_done_pin = spi_board->done_pin;
/* 5: open SPI interface */ /* 5: open SPI interface */
_spi = new FtdiSpi(spi_cable, spi_board->spi_pins_config, _spi = new FtdiSpi(spi_cable, spi_board->spi_pins_config,
jtag->getClkFreq(), verbose > 0); jtag->getClkFreq(), verbose > 0);
/* 6: configure pins direction and default state */ /* 6: configure pins direction and default state */
_spi->gpio_set_output(_oe_pin | _rst_pin | _cs_pin); init_common(prg_type);
}
void Efinix::init_common(const Device::prog_type_t &prg_type)
{
_spi->gpio_set_input(_done_pin);
_spi->gpio_set_output(_rst_pin | _oe_pin);
switch (prg_type) {
case Device::WR_FLASH:
_mode = (_jtag) ? Device::FLASH_MODE : Device::SPI_MODE;
break;
case Device::WR_SRAM:
if (!_jtag) {
throw std::runtime_error("Efinix: SRAM load requires jtag");
}
_mode = MEM_MODE;
break;
default:
_mode = NONE_MODE;
}
} }
Efinix::~Efinix() Efinix::~Efinix()
{} {
if (_jtag && _spi)
delete _spi;
}
void Efinix::reset() void Efinix::reset()
{ {
if (_ftdi_jtag) // not supported if (!_spi) // not supported
return; return;
uint32_t timeout = 1000; uint32_t timeout = 1000;
_spi->gpio_clear(_rst_pin | _oe_pin); _spi->gpio_clear(_rst_pin | _oe_pin);
usleep(1000); usleep(1000);
_spi->gpio_set(_rst_pin | _oe_pin); _spi->gpio_set(_rst_pin | _oe_pin);
printInfo("Reset ", false); printInfo("Reset ", false);
do { do {
timeout--; timeout--;
usleep(12000); usleep(12000);
} while (((_spi->gpio_get(true) & _done_pin) == 0) || timeout > 0); } while (((_spi->gpio_get(true) & _done_pin) == 0) && timeout > 0);
if (timeout == 0) if (timeout == 0)
printError("FAIL"); printError("FAIL");
else else
@ -130,6 +161,8 @@ void Efinix::program(unsigned int offset, bool unprotect_flash)
{ {
if (_file_extension.empty()) if (_file_extension.empty())
return; return;
if (_mode == Device::NONE_MODE)
return;
ConfigBitstreamParser *bit; ConfigBitstreamParser *bit;
try { try {
@ -156,16 +189,28 @@ void Efinix::program(unsigned int offset, bool unprotect_flash)
return; return;
} }
unsigned char *data = bit->getData(); const uint8_t *data = bit->getData();
int length = bit->getLength() / 8; const int length = bit->getLength() / 8;
if (_verbose) if (_verbose)
bit->displayHeader(); bit->displayHeader();
if (_ftdi_jtag) switch (_mode) {
programJTAG(data, length); case MEM_MODE:
else programJTAG(data, length);
programSPI(offset, data, length, unprotect_flash); break;
case FLASH_MODE:
if (_jtag)
SPIInterface::write(offset, const_cast<uint8_t *>(data),
length, unprotect_flash);
else
programSPI(offset, data, length, unprotect_flash);
break;
default:
return;
}
delete bit;
} }
bool Efinix::dumpFlash(uint32_t base_addr, uint32_t len) bool Efinix::dumpFlash(uint32_t base_addr, uint32_t len)
@ -203,11 +248,9 @@ bool Efinix::dumpFlash(uint32_t base_addr, uint32_t len)
return false; return false;
} }
void Efinix::programSPI(unsigned int offset, uint8_t *data, int length, void Efinix::programSPI(unsigned int offset, const uint8_t *data,
bool unprotect_flash) const int length, const bool unprotect_flash)
{ {
uint32_t timeout = 1000;
_spi->gpio_clear(_rst_pin | _oe_pin); _spi->gpio_clear(_rst_pin | _oe_pin);
SPIFlash flash(reinterpret_cast<SPIInterface *>(_spi), unprotect_flash, SPIFlash flash(reinterpret_cast<SPIInterface *>(_spi), unprotect_flash,
@ -217,24 +260,13 @@ void Efinix::programSPI(unsigned int offset, uint8_t *data, int length,
printf("%02x\n", flash.read_status_reg()); printf("%02x\n", flash.read_status_reg());
flash.read_id(); flash.read_id();
flash.erase_and_prog(offset, data, length); flash.erase_and_prog(offset, const_cast<uint8_t *>(data), length);
/* verify write if required */ /* verify write if required */
if (_verify) if (_verify)
flash.verify(offset, data, length); flash.verify(offset, data, length);
_spi->gpio_set(_rst_pin | _oe_pin); reset();
usleep(12000);
printInfo("Wait for CDONE ", false);
do {
timeout--;
usleep(12000);
} while (((_spi->gpio_get(true) & _done_pin) == 0) && timeout > 0);
if (timeout == 0)
printError("FAIL");
else
printSuccess("DONE");
} }
#define SAMPLE_PRELOAD 0x02 #define SAMPLE_PRELOAD 0x02
@ -243,8 +275,9 @@ void Efinix::programSPI(unsigned int offset, uint8_t *data, int length,
#define IDCODE 0x03 #define IDCODE 0x03
#define PROGRAM 0x04 #define PROGRAM 0x04
#define ENTERUSER 0x07 #define ENTERUSER 0x07
#define USER1 0x08
void Efinix::programJTAG(uint8_t *data, int length) void Efinix::programJTAG(const uint8_t *data, const int length)
{ {
int xfer_len = 512, tx_end; int xfer_len = 512, tx_end;
uint8_t tx[512]; uint8_t tx[512];
@ -299,4 +332,155 @@ void Efinix::programJTAG(uint8_t *data, int length)
memset(tx, 0, 512); memset(tx, 0, 512);
_jtag->shiftDR(tx, NULL, 100); _jtag->shiftDR(tx, NULL, 100);
_jtag->shiftIR(IDCODE, _irlen); _jtag->shiftIR(IDCODE, _irlen);
uint8_t idc[4];
_jtag->shiftDR(NULL, idc, 4);
printf("%02x%02x%02x%02x\n",
idc[0], idc[1], idc[2], idc[3]);
}
bool Efinix::post_flash_access()
{
if (_skip_reset)
printInfo("Skip resetting device");
else
reset();
return true;
}
bool Efinix::prepare_flash_access()
{
if (_skip_load_bridge) {
printInfo("Skip loading bridge for spiOverjtag");
return true;
}
std::string bitname;
if (!_spiOverJtagPath.empty()) {
bitname = _spiOverJtagPath;
} else {
if (_device_package.empty()) {
printError("Can't program SPI flash: missing device-package information");
return false;
}
bitname = get_shell_env_var("OPENFPGALOADER_SOJ_DIR",
DATA_DIR "/openFPGALoader");
bitname += "/spiOverJtag_efinix_" + _device_package + ".bit.gz";
}
#if defined (_WIN64) || defined (_WIN32)
/* Convert relative path embedded at compile time to an absolute path */
bitname = PathHelper::absolutePath(bitname);
#endif
std::cout << "use: " << bitname << std::endl;
/* first: load spi over jtag */
try {
EfinixHexParser bridge(bitname);
bridge.parse();
const uint8_t *data = bridge.getData();
const int length = bridge.getLength() / 8;
programJTAG(data, length);
} catch (std::exception &e) {
printError(e.what());
return false;
}
return true;
}
/* */
/* SPI interface */
/* */
/*
* jtag : jtag interface
* cmd : opcode for SPI flash
* tx : buffer to send
* rx : buffer to fill
* len : number of byte to send/receive (cmd not comprise)
* so to send only a cmd set len to 0 (or omit this param)
*/
int Efinix::spi_put(uint8_t cmd,
uint8_t *tx, uint8_t *rx, uint32_t len)
{
int kXferLen = len + 1 + ((rx == NULL) ? 0 : 1);
uint8_t jtx[kXferLen];
jtx[0] = EfinixHexParser::reverseByte(cmd);
uint8_t jrx[kXferLen];
if (tx != NULL) {
for (uint32_t i=0; i < len; i++)
jtx[i+1] = EfinixHexParser::reverseByte(tx[i]);
}
/* addr BSCAN user1 */
_jtag->shiftIR(USER1, _irlen);
/* send first already stored cmd,
* in the same time store each byte
* to next
*/
_jtag->shiftDR(jtx, (rx == NULL)? NULL: jrx, 8*kXferLen);
if (rx != NULL) {
for (uint32_t i=0; i < len; i++)
rx[i] = EfinixHexParser::reverseByte(jrx[i+1] >> 1) | (jrx[i+2] & 0x01);
}
return 0;
}
int Efinix::spi_put(uint8_t *tx, uint8_t *rx, uint32_t len)
{
int kXferLen = len + ((rx == NULL) ? 0 : 1);
uint8_t jtx[kXferLen];
uint8_t jrx[kXferLen];
if (tx != NULL) {
for (uint32_t i=0; i < len; i++)
jtx[i] = EfinixHexParser::reverseByte(tx[i]);
}
/* addr BSCAN user1 */
_jtag->shiftIR(USER1, _irlen);
/* send first already stored cmd,
* in the same time store each byte
* to next
*/
_jtag->shiftDR(jtx, (rx == NULL)? NULL: jrx, 8*kXferLen);
if (rx != NULL) {
for (uint32_t i=0; i < len; i++)
rx[i] = EfinixHexParser::reverseByte(jrx[i] >> 1) | (jrx[i+1] & 0x01);
}
return 0;
}
int Efinix::spi_wait(uint8_t cmd, uint8_t mask, uint8_t cond,
uint32_t timeout, bool verbose)
{
uint8_t rx[2], dummy[2], tmp;
uint8_t tx = EfinixHexParser::reverseByte(cmd);
uint32_t count = 0;
_jtag->shiftIR(USER1, _irlen, Jtag::UPDATE_IR);
_jtag->shiftDR(&tx, NULL, 8, Jtag::SHIFT_DR);
do {
_jtag->shiftDR(dummy, rx, 8*2, Jtag::SHIFT_DR);
tmp = (EfinixHexParser::reverseByte(rx[0] >> 1)) | (0x01 & rx[1]);
count++;
if (count == timeout){
printf("timeout: %x %x %x\n", tmp, rx[0], rx[1]);
break;
}
if (verbose) {
printf("%x %x %x %u\n", tmp, mask, cond, count);
}
} while ((tmp & mask) != cond);
_jtag->shiftDR(dummy, rx, 8*2, Jtag::EXIT1_DR);
_jtag->go_test_logic_reset();
if (count == timeout) {
printf("%x\n", tmp);
std::cout << "wait: Error" << std::endl;
return -ETIME;
}
return 0;
} }

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
/* /*
* Copyright (C) 2020 Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com> * Copyright (C) 2020-2023 Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
*/ */
#ifndef SRC_EFINIX_HPP_ #ifndef SRC_EFINIX_HPP_
@ -12,32 +12,41 @@
#include "ftdiJtagMPSSE.hpp" #include "ftdiJtagMPSSE.hpp"
#include "ftdispi.hpp" #include "ftdispi.hpp"
#include "jtag.hpp" #include "jtag.hpp"
#include "spiInterface.hpp"
class Efinix: public Device { class Efinix: public Device, SPIInterface {
public: public:
Efinix(FtdiSpi *spi, const std::string &filename, Efinix(FtdiSpi *spi, const std::string &filename,
const std::string &file_type, const std::string &file_type,
uint16_t rst_pin, uint16_t done_pin, uint16_t oe_pin, uint16_t rst_pin, uint16_t done_pin, uint16_t oe_pin,
bool verify, int8_t verbose); bool verify, int8_t verbose);
Efinix(Jtag* jtag, const std::string &filename, Efinix(Jtag* jtag, const std::string &filename,
const std::string &file_type, const std::string &file_type, Device::prog_type_t prg_type,
const std::string &board_name, const std::string &board_name, const std::string &device_package,
const std::string &spiOverJtagPath,
bool verify, int8_t verbose); bool verify, int8_t verbose);
~Efinix(); ~Efinix();
void program(unsigned int offset, bool unprotect_flash) override; void program(unsigned int offset, bool unprotect_flash) override;
bool dumpFlash(uint32_t base_addr, uint32_t len) override; bool dumpFlash(uint32_t base_addr, uint32_t len) override;
virtual bool protect_flash(uint32_t len) override { bool protect_flash(uint32_t len) override {
(void) len; (void) len;
printError("protect flash not supported"); return false;} printError("protect flash not supported"); return false;}
virtual bool unprotect_flash() override { bool unprotect_flash() override {
printError("unprotect flash not supported"); return false;} printError("unprotect flash not supported"); return false;}
virtual bool bulk_erase_flash() override { bool bulk_erase_flash() override {
printError("bulk erase flash not supported"); return false;} printError("bulk erase flash not supported"); return false;}
/* not supported in SPI Active mode */ /* not supported in SPI Active mode */
int idCode() override {return 0;} int idCode() override {return 0;}
void reset() override; void reset() override;
/* 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 = false) override;
private: private:
/* list of efinix family devices */ /* list of efinix family devices */
enum efinix_family_t { enum efinix_family_t {
@ -45,9 +54,12 @@ class Efinix: public Device {
TRION_FAMILY, TRION_FAMILY,
UNKNOWN_FAMILY = 999 UNKNOWN_FAMILY = 999
}; };
void programSPI(unsigned int offset, uint8_t *data, int length, void init_common(const Device::prog_type_t &prg_type);
bool unprotect_flash); void programSPI(unsigned int offset, const uint8_t *data,
void programJTAG(uint8_t *data, int length); const int length, const bool unprotect_flash);
void programJTAG(const uint8_t *data, const int length);
bool post_flash_access() override;
bool prepare_flash_access() override;
FtdiSpi *_spi; FtdiSpi *_spi;
FtdiJtagMPSSE *_ftdi_jtag; FtdiJtagMPSSE *_ftdi_jtag;
uint16_t _rst_pin; uint16_t _rst_pin;
@ -56,6 +68,8 @@ class Efinix: public Device {
uint16_t _oe_pin; uint16_t _oe_pin;
efinix_family_t _fpga_family; efinix_family_t _fpga_family;
int _irlen; int _irlen;
std::string _device_package;
std::string _spiOverJtagPath;
}; };
#endif // SRC_EFINIX_HPP_ #endif // SRC_EFINIX_HPP_

View File

@ -556,7 +556,8 @@ int main(int argc, char **argv)
args.prg_type, args.verify, args.verbose); args.prg_type, args.verify, args.verbose);
} else if (fab == "efinix") { } else if (fab == "efinix") {
fpga = new Efinix(jtag, args.bit_file, args.file_type, fpga = new Efinix(jtag, args.bit_file, args.file_type,
/*DBUS4 | DBUS7, DBUS5*/args.board, args.verify, args.verbose); args.prg_type, args.board, args.fpga_part, args.bridge_path,
args.verify, args.verbose);
} else if (fab == "Gowin") { } else if (fab == "Gowin") {
fpga = new Gowin(jtag, args.bit_file, args.file_type, args.mcufw, 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);