2 * yosys -- Yosys Open SYnthesis Suite
4 * Copyright (C) 2018 whitequark <whitequark@whitequark.org>
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 #include "kernel/yosys.h"
21 #include "backends/ilang/ilang_backend.h"
24 using namespace ILANG_BACKEND
;
25 PRIVATE_NAMESPACE_BEGIN
27 struct BugpointPass
: public Pass
{
28 BugpointPass() : Pass("bugpoint", "minimize testcases") { }
29 void help() YS_OVERRIDE
31 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
33 log(" bugpoint [options] -script <filename>\n");
35 log("This command minimizes the current design that is known to crash Yosys with the\n");
36 log("given script into a smaller testcase. It does this by removing an arbitrary part\n");
37 log("of the design and recursively invokes a new Yosys process with this modified design\n");
38 log("and the same script, repeating these steps while it can find a smaller design that\n");
39 log("still causes a crash. Once this command finishes, it replaces the current design\n");
40 log("with the smallest testcase it was able to produce.\n");
42 log(" -script <filename>\n");
43 log(" use this script to crash Yosys. required.\n");
45 log(" -yosys <filename>\n");
46 log(" use this Yosys binary. if not specified, `yosys` is used.\n");
48 log(" -grep <string>\n");
49 log(" only consider crashes that place this string in the log file.\n");
52 log(" run `proc_clean; clean -purge` after each minimization step. converges\n");
53 log(" faster, but produces larger testcases, and may fail to produce any\n");
54 log(" testcase at all if the crash is related to dangling wires.\n");
57 log(" run `proc_clean; clean -purge` before checking testcase and after\n");
58 log(" finishing. produces smaller and more useful testcases, but may fail to\n");
59 log(" produce any testcase at all if the crash is related to dangling wires.\n");
61 log("It is possible to constrain which parts of the design will be considered for\n");
62 log("removal. Unless one or more of the following options are specified, all parts\n");
63 log("will be considered.\n");
66 log(" try to remove modules. modules with a (* bugpoint_keep *) attribute\n");
67 log(" will be skipped.\n");
70 log(" try to remove module ports. ports with a (* bugpoint_keep *) attribute\n");
71 log(" will be skipped (useful for clocks, resets, etc.)\n");
74 log(" try to remove cells. cells with a (* bugpoint_keep *) attribute will\n");
75 log(" be skipped.\n");
77 log(" -connections\n");
78 log(" try to reconnect ports to 'x.\n");
81 log(" try to remove process assigns from cases.\n");
84 log(" try to remove process updates from syncs.\n");
88 bool run_yosys(RTLIL::Design
*design
, string yosys_cmd
, string script
)
92 std::ofstream
f("bugpoint-case.il");
93 ILANG_BACKEND::dump_design(f
, design
, /*only_selected=*/false, /*flag_m=*/true, /*flag_n=*/false);
96 string yosys_cmdline
= stringf("%s -qq -L bugpoint-case.log -s %s bugpoint-case.il", yosys_cmd
.c_str(), script
.c_str());
97 return run_command(yosys_cmdline
) == 0;
100 bool check_logfile(string grep
)
105 std::ifstream
f("bugpoint-case.log");
110 if (line
.find(grep
) != std::string::npos
)
116 RTLIL::Design
*clean_design(RTLIL::Design
*design
, bool do_clean
= true, bool do_delete
= false)
121 RTLIL::Design
*design_copy
= new RTLIL::Design
;
122 for (auto module
: design
->modules())
123 design_copy
->add(module
->clone());
124 Pass::call(design_copy
, "proc_clean -quiet");
125 Pass::call(design_copy
, "clean -purge");
132 RTLIL::Design
*simplify_something(RTLIL::Design
*design
, int &seed
, bool stage2
, bool modules
, bool ports
, bool cells
, bool connections
, bool assigns
, bool updates
)
134 RTLIL::Design
*design_copy
= new RTLIL::Design
;
135 for (auto module
: design
->modules())
136 design_copy
->add(module
->clone());
141 Module
*removed_module
= nullptr;
142 for (auto module
: design_copy
->modules())
144 if (module
->get_blackbox_attribute())
147 if (module
->get_bool_attribute(ID::bugpoint_keep
))
152 log_header(design
, "Trying to remove module %s.\n", log_id(module
));
153 removed_module
= module
;
157 if (removed_module
) {
158 design_copy
->remove(removed_module
);
164 for (auto mod
: design_copy
->modules())
166 if (mod
->get_blackbox_attribute())
169 for (auto wire
: mod
->wires())
174 if (!stage2
&& wire
->get_bool_attribute(ID($bugpoint
)))
177 if (wire
->get_bool_attribute(ID::bugpoint_keep
))
182 log_header(design
, "Trying to remove module port %s.\n", log_id(wire
));
183 wire
->port_input
= wire
->port_output
= false;
192 for (auto mod
: design_copy
->modules())
194 if (mod
->get_blackbox_attribute())
198 Cell
*removed_cell
= nullptr;
199 for (auto cell
: mod
->cells())
201 if (cell
->get_bool_attribute(ID::bugpoint_keep
))
206 log_header(design
, "Trying to remove cell %s.%s.\n", log_id(mod
), log_id(cell
));
212 mod
->remove(removed_cell
);
219 for (auto mod
: design_copy
->modules())
221 if (mod
->get_blackbox_attribute())
224 for (auto cell
: mod
->cells())
226 for (auto it
: cell
->connections_
)
228 RTLIL::SigSpec port
= cell
->getPort(it
.first
);
229 bool is_undef
= port
.is_fully_undef();
230 bool is_port
= port
.is_wire() && (port
.as_wire()->port_input
|| port
.as_wire()->port_output
);
232 if(is_undef
|| (!stage2
&& is_port
))
237 log_header(design
, "Trying to remove cell port %s.%s.%s.\n", log_id(mod
), log_id(cell
), log_id(it
.first
));
238 RTLIL::SigSpec
port_x(State::Sx
, port
.size());
239 cell
->unsetPort(it
.first
);
240 cell
->setPort(it
.first
, port_x
);
244 if (!stage2
&& (cell
->input(it
.first
) || cell
->output(it
.first
)) && index
++ == seed
)
246 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
));
247 RTLIL::Wire
*wire
= mod
->addWire(NEW_ID
, port
.size());
248 wire
->set_bool_attribute(ID($bugpoint
));
249 wire
->port_input
= cell
->input(it
.first
);
250 wire
->port_output
= cell
->output(it
.first
);
251 cell
->unsetPort(it
.first
);
252 cell
->setPort(it
.first
, wire
);
262 for (auto mod
: design_copy
->modules())
264 if (mod
->get_blackbox_attribute())
267 for (auto &pr
: mod
->processes
)
269 vector
<RTLIL::CaseRule
*> cases
= {&pr
.second
->root_case
};
270 while (!cases
.empty())
272 RTLIL::CaseRule
*cs
= cases
[0];
273 cases
.erase(cases
.begin());
274 for (auto it
= cs
->actions
.begin(); it
!= cs
->actions
.end(); ++it
)
278 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
));
279 cs
->actions
.erase(it
);
283 for (auto &sw
: cs
->switches
)
284 cases
.insert(cases
.end(), sw
->cases
.begin(), sw
->cases
.end());
291 for (auto mod
: design_copy
->modules())
293 if (mod
->get_blackbox_attribute())
296 for (auto &pr
: mod
->processes
)
298 for (auto &sy
: pr
.second
->syncs
)
300 for (auto it
= sy
->actions
.begin(); it
!= sy
->actions
.end(); ++it
)
304 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
));
305 sy
->actions
.erase(it
);
316 void execute(std::vector
<std::string
> args
, RTLIL::Design
*design
) YS_OVERRIDE
318 string yosys_cmd
= "yosys", script
, grep
;
319 bool fast
= false, clean
= false;
320 bool modules
= false, ports
= false, cells
= false, connections
= false, assigns
= false, updates
= false, has_part
= false;
322 log_header(design
, "Executing BUGPOINT pass (minimize testcases).\n");
326 for (argidx
= 1; argidx
< args
.size(); argidx
++)
328 if (args
[argidx
] == "-yosys" && argidx
+ 1 < args
.size()) {
329 yosys_cmd
= args
[++argidx
];
332 if (args
[argidx
] == "-script" && argidx
+ 1 < args
.size()) {
333 script
= args
[++argidx
];
336 if (args
[argidx
] == "-grep" && argidx
+ 1 < args
.size()) {
337 grep
= args
[++argidx
];
340 if (args
[argidx
] == "-fast") {
344 if (args
[argidx
] == "-clean") {
348 if (args
[argidx
] == "-modules") {
353 if (args
[argidx
] == "-ports") {
358 if (args
[argidx
] == "-cells") {
363 if (args
[argidx
] == "-connections") {
368 if (args
[argidx
] == "-assigns") {
373 if (args
[argidx
] == "-updates") {
380 extra_args(args
, argidx
, design
);
383 log_cmd_error("Missing -script option.\n");
395 if (!design
->full_selection())
396 log_cmd_error("This command only operates on fully selected designs!\n");
398 RTLIL::Design
*crashing_design
= clean_design(design
, clean
);
399 if (run_yosys(crashing_design
, yosys_cmd
, script
))
400 log_cmd_error("The provided script file and Yosys binary do not crash on this design!\n");
401 if (!check_logfile(grep
))
402 log_cmd_error("The provided grep string is not found in the log file!\n");
405 bool found_something
= false, stage2
= false;
408 if (RTLIL::Design
*simplified
= simplify_something(crashing_design
, seed
, stage2
, modules
, ports
, cells
, connections
, assigns
, updates
))
410 simplified
= clean_design(simplified
, fast
, /*do_delete=*/true);
415 RTLIL::Design
*testcase
= clean_design(simplified
);
416 crashes
= !run_yosys(testcase
, yosys_cmd
, script
);
421 crashes
= !run_yosys(simplified
, yosys_cmd
, script
);
424 if (crashes
&& check_logfile(grep
))
426 log("Testcase crashes.\n");
427 if (crashing_design
!= design
)
428 delete crashing_design
;
429 crashing_design
= simplified
;
430 found_something
= true;
434 log("Testcase does not crash.\n");
443 found_something
= false;
448 log("Demoting introduced module ports.\n");
453 log("Simplifications exhausted.\n");
460 if (crashing_design
!= design
)
462 Pass::call(design
, "design -reset");
463 crashing_design
= clean_design(crashing_design
, clean
, /*do_delete=*/true);
464 for (auto module
: crashing_design
->modules())
465 design
->add(module
->clone());
466 delete crashing_design
;
473 PRIVATE_NAMESPACE_END