mirror of
https://github.com/YosysHQ/yosys
synced 2025-10-08 17:01:57 +00:00
For consistency. Also trying a new thing: only rebuilding objects that use the pybind11 library. The idea is these are the only objects that include the Python/pybind headers and thus the only ones that depend on the Python ABI in any capacity, so other objects can be reused across wheel builds. This has the potential to cut down build times.
807 lines
26 KiB
C++
807 lines
26 KiB
C++
/*
|
|
* yosys -- Yosys Open SYnthesis Suite
|
|
*
|
|
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
|
|
*
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*
|
|
*/
|
|
|
|
#include "kernel/yosys.h"
|
|
#include "kernel/hashlib.h"
|
|
#include "libs/sha1/sha1.h"
|
|
#define CXXOPTS_VECTOR_DELIMITER '\0'
|
|
#include "libs/cxxopts/include/cxxopts.hpp"
|
|
#include <iostream>
|
|
|
|
#ifdef YOSYS_ENABLE_READLINE
|
|
# include <readline/readline.h>
|
|
# include <readline/history.h>
|
|
#endif
|
|
|
|
#ifdef YOSYS_ENABLE_EDITLINE
|
|
# include <editline/readline.h>
|
|
#endif
|
|
|
|
#ifdef YOSYS_ENABLE_TCL
|
|
# include <tcl.h>
|
|
#endif
|
|
|
|
#ifdef YOSYS_ENABLE_PYTHON
|
|
# include <Python.h>
|
|
# include <pybind11/pybind11.h>
|
|
namespace py = pybind11;
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <limits.h>
|
|
#include <errno.h>
|
|
#ifndef __STDC_FORMAT_MACROS
|
|
# define __STDC_FORMAT_MACROS
|
|
#endif
|
|
#include <inttypes.h>
|
|
|
|
#if defined (__linux__) || defined(__FreeBSD__)
|
|
# include <sys/resource.h>
|
|
# include <sys/types.h>
|
|
# include <unistd.h>
|
|
#endif
|
|
|
|
#ifdef __FreeBSD__
|
|
# include <sys/sysctl.h>
|
|
# include <sys/user.h>
|
|
#endif
|
|
|
|
#if !defined(_WIN32) || defined(__MINGW32__)
|
|
# include <unistd.h>
|
|
#endif
|
|
|
|
USING_YOSYS_NAMESPACE
|
|
|
|
#ifdef EMSCRIPTEN
|
|
# include <sys/stat.h>
|
|
# include <sys/types.h>
|
|
# include <emscripten.h>
|
|
|
|
extern "C" int main(int, char**);
|
|
extern "C" void run(const char*);
|
|
extern "C" const char *errmsg();
|
|
extern "C" const char *prompt();
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
EM_ASM(
|
|
if (ENVIRONMENT_IS_NODE)
|
|
{
|
|
FS.mkdir('/hostcwd');
|
|
FS.mount(NODEFS, { root: '.' }, '/hostcwd');
|
|
FS.mkdir('/hostfs');
|
|
FS.mount(NODEFS, { root: '/' }, '/hostfs');
|
|
}
|
|
);
|
|
|
|
mkdir("/work", 0777);
|
|
chdir("/work");
|
|
log_files.push_back(stdout);
|
|
log_error_stderr = true;
|
|
yosys_banner();
|
|
yosys_setup();
|
|
#ifdef YOSYS_ENABLE_PYTHON
|
|
py::object sys = py::module_::import("sys");
|
|
sys.attr("path").attr("append")(proc_self_dirname());
|
|
sys.attr("path").attr("append")(proc_share_dirname());
|
|
#endif
|
|
|
|
if (argc == 2)
|
|
{
|
|
// Run the first argument as a script file
|
|
run_frontend(argv[1], "script");
|
|
}
|
|
}
|
|
|
|
void run(const char *command)
|
|
{
|
|
int selSize = GetSize(yosys_get_design()->selection_stack);
|
|
try {
|
|
log_last_error = "Internal error (see JavaScript console for details)";
|
|
run_pass(command);
|
|
log_last_error = "";
|
|
} catch (...) {
|
|
while (GetSize(yosys_get_design()->selection_stack) > selSize)
|
|
yosys_get_design()->pop_selection();
|
|
throw;
|
|
}
|
|
}
|
|
|
|
const char *errmsg()
|
|
{
|
|
return log_last_error.c_str();
|
|
}
|
|
|
|
const char *prompt()
|
|
{
|
|
const char *p = create_prompt(yosys_get_design(), 0);
|
|
while (*p == '\n') p++;
|
|
return p;
|
|
}
|
|
|
|
#else /* EMSCRIPTEN */
|
|
|
|
#if defined(YOSYS_ENABLE_READLINE) || defined(YOSYS_ENABLE_EDITLINE)
|
|
int yosys_history_offset = 0;
|
|
std::string yosys_history_file;
|
|
#endif
|
|
|
|
#if defined(__wasm)
|
|
extern "C" {
|
|
// FIXME: WASI does not currently support exceptions.
|
|
void* __cxa_allocate_exception(size_t thrown_size) throw() {
|
|
return malloc(thrown_size);
|
|
}
|
|
bool __cxa_uncaught_exception() throw();
|
|
void __cxa_throw(void* thrown_exception, struct std::type_info * tinfo, void (*dest)(void*)) {
|
|
std::terminate();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void yosys_atexit()
|
|
{
|
|
#if defined(YOSYS_ENABLE_READLINE) || defined(YOSYS_ENABLE_EDITLINE)
|
|
if (!yosys_history_file.empty()) {
|
|
#if defined(YOSYS_ENABLE_READLINE)
|
|
if (yosys_history_offset > 0) {
|
|
history_truncate_file(yosys_history_file.c_str(), 100);
|
|
append_history(where_history() - yosys_history_offset, yosys_history_file.c_str());
|
|
} else
|
|
write_history(yosys_history_file.c_str());
|
|
#else
|
|
write_history(yosys_history_file.c_str());
|
|
#endif
|
|
}
|
|
|
|
clear_history();
|
|
#if defined(YOSYS_ENABLE_READLINE)
|
|
HIST_ENTRY **hist_list = history_list();
|
|
if (hist_list != NULL)
|
|
free(hist_list);
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
#if defined(__OpenBSD__)
|
|
namespace Yosys {
|
|
extern char *yosys_argv0;
|
|
extern char yosys_path[PATH_MAX];
|
|
};
|
|
#endif
|
|
#ifdef YOSYS_ENABLE_TCL
|
|
namespace Yosys {
|
|
extern int yosys_tcl_iterp_init(Tcl_Interp *interp);
|
|
extern void yosys_tcl_activate_repl();
|
|
};
|
|
#endif
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
std::string frontend_command = "auto";
|
|
std::string backend_command = "auto";
|
|
std::vector<std::string> vlog_defines;
|
|
std::vector<std::string> passes_commands;
|
|
std::vector<std::string> frontend_files;
|
|
std::vector<std::string> plugin_filenames;
|
|
std::vector<std::string> special_args;
|
|
std::string output_filename = "";
|
|
std::string scriptfile = "";
|
|
std::string depsfile = "";
|
|
std::string topmodule = "";
|
|
std::string perffile = "";
|
|
bool scriptfile_tcl = false;
|
|
bool scriptfile_python = false;
|
|
bool print_banner = true;
|
|
bool print_stats = true;
|
|
bool call_abort = false;
|
|
bool timing_details = false;
|
|
bool run_shell = true;
|
|
bool run_tcl_shell = false;
|
|
bool mode_v = false;
|
|
bool mode_q = false;
|
|
|
|
cxxopts::Options options(argv[0], "Yosys Open SYnthesis Suite");
|
|
options.set_width(SIZE_MAX);
|
|
|
|
options.add_options("operation")
|
|
("b,backend", "use <backend> for the output file specified on the command line",
|
|
cxxopts::value<std::string>(), "<backend>")
|
|
("f,frontend", "use <frontend> for the input files on the command line",
|
|
cxxopts::value<std::string>(), "<frontend>")
|
|
("s,scriptfile", "execute the commands in <scriptfile>",
|
|
cxxopts::value<std::string>(), "<scriptfile>")
|
|
#ifdef YOSYS_ENABLE_TCL
|
|
("c,tcl-scriptfile", "execute the commands in the TCL <tcl_scriptfile> (see 'help tcl' for details)",
|
|
cxxopts::value<std::string>(),"<tcl_scriptfile>")
|
|
("C,tcl-interactive", "enters TCL interactive shell mode")
|
|
#endif // YOSYS_ENABLE_TCL
|
|
#ifdef YOSYS_ENABLE_PYTHON
|
|
("y,py-scriptfile", "execute the Python <script>",
|
|
cxxopts::value<std::string>(), "<script>")
|
|
#endif // YOSYS_ENABLE_PYTHON
|
|
("p,commands", "execute <commands> (to chain commands, separate them with semicolon + whitespace: 'cmd1; cmd2')",
|
|
cxxopts::value<std::vector<std::string>>(), "<commands>")
|
|
("r,top", "elaborate the specified HDL <top> module",
|
|
cxxopts::value<std::string>(), "<top>")
|
|
("m,plugin", "load the specified <plugin> module",
|
|
cxxopts::value<std::vector<std::string>>(), "<plugin>")
|
|
("D,define", "set the specified Verilog define to <value> if supplied via command \"read -define\"",
|
|
cxxopts::value<std::vector<std::string>>(), "<define>[=<value>]")
|
|
("S,synth", "shortcut for calling the \"synth\" command, a default script for transforming " \
|
|
"the Verilog input to a gate-level netlist. For example: " \
|
|
"yosys -o output.blif -S input.v " \
|
|
"For more complex synthesis jobs it is recommended to use the read_* and write_* " \
|
|
"commands in a script file instead of specifying input and output files on the " \
|
|
"command line.")
|
|
("H", "print the command list")
|
|
("h,help", "print this help message. If given, print help for <command>.",
|
|
cxxopts::value<std::string>(), "[<command>]")
|
|
("V,version", "print version information and exit")
|
|
("infile", "input files", cxxopts::value<std::vector<std::string>>())
|
|
;
|
|
options.add_options("logging")
|
|
("Q", "suppress printing of banner (copyright, disclaimer, version)")
|
|
("T", "suppress printing of footer (log hash, version, timing statistics)")
|
|
("no-version", "suppress writing out Yosys version anywhere excluding -V, --version")
|
|
("q,quiet", "quiet operation. Only write warnings and error messages to console. " \
|
|
"Use this option twice to also quiet warning messages")
|
|
("v,verbose", "print log headers up to <level> to the console. " \
|
|
"Implies -q for everything except the 'End of script.' message.",
|
|
cxxopts::value<int>(), "<level>")
|
|
("t,timestamp", "annotate all log messages with a time stamp")
|
|
("d,detailed-timing", "print more detailed timing stats at exit")
|
|
("l,logfile", "write log messages to <logfile>",
|
|
cxxopts::value<std::vector<std::string>>(), "<logfile>")
|
|
("L,line-buffered-logfile", "like -l but open <logfile> in line buffered mode",
|
|
cxxopts::value<std::vector<std::string>>(), "<logfile>")
|
|
("o,outfile", "write the design to <outfile> on exit",
|
|
cxxopts::value<std::string>(), "<outfile>")
|
|
("P,dump-design", "dump the design when printing the specified log header to a file. " \
|
|
"yosys_dump_<header_id>.il is used as filename if none is specified. " \
|
|
"Use 'ALL' as <header_id> to dump at every header.",
|
|
cxxopts::value<std::vector<std::string>>(), "<header_id>[:<filename>]")
|
|
("W,warning-as-warning", "print a warning for all log messages matching <regex>",
|
|
cxxopts::value<std::vector<std::string>>(), "<regex>")
|
|
("w,warning-as-message", "if a warning message matches <regex>, it is printed as regular message instead",
|
|
cxxopts::value<std::vector<std::string>>(), "<regex>")
|
|
("e,warning-as-error", "if a warning message matches <regex>, it is printed as error message instead",
|
|
cxxopts::value<std::vector<std::string>>(), "<regex>")
|
|
("E,deps-file", "write a Makefile dependencies file <depsfile> with input and output file names",
|
|
cxxopts::value<std::string>(), "<depsfile>")
|
|
;
|
|
options.add_options("developer")
|
|
("X,trace", "enable tracing of core data structure changes. for debugging")
|
|
("M,randomize-pointers", "will slightly randomize allocated pointer addresses. for debugging")
|
|
("autoidx", "start counting autoidx up from <seed>, similar effect to --hash-seed",
|
|
cxxopts::value<uint64_t>(), "<idx>")
|
|
("hash-seed", "mix up hashing values with <seed>, for extreme optimization and testing",
|
|
cxxopts::value<uint64_t>(), "<seed>")
|
|
("A,abort", "will call abort() at the end of the script. for debugging")
|
|
("x,experimental", "do not print warnings for the experimental <feature>",
|
|
cxxopts::value<std::vector<std::string>>(), "<feature>")
|
|
("g,debug", "globally enable debug log messages")
|
|
("perffile", "write a JSON performance log to <perffile>", cxxopts::value<std::string>(), "<perffile>")
|
|
;
|
|
|
|
options.parse_positional({"infile"});
|
|
options.positional_help("[<infile> [..]]");
|
|
|
|
// We can't have -h optionally require an argument
|
|
// 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);
|
|
}
|
|
try {
|
|
// Check for "--" in arguments
|
|
auto it = std::find(argv, argv + argc, std::string("--"));
|
|
if (it != argv + argc) {
|
|
special_args.assign(it + 1, argv + argc);
|
|
// Remove these arguments from cxxopts parsing
|
|
argc = std::distance(argv, it);
|
|
}
|
|
|
|
auto result = options.parse(argc, argv);
|
|
|
|
if (result.count("M")) memhasher_on();
|
|
if (result.count("X")) yosys_xtrace += result.count("X");
|
|
if (result.count("A")) call_abort = true;
|
|
if (result.count("Q")) print_banner = false;
|
|
if (result.count("T")) print_stats = false;
|
|
if (result.count("no-version")) yosys_write_versions = false;
|
|
if (result.count("V")) {
|
|
std::cout << yosys_version_str << std::endl;
|
|
exit(0);
|
|
}
|
|
if (result.count("S")) {
|
|
passes_commands.push_back("synth");
|
|
run_shell = false;
|
|
}
|
|
if (result.count("C")) run_tcl_shell = true;
|
|
if (result.count("g")) log_force_debug++;
|
|
if (result.count("m")) plugin_filenames = result["m"].as<std::vector<std::string>>();
|
|
if (result.count("f")) frontend_command = result["f"].as<std::string>();
|
|
if (result.count("H")) {
|
|
passes_commands.push_back("help");
|
|
run_shell = false;
|
|
}
|
|
if (result.count("h")) {
|
|
std::string res = result["h"].as<std::string>();
|
|
passes_commands.push_back("help " + res);
|
|
run_shell = false;
|
|
}
|
|
if (result.count("b")) {
|
|
backend_command = result["b"].as<std::string>();
|
|
run_shell = false;
|
|
}
|
|
if (result.count("p")) {
|
|
auto cmds = result["p"].as<std::vector<std::string>>();
|
|
passes_commands.insert(passes_commands.end(), cmds.begin(), cmds.end());
|
|
run_shell = false;
|
|
}
|
|
if (result.count("o")) {
|
|
output_filename = result["o"].as<std::string>();
|
|
run_shell = false;
|
|
}
|
|
for (const auto& key : {"l", "L"}) {
|
|
if (result.count(key)) {
|
|
for (const auto& filename : result[key].as<std::vector<std::string>>()) {
|
|
if (FILE* f = fopen(filename.c_str(), "wt")) {
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (result.count("q")) {
|
|
mode_q = true;
|
|
if (log_errfile == stderr) log_quiet_warnings = true;
|
|
log_errfile = stderr;
|
|
}
|
|
if (result.count("v")) {
|
|
mode_v = true;
|
|
log_errfile = stderr;
|
|
log_verbose_level = result["v"].as<int>();
|
|
}
|
|
if (result.count("t")) log_time = true;
|
|
if (result.count("d")) timing_details = true;
|
|
for (const auto& key : {"s", "c"}) {
|
|
if (result.count(key)) {
|
|
scriptfile = result[key].as<std::string>();
|
|
scriptfile_tcl = std::string(key) == "c";
|
|
run_shell = false;
|
|
}
|
|
}
|
|
if (result.count("y")) {
|
|
scriptfile = result["y"].as<std::string>();
|
|
scriptfile_python = true;
|
|
run_shell = false;
|
|
}
|
|
for (const auto& key : {"W", "w", "e"}) {
|
|
if (result.count(key)) {
|
|
auto regexes = result[key].as<std::vector<std::string>>();
|
|
for (const auto& regex : regexes) {
|
|
if (std::string(key) == "W")
|
|
log_warn_regexes.push_back(std::regex(regex));
|
|
if (std::string(key) == "w")
|
|
log_nowarn_regexes.push_back(std::regex(regex));
|
|
if (std::string(key) == "e")
|
|
log_werror_regexes.push_back(std::regex(regex));
|
|
}
|
|
}
|
|
}
|
|
if (result.count("r")) topmodule = result["r"].as<std::string>();
|
|
if (result.count("D")) vlog_defines = result["D"].as<std::vector<std::string>>();
|
|
if (result.count("P")) {
|
|
auto dump_args = result["P"].as<std::vector<std::string>>();
|
|
for (const auto& arg : dump_args) {
|
|
auto tokens = split_tokens(arg, ":");
|
|
if (!tokens.empty() && tokens[0] == "ALL") {
|
|
if (tokens.size() != 1) {
|
|
std::cerr << "Invalid number of tokens in -P ALL." << std::endl;
|
|
exit(1);
|
|
}
|
|
log_hdump_all = true;
|
|
} else {
|
|
if (!tokens.empty() && !tokens[0].empty() && tokens[0].back() == '.')
|
|
tokens[0].pop_back();
|
|
if (tokens.size() == 1)
|
|
tokens.push_back("yosys_dump_" + tokens[0] + ".il");
|
|
if (tokens.size() != 2) {
|
|
std::cerr << "Invalid number of tokens in -P." << std::endl;
|
|
exit(1);
|
|
}
|
|
log_hdump[tokens[0]].insert(tokens[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 (result.count("autoidx")) {
|
|
int idx = result["autoidx"].as<uint64_t>();
|
|
autoidx = idx;
|
|
}
|
|
if (result.count("hash-seed")) {
|
|
int seed = result["hash-seed"].as<uint64_t>();
|
|
Hasher::set_fudge((Hasher::hash_t)seed);
|
|
}
|
|
|
|
if (log_errfile == NULL) {
|
|
log_files.push_back(stdout);
|
|
log_error_stderr = true;
|
|
}
|
|
|
|
if (print_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)
|
|
std::string state_dir;
|
|
#if defined(_WIN32)
|
|
if (getenv("HOMEDRIVE") != NULL && getenv("HOMEPATH") != NULL) {
|
|
state_dir = stringf("%s%s/.local/state", getenv("HOMEDRIVE"), getenv("HOMEPATH"));
|
|
} else {
|
|
log_debug("$HOMEDRIVE and/or $HOMEPATH is empty. No history file will be created.\n");
|
|
}
|
|
#else
|
|
if (getenv("XDG_STATE_HOME") == NULL || getenv("XDG_STATE_HOME")[0] == '\0') {
|
|
if (getenv("HOME") != NULL) {
|
|
state_dir = stringf("%s/.local/state", getenv("HOME"));
|
|
} else {
|
|
log_debug("$HOME is empty. No history file will be created.\n");
|
|
}
|
|
} else {
|
|
state_dir = stringf("%s", getenv("XDG_STATE_HOME"));
|
|
}
|
|
#endif
|
|
|
|
if (!state_dir.empty()) {
|
|
std::string yosys_dir = state_dir + "/yosys";
|
|
create_directory(yosys_dir);
|
|
|
|
yosys_history_file = yosys_dir + "/history";
|
|
read_history(yosys_history_file.c_str());
|
|
yosys_history_offset = where_history();
|
|
}
|
|
#endif
|
|
|
|
if (print_stats)
|
|
log_hasher = new SHA1;
|
|
|
|
#if defined(__OpenBSD__)
|
|
// save the executable origin for proc_self_dirname()
|
|
yosys_argv0 = argv[0];
|
|
realpath(yosys_argv0, yosys_path);
|
|
#endif
|
|
|
|
#if defined(__linux__)
|
|
// set stack size to >= 128 MB
|
|
{
|
|
struct rlimit rl;
|
|
const rlim_t stack_size = 128L * 1024L * 1024L;
|
|
if (getrlimit(RLIMIT_STACK, &rl) == 0 && rl.rlim_cur < stack_size) {
|
|
rl.rlim_cur = stack_size;
|
|
setrlimit(RLIMIT_STACK, &rl);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
yosys_setup();
|
|
#ifdef YOSYS_ENABLE_PYTHON
|
|
py::object sys = py::module_::import("sys");
|
|
sys.attr("path").attr("append")(proc_self_dirname());
|
|
sys.attr("path").attr("append")(proc_share_dirname());
|
|
#endif
|
|
log_error_atexit = yosys_atexit;
|
|
|
|
for (auto &fn : plugin_filenames)
|
|
load_plugin(fn, {});
|
|
|
|
log_suppressed();
|
|
|
|
if (!vlog_defines.empty()) {
|
|
std::string vdef_cmd = "read -define";
|
|
for (auto vdef : vlog_defines)
|
|
vdef_cmd += " " + vdef;
|
|
run_pass(vdef_cmd);
|
|
}
|
|
|
|
if (scriptfile.empty() || (!scriptfile_tcl && !scriptfile_python)) {
|
|
// Without a TCL or Python script, arguments following '--'
|
|
// are also treated as frontend files
|
|
for (auto special_arg : special_args)
|
|
frontend_files.push_back(special_arg);
|
|
}
|
|
|
|
for (auto it = frontend_files.begin(); it != frontend_files.end(); ++it) {
|
|
if (run_frontend((*it).c_str(), frontend_command))
|
|
run_shell = false;
|
|
}
|
|
|
|
if (!topmodule.empty())
|
|
run_pass("hierarchy -top " + topmodule);
|
|
if (!scriptfile.empty()) {
|
|
if (scriptfile_tcl) {
|
|
#ifdef YOSYS_ENABLE_TCL
|
|
int tcl_argc = special_args.size();
|
|
std::vector<Tcl_Obj*> script_args;
|
|
Tcl_Interp *interp = yosys_get_tcl_interp();
|
|
for (auto arg : special_args)
|
|
script_args.push_back(Tcl_NewStringObj(arg.c_str(), arg.length()));
|
|
|
|
Tcl_ObjSetVar2(interp, Tcl_NewStringObj("argc", 4), NULL, Tcl_NewIntObj(tcl_argc), 0);
|
|
Tcl_ObjSetVar2(interp, Tcl_NewStringObj("argv", 4), NULL, Tcl_NewListObj(tcl_argc, script_args.data()), 0);
|
|
Tcl_ObjSetVar2(interp, Tcl_NewStringObj("argv0", 5), NULL, Tcl_NewStringObj(scriptfile.c_str(), scriptfile.length()), 0);
|
|
|
|
if (Tcl_EvalFile(interp, scriptfile.c_str()) != TCL_OK)
|
|
log_error("TCL interpreter returned an error: %s\n", Tcl_GetStringResult(yosys_get_tcl_interp()));
|
|
#else
|
|
log_error("Can't execute TCL script: this version of yosys is not built with TCL support enabled.\n");
|
|
#endif
|
|
} else if (scriptfile_python) {
|
|
#ifdef YOSYS_ENABLE_PYTHON
|
|
py::list new_argv;
|
|
int py_argc = special_args.size() + 1;
|
|
new_argv.append(scriptfile);
|
|
for (int i = 1; i < py_argc; ++i)
|
|
new_argv.append(special_args[i - 1]);
|
|
|
|
py::setattr(sys, "argv", new_argv);
|
|
|
|
py::object Path = py::module_::import("pathlib").attr("Path");
|
|
py::object scriptfile_python_path = Path(scriptfile).attr("parent");
|
|
|
|
sys.attr("path").attr("insert")(0, py::str(scriptfile_python_path));
|
|
|
|
FILE *scriptfp = fopen(scriptfile.c_str(), "r");
|
|
if (scriptfp == nullptr) {
|
|
log_error("Failed to open file '%s' for reading.\n", scriptfile);
|
|
}
|
|
if (PyRun_SimpleFile(scriptfp, scriptfile.c_str()) != 0) {
|
|
log_flush();
|
|
PyErr_Print();
|
|
log_error("Python interpreter encountered an exception.");
|
|
}
|
|
#else
|
|
log_error("Can't execute Python script: this version of yosys is not built with Python support enabled.\n");
|
|
#endif
|
|
} else
|
|
run_frontend(scriptfile, "script");
|
|
}
|
|
|
|
for (auto it = passes_commands.begin(); it != passes_commands.end(); it++)
|
|
run_pass(*it);
|
|
|
|
if (run_tcl_shell) {
|
|
#ifdef YOSYS_ENABLE_TCL
|
|
yosys_tcl_activate_repl();
|
|
Tcl_Main(argc, argv, yosys_tcl_iterp_init);
|
|
#else
|
|
log_error("Can't exectue TCL shell: this version of yosys is not built with TCL support enabled.\n");
|
|
#endif
|
|
} else {
|
|
if (run_shell)
|
|
shell(yosys_design);
|
|
else
|
|
run_backend(output_filename, backend_command);
|
|
}
|
|
|
|
yosys_design->check();
|
|
for (auto it : saved_designs)
|
|
it.second->check();
|
|
for (auto it : pushed_designs)
|
|
it->check();
|
|
|
|
if (!depsfile.empty())
|
|
{
|
|
FILE *f = fopen(depsfile.c_str(), "wt");
|
|
if (f == nullptr)
|
|
log_error("Can't open dependencies file for writing: %s\n", strerror(errno));
|
|
bool first = true;
|
|
for (auto fn : yosys_output_files) {
|
|
fprintf(f, "%s%s", first ? "" : " ", escape_filename_spaces(fn).c_str());
|
|
first = false;
|
|
}
|
|
fprintf(f, ":");
|
|
for (auto fn : yosys_input_files) {
|
|
if (yosys_output_files.count(fn) == 0)
|
|
fprintf(f, " %s", escape_filename_spaces(fn).c_str());
|
|
}
|
|
fprintf(f, "\n");
|
|
}
|
|
|
|
if (log_expect_no_warnings && log_warnings_count_noexpect)
|
|
log_error("Unexpected warnings found: %d unique messages, %d total, %d expected\n", GetSize(log_warnings),
|
|
log_warnings_count, log_warnings_count - log_warnings_count_noexpect);
|
|
|
|
if (print_stats)
|
|
{
|
|
std::string hash = log_hasher->final().substr(0, 10);
|
|
delete log_hasher;
|
|
log_hasher = nullptr;
|
|
|
|
log_time = false;
|
|
yosys_xtrace = 0;
|
|
log_spacer();
|
|
|
|
if (mode_v && !mode_q)
|
|
log_files.push_back(stderr);
|
|
|
|
if (log_warnings_count)
|
|
log("Warnings: %d unique messages, %d total\n", GetSize(log_warnings), log_warnings_count);
|
|
|
|
if (!log_experimentals.empty())
|
|
log("Warnings: %d experimental features used (not excluded with -x).\n", GetSize(log_experimentals));
|
|
|
|
#ifdef _WIN32
|
|
log("End of script. Logfile hash: %s\n", hash);
|
|
#else
|
|
std::string meminfo;
|
|
std::string stats_divider = ", ";
|
|
|
|
struct rusage ru_buffer;
|
|
getrusage(RUSAGE_SELF, &ru_buffer);
|
|
if (yosys_design->scratchpad_get_bool("print_stats.include_children")) {
|
|
struct rusage ru_buffer_children;
|
|
getrusage(RUSAGE_CHILDREN, &ru_buffer_children);
|
|
ru_buffer.ru_utime.tv_sec += ru_buffer_children.ru_utime.tv_sec;
|
|
ru_buffer.ru_utime.tv_usec += ru_buffer_children.ru_utime.tv_usec;
|
|
ru_buffer.ru_stime.tv_sec += ru_buffer_children.ru_stime.tv_sec;
|
|
ru_buffer.ru_stime.tv_usec += ru_buffer_children.ru_stime.tv_usec;
|
|
#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__)
|
|
ru_buffer.ru_maxrss = std::max(ru_buffer.ru_maxrss, ru_buffer_children.ru_maxrss);
|
|
#endif
|
|
}
|
|
#if defined(__linux__) || defined(__FreeBSD__)
|
|
meminfo = stringf(", MEM: %.2f MB peak",
|
|
ru_buffer.ru_maxrss / 1024.0);
|
|
#elif defined(__APPLE__)
|
|
// https://stackoverflow.com/questions/59913657/strange-values-of-get-rusage-maxrss-on-macos-and-linux
|
|
meminfo = stringf(", MEM: %.2f MB peak",
|
|
ru_buffer.ru_maxrss / (1024.0 * 1024.0));
|
|
#endif
|
|
log("End of script. Logfile hash: %s%sCPU: user %.2fs system %.2fs%s\n", hash,
|
|
stats_divider.c_str(), ru_buffer.ru_utime.tv_sec + 1e-6 * ru_buffer.ru_utime.tv_usec,
|
|
ru_buffer.ru_stime.tv_sec + 1e-6 * ru_buffer.ru_stime.tv_usec, meminfo.c_str());
|
|
#endif
|
|
log("%s\n", yosys_maybe_version());
|
|
|
|
int64_t total_ns = 0;
|
|
std::set<tuple<int64_t, int, std::string>> timedat;
|
|
|
|
for (auto &it : pass_register)
|
|
if (it.second->call_counter) {
|
|
total_ns += it.second->runtime_ns + 1;
|
|
timedat.insert(make_tuple(it.second->runtime_ns + 1, it.second->call_counter, it.first));
|
|
}
|
|
|
|
if (timing_details)
|
|
{
|
|
log("Time spent:\n");
|
|
for (auto it = timedat.rbegin(); it != timedat.rend(); it++) {
|
|
log("%5d%% %5d calls %8.3f sec %s\n", int(100*std::get<0>(*it) / total_ns),
|
|
std::get<1>(*it), std::get<0>(*it) / 1000000000.0, std::get<2>(*it).c_str());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int out_count = 0;
|
|
log("Time spent:");
|
|
for (auto it = timedat.rbegin(); it != timedat.rend() && out_count < 4; it++, out_count++) {
|
|
if (out_count >= 2 && (std::get<0>(*it) < 1000000000 || int(100*std::get<0>(*it) / total_ns) < 20)) {
|
|
log(", ...");
|
|
break;
|
|
}
|
|
log("%s %d%% %dx %s (%d sec)", out_count ? "," : "", int(100*std::get<0>(*it) / total_ns),
|
|
std::get<1>(*it), std::get<2>(*it).c_str(), int(std::get<0>(*it) / 1000000000));
|
|
}
|
|
log("%s\n", out_count ? "" : " no commands executed");
|
|
}
|
|
if(!perffile.empty())
|
|
{
|
|
FILE *f = fopen(perffile.c_str(), "wt");
|
|
if (f == nullptr)
|
|
log_error("Can't open performance log file for writing: %s\n", strerror(errno));
|
|
|
|
fprintf(f, "{\n");
|
|
fprintf(f, " \"generator\": \"%s\",\n", yosys_maybe_version());
|
|
fprintf(f, " \"total_ns\": %" PRIu64 ",\n", total_ns);
|
|
fprintf(f, " \"passes\": {");
|
|
|
|
bool first = true;
|
|
for (auto it = timedat.rbegin(); it != timedat.rend(); it++) {
|
|
if (!first)
|
|
fprintf(f, ",");
|
|
fprintf(f, "\n \"%s\": {\n", std::get<2>(*it).c_str());
|
|
fprintf(f, " \"runtime_ns\": %" PRIu64 ",\n", std::get<0>(*it));
|
|
fprintf(f, " \"num_calls\": %u\n", std::get<1>(*it));
|
|
fprintf(f, " }");
|
|
first = false;
|
|
}
|
|
fprintf(f, "\n }\n}\n");
|
|
}
|
|
}
|
|
|
|
#if defined(YOSYS_ENABLE_COVER) && (defined(__linux__) || defined(__FreeBSD__))
|
|
if (getenv("YOSYS_COVER_DIR") || getenv("YOSYS_COVER_FILE"))
|
|
{
|
|
string filename;
|
|
FILE *f;
|
|
|
|
if (getenv("YOSYS_COVER_DIR")) {
|
|
filename = stringf("%s/yosys_cover_%d_XXXXXX.txt", getenv("YOSYS_COVER_DIR"), getpid());
|
|
filename = make_temp_file(filename);
|
|
} else {
|
|
filename = getenv("YOSYS_COVER_FILE");
|
|
}
|
|
|
|
f = fopen(filename.c_str(), "a+");
|
|
|
|
if (f == NULL)
|
|
log_error("Can't create coverage file `%s'.\n", filename);
|
|
|
|
log("<writing coverage file \"%s\">\n", filename);
|
|
|
|
for (auto &it : get_coverage_data())
|
|
fprintf(f, "%-60s %10d %s\n", it.second.first.c_str(), it.second.second, it.first.c_str());
|
|
|
|
fclose(f);
|
|
}
|
|
#endif
|
|
|
|
log_check_expected();
|
|
|
|
yosys_atexit();
|
|
|
|
memhasher_off();
|
|
if (call_abort)
|
|
abort();
|
|
|
|
log_flush();
|
|
#if defined(_MSC_VER)
|
|
_exit(0);
|
|
#elif defined(_WIN32)
|
|
_Exit(0);
|
|
#endif
|
|
|
|
yosys_shutdown();
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif /* EMSCRIPTEN */
|