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

View File

@ -49,17 +49,20 @@ 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
--secondary-bitstream arg
secondary bitstream (some Xilinx UltraScale
boards)
-b, --board arg board name, may be used instead of cable -b, --board arg board name, may be used instead of cable
-B, --bridge arg disable spiOverJtag model detection by providing -B, --bridge arg disable spiOverJtag model detection by
bitstream(intel/xilinx) providing bitstream(intel/xilinx)
-c, --cable arg jtag interface -c, --cable arg jtag interface
--invert-read-edge JTAG mode / FTDI: read on negative edge instead --invert-read-edge JTAG mode / FTDI: read on negative edge
of positive instead of positive
--vid arg probe Vendor ID --vid arg probe Vendor ID
--pid arg probe Product ID --pid arg probe Product ID
--cable-index arg probe index (FTDI and cmsisDAP) --cable-index arg probe index (FTDI and cmsisDAP)
@ -68,16 +71,19 @@ openFPGALoader -- a program to flash FPGA
--ftdi-serial arg FTDI chip serial number --ftdi-serial arg FTDI chip serial number
--ftdi-channel arg FTDI chip channel number (channels 0-3 map to --ftdi-channel arg FTDI chip channel number (channels 0-3 map to
A-D) A-D)
-d, --device arg device to use (/dev/ttyUSBx)
--detect detect FPGA --detect detect FPGA
--dfu DFU mode --dfu DFU mode
--dump-flash Dump flash mode --dump-flash Dump flash mode
--bulk-erase Bulk erase flash
--target-flash arg for boards with multiple flash chips (some
Xilinx UltraScale boards), select the target
flash: primary (default), secondary or both
--external-flash select ext flash for device with internal and --external-flash select ext flash for device with internal and
external storage external storage
--file-size arg provides size in Byte to dump, must be used with --file-size arg provides size in Byte to dump, must be used
dump-flash with dump-flash
--file-type arg provides file type instead of let's deduced by --file-type arg provides file type instead of let's deduced
using extension by using extension
--flash-sector arg flash sector (Lattice parts only) --flash-sector arg flash sector (Lattice parts only)
--fpga-part arg fpga model flavor + package --fpga-part arg fpga model flavor + package
--freq arg jtag frequency (Hz) --freq arg jtag frequency (Hz)
@ -95,18 +101,17 @@ openFPGALoader -- a program to flash FPGA
--quiet Produce quiet output (no progress bar) --quiet Produce quiet output (no progress bar)
-r, --reset reset FPGA after operations -r, --reset reset FPGA after operations
--scan-usb scan USB to display connected probes --scan-usb scan USB to display connected probes
--skip-load-bridge skip writing bridge to SRAM when in write-flash --skip-load-bridge skip writing bridge to SRAM when in
mode write-flash mode
--skip-reset skip resetting the device when in write-flash --skip-reset skip resetting the device when in write-flash
mode mode
--spi SPI mode (only for FTDI in serial mode) --spi SPI mode (only for FTDI in serial mode)
--unprotect-flash Unprotect flash blocks --unprotect-flash Unprotect flash blocks
-v, --verbose Produce verbose output -v, --verbose Produce verbose output
--verbose-level arg verbose level -1: quiet, 0: normal, 1:verbose, --verbose-level arg verbose level -1: quiet, 0: normal,
2:debug 1:verbose, 2:debug
-h, --help Give this help list -h, --help Give this help list
--verify Verify write operation (SPI Flash only) --verify Verify write operation (SPI Flash only)
--xvc Xilinx Virtual Cable Functions
--port arg Xilinx Virtual Cable Port (default 3721) --port arg Xilinx Virtual Cable Port (default 3721)
--mcufw arg Microcontroller firmware --mcufw arg Microcontroller firmware
--conmcu Connect JTAG to MCU --conmcu Connect JTAG to MCU

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,6 +729,10 @@ 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))
@ -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,32 +377,29 @@ 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");
return; if (bit)
}
printSuccess("DONE");
printInfo("Parse file ", false);
if (bit->parse() == EXIT_FAILURE) {
printError("FAIL");
delete bit; delete bit;
if (secondary_bit)
delete secondary_bit;
return; return;
} else {
printSuccess("DONE");
} }
if (_verbose) if (_verbose) {
if (bit)
bit->displayHeader(); bit->displayHeader();
if (secondary_bit)
secondary_bit->displayHeader();
}
if (_fpga_family == XCF_FAMILY) { if (_fpga_family == XCF_FAMILY) {
xcf_program(bit); xcf_program(bit);
@ -368,7 +408,17 @@ void Xilinx::program(unsigned int offset, bool unprotect_flash)
} }
if (_mode == Device::SPI_MODE) { if (_mode == Device::SPI_MODE) {
if (_flash_chips & PRIMARY_FLASH) {
select_flash_chip(PRIMARY_FLASH);
program_spi(bit, offset, unprotect_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 */
}; };