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)
|
||||
--detect detect FPGA, add -f to show connected flash
|
||||
--dfu DFU mode
|
||||
--passive-serial USB-Blaster passive serial mode
|
||||
--dump-flash Dump flash mode
|
||||
--bulk-erase Bulk erase 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``
|
||||
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)
|
||||
=========================
|
||||
|
||||
|
|
|
|||
48
src/main.cpp
48
src/main.cpp
|
|
@ -57,6 +57,9 @@
|
|||
#include "progressBar.hpp"
|
||||
#include "spiFlash.hpp"
|
||||
#include "rawParser.hpp"
|
||||
#ifdef ENABLE_USBBLASTER
|
||||
#include "usbBlaster.hpp"
|
||||
#endif
|
||||
#ifdef ENABLE_XILINX_SUPPORT
|
||||
#include "xilinx.hpp"
|
||||
#endif
|
||||
|
|
@ -93,6 +96,7 @@ struct arguments {
|
|||
bool is_list_command;
|
||||
bool spi;
|
||||
bool dfu;
|
||||
bool passive_serial;
|
||||
std::string file_type;
|
||||
std::string fpga_part;
|
||||
std::string bridge_path;
|
||||
|
|
@ -157,8 +161,8 @@ int main(int argc, char **argv)
|
|||
false, false, false, false, false,
|
||||
0, "", "", "", "-", "", -1,
|
||||
-1, 0, false, "-", false, false, false, false, Device::PRG_NONE, false,
|
||||
/* spi dfu file_type fpga_part bridge_path probe_firmware */
|
||||
false, false, "", "", "", "",
|
||||
/* spi dfu passive_serial file_type fpga_part bridge_path probe_firmware */
|
||||
false, false, false, "", "", "", "",
|
||||
/* index_chain file_size target_flash external_flash spi_flash_type altsetting */
|
||||
-1, 0, "primary", false, true, -1,
|
||||
/* vid, pid, index bus_addr, device_addr */
|
||||
|
|
@ -381,6 +385,36 @@ int main(int argc, char **argv)
|
|||
}
|
||||
#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 */
|
||||
|
||||
|
||||
|
|
@ -941,6 +975,8 @@ int parse_opt(int argc, char **argv, struct arguments *args,
|
|||
("detect", "detect FPGA, add -f to show connected flash",
|
||||
cxxopts::value<bool>(args->detect))
|
||||
("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")
|
||||
("bulk-erase", "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"))
|
||||
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")) {
|
||||
double freq;
|
||||
if (parse_eng(freqo, &freq)) {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include <libusb.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
|
|
@ -41,7 +42,8 @@
|
|||
UsbBlaster::UsbBlaster(const cable_t &cable, const std::string &firmware_path,
|
||||
int8_t verbose):
|
||||
_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)
|
||||
#ifdef ENABLE_USB_BLASTERI
|
||||
|
|
@ -85,6 +87,7 @@ UsbBlaster::UsbBlaster(const cable_t &cable, const std::string &firmware_path,
|
|||
|
||||
UsbBlaster::~UsbBlaster()
|
||||
{
|
||||
if (!_passive_serial_mode)
|
||||
_in_buf[_nb_bit++] = 0;
|
||||
flush();
|
||||
free(_in_buf);
|
||||
|
|
@ -305,6 +308,123 @@ int UsbBlaster::flush()
|
|||
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
|
||||
*/
|
||||
int UsbBlaster::writeByte(uint8_t *tdo, int nb_byte)
|
||||
|
|
|
|||
|
|
@ -82,12 +82,24 @@ class UsbBlaster : public JtagInterface {
|
|||
|
||||
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:
|
||||
UsbBlaster_ll *ll_driver;
|
||||
int writeByte(uint8_t *tdo, int nb_byte);
|
||||
int writeBit(uint8_t *tdo, int nb_bit);
|
||||
int write(bool read, int rd_len);
|
||||
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;
|
||||
|
||||
int8_t _verbose;
|
||||
|
|
@ -97,6 +109,7 @@ class UsbBlaster : public JtagInterface {
|
|||
int _nb_bit;
|
||||
uint8_t _curr_tms;
|
||||
uint16_t _buffer_size;
|
||||
bool _passive_serial_mode;
|
||||
};
|
||||
|
||||
#ifdef ENABLE_USB_BLASTERI
|
||||
|
|
|
|||
Loading…
Reference in New Issue