feat: add passive serial mode for usb blaster (altera/intel)

This commit is contained in:
Gonzalo Avila Alterach 2026-06-11 16:56:28 -03:00
parent bf8106ef5f
commit aab38dd51e
5 changed files with 195 additions and 4 deletions

View File

@ -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

13
doc/vendors/intel.rst vendored
View File

@ -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)
========================= =========================

View File

@ -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)) {

View File

@ -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)

View File

@ -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