bugpoint: add -command option.
[yosys.git] / passes / cmds / bugpoint.cc
index ad6a07fa017325776361cf0751aa426bca403b81..d32d33560d0b5d21fd965cf3e17a36a9765f1d69 100644 (file)
  */
 
 #include "kernel/yosys.h"
-#include "backends/ilang/ilang_backend.h"
+#include "backends/rtlil/rtlil_backend.h"
 
 USING_YOSYS_NAMESPACE
-using namespace ILANG_BACKEND;
+using namespace RTLIL_BACKEND;
 PRIVATE_NAMESPACE_BEGIN
 
 struct BugpointPass : public Pass {
        BugpointPass() : Pass("bugpoint", "minimize testcases") { }
-       void help() YS_OVERRIDE
+       void help() override
        {
                //   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
                log("\n");
-               log("    bugpoint [options]\n");
+               log("    bugpoint [options] [-script <filename> | -command \"<command>\"]\n");
                log("\n");
-               log("This command minimizes testcases that crash Yosys. It removes an arbitrary part\n");
-               log("of the design and recursively invokes Yosys with a given script, repeating these\n");
-               log("steps while it can find a smaller design that still causes a crash. Once this\n");
-               log("command finishes, it replaces the current design with the smallest testcase it\n");
-               log("was able to produce.\n");
+               log("This command minimizes the current design that is known to crash Yosys with the\n");
+               log("given script into a smaller testcase. It does this by removing an arbitrary part\n");
+               log("of the design and recursively invokes a new Yosys process with this modified design\n");
+               log("and the same script, repeating these steps while it can find a smaller design that\n");
+               log("still causes a crash. Once this command finishes, it replaces the current design\n");
+               log("with the smallest testcase it was able to produce.\n");
                log("\n");
-               log("It is possible to specify the kinds of design part that will be removed. If none\n");
-               log("are specified, all parts of design will be removed.\n");
+               log("    -script <filename> | -command \"<command>\"\n");
+               log("        use this script file or command to crash Yosys. required.\n");
                log("\n");
                log("    -yosys <filename>\n");
                log("        use this Yosys binary. if not specified, `yosys` is used.\n");
                log("\n");
-               log("    -script <filename>\n");
-               log("        use this script to crash Yosys. required.\n");
-               log("\n");
                log("    -grep <string>\n");
                log("        only consider crashes that place this string in the log file.\n");
                log("\n");
@@ -60,14 +58,21 @@ struct BugpointPass : public Pass {
                log("        finishing. produces smaller and more useful testcases, but may fail to\n");
                log("        produce any testcase at all if the crash is related to dangling wires.\n");
                log("\n");
+               log("It is possible to constrain which parts of the design will be considered for\n");
+               log("removal. Unless one or more of the following options are specified, all parts\n");
+               log("will be considered.\n");
+               log("\n");
                log("    -modules\n");
-               log("        try to remove modules.\n");
+               log("        try to remove modules. modules with a (* bugpoint_keep *) attribute\n");
+               log("        will be skipped.\n");
                log("\n");
                log("    -ports\n");
-               log("        try to remove module ports.\n");
+               log("        try to remove module ports. ports with a (* bugpoint_keep *) attribute\n");
+               log("        will be skipped (useful for clocks, resets, etc.)\n");
                log("\n");
                log("    -cells\n");
-               log("        try to remove cells.\n");
+               log("        try to remove cells. cells with a (* bugpoint_keep *) attribute will\n");
+               log("        be skipped.\n");
                log("\n");
                log("    -connections\n");
                log("        try to reconnect ports to 'x.\n");
@@ -80,15 +85,15 @@ struct BugpointPass : public Pass {
                log("\n");
        }
 
-       bool run_yosys(RTLIL::Design *design, string yosys_cmd, string script)
+       bool run_yosys(RTLIL::Design *design, string yosys_cmd, string yosys_arg)
        {
                design->sort();
 
                std::ofstream f("bugpoint-case.il");
-               ILANG_BACKEND::dump_design(f, design, /*only_selected=*/false, /*flag_m=*/true, /*flag_n=*/false);
+               RTLIL_BACKEND::dump_design(f, design, /*only_selected=*/false, /*flag_m=*/true, /*flag_n=*/false);
                f.close();
 
-               string yosys_cmdline = stringf("%s -qq -L bugpoint-case.log -s %s bugpoint-case.il", yosys_cmd.c_str(), script.c_str());
+               string yosys_cmdline = stringf("%s -qq -L bugpoint-case.log %s bugpoint-case.il", yosys_cmd.c_str(), yosys_arg.c_str());
                return run_command(yosys_cmdline) == 0;
        }
 
@@ -133,18 +138,26 @@ struct BugpointPass : public Pass {
                int index = 0;
                if (modules)
                {
+                       Module *removed_module = nullptr;
                        for (auto module : design_copy->modules())
                        {
                                if (module->get_blackbox_attribute())
                                        continue;
 
+                               if (module->get_bool_attribute(ID::bugpoint_keep))
+                                   continue;
+
                                if (index++ == seed)
                                {
-                                       log("Trying to remove module %s.\n", module->name.c_str());
-                                       design_copy->remove(module);
-                                       return design_copy;
+                                       log_header(design, "Trying to remove module %s.\n", log_id(module));
+                                       removed_module = module;
+                                       break;
                                }
                        }
+                       if (removed_module) {
+                               design_copy->remove(removed_module);
+                               return design_copy;
+                       }
                }
                if (ports)
                {
@@ -155,18 +168,21 @@ struct BugpointPass : public Pass {
 
                                for (auto wire : mod->wires())
                                {
+                                       if (!wire->port_id)
+                                               continue;
+
                                        if (!stage2 && wire->get_bool_attribute(ID($bugpoint)))
                                                continue;
 
-                                       if (wire->port_input || wire->port_output)
+                                       if (wire->get_bool_attribute(ID::bugpoint_keep))
+                                               continue;
+
+                                       if (index++ == seed)
                                        {
-                                               if (index++ == seed)
-                                               {
-                                                       log("Trying to remove module port %s.\n", log_signal(wire));
-                                                       wire->port_input = wire->port_output = false;
-                                                       mod->fixup_ports();
-                                                       return design_copy;
-                                               }
+                                               log_header(design, "Trying to remove module port %s.\n", log_id(wire));
+                                               wire->port_input = wire->port_output = false;
+                                               mod->fixup_ports();
+                                               return design_copy;
                                        }
                                }
                        }
@@ -178,15 +194,24 @@ struct BugpointPass : public Pass {
                                if (mod->get_blackbox_attribute())
                                        continue;
 
+
+                               Cell *removed_cell = nullptr;
                                for (auto cell : mod->cells())
                                {
+                                       if (cell->get_bool_attribute(ID::bugpoint_keep))
+                                               continue;
+
                                        if (index++ == seed)
                                        {
-                                               log("Trying to remove cell %s.%s.\n", mod->name.c_str(), cell->name.c_str());
-                                               mod->remove(cell);
-                                               return design_copy;
+                                               log_header(design, "Trying to remove cell %s.%s.\n", log_id(mod), log_id(cell));
+                                               removed_cell = cell;
+                                               break;
                                        }
                                }
+                               if (removed_cell) {
+                                       mod->remove(removed_cell);
+                                       return design_copy;
+                               }
                        }
                }
                if (connections)
@@ -209,7 +234,7 @@ struct BugpointPass : public Pass {
 
                                                if (index++ == seed)
                                                {
-                                                       log("Trying to remove cell port %s.%s.%s.\n", mod->name.c_str(), cell->name.c_str(), it.first.c_str());
+                                                       log_header(design, "Trying to remove cell port %s.%s.%s.\n", log_id(mod), log_id(cell), log_id(it.first));
                                                        RTLIL::SigSpec port_x(State::Sx, port.size());
                                                        cell->unsetPort(it.first);
                                                        cell->setPort(it.first, port_x);
@@ -218,7 +243,7 @@ struct BugpointPass : public Pass {
 
                                                if (!stage2 && (cell->input(it.first) || cell->output(it.first)) && index++ == seed)
                                                {
-                                                       log("Trying to expose cell port %s.%s.%s as module port.\n", mod->name.c_str(), cell->name.c_str(), it.first.c_str());
+                                                       log_header(design, "Trying to expose cell port %s.%s.%s as module port.\n", log_id(mod), log_id(cell), log_id(it.first));
                                                        RTLIL::Wire *wire = mod->addWire(NEW_ID, port.size());
                                                        wire->set_bool_attribute(ID($bugpoint));
                                                        wire->port_input = cell->input(it.first);
@@ -250,7 +275,7 @@ struct BugpointPass : public Pass {
                                                {
                                                        if (index++ == seed)
                                                        {
-                                                               log("Trying to remove assign %s %s in %s.%s.\n", log_signal((*it).first), log_signal((*it).second), mod->name.c_str(), pr.first.c_str());
+                                                               log_header(design, "Trying to remove assign %s %s in %s.%s.\n", log_signal(it->first), log_signal(it->second), log_id(mod), log_id(pr.first));
                                                                cs->actions.erase(it);
                                                                return design_copy;
                                                        }
@@ -276,7 +301,7 @@ struct BugpointPass : public Pass {
                                                {
                                                        if (index++ == seed)
                                                        {
-                                                               log("Trying to remove sync %s update %s %s in %s.%s.\n", log_signal(sy->signal), log_signal((*it).first), log_signal((*it).second), mod->name.c_str(), pr.first.c_str());
+                                                               log_header(design, "Trying to remove sync %s update %s %s in %s.%s.\n", log_signal(sy->signal), log_signal(it->first), log_signal(it->second), log_id(mod), log_id(pr.first));
                                                                sy->actions.erase(it);
                                                                return design_copy;
                                                        }
@@ -288,12 +313,15 @@ struct BugpointPass : public Pass {
                return nullptr;
        }
 
-       void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+       void execute(std::vector<std::string> args, RTLIL::Design *design) override
        {
-               string yosys_cmd = "yosys", script, grep;
+               string yosys_cmd = "yosys", yosys_arg, grep;
                bool fast = false, clean = false;
                bool modules = false, ports = false, cells = false, connections = false, assigns = false, updates = false, has_part = false;
 
+               log_header(design, "Executing BUGPOINT pass (minimize testcases).\n");
+               log_push();
+
                size_t argidx;
                for (argidx = 1; argidx < args.size(); argidx++)
                {
@@ -302,7 +330,15 @@ struct BugpointPass : public Pass {
                                continue;
                        }
                        if (args[argidx] == "-script" && argidx + 1 < args.size()) {
-                               script = args[++argidx];
+                               if (!yosys_arg.empty())
+                                       log_cmd_error("A -script or -command option can be only provided once!\n");
+                               yosys_arg = stringf("-s %s", args[++argidx].c_str());
+                               continue;
+                       }
+                       if (args[argidx] == "-command" && argidx + 1 < args.size()) {
+                               if (!yosys_arg.empty())
+                                       log_cmd_error("A -script or -command option can be only provided once!\n");
+                               yosys_arg = stringf("-p %s", args[++argidx].c_str());
                                continue;
                        }
                        if (args[argidx] == "-grep" && argidx + 1 < args.size()) {
@@ -351,8 +387,8 @@ struct BugpointPass : public Pass {
                }
                extra_args(args, argidx, design);
 
-               if (script.empty())
-                       log_cmd_error("Missing -script option.\n");
+               if (yosys_arg.empty())
+                       log_cmd_error("Missing -script or -command option.\n");
 
                if (!has_part)
                {
@@ -368,8 +404,8 @@ struct BugpointPass : public Pass {
                        log_cmd_error("This command only operates on fully selected designs!\n");
 
                RTLIL::Design *crashing_design = clean_design(design, clean);
-               if (run_yosys(crashing_design, yosys_cmd, script))
-                       log_cmd_error("The provided script file and Yosys binary do not crash on this design!\n");
+               if (run_yosys(crashing_design, yosys_cmd, yosys_arg))
+                       log_cmd_error("The provided script file or command and Yosys binary do not crash on this design!\n");
                if (!check_logfile(grep))
                        log_cmd_error("The provided grep string is not found in the log file!\n");
 
@@ -385,12 +421,12 @@ struct BugpointPass : public Pass {
                                if (clean)
                                {
                                        RTLIL::Design *testcase = clean_design(simplified);
-                                       crashes = !run_yosys(testcase, yosys_cmd, script);
+                                       crashes = !run_yosys(testcase, yosys_cmd, yosys_arg);
                                        delete testcase;
                                }
                                else
                                {
-                                       crashes = !run_yosys(simplified, yosys_cmd, script);
+                                       crashes = !run_yosys(simplified, yosys_cmd, yosys_arg);
                                }
 
                                if (crashes && check_logfile(grep))
@@ -437,6 +473,8 @@ struct BugpointPass : public Pass {
                                design->add(module->clone());
                        delete crashing_design;
                }
+
+               log_pop();
        }
 } BugpointPass;