driver: switch to cxxopts, replace -B

This commit is contained in:
Emil J. Tywoniak 2024-10-09 15:07:56 +02:00
parent 038e262332
commit 575415ade2
3 changed files with 219 additions and 323 deletions

3
.gitmodules vendored
View File

@ -1,3 +1,6 @@
[submodule "abc"] [submodule "abc"]
path = abc path = abc
url = https://github.com/YosysHQ/abc url = https://github.com/YosysHQ/abc
[submodule "libs/cxxopts"]
path = libs/cxxopts
url = git@github.com:jarro2783/cxxopts.git

View File

@ -19,6 +19,8 @@
#include "kernel/yosys.h" #include "kernel/yosys.h"
#include "libs/sha1/sha1.h" #include "libs/sha1/sha1.h"
#include "libs/cxxopts/include/cxxopts.hpp"
#include <iostream>
#ifdef YOSYS_ENABLE_READLINE #ifdef YOSYS_ENABLE_READLINE
# include <readline/readline.h> # include <readline/readline.h>
@ -55,55 +57,6 @@
USING_YOSYS_NAMESPACE USING_YOSYS_NAMESPACE
char *optarg;
int optind = 1, optcur = 1, optopt = 0;
int getopt(int argc, char **argv, const char *optstring)
{
if (optind >= argc)
return -1;
if (argv[optind][0] != '-' || argv[optind][1] == 0) {
optopt = 1;
optarg = argv[optind++];
return optopt;
}
bool takes_arg = false;
optopt = argv[optind][optcur];
if (optopt == '-') {
++optind;
return -1;
}
for (int i = 0; optstring[i]; i++)
if (optopt == optstring[i] && optstring[i + 1] == ':')
takes_arg = true;
if (!takes_arg) {
if (argv[optind][++optcur] == 0)
optind++, optcur = 1;
return optopt;
}
if (argv[optind][++optcur]) {
optarg = argv[optind++] + optcur;
optcur = 1;
return optopt;
}
if (++optind >= argc) {
fprintf(stderr, "%s: option '-%c' expects an argument\n", argv[0], optopt);
optopt = '?';
return optopt;
}
optarg = argv[optind];
optind++, optcur = 1;
return optopt;
}
#ifdef EMSCRIPTEN #ifdef EMSCRIPTEN
# include <sys/stat.h> # include <sys/stat.h>
# include <sys/types.h> # include <sys/types.h>
@ -235,6 +188,7 @@ int main(int argc, char **argv)
std::vector<std::string> passes_commands; std::vector<std::string> passes_commands;
std::vector<std::string> frontend_files; std::vector<std::string> frontend_files;
std::vector<std::string> plugin_filenames; std::vector<std::string> plugin_filenames;
std::vector<std::string> special_args;
std::string output_filename = ""; std::string output_filename = "";
std::string scriptfile = ""; std::string scriptfile = "";
std::string depsfile = ""; std::string depsfile = "";
@ -251,296 +205,227 @@ int main(int argc, char **argv)
bool mode_v = false; bool mode_v = false;
bool mode_q = false; bool mode_q = false;
if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "-help") || !strcmp(argv[1], "--help"))) cxxopts::Options options(argv[0], "Yosys Open SYnthesis Suite");
{ options.set_width(SIZE_MAX);
printf("\n");
printf("Usage: %s [options] [<infile> [..]]\n", argv[0]); options.add_options("operation")
printf("\n"); ("b,backend", "use <backend> for the output file specified on the command line",
printf(" -Q\n"); cxxopts::value<std::string>(), "<backend>")
printf(" suppress printing of banner (copyright, disclaimer, version)\n"); ("f,frontend", "use <frontend> for the input files on the command line",
printf("\n"); cxxopts::value<std::string>(), "<frontend>")
printf(" -T\n"); ("s,scriptfile", "execute the commands in <scriptfile>",
printf(" suppress printing of footer (log hash, version, timing statistics)\n"); cxxopts::value<std::string>(), "<scriptfile>")
printf("\n");
printf(" -q\n");
printf(" quiet operation. only write warnings and error messages to console\n");
printf(" use this option twice to also quiet warning messages\n");
printf("\n");
printf(" -v <level>\n");
printf(" print log headers up to level <level> to the console. (this\n");
printf(" implies -q for everything except the 'End of script.' message.)\n");
printf("\n");
printf(" -t\n");
printf(" annotate all log messages with a time stamp\n");
printf("\n");
printf(" -d\n");
printf(" print more detailed timing stats at exit\n");
printf("\n");
printf(" -l logfile\n");
printf(" write log messages to the specified file\n");
printf("\n");
printf(" -L logfile\n");
printf(" like -l but open log file in line buffered mode\n");
printf("\n");
printf(" -o outfile\n");
printf(" write the design to the specified file on exit\n");
printf("\n");
printf(" -b backend\n");
printf(" use this backend for the output file specified on the command line\n");
printf("\n");
printf(" -f frontend\n");
printf(" use the specified frontend for the input files on the command line\n");
printf("\n");
printf(" -H\n");
printf(" print the command list\n");
printf("\n");
printf(" -h command\n");
printf(" print the help message for the specified command\n");
printf("\n");
printf(" -s scriptfile\n");
printf(" execute the commands in the script file\n");
#ifdef YOSYS_ENABLE_TCL #ifdef YOSYS_ENABLE_TCL
printf("\n"); ("c,tcl-scriptfile", "execute the commands in the TCL <tcl_scriptfile> (see 'help tcl' for details)",
printf(" -c tcl_scriptfile\n"); cxxopts::value<std::string>(),"<tcl_scriptfile>")
printf(" execute the commands in the tcl script file (see 'help tcl' for details)\n"); ("C,tcl-interactive", "enters TCL interactive shell mode")
printf("\n"); #endif // YOSYS_ENABLE_TCL
printf(" -C\n");
printf(" enters TCL interactive shell mode\n");
#endif
#ifdef WITH_PYTHON #ifdef WITH_PYTHON
printf("\n"); ("y,py-scriptfile", "execute the Python <script>",
printf(" -y python_scriptfile\n"); cxxopts::value<std::vector<std::string>>(), "<script>")
printf(" execute a python script with libyosys available as a built-in module\n"); #endif // WITH_PYTHON
#endif ("p,commands", "execute <commands> (to chain commands, separate them with semicolon + whitespace: 'cmd1; cmd2')",
printf("\n"); cxxopts::value<std::vector<std::string>>(), "<commands>")
printf(" -p command\n"); ("r,top", "elaborate the specified HDL <top> module",
printf(" execute the commands (to chain commands, separate them with semicolon + whitespace: 'cmd1; cmd2')\n"); cxxopts::value<std::string>(), "<top>")
printf("\n"); ("m,plugin", "load the specified <plugin> module",
printf(" -m module_file\n"); cxxopts::value<std::vector<std::string>>(), "<plugin>")
printf(" load the specified module (aka plugin)\n"); ("D,define", "set the specified Verilog define to <value> if supplied via command \"read -define\"",
printf("\n"); cxxopts::value<std::vector<std::string>>(), "<define>[=<value>]")
printf(" -X\n"); ("S,synth", "shortcut for calling the \"synth\" command, a default script for transforming " \
printf(" enable tracing of core data structure changes. for debugging\n"); "the Verilog input to a gate-level netlist. For example: " \
printf("\n"); "yosys -o output.blif -S input.v " \
printf(" -M\n"); "For more complex synthesis jobs it is recommended to use the read_* and write_* " \
printf(" will slightly randomize allocated pointer addresses. for debugging\n"); "commands in a script file instead of specifying input and output files on the " \
printf("\n"); "command line.")
printf(" -A\n"); ("H", "print the command list")
printf(" will call abort() at the end of the script. for debugging\n"); ("h,help", "print this help message. If given, print help for <command>.",
printf("\n"); cxxopts::value<std::string>(), "[<command>]")
printf(" -r <module_name>\n"); ("V,version", "print version information and exit")
printf(" elaborate command line arguments using the specified top module\n"); ("infile", "input files", cxxopts::value<std::vector<std::string>>())
printf("\n"); ;
printf(" -D <macro>[=<value>]\n"); options.add_options("logging")
printf(" set the specified Verilog define (via \"read -define\")\n"); ("Q", "suppress printing of banner (copyright, disclaimer, version)")
printf("\n"); ("T", "suppress printing of footer (log hash, version, timing statistics)")
printf(" -P <header_id>[:<filename>]\n"); ("q,quiet", "quiet operation. Only write warnings and error messages to console. " \
printf(" dump the design when printing the specified log header to a file.\n"); "Use this option twice to also quiet warning messages")
printf(" yosys_dump_<header_id>.il is used as filename if none is specified.\n"); ("v,verbose", "print log headers up to <level> to the console. " \
printf(" Use 'ALL' as <header_id> to dump at every header.\n"); "Implies -q for everything except the 'End of script.' message.",
printf("\n"); cxxopts::value<int>(), "<level>")
printf(" -W regex\n"); ("t,timestamp", "annotate all log messages with a time stamp")
printf(" print a warning for all log messages matching the regex.\n"); ("d,detailed-timing", "print more detailed timing stats at exit")
printf("\n"); ("l,logfile", "write log messages to <logfile>",
printf(" -w regex\n"); cxxopts::value<std::vector<std::string>>(), "<logfile>")
printf(" if a warning message matches the regex, it is printed as regular\n"); ("L,line-buffered-logfile", "like -l but open <logfile> in line buffered mode",
printf(" message instead.\n"); cxxopts::value<std::vector<std::string>>(), "<logfile>")
printf("\n"); ("o,outfile", "write the design to <outfile> on exit",
printf(" -e regex\n"); cxxopts::value<std::string>(), "<outfile>")
printf(" if a warning message matches the regex, it is printed as error\n"); ("P,dump-design", "dump the design when printing the specified log header to a file. " \
printf(" message instead and the tool terminates with a nonzero return code.\n"); "yosys_dump_<header_id>.il is used as filename if none is specified. " \
printf("\n"); "Use 'ALL' as <header_id> to dump at every header.",
printf(" -E <depsfile>\n"); cxxopts::value<std::vector<std::string>>(), "<header_id>[:<filename>]")
printf(" write a Makefile dependencies file with in- and output file names\n"); ("W,warning-as-warning", "print a warning for all log messages matching <regex>",
printf("\n"); cxxopts::value<std::vector<std::string>>(), "<regex>")
printf(" -x <feature>\n"); ("w,warning-as-message", "if a warning message matches <regex>, it is printed as regular message instead",
printf(" do not print warnings for the specified experimental feature\n"); cxxopts::value<std::vector<std::string>>(), "<regex>")
printf("\n"); ("e,warning-as-error", "if a warning message matches <regex>, it is printed as error message instead",
printf(" -g\n"); cxxopts::value<std::vector<std::string>>(), "<regex>")
printf(" globally enable debug log messages\n"); ("E,deps-file", "write a Makefile dependencies file <depsfile> with input and output file names",
printf("\n"); cxxopts::value<std::string>(), "<depsfile>")
printf(" -V\n"); ;
printf(" print version information and exit\n"); options.add_options("developer")
printf("\n"); ("X,trace", "enable tracing of core data structure changes. for debugging")
printf("The option -S is a shortcut for calling the \"synth\" command, a default\n"); ("M,randomize-pointers", "will slightly randomize allocated pointer addresses. for debugging")
printf("script for transforming the Verilog input to a gate-level netlist. For example:\n"); ("A,abort", "will call abort() at the end of the script. for debugging")
printf("\n"); ("x,experimental", "do not print warnings for the experimental <feature>",
printf(" yosys -o output.blif -S input.v\n"); cxxopts::value<std::vector<std::string>>(), "<feature>")
printf("\n"); ("g,debug", "globally enable debug log messages")
printf("For more complex synthesis jobs it is recommended to use the read_* and write_*\n"); ("perffile", "write a JSON performance log to <perffile>", cxxopts::value<std::string>(), "<perffile>")
printf("commands in a script file instead of specifying input and output files on the\n"); ;
printf("command line.\n");
printf("\n"); options.parse_positional({"infile"});
printf("When no commands, script files or input files are specified on the command\n"); options.positional_help("[<infile> [..]]");
printf("line, yosys automatically enters the interactive command mode. Use the 'help'\n");
printf("command to get information on the individual commands.\n"); // We can't have -h optionally require an argument
printf("\n"); // cxxopts does have an implit argument concept but that doesn't work for us
// cxxopts is therefore instructed allowed to only handle the command help case
if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "-help") || !strcmp(argv[1], "--help"))) {
std::cout << options.help() << std::endl;
exit(0); exit(0);
} }
try {
if (argc == 2 && (!strcmp(argv[1], "-V") || !strcmp(argv[1], "-version") || !strcmp(argv[1], "--version"))) // Check for "--" in arguments
{ auto it = std::find(argv, argv + argc, std::string("--"));
printf("%s\n", yosys_version_str); if (it != argv + argc) {
exit(0); special_args.assign(it + 1, argv + argc);
// Remove these arguments from cxxopts parsing
argc = std::distance(argv, it);
} }
int opt; auto result = options.parse(argc, argv);
while ((opt = getopt(argc, argv, "MXAQTVCSgm:f:Hh:b:o:p:l:L:qv:tds:c:y:W:w:e:r:D:P:E:x:B:")) != -1)
{ if (result.count("M")) memhasher_on();
switch (opt) if (result.count("X")) yosys_xtrace++;
{ if (result.count("A")) call_abort = true;
case 'M': if (result.count("Q")) print_banner = false;
memhasher_on(); if (result.count("T")) print_stats = false;
break; if (result.count("V")) {
case 'X': std::cout << yosys_version_str << std::endl;
yosys_xtrace++;
break;
case 'A':
call_abort = true;
break;
case 'Q':
print_banner = false;
break;
case 'T':
print_stats = false;
break;
case 'V':
printf("%s\n", yosys_version_str);
exit(0); exit(0);
case 'S': }
if (result.count("S")) {
passes_commands.push_back("synth"); passes_commands.push_back("synth");
run_shell = false; run_shell = false;
break; }
case 'g': if (result.count("C")) run_tcl_shell = true;
log_force_debug++; if (result.count("g")) log_force_debug++;
break; if (result.count("m")) plugin_filenames = result["m"].as<std::vector<std::string>>();
case 'm': if (result.count("f")) frontend_command = result["f"].as<std::string>();
plugin_filenames.push_back(optarg); if (result.count("H")) {
break;
case 'f':
frontend_command = optarg;
break;
case 'H':
passes_commands.push_back("help"); passes_commands.push_back("help");
run_shell = false; run_shell = false;
break; }
case 'h': if (result.count("h")) {
passes_commands.push_back(stringf("help %s", optarg)); std::string res = result["h"].as<std::string>();
passes_commands.push_back("help " + res);
run_shell = false; run_shell = false;
break; }
case 'b': if (result.count("b")) {
backend_command = optarg; backend_command = result["b"].as<std::string>();
run_shell = false; run_shell = false;
break; }
case 'p': if (result.count("p")) {
passes_commands.push_back(optarg); auto cmds = result["p"].as<std::vector<std::string>>();
passes_commands.insert(passes_commands.end(), cmds.begin(), cmds.end());
run_shell = false; run_shell = false;
break; }
case 'o': if (result.count("o")) {
output_filename = optarg; output_filename = result["o"].as<std::string>();
run_shell = false; run_shell = false;
break; }
case 'l': for (const auto& key : {"l", "L"}) {
case 'L': if (result.count(key)) {
log_files.push_back(fopen(optarg, "wt")); for (const auto& filename : result[key].as<std::vector<std::string>>()) {
if (log_files.back() == NULL) { if (FILE* f = fopen(filename.c_str(), "wt")) {
fprintf(stderr, "Can't open log file `%s' for writing!\n", optarg); log_files.push_back(f);
if (key[0] == 'L') setvbuf(f, NULL, _IOLBF, 0);
} else {
std::cerr << "Can't open log file `" << filename << "' for writing!\n";
exit(1); exit(1);
} }
if (opt == 'L') }
setvbuf(log_files.back(), NULL, _IOLBF, 0); }
break; }
case 'q': if (result.count("q")) {
mode_q = true; mode_q = true;
if (log_errfile == stderr) if (log_errfile == stderr) log_quiet_warnings = true;
log_quiet_warnings = true;
log_errfile = stderr; log_errfile = stderr;
break; }
case 'v': if (result.count("v")) {
mode_v = true; mode_v = true;
log_errfile = stderr; log_errfile = stderr;
log_verbose_level = atoi(optarg); log_verbose_level = result["v"].as<int>();
break; }
case 't': if (result.count("t")) log_time = true;
log_time = true; if (result.count("d")) timing_details = true;
break; for (const auto& key : {"s", "c"}) {
case 'd': if (result.count(key)) {
timing_details = true; scriptfile = result[key].as<std::string>();
break; scriptfile_tcl = std::string(key) == "c";
case 's':
scriptfile = optarg;
scriptfile_tcl = false;
scriptfile_python = false;
run_shell = false; run_shell = false;
break; }
case 'c': }
scriptfile = optarg; if (result.count("y")) {
scriptfile_tcl = true; scriptfile = result["y"].as<std::string>();
scriptfile_python = false;
run_shell = false;
break;
case 'y':
scriptfile = optarg;
scriptfile_tcl = false;
scriptfile_python = true; scriptfile_python = true;
run_shell = false; run_shell = false;
break; }
case 'W': for (const auto& key : {"W", "w", "e"}) {
log_warn_regexes.push_back(YS_REGEX_COMPILE(optarg)); if (result.count(key)) {
break; auto regexes = result[key].as<std::vector<std::string>>();
case 'w': for (const auto& regex : regexes) {
log_nowarn_regexes.push_back(YS_REGEX_COMPILE(optarg)); if (std::string(key) == "W")
break; log_warn_regexes.push_back(std::regex(regex));
case 'e': if (std::string(key) == "w")
log_werror_regexes.push_back(YS_REGEX_COMPILE(optarg)); log_nowarn_regexes.push_back(std::regex(regex));
break; if (std::string(key) == "e")
case 'r': log_werror_regexes.push_back(std::regex(regex));
topmodule = optarg; }
break; }
case 'D': }
vlog_defines.push_back(optarg); if (result.count("r")) topmodule = result["r"].as<std::string>();
break; if (result.count("D")) vlog_defines = result["D"].as<std::vector<std::string>>();
case 'P': if (result.count("P")) {
{ auto dump_args = result["P"].as<std::vector<std::string>>();
auto args = split_tokens(optarg, ":"); for (const auto& arg : dump_args) {
if (!args.empty() && args[0] == "ALL") { auto tokens = split_tokens(arg, ":");
if (GetSize(args) != 1) { if (!tokens.empty() && tokens[0] == "ALL") {
fprintf(stderr, "Invalid number of tokens in -D ALL.\n"); if (tokens.size() != 1) {
std::cerr << "Invalid number of tokens in -P ALL." << std::endl;
exit(1); exit(1);
} }
log_hdump_all = true; log_hdump_all = true;
} else { } else {
if (!args.empty() && !args[0].empty() && args[0].back() == '.') if (!tokens.empty() && !tokens[0].empty() && tokens[0].back() == '.')
args[0].pop_back(); tokens[0].pop_back();
if (GetSize(args) == 1) if (tokens.size() == 1)
args.push_back("yosys_dump_" + args[0] + ".il"); tokens.push_back("yosys_dump_" + tokens[0] + ".il");
if (GetSize(args) != 2) { if (tokens.size() != 2) {
fprintf(stderr, "Invalid number of tokens in -D.\n"); std::cerr << "Invalid number of tokens in -P." << std::endl;
exit(1); exit(1);
} }
log_hdump[args[0]].insert(args[1]); log_hdump[tokens[0]].insert(tokens[1]);
} }
} }
break;
case 'E':
depsfile = optarg;
break;
case 'x':
log_experimentals_ignored.insert(optarg);
break;
case 'B':
perffile = optarg;
break;
case 'C':
run_tcl_shell = true;
break;
case '\001':
frontend_files.push_back(optarg);
break;
default:
fprintf(stderr, "Run '%s -h' for help.\n", argv[0]);
exit(1);
} }
if (result.count("E")) depsfile = result["E"].as<std::string>();
if (result.count("x")) {
auto ignores = result["x"].as<std::vector<std::string>>();
log_experimentals_ignored.insert(ignores.begin(), ignores.end());
}
if (result.count("perffile")) perffile = result["perffile"].as<std::string>();
if (result.count("infile")) {
frontend_files = result["infile"].as<std::vector<std::string>>();
} }
if (log_errfile == NULL) { if (log_errfile == NULL) {
@ -551,6 +436,13 @@ int main(int argc, char **argv)
if (print_banner) if (print_banner)
yosys_banner(); yosys_banner();
}
catch (const cxxopts::exceptions::parsing& e) {
std::cerr << "Error parsing options: " << e.what() << std::endl;
std::cerr << "Run '" << argv[0] << " --help' for help." << std::endl;
exit(1);
}
#if defined(YOSYS_ENABLE_READLINE) || defined(YOSYS_ENABLE_EDITLINE) #if defined(YOSYS_ENABLE_READLINE) || defined(YOSYS_ENABLE_EDITLINE)
std::string state_dir; std::string state_dir;
#if defined(_WIN32) #if defined(_WIN32)
@ -622,10 +514,10 @@ int main(int argc, char **argv)
} }
if (scriptfile.empty() || (!scriptfile_tcl && !scriptfile_python)) { if (scriptfile.empty() || (!scriptfile_tcl && !scriptfile_python)) {
// Without a TCL or Python script, arguments following '--' are also // Without a TCL or Python script, arguments following '--'
// treated as frontend files // are also treated as frontend files
for (int i = optind; i < argc; ++i) for (auto special_arg : special_args)
frontend_files.push_back(argv[i]); frontend_files.push_back(special_arg);
} }
for (auto it = frontend_files.begin(); it != frontend_files.end(); ++it) { for (auto it = frontend_files.begin(); it != frontend_files.end(); ++it) {

1
libs/cxxopts Submodule

@ -0,0 +1 @@
Subproject commit 4bf61f08697b110d9e3991864650a405b3dd515d