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]\n");
35 log("This command minimizes testcases that crash Yosys. It removes an arbitrary part\n");
36 log("of the design and recursively invokes Yosys with a given script, repeating these\n");
37 log("steps while it can find a smaller design that still causes a crash. Once this\n");
38 log("command finishes, it replaces the current design with the smallest testcase it\n");
39 log("was able to produce.\n");
41 log("It is possible to specify the kinds of design part that will be removed. If none\n");
42 log("are specified, all parts of design will be removed.\n");
44 log(" -yosys <filename>\n");
45 log(" use this Yosys binary. if not specified, `yosys` is used.\n");
47 log(" -script <filename>\n");
48 log(" use this script to crash Yosys. required.\n");
50 log(" -grep <string>\n");
51 log(" only consider crashes that place this string in the log file.\n");
54 log(" run `proc_clean; clean -purge` after each minimization step. converges\n");
55 log(" faster, but produces larger testcases, and may fail to produce any\n");
56 log(" testcase at all if the crash is related to dangling wires.\n");
59 log(" run `proc_clean; clean -purge` before checking testcase and after\n");
60 log(" finishing. produces smaller and more useful testcases, but may fail to\n");
61 log(" produce any testcase at all if the crash is related to dangling wires.\n");
64 log(" try to remove modules.\n");
67 log(" try to remove module ports.\n");
70 log(" try to remove cells.\n");
72 log(" -connections\n");
73 log(" try to reconnect ports to 'x.\n");
76 log(" try to remove process assigns from cases.\n");
79 log(" try to remove process updates from syncs.\n");
83 bool run_yosys(RTLIL::Design
*design
, string yosys_cmd
, string script
)
87 std::ofstream
f("bugpoint-case.il");
88 ILANG_BACKEND::dump_design(f
, design
, /*only_selected=*/false, /*flag_m=*/true, /*flag_n=*/false);
91 string yosys_cmdline
= stringf("%s -qq -L bugpoint-case.log -s %s bugpoint-case.il", yosys_cmd
.c_str(), script
.c_str());
92 return run_command(yosys_cmdline
) == 0;
95 bool check_logfile(string grep
)
100 std::ifstream
f("bugpoint-case.log");
105 if (line
.find(grep
) != std::string::npos
)
111 RTLIL::Design
*clean_design(RTLIL::Design
*design
, bool do_clean
= true, bool do_delete
= false)
116 RTLIL::Design
*design_copy
= new RTLIL::Design
;
117 for (auto module
: design
->modules())
118 design_copy
->add(module
->clone());
119 Pass::call(design_copy
, "proc_clean -quiet");
120 Pass::call(design_copy
, "clean -purge");
127 RTLIL::Design
*simplify_something(RTLIL::Design
*design
, int &seed
, bool stage2
, bool modules
, bool ports
, bool cells
, bool connections
, bool assigns
, bool updates
)
129 RTLIL::Design
*design_copy
= new RTLIL::Design
;
130 for (auto module
: design
->modules())
131 design_copy
->add(module
->clone());
136 for (auto module
: design_copy
->modules())
138 if (module
->get_blackbox_attribute())
143 log("Trying to remove module %s.\n", module
->name
.c_str());
144 design_copy
->remove(module
);
151 for (auto mod
: design_copy
->modules())
153 if (mod
->get_blackbox_attribute())
156 for (auto wire
: mod
->wires())
158 if (!stage2
&& wire
->get_bool_attribute(ID($bugpoint
)))
161 if (wire
->port_input
|| wire
->port_output
)
165 log("Trying to remove module port %s.\n", log_signal(wire
));
166 wire
->port_input
= wire
->port_output
= false;
176 for (auto mod
: design_copy
->modules())
178 if (mod
->get_blackbox_attribute())
181 for (auto cell
: mod
->cells())
185 log("Trying to remove cell %s.%s.\n", mod
->name
.c_str(), cell
->name
.c_str());
194 for (auto mod
: design_copy
->modules())
196 if (mod
->get_blackbox_attribute())
199 for (auto cell
: mod
->cells())
201 for (auto it
: cell
->connections_
)
203 RTLIL::SigSpec port
= cell
->getPort(it
.first
);
204 bool is_undef
= port
.is_fully_undef();
205 bool is_port
= port
.is_wire() && (port
.as_wire()->port_input
|| port
.as_wire()->port_output
);
207 if(is_undef
|| (!stage2
&& is_port
))
212 log("Trying to remove cell port %s.%s.%s.\n", mod
->name
.c_str(), cell
->name
.c_str(), it
.first
.c_str());
213 RTLIL::SigSpec
port_x(State::Sx
, port
.size());
214 cell
->unsetPort(it
.first
);
215 cell
->setPort(it
.first
, port_x
);
219 if (!stage2
&& (cell
->input(it
.first
) || cell
->output(it
.first
)) && index
++ == seed
)
221 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());
222 RTLIL::Wire
*wire
= mod
->addWire(NEW_ID
, port
.size());
223 wire
->set_bool_attribute(ID($bugpoint
));
224 wire
->port_input
= cell
->input(it
.first
);
225 wire
->port_output
= cell
->output(it
.first
);
226 cell
->unsetPort(it
.first
);
227 cell
->setPort(it
.first
, wire
);
237 for (auto mod
: design_copy
->modules())
239 if (mod
->get_blackbox_attribute())
242 for (auto &pr
: mod
->processes
)
244 vector
<RTLIL::CaseRule
*> cases
= {&pr
.second
->root_case
};
245 while (!cases
.empty())
247 RTLIL::CaseRule
*cs
= cases
[0];
248 cases
.erase(cases
.begin());
249 for (auto it
= cs
->actions
.begin(); it
!= cs
->actions
.end(); ++it
)
253 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());
254 cs
->actions
.erase(it
);
258 for (auto &sw
: cs
->switches
)
259 cases
.insert(cases
.end(), sw
->cases
.begin(), sw
->cases
.end());
266 for (auto mod
: design_copy
->modules())
268 if (mod
->get_blackbox_attribute())
271 for (auto &pr
: mod
->processes
)
273 for (auto &sy
: pr
.second
->syncs
)
275 for (auto it
= sy
->actions
.begin(); it
!= sy
->actions
.end(); ++it
)
279 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());
280 sy
->actions
.erase(it
);
291 void execute(std::vector
<std::string
> args
, RTLIL::Design
*design
) YS_OVERRIDE
293 string yosys_cmd
= "yosys", script
, grep
;
294 bool fast
= false, clean
= false;
295 bool modules
= false, ports
= false, cells
= false, connections
= false, assigns
= false, updates
= false, has_part
= false;
298 for (argidx
= 1; argidx
< args
.size(); argidx
++)
300 if (args
[argidx
] == "-yosys" && argidx
+ 1 < args
.size()) {
301 yosys_cmd
= args
[++argidx
];
304 if (args
[argidx
] == "-script" && argidx
+ 1 < args
.size()) {
305 script
= args
[++argidx
];
308 if (args
[argidx
] == "-grep" && argidx
+ 1 < args
.size()) {
309 grep
= args
[++argidx
];
312 if (args
[argidx
] == "-fast") {
316 if (args
[argidx
] == "-clean") {
320 if (args
[argidx
] == "-modules") {
325 if (args
[argidx
] == "-ports") {
330 if (args
[argidx
] == "-cells") {
335 if (args
[argidx
] == "-connections") {
340 if (args
[argidx
] == "-assigns") {
345 if (args
[argidx
] == "-updates") {
352 extra_args(args
, argidx
, design
);
355 log_cmd_error("Missing -script option.\n");
367 if (!design
->full_selection())
368 log_cmd_error("This command only operates on fully selected designs!\n");
370 RTLIL::Design
*crashing_design
= clean_design(design
, clean
);
371 if (run_yosys(crashing_design
, yosys_cmd
, script
))
372 log_cmd_error("The provided script file and Yosys binary do not crash on this design!\n");
373 if (!check_logfile(grep
))
374 log_cmd_error("The provided grep string is not found in the log file!\n");
377 bool found_something
= false, stage2
= false;
380 if (RTLIL::Design
*simplified
= simplify_something(crashing_design
, seed
, stage2
, modules
, ports
, cells
, connections
, assigns
, updates
))
382 simplified
= clean_design(simplified
, fast
, /*do_delete=*/true);
387 RTLIL::Design
*testcase
= clean_design(simplified
);
388 crashes
= !run_yosys(testcase
, yosys_cmd
, script
);
393 crashes
= !run_yosys(simplified
, yosys_cmd
, script
);
396 if (crashes
&& check_logfile(grep
))
398 log("Testcase crashes.\n");
399 if (crashing_design
!= design
)
400 delete crashing_design
;
401 crashing_design
= simplified
;
402 found_something
= true;
406 log("Testcase does not crash.\n");
415 found_something
= false;
420 log("Demoting introduced module ports.\n");
425 log("Simplifications exhausted.\n");
432 if (crashing_design
!= design
)
434 Pass::call(design
, "design -reset");
435 crashing_design
= clean_design(crashing_design
, clean
, /*do_delete=*/true);
436 for (auto module
: crashing_design
->modules())
437 design
->add(module
->clone());
438 delete crashing_design
;
443 PRIVATE_NAMESPACE_END