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 `clean -purge` after each minimization step. converges faster, but\n");
55 log(" produces larger testcases, and may fail to produce any testcase at all if\n");
56 log(" the crash is related to dangling wires.\n");
59 log(" run `clean -purge` before checking testcase and after finishing. produces\n");
60 log(" smaller and more useful testcases, but may fail to produce any testcase\n");
61 log(" 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");
77 bool run_yosys(RTLIL::Design
*design
, string yosys_cmd
, string script
)
81 std::ofstream
f("bugpoint-case.il");
82 ILANG_BACKEND::dump_design(f
, design
, /*only_selected=*/false, /*flag_m=*/true, /*flag_n=*/false);
85 string yosys_cmdline
= stringf("%s -qq -L bugpoint-case.log -s %s bugpoint-case.il", yosys_cmd
.c_str(), script
.c_str());
86 return run_command(yosys_cmdline
) == 0;
89 bool check_logfile(string grep
)
94 std::ifstream
f("bugpoint-case.log");
99 if (line
.find(grep
) != std::string::npos
)
105 RTLIL::Design
*clean_design(RTLIL::Design
*design
, bool do_clean
= true, bool do_delete
= false)
110 RTLIL::Design
*design_copy
= new RTLIL::Design
;
111 for (auto &it
: design
->modules_
)
112 design_copy
->add(it
.second
->clone());
113 Pass::call(design_copy
, "clean -purge");
120 RTLIL::Design
*simplify_something(RTLIL::Design
*design
, int &seed
, bool stage2
, bool modules
, bool ports
, bool cells
, bool connections
)
122 RTLIL::Design
*design_copy
= new RTLIL::Design
;
123 for (auto &it
: design
->modules_
)
124 design_copy
->add(it
.second
->clone());
129 for (auto &it
: design_copy
->modules_
)
131 if (it
.second
->get_blackbox_attribute())
136 log("Trying to remove module %s.\n", it
.first
.c_str());
137 design_copy
->remove(it
.second
);
144 for (auto mod
: design_copy
->modules())
146 if (mod
->get_blackbox_attribute())
149 for (auto wire
: mod
->wires())
151 if (!stage2
&& wire
->get_bool_attribute("$bugpoint"))
154 if (wire
->port_input
|| wire
->port_output
)
158 log("Trying to remove module port %s.\n", log_signal(wire
));
159 wire
->port_input
= wire
->port_output
= false;
169 for (auto mod
: design_copy
->modules())
171 if (mod
->get_blackbox_attribute())
174 for (auto &it
: mod
->cells_
)
178 log("Trying to remove cell %s.%s.\n", mod
->name
.c_str(), it
.first
.c_str());
179 mod
->remove(it
.second
);
187 for (auto mod
: design_copy
->modules())
189 if (mod
->get_blackbox_attribute())
192 for (auto cell
: mod
->cells())
194 for (auto it
: cell
->connections_
)
196 RTLIL::SigSpec port
= cell
->getPort(it
.first
);
197 bool is_undef
= port
.is_fully_undef();
198 bool is_port
= port
.is_wire() && (port
.as_wire()->port_input
|| port
.as_wire()->port_output
);
200 if(is_undef
|| (!stage2
&& is_port
))
205 log("Trying to remove cell port %s.%s.%s.\n", mod
->name
.c_str(), cell
->name
.c_str(), it
.first
.c_str());
206 RTLIL::SigSpec
port_x(State::Sx
, port
.size());
207 cell
->unsetPort(it
.first
);
208 cell
->setPort(it
.first
, port_x
);
212 if (!stage2
&& (cell
->input(it
.first
) || cell
->output(it
.first
)) && index
++ == seed
)
214 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());
215 RTLIL::Wire
*wire
= mod
->addWire(NEW_ID
, port
.size());
216 wire
->set_bool_attribute("$bugpoint");
217 wire
->port_input
= cell
->input(it
.first
);
218 wire
->port_output
= cell
->output(it
.first
);
219 cell
->unsetPort(it
.first
);
220 cell
->setPort(it
.first
, wire
);
231 void execute(std::vector
<std::string
> args
, RTLIL::Design
*design
) YS_OVERRIDE
233 string yosys_cmd
= "yosys", script
, grep
;
234 bool fast
= false, clean
= false;
235 bool modules
= false, ports
= false, cells
= false, connections
= false, has_part
= false;
238 for (argidx
= 1; argidx
< args
.size(); argidx
++)
240 if (args
[argidx
] == "-yosys" && argidx
+ 1 < args
.size()) {
241 yosys_cmd
= args
[++argidx
];
244 if (args
[argidx
] == "-script" && argidx
+ 1 < args
.size()) {
245 script
= args
[++argidx
];
248 if (args
[argidx
] == "-grep" && argidx
+ 1 < args
.size()) {
249 grep
= args
[++argidx
];
252 if (args
[argidx
] == "-fast") {
256 if (args
[argidx
] == "-clean") {
260 if (args
[argidx
] == "-modules") {
265 if (args
[argidx
] == "-ports") {
270 if (args
[argidx
] == "-cells") {
275 if (args
[argidx
] == "-connections") {
282 extra_args(args
, argidx
, design
);
292 if (!design
->full_selection())
293 log_cmd_error("This command only operates on fully selected designs!\n");
295 RTLIL::Design
*crashing_design
= clean_design(design
, clean
);
296 if (run_yosys(crashing_design
, yosys_cmd
, script
))
297 log_cmd_error("The provided script file and Yosys binary do not crash on this design!\n");
298 if (!check_logfile(grep
))
299 log_cmd_error("The provided grep string is not found in the log file!\n");
302 bool found_something
= false, stage2
= false;
305 if (RTLIL::Design
*simplified
= simplify_something(crashing_design
, seed
, stage2
, modules
, ports
, cells
, connections
))
307 simplified
= clean_design(simplified
, fast
, /*do_delete=*/true);
312 RTLIL::Design
*testcase
= clean_design(simplified
);
313 crashes
= !run_yosys(testcase
, yosys_cmd
, script
);
318 crashes
= !run_yosys(simplified
, yosys_cmd
, script
);
321 if (crashes
&& check_logfile(grep
))
323 log("Testcase crashes.\n");
324 if (crashing_design
!= design
)
325 delete crashing_design
;
326 crashing_design
= simplified
;
327 found_something
= true;
331 log("Testcase does not crash.\n");
340 found_something
= false;
345 log("Demoting introduced module ports.\n");
350 log("Simplifications exhausted.\n");
357 if (crashing_design
!= design
)
359 Pass::call(design
, "design -reset");
360 crashing_design
= clean_design(crashing_design
, clean
, /*do_delete=*/true);
361 for (auto &it
: crashing_design
->modules_
)
362 design
->add(it
.second
->clone());
363 delete crashing_design
;
368 PRIVATE_NAMESPACE_END