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 Module
*removed_module
= nullptr;
137 for (auto module
: design_copy
->modules())
139 if (module
->get_blackbox_attribute())
144 log("Trying to remove module %s.\n", module
->name
.c_str());
145 removed_module
= module
;
149 if (removed_module
) {
150 design_copy
->remove(removed_module
);
156 for (auto mod
: design_copy
->modules())
158 if (mod
->get_blackbox_attribute())
161 for (auto wire
: mod
->wires())
163 if (!stage2
&& wire
->get_bool_attribute(ID($bugpoint
)))
166 if (wire
->port_input
|| wire
->port_output
)
170 log("Trying to remove module port %s.\n", log_signal(wire
));
171 wire
->port_input
= wire
->port_output
= false;
181 for (auto mod
: design_copy
->modules())
183 if (mod
->get_blackbox_attribute())
186 Cell
*removed_cell
= nullptr;
187 for (auto cell
: mod
->cells())
191 log("Trying to remove cell %s.%s.\n", mod
->name
.c_str(), cell
->name
.c_str());
197 mod
->remove(removed_cell
);
204 for (auto mod
: design_copy
->modules())
206 if (mod
->get_blackbox_attribute())
209 for (auto cell
: mod
->cells())
211 for (auto it
: cell
->connections_
)
213 RTLIL::SigSpec port
= cell
->getPort(it
.first
);
214 bool is_undef
= port
.is_fully_undef();
215 bool is_port
= port
.is_wire() && (port
.as_wire()->port_input
|| port
.as_wire()->port_output
);
217 if(is_undef
|| (!stage2
&& is_port
))
222 log("Trying to remove cell port %s.%s.%s.\n", mod
->name
.c_str(), cell
->name
.c_str(), it
.first
.c_str());
223 RTLIL::SigSpec
port_x(State::Sx
, port
.size());
224 cell
->unsetPort(it
.first
);
225 cell
->setPort(it
.first
, port_x
);
229 if (!stage2
&& (cell
->input(it
.first
) || cell
->output(it
.first
)) && index
++ == seed
)
231 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());
232 RTLIL::Wire
*wire
= mod
->addWire(NEW_ID
, port
.size());
233 wire
->set_bool_attribute(ID($bugpoint
));
234 wire
->port_input
= cell
->input(it
.first
);
235 wire
->port_output
= cell
->output(it
.first
);
236 cell
->unsetPort(it
.first
);
237 cell
->setPort(it
.first
, wire
);
247 for (auto mod
: design_copy
->modules())
249 if (mod
->get_blackbox_attribute())
252 for (auto &pr
: mod
->processes
)
254 vector
<RTLIL::CaseRule
*> cases
= {&pr
.second
->root_case
};
255 while (!cases
.empty())
257 RTLIL::CaseRule
*cs
= cases
[0];
258 cases
.erase(cases
.begin());
259 for (auto it
= cs
->actions
.begin(); it
!= cs
->actions
.end(); ++it
)
263 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());
264 cs
->actions
.erase(it
);
268 for (auto &sw
: cs
->switches
)
269 cases
.insert(cases
.end(), sw
->cases
.begin(), sw
->cases
.end());
276 for (auto mod
: design_copy
->modules())
278 if (mod
->get_blackbox_attribute())
281 for (auto &pr
: mod
->processes
)
283 for (auto &sy
: pr
.second
->syncs
)
285 for (auto it
= sy
->actions
.begin(); it
!= sy
->actions
.end(); ++it
)
289 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());
290 sy
->actions
.erase(it
);
301 void execute(std::vector
<std::string
> args
, RTLIL::Design
*design
) YS_OVERRIDE
303 string yosys_cmd
= "yosys", script
, grep
;
304 bool fast
= false, clean
= false;
305 bool modules
= false, ports
= false, cells
= false, connections
= false, assigns
= false, updates
= false, has_part
= false;
308 for (argidx
= 1; argidx
< args
.size(); argidx
++)
310 if (args
[argidx
] == "-yosys" && argidx
+ 1 < args
.size()) {
311 yosys_cmd
= args
[++argidx
];
314 if (args
[argidx
] == "-script" && argidx
+ 1 < args
.size()) {
315 script
= args
[++argidx
];
318 if (args
[argidx
] == "-grep" && argidx
+ 1 < args
.size()) {
319 grep
= args
[++argidx
];
322 if (args
[argidx
] == "-fast") {
326 if (args
[argidx
] == "-clean") {
330 if (args
[argidx
] == "-modules") {
335 if (args
[argidx
] == "-ports") {
340 if (args
[argidx
] == "-cells") {
345 if (args
[argidx
] == "-connections") {
350 if (args
[argidx
] == "-assigns") {
355 if (args
[argidx
] == "-updates") {
362 extra_args(args
, argidx
, design
);
365 log_cmd_error("Missing -script option.\n");
377 if (!design
->full_selection())
378 log_cmd_error("This command only operates on fully selected designs!\n");
380 RTLIL::Design
*crashing_design
= clean_design(design
, clean
);
381 if (run_yosys(crashing_design
, yosys_cmd
, script
))
382 log_cmd_error("The provided script file and Yosys binary do not crash on this design!\n");
383 if (!check_logfile(grep
))
384 log_cmd_error("The provided grep string is not found in the log file!\n");
387 bool found_something
= false, stage2
= false;
390 if (RTLIL::Design
*simplified
= simplify_something(crashing_design
, seed
, stage2
, modules
, ports
, cells
, connections
, assigns
, updates
))
392 simplified
= clean_design(simplified
, fast
, /*do_delete=*/true);
397 RTLIL::Design
*testcase
= clean_design(simplified
);
398 crashes
= !run_yosys(testcase
, yosys_cmd
, script
);
403 crashes
= !run_yosys(simplified
, yosys_cmd
, script
);
406 if (crashes
&& check_logfile(grep
))
408 log("Testcase crashes.\n");
409 if (crashing_design
!= design
)
410 delete crashing_design
;
411 crashing_design
= simplified
;
412 found_something
= true;
416 log("Testcase does not crash.\n");
425 found_something
= false;
430 log("Demoting introduced module ports.\n");
435 log("Simplifications exhausted.\n");
442 if (crashing_design
!= design
)
444 Pass::call(design
, "design -reset");
445 crashing_design
= clean_design(crashing_design
, clean
, /*do_delete=*/true);
446 for (auto module
: crashing_design
->modules())
447 design
->add(module
->clone());
448 delete crashing_design
;
453 PRIVATE_NAMESPACE_END