feat: add passive serial mode for usb blaster (altera/intel)
This commit is contained in:
parent
bf8106ef5f
commit
aab38dd51e
|
|
@ -78,6 +78,7 @@ openFPGALoader -- a program to flash FPGA
|
||||||
-d, --device arg device to use (/dev/ttyUSBx)
|
-d, --device arg device to use (/dev/ttyUSBx)
|
||||||
--detect detect FPGA, add -f to show connected flash
|
--detect detect FPGA, add -f to show connected flash
|
||||||
--dfu DFU mode
|
--dfu DFU mode
|
||||||
|
--passive-serial USB-Blaster passive serial mode
|
||||||
--dump-flash Dump flash mode
|
--dump-flash Dump flash mode
|
||||||
--bulk-erase Bulk erase flash
|
--bulk-erase Bulk erase flash
|
||||||
--enable-quad Enable quad mode for SPI Flash
|
--enable-quad Enable quad mode for SPI Flash
|
||||||
|
|
|
||||||
|
|
@ -199,6 +199,19 @@ default behavior and user must explictly change this by using
|
||||||
* ``--flash-sector UFM1,CFM2`` to erase and update ``UFM1``, ``UFM0``
|
* ``--flash-sector UFM1,CFM2`` to erase and update ``UFM1``, ``UFM0``
|
||||||
and ``CFM2`` (equivalent to ``--flash-sector UFM1,UFM0,CFM2``)
|
and ``CFM2`` (equivalent to ``--flash-sector UFM1,UFM0,CFM2``)
|
||||||
|
|
||||||
|
USB-Blaster passive serial mode
|
||||||
|
-------------------------------
|
||||||
|
|
||||||
|
USB-Blaster I cables can load a raw RBF directly through the passive
|
||||||
|
serial pins:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
openFPGALoader -c usb-blaster --passive-serial project_name.rbf
|
||||||
|
|
||||||
|
The pin mapping is TCK to DCLK, TDO to CONF_DONE, TMS to nCONFIG, and TDI to
|
||||||
|
DATA0. RBF bytes are transmitted LSB first.
|
||||||
|
|
||||||
Intel/Altera (Old Boards)
|
Intel/Altera (Old Boards)
|
||||||
=========================
|
=========================
|
||||||
|
|
||||||
|
|
|
||||||
48
src/main.cpp
48
src/main.cpp
|
|
@ -57,6 +57,9 @@
|
||||||
#include "progressBar.hpp"
|
#include "progressBar.hpp"
|
||||||
#include "spiFlash.hpp"
|
#include "spiFlash.hpp"
|
||||||
#include "rawParser.hpp"
|
#include "rawParser.hpp"
|
||||||
|
#ifdef ENABLE_USBBLASTER
|
||||||
|
#include "usbBlaster.hpp"
|
||||||
|
#endif
|
||||||
#ifdef ENABLE_XILINX_SUPPORT
|
#ifdef ENABLE_XILINX_SUPPORT
|
||||||
#include "xilinx.hpp"
|
#include "xilinx.hpp"
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -93,6 +96,7 @@ struct arguments {
|
||||||
bool is_list_command;
|
bool is_list_command;
|
||||||
bool spi;
|
bool spi;
|
||||||
bool dfu;
|
bool dfu;
|
||||||
|
bool passive_serial;
|
||||||
std::string file_type;
|
std::string file_type;
|
||||||
std::string fpga_part;
|
std::string fpga_part;
|
||||||
std::string bridge_path;
|
std::string bridge_path;
|
||||||
|
|
@ -157,8 +161,8 @@ int main(int argc, char **argv)
|
||||||
false, false, false, false, false,
|
false, false, false, false, false,
|
||||||
0, "", "", "", "-", "", -1,
|
0, "", "", "", "-", "", -1,
|
||||||
-1, 0, false, "-", false, false, false, false, Device::PRG_NONE, false,
|
-1, 0, false, "-", false, false, false, false, Device::PRG_NONE, false,
|
||||||
/* spi dfu file_type fpga_part bridge_path probe_firmware */
|
/* spi dfu passive_serial file_type fpga_part bridge_path probe_firmware */
|
||||||
false, false, "", "", "", "",
|
false, false, false, "", "", "", "",
|
||||||
/* index_chain file_size target_flash external_flash spi_flash_type altsetting */
|
/* index_chain file_size target_flash external_flash spi_flash_type altsetting */
|
||||||
-1, 0, "primary", false, true, -1,
|
-1, 0, "primary", false, true, -1,
|
||||||
/* vid, pid, index bus_addr, device_addr */
|
/* vid, pid, index bus_addr, device_addr */
|
||||||
|
|
@ -381,6 +385,36 @@ int main(int argc, char **argv)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* ------------------------------ */
|
||||||
|
/* USB-Blaster passive serial mode */
|
||||||
|
/* ------------------------------ */
|
||||||
|
if (args.passive_serial) {
|
||||||
|
#ifdef ENABLE_USBBLASTER
|
||||||
|
if (cable.type != MODE_USBBLASTER) {
|
||||||
|
printError("Passive serial mode requires a USB-Blaster cable");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
RawParser bit(args.bit_file, false);
|
||||||
|
if (bit.parse() != EXIT_SUCCESS)
|
||||||
|
throw std::runtime_error("Failed to parse raw RBF file");
|
||||||
|
|
||||||
|
UsbBlaster blaster(cable, args.probe_firmware, args.verbose);
|
||||||
|
blaster.programPassiveSerial(bit.getData(), bit.getLength() / 8);
|
||||||
|
} catch (std::exception &e) {
|
||||||
|
printError("Passive serial programming failed: " + std::string(e.what()));
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
printSuccess("Passive serial programming DONE");
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
#else
|
||||||
|
printError("USB-Blaster support was not enabled at compile time");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/* jtag base */
|
/* jtag base */
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -941,6 +975,8 @@ int parse_opt(int argc, char **argv, struct arguments *args,
|
||||||
("detect", "detect FPGA, add -f to show connected flash",
|
("detect", "detect FPGA, add -f to show connected flash",
|
||||||
cxxopts::value<bool>(args->detect))
|
cxxopts::value<bool>(args->detect))
|
||||||
("dfu", "DFU mode", cxxopts::value<bool>(args->dfu))
|
("dfu", "DFU mode", cxxopts::value<bool>(args->dfu))
|
||||||
|
("passive-serial", "USB-Blaster passive serial mode",
|
||||||
|
cxxopts::value<bool>(args->passive_serial))
|
||||||
("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))
|
||||||
|
|
@ -1111,6 +1147,14 @@ int parse_opt(int argc, char **argv, struct arguments *args,
|
||||||
else if (result.count("external-flash"))
|
else if (result.count("external-flash"))
|
||||||
args->prg_type = Device::WR_FLASH;
|
args->prg_type = Device::WR_FLASH;
|
||||||
|
|
||||||
|
if (args->passive_serial) {
|
||||||
|
if (args->spi || args->dfu || args->xvc || args->detect) {
|
||||||
|
printError("Error: --passive-serial cannot be combined with "
|
||||||
|
"--spi, --dfu, --xvc or --detect");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (result.count("freq")) {
|
if (result.count("freq")) {
|
||||||
double freq;
|
double freq;
|
||||||
if (parse_eng(freqo, &freq)) {
|
if (parse_eng(freqo, &freq)) {
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
#include <libusb.h>
|
#include <libusb.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
@ -41,7 +42,8 @@
|
||||||
UsbBlaster::UsbBlaster(const cable_t &cable, const std::string &firmware_path,
|
UsbBlaster::UsbBlaster(const cable_t &cable, const std::string &firmware_path,
|
||||||
int8_t verbose):
|
int8_t verbose):
|
||||||
_verbose(verbose > 1), _nb_bit(0),
|
_verbose(verbose > 1), _nb_bit(0),
|
||||||
_curr_tms(0), _buffer_size(64)
|
_curr_tms(0), _buffer_size(64),
|
||||||
|
_passive_serial_mode(false)
|
||||||
{
|
{
|
||||||
if (cable.pid == 0x6001 || cable.pid == 0x6002 || cable.pid == 0x6003)
|
if (cable.pid == 0x6001 || cable.pid == 0x6002 || cable.pid == 0x6003)
|
||||||
#ifdef ENABLE_USB_BLASTERI
|
#ifdef ENABLE_USB_BLASTERI
|
||||||
|
|
@ -85,7 +87,8 @@ UsbBlaster::UsbBlaster(const cable_t &cable, const std::string &firmware_path,
|
||||||
|
|
||||||
UsbBlaster::~UsbBlaster()
|
UsbBlaster::~UsbBlaster()
|
||||||
{
|
{
|
||||||
_in_buf[_nb_bit++] = 0;
|
if (!_passive_serial_mode)
|
||||||
|
_in_buf[_nb_bit++] = 0;
|
||||||
flush();
|
flush();
|
||||||
free(_in_buf);
|
free(_in_buf);
|
||||||
}
|
}
|
||||||
|
|
@ -305,6 +308,123 @@ int UsbBlaster::flush()
|
||||||
return write(false, 0);
|
return write(false, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int UsbBlaster::setPassiveSerialPins(bool dclk, bool nconfig, bool data0)
|
||||||
|
{
|
||||||
|
uint8_t value = DEFAULT | DO_WRITE | DO_BITBB;
|
||||||
|
if (dclk)
|
||||||
|
value |= _tck_pin;
|
||||||
|
if (nconfig)
|
||||||
|
value |= _tms_pin;
|
||||||
|
if (data0)
|
||||||
|
value |= _tdi_pin;
|
||||||
|
|
||||||
|
return ll_driver->write(&value, 1, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UsbBlaster::readConfDone()
|
||||||
|
{
|
||||||
|
uint8_t value = DEFAULT | DO_READ | DO_BITBB | _tms_pin;
|
||||||
|
uint8_t input = 0;
|
||||||
|
int ret = ll_driver->write(&value, 1, &input, 1);
|
||||||
|
if (ret <= 0)
|
||||||
|
throw std::runtime_error("Failed to read CONF_DONE");
|
||||||
|
|
||||||
|
return (input & 0x01) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int UsbBlaster::shiftPassiveSerial(const uint8_t *data, uint32_t length)
|
||||||
|
{
|
||||||
|
uint8_t buffer[64];
|
||||||
|
uint32_t offset = 0;
|
||||||
|
|
||||||
|
while (offset < length) {
|
||||||
|
uint32_t chunk = length - offset;
|
||||||
|
if (chunk > 63)
|
||||||
|
chunk = 63;
|
||||||
|
|
||||||
|
buffer[0] = DO_SHIFT | DO_WRITE | static_cast<uint8_t>(chunk);
|
||||||
|
memcpy(&buffer[1], &data[offset], chunk);
|
||||||
|
int ret = ll_driver->write(buffer, static_cast<int>(chunk + 1), NULL, 0);
|
||||||
|
if (ret != static_cast<int>(chunk + 1))
|
||||||
|
return -1;
|
||||||
|
offset += chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
int UsbBlaster::clockPassiveSerial(uint32_t cycles)
|
||||||
|
{
|
||||||
|
uint8_t buffer[64];
|
||||||
|
uint8_t value = DEFAULT | DO_WRITE | DO_BITBB | _tms_pin;
|
||||||
|
uint32_t remaining = cycles;
|
||||||
|
|
||||||
|
while (remaining != 0) {
|
||||||
|
uint32_t chunk = remaining;
|
||||||
|
if (chunk > 31)
|
||||||
|
chunk = 31;
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < chunk; i++) {
|
||||||
|
buffer[2 * i] = value;
|
||||||
|
buffer[2 * i + 1] = value | _tck_pin;
|
||||||
|
}
|
||||||
|
buffer[2 * chunk] = value;
|
||||||
|
|
||||||
|
int length = static_cast<int>(2 * chunk + 1);
|
||||||
|
if (ll_driver->write(buffer, length, NULL, 0) != length)
|
||||||
|
return -1;
|
||||||
|
remaining -= chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
return static_cast<int>(cycles);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UsbBlaster::programPassiveSerial(const uint8_t *data, uint32_t length)
|
||||||
|
{
|
||||||
|
if (data == NULL || length == 0)
|
||||||
|
throw std::runtime_error("Passive serial bitstream is empty");
|
||||||
|
if (flush() < 0)
|
||||||
|
throw std::runtime_error("Failed to flush USB-Blaster");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The normal destructor command drives TMS low, which is nCONFIG in
|
||||||
|
* passive serial mode and would immediately reset the configured FPGA.
|
||||||
|
*/
|
||||||
|
_passive_serial_mode = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (setPassiveSerialPins(false, false, false) != 1)
|
||||||
|
throw std::runtime_error("Failed to assert nCONFIG");
|
||||||
|
usleep(5);
|
||||||
|
|
||||||
|
if (setPassiveSerialPins(false, true, false) != 1)
|
||||||
|
throw std::runtime_error("Failed to release nCONFIG");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* nSTATUS is not available. Assume device will be ready after a small delay.
|
||||||
|
*/
|
||||||
|
usleep(2000);
|
||||||
|
if (readConfDone())
|
||||||
|
throw std::runtime_error("CONF_DONE did not clear after nCONFIG");
|
||||||
|
|
||||||
|
if (shiftPassiveSerial(data, length) != static_cast<int>(length))
|
||||||
|
throw std::runtime_error("Failed to write passive serial data");
|
||||||
|
|
||||||
|
if (!readConfDone())
|
||||||
|
throw std::runtime_error("CONF_DONE is low after passive serial programming");
|
||||||
|
|
||||||
|
/* Complete device initialization after CONF_DONE is asserted. */
|
||||||
|
if (clockPassiveSerial(10) != 10)
|
||||||
|
throw std::runtime_error("Failed to write passive serial startup clocks");
|
||||||
|
} catch (...) {
|
||||||
|
setPassiveSerialPins(false, true, false);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setPassiveSerialPins(false, true, false) != 1)
|
||||||
|
throw std::runtime_error("Failed to leave passive serial pins idle");
|
||||||
|
}
|
||||||
|
|
||||||
/* simply call write and return buffer
|
/* simply call write and return buffer
|
||||||
*/
|
*/
|
||||||
int UsbBlaster::writeByte(uint8_t *tdo, int nb_byte)
|
int UsbBlaster::writeByte(uint8_t *tdo, int nb_byte)
|
||||||
|
|
|
||||||
|
|
@ -82,12 +82,24 @@ class UsbBlaster : public JtagInterface {
|
||||||
|
|
||||||
int flush() override;
|
int flush() override;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief configure an FPGA using the passive serial pin mapping
|
||||||
|
* TCK=DCLK, TDO=CONF_DONE, TMS=nCONFIG and TDI=DATA0
|
||||||
|
* \param data raw configuration bytes, shifted LSB first
|
||||||
|
* \param length number of bytes to send
|
||||||
|
*/
|
||||||
|
void programPassiveSerial(const uint8_t *data, uint32_t length);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
UsbBlaster_ll *ll_driver;
|
UsbBlaster_ll *ll_driver;
|
||||||
int writeByte(uint8_t *tdo, int nb_byte);
|
int writeByte(uint8_t *tdo, int nb_byte);
|
||||||
int writeBit(uint8_t *tdo, int nb_bit);
|
int writeBit(uint8_t *tdo, int nb_bit);
|
||||||
int write(bool read, int rd_len);
|
int write(bool read, int rd_len);
|
||||||
int setBitmode(uint8_t mode);
|
int setBitmode(uint8_t mode);
|
||||||
|
int setPassiveSerialPins(bool dclk, bool nconfig, bool data0);
|
||||||
|
bool readConfDone();
|
||||||
|
int shiftPassiveSerial(const uint8_t *data, uint32_t length);
|
||||||
|
int clockPassiveSerial(uint32_t cycles);
|
||||||
uint8_t *_in_buf;
|
uint8_t *_in_buf;
|
||||||
|
|
||||||
int8_t _verbose;
|
int8_t _verbose;
|
||||||
|
|
@ -97,6 +109,7 @@ class UsbBlaster : public JtagInterface {
|
||||||
int _nb_bit;
|
int _nb_bit;
|
||||||
uint8_t _curr_tms;
|
uint8_t _curr_tms;
|
||||||
uint16_t _buffer_size;
|
uint16_t _buffer_size;
|
||||||
|
bool _passive_serial_mode;
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef ENABLE_USB_BLASTERI
|
#ifdef ENABLE_USB_BLASTERI
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue