diff --git a/README.md b/README.md index 6cedaf4..553349e 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,7 @@ __Supported cables:__ * [DirtyJTAG](https://github.com/jeanthom/DirtyJTAG): JTAG probe firmware for STM32F1 * JTAG-HS3: jtag programmer cable from digilent * FT2232: generic programmer cable based on Ftdi FT2232 +* FT232RL and FT231X: generic USB<->UART converters in bitbang mode * Tang Nano USB-JTAG interface: FT2232C clone based on CH552 microcontroler (with some limitations and workaround) @@ -110,6 +111,7 @@ openFPGALoader -- a program to flash cyclone10 LP FPGA -m, --write-sram write bitstream in SRAM (default: true, only for Gowin and ECP5 devices) -o, --offset=OFFSET start offset in EEPROM + --pins arg pin config (only for bitbang) TDI:TDO:TCK:TMS -r, --reset reset FPGA after operations -v, --verbose Produce verbose output -h, --help Give this help list @@ -157,6 +159,33 @@ openFPGALoader [options] -r openFPGALoader [options] /path/to/bitstream.ext ``` +#### bitbang mode and pins configuration + +*FT232R* and *ft231X* may be used as JTAG programmer. JTAG communications are +emulated in bitbang mode. + +To use these devices user needs to provides both the cable and the pin mapping: +```bash +openFPGALoader [options] -cft23XXX --pins=TDI:TDO:TCK:TMS /path/to/bitstream.ext +``` +where: +* ft23XXX may be **ft232RL** or **ft231X** +* TDI:TDO:TCK:TMS may be the pin ID (0 <= id <= 7) or string value + +allowed values are: + +| value | ID | +|-------|----| +| TXD | 0 | +| RXD | 1 | +| RTS | 2 | +| CTS | 3 | +| DTR | 4 | +| DSR | 5 | +| DCD | 6 | +| RI | 7 | + + ### CYC1000 #### loading in memory: diff --git a/src/cxxopts.hpp b/src/cxxopts.hpp index 52ebb6a..098617b 100644 --- a/src/cxxopts.hpp +++ b/src/cxxopts.hpp @@ -46,7 +46,7 @@ THE SOFTWARE. #endif #ifndef CXXOPTS_VECTOR_DELIMITER -#define CXXOPTS_VECTOR_DELIMITER ',' +#define CXXOPTS_VECTOR_DELIMITER ':' #endif #define CXXOPTS__VERSION_MAJOR 2 diff --git a/src/main.cpp b/src/main.cpp index e97f3ae..662ffed 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -45,6 +45,7 @@ struct arguments { string cable; uint32_t freq; string board; + bool pin_config; bool list_cables; bool list_boards; bool list_fpga; @@ -53,21 +54,21 @@ struct arguments { bool is_list_command; }; -int parse_opt(int argc, char **argv, struct arguments *args); +int parse_opt(int argc, char **argv, struct arguments *args, jtag_pins_conf_t *pins_config); void displaySupported(const struct arguments &args); int main(int argc, char **argv) { cable_t cable; - jtag_pins_conf_t *pins_config = NULL; + jtag_pins_conf_t pins_config = {0, 0, 0, 0}; /* command line args. */ struct arguments args = {false, false, false, 0, "", "-", "-", 6000000, "-", - false, false, false, false, true, false}; + false, false, false, false, false, true, false}; /* parse arguments */ try { - if (parse_opt(argc, argv, &args)) + if (parse_opt(argc, argv, &args, &pins_config)) return EXIT_SUCCESS; } catch (std::exception &e) { printError("Error in parse arg step"); @@ -81,8 +82,15 @@ int main(int argc, char **argv) /* 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()) { - /* set pins config */ - pins_config = &board_list[args.board].pins_config; + /* 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; + } /* search for cable */ auto t = cable_list.find(board_list[args.board].cable_name); if (t == cable_list.end()) { @@ -109,9 +117,9 @@ int main(int argc, char **argv) Jtag *jtag; try { if (args.device == "-") - jtag = new Jtag(cable, pins_config, args.freq, false); + jtag = new Jtag(cable, &pins_config, args.freq, false); else - jtag = new Jtag(cable, pins_config, args.device, args.freq, false); + jtag = new Jtag(cable, &pins_config, args.device, args.freq, false); } catch (std::exception &e) { printError("Error: Failed to claim cable"); return EXIT_FAILURE; @@ -215,10 +223,11 @@ static int parse_eng(string arg, double *dst) { } /* arguments parser */ -int parse_opt(int argc, char **argv, struct arguments *args) +int parse_opt(int argc, char **argv, struct arguments *args, jtag_pins_conf_t *pins_config) { string freqo; + vector pins; try { cxxopts::Options options(argv[0], "openFPGALoader -- a program to flash FPGA", ""); @@ -252,6 +261,8 @@ int parse_opt(int argc, char **argv, struct arguments *args) "write bitstream in SRAM (default: true, only for Gowin and ECP5 devices)") ("o,offset", "start offset in EEPROM", cxxopts::value(args->offset)) + ("pins", "pin config (only for ft232R) TDI:TDO:TCK:TMS", + cxxopts::value>(pins)) ("r,reset", "reset FPGA after operations", cxxopts::value(args->reset)) ("v,verbose", "Produce verbose output", cxxopts::value(args->verbose)) @@ -299,6 +310,58 @@ int parse_opt(int argc, char **argv, struct arguments *args) args->freq = static_cast(freq); } + if (result.count("pins")) { + if (pins.size() != 4) { + printError("Error: pin_config need 4 pins"); + throw std::exception(); + } + + static std::map 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; + } + if (args->list_cables || args->list_boards || args->list_fpga) args->is_list_command = true;