Merge pull request #298 from barbedo/vcu118-flash-cli-options

Add target-flash and secondary-bitstream CLI options for VCU118
This commit is contained in:
Gwenhael Goavec-Merou 2023-02-07 18:55:42 +01:00 committed by GitHub
commit e8fbc1539a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 269 additions and 103 deletions

125
README.md
View File

@ -49,68 +49,73 @@ openFPGALoader -c cmsisdap fpga_bitstream.bit
## Usage ## Usage
``` ```
Usage: ./openFPGALoader [OPTION...] BIT_FILE Usage: openFPGALoader [OPTION...] BIT_FILE
openFPGALoader -- a program to flash FPGA openFPGALoader -- a program to flash FPGA
--altsetting arg DFU interface altsetting (only for DFU mode) --altsetting arg DFU interface altsetting (only for DFU mode)
--bitstream arg bitstream --bitstream arg bitstream
-b, --board arg board name, may be used instead of cable --secondary-bitstream arg
-B, --bridge arg disable spiOverJtag model detection by providing secondary bitstream (some Xilinx UltraScale
bitstream(intel/xilinx) boards)
-c, --cable arg jtag interface -b, --board arg board name, may be used instead of cable
--invert-read-edge JTAG mode / FTDI: read on negative edge instead -B, --bridge arg disable spiOverJtag model detection by
of positive providing bitstream(intel/xilinx)
--vid arg probe Vendor ID -c, --cable arg jtag interface
--pid arg probe Product ID --invert-read-edge JTAG mode / FTDI: read on negative edge
--cable-index arg probe index (FTDI and cmsisDAP) instead of positive
--busdev-num arg select a probe by it bus and device number --vid arg probe Vendor ID
(bus_num:device_addr) --pid arg probe Product ID
--ftdi-serial arg FTDI chip serial number --cable-index arg probe index (FTDI and cmsisDAP)
--ftdi-channel arg FTDI chip channel number (channels 0-3 map to --busdev-num arg select a probe by it bus and device number
A-D) (bus_num:device_addr)
-d, --device arg device to use (/dev/ttyUSBx) --ftdi-serial arg FTDI chip serial number
--detect detect FPGA --ftdi-channel arg FTDI chip channel number (channels 0-3 map to
--dfu DFU mode A-D)
--dump-flash Dump flash mode --detect detect FPGA
--external-flash select ext flash for device with internal and --dfu DFU mode
external storage --dump-flash Dump flash mode
--file-size arg provides size in Byte to dump, must be used with --bulk-erase Bulk erase flash
dump-flash --target-flash arg for boards with multiple flash chips (some
--file-type arg provides file type instead of let's deduced by Xilinx UltraScale boards), select the target
using extension flash: primary (default), secondary or both
--flash-sector arg flash sector (Lattice parts only) --external-flash select ext flash for device with internal and
--fpga-part arg fpga model flavor + package external storage
--freq arg jtag frequency (Hz) --file-size arg provides size in Byte to dump, must be used
-f, --write-flash write bitstream in flash (default: false) with dump-flash
--index-chain arg device index in JTAG-chain --file-type arg provides file type instead of let's deduced
--ip arg IP address (only for XVC client) by using extension
--list-boards list all supported boards --flash-sector arg flash sector (Lattice parts only)
--list-cables list all supported cables --fpga-part arg fpga model flavor + package
--list-fpga list all supported FPGA --freq arg jtag frequency (Hz)
-m, --write-sram write bitstream in SRAM (default: true) -f, --write-flash write bitstream in flash (default: false)
-o, --offset arg start offset in EEPROM --index-chain arg device index in JTAG-chain
--pins arg pin config TDI:TDO:TCK:TMS --ip arg IP address (only for XVC client)
--probe-firmware arg firmware for JTAG probe (usbBlasterII) --list-boards list all supported boards
--protect-flash arg protect SPI flash area --list-cables list all supported cables
--quiet Produce quiet output (no progress bar) --list-fpga list all supported FPGA
-r, --reset reset FPGA after operations -m, --write-sram write bitstream in SRAM (default: true)
--scan-usb scan USB to display connected probes -o, --offset arg start offset in EEPROM
--skip-load-bridge skip writing bridge to SRAM when in write-flash --pins arg pin config TDI:TDO:TCK:TMS
mode --probe-firmware arg firmware for JTAG probe (usbBlasterII)
--skip-reset skip resetting the device when in write-flash --protect-flash arg protect SPI flash area
mode --quiet Produce quiet output (no progress bar)
--spi SPI mode (only for FTDI in serial mode) -r, --reset reset FPGA after operations
--unprotect-flash Unprotect flash blocks --scan-usb scan USB to display connected probes
-v, --verbose Produce verbose output --skip-load-bridge skip writing bridge to SRAM when in
--verbose-level arg verbose level -1: quiet, 0: normal, 1:verbose, write-flash mode
2:debug --skip-reset skip resetting the device when in write-flash
-h, --help Give this help list mode
--verify Verify write operation (SPI Flash only) --spi SPI mode (only for FTDI in serial mode)
--xvc Xilinx Virtual Cable Functions --unprotect-flash Unprotect flash blocks
--port arg Xilinx Virtual Cable Port (default 3721) -v, --verbose Produce verbose output
--mcufw arg Microcontroller firmware --verbose-level arg verbose level -1: quiet, 0: normal,
--conmcu Connect JTAG to MCU 1:verbose, 2:debug
-V, --Version Print program version -h, --help Give this help list
--verify Verify write operation (SPI Flash only)
--port arg Xilinx Virtual Cable Port (default 3721)
--mcufw arg Microcontroller firmware
--conmcu Connect JTAG to MCU
-V, --Version Print program version
Mandatory or optional arguments to long options are also mandatory or optional Mandatory or optional arguments to long options are also mandatory or optional
for any corresponding short options. for any corresponding short options.

View File

@ -689,4 +689,4 @@
URL: https://www.xilinx.com/products/boards-and-kits/vcu118.html URL: https://www.xilinx.com/products/boards-and-kits/vcu118.html
FPGA: Virtex UltraScale+ xcvu9p-flga2104 FPGA: Virtex UltraScale+ xcvu9p-flga2104
Memory: OK Memory: OK
Flash: NA Flash: OK

View File

@ -47,6 +47,7 @@ struct arguments {
bool reset, detect, verify, scan_usb; bool reset, detect, verify, scan_usb;
unsigned int offset; unsigned int offset;
string bit_file; string bit_file;
string secondary_bit_file;
string device; string device;
string cable; string cable;
string ftdi_serial; string ftdi_serial;
@ -68,6 +69,7 @@ struct arguments {
string probe_firmware; string probe_firmware;
int index_chain; int index_chain;
unsigned int file_size; unsigned int file_size;
string target_flash;
bool external_flash; bool external_flash;
int16_t altsetting; int16_t altsetting;
uint16_t vid; uint16_t vid;
@ -105,12 +107,12 @@ int main(int argc, char **argv)
jtag_pins_conf_t pins_config = {0, 0, 0, 0}; jtag_pins_conf_t pins_config = {0, 0, 0, 0};
/* command line args. */ /* command line args. */
struct arguments args = {0, false, false, false, false, 0, "", "", "-", "", -1, struct arguments args = {0, false, false, false, false, 0, "", "", "", "-", "", -1,
0, false, "-", false, false, false, false, Device::PRG_NONE, false, 0, false, "-", false, false, false, false, Device::PRG_NONE, false,
/* spi dfu file_type fpga_part bridge_path probe_firmware */ /* spi dfu file_type fpga_part bridge_path probe_firmware */
false, false, "", "", "", "", false, false, "", "", "", "",
/* index_chain file_size external_flash altsetting */ /* index_chain file_size target_flash external_flash altsetting */
-1, 0, false, -1, -1, 0, "primary", false, -1,
/* vid, pid, index bus_addr, device_addr */ /* vid, pid, index bus_addr, device_addr */
0, 0, -1, 0, 0, 0, 0, -1, 0, 0,
"127.0.0.1", 0, false, false, "", false, false, "127.0.0.1", 0, false, false, "", false, false,
@ -536,9 +538,9 @@ int main(int argc, char **argv)
Device *fpga; Device *fpga;
try { try {
if (fab == "xilinx") { if (fab == "xilinx") {
fpga = new Xilinx(jtag, args.bit_file, args.file_type, fpga = new Xilinx(jtag, args.bit_file, args.secondary_bit_file,
args.prg_type, args.fpga_part, args.bridge_path, args.verify, args.file_type, args.prg_type, args.fpga_part, args.bridge_path,
args.verbose); args.target_flash, args.verify, args.verbose);
} else if (fab == "altera") { } else if (fab == "altera") {
fpga = new Altera(jtag, args.bit_file, args.file_type, fpga = new Altera(jtag, args.bit_file, args.file_type,
args.prg_type, args.fpga_part, args.bridge_path, args.verify, args.prg_type, args.fpga_part, args.bridge_path, args.verify,
@ -569,7 +571,9 @@ int main(int argc, char **argv)
return EXIT_FAILURE; return EXIT_FAILURE;
} }
if ((!args.bit_file.empty() || !args.file_type.empty()) if ((!args.bit_file.empty() ||
!args.secondary_bit_file.empty() ||
!args.file_type.empty())
&& args.prg_type != Device::RD_FLASH) { && args.prg_type != Device::RD_FLASH) {
try { try {
fpga->program(args.offset, args.unprotect_flash); fpga->program(args.offset, args.unprotect_flash);
@ -691,6 +695,9 @@ int parse_opt(int argc, char **argv, struct arguments *args,
cxxopts::value<int16_t>(args->altsetting)) cxxopts::value<int16_t>(args->altsetting))
("bitstream", "bitstream", ("bitstream", "bitstream",
cxxopts::value<std::string>(args->bit_file)) cxxopts::value<std::string>(args->bit_file))
("secondary-bitstream", "secondary bitstream (some Xilinx"
" UltraScale boards)",
cxxopts::value<std::string>(args->secondary_bit_file))
("b,board", "board name, may be used instead of cable", ("b,board", "board name, may be used instead of cable",
cxxopts::value<string>(args->board)) cxxopts::value<string>(args->board))
("B,bridge", "disable spiOverJtag model detection by providing " ("B,bridge", "disable spiOverJtag model detection by providing "
@ -722,8 +729,12 @@ int parse_opt(int argc, char **argv, struct arguments *args,
("dump-flash", "Dump flash mode") ("dump-flash", "Dump flash mode")
("bulk-erase", "Bulk erase flash", ("bulk-erase", "Bulk erase flash",
cxxopts::value<bool>(args->bulk_erase_flash)) cxxopts::value<bool>(args->bulk_erase_flash))
("target-flash",
"for boards with multiple flash chips (some Xilinx UltraScale"
" boards), select the target flash: primary (default), secondary or both",
cxxopts::value<string>(args->target_flash))
("external-flash", ("external-flash",
"select ext flash for device with internal and external storage", "select ext flash for device with internal and external storage",
cxxopts::value<bool>(args->external_flash)) cxxopts::value<bool>(args->external_flash))
("file-size", ("file-size",
"provides size in Byte to dump, must be used with dump-flash", "provides size in Byte to dump, must be used with dump-flash",
@ -918,11 +929,25 @@ int parse_opt(int argc, char **argv, struct arguments *args,
args->pin_config = true; args->pin_config = true;
} }
if (args->target_flash == "both" || args->target_flash == "secondary") {
if ((args->prg_type == Device::WR_FLASH || args->prg_type == Device::RD_FLASH) &&
args->secondary_bit_file.empty() &&
!args->protect_flash &&
!args->unprotect_flash &&
!args->bulk_erase_flash
) {
printError("Error: secondary bitfile not specified");
cout << options.help() << endl;
throw std::exception();
}
}
if (args->list_cables || args->list_boards || args->list_fpga || if (args->list_cables || args->list_boards || args->list_fpga ||
args->scan_usb) args->scan_usb)
args->is_list_command = true; args->is_list_command = true;
if (args->bit_file.empty() && if (args->bit_file.empty() &&
args->secondary_bit_file.empty() &&
args->file_type.empty() && args->file_type.empty() &&
!args->is_list_command && !args->is_list_command &&
!args->detect && !args->detect &&

View File

@ -27,6 +27,8 @@ class SPIInterface {
bool protect_flash(uint32_t len); bool protect_flash(uint32_t len);
bool unprotect_flash(); bool unprotect_flash();
bool bulk_erase_flash(); bool bulk_erase_flash();
void set_filename(const std::string &filename) {_spif_filename = filename;}
/*! /*!
* \brief write len byte into flash starting at offset, * \brief write len byte into flash starting at offset,
* optionally verify after write and unprotect * optionally verify after write and unprotect

View File

@ -20,6 +20,7 @@
#include "rawParser.hpp" #include "rawParser.hpp"
#include "display.hpp" #include "display.hpp"
#include "spiInterface.hpp"
#include "xilinx.hpp" #include "xilinx.hpp"
#include "xilinxMapParser.hpp" #include "xilinxMapParser.hpp"
#include "part.hpp" #include "part.hpp"
@ -98,15 +99,40 @@ static uint8_t *get_ircode(
return inst_map.at(inst).data(); return inst_map.at(inst).data();
} }
static void open_bitfile(
const std::string &filename, const std::string &extension,
ConfigBitstreamParser **parser, bool reverse, bool verbose)
{
printInfo("Open file ", false);
if (extension == "bit") {
*parser = new BitParser(filename, reverse, verbose);
} else if (extension == "mcs") {
*parser = new McsParser(filename, reverse, verbose);
} else {
*parser = new RawParser(filename, reverse);
}
printSuccess("DONE");
printInfo("Parse file ", false);
if ((*parser)->parse() == EXIT_FAILURE) {
throw std::runtime_error("Failed to parse bitstream");
}
printSuccess("DONE");
}
Xilinx::Xilinx(Jtag *jtag, const std::string &filename, Xilinx::Xilinx(Jtag *jtag, const std::string &filename,
const std::string &secondary_filename,
const std::string &file_type, const std::string &file_type,
Device::prog_type_t prg_type, Device::prog_type_t prg_type,
const std::string &device_package, const std::string &spiOverJtagPath, const std::string &device_package, const std::string &spiOverJtagPath,
const std::string &target_flash,
bool verify, int8_t verbose): bool verify, int8_t verbose):
Device(jtag, filename, file_type, verify, verbose), Device(jtag, filename, file_type, verify, verbose),
SPIInterface(filename, verbose, 256, verify), SPIInterface(filename, verbose, 256, verify),
_device_package(device_package), _spiOverJtagPath(spiOverJtagPath), _device_package(device_package), _spiOverJtagPath(spiOverJtagPath),
_irlen(6) _irlen(6), _filename(filename), _secondary_filename(secondary_filename)
{ {
if (prg_type == Device::RD_FLASH) { if (prg_type == Device::RD_FLASH) {
_mode = Device::READ_MODE; _mode = Device::READ_MODE;
@ -125,7 +151,23 @@ Xilinx::Xilinx(Jtag *jtag, const std::string &filename,
} }
} }
_user_instruction = "USER1"; select_flash_chip(PRIMARY_FLASH);
if (target_flash == "primary") {
_flash_chips = PRIMARY_FLASH;
} else if (target_flash == "secondary") {
_flash_chips = SECONDARY_FLASH;
} else if (target_flash == "both") {
_flash_chips = (PRIMARY_FLASH | SECONDARY_FLASH);
} else {
throw std::runtime_error("Error: unknown flash target: " + target_flash);
}
if (_flash_chips & SECONDARY_FLASH) {
_secondary_file_extension = secondary_filename.substr(
secondary_filename.find_last_of(".") + 1);
_mode = Device::SPI_MODE;
}
uint32_t idcode = _jtag->get_target_device_id(); uint32_t idcode = _jtag->get_target_device_id();
std::string family = fpga_list[idcode].family; std::string family = fpga_list[idcode].family;
@ -299,7 +341,8 @@ int Xilinx::idCode()
void Xilinx::program(unsigned int offset, bool unprotect_flash) void Xilinx::program(unsigned int offset, bool unprotect_flash)
{ {
ConfigBitstreamParser *bit; ConfigBitstreamParser *bit = nullptr;
ConfigBitstreamParser *secondary_bit = nullptr;
bool reverse = false; bool reverse = false;
/* nothing to do */ /* nothing to do */
@ -334,33 +377,30 @@ void Xilinx::program(unsigned int offset, bool unprotect_flash)
if (_mode == Device::MEM_MODE || _fpga_family == XCF_FAMILY) if (_mode == Device::MEM_MODE || _fpga_family == XCF_FAMILY)
reverse = true; reverse = true;
printInfo("Open file ", false);
try { try {
if (_file_extension == "bit") if (_flash_chips & PRIMARY_FLASH) {
bit = new BitParser(_filename, reverse, _verbose); open_bitfile(_filename, _file_extension, &bit, reverse, _verbose);
else if (_file_extension == "mcs") }
bit = new McsParser(_filename, reverse, _verbose); if (_flash_chips & SECONDARY_FLASH) {
else open_bitfile(_secondary_filename, _secondary_file_extension,
bit = new RawParser(_filename, reverse); &secondary_bit, reverse, _verbose);
}
} catch (std::exception &e) { } catch (std::exception &e) {
printError("FAIL"); printError("FAIL");
if (bit)
delete bit;
if (secondary_bit)
delete secondary_bit;
return; return;
} }
printSuccess("DONE"); if (_verbose) {
if (bit)
printInfo("Parse file ", false); bit->displayHeader();
if (bit->parse() == EXIT_FAILURE) { if (secondary_bit)
printError("FAIL"); secondary_bit->displayHeader();
delete bit;
return;
} else {
printSuccess("DONE");
} }
if (_verbose)
bit->displayHeader();
if (_fpga_family == XCF_FAMILY) { if (_fpga_family == XCF_FAMILY) {
xcf_program(bit); xcf_program(bit);
delete bit; delete bit;
@ -368,7 +408,17 @@ void Xilinx::program(unsigned int offset, bool unprotect_flash)
} }
if (_mode == Device::SPI_MODE) { if (_mode == Device::SPI_MODE) {
program_spi(bit, offset, unprotect_flash); if (_flash_chips & PRIMARY_FLASH) {
select_flash_chip(PRIMARY_FLASH);
program_spi(bit, offset, unprotect_flash);
}
if (_flash_chips & SECONDARY_FLASH) {
select_flash_chip(SECONDARY_FLASH);
program_spi(secondary_bit, offset, unprotect_flash);
}
reset();
} else { } else {
if (_fpga_family == SPARTAN3_FAMILY) if (_fpga_family == SPARTAN3_FAMILY)
xc3s_flow_program(bit); xc3s_flow_program(bit);
@ -570,8 +620,65 @@ bool Xilinx::dumpFlash(uint32_t base_addr, uint32_t len)
return true; return true;
} }
/* dump SPI Flash */ if (_flash_chips & PRIMARY_FLASH) {
return SPIInterface::dump(base_addr, len); select_flash_chip(PRIMARY_FLASH);
SPIInterface::set_filename(_filename);
if (!SPIInterface::dump(base_addr, len))
return false;
}
if (_flash_chips & SECONDARY_FLASH) {
select_flash_chip(SECONDARY_FLASH);
SPIInterface::set_filename(_secondary_filename);
if (!SPIInterface::dump(base_addr, len))
return false;
}
return true;
}
bool Xilinx::protect_flash(uint32_t len)
{
if (_flash_chips & PRIMARY_FLASH) {
select_flash_chip(PRIMARY_FLASH);
if (!SPIInterface::protect_flash(len))
return false;
}
if (_flash_chips & SECONDARY_FLASH) {
select_flash_chip(SECONDARY_FLASH);
if (!SPIInterface::protect_flash(len))
return false;
}
return true;
}
bool Xilinx::unprotect_flash()
{
if (_flash_chips & PRIMARY_FLASH) {
select_flash_chip(PRIMARY_FLASH);
if (!SPIInterface::unprotect_flash())
return false;
}
if (_flash_chips & SECONDARY_FLASH) {
select_flash_chip(SECONDARY_FLASH);
if (!SPIInterface::unprotect_flash())
return false;
}
return true;
}
bool Xilinx::bulk_erase_flash()
{
if (_flash_chips & PRIMARY_FLASH) {
select_flash_chip(PRIMARY_FLASH);
if (!SPIInterface::bulk_erase_flash())
return false;
}
if (_flash_chips & SECONDARY_FLASH) {
select_flash_chip(SECONDARY_FLASH);
if (!SPIInterface::bulk_erase_flash())
return false;
}
return true;
} }
/* flow program for xc3s (legacy mode) */ /* flow program for xc3s (legacy mode) */
@ -1460,3 +1567,15 @@ int Xilinx::spi_wait(uint8_t cmd, uint8_t mask, uint8_t cond,
return 0; return 0;
} }
} }
void Xilinx::select_flash_chip(xilinx_flash_chip_t flash_chip) {
switch (flash_chip) {
case SECONDARY_FLASH:
_user_instruction = "USER2";
break;
case PRIMARY_FLASH:
default:
_user_instruction = "USER1";
break;
}
}

View File

@ -12,14 +12,17 @@
#include "device.hpp" #include "device.hpp"
#include "jtag.hpp" #include "jtag.hpp"
#include "spiInterface.hpp" #include "spiInterface.hpp"
#include "jedParser.hpp"
class Xilinx: public Device, SPIInterface { class Xilinx: public Device, SPIInterface {
public: public:
Xilinx(Jtag *jtag, const std::string &filename, Xilinx(Jtag *jtag, const std::string &filename,
const std::string &secondary_filename,
const std::string &file_type, const std::string &file_type,
Device::prog_type_t prg_type, Device::prog_type_t prg_type,
const std::string &device_package, const std::string &device_package,
const std::string &spiOverJtagPath, const std::string &spiOverJtagPath,
const std::string &target_flash,
bool verify, int8_t verbose); bool verify, int8_t verbose);
~Xilinx(); ~Xilinx();
@ -32,21 +35,15 @@ class Xilinx: public Device, SPIInterface {
/*! /*!
* \brief protect SPI flash blocks * \brief protect SPI flash blocks
*/ */
bool protect_flash(uint32_t len) override { bool protect_flash(uint32_t len) override;
return SPIInterface::protect_flash(len);
}
/*! /*!
* \brief unprotect SPI flash blocks * \brief unprotect SPI flash blocks
*/ */
bool unprotect_flash() override { bool unprotect_flash() override;
return SPIInterface::unprotect_flash();
}
/*! /*!
* \brief erase SPI flash blocks * \brief erase SPI flash blocks
*/ */
bool bulk_erase_flash() override { bool bulk_erase_flash() override;
return SPIInterface::bulk_erase_flash();
}
int idCode() override; int idCode() override;
void reset() override; void reset() override;
@ -185,6 +182,20 @@ class Xilinx: public Device, SPIInterface {
* \return false if missing device mode, true otherwise * \return false if missing device mode, true otherwise
*/ */
bool load_bridge(); bool load_bridge();
enum xilinx_flash_chip_t {
PRIMARY_FLASH = 0x1,
SECONDARY_FLASH = 0x2
};
/*!
* \brief Starting from UltraScale, Xilinx devices can support dual
* QSPI flash configuration, with two different flash chips
* on the board. Target the selected one via the bridge by
* chaging the USER instruction to use.
*/
void select_flash_chip(xilinx_flash_chip_t flash_chip);
std::string _device_package; std::string _device_package;
std::string _spiOverJtagPath; /**< spiOverJtag explicit path */ std::string _spiOverJtagPath; /**< spiOverJtag explicit path */
int _xc95_line_len; /**< xc95 only: number of col by flash line */ int _xc95_line_len; /**< xc95 only: number of col by flash line */
@ -194,6 +205,10 @@ class Xilinx: public Device, SPIInterface {
char _cpld_base_name[7]; /**< cpld name (without package size) */ char _cpld_base_name[7]; /**< cpld name (without package size) */
int _irlen; /**< IR bit length */ int _irlen; /**< IR bit length */
std::map<std::string, std::vector<uint8_t>> _ircode_map; /**< bscan instructions based on model */ std::map<std::string, std::vector<uint8_t>> _ircode_map; /**< bscan instructions based on model */
std::string _filename; /* path to the primary flash file */
std::string _secondary_filename; /* path to the secondary flash file (SPIx8) */
std::string _secondary_file_extension; /* file type for the secondary flash file */
int _flash_chips; /* bitfield to select the target in boards with two flash chips */
std::string _user_instruction; /* which USER bscan instruction to interface with SPI */ std::string _user_instruction; /* which USER bscan instruction to interface with SPI */
}; };