diff --git a/.gitignore b/.gitignore index 770fc1034..b90e00a92 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ *.whl *~ __pycache__ +/.cache /.cproject /.project /.settings @@ -19,6 +20,7 @@ __pycache__ /qtcreator.config /qtcreator.creator /qtcreator.creator.user +/compile_commands.json /coverage.info /coverage_html /Makefile.conf diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index 931454ada..b9958c5fb 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -616,7 +616,7 @@ std::string escape_c_string(const std::string &input) output.push_back('\\'); output.push_back(c); } else { - char l = c & 0x3, m = (c >> 3) & 0x3, h = (c >> 6) & 0x3; + char l = c & 0x7, m = (c >> 3) & 0x7, h = (c >> 6) & 0x3; output.append("\\"); output.push_back('0' + h); output.push_back('0' + m); diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc index 75b460f61..9fed0f2e4 100644 --- a/frontends/verific/verific.cc +++ b/frontends/verific/verific.cc @@ -3123,6 +3123,9 @@ std::string verific_import(Design *design, const std::mapfirst.c_str()); nl->PostElaborationProcess(); + + log(" Running operator optimization for %s.\n", it->first.c_str()); + nl->OperatorOptimization(); } if (nl_done.count(it->first) == 0) { @@ -4494,7 +4497,7 @@ struct ReadPass : public Pass { log("\n"); log(" read {-f|-F} \n"); log("\n"); - log("Load and execute the specified command file. (Requires Verific.)\n"); + log("Load and execute the specified command file.\n"); log("Check verific command for more information about supported commands in file.\n"); log("\n"); log("\n"); @@ -4608,10 +4611,14 @@ struct ReadPass : public Pass { if (args[1] == "-f" || args[1] == "-F") { if (use_verific) { args[0] = "verific"; - Pass::call(design, args); } else { - cmd_error(args, 1, "This version of Yosys is built without Verific support.\n"); +#if !defined(__wasm) + args[0] = "read_verilog_file_list"; +#else + cmd_error(args, 1, "Command files are not supported on this platform.\n"); +#endif } + Pass::call(design, args); return; } diff --git a/frontends/verilog/verilog_frontend.cc b/frontends/verilog/verilog_frontend.cc index d363d71fb..e4e705c39 100644 --- a/frontends/verilog/verilog_frontend.cc +++ b/frontends/verilog/verilog_frontend.cc @@ -26,6 +26,10 @@ * */ +#if !defined(__wasm) +#include +#endif + #include "verilog_frontend.h" #include "preproc.h" #include "kernel/yosys.h" @@ -672,6 +676,89 @@ struct VerilogDefines : public Pass { } } VerilogDefines; +#if !defined(__wasm) + +static void parse_file_list(const std::string &file_list_path, RTLIL::Design *design, bool relative_to_file_list_path) +{ + std::ifstream flist(file_list_path); + if (!flist.is_open()) { + log_error("Verilog file list file does not exist"); + exit(1); + } + + std::filesystem::path file_list_parent_dir = std::filesystem::path(file_list_path).parent_path(); + + std::string v_file_name; + while (std::getline(flist, v_file_name)) { + if (v_file_name.empty()) { + continue; + } + + std::filesystem::path verilog_file_path; + if (relative_to_file_list_path) { + verilog_file_path = file_list_parent_dir / v_file_name; + } else { + verilog_file_path = std::filesystem::current_path() / v_file_name; + } + + bool is_sv = (verilog_file_path.extension() == ".sv"); + + std::vector read_verilog_cmd = {"read_verilog", "-defer"}; + if (is_sv) { + read_verilog_cmd.push_back("-sv"); + } + read_verilog_cmd.push_back(verilog_file_path.string()); + Pass::call(design, read_verilog_cmd); + } + + flist.close(); +} + +struct VerilogFileList : public Pass { + VerilogFileList() : Pass("read_verilog_file_list", "Parse a Verilog file list") {} + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" read_verilog_file_list [options]\n"); + log("\n"); + log("Parse a Verilog file list, and pass the list of Verilog files to read_verilog\n"); + log("command\n"); + log("\n"); + log(" -F file_list_path\n"); + log(" File list file contains list of Verilog files to be parsed, any path is\n"); + log(" treated relative to the file list file\n"); + log("\n"); + log(" -f file_list_path\n"); + log(" File list file contains list of Verilog files to be parsed, any path is\n"); + log(" treated relative to current working directroy\n"); + log("\n"); + } + + void execute(std::vector args, RTLIL::Design *design) override + { + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + std::string arg = args[argidx]; + if (arg == "-F" && argidx + 1 < args.size()) { + std::string file_list_path = args[++argidx]; + parse_file_list(file_list_path, design, true); + continue; + } + if (arg == "-f" && argidx + 1 < args.size()) { + std::string file_list_path = args[++argidx]; + parse_file_list(file_list_path, design, false); + continue; + } + break; + } + + extra_args(args, argidx, design); + } +} VerilogFilelist; + +#endif + YOSYS_NAMESPACE_END // the yyerror function used by bison to report parser errors diff --git a/kernel/fmt.cc b/kernel/fmt.cc index 6ba1150ce..6e9f2924a 100644 --- a/kernel/fmt.cc +++ b/kernel/fmt.cc @@ -634,10 +634,11 @@ std::string escape_cxx_string(const std::string &input) output.push_back('\\'); output.push_back(c); } else { - char l = c & 0xf, h = (c >> 4) & 0xf; - output.append("\\x"); - output.push_back((h < 10 ? '0' + h : 'a' + h - 10)); - output.push_back((l < 10 ? '0' + l : 'a' + l - 10)); + char l = c & 0x7, m = (c >> 3) & 0x7, h = (c >> 6) & 0x3; + output.push_back('\\'); + output.push_back('0' + h); + output.push_back('0' + m); + output.push_back('0' + l); } } output.push_back('"'); diff --git a/kernel/yosys_common.h b/kernel/yosys_common.h index 9678ff965..dbd683380 100644 --- a/kernel/yosys_common.h +++ b/kernel/yosys_common.h @@ -334,7 +334,19 @@ RTLIL::IdString new_id_suffix(std::string file, int line, std::string func, std: #define NEW_ID_SUFFIX(suffix) \ YOSYS_NAMESPACE_PREFIX new_id_suffix(__FILE__, __LINE__, __FUNCTION__, suffix) -#define NEW_ID2 module->uniquify(cell->name.str()) +inline std::string removeNumericSuffix(const std::string& str) { + size_t pos = str.rfind('_'); // Find the last underscore + + if (pos != std::string::npos && pos + 1 < str.size()) { + // Check if everything after the underscore is a digit + if (std::all_of(str.begin() + pos + 1, str.end(), ::isdigit)) { + return str.substr(0, pos); // Return the string without the suffix + } + } + return str; // Return unchanged if no numeric suffix found +} + +#define NEW_ID2 module->uniquify(removeNumericSuffix(cell->name.str())) #define NEW_ID2_SUFFIX(suffix) module->uniquify(cell->name.str() + "_" + suffix) #define NEW_ID3 module->uniquify(cell_name.str()) #define NEW_ID3_SUFFIX(suffix) module->uniquify(cell_name.str() + "_" + suffix) diff --git a/passes/cmds/splitcells.cc b/passes/cmds/splitcells.cc index e30e87d1f..c5d12413b 100644 --- a/passes/cmds/splitcells.cc +++ b/passes/cmds/splitcells.cc @@ -88,7 +88,7 @@ struct SplitcellsWorker } if (GetSize(slices) <= 1) return 0; if (limit != -1 && GetSize(slices) > limit) { // skip if number of slices is above limit - log("Skipping %s cell %s/%s with high slice count %d.\n", log_id(cell->type), log_id(module), log_id(cell), GetSize(slices)); + log_debug("Skipping %s cell %s/%s with high slice count %d.\n", log_id(cell->type), log_id(module), log_id(cell), GetSize(slices)); return 0; } slices.push_back(GetSize(outsig)); @@ -107,8 +107,7 @@ struct SplitcellsWorker auto slice_signal = [&](SigSpec old_sig) -> SigSpec { SigSpec new_sig; - for (int i = 0; i < GetSize(old_sig); i += GetSize(outsig)) { - int offset = i+slice_lsb; + for (int offset = slice_lsb; offset < GetSize(old_sig); offset += GetSize(outsig)) { int length = std::min(GetSize(old_sig)-offset, slice_msb-slice_lsb+1); new_sig.append(old_sig.extract(offset, length)); } @@ -159,7 +158,7 @@ struct SplitcellsWorker if (GetSize(slices) <= 1) return 0; if (limit != -1 && GetSize(slices) > limit) { // skip if number of slices is above limit - log("Skipping %s cell %s/%s with high slice count %d.\n", log_id(cell->type), log_id(module), log_id(cell), GetSize(slices)); + log_debug("Skipping %s cell %s/%s with high slice count %d.\n", log_id(cell->type), log_id(module), log_id(cell), GetSize(slices)); return 0; } slices.push_back(GetSize(outsig)); diff --git a/passes/opt/muxpack.cc b/passes/opt/muxpack.cc index fe60d5404..bb3fffff2 100644 --- a/passes/opt/muxpack.cc +++ b/passes/opt/muxpack.cc @@ -436,9 +436,15 @@ struct MuxpackPass : public Pass { int pmux_count = 0; for (auto module : design->selected_modules()) { - MuxpackWorker worker(module, assume_excl, make_excl); - mux_count += worker.mux_count; - pmux_count += worker.pmux_count; + int worker_mux_count = 0; + int worker_pmux_count = 0; + do { + MuxpackWorker worker(module, assume_excl, make_excl); + mux_count += worker.mux_count; + pmux_count += worker.pmux_count; + worker_mux_count = worker.mux_count; + worker_pmux_count = worker.pmux_count; + } while (worker_mux_count + worker_pmux_count > 0); } log("Converted %d (p)mux cells into %d pmux cells.\n", mux_count, pmux_count); diff --git a/passes/opt/opt_expr.cc b/passes/opt/opt_expr.cc index 5a39378d9..4f810c35a 100644 --- a/passes/opt/opt_expr.cc +++ b/passes/opt/opt_expr.cc @@ -1520,6 +1520,27 @@ skip_identity: goto next_cell; } + if (consume_x && mux_bool && (cell->type == ID($_MUX_) || (cell->type == ID($mux) && cell->parameters[ID::WIDTH] == 1)) && cell->getPort(ID::A) == State::S1) { + cover_list("opt.opt_expr.mux_ornot", "$mux", "$_MUX_", cell->type.str()); + log_debug("Replacing %s cell `%s' in module `%s' with or-gate and not-gate.\n", log_id(cell->type), log_id(cell), log_id(module)); + cell->setPort(ID::A, module->Not(NEW_ID2_SUFFIX("not"), cell->getPort(ID::S), false, cell->get_src_attribute())); + cell->unsetPort(ID::S); + if (cell->type == ID($mux)) { + Const width = cell->parameters[ID::WIDTH]; + cell->parameters[ID::A_WIDTH] = width; + cell->parameters[ID::B_WIDTH] = width; + cell->parameters[ID::Y_WIDTH] = width; + cell->parameters[ID::A_SIGNED] = 0; + cell->parameters[ID::B_SIGNED] = 0; + cell->parameters.erase(ID::WIDTH); + cell->type = ID($or); + } else + cell->type = ID($_OR_); + module->rename(cell, NEW_ID2_SUFFIX("ornot")); + did_something = true; + goto next_cell; + } + if (consume_x && mux_bool && cell->type.in(ID($mux), ID($_MUX_)) && cell->getPort(ID::B) == State::S1) { cover_list("opt.opt_expr.mux_or", "$mux", "$_MUX_", cell->type.str()); log_debug("Replacing %s cell `%s' in module `%s' with or-gate.\n", log_id(cell->type), log_id(cell), log_id(module)); @@ -1540,6 +1561,27 @@ skip_identity: goto next_cell; } + if (consume_x && mux_bool && cell->type.in(ID($mux), ID($_MUX_)) && cell->getPort(ID::B) == State::S0) { + cover_list("opt.opt_expr.mux_andnot", "$mux", "$_MUX_", cell->type.str()); + log_debug("Replacing %s cell `%s' in module `%s' with and-gate and not-gate.\n", log_id(cell->type), log_id(cell), log_id(module)); + cell->setPort(ID::B, module->Not(NEW_ID2_SUFFIX("not"), cell->getPort(ID::S), false, cell->get_src_attribute())); + cell->unsetPort(ID::S); + if (cell->type == ID($mux)) { + Const width = cell->parameters[ID::WIDTH]; + cell->parameters[ID::A_WIDTH] = width; + cell->parameters[ID::B_WIDTH] = width; + cell->parameters[ID::Y_WIDTH] = width; + cell->parameters[ID::A_SIGNED] = 0; + cell->parameters[ID::B_SIGNED] = 0; + cell->parameters.erase(ID::WIDTH); + cell->type = ID($and); + } else + cell->type = ID($_AND_); + module->rename(cell, NEW_ID2_SUFFIX("andnot")); + did_something = true; + goto next_cell; + } + if (mux_undef && cell->type.in(ID($mux), ID($pmux))) { RTLIL::SigSpec new_a, new_b, new_s; int width = GetSize(cell->getPort(ID::A)); diff --git a/passes/silimate/.gitignore b/passes/silimate/.gitignore new file mode 100644 index 000000000..9342ac23b --- /dev/null +++ b/passes/silimate/.gitignore @@ -0,0 +1 @@ +/peepopt*.h \ No newline at end of file diff --git a/passes/silimate/Makefile.inc b/passes/silimate/Makefile.inc index c936b0f54..c60e0693e 100644 --- a/passes/silimate/Makefile.inc +++ b/passes/silimate/Makefile.inc @@ -2,6 +2,7 @@ OBJS += passes/silimate/activity.o OBJS += passes/silimate/annotate_cell_fanout.o OBJS += passes/silimate/annotate_logic_depth.o +OBJS += passes/silimate/breakreduce.o OBJS += passes/silimate/breaksop.o OBJS += passes/silimate/bus_rebuild.o OBJS += passes/silimate/longloop_select.o @@ -11,3 +12,13 @@ OBJS += passes/silimate/segv.o OBJS += passes/silimate/selectconst.o OBJS += passes/silimate/splitfanout.o OBJS += passes/silimate/splitnetlist.o + +OBJS += passes/silimate/opt_expand.o +GENFILES += passes/silimate/peepopt_expand.h +passes/silimate/opt_expand.o: passes/silimate/peepopt_expand.h +$(eval $(call add_extra_objs,passes/silimate/peepopt_expand.h)) + +PEEPOPT_PATTERN = passes/silimate/peepopt_expand.pmg + +passes/silimate/peepopt_expand.h: passes/pmgen/pmgen.py $(PEEPOPT_PATTERN) + $(P) mkdir -p $(dir $@) && $(PYTHON_EXECUTABLE) $< -o $@ -p peepopt $(filter-out $<,$^) diff --git a/passes/silimate/breakreduce.cc b/passes/silimate/breakreduce.cc new file mode 100644 index 000000000..95d8e4a83 --- /dev/null +++ b/passes/silimate/breakreduce.cc @@ -0,0 +1,241 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Claire Xenia Wolf + * Copyright (C) 2025 Akash Levy + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include "kernel/sigtools.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +static void logic_reduce(RTLIL::Module *module, RTLIL::SigSpec &sig, RTLIL::Cell *cell) +{ + while (sig.size() > 1) + { + RTLIL::SigSpec sig_t = module->addWire(NEW_ID2_SUFFIX("t"), sig.size() / 2); // SILIMATE: Improve the naming + + for (int i = 0; i < sig.size(); i += 2) + { + if (i+1 == sig.size()) { + sig_t.append(sig[i]); + continue; + } + + RTLIL::Cell *gate = module->addCell(NEW_ID2, ID($or)); // SILIMATE: Improve the naming + gate->attributes = cell->attributes; + gate->setPort(ID::A, sig[i]); + gate->setPort(ID::B, sig[i+1]); + gate->setPort(ID::Y, sig_t[i/2]); + gate->fixup_parameters(); + } + + sig = sig_t; + } + + if (sig.size() == 0) + sig = State::S0; +} + +void breakreduce(RTLIL::Module *module, RTLIL::Cell *cell) +{ + RTLIL::SigSpec sig_a = cell->getPort(ID::A); + RTLIL::SigSpec sig_y = cell->getPort(ID::Y); + + if (sig_y.size() == 0) + return; + + if (sig_a.size() == 0) { + if (cell->type == ID($reduce_and)) module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(1, sig_y.size()))); + if (cell->type == ID($reduce_or)) module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(0, sig_y.size()))); + if (cell->type == ID($reduce_xor)) module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(0, sig_y.size()))); + if (cell->type == ID($reduce_xnor)) module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(1, sig_y.size()))); + if (cell->type == ID($reduce_bool)) module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(0, sig_y.size()))); + return; + } + + if (sig_y.size() > 1) { + module->connect(RTLIL::SigSig(sig_y.extract(1, sig_y.size()-1), RTLIL::SigSpec(0, sig_y.size()-1))); + sig_y = sig_y.extract(0, 1); + } + + IdString gate_type; + if (cell->type == ID($reduce_and)) gate_type = ID($and); + if (cell->type == ID($reduce_or)) gate_type = ID($or); + if (cell->type == ID($reduce_xor)) gate_type = ID($xor); + if (cell->type == ID($reduce_xnor)) gate_type = ID($xor); + if (cell->type == ID($reduce_bool)) gate_type = ID($or); + log_assert(!gate_type.empty()); + + RTLIL::Cell *last_output_cell = NULL; + + while (sig_a.size() > 1) + { + RTLIL::SigSpec sig_t = module->addWire(NEW_ID2_SUFFIX("t"), sig_a.size() / 2); // SILIMATE: Improve the naming + + for (int i = 0; i < sig_a.size(); i += 2) + { + if (i+1 == sig_a.size()) { + sig_t.append(sig_a[i]); + continue; + } + + RTLIL::Cell *gate = module->addCell(NEW_ID2, gate_type); // SILIMATE: Improve the naming + gate->attributes = cell->attributes; + gate->setPort(ID::A, sig_a[i]); + gate->setPort(ID::B, sig_a[i+1]); + gate->setPort(ID::Y, sig_t[i/2]); + gate->fixup_parameters(); + last_output_cell = gate; + } + + sig_a = sig_t; + } + + if (cell->type == ID($reduce_xnor)) { + RTLIL::SigSpec sig_t = module->addWire(NEW_ID2_SUFFIX("t")); // SILIMATE: Improve the naming + RTLIL::Cell *gate = module->addCell(NEW_ID2, ID($not)); // SILIMATE: Improve the naming + gate->attributes = cell->attributes; + gate->setPort(ID::A, sig_a); + gate->setPort(ID::Y, sig_t); + gate->fixup_parameters(); + last_output_cell = gate; + sig_a = sig_t; + } + + if (last_output_cell == NULL) { + module->connect(RTLIL::SigSig(sig_y, sig_a)); + } else { + last_output_cell->setPort(ID::Y, sig_y); + } + + module->remove(cell); +} + +void breaklognot(RTLIL::Module *module, RTLIL::Cell *cell) +{ + RTLIL::SigSpec sig_a = cell->getPort(ID::A); + logic_reduce(module, sig_a, cell); + + RTLIL::SigSpec sig_y = cell->getPort(ID::Y); + + if (sig_y.size() == 0) + return; + + if (sig_y.size() > 1) { + module->connect(RTLIL::SigSig(sig_y.extract(1, sig_y.size()-1), RTLIL::SigSpec(0, sig_y.size()-1))); + sig_y = sig_y.extract(0, 1); + } + + RTLIL::Cell *gate = module->addCell(NEW_ID2, ID($not)); // SILIMATE: Improve the naming + gate->attributes = cell->attributes; + gate->setPort(ID::A, sig_a); + gate->setPort(ID::Y, sig_y); + gate->fixup_parameters(); + + module->remove(cell); +} + +void breaklogbin(RTLIL::Module *module, RTLIL::Cell *cell) +{ + RTLIL::SigSpec sig_a = cell->getPort(ID::A); + logic_reduce(module, sig_a, cell); + + RTLIL::SigSpec sig_b = cell->getPort(ID::B); + logic_reduce(module, sig_b, cell); + + RTLIL::SigSpec sig_y = cell->getPort(ID::Y); + + if (sig_y.size() == 0) + return; + + if (sig_y.size() > 1) { + module->connect(RTLIL::SigSig(sig_y.extract(1, sig_y.size()-1), RTLIL::SigSpec(0, sig_y.size()-1))); + sig_y = sig_y.extract(0, 1); + } + + IdString gate_type; + if (cell->type == ID($logic_and)) gate_type = ID($and); + if (cell->type == ID($logic_or)) gate_type = ID($or); + log_assert(!gate_type.empty()); + + RTLIL::Cell *gate = module->addCell(NEW_ID2, gate_type); // SILIMATE: Improve the naming + gate->attributes = cell->attributes; + gate->setPort(ID::A, sig_a); + gate->setPort(ID::B, sig_b); + gate->setPort(ID::Y, sig_y); + gate->fixup_parameters(); + + module->remove(cell); +} + +void breakeqne(RTLIL::Module *module, RTLIL::Cell *cell) +{ + RTLIL::SigSpec sig_a = cell->getPort(ID::A); + RTLIL::SigSpec sig_b = cell->getPort(ID::B); + RTLIL::SigSpec sig_y = cell->getPort(ID::Y); + bool is_signed = cell->parameters.at(ID::A_SIGNED).as_bool(); + bool is_ne = cell->type.in(ID($ne), ID($nex)); + + RTLIL::SigSpec xor_out = module->addWire(NEW_ID2_SUFFIX("xor"), max(GetSize(sig_a), GetSize(sig_b))); // SILIMATE: Improve the naming + RTLIL::Cell *xor_cell = module->addXor(NEW_ID2, sig_a, sig_b, xor_out, is_signed, cell->get_src_attribute()); // SILIMATE: Improve the naming + xor_cell->attributes = cell->attributes; + + RTLIL::SigSpec reduce_out = is_ne ? sig_y : module->addWire(NEW_ID2_SUFFIX("reduce_out")); // SILIMATE: Improve the naming + RTLIL::Cell *reduce_cell = module->addReduceOr(NEW_ID2_SUFFIX("reduce_or"), xor_out, reduce_out, false, cell->get_src_attribute()); // SILIMATE: Improve the naming + reduce_cell->attributes = cell->attributes; + breakreduce(module, reduce_cell); + + if (!is_ne) { + RTLIL::Cell *not_cell = module->addLogicNot(NEW_ID2_SUFFIX("not"), reduce_out, sig_y, false, cell->get_src_attribute()); // SILIMATE: Improve the naming + not_cell->attributes = cell->attributes; + breaklognot(module, not_cell); + } + + module->remove(cell); +} + +struct BreakReducePass : public Pass { + BreakReducePass() : Pass("breakreduce", "break reduce-style cells into trees of primitives") { } + void help() override + { + log("\n"); + log(" breakreduce [selection]\n"); + log("\n"); + log("Break reduce-style ($reduce_*/$logic_*/$*eq*) cells into trees of primitives.\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *design) override + { + log_header(design, "Executing BREAKREDUCE pass (break reduce-style cells into trees of primitives).\n"); + extra_args(args, 1, design); + + for (auto module : design->selected_modules()) + for (auto cell : module->selected_cells()) + if (cell->type.in("$reduce_and", "$reduce_or", "$reduce_xor", "$reduce_xnor", "$reduce_bool")) + breakreduce(module, cell); + else if (cell->type.in("$logic_and", "$logic_or")) + breaklogbin(module, cell); + else if (cell->type.in("$logic_not")) + breaklognot(module, cell); + else if (cell->type.in("$eq", "$ne", "$eqx", "$nex")) + breakeqne(module, cell); + } +} BreakReducePass; + +PRIVATE_NAMESPACE_END diff --git a/passes/silimate/opt_balance_tree.cc b/passes/silimate/opt_balance_tree.cc index 929504d6c..5d893658a 100644 --- a/passes/silimate/opt_balance_tree.cc +++ b/passes/silimate/opt_balance_tree.cc @@ -444,7 +444,9 @@ struct OptBalanceTreePass : public Pass { log(" will be replicated and balanced into a tree, but the original\n"); log(" cell will remain, driving its original loads.\n"); log(" -fanout_limit n\n"); - log(" max fanout to split.\n"); + log(" Max fanout to split.\n"); + log(" -arith_only\n"); + log(" Only balance arithmetic cells.\n"); log("\n"); } void execute(std::vector args, RTLIL::Design *design) override @@ -452,6 +454,7 @@ struct OptBalanceTreePass : public Pass { log_header(design, "Executing OPT_BALANCE_TREE pass (cell cascades to trees).\n"); bool allow_off_chain = false; + bool arith_only = false; size_t argidx; int limit = -1; for (argidx = 1; argidx < args.size(); argidx++) { @@ -463,13 +466,18 @@ struct OptBalanceTreePass : public Pass { limit = std::stoi(args[++argidx]); continue; } + if (args[argidx] == "-arith_only") { + arith_only = true; + continue; + } break; } extra_args(args, argidx, design); // Count of all cells that were packed dict cell_count; - const vector cell_types = {ID($and), ID($or), ID($xor), ID($xnor), ID($add), ID($mul)}; + vector cell_types = {ID($and), ID($or), ID($xor), ID($xnor), ID($add), ID($mul)}; + if (arith_only) cell_types = {ID($add), ID($mul)}; for (auto module : design->selected_modules()) { OptBalanceTreeWorker worker(design, module, cell_types, allow_off_chain, limit); for (auto cell : worker.cell_count) { diff --git a/passes/silimate/opt_expand.cc b/passes/silimate/opt_expand.cc new file mode 100644 index 000000000..ddb13b0c1 --- /dev/null +++ b/passes/silimate/opt_expand.cc @@ -0,0 +1,76 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Claire Xenia Wolf + * Akash Levy + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ +#include "kernel/yosys.h" +#include "kernel/sigtools.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +bool did_something; + +#include "passes/silimate/peepopt_expand.h" + +struct OptExpandPass : public Pass { + OptExpandPass() : Pass("opt_expand", "expand conjunction") { } + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" opt_expand [selection]\n"); + log("\n"); + log("This pass expands conjunction (AND) operations into disjunction (OR).\n"); + log("\n"); + log("y = (a | b) & c ===> y = (a & c) | (b & c)\n"); + log("\n"); + log(" -max_iters n\n"); + log(" max number of pass iterations to run.\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *design) override + { + log_header(design, "Executing OPT_EXPAND pass (expand conjunction into disjunction).\n"); + + size_t argidx; + int max_iters = 10000; + for (argidx = 1; argidx < args.size(); argidx++) { + // No extra arguments + if (args[argidx] == "-max_iters" && argidx + 1 < args.size()) { + max_iters = std::stoi(args[++argidx]); + continue; + } + break; + } + extra_args(args, argidx, design); + for (auto module : design->selected_modules()) + { + did_something = true; + for (int i = 0; did_something && i < max_iters; i++) + { + log("ITERATION OF OPT_EXPAND\n"); + did_something = false; + peepopt_pm pm(module); + pm.setup(module->selected_cells()); + pm.run_expand(); + } + } + } +} PeepoptPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/silimate/peepopt_expand.pmg b/passes/silimate/peepopt_expand.pmg new file mode 100644 index 000000000..2c1787854 --- /dev/null +++ b/passes/silimate/peepopt_expand.pmg @@ -0,0 +1,75 @@ +pattern expand +// +// Authored by Akash Levy of Silimate, Inc. under ISC license. +// +// Expand logical conjunction (&) across (|) +// +// y = (a | b) & c ===> y = (a & c) | (b & c) +// + +state and_a and_b and_y or_a or_b or_y + +match or_gate + // Select OR gate + select or_gate->type.in($or, $_OR_) + set or_a port(or_gate, \A) + set or_b port(or_gate, \B) + set or_y port(or_gate, \Y) +endmatch + +code + // Fanout of each OR gate Y bit should be 1 (no bit-split) + if (nusers(or_y) != 2) + reject; +endcode + +match and_gate + // Select AND gate + select and_gate->type.in($and, $_AND_) + + // Set ports, allowing A and B to be swapped + choice A {\A, \B} + define B (A == \A ? \B : \A) + set and_a port(and_gate, A) + set and_b port(and_gate, B) + set and_y port(and_gate, \Y) + + // Connection + index port(and_gate, A) === or_y +endmatch + +code and_a and_b and_y or_a or_b or_y + // Unset all ports + and_gate->unsetPort(\A); + and_gate->unsetPort(\B); + and_gate->unsetPort(\Y); + or_gate->unsetPort(\A); + or_gate->unsetPort(\B); + or_gate->unsetPort(\Y); + + // Create new intermediate wires + Cell *cell = and_gate; + Wire *new_or_a = module->addWire(NEW_ID2, GetSize(and_y)); + Wire *new_or_b = module->addWire(NEW_ID2, GetSize(and_y)); + + // Create new AND gates connected to the OR gate + module->addAnd(NEW_ID2, or_a, and_b, new_or_a, false, cell->get_src_attribute()); + module->addAnd(NEW_ID2, or_b, and_b, new_or_b, false, cell->get_src_attribute()); + + // Update OR gate ports + or_gate->setPort(\A, new_or_a); + or_gate->setPort(\B, new_or_b); + or_gate->setPort(\Y, and_y); + + // Rename OR gate for formal + cell = or_gate; + module->rename(or_gate, NEW_ID2); + + // Remove AND gate + autoremove(and_gate); + + // Log, fixup, accept + log("expand pattern in %s: and=%s, or=%s\n", log_id(module), log_id(and_gate), log_id(or_gate)); + did_something = true; + accept; +endcode diff --git a/passes/techmap/simplemap.cc b/passes/techmap/simplemap.cc index 4ca3b8ab7..4e596911c 100644 --- a/passes/techmap/simplemap.cc +++ b/passes/techmap/simplemap.cc @@ -245,7 +245,7 @@ void simplemap_eqne(RTLIL::Module *module, RTLIL::Cell *cell) bool is_signed = cell->parameters.at(ID::A_SIGNED).as_bool(); bool is_ne = cell->type.in(ID($ne), ID($nex)); - RTLIL::SigSpec xor_out = module->addWire(NEW_ID2_SUFFIX("xor_out"), max(GetSize(sig_a), GetSize(sig_b))); // SILIMATE: Improve the naming + RTLIL::SigSpec xor_out = module->addWire(NEW_ID2_SUFFIX("xor"), max(GetSize(sig_a), GetSize(sig_b))); // SILIMATE: Improve the naming RTLIL::Cell *xor_cell = module->addXor(NEW_ID2, sig_a, sig_b, xor_out, is_signed, cell->get_src_attribute()); // SILIMATE: Improve the naming xor_cell->attributes = cell->attributes; simplemap_bitop(module, xor_cell); diff --git a/tests/various/bug4909.ys b/tests/various/bug4909.ys new file mode 100644 index 000000000..bf8cfb45b --- /dev/null +++ b/tests/various/bug4909.ys @@ -0,0 +1,44 @@ +read_rtlil << EOF +autoidx 20 +attribute \src "3510.v:2.1-26.10" +attribute \cells_not_processed 1 +attribute \tamara_triplicate 1 +module \top + attribute \src "3510.v:14.3-17.8" + wire width 4 $0\reg5[3:0] + attribute $bugpoint 1 + wire width 4 $auto$bugpoint.cc:258:simplify_something$12 + wire $delete_wire$14 + attribute \src "3510.v:13.19-13.59" + wire width 4 $xnor$3510.v:13$1_Y + attribute \src "3510.v:11.23-11.27" + wire width 4 \reg5 + attribute \src "3510.v:8.24-8.29" + wire width 3 \wire4 + attribute \src "3510.v:3.33-3.34" + wire width 12 output 1 \y + attribute \src "3510.v:13.19-13.59" + cell $xnor $xnor$3510.v:13$1 + parameter \A_SIGNED 0 + parameter \A_WIDTH 3 + parameter \B_SIGNED 0 + parameter \B_WIDTH 4 + parameter \Y_WIDTH 4 + connect \A 3'x + connect \B $auto$bugpoint.cc:258:simplify_something$12 + connect \Y $xnor$3510.v:13$1_Y + end + attribute \src "3510.v:14.3-17.8" + process $proc$3510.v:14$2 + assign $0\reg5[3:0] { \wire4 [2] \wire4 } + sync posedge $delete_wire$14 + update \reg5 $0\reg5[3:0] + end + connect \y [4:0] { \reg5 1'0 } + connect \wire4 $xnor$3510.v:13$1_Y [2:0] +end +EOF + +prep +splitcells +