2019-09-26 18:29:20 +02:00
|
|
|
/*
|
|
|
|
|
* Copyright (C) 2019 Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
|
|
|
|
|
*
|
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
* (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
|
*/
|
2020-07-23 18:32:18 +02:00
|
|
|
#include "cxxopts.hpp"
|
2019-09-26 18:29:20 +02:00
|
|
|
#include <fstream>
|
2019-12-07 06:40:29 +01:00
|
|
|
#include <iomanip>
|
2019-09-26 18:29:20 +02:00
|
|
|
#include <iostream>
|
|
|
|
|
#include <map>
|
|
|
|
|
#include <sstream>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
2019-09-28 15:46:12 +02:00
|
|
|
#include "altera.hpp"
|
2020-08-20 16:58:20 +02:00
|
|
|
#include "anlogic.hpp"
|
2019-09-26 18:29:20 +02:00
|
|
|
#include "board.hpp"
|
|
|
|
|
#include "cable.hpp"
|
|
|
|
|
#include "device.hpp"
|
2019-12-07 06:40:29 +01:00
|
|
|
#include "display.hpp"
|
2020-10-06 08:38:24 +02:00
|
|
|
#include "ftdispi.hpp"
|
2019-12-06 07:29:28 +01:00
|
|
|
#include "gowin.hpp"
|
2019-11-18 16:04:50 +01:00
|
|
|
#include "lattice.hpp"
|
2020-03-06 09:05:57 +01:00
|
|
|
#include "jtag.hpp"
|
2019-09-28 15:46:12 +02:00
|
|
|
#include "part.hpp"
|
2020-10-06 08:38:24 +02:00
|
|
|
#include "spiFlash.hpp"
|
2020-10-07 07:47:46 +02:00
|
|
|
#include "rawParser.hpp"
|
2019-09-26 18:29:20 +02:00
|
|
|
#include "xilinx.hpp"
|
|
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
|
|
struct arguments {
|
2020-02-22 20:55:51 +01:00
|
|
|
bool verbose, reset, detect;
|
2019-09-26 18:29:20 +02:00
|
|
|
unsigned int offset;
|
|
|
|
|
string bit_file;
|
2019-11-19 09:02:24 +01:00
|
|
|
string device;
|
2019-09-26 18:29:20 +02:00
|
|
|
string cable;
|
2020-09-29 14:16:30 +02:00
|
|
|
string ftdi_serial;
|
2020-09-22 10:45:44 +02:00
|
|
|
int ftdi_channel;
|
2020-05-25 10:31:37 +02:00
|
|
|
uint32_t freq;
|
2019-09-26 18:29:20 +02:00
|
|
|
string board;
|
2020-08-04 17:36:33 +02:00
|
|
|
bool pin_config;
|
2019-12-07 06:40:29 +01:00
|
|
|
bool list_cables;
|
|
|
|
|
bool list_boards;
|
|
|
|
|
bool list_fpga;
|
2020-01-04 17:37:16 +01:00
|
|
|
bool write_flash;
|
|
|
|
|
bool write_sram;
|
2020-02-16 10:13:09 +01:00
|
|
|
bool is_list_command;
|
2020-10-06 08:38:24 +02:00
|
|
|
bool spi;
|
2019-09-26 18:29:20 +02:00
|
|
|
};
|
|
|
|
|
|
2020-08-04 17:36:33 +02:00
|
|
|
int parse_opt(int argc, char **argv, struct arguments *args, jtag_pins_conf_t *pins_config);
|
2019-12-07 06:40:29 +01:00
|
|
|
|
|
|
|
|
void displaySupported(const struct arguments &args);
|
2019-09-26 18:29:20 +02:00
|
|
|
|
|
|
|
|
int main(int argc, char **argv)
|
|
|
|
|
{
|
2020-03-07 10:56:34 +01:00
|
|
|
cable_t cable;
|
2020-08-04 17:36:33 +02:00
|
|
|
jtag_pins_conf_t pins_config = {0, 0, 0, 0};
|
2019-09-26 18:29:20 +02:00
|
|
|
|
|
|
|
|
/* command line args. */
|
2020-10-07 07:37:40 +02:00
|
|
|
struct arguments args = {false, false, false, 0, "", "", "-", "", -1, 6000000, "-",
|
2020-10-06 08:38:24 +02:00
|
|
|
false, false, false, false, false, true, false, false};
|
2019-09-26 18:29:20 +02:00
|
|
|
/* parse arguments */
|
2020-07-23 18:32:18 +02:00
|
|
|
try {
|
2020-08-04 17:36:33 +02:00
|
|
|
if (parse_opt(argc, argv, &args, &pins_config))
|
2020-07-23 18:32:18 +02:00
|
|
|
return EXIT_SUCCESS;
|
|
|
|
|
} catch (std::exception &e) {
|
|
|
|
|
printError("Error in parse arg step");
|
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
|
}
|
2019-09-26 18:29:20 +02:00
|
|
|
|
2020-02-16 10:13:09 +01:00
|
|
|
if (args.is_list_command) {
|
2019-12-07 06:40:29 +01:00
|
|
|
displaySupported(args);
|
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-26 18:29:20 +02:00
|
|
|
/* if a board name is specified try to use this to determine cable */
|
|
|
|
|
if (args.board[0] != '-' && board_list.find(args.board) != board_list.end()) {
|
2020-08-04 17:36:33 +02:00
|
|
|
/* set pins config (only when user has not already provided
|
|
|
|
|
* configuration
|
|
|
|
|
*/
|
|
|
|
|
if (!args.pin_config) {
|
|
|
|
|
pins_config.tdi_pin = board_list[args.board].pins_config.tdi_pin;
|
|
|
|
|
pins_config.tdo_pin = board_list[args.board].pins_config.tdo_pin;
|
|
|
|
|
pins_config.tms_pin = board_list[args.board].pins_config.tms_pin;
|
|
|
|
|
pins_config.tck_pin = board_list[args.board].pins_config.tck_pin;
|
|
|
|
|
}
|
2020-03-07 11:44:17 +01:00
|
|
|
/* search for cable */
|
2020-03-07 11:00:29 +01:00
|
|
|
auto t = cable_list.find(board_list[args.board].cable_name);
|
2019-09-26 18:29:20 +02:00
|
|
|
if (t == cable_list.end()) {
|
2020-02-01 18:07:42 +01:00
|
|
|
cout << "Board " << args.board << " has not default cable" << endl;
|
2020-10-21 13:41:26 +02:00
|
|
|
} else {
|
|
|
|
|
if (args.cable[0] == '-') { // no use selection
|
|
|
|
|
args.cable = (*t).first; // use board default cable
|
|
|
|
|
} else {
|
|
|
|
|
cout << "Board default cable overridden with " << args.cable << endl;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-02-01 18:07:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (args.cable[0] == '-') { /* if no board and no cable */
|
2019-11-21 08:39:27 +01:00
|
|
|
if (args.verbose)
|
|
|
|
|
cout << "No cable or board specified: using direct ft2232 interface" << endl;
|
2019-09-26 18:29:20 +02:00
|
|
|
args.cable = "ft2232";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto select_cable = cable_list.find(args.cable);
|
|
|
|
|
if (select_cable == cable_list.end()) {
|
2020-07-11 07:47:27 +02:00
|
|
|
printError("error : " + args.cable + " not found");
|
2019-09-26 18:29:20 +02:00
|
|
|
return EXIT_FAILURE;
|
|
|
|
|
}
|
|
|
|
|
cable = select_cable->second;
|
|
|
|
|
|
2020-09-22 10:45:44 +02:00
|
|
|
if (args.ftdi_channel != -1) {
|
2020-09-22 18:22:51 +02:00
|
|
|
if (cable.type != MODE_FTDI_SERIAL && cable.type != MODE_FTDI_BITBANG){
|
|
|
|
|
printError("Error: FTDI channel param is for FTDI cables.");
|
2020-09-22 10:45:44 +02:00
|
|
|
return EXIT_FAILURE;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-22 18:22:51 +02:00
|
|
|
int mapping[] = {INTERFACE_A, INTERFACE_B, INTERFACE_C, INTERFACE_D};
|
|
|
|
|
cable.config.interface = mapping[args.ftdi_channel];
|
2020-09-22 10:45:44 +02:00
|
|
|
}
|
|
|
|
|
|
2020-10-07 07:37:40 +02:00
|
|
|
if (!args.ftdi_serial.empty()) {
|
2020-09-29 14:16:30 +02:00
|
|
|
if (cable.type != MODE_FTDI_SERIAL && cable.type != MODE_FTDI_BITBANG){
|
|
|
|
|
printError("Error: FTDI serial param is for FTDI cables.");
|
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-07 07:47:46 +02:00
|
|
|
/* FLASH direct access */
|
|
|
|
|
if (args.spi) {
|
|
|
|
|
FtdiSpi *spi = NULL;
|
|
|
|
|
RawParser *bit = NULL;
|
|
|
|
|
|
|
|
|
|
try {
|
2020-10-30 08:24:45 +01:00
|
|
|
spi = new FtdiSpi(cable.config, {}, args.freq, args.verbose);
|
2020-10-07 07:47:46 +02:00
|
|
|
} catch (std::exception &e) {
|
|
|
|
|
printError("Error: Failed to claim cable");
|
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SPIFlash flash((SPIInterface *)spi, args.verbose);
|
|
|
|
|
flash.power_up();
|
|
|
|
|
flash.reset();
|
|
|
|
|
flash.read_id();
|
|
|
|
|
|
|
|
|
|
if (!args.bit_file.empty()) {
|
|
|
|
|
printInfo("Open file " + args.bit_file + " ", false);
|
|
|
|
|
try {
|
|
|
|
|
bit = new RawParser(args.bit_file, false);
|
|
|
|
|
printSuccess("DONE");
|
|
|
|
|
} catch (std::exception &e) {
|
|
|
|
|
printError("FAIL");
|
|
|
|
|
delete spi;
|
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
printInfo("Parse file ", false);
|
|
|
|
|
if (bit->parse() == EXIT_FAILURE) {
|
|
|
|
|
printError("FAIL");
|
|
|
|
|
delete spi;
|
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
|
} else {
|
|
|
|
|
printSuccess("DONE");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
flash.erase_and_prog(args.offset, bit->getData(), bit->getLength()/8);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
delete bit;
|
|
|
|
|
delete spi;
|
|
|
|
|
|
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-26 18:29:20 +02:00
|
|
|
/* jtag base */
|
2020-03-06 09:05:57 +01:00
|
|
|
Jtag *jtag;
|
2020-07-11 07:23:24 +02:00
|
|
|
try {
|
2020-09-29 14:16:30 +02:00
|
|
|
jtag = new Jtag(cable, &pins_config, args.device, args.ftdi_serial, args.freq, false);
|
2020-07-11 07:23:24 +02:00
|
|
|
} catch (std::exception &e) {
|
|
|
|
|
printError("Error: Failed to claim cable");
|
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
|
}
|
2019-09-26 18:29:20 +02:00
|
|
|
|
2019-09-28 15:46:12 +02:00
|
|
|
/* chain detection */
|
2019-09-26 18:29:20 +02:00
|
|
|
vector<int> listDev;
|
|
|
|
|
int found = jtag->detectChain(listDev, 5);
|
2019-09-28 15:46:12 +02:00
|
|
|
|
2019-11-21 08:39:27 +01:00
|
|
|
if (args.verbose)
|
|
|
|
|
cout << "found " << std::to_string(found) << " devices" << endl;
|
2019-09-26 18:29:20 +02:00
|
|
|
if (found > 1) {
|
2020-07-11 07:47:27 +02:00
|
|
|
printError("Error: currently only one device is supported");
|
2020-08-19 15:15:37 +02:00
|
|
|
delete(jtag);
|
2019-09-28 15:46:12 +02:00
|
|
|
return EXIT_FAILURE;
|
|
|
|
|
} else if (found < 1) {
|
2020-07-11 07:47:27 +02:00
|
|
|
printError("Error: no device found");
|
2020-08-19 15:15:37 +02:00
|
|
|
delete(jtag);
|
2019-09-26 18:29:20 +02:00
|
|
|
return EXIT_FAILURE;
|
|
|
|
|
}
|
2019-09-28 15:46:12 +02:00
|
|
|
|
|
|
|
|
int idcode = listDev[0];
|
|
|
|
|
|
2019-09-26 18:29:20 +02:00
|
|
|
if (fpga_list.find(idcode) == fpga_list.end()) {
|
2019-12-06 07:29:28 +01:00
|
|
|
cerr << "Error: device " << hex << idcode << " not supported" << endl;
|
2020-08-20 16:58:20 +02:00
|
|
|
delete(jtag);
|
2020-07-11 07:47:27 +02:00
|
|
|
return EXIT_FAILURE;
|
2020-02-22 20:55:51 +01:00
|
|
|
} else if (args.verbose || args.detect) {
|
2019-12-06 19:59:35 +01:00
|
|
|
printf("idcode 0x%x\nmanufacturer %s\nmodel %s\nfamily %s\n",
|
2019-09-26 18:29:20 +02:00
|
|
|
idcode,
|
2019-12-06 19:59:35 +01:00
|
|
|
fpga_list[idcode].manufacturer.c_str(),
|
2019-09-26 18:29:20 +02:00
|
|
|
fpga_list[idcode].model.c_str(),
|
|
|
|
|
fpga_list[idcode].family.c_str());
|
|
|
|
|
}
|
2020-02-22 20:55:51 +01:00
|
|
|
|
|
|
|
|
if (args.detect == true) {
|
|
|
|
|
delete jtag;
|
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-06 19:59:35 +01:00
|
|
|
string fab = fpga_list[idcode].manufacturer;
|
2019-09-26 18:29:20 +02:00
|
|
|
|
2019-09-28 15:46:12 +02:00
|
|
|
Device *fpga;
|
|
|
|
|
if (fab == "xilinx") {
|
2020-09-25 18:58:31 +02:00
|
|
|
fpga = new Xilinx(jtag, args.bit_file, args.write_flash, args.write_sram,
|
|
|
|
|
args.verbose);
|
2019-11-18 16:04:50 +01:00
|
|
|
} else if (fab == "altera") {
|
2019-11-21 09:27:24 +01:00
|
|
|
fpga = new Altera(jtag, args.bit_file, args.verbose);
|
2020-08-20 16:58:20 +02:00
|
|
|
} else if (fab == "anlogic") {
|
2020-08-24 08:55:27 +02:00
|
|
|
fpga = new Anlogic(jtag, args.bit_file, args.write_flash, args.write_sram,
|
|
|
|
|
args.verbose);
|
2019-12-27 14:00:30 +01:00
|
|
|
} else if (fab == "Gowin") {
|
2020-01-04 17:37:16 +01:00
|
|
|
fpga = new Gowin(jtag, args.bit_file, args.write_flash, args.write_sram,
|
|
|
|
|
args.verbose);
|
2019-12-27 14:00:30 +01:00
|
|
|
} else if (fab == "lattice") {
|
2020-04-22 15:23:38 +02:00
|
|
|
fpga = new Lattice(jtag, args.bit_file, args.write_flash, args.write_sram,
|
|
|
|
|
args.verbose);
|
2019-12-27 14:00:30 +01:00
|
|
|
} else {
|
2020-07-11 07:47:27 +02:00
|
|
|
printError("Error: manufacturer " + fab + " not supported");
|
2019-12-27 14:00:30 +01:00
|
|
|
delete(jtag);
|
|
|
|
|
return EXIT_FAILURE;
|
2019-09-26 18:29:20 +02:00
|
|
|
}
|
|
|
|
|
|
2020-02-16 16:55:37 +01:00
|
|
|
if (!args.bit_file.empty()) {
|
|
|
|
|
try {
|
|
|
|
|
fpga->program(args.offset);
|
|
|
|
|
} catch (std::exception &e) {
|
|
|
|
|
delete(fpga);
|
|
|
|
|
delete(jtag);
|
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-09-28 15:46:12 +02:00
|
|
|
|
|
|
|
|
if (args.reset)
|
|
|
|
|
fpga->reset();
|
|
|
|
|
|
|
|
|
|
delete(fpga);
|
2019-11-19 09:03:09 +01:00
|
|
|
delete(jtag);
|
2019-09-26 18:29:20 +02:00
|
|
|
}
|
|
|
|
|
|
2020-07-25 04:55:41 +02:00
|
|
|
// parse double from string in engineering notation
|
2020-05-25 10:31:37 +02:00
|
|
|
// can deal with postfixes k and m, add more when required
|
2020-07-25 04:55:41 +02:00
|
|
|
static int parse_eng(string arg, double *dst) {
|
2020-05-25 10:31:37 +02:00
|
|
|
try {
|
|
|
|
|
size_t end;
|
|
|
|
|
double base = stod(arg, &end);
|
|
|
|
|
if (end == arg.size()) {
|
|
|
|
|
*dst = base;
|
|
|
|
|
return 0;
|
|
|
|
|
} else if (end == (arg.size() - 1)) {
|
|
|
|
|
switch (arg.back()) {
|
|
|
|
|
case 'k': case 'K':
|
|
|
|
|
*dst = (uint32_t)(1e3 * base);
|
|
|
|
|
return 0;
|
|
|
|
|
case 'm': case 'M':
|
|
|
|
|
*dst = (uint32_t)(1e6 * base);
|
|
|
|
|
return 0;
|
|
|
|
|
default:
|
|
|
|
|
return EINVAL;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
return EINVAL;
|
|
|
|
|
}
|
|
|
|
|
} catch (...) {
|
2020-07-25 04:55:41 +02:00
|
|
|
cerr << "error : speed: invalid format" << endl;
|
2020-05-25 10:31:37 +02:00
|
|
|
return EINVAL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-26 18:29:20 +02:00
|
|
|
/* arguments parser */
|
2020-08-04 17:36:33 +02:00
|
|
|
int parse_opt(int argc, char **argv, struct arguments *args, jtag_pins_conf_t *pins_config)
|
2019-09-26 18:29:20 +02:00
|
|
|
{
|
2020-07-23 18:32:18 +02:00
|
|
|
|
|
|
|
|
string freqo;
|
2020-08-04 17:36:33 +02:00
|
|
|
vector<string> pins;
|
2020-07-23 18:32:18 +02:00
|
|
|
try {
|
|
|
|
|
cxxopts::Options options(argv[0], "openFPGALoader -- a program to flash FPGA",
|
|
|
|
|
"<gwenhael.goavec-merou@trabucayre.com>");
|
|
|
|
|
options
|
|
|
|
|
.positional_help("BIT_FILE")
|
|
|
|
|
.show_positional_help();
|
|
|
|
|
|
|
|
|
|
options
|
|
|
|
|
.add_options()
|
|
|
|
|
("bitstream", "bitstream",
|
|
|
|
|
cxxopts::value<std::string>(args->bit_file))
|
|
|
|
|
("b,board", "board name, may be used instead of cable",
|
|
|
|
|
cxxopts::value<string>(args->board))
|
|
|
|
|
("c,cable", "jtag interface", cxxopts::value<string>(args->cable))
|
2020-09-29 14:16:30 +02:00
|
|
|
("ftdi-serial", "FTDI chip serial number", cxxopts::value<string>(args->ftdi_serial))
|
2020-09-22 18:22:51 +02:00
|
|
|
("ftdi-channel", "FTDI chip channel number (channels 0-3 map to A-D)", cxxopts::value<int>(args->ftdi_channel))
|
2020-03-14 19:42:07 +01:00
|
|
|
#ifdef USE_UDEV
|
2020-07-23 18:32:18 +02:00
|
|
|
("d,device", "device to use (/dev/ttyUSBx)",
|
|
|
|
|
cxxopts::value<string>(args->device))
|
2020-03-14 19:42:07 +01:00
|
|
|
#endif
|
2020-07-23 18:32:18 +02:00
|
|
|
("detect", "detect FPGA",
|
|
|
|
|
cxxopts::value<bool>(args->detect))
|
|
|
|
|
("freq", "jtag frequency (Hz)", cxxopts::value<string>(freqo))
|
|
|
|
|
("f,write-flash",
|
|
|
|
|
"write bitstream in flash (default: false, only for Gowin and ECP5 devices)")
|
|
|
|
|
("list-boards", "list all supported boards",
|
|
|
|
|
cxxopts::value<bool>(args->list_boards))
|
|
|
|
|
("list-cables", "list all supported cables",
|
|
|
|
|
cxxopts::value<bool>(args->list_cables))
|
|
|
|
|
("list-fpga", "list all supported FPGA",
|
|
|
|
|
cxxopts::value<bool>(args->list_fpga))
|
|
|
|
|
("m,write-sram",
|
|
|
|
|
"write bitstream in SRAM (default: true, only for Gowin and ECP5 devices)")
|
|
|
|
|
("o,offset", "start offset in EEPROM",
|
|
|
|
|
cxxopts::value<unsigned int>(args->offset))
|
2020-08-04 17:36:33 +02:00
|
|
|
("pins", "pin config (only for ft232R) TDI:TDO:TCK:TMS",
|
|
|
|
|
cxxopts::value<vector<string>>(pins))
|
2020-07-23 18:32:18 +02:00
|
|
|
("r,reset", "reset FPGA after operations",
|
|
|
|
|
cxxopts::value<bool>(args->reset))
|
2020-10-06 08:38:24 +02:00
|
|
|
("spi", "SPI mode (only for FTDI in serial mode)",
|
|
|
|
|
cxxopts::value<bool>(args->spi))
|
2020-07-23 18:32:18 +02:00
|
|
|
("v,verbose", "Produce verbose output", cxxopts::value<bool>(args->verbose))
|
|
|
|
|
("h,help", "Give this help list")
|
|
|
|
|
("V,Version", "Print program version");
|
|
|
|
|
|
|
|
|
|
options.parse_positional({"bitstream"});
|
|
|
|
|
auto result = options.parse(argc, argv);
|
|
|
|
|
|
|
|
|
|
if (result.count("help")) {
|
|
|
|
|
cout << options.help() << endl;
|
|
|
|
|
return 1;
|
2020-05-25 10:31:37 +02:00
|
|
|
}
|
2020-07-23 18:32:18 +02:00
|
|
|
|
|
|
|
|
if (result.count("Version")) {
|
|
|
|
|
cout << "openFPGALoader v0.1" << endl;
|
|
|
|
|
return 1;
|
2020-05-25 10:31:37 +02:00
|
|
|
}
|
2020-07-23 18:32:18 +02:00
|
|
|
|
|
|
|
|
if (result.count("write-flash") && result.count("write-sram")) {
|
|
|
|
|
printError("Error: both write to flash and write to ram enabled");
|
|
|
|
|
throw std::exception();
|
2020-02-16 10:13:09 +01:00
|
|
|
}
|
2020-07-23 18:32:18 +02:00
|
|
|
|
|
|
|
|
if (result.count("write-flash")) {
|
|
|
|
|
args->write_flash = true;
|
|
|
|
|
args->write_sram = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (result.count("write-sram")) {
|
|
|
|
|
args->write_flash = false;
|
|
|
|
|
args->write_sram = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (result.count("freq")) {
|
|
|
|
|
double freq;
|
|
|
|
|
if (parse_eng(freqo, &freq)) {
|
|
|
|
|
printError("Error: invalid format for --freq");
|
|
|
|
|
throw std::exception();
|
|
|
|
|
}
|
|
|
|
|
if (freq < 1) {
|
|
|
|
|
printError("Error: --freq must be positive");
|
|
|
|
|
throw std::exception();
|
|
|
|
|
}
|
|
|
|
|
args->freq = static_cast<uint32_t>(freq);
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-22 18:22:51 +02:00
|
|
|
if (result.count("ftdi-channel")) {
|
|
|
|
|
if (args->ftdi_channel < 0 || args->ftdi_channel > 3) {
|
|
|
|
|
printError("Error: valid FTDI channels are 0-3.");
|
|
|
|
|
throw std::exception();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-04 17:36:33 +02:00
|
|
|
if (result.count("pins")) {
|
|
|
|
|
if (pins.size() != 4) {
|
|
|
|
|
printError("Error: pin_config need 4 pins");
|
|
|
|
|
throw std::exception();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static std::map <std::string, int> pins_list = {
|
|
|
|
|
{"TXD", FT232RL_TXD},
|
|
|
|
|
{"RXD", FT232RL_RXD},
|
|
|
|
|
{"RTS", FT232RL_RTS},
|
|
|
|
|
{"CTS", FT232RL_CTS},
|
|
|
|
|
{"DTR", FT232RL_DTR},
|
|
|
|
|
{"DSR", FT232RL_DSR},
|
|
|
|
|
{"DCD", FT232RL_DCD},
|
|
|
|
|
{"RI" , FT232RL_RI }};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
|
|
|
int pin_num;
|
|
|
|
|
try {
|
|
|
|
|
pin_num = std::stoi(pins[i], nullptr, 0);
|
|
|
|
|
} catch (std::exception &e) {
|
|
|
|
|
if (pins_list.find(pins[i]) == pins_list.end()) {
|
|
|
|
|
printError("Invalid pin name");
|
|
|
|
|
throw std::exception();
|
|
|
|
|
}
|
|
|
|
|
pin_num = pins_list[pins[i]];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pin_num > 7 || pin_num < 0) {
|
|
|
|
|
printError("Invalid pin ID");
|
|
|
|
|
throw std::exception();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (i) {
|
|
|
|
|
case 0:
|
|
|
|
|
pins_config->tdi_pin = pin_num;
|
|
|
|
|
break;
|
|
|
|
|
case 1:
|
|
|
|
|
pins_config->tdo_pin = pin_num;
|
|
|
|
|
break;
|
|
|
|
|
case 2:
|
|
|
|
|
pins_config->tck_pin = pin_num;
|
|
|
|
|
break;
|
|
|
|
|
case 3:
|
|
|
|
|
pins_config->tms_pin = pin_num;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
args->pin_config = true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-23 18:32:18 +02:00
|
|
|
if (args->list_cables || args->list_boards || args->list_fpga)
|
|
|
|
|
args->is_list_command = true;
|
|
|
|
|
|
|
|
|
|
if (args->bit_file.empty() &&
|
|
|
|
|
!args->is_list_command &&
|
|
|
|
|
!args->detect &&
|
|
|
|
|
!args->reset) {
|
|
|
|
|
printError("Error: bitfile not specified");
|
|
|
|
|
cout << options.help() << endl;
|
|
|
|
|
throw std::exception();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} catch (const cxxopts::OptionException& e) {
|
|
|
|
|
cerr << "Error parsing options: " << e.what() << endl;
|
|
|
|
|
throw std::exception();
|
2019-09-26 18:29:20 +02:00
|
|
|
}
|
2020-07-23 18:32:18 +02:00
|
|
|
|
2019-09-26 18:29:20 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-07 06:40:29 +01:00
|
|
|
/* display list of cables, boards and devices supported */
|
|
|
|
|
void displaySupported(const struct arguments &args)
|
|
|
|
|
{
|
|
|
|
|
if (args.list_cables == true) {
|
|
|
|
|
stringstream t;
|
|
|
|
|
t << setw(15) << left << "cable name:" << "vid:pid";
|
|
|
|
|
printSuccess(t.str());
|
|
|
|
|
for (auto b = cable_list.begin(); b != cable_list.end(); b++) {
|
2020-03-07 10:56:34 +01:00
|
|
|
FTDIpp_MPSSE::mpsse_bit_config c = (*b).second.config;
|
2019-12-07 06:40:29 +01:00
|
|
|
stringstream ss;
|
|
|
|
|
ss << setw(15) << left << (*b).first;
|
|
|
|
|
ss << "0x" << hex << c.vid << ":" << c.pid;
|
|
|
|
|
printInfo(ss.str());
|
|
|
|
|
}
|
|
|
|
|
cout << endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (args.list_boards) {
|
|
|
|
|
stringstream t;
|
|
|
|
|
t << setw(15) << left << "board name:" << "cable_name";
|
|
|
|
|
printSuccess(t.str());
|
|
|
|
|
for (auto b = board_list.begin(); b != board_list.end(); b++) {
|
|
|
|
|
stringstream ss;
|
2020-03-07 11:00:29 +01:00
|
|
|
target_cable_t c = (*b).second;
|
|
|
|
|
ss << setw(15) << left << (*b).first << " " << c.cable_name;
|
2019-12-07 06:40:29 +01:00
|
|
|
printInfo(ss.str());
|
|
|
|
|
}
|
|
|
|
|
cout << endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (args.list_fpga) {
|
|
|
|
|
stringstream t;
|
|
|
|
|
t << setw(12) << left << "IDCode" << setw(14) << "manufacturer";
|
|
|
|
|
t << setw(15) << "family" << setw(20) << "model";
|
|
|
|
|
printSuccess(t.str());
|
|
|
|
|
for (auto b = fpga_list.begin(); b != fpga_list.end(); b++) {
|
|
|
|
|
fpga_model fpga = (*b).second;
|
|
|
|
|
stringstream ss, idCode;
|
|
|
|
|
idCode << "0x" << hex << (*b).first;
|
|
|
|
|
ss << setw(12) << left << idCode.str();
|
|
|
|
|
ss << setw(14) << fpga.manufacturer << setw(15) << fpga.family;
|
|
|
|
|
ss << setw(20) << fpga.model;
|
|
|
|
|
printInfo(ss.str());
|
|
|
|
|
}
|
|
|
|
|
cout << endl;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-02-16 10:13:09 +01:00
|
|
|
|