diff options
Diffstat (limited to 'passes')
96 files changed, 3338 insertions, 557 deletions
diff --git a/passes/cmds/check.cc b/passes/cmds/check.cc index 2ad84838..b3622cb1 100644 --- a/passes/cmds/check.cc +++ b/passes/cmds/check.cc @@ -68,7 +68,7 @@ struct CheckPass : public Pass { } extra_args(args, argidx, design); - log_header("Executing CHECK pass (checking for obvious problems).\n"); + log_header(design, "Executing CHECK pass (checking for obvious problems).\n"); for (auto module : design->selected_whole_modules_warn()) { diff --git a/passes/cmds/connwrappers.cc b/passes/cmds/connwrappers.cc index 7828dce1..c9ab226d 100644 --- a/passes/cmds/connwrappers.cc +++ b/passes/cmds/connwrappers.cc @@ -198,7 +198,7 @@ struct ConnwrappersPass : public Pass { } extra_args(args, argidx, design); - log_header("Executing CONNWRAPPERS pass (connect extended ports of wrapper cells).\n"); + log_header(design, "Executing CONNWRAPPERS pass (connect extended ports of wrapper cells).\n"); for (auto &mod_it : design->modules_) if (design->selected(mod_it.second)) diff --git a/passes/cmds/cover.cc b/passes/cmds/cover.cc index 5644066a..1475475c 100644 --- a/passes/cmds/cover.cc +++ b/passes/cmds/cover.cc @@ -124,7 +124,7 @@ struct CoverPass : public Pass { extra_args(args, argidx, design); if (do_log) { - log_header("Printing code coverage counters.\n"); + log_header(design, "Printing code coverage counters.\n"); log("\n"); } diff --git a/passes/cmds/plugin.cc b/passes/cmds/plugin.cc index e2d80d9b..828c671d 100644 --- a/passes/cmds/plugin.cc +++ b/passes/cmds/plugin.cc @@ -31,19 +31,23 @@ std::map<std::string, std::string> loaded_plugin_aliases; #ifdef YOSYS_ENABLE_PLUGINS void load_plugin(std::string filename, std::vector<std::string> aliases) { + std::string orig_filename = filename; + if (filename.find('/') == std::string::npos) filename = "./" + filename; if (!loaded_plugins.count(filename)) { void *hdl = dlopen(filename.c_str(), RTLD_LAZY|RTLD_LOCAL); + if (hdl == NULL && orig_filename.find('/') == std::string::npos) + hdl = dlopen((proc_share_dirname() + "plugins/" + orig_filename + ".so").c_str(), RTLD_LAZY|RTLD_LOCAL); if (hdl == NULL) log_cmd_error("Can't load module `%s': %s\n", filename.c_str(), dlerror()); - loaded_plugins[filename] = hdl; + loaded_plugins[orig_filename] = hdl; Pass::init_register(); } for (auto &alias : aliases) - loaded_plugin_aliases[alias] = filename; + loaded_plugin_aliases[alias] = orig_filename; } #else void load_plugin(std::string, std::vector<std::string>) diff --git a/passes/cmds/qwp.cc b/passes/cmds/qwp.cc index 8ec815a7..1b800b6d 100644 --- a/passes/cmds/qwp.cc +++ b/passes/cmds/qwp.cc @@ -40,6 +40,7 @@ struct QwpConfig { bool ltr; bool alpha; + bool verbose; double grid; std::ofstream dump_file; @@ -47,6 +48,7 @@ struct QwpConfig QwpConfig() { ltr = false; alpha = false; + verbose = false; grid = 1.0 / 16; } }; @@ -211,10 +213,16 @@ struct QwpWorker // // M := [AA Ay] + if (config.verbose) + log("> System size: %d^2\n", GetSize(nodes)); + // Row major order int N = GetSize(nodes), N1 = N+1; vector<double> M(N * N1); + if (config.verbose) + log("> Edge constraints: %d\n", GetSize(edges)); + // Edge constraints: // A[i,:] := [ 0 0 .... 0 weight 0 ... 0 -weight 0 ... 0 0], y[i] := 0 // @@ -232,6 +240,9 @@ struct QwpWorker M[idx2 + idx1*N1] += -weight * weight; } + if (config.verbose) + log("> Node constraints: %d\n", GetSize(nodes)); + // Node constraints: // A[i,:] := [ 0 0 .... 0 weight 0 ... 0 0], y[i] := weight * pos // @@ -263,6 +274,9 @@ struct QwpWorker } #endif + if (config.verbose) + log("> Solving\n"); + // Solve "AA*x = Ay" // (least squares fit for "A*x = y") // @@ -277,6 +291,9 @@ struct QwpWorker // gaussian elimination for (int i = 0; i < N; i++) { + if (config.verbose && ((i+1) % (N/15)) == 0) + log("> Solved %d%%: %d/%d\n", (100*(i+1))/N, i+1, N); + // find best row int best_row = queue.front(); int best_row_queue_idx = 0; @@ -312,6 +329,9 @@ struct QwpWorker } } + if (config.verbose) + log("> Solved\n"); + log_assert(queue.empty()); log_assert(GetSize(pivot_cache) == N); @@ -334,6 +354,9 @@ struct QwpWorker } #endif + if (config.verbose) + log("> Update nodes\n"); + // update node positions for (int i = 0; i < N; i++) { @@ -778,6 +801,9 @@ struct QwpPass : public Pass { log(" -dump <html_file_name>\n"); log(" Dump a protocol of the placement algorithm to the html file.\n"); log("\n"); + log(" -v\n"); + log(" Verbose solver output for profiling or debugging\n"); + log("\n"); log("Note: This implementation of a quadratic wirelength placer uses exact\n"); log("dense matrix operations. It is only a toy-placer for small circuits.\n"); log("\n"); @@ -787,7 +813,7 @@ struct QwpPass : public Pass { QwpConfig config; xorshift32_state = 123456789; - log_header("Executing QWP pass (quadratic wirelength placer).\n"); + log_header(design, "Executing QWP pass (quadratic wirelength placer).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { @@ -799,6 +825,10 @@ struct QwpPass : public Pass { config.alpha = true; continue; } + if (args[argidx] == "-v") { + config.verbose = true; + continue; + } if (args[argidx] == "-grid" && argidx+1 < args.size()) { config.grid = 1.0 / atoi(args[++argidx].c_str()); continue; diff --git a/passes/cmds/scc.cc b/passes/cmds/scc.cc index 532026f2..bb6d7447 100644 --- a/passes/cmds/scc.cc +++ b/passes/cmds/scc.cc @@ -181,10 +181,10 @@ struct SccWorker cell2scc[cell] = sccList.size(); scc.insert(cell); sccList.push_back(scc); - workQueue.erase(cell); log("\n"); - } else - run(cell, 0, maxDepth); + } + + run(cell, 0, maxDepth); } log("Found %d SCCs in module %s.\n", int(sccList.size()), RTLIL::id2cstr(module->name)); @@ -264,7 +264,7 @@ struct SccPass : public Pass { int maxDepth = -1; int expect = -1; - log_header("Executing SCC pass (detecting logic loops).\n"); + log_header(design, "Executing SCC pass (detecting logic loops).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { diff --git a/passes/cmds/select.cc b/passes/cmds/select.cc index 3e64dd84..d2e1a2e2 100644 --- a/passes/cmds/select.cc +++ b/passes/cmds/select.cc @@ -952,7 +952,7 @@ struct SelectPass : public Pass { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" select [ -add | -del | -set <name> ] {-read <filename> | <selection>}\n"); - log(" select [ -assert-none | -assert-any ] {-read <filename> | <selection>}\n"); + log(" select [ <assert_option> ] {-read <filename> | <selection>}\n"); log(" select [ -list | -write <filename> | -count | -clear ]\n"); log(" select -module <modname>\n"); log("\n"); @@ -988,6 +988,14 @@ struct SelectPass : public Pass { log(" do not modify the current selection. instead assert that the given\n"); log(" selection contains exactly N objects.\n"); log("\n"); + log(" -assert-max N\n"); + log(" do not modify the current selection. instead assert that the given\n"); + log(" selection contains less than or exactly N objects.\n"); + log("\n"); + log(" -assert-min N\n"); + log(" do not modify the current selection. instead assert that the given\n"); + log(" selection contains at least N objects.\n"); + log("\n"); log(" -list\n"); log(" list all objects in the current selection\n"); log("\n"); @@ -1168,6 +1176,8 @@ struct SelectPass : public Pass { bool assert_none = false; bool assert_any = false; int assert_count = -1; + int assert_max = -1; + int assert_min = -1; std::string write_file, read_file; std::string set_name, sel_str; @@ -1197,6 +1207,14 @@ struct SelectPass : public Pass { assert_count = atoi(args[++argidx].c_str()); continue; } + if (arg == "-assert-max" && argidx+1 < args.size()) { + assert_max = atoi(args[++argidx].c_str()); + continue; + } + if (arg == "-assert-min" && argidx+1 < args.size()) { + assert_min = atoi(args[++argidx].c_str()); + continue; + } if (arg == "-clear") { clear_mode = true; continue; @@ -1273,14 +1291,14 @@ struct SelectPass : public Pass { if (none_mode && args.size() != 2) log_cmd_error("Option -none can not be combined with any other options.\n"); - if (add_mode + del_mode + assert_none + assert_any + (assert_count >= 0) > 1) - log_cmd_error("Options -add, -del, -assert-none, -assert-any or -assert-count can not be combined.\n"); + if (add_mode + del_mode + assert_none + assert_any + (assert_count >= 0) + (assert_max >= 0) + (assert_min >= 0) > 1) + log_cmd_error("Options -add, -del, -assert-none, -assert-any, assert-count, -assert-max or -assert-min can not be combined.\n"); - if ((list_mode || !write_file.empty() || count_mode) && (add_mode || del_mode || assert_none || assert_any || assert_count >= 0)) - log_cmd_error("Options -list, -write and -count can not be combined with -add, -del, -assert-none, -assert-any or -assert-count.\n"); + if ((list_mode || !write_file.empty() || count_mode) && (add_mode || del_mode || assert_none || assert_any || assert_count >= 0 || assert_max >= 0 || assert_min >= 0)) + log_cmd_error("Options -list, -write and -count can not be combined with -add, -del, -assert-none, -assert-any, assert-count, -assert-max, or -assert-min.\n"); - if (!set_name.empty() && (list_mode || !write_file.empty() || count_mode || add_mode || del_mode || assert_none || assert_any || assert_count >= 0)) - log_cmd_error("Option -set can not be combined with -list, -write, -count, -add, -del, -assert-none, -assert-any or -assert-count.\n"); + if (!set_name.empty() && (list_mode || !write_file.empty() || count_mode || add_mode || del_mode || assert_none || assert_any || assert_count >= 0 || assert_max >= 0 || assert_min >= 0)) + log_cmd_error("Option -set can not be combined with -list, -write, -count, -add, -del, -assert-none, -assert-any, -assert-count, -assert-max, or -assert-min.\n"); if (work_stack.size() == 0 && got_module) { RTLIL::Selection sel; @@ -1385,7 +1403,7 @@ struct SelectPass : public Pass { return; } - if (assert_count >= 0) + if (assert_count >= 0 || assert_max >= 0 || assert_min >= 0) { int total_count = 0; if (work_stack.size() == 0) @@ -1407,9 +1425,15 @@ struct SelectPass : public Pass { if (sel->selected_member(mod_it.first, it.first)) total_count++; } - if (assert_count != total_count) + if (assert_count >= 0 && assert_count != total_count) log_error("Assertion failed: selection contains %d elements instead of the asserted %d:%s\n", total_count, assert_count, sel_str.c_str()); + if (assert_max >= 0 && assert_max < total_count) + log_error("Assertion failed: selection contains %d elements, more than the maximum number %d:%s\n", + total_count, assert_max, sel_str.c_str()); + if (assert_min >= 0 && assert_min > total_count) + log_error("Assertion failed: selection contains %d elements, less than the minimum number %d:%s\n", + total_count, assert_min, sel_str.c_str()); return; } diff --git a/passes/cmds/setattr.cc b/passes/cmds/setattr.cc index 75c738b6..9b05ae32 100644 --- a/passes/cmds/setattr.cc +++ b/passes/cmds/setattr.cc @@ -215,6 +215,12 @@ struct ChparamPass : public Pass { } break; } + + for (int i = argidx; i < GetSize(args); i++) + if (design->module("$abstract\\" + args[i]) != nullptr && + design->module(RTLIL::escape_id(args[i])) == nullptr) + args[i] = "$abstract\\" + args[i]; + extra_args(args, argidx, design); do_setunset(new_parameters, setunset_list); diff --git a/passes/cmds/setundef.cc b/passes/cmds/setundef.cc index 9ca2e874..26b2eb87 100644 --- a/passes/cmds/setundef.cc +++ b/passes/cmds/setundef.cc @@ -79,11 +79,15 @@ struct SetundefPass : public Pass { log(" replace with random bits using the specified integer als seed\n"); log(" value for the random number generator.\n"); log("\n"); + log(" -init\n"); + log(" also create/update init values for flip-flops\n"); + log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { bool got_value = false; bool undriven_mode = false; + bool init_mode = false; SetundefWorker worker; size_t argidx; @@ -103,6 +107,10 @@ struct SetundefPass : public Pass { worker.next_bit_mode = 1; continue; } + if (args[argidx] == "-init") { + init_mode = true; + continue; + } if (args[argidx] == "-random" && !got_value && argidx+1 < args.size()) { got_value = true; worker.next_bit_mode = 2; @@ -118,12 +126,8 @@ struct SetundefPass : public Pass { if (!got_value) log_cmd_error("One of the options -zero, -one, or -random <seed> must be specified.\n"); - for (auto &mod_it : design->modules_) + for (auto module : design->selected_modules()) { - RTLIL::Module *module = mod_it.second; - if (!design->selected(module)) - continue; - if (undriven_mode) { if (!module->processes.empty()) @@ -151,6 +155,86 @@ struct SetundefPass : public Pass { } } + if (init_mode) + { + SigMap sigmap(module); + pool<SigBit> ffbits; + pool<Wire*> initwires; + + pool<IdString> fftypes; + fftypes.insert("$dff"); + fftypes.insert("$dffe"); + fftypes.insert("$dffsr"); + fftypes.insert("$adff"); + + std::vector<char> list_np = {'N', 'P'}, list_01 = {'0', '1'}; + + for (auto c1 : list_np) + fftypes.insert(stringf("$_DFF_%c_", c1)); + + for (auto c1 : list_np) + for (auto c2 : list_np) + fftypes.insert(stringf("$_DFFE_%c%c_", c1, c2)); + + for (auto c1 : list_np) + for (auto c2 : list_np) + for (auto c3 : list_01) + fftypes.insert(stringf("$_DFF_%c%c%c_", c1, c2, c3)); + + for (auto c1 : list_np) + for (auto c2 : list_np) + for (auto c3 : list_np) + fftypes.insert(stringf("$_DFFSR_%c%c%c_", c1, c2, c3)); + + for (auto cell : module->cells()) + { + if (!fftypes.count(cell->type)) + continue; + + for (auto bit : sigmap(cell->getPort("\\Q"))) + ffbits.insert(bit); + } + + for (auto wire : module->wires()) + { + if (!wire->attributes.count("\\init")) + continue; + + for (auto bit : sigmap(wire)) + ffbits.erase(bit); + + initwires.insert(wire); + } + + for (int wire_types = 0; wire_types < 2; wire_types++) + for (auto wire : module->wires()) + { + if (wire->name[0] == (wire_types ? '\\' : '$')) + next_wire: + continue; + + for (auto bit : sigmap(wire)) + if (!ffbits.count(bit)) + goto next_wire; + + for (auto bit : sigmap(wire)) + ffbits.erase(bit); + + initwires.insert(wire); + } + + for (auto wire : initwires) + { + Const &initval = wire->attributes["\\init"]; + + for (int i = 0; i < GetSize(wire); i++) + if (GetSize(initval) <= i) + initval.bits.push_back(worker.next_bit()); + else if (initval.bits[i] == State::Sx) + initval.bits[i] = worker.next_bit(); + } + } + module->rewrite_sigspecs(worker); } } diff --git a/passes/cmds/show.cc b/passes/cmds/show.cc index 3b03d680..3a3939a8 100644 --- a/passes/cmds/show.cc +++ b/passes/cmds/show.cc @@ -582,8 +582,9 @@ struct ShowPass : public Pass { log(" Run the specified command with the graphics file as parameter.\n"); log("\n"); log(" -format <format>\n"); - log(" Generate a graphics file in the specified format.\n"); - log(" Usually <format> is 'svg' or 'ps'.\n"); + log(" Generate a graphics file in the specified format. Use 'dot' to just\n"); + log(" generate a .dot file, or other <format> strings such as 'svg' or 'ps'\n"); + log(" to generate files in other formats (this calls the 'dot' command).\n"); log("\n"); log(" -lib <verilog_or_ilang_file>\n"); log(" Use the specified library file for determining whether cell ports are\n"); @@ -646,12 +647,13 @@ struct ShowPass : public Pass { log("unless another prefix is specified using -prefix <prefix>.\n"); log("\n"); log("Yosys on Windows and YosysJS use different defaults: The output is written\n"); - log("to 'show.dot' in the current directory and new viewer is launched.\n"); + log("to 'show.dot' in the current directory and new viewer is launched each time\n"); + log("the 'show' command is executed.\n"); log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Generating Graphviz representation of design.\n"); + log_header(design, "Generating Graphviz representation of design.\n"); log_push(); std::vector<std::pair<std::string, RTLIL::Selection>> color_selections; @@ -759,7 +761,7 @@ struct ShowPass : public Pass { } extra_args(args, argidx, design); - if (format != "ps") { + if (format != "ps" && format != "dot") { int modcount = 0; for (auto &mod_it : design->modules_) { if (mod_it.second->get_bool_attribute("\\blackbox")) @@ -770,7 +772,7 @@ struct ShowPass : public Pass { modcount++; } if (modcount > 1) - log_cmd_error("For formats different than 'ps' only one module must be selected.\n"); + log_cmd_error("For formats different than 'ps' or 'dot' only one module must be selected.\n"); } for (auto filename : libfiles) { @@ -784,7 +786,7 @@ struct ShowPass : public Pass { } if (libs.size() > 0) - log_header("Continuing show pass.\n"); + log_header(design, "Continuing show pass.\n"); std::string dot_file = stringf("%s.dot", prefix.c_str()); std::string out_file = stringf("%s.%s", prefix.c_str(), format.empty() ? "svg" : format.c_str()); @@ -806,7 +808,7 @@ struct ShowPass : public Pass { log_cmd_error("Nothing there to show.\n"); if (format != "dot" && !format.empty()) { - std::string cmd = stringf("dot -T%s -o '%s.new' '%s' && mv '%s.new' '%s'", format.c_str(), out_file.c_str(), dot_file.c_str(), out_file.c_str(), out_file.c_str()); + std::string cmd = stringf("dot -T%s '%s' > '%s.new' && mv '%s.new' '%s'", format.c_str(), dot_file.c_str(), out_file.c_str(), out_file.c_str(), out_file.c_str()); log("Exec: %s\n", cmd.c_str()); if (run_command(cmd) != 0) log_cmd_error("Shell command failed!\n"); diff --git a/passes/cmds/splice.cc b/passes/cmds/splice.cc index 2556fb74..7418ec4d 100644 --- a/passes/cmds/splice.cc +++ b/passes/cmds/splice.cc @@ -341,7 +341,7 @@ struct SplicePass : public Pass { if (!ports.empty() && !no_ports.empty()) log_cmd_error("The options -port and -no_port are exclusive!\n"); - log_header("Executing SPLICE pass (creating cells for signal splicing).\n"); + log_header(design, "Executing SPLICE pass (creating cells for signal splicing).\n"); for (auto &mod_it : design->modules_) { diff --git a/passes/cmds/splitnets.cc b/passes/cmds/splitnets.cc index 0d7892d7..14eeb066 100644 --- a/passes/cmds/splitnets.cc +++ b/passes/cmds/splitnets.cc @@ -109,7 +109,7 @@ struct SplitnetsPass : public Pass { bool flag_driver = false; std::string format = "[]:"; - log_header("Executing SPLITNETS pass (splitting up multi-bit signals).\n"); + log_header(design, "Executing SPLITNETS pass (splitting up multi-bit signals).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) diff --git a/passes/cmds/stat.cc b/passes/cmds/stat.cc index 048933fc..362a0edf 100644 --- a/passes/cmds/stat.cc +++ b/passes/cmds/stat.cc @@ -232,7 +232,7 @@ struct StatPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Printing statistics.\n"); + log_header(design, "Printing statistics.\n"); bool width_mode = false; RTLIL::Module *top_mod = NULL; diff --git a/passes/cmds/tee.cc b/passes/cmds/tee.cc index a0484090..3db2dbf0 100644 --- a/passes/cmds/tee.cc +++ b/passes/cmds/tee.cc @@ -45,10 +45,14 @@ struct TeePass : public Pass { log(" -a logfile\n"); log(" Write output to this file, append if exists.\n"); log("\n"); + log(" +INT, -INT\n"); + log(" Add/subract INT from the -v setting for this command.\n"); + log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { std::vector<FILE*> backup_log_files, files_to_close; + int backup_log_verbose_level = log_verbose_level; backup_log_files = log_files; size_t argidx; @@ -70,6 +74,10 @@ struct TeePass : public Pass { files_to_close.push_back(f); continue; } + if (GetSize(args[argidx]) >= 2 && (args[argidx][0] == '-' || args[argidx][0] == '+') && args[argidx][1] >= '0' && args[argidx][1] <= '9') { + log_verbose_level += atoi(args[argidx].c_str()); + continue; + } break; } @@ -85,6 +93,8 @@ struct TeePass : public Pass { for (auto cf : files_to_close) fclose(cf); + + log_verbose_level = backup_log_verbose_level; log_files = backup_log_files; } } TeePass; diff --git a/passes/cmds/torder.cc b/passes/cmds/torder.cc index 50317c02..56223610 100644 --- a/passes/cmds/torder.cc +++ b/passes/cmds/torder.cc @@ -48,7 +48,7 @@ struct TorderPass : public Pass { bool noautostop = false; dict<IdString, pool<IdString>> stop_db; - log_header("Executing TORDER pass (print cells in topological order).\n"); + log_header(design, "Executing TORDER pass (print cells in topological order).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { diff --git a/passes/equiv/equiv_induct.cc b/passes/equiv/equiv_induct.cc index cdb951ec..c958c3de 100644 --- a/passes/equiv/equiv_induct.cc +++ b/passes/equiv/equiv_induct.cc @@ -198,7 +198,7 @@ struct EquivInductPass : public Pass { bool model_undef = false; int max_seq = 4; - log_header("Executing EQUIV_INDUCT pass.\n"); + log_header(design, "Executing EQUIV_INDUCT pass.\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { diff --git a/passes/equiv/equiv_make.cc b/passes/equiv/equiv_make.cc index 8b063c54..40ca4262 100644 --- a/passes/equiv/equiv_make.cc +++ b/passes/equiv/equiv_make.cc @@ -464,7 +464,7 @@ struct EquivMakePass : public Pass { worker.read_blacklists(); worker.read_encfiles(); - log_header("Executing EQUIV_MAKE pass (creating equiv checking module).\n"); + log_header(design, "Executing EQUIV_MAKE pass (creating equiv checking module).\n"); worker.equiv_mod = design->addModule(RTLIL::escape_id(args[argidx+2])); worker.run(); diff --git a/passes/equiv/equiv_mark.cc b/passes/equiv/equiv_mark.cc index 3e9819d1..22c50176 100644 --- a/passes/equiv/equiv_mark.cc +++ b/passes/equiv/equiv_mark.cc @@ -218,7 +218,7 @@ struct EquivMarkPass : public Pass { } virtual void execute(std::vector<std::string> args, Design *design) { - log_header("Executing EQUIV_MARK pass.\n"); + log_header(design, "Executing EQUIV_MARK pass.\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { diff --git a/passes/equiv/equiv_miter.cc b/passes/equiv/equiv_miter.cc index 982176c4..eb2e5a17 100644 --- a/passes/equiv/equiv_miter.cc +++ b/passes/equiv/equiv_miter.cc @@ -333,7 +333,7 @@ struct EquivMiterPass : public Pass { found_two_modules: log_cmd_error("Exactly one module must be selected for 'equiv_miter'!\n"); - log_header("Executing EQUIV_MITER pass.\n"); + log_header(design, "Executing EQUIV_MITER pass.\n"); worker.miter_module = design->addModule(worker.miter_name); worker.run(); diff --git a/passes/equiv/equiv_purge.cc b/passes/equiv/equiv_purge.cc index f4141ad4..163b1009 100644 --- a/passes/equiv/equiv_purge.cc +++ b/passes/equiv/equiv_purge.cc @@ -189,7 +189,7 @@ struct EquivPurgePass : public Pass { } virtual void execute(std::vector<std::string> args, Design *design) { - log_header("Executing EQUIV_PURGE pass.\n"); + log_header(design, "Executing EQUIV_PURGE pass.\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { diff --git a/passes/equiv/equiv_remove.cc b/passes/equiv/equiv_remove.cc index b5c383b6..770497a5 100644 --- a/passes/equiv/equiv_remove.cc +++ b/passes/equiv/equiv_remove.cc @@ -46,7 +46,7 @@ struct EquivRemovePass : public Pass { bool mode_gate = false; int remove_count = 0; - log_header("Executing EQUIV_REMOVE pass.\n"); + log_header(design, "Executing EQUIV_REMOVE pass.\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { diff --git a/passes/equiv/equiv_simple.cc b/passes/equiv/equiv_simple.cc index fa22dc62..49963ed6 100644 --- a/passes/equiv/equiv_simple.cc +++ b/passes/equiv/equiv_simple.cc @@ -277,7 +277,7 @@ struct EquivSimplePass : public Pass { int success_counter = 0; int max_seq = 1; - log_header("Executing EQUIV_SIMPLE pass.\n"); + log_header(design, "Executing EQUIV_SIMPLE pass.\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { diff --git a/passes/equiv/equiv_status.cc b/passes/equiv/equiv_status.cc index 8a2f5e05..7b9230b3 100644 --- a/passes/equiv/equiv_status.cc +++ b/passes/equiv/equiv_status.cc @@ -41,7 +41,7 @@ struct EquivStatusPass : public Pass { bool assert_mode = false; int unproven_count = 0; - log_header("Executing EQUIV_STATUS pass.\n"); + log_header(design, "Executing EQUIV_STATUS pass.\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { diff --git a/passes/equiv/equiv_struct.cc b/passes/equiv/equiv_struct.cc index 2c85d2d3..c4ced6a7 100644 --- a/passes/equiv/equiv_struct.cc +++ b/passes/equiv/equiv_struct.cc @@ -321,7 +321,7 @@ struct EquivStructPass : public Pass { bool mode_fwd = false; int max_iter = -1; - log_header("Executing EQUIV_STRUCT pass.\n"); + log_header(design, "Executing EQUIV_STRUCT pass.\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { diff --git a/passes/fsm/fsm.cc b/passes/fsm/fsm.cc index 3f5564fc..3b537ecd 100644 --- a/passes/fsm/fsm.cc +++ b/passes/fsm/fsm.cc @@ -76,7 +76,7 @@ struct FsmPass : public Pass { std::string encfile_opt; std::string encoding_opt; - log_header("Executing FSM pass (extract and optimize FSM).\n"); + log_header(design, "Executing FSM pass (extract and optimize FSM).\n"); log_push(); size_t argidx; diff --git a/passes/fsm/fsm_detect.cc b/passes/fsm/fsm_detect.cc index 740113e3..6a560f16 100644 --- a/passes/fsm/fsm_detect.cc +++ b/passes/fsm/fsm_detect.cc @@ -36,18 +36,21 @@ static SigPool sig_at_port; static bool check_state_mux_tree(RTLIL::SigSpec old_sig, RTLIL::SigSpec sig, pool<Cell*> &recursion_monitor) { - if (sig_at_port.check_any(assign_map(sig))) - return false; - - if (sig.is_fully_const() || old_sig == sig) + if (sig.is_fully_const() || old_sig == sig) { return true; + } + + if (sig_at_port.check_any(assign_map(sig))) { + return false; + } std::set<sig2driver_entry_t> cellport_list; sig2driver.find(sig, cellport_list); for (auto &cellport : cellport_list) { - if ((cellport.first->type != "$mux" && cellport.first->type != "$pmux") || cellport.second != "\\Y") + if ((cellport.first->type != "$mux" && cellport.first->type != "$pmux") || cellport.second != "\\Y") { return false; + } if (recursion_monitor.count(cellport.first)) { log_warning("logic loop in mux tree at signal %s in module %s.\n", @@ -110,28 +113,134 @@ static bool check_state_users(RTLIL::SigSpec sig) static void detect_fsm(RTLIL::Wire *wire) { - if (wire->attributes.count("\\init") > 0) - return; - if (wire->attributes.count("\\fsm_encoding") > 0 || wire->width <= 1) + bool has_fsm_encoding_attr = wire->attributes.count("\\fsm_encoding") > 0 && wire->attributes.at("\\fsm_encoding").decode_string() != "none"; + bool has_fsm_encoding_none = wire->attributes.count("\\fsm_encoding") > 0 && wire->attributes.at("\\fsm_encoding").decode_string() == "none"; + bool has_init_attr = wire->attributes.count("\\init") > 0; + bool is_module_port = sig_at_port.check_any(assign_map(RTLIL::SigSpec(wire))); + bool looks_like_state_reg = false, looks_like_good_state_reg = false; + bool is_self_resetting = false; + + if (has_fsm_encoding_none) return; - if (sig_at_port.check_any(assign_map(RTLIL::SigSpec(wire)))) + + if (wire->width <= 1) { + if (has_fsm_encoding_attr) { + log_warning("Removing fsm_encoding attribute from 1-bit net: %s.%s\n", log_id(wire->module), log_id(wire)); + wire->attributes.erase("\\fsm_encoding"); + } return; + } std::set<sig2driver_entry_t> cellport_list; sig2driver.find(RTLIL::SigSpec(wire), cellport_list); - for (auto &cellport : cellport_list) { + + for (auto &cellport : cellport_list) + { if ((cellport.first->type != "$dff" && cellport.first->type != "$adff") || cellport.second != "\\Q") continue; + muxtree_cells.clear(); pool<Cell*> recursion_monitor; RTLIL::SigSpec sig_q = assign_map(cellport.first->getPort("\\Q")); RTLIL::SigSpec sig_d = assign_map(cellport.first->getPort("\\D")); - if (sig_q == RTLIL::SigSpec(wire) && check_state_mux_tree(sig_q, sig_d, recursion_monitor) && check_state_users(sig_q)) { - log("Found FSM state register %s in module %s.\n", wire->name.c_str(), module->name.c_str()); - wire->attributes["\\fsm_encoding"] = RTLIL::Const("auto"); - return; + + if (sig_q != assign_map(wire)) + continue; + + looks_like_state_reg = check_state_mux_tree(sig_q, sig_d, recursion_monitor); + looks_like_good_state_reg = check_state_users(sig_q); + + if (!looks_like_state_reg) + break; + + ConstEval ce(wire->module); + + std::set<sig2driver_entry_t> cellport_list; + sig2user.find(sig_q, cellport_list); + + for (auto &cellport : cellport_list) + { + RTLIL::Cell *cell = cellport.first; + bool set_output = false, clr_output = false; + + if (cell->type == "$ne") + set_output = true; + + if (cell->type == "$eq") + clr_output = true; + + if (!set_output && !clr_output) { + clr_output = true; + for (auto &port_it : cell->connections()) + if (port_it.first != "\\A" || port_it.first != "\\Y") + clr_output = false; + } + + if (set_output || clr_output) { + for (auto &port_it : cell->connections()) + if (cell->output(port_it.first)) { + SigSpec sig = assign_map(port_it.second); + Const val(set_output ? State::S1 : State::S0, GetSize(sig)); + ce.set(sig, val); + } + } + } + + SigSpec sig_y = sig_d, sig_undef; + if (ce.eval(sig_y, sig_undef)) + is_self_resetting = true; + } + + if (has_fsm_encoding_attr) + { + vector<string> warnings; + + if (is_module_port) + warnings.push_back("Forcing fsm recoding on module port might result in larger circuit.\n"); + + if (!looks_like_good_state_reg) + warnings.push_back("Users of state reg look like fsm recoding might result in larger circuit.\n"); + + if (has_init_attr) + warnings.push_back("Init value on fsm state registers are ignored. Possible simulation-synthesis mismatch!"); + + if (!looks_like_state_reg) + warnings.push_back("Doesn't look like a proper FSM. Possible simulation-synthesis mismatch!\n"); + + if (is_self_resetting) + warnings.push_back("FSM seems to be self-resetting. Possible simulation-synthesis mismatch!\n"); + + if (!warnings.empty()) { + string warnmsg = stringf("Regarding the user-specified fsm_encoding attribute on %s.%s:\n", log_id(wire->module), log_id(wire)); + for (auto w : warnings) warnmsg += " " + w; + log_warning("%s", warnmsg.c_str()); + } else { + log("FSM state register %s.%s already has fsm_encoding attribute.\n", log_id(wire->module), log_id(wire)); } } + else + if (looks_like_state_reg && looks_like_good_state_reg && !has_init_attr && !is_module_port && !is_self_resetting) + { + log("Found FSM state register %s.%s.\n", log_id(wire->module), log_id(wire)); + wire->attributes["\\fsm_encoding"] = RTLIL::Const("auto"); + } + else + if (looks_like_state_reg) + { + log("Not marking %s.%s as FSM state register:\n", log_id(wire->module), log_id(wire)); + + if (is_module_port) + log(" Register is connected to module port.\n"); + + if (!looks_like_good_state_reg) + log(" Users of register don't seem to benefit from recoding.\n"); + + if (has_init_attr) + log(" Register has an initialization value."); + + if (is_self_resetting) + log(" Circuit seems to be self-resetting.\n"); + } } struct FsmDetectPass : public Pass { @@ -154,7 +263,7 @@ struct FsmDetectPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing FSM_DETECT pass (finding FSMs in design).\n"); + log_header(design, "Executing FSM_DETECT pass (finding FSMs in design).\n"); extra_args(args, 1, design); CellTypes ct; diff --git a/passes/fsm/fsm_expand.cc b/passes/fsm/fsm_expand.cc index 43c9a792..3ded3f37 100644 --- a/passes/fsm/fsm_expand.cc +++ b/passes/fsm/fsm_expand.cc @@ -258,7 +258,7 @@ struct FsmExpandPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing FSM_EXPAND pass (merging auxiliary logic into FSMs).\n"); + log_header(design, "Executing FSM_EXPAND pass (merging auxiliary logic into FSMs).\n"); extra_args(args, 1, design); for (auto &mod_it : design->modules_) { diff --git a/passes/fsm/fsm_export.cc b/passes/fsm/fsm_export.cc index 0eff2884..1cbfcfae 100644 --- a/passes/fsm/fsm_export.cc +++ b/passes/fsm/fsm_export.cc @@ -152,7 +152,7 @@ struct FsmExportPass : public Pass { bool flag_origenc = false; size_t argidx; - log_header("Executing FSM_EXPORT pass (exporting FSMs in KISS2 file format).\n"); + log_header(design, "Executing FSM_EXPORT pass (exporting FSMs in KISS2 file format).\n"); for (argidx = 1; argidx < args.size(); argidx++) { arg = args[argidx]; diff --git a/passes/fsm/fsm_extract.cc b/passes/fsm/fsm_extract.cc index d61ac568..8a4ee3f2 100644 --- a/passes/fsm/fsm_extract.cc +++ b/passes/fsm/fsm_extract.cc @@ -92,12 +92,15 @@ static bool find_states(RTLIL::SigSpec sig, const RTLIL::SigSpec &dff_out, RTLIL if (reset_state && RTLIL::SigSpec(*reset_state).is_fully_undef()) do { + SigSpec new_reset_state; if (sig_aa.is_fully_def()) - *reset_state = sig_aa.as_const(); + new_reset_state = sig_aa.as_const(); else if (sig_bb.is_fully_def()) - *reset_state = sig_bb.as_const(); + new_reset_state = sig_bb.as_const(); else break; + new_reset_state.extend_u0(GetSize(*reset_state)); + *reset_state = new_reset_state.as_const(); log(" found reset state: %s (guessed from mux tree)\n", log_signal(*reset_state)); } while (0); @@ -416,7 +419,7 @@ struct FsmExtractPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing FSM_EXTRACT pass (extracting FSM from design).\n"); + log_header(design, "Executing FSM_EXTRACT pass (extracting FSM from design).\n"); extra_args(args, 1, design); CellTypes ct; diff --git a/passes/fsm/fsm_info.cc b/passes/fsm/fsm_info.cc index 20db82c1..2cc1a7d5 100644 --- a/passes/fsm/fsm_info.cc +++ b/passes/fsm/fsm_info.cc @@ -43,7 +43,7 @@ struct FsmInfoPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing FSM_INFO pass (dumping all available information on FSM cells).\n"); + log_header(design, "Executing FSM_INFO pass (dumping all available information on FSM cells).\n"); extra_args(args, 1, design); for (auto &mod_it : design->modules_) diff --git a/passes/fsm/fsm_map.cc b/passes/fsm/fsm_map.cc index 574b9a20..5b32ed59 100644 --- a/passes/fsm/fsm_map.cc +++ b/passes/fsm/fsm_map.cc @@ -335,7 +335,7 @@ struct FsmMapPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing FSM_MAP pass (mapping FSMs to basic logic).\n"); + log_header(design, "Executing FSM_MAP pass (mapping FSMs to basic logic).\n"); extra_args(args, 1, design); for (auto &mod_it : design->modules_) { diff --git a/passes/fsm/fsm_opt.cc b/passes/fsm/fsm_opt.cc index a7cc95ff..5b1da44f 100644 --- a/passes/fsm/fsm_opt.cc +++ b/passes/fsm/fsm_opt.cc @@ -336,7 +336,7 @@ struct FsmOptPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing FSM_OPT pass (simple optimizations of FSMs).\n"); + log_header(design, "Executing FSM_OPT pass (simple optimizations of FSMs).\n"); extra_args(args, 1, design); for (auto &mod_it : design->modules_) { diff --git a/passes/fsm/fsm_recode.cc b/passes/fsm/fsm_recode.cc index a4b45295..5102d833 100644 --- a/passes/fsm/fsm_recode.cc +++ b/passes/fsm/fsm_recode.cc @@ -157,7 +157,7 @@ struct FsmRecodePass : public Pass { FILE *encfile = NULL; std::string default_encoding; - log_header("Executing FSM_RECODE pass (re-assigning FSM state encoding).\n"); + log_header(design, "Executing FSM_RECODE pass (re-assigning FSM state encoding).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { std::string arg = args[argidx]; diff --git a/passes/hierarchy/hierarchy.cc b/passes/hierarchy/hierarchy.cc index fcc30d17..4d1e3987 100644 --- a/passes/hierarchy/hierarchy.cc +++ b/passes/hierarchy/hierarchy.cc @@ -261,7 +261,7 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check return did_something; } -void hierarchy_worker(RTLIL::Design *design, std::set<RTLIL::Module*> &used, RTLIL::Module *mod, int indent) +void hierarchy_worker(RTLIL::Design *design, std::set<RTLIL::Module*, IdString::compare_ptr_by_name<Module>> &used, RTLIL::Module *mod, int indent) { if (used.count(mod) > 0) return; @@ -287,7 +287,7 @@ void hierarchy_worker(RTLIL::Design *design, std::set<RTLIL::Module*> &used, RTL void hierarchy_clean(RTLIL::Design *design, RTLIL::Module *top, bool purge_lib) { - std::set<RTLIL::Module*> used; + std::set<RTLIL::Module*, IdString::compare_ptr_by_name<Module>> used; hierarchy_worker(design, used, top, 0); std::vector<RTLIL::Module*> del_modules; @@ -297,8 +297,6 @@ void hierarchy_clean(RTLIL::Design *design, RTLIL::Module *top, bool purge_lib) int del_counter = 0; for (auto mod : del_modules) { - if (mod->name.substr(0, 9) == "$abstract") - continue; if (!purge_lib && mod->get_bool_attribute("\\blackbox")) continue; log("Removing unused module `%s'.\n", mod->name.c_str()); @@ -324,10 +322,12 @@ bool set_keep_assert(std::map<RTLIL::Module*, bool> &cache, RTLIL::Module *mod) int find_top_mod_score(Design *design, Module *module, dict<Module*, int> &db) { if (db.count(module) == 0) { + int score = 0; db[module] = 0; for (auto cell : module->cells()) if (design->module(cell->type)) - db[module] = max(db[module], find_top_mod_score(design, design->module(cell->type), db) + 1); + score = max(score, find_top_mod_score(design, design->module(cell->type), db) + 1); + db[module] = score; } return db.at(module); } @@ -398,7 +398,7 @@ struct HierarchyPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing HIERARCHY pass (managing design hierarchy).\n"); + log_header(design, "Executing HIERARCHY pass (managing design hierarchy).\n"); bool flag_check = false; bool purge_lib = false; @@ -508,7 +508,7 @@ struct HierarchyPass : public Pass { top_mod = mod_it.second; if (top_mod == nullptr && auto_top_mode) { - log_header("Finding top of design hierarchy..\n"); + log_header(design, "Finding top of design hierarchy..\n"); dict<Module*, int> db; for (Module *mod : design->selected_modules()) { int score = find_top_mod_score(design, mod, db); @@ -525,9 +525,9 @@ struct HierarchyPass : public Pass { { did_something = false; - std::set<RTLIL::Module*> used_modules; + std::set<RTLIL::Module*, IdString::compare_ptr_by_name<Module>> used_modules; if (top_mod != NULL) { - log_header("Analyzing design hierarchy..\n"); + log_header(design, "Analyzing design hierarchy..\n"); hierarchy_worker(design, used_modules, top_mod, 0); } else { for (auto mod : design->modules()) @@ -541,7 +541,7 @@ struct HierarchyPass : public Pass { } if (top_mod != NULL) { - log_header("Analyzing design hierarchy..\n"); + log_header(design, "Analyzing design hierarchy..\n"); hierarchy_clean(design, top_mod, purge_lib); } diff --git a/passes/hierarchy/singleton.cc b/passes/hierarchy/singleton.cc index 5715c0eb..03c365fb 100644 --- a/passes/hierarchy/singleton.cc +++ b/passes/hierarchy/singleton.cc @@ -43,7 +43,7 @@ struct SingletonPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing SINGLETON pass (creating singleton modules).\n"); + log_header(design, "Executing SINGLETON pass (creating singleton modules).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) diff --git a/passes/hierarchy/submod.cc b/passes/hierarchy/submod.cc index d4e8c96c..9f312f82 100644 --- a/passes/hierarchy/submod.cc +++ b/passes/hierarchy/submod.cc @@ -298,7 +298,7 @@ struct SubmodPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing SUBMOD pass (moving cells to submodules as requested).\n"); + log_header(design, "Executing SUBMOD pass (moving cells to submodules as requested).\n"); log_push(); std::string opt_name; @@ -321,7 +321,7 @@ struct SubmodPass : public Pass { if (opt_name.empty()) { Pass::call(design, "opt_clean"); - log_header("Continuing SUBMOD pass.\n"); + log_header(design, "Continuing SUBMOD pass.\n"); std::set<RTLIL::IdString> handled_modules; @@ -356,7 +356,7 @@ struct SubmodPass : public Pass { log("Nothing selected -> do nothing.\n"); else { Pass::call_on_module(design, module, "opt_clean"); - log_header("Continuing SUBMOD pass.\n"); + log_header(design, "Continuing SUBMOD pass.\n"); SubmodWorker worker(design, module, copy_mode, opt_name); } } diff --git a/passes/memory/Makefile.inc b/passes/memory/Makefile.inc index aeff225d..ad359c01 100644 --- a/passes/memory/Makefile.inc +++ b/passes/memory/Makefile.inc @@ -6,4 +6,5 @@ OBJS += passes/memory/memory_collect.o OBJS += passes/memory/memory_unpack.o OBJS += passes/memory/memory_bram.o OBJS += passes/memory/memory_map.o +OBJS += passes/memory/memory_memx.o diff --git a/passes/memory/memory.cc b/passes/memory/memory.cc index 4e74d1a4..947d598b 100644 --- a/passes/memory/memory.cc +++ b/passes/memory/memory.cc @@ -31,14 +31,15 @@ struct MemoryPass : public Pass { { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" memory [-nomap] [-nordff] [-bram <bram_rules>] [selection]\n"); + log(" memory [-nomap] [-nordff] [-memx] [-bram <bram_rules>] [selection]\n"); log("\n"); log("This pass calls all the other memory_* passes in a useful order:\n"); log("\n"); - log(" memory_dff [-nordff]\n"); + log(" memory_dff [-nordff] (-memx implies -nordff)\n"); log(" opt_clean\n"); log(" memory_share\n"); log(" opt_clean\n"); + log(" memory_memx (when called with -memx)\n"); log(" memory_collect\n"); log(" memory_bram -rules <bram_rules> (when called with -bram)\n"); log(" memory_map (skipped if called with -nomap)\n"); @@ -51,9 +52,10 @@ struct MemoryPass : public Pass { { bool flag_nomap = false; bool flag_nordff = false; + bool flag_memx = false; string memory_bram_opts; - log_header("Executing MEMORY pass.\n"); + log_header(design, "Executing MEMORY pass.\n"); log_push(); size_t argidx; @@ -66,6 +68,11 @@ struct MemoryPass : public Pass { flag_nordff = true; continue; } + if (args[argidx] == "-memx") { + flag_nordff = true; + flag_memx = true; + continue; + } if (argidx+1 < args.size() && args[argidx] == "-bram") { memory_bram_opts += " -rules " + args[++argidx]; continue; @@ -77,6 +84,8 @@ struct MemoryPass : public Pass { Pass::call(design, flag_nordff ? "memory_dff -nordff" : "memory_dff"); Pass::call(design, "opt_clean"); Pass::call(design, "memory_share"); + if (flag_memx) + Pass::call(design, "memory_memx"); Pass::call(design, "opt_clean"); Pass::call(design, "memory_collect"); diff --git a/passes/memory/memory_bram.cc b/passes/memory/memory_bram.cc index f2d9b584..a7f9cf38 100644 --- a/passes/memory/memory_bram.cc +++ b/passes/memory/memory_bram.cc @@ -656,6 +656,9 @@ grow_read_ports:; bool transp = rd_transp[cell_port_i] == State::S1; SigBit clksig = rd_clk[cell_port_i]; + if (wr_ports == 0) + transp = false; + pair<SigBit, bool> clkdom(clksig, clkpol); if (!clken) clkdom = pair<SigBit, bool>(State::S1, false); @@ -826,7 +829,7 @@ grow_read_ports:; State padding = State::Sx; for (int j = 0; j < bram.dbits; j++) if (init_offset+i < GetSize(initdata) && init_shift+j < GetSize(initdata[init_offset+i])) - padding = initparam[i*bram.dbits+j] = initdata[init_offset+i][init_shift+j]; + initparam[i*bram.dbits+j] = initdata[init_offset+i][init_shift+j]; else initparam[i*bram.dbits+j] = padding; } @@ -1211,7 +1214,7 @@ struct MemoryBramPass : public Pass { { rules_t rules; - log_header("Executing MEMORY_BRAM pass (mapping $mem cells to block memories).\n"); + log_header(design, "Executing MEMORY_BRAM pass (mapping $mem cells to block memories).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { diff --git a/passes/memory/memory_collect.cc b/passes/memory/memory_collect.cc index 5c0acb3e..ab66e3fb 100644 --- a/passes/memory/memory_collect.cc +++ b/passes/memory/memory_collect.cc @@ -37,8 +37,6 @@ Cell *handle_memory(Module *module, RTLIL::Memory *memory) log("Collecting $memrd, $memwr and $meminit for memory `%s' in module `%s':\n", memory->name.c_str(), module->name.c_str()); - int addr_bits = 0; - Const init_data(State::Sx, memory->size * memory->width); SigMap sigmap(module); @@ -59,16 +57,28 @@ Cell *handle_memory(Module *module, RTLIL::Memory *memory) SigSpec sig_rd_data; SigSpec sig_rd_en; + int addr_bits = 0; std::vector<Cell*> memcells; for (auto &cell_it : module->cells_) { Cell *cell = cell_it.second; if (cell->type.in("$memrd", "$memwr", "$meminit") && memory->name == cell->parameters["\\MEMID"].decode_string()) { - addr_bits = max(addr_bits, cell->getParam("\\ABITS").as_int()); + SigSpec addr = sigmap(cell->getPort("\\ADDR")); + for (int i = 0; i < GetSize(addr); i++) + if (addr[i] != State::S0) + addr_bits = std::max(addr_bits, i+1); memcells.push_back(cell); } } + if (memory->start_offset == 0 && addr_bits < 30 && (1 << addr_bits) < memory->size) + memory->size = 1 << addr_bits; + + if (memory->start_offset >= 0) + addr_bits = std::min(addr_bits, ceil_log2(memory->size + memory->start_offset)); + + addr_bits = std::max(addr_bits, 1); + if (memcells.empty()) { log(" no cells found. removing memory.\n"); return nullptr; @@ -247,7 +257,7 @@ struct MemoryCollectPass : public Pass { log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing MEMORY_COLLECT pass (generating $mem cells).\n"); + log_header(design, "Executing MEMORY_COLLECT pass (generating $mem cells).\n"); extra_args(args, 1, design); for (auto &mod_it : design->modules_) if (design->selected(mod_it.second)) diff --git a/passes/memory/memory_dff.cc b/passes/memory/memory_dff.cc index beb2016a..40691d16 100644 --- a/passes/memory/memory_dff.cc +++ b/passes/memory/memory_dff.cc @@ -283,7 +283,7 @@ struct MemoryDffPass : public Pass { { bool flag_wr_only = false; - log_header("Executing MEMORY_DFF pass (merging $dff cells to $memrd and $memwr).\n"); + log_header(design, "Executing MEMORY_DFF pass (merging $dff cells to $memrd and $memwr).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { diff --git a/passes/memory/memory_map.cc b/passes/memory/memory_map.cc index 0b8ccb36..bffeec85 100644 --- a/passes/memory/memory_map.cc +++ b/passes/memory/memory_map.cc @@ -363,7 +363,7 @@ struct MemoryMapPass : public Pass { log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing MEMORY_MAP pass (converting $mem cells to logic and flip-flops).\n"); + log_header(design, "Executing MEMORY_MAP pass (converting $mem cells to logic and flip-flops).\n"); extra_args(args, 1, design); for (auto mod : design->selected_modules()) MemoryMapWorker(design, mod); diff --git a/passes/memory/memory_memx.cc b/passes/memory/memory_memx.cc new file mode 100644 index 00000000..2b02e249 --- /dev/null +++ b/passes/memory/memory_memx.cc @@ -0,0 +1,92 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * + * 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/register.h" +#include "kernel/log.h" +#include <sstream> +#include <set> +#include <stdlib.h> + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct MemoryMemxPass : public Pass { + MemoryMemxPass() : Pass("memory_memx", "emulate vlog sim behavior for mem ports") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" memory_memx [selection]\n"); + log("\n"); + log("This pass adds additional circuitry that emulates the Verilog simulation\n"); + log("behavior for out-of-bounds memory reads and writes.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { + log_header(design, "Executing MEMORY_MEMX pass (converting $mem cells to logic and flip-flops).\n"); + extra_args(args, 1, design); + + for (auto module : design->selected_modules()) + { + vector<Cell*> mem_port_cells; + + for (auto cell : module->selected_cells()) + if (cell->type.in("$memrd", "$memwr")) + mem_port_cells.push_back(cell); + + for (auto cell : mem_port_cells) + { + IdString memid = cell->getParam("\\MEMID").decode_string(); + RTLIL::Memory *mem = module->memories.at(memid); + + int lowest_addr = mem->start_offset; + int highest_addr = mem->start_offset + mem->size - 1; + + SigSpec addr = cell->getPort("\\ADDR"); + addr.extend_u0(32); + + SigSpec addr_ok = module->Nex(NEW_ID, module->ReduceXor(NEW_ID, addr), module->ReduceXor(NEW_ID, {addr, State::S1})); + if (lowest_addr != 0) + addr_ok = module->LogicAnd(NEW_ID, addr_ok, module->Ge(NEW_ID, addr, lowest_addr)); + addr_ok = module->LogicAnd(NEW_ID, addr_ok, module->Le(NEW_ID, addr, highest_addr)); + + if (cell->type == "$memrd") + { + if (cell->getParam("\\CLK_ENABLE").as_bool()) + log_error("Cell %s.%s (%s) has an enabled clock. Clocked $memrd cells are not supported by memory_memx!\n", + log_id(module), log_id(cell), log_id(cell->type)); + + SigSpec rdata = cell->getPort("\\DATA"); + Wire *raw_rdata = module->addWire(NEW_ID, GetSize(rdata)); + module->addMux(NEW_ID, SigSpec(State::Sx, GetSize(rdata)), raw_rdata, addr_ok, rdata); + cell->setPort("\\DATA", raw_rdata); + } + + if (cell->type == "$memwr") + { + SigSpec en = cell->getPort("\\EN"); + en = module->And(NEW_ID, en, addr_ok.repeat(GetSize(en))); + cell->setPort("\\EN", en); + } + } + } + } +} MemoryMemxPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/memory/memory_share.cc b/passes/memory/memory_share.cc index 3a6fd0b4..ca09ac52 100644 --- a/passes/memory/memory_share.cc +++ b/passes/memory/memory_share.cc @@ -43,7 +43,7 @@ struct MemoryShareWorker CellTypes cone_ct; std::map<RTLIL::SigBit, std::pair<RTLIL::Cell*, int>> sig_to_mux; - std::map<std::set<std::map<RTLIL::SigBit, bool>>, RTLIL::SigBit> conditions_logic_cache; + std::map<pair<std::set<std::map<SigBit, bool>>, SigBit>, SigBit> conditions_logic_cache; // ----------------------------------------------------------------- @@ -109,10 +109,12 @@ struct MemoryShareWorker return false; } - RTLIL::SigBit conditions_to_logic(std::set<std::map<RTLIL::SigBit, bool>> &conditions, int &created_conditions) + RTLIL::SigBit conditions_to_logic(std::set<std::map<RTLIL::SigBit, bool>> &conditions, SigBit olden, int &created_conditions) { - if (conditions_logic_cache.count(conditions)) - return conditions_logic_cache.at(conditions); + auto key = make_pair(conditions, olden); + + if (conditions_logic_cache.count(key)) + return conditions_logic_cache.at(key); RTLIL::SigSpec terms; for (auto &cond : conditions) { @@ -125,13 +127,16 @@ struct MemoryShareWorker created_conditions++; } - if (terms.size() == 0) + if (olden.wire != nullptr || olden != State::S1) + terms.append(olden); + + if (GetSize(terms) == 0) terms = State::S1; - if (terms.size() > 1) + if (GetSize(terms) > 1) terms = module->ReduceAnd(NEW_ID, terms); - return conditions_logic_cache[conditions] = terms; + return conditions_logic_cache[key] = terms; } void translate_rd_feedback_to_en(std::string memid, std::vector<RTLIL::Cell*> &rd_ports, std::vector<RTLIL::Cell*> &wr_ports) @@ -140,15 +145,14 @@ struct MemoryShareWorker std::map<RTLIL::SigBit, std::set<RTLIL::SigBit>> muxtree_upstream_map; std::set<RTLIL::SigBit> non_feedback_nets; - for (auto wire_it : module->wires_) - if (wire_it.second->port_output) { - std::vector<RTLIL::SigBit> bits = RTLIL::SigSpec(wire_it.second); + for (auto wire : module->wires()) + if (wire->port_output) { + std::vector<RTLIL::SigBit> bits = sigmap(wire); non_feedback_nets.insert(bits.begin(), bits.end()); } - for (auto cell_it : module->cells_) + for (auto cell : module->cells()) { - RTLIL::Cell *cell = cell_it.second; bool ignore_data_port = false; if (cell->type == "$mux" || cell->type == "$pmux") @@ -173,7 +177,7 @@ struct MemoryShareWorker cell->parameters.at("\\MEMID").decode_string() == memid) ignore_data_port = true; - for (auto conn : cell_it.second->connections()) + for (auto conn : cell->connections()) { if (ignore_data_port && conn.first == "\\DATA") continue; @@ -240,13 +244,8 @@ struct MemoryShareWorker std::map<RTLIL::SigBit, bool> state; std::set<std::map<RTLIL::SigBit, bool>> conditions; - if (cell_en[i].wire != NULL) { - state[cell_en[i]] = false; - conditions.insert(state); - } - find_data_feedback(async_rd_bits.at(sig_addr).at(i), cell_data[i], state, conditions); - cell_en[i] = conditions_to_logic(conditions, created_conditions); + cell_en[i] = conditions_to_logic(conditions, cell_en[i], created_conditions); } if (created_conditions) { @@ -620,6 +619,12 @@ struct MemoryShareWorker RTLIL::SigBit this_en_active = module->ReduceOr(NEW_ID, this_en); + if (GetSize(last_addr) < GetSize(this_addr)) + last_addr.extend_u0(GetSize(this_addr)); + else + this_addr.extend_u0(GetSize(last_addr)); + + wr_ports[i]->setParam("\\ABITS", GetSize(this_addr)); wr_ports[i]->setPort("\\ADDR", module->Mux(NEW_ID, last_addr, this_addr, this_en_active)); wr_ports[i]->setPort("\\DATA", module->Mux(NEW_ID, last_data, this_data, this_en_active)); @@ -666,10 +671,8 @@ struct MemoryShareWorker std::map<std::string, std::pair<std::vector<RTLIL::Cell*>, std::vector<RTLIL::Cell*>>> memindex; sigmap_xmux = sigmap; - for (auto &it : module->cells_) + for (auto cell : module->cells()) { - RTLIL::Cell *cell = it.second; - if (cell->type == "$memrd") memindex[cell->parameters.at("\\MEMID").decode_string()].first.push_back(cell); @@ -746,11 +749,11 @@ struct MemorySharePass : public Pass { log("\n"); log("Note that in addition to the algorithms implemented in this pass, the $memrd\n"); log("and $memwr cells are also subject to generic resource sharing passes (and other\n"); - log("optimizations) such as opt_share.\n"); + log("optimizations) such as \"share\" and \"opt_merge\".\n"); log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing MEMORY_SHARE pass (consolidating $memrc/$memwr cells).\n"); + log_header(design, "Executing MEMORY_SHARE pass (consolidating $memrd/$memwr cells).\n"); extra_args(args, 1, design); for (auto module : design->selected_modules()) MemoryShareWorker(design, module); diff --git a/passes/memory/memory_unpack.cc b/passes/memory/memory_unpack.cc index 60724da7..a0fc31b5 100644 --- a/passes/memory/memory_unpack.cc +++ b/passes/memory/memory_unpack.cc @@ -138,7 +138,7 @@ struct MemoryUnpackPass : public Pass { log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing MEMORY_UNPACK pass (generating $memrd/$memwr cells form $mem cells).\n"); + log_header(design, "Executing MEMORY_UNPACK pass (generating $memrd/$memwr cells form $mem cells).\n"); extra_args(args, 1, design); for (auto &mod_it : design->modules_) if (design->selected(mod_it.second)) diff --git a/passes/opt/Makefile.inc b/passes/opt/Makefile.inc index 43defb78..a8b1537b 100644 --- a/passes/opt/Makefile.inc +++ b/passes/opt/Makefile.inc @@ -1,11 +1,11 @@ OBJS += passes/opt/opt.o -OBJS += passes/opt/opt_share.o +OBJS += passes/opt/opt_merge.o OBJS += passes/opt/opt_muxtree.o OBJS += passes/opt/opt_reduce.o OBJS += passes/opt/opt_rmdff.o OBJS += passes/opt/opt_clean.o -OBJS += passes/opt/opt_const.o +OBJS += passes/opt/opt_expr.o ifneq ($(SMALL),1) OBJS += passes/opt/share.o diff --git a/passes/opt/opt.cc b/passes/opt/opt.cc index f5389d8e..13ea5469 100644 --- a/passes/opt/opt.cc +++ b/passes/opt/opt.cc @@ -37,23 +37,23 @@ struct OptPass : public Pass { log("a series of trivial optimizations and cleanups. This pass executes the other\n"); log("passes in the following order:\n"); log("\n"); - log(" opt_const [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc]\n"); - log(" opt_share [-share_all] -nomux\n"); + log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc]\n"); + log(" opt_merge [-share_all] -nomux\n"); log("\n"); log(" do\n"); log(" opt_muxtree\n"); log(" opt_reduce [-fine] [-full]\n"); - log(" opt_share [-share_all]\n"); + log(" opt_merge [-share_all]\n"); log(" opt_rmdff\n"); log(" opt_clean [-purge]\n"); - log(" opt_const [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc]\n"); + log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc]\n"); log(" while <changed design>\n"); log("\n"); log("When called with -fast the following script is used instead:\n"); log("\n"); log(" do\n"); - log(" opt_const [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc]\n"); - log(" opt_share [-share_all]\n"); + log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc]\n"); + log(" opt_merge [-share_all]\n"); log(" opt_rmdff\n"); log(" opt_clean [-purge]\n"); log(" while <changed design in opt_rmdff>\n"); @@ -66,12 +66,12 @@ struct OptPass : public Pass { virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { std::string opt_clean_args; - std::string opt_const_args; + std::string opt_expr_args; std::string opt_reduce_args; - std::string opt_share_args; + std::string opt_merge_args; bool fast_mode = false; - log_header("Executing OPT pass (performing simple optimizations).\n"); + log_header(design, "Executing OPT pass (performing simple optimizations).\n"); log_push(); size_t argidx; @@ -81,37 +81,37 @@ struct OptPass : public Pass { continue; } if (args[argidx] == "-mux_undef") { - opt_const_args += " -mux_undef"; + opt_expr_args += " -mux_undef"; continue; } if (args[argidx] == "-mux_bool") { - opt_const_args += " -mux_bool"; + opt_expr_args += " -mux_bool"; continue; } if (args[argidx] == "-undriven") { - opt_const_args += " -undriven"; + opt_expr_args += " -undriven"; continue; } if (args[argidx] == "-clkinv") { - opt_const_args += " -clkinv"; + opt_expr_args += " -clkinv"; continue; } if (args[argidx] == "-fine") { - opt_const_args += " -fine"; + opt_expr_args += " -fine"; opt_reduce_args += " -fine"; continue; } if (args[argidx] == "-full") { - opt_const_args += " -full"; + opt_expr_args += " -full"; opt_reduce_args += " -full"; continue; } if (args[argidx] == "-keepdc") { - opt_const_args += " -keepdc"; + opt_expr_args += " -keepdc"; continue; } if (args[argidx] == "-share_all") { - opt_share_args += " -share_all"; + opt_merge_args += " -share_all"; continue; } if (args[argidx] == "-fast") { @@ -125,32 +125,32 @@ struct OptPass : public Pass { if (fast_mode) { while (1) { - Pass::call(design, "opt_const" + opt_const_args); - Pass::call(design, "opt_share" + opt_share_args); + Pass::call(design, "opt_expr" + opt_expr_args); + Pass::call(design, "opt_merge" + opt_merge_args); design->scratchpad_unset("opt.did_something"); Pass::call(design, "opt_rmdff"); if (design->scratchpad_get_bool("opt.did_something") == false) break; Pass::call(design, "opt_clean" + opt_clean_args); - log_header("Rerunning OPT passes. (Removed registers in this run.)\n"); + log_header(design, "Rerunning OPT passes. (Removed registers in this run.)\n"); } Pass::call(design, "opt_clean" + opt_clean_args); } else { - Pass::call(design, "opt_const" + opt_const_args); - Pass::call(design, "opt_share -nomux" + opt_share_args); + Pass::call(design, "opt_expr" + opt_expr_args); + Pass::call(design, "opt_merge -nomux" + opt_merge_args); while (1) { design->scratchpad_unset("opt.did_something"); Pass::call(design, "opt_muxtree"); Pass::call(design, "opt_reduce" + opt_reduce_args); - Pass::call(design, "opt_share" + opt_share_args); + Pass::call(design, "opt_merge" + opt_merge_args); Pass::call(design, "opt_rmdff"); Pass::call(design, "opt_clean" + opt_clean_args); - Pass::call(design, "opt_const" + opt_const_args); + Pass::call(design, "opt_expr" + opt_expr_args); if (design->scratchpad_get_bool("opt.did_something") == false) break; - log_header("Rerunning OPT passes. (Maybe there is more to do..)\n"); + log_header(design, "Rerunning OPT passes. (Maybe there is more to do..)\n"); } } @@ -158,7 +158,7 @@ struct OptPass : public Pass { design->sort(); design->check(); - log_header(fast_mode ? "Finished fast OPT passes.\n" : "Finished OPT passes. (There is nothing left to do.)\n"); + log_header(design, fast_mode ? "Finished fast OPT passes.\n" : "Finished OPT passes. (There is nothing left to do.)\n"); log_pop(); } } OptPass; diff --git a/passes/opt/opt_clean.cc b/passes/opt/opt_clean.cc index 175e8e11..6600ffa2 100644 --- a/passes/opt/opt_clean.cc +++ b/passes/opt/opt_clean.cc @@ -156,6 +156,9 @@ bool compare_signals(RTLIL::SigBit &s1, RTLIL::SigBit &s2, SigPool ®s, SigPoo if (w1->port_input != w2->port_input) return w2->port_input; + if ((w1->port_input && w1->port_output) != (w2->port_input && w2->port_output)) + return !(w2->port_input && w2->port_output); + if (w1->name[0] == '\\' && w2->name[0] == '\\') { if (regs.check_any(s1) != regs.check_any(s2)) return regs.check_any(s2); @@ -380,7 +383,7 @@ struct OptCleanPass : public Pass { { bool purge_mode = false; - log_header("Executing OPT_CLEAN pass (remove unused cells and wires).\n"); + log_header(design, "Executing OPT_CLEAN pass (remove unused cells and wires).\n"); log_push(); size_t argidx; diff --git a/passes/opt/opt_const.cc b/passes/opt/opt_expr.cc index 0fcacf0a..b62eae28 100644 --- a/passes/opt/opt_const.cc +++ b/passes/opt/opt_expr.cc @@ -179,7 +179,7 @@ bool group_cell_inputs(RTLIL::Module *module, RTLIL::Cell *cell, bool commutativ log("\n"); } - cover_list("opt.opt_const.fine.group", "$not", "$pos", "$and", "$or", "$xor", "$xnor", cell->type.str()); + cover_list("opt.opt_expr.fine.group", "$not", "$pos", "$and", "$or", "$xor", "$xnor", cell->type.str()); module->remove(cell); did_something = true; @@ -304,7 +304,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons for (auto cell : cells.sorted) { -#define ACTION_DO(_p_, _s_) do { cover("opt.opt_const.action_" S__LINE__); replace_cell(assign_map, module, cell, input.as_string(), _p_, _s_); goto next_cell; } while (0) +#define ACTION_DO(_p_, _s_) do { cover("opt.opt_expr.action_" S__LINE__); replace_cell(assign_map, module, cell, input.as_string(), _p_, _s_); goto next_cell; } while (0) #define ACTION_DO_Y(_v_) ACTION_DO("\\Y", RTLIL::SigSpec(RTLIL::State::S ## _v_)) if (clkinv) @@ -342,6 +342,68 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons handle_clkpol_celltype_swap(cell, "$_DLATCHSR_??N_", "$_DLATCHSR_??P_", "\\R", assign_map, invert_map); } + bool detect_const_and = false; + bool detect_const_or = false; + + if (cell->type.in("$reduce_and", "$_AND_")) + detect_const_and = true; + + if (cell->type.in("$and", "$logic_and") && GetSize(cell->getPort("\\A")) == 1 && GetSize(cell->getPort("\\B")) == 1) + detect_const_and = true; + + if (cell->type.in("$reduce_or", "$reduce_bool", "$_OR_")) + detect_const_or = true; + + if (cell->type.in("$or", "$logic_or") && GetSize(cell->getPort("\\A")) == 1 && GetSize(cell->getPort("\\B")) == 1) + detect_const_or = true; + + if (detect_const_and || detect_const_or) + { + pool<SigBit> input_bits = assign_map(cell->getPort("\\A")).to_sigbit_pool(); + bool found_zero = false, found_one = false, found_inv = false; + + if (cell->hasPort("\\B")) { + vector<SigBit> more_bits = assign_map(cell->getPort("\\B")).to_sigbit_vector(); + input_bits.insert(more_bits.begin(), more_bits.end()); + } + + for (auto bit : input_bits) { + if (bit == State::S0) + found_zero = true; + if (bit == State::S1) + found_one = true; + if (invert_map.count(bit) && input_bits.count(invert_map.at(bit))) + found_inv = true; + } + + if (detect_const_and && (found_zero || found_inv)) { + cover("opt.opt_expr.const_and"); + replace_cell(assign_map, module, cell, "const_and", "\\Y", RTLIL::State::S0); + goto next_cell; + } + + if (detect_const_or && (found_one || found_inv)) { + cover("opt.opt_expr.const_or"); + replace_cell(assign_map, module, cell, "const_or", "\\Y", RTLIL::State::S1); + goto next_cell; + } + } + + if (cell->type.in("$reduce_and", "$reduce_or", "$reduce_bool", "$reduce_xor", "$reduce_xnor", "$neg") && + GetSize(cell->getPort("\\A")) == 1 && GetSize(cell->getPort("\\Y")) == 1) + { + if (cell->type == "$reduce_xnor") { + cover("opt.opt_expr.reduce_xnor_not"); + log("Replacing %s cell `%s' in module `%s' with $not cell.\n", + log_id(cell->type), log_id(cell->name), log_id(module)); + cell->type = "$not"; + } else { + cover("opt.opt_expr.unary_buffer"); + replace_cell(assign_map, module, cell, "unary_buffer", "\\Y", cell->getPort("\\A")); + } + goto next_cell; + } + if (do_fine) { if (cell->type == "$not" || cell->type == "$pos" || @@ -366,7 +428,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } if (new_a != RTLIL::State::Sm && RTLIL::SigSpec(new_a) != sig_a) { - cover("opt.opt_const.fine.$reduce_and"); + cover("opt.opt_expr.fine.$reduce_and"); log("Replacing port A of %s cell `%s' in module `%s' with constant driver: %s -> %s\n", cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_a), log_signal(new_a)); cell->setPort("\\A", sig_a = new_a); @@ -392,7 +454,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } if (new_a != RTLIL::State::Sm && RTLIL::SigSpec(new_a) != sig_a) { - cover_list("opt.opt_const.fine.A", "$logic_not", "$logic_and", "$logic_or", "$reduce_or", "$reduce_bool", cell->type.str()); + cover_list("opt.opt_expr.fine.A", "$logic_not", "$logic_and", "$logic_or", "$reduce_or", "$reduce_bool", cell->type.str()); log("Replacing port A of %s cell `%s' in module `%s' with constant driver: %s -> %s\n", cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_a), log_signal(new_a)); cell->setPort("\\A", sig_a = new_a); @@ -418,7 +480,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } if (new_b != RTLIL::State::Sm && RTLIL::SigSpec(new_b) != sig_b) { - cover_list("opt.opt_const.fine.B", "$logic_and", "$logic_or", cell->type.str()); + cover_list("opt.opt_expr.fine.B", "$logic_and", "$logic_or", cell->type.str()); log("Replacing port B of %s cell `%s' in module `%s' with constant driver: %s -> %s\n", cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_b), log_signal(new_b)); cell->setPort("\\B", sig_b = new_b); @@ -428,18 +490,6 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } } - if (cell->type == "$logic_or" && (assign_map(cell->getPort("\\A")) == RTLIL::State::S1 || assign_map(cell->getPort("\\B")) == RTLIL::State::S1)) { - cover("opt.opt_const.one_high"); - replace_cell(assign_map, module, cell, "one high", "\\Y", RTLIL::State::S1); - goto next_cell; - } - - if (cell->type == "$logic_and" && (assign_map(cell->getPort("\\A")) == RTLIL::State::S0 || assign_map(cell->getPort("\\B")) == RTLIL::State::S0)) { - cover("opt.opt_const.one_low"); - replace_cell(assign_map, module, cell, "one low", "\\Y", RTLIL::State::S0); - goto next_cell; - } - if (cell->type == "$reduce_xor" || cell->type == "$reduce_xnor" || cell->type == "$shift" || cell->type == "$shiftx" || cell->type == "$shl" || cell->type == "$shr" || cell->type == "$sshl" || cell->type == "$sshr" || cell->type == "$lt" || cell->type == "$le" || cell->type == "$ge" || cell->type == "$gt" || @@ -462,7 +512,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if (0) { found_the_x_bit: - cover_list("opt.opt_const.xbit", "$reduce_xor", "$reduce_xnor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", + cover_list("opt.opt_expr.xbit", "$reduce_xor", "$reduce_xnor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", "$lt", "$le", "$ge", "$gt", "$neg", "$add", "$sub", "$mul", "$div", "$mod", "$pow", cell->type.str()); if (cell->type == "$reduce_xor" || cell->type == "$reduce_xnor" || cell->type == "$lt" || cell->type == "$le" || cell->type == "$ge" || cell->type == "$gt") @@ -475,13 +525,13 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if ((cell->type == "$_NOT_" || cell->type == "$not" || cell->type == "$logic_not") && cell->getPort("\\Y").size() == 1 && invert_map.count(assign_map(cell->getPort("\\A"))) != 0) { - cover_list("opt.opt_const.invert.double", "$_NOT_", "$not", "$logic_not", cell->type.str()); + cover_list("opt.opt_expr.invert.double", "$_NOT_", "$not", "$logic_not", cell->type.str()); replace_cell(assign_map, module, cell, "double_invert", "\\Y", invert_map.at(assign_map(cell->getPort("\\A")))); goto next_cell; } if ((cell->type == "$_MUX_" || cell->type == "$mux") && invert_map.count(assign_map(cell->getPort("\\S"))) != 0) { - cover_list("opt.opt_const.invert.muxsel", "$_MUX_", "$mux", cell->type.str()); + cover_list("opt.opt_expr.invert.muxsel", "$_MUX_", "$mux", cell->type.str()); log("Optimizing away select inverter for %s cell `%s' in module `%s'.\n", log_id(cell->type), log_id(cell), log_id(module)); RTLIL::SigSpec tmp = cell->getPort("\\A"); cell->setPort("\\A", cell->getPort("\\B")); @@ -564,7 +614,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if (input.match(" 1")) ACTION_DO("\\Y", input.extract(1, 1)); if (input.match("01 ")) ACTION_DO("\\Y", input.extract(0, 1)); if (input.match("10 ")) { - cover("opt.opt_const.mux_to_inv"); + cover("opt.opt_expr.mux_to_inv"); cell->type = "$_NOT_"; cell->setPort("\\A", input.extract(0, 1)); cell->unsetPort("\\B"); @@ -599,7 +649,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons log_assert(GetSize(a) == GetSize(b)); for (int i = 0; i < GetSize(a); i++) { if (a[i].wire == NULL && b[i].wire == NULL && a[i] != b[i] && a[i].data <= RTLIL::State::S1 && b[i].data <= RTLIL::State::S1) { - cover_list("opt.opt_const.eqneq.isneq", "$eq", "$ne", "$eqx", "$nex", cell->type.str()); + cover_list("opt.opt_expr.eqneq.isneq", "$eq", "$ne", "$eqx", "$nex", cell->type.str()); RTLIL::SigSpec new_y = RTLIL::SigSpec((cell->type == "$eq" || cell->type == "$eqx") ? RTLIL::State::S0 : RTLIL::State::S1); new_y.extend_u0(cell->parameters["\\Y_WIDTH"].as_int(), false); replace_cell(assign_map, module, cell, "isneq", "\\Y", new_y); @@ -612,7 +662,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } if (new_a.size() == 0) { - cover_list("opt.opt_const.eqneq.empty", "$eq", "$ne", "$eqx", "$nex", cell->type.str()); + cover_list("opt.opt_expr.eqneq.empty", "$eq", "$ne", "$eqx", "$nex", cell->type.str()); RTLIL::SigSpec new_y = RTLIL::SigSpec((cell->type == "$eq" || cell->type == "$eqx") ? RTLIL::State::S1 : RTLIL::State::S0); new_y.extend_u0(cell->parameters["\\Y_WIDTH"].as_int(), false); replace_cell(assign_map, module, cell, "empty", "\\Y", new_y); @@ -620,7 +670,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } if (new_a.size() < a.size() || new_b.size() < b.size()) { - cover_list("opt.opt_const.eqneq.resize", "$eq", "$ne", "$eqx", "$nex", cell->type.str()); + cover_list("opt.opt_expr.eqneq.resize", "$eq", "$ne", "$eqx", "$nex", cell->type.str()); cell->setPort("\\A", new_a); cell->setPort("\\B", new_b); cell->parameters["\\A_WIDTH"] = new_a.size(); @@ -635,7 +685,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons RTLIL::SigSpec b = assign_map(cell->getPort("\\B")); if (a.is_fully_const() && !b.is_fully_const()) { - cover_list("opt.opt_const.eqneq.swapconst", "$eq", "$ne", cell->type.str()); + cover_list("opt.opt_expr.eqneq.swapconst", "$eq", "$ne", cell->type.str()); cell->setPort("\\A", b); cell->setPort("\\B", a); std::swap(a, b); @@ -646,7 +696,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons RTLIL::SigSpec input = b; ACTION_DO("\\Y", cell->getPort("\\A")); } else { - cover_list("opt.opt_const.eqneq.isnot", "$eq", "$ne", cell->type.str()); + cover_list("opt.opt_expr.eqneq.isnot", "$eq", "$ne", cell->type.str()); log("Replacing %s cell `%s' in module `%s' with inverter.\n", log_id(cell->type), log_id(cell), log_id(module)); cell->type = "$not"; cell->parameters.erase("\\B_WIDTH"); @@ -661,7 +711,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if ((cell->type == "$eq" || cell->type == "$ne") && (assign_map(cell->getPort("\\A")).is_fully_zero() || assign_map(cell->getPort("\\B")).is_fully_zero())) { - cover_list("opt.opt_const.eqneq.cmpzero", "$eq", "$ne", cell->type.str()); + cover_list("opt.opt_expr.eqneq.cmpzero", "$eq", "$ne", cell->type.str()); log("Replacing %s cell `%s' in module `%s' with %s.\n", log_id(cell->type), log_id(cell), log_id(module), "$eq" ? "$logic_not" : "$reduce_bool"); cell->type = cell->type == "$eq" ? "$logic_not" : "$reduce_bool"; @@ -699,7 +749,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons sig_y[i] = sig_a[GetSize(sig_a)-1]; } - cover_list("opt.opt_const.constshift", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", cell->type.str()); + cover_list("opt.opt_expr.constshift", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", cell->type.str()); log("Replacing %s cell `%s' (B=%s, SHR=%d) in module `%s' with fixed wiring: %s\n", log_id(cell->type), log_id(cell), log_signal(assign_map(cell->getPort("\\B"))), shift_bits, log_id(module), log_signal(sig_y)); @@ -760,9 +810,9 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if (identity_wrt_a || identity_wrt_b) { if (identity_wrt_a) - cover_list("opt.opt_const.identwrt.a", "$add", "$sub", "$or", "$xor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", "$mul", "$div", cell->type.str()); + cover_list("opt.opt_expr.identwrt.a", "$add", "$sub", "$or", "$xor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", "$mul", "$div", cell->type.str()); if (identity_wrt_b) - cover_list("opt.opt_const.identwrt.b", "$add", "$sub", "$or", "$xor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", "$mul", "$div", cell->type.str()); + cover_list("opt.opt_expr.identwrt.b", "$add", "$sub", "$or", "$xor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", "$mul", "$div", cell->type.str()); log("Replacing %s cell `%s' in module `%s' with identity for port %c.\n", cell->type.c_str(), cell->name.c_str(), module->name.c_str(), identity_wrt_a ? 'A' : 'B'); @@ -786,14 +836,14 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if (mux_bool && (cell->type == "$mux" || cell->type == "$_MUX_") && cell->getPort("\\A") == RTLIL::SigSpec(0, 1) && cell->getPort("\\B") == RTLIL::SigSpec(1, 1)) { - cover_list("opt.opt_const.mux_bool", "$mux", "$_MUX_", cell->type.str()); + cover_list("opt.opt_expr.mux_bool", "$mux", "$_MUX_", cell->type.str()); replace_cell(assign_map, module, cell, "mux_bool", "\\Y", cell->getPort("\\S")); goto next_cell; } if (mux_bool && (cell->type == "$mux" || cell->type == "$_MUX_") && cell->getPort("\\A") == RTLIL::SigSpec(1, 1) && cell->getPort("\\B") == RTLIL::SigSpec(0, 1)) { - cover_list("opt.opt_const.mux_invert", "$mux", "$_MUX_", cell->type.str()); + cover_list("opt.opt_expr.mux_invert", "$mux", "$_MUX_", cell->type.str()); log("Replacing %s cell `%s' in module `%s' with inverter.\n", log_id(cell->type), log_id(cell), log_id(module)); cell->setPort("\\A", cell->getPort("\\S")); cell->unsetPort("\\B"); @@ -812,7 +862,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } if (consume_x && mux_bool && (cell->type == "$mux" || cell->type == "$_MUX_") && cell->getPort("\\A") == RTLIL::SigSpec(0, 1)) { - cover_list("opt.opt_const.mux_and", "$mux", "$_MUX_", cell->type.str()); + cover_list("opt.opt_expr.mux_and", "$mux", "$_MUX_", cell->type.str()); log("Replacing %s cell `%s' in module `%s' with and-gate.\n", log_id(cell->type), log_id(cell), log_id(module)); cell->setPort("\\A", cell->getPort("\\S")); cell->unsetPort("\\S"); @@ -832,7 +882,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } if (consume_x && mux_bool && (cell->type == "$mux" || cell->type == "$_MUX_") && cell->getPort("\\B") == RTLIL::SigSpec(1, 1)) { - cover_list("opt.opt_const.mux_or", "$mux", "$_MUX_", cell->type.str()); + cover_list("opt.opt_expr.mux_or", "$mux", "$_MUX_", cell->type.str()); log("Replacing %s cell `%s' in module `%s' with or-gate.\n", log_id(cell->type), log_id(cell), log_id(module)); cell->setPort("\\B", cell->getPort("\\S")); cell->unsetPort("\\S"); @@ -856,7 +906,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons int width = cell->getPort("\\A").size(); if ((cell->getPort("\\A").is_fully_undef() && cell->getPort("\\B").is_fully_undef()) || cell->getPort("\\S").is_fully_undef()) { - cover_list("opt.opt_const.mux_undef", "$mux", "$pmux", cell->type.str()); + cover_list("opt.opt_expr.mux_undef", "$mux", "$pmux", cell->type.str()); replace_cell(assign_map, module, cell, "mux_undef", "\\Y", cell->getPort("\\A")); goto next_cell; } @@ -875,17 +925,17 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons new_s = new_s.extract(0, new_s.size()-1); } if (new_s.size() == 0) { - cover_list("opt.opt_const.mux_empty", "$mux", "$pmux", cell->type.str()); + cover_list("opt.opt_expr.mux_empty", "$mux", "$pmux", cell->type.str()); replace_cell(assign_map, module, cell, "mux_empty", "\\Y", new_a); goto next_cell; } if (new_a == RTLIL::SigSpec(RTLIL::State::S0) && new_b == RTLIL::SigSpec(RTLIL::State::S1)) { - cover_list("opt.opt_const.mux_sel01", "$mux", "$pmux", cell->type.str()); + cover_list("opt.opt_expr.mux_sel01", "$mux", "$pmux", cell->type.str()); replace_cell(assign_map, module, cell, "mux_sel01", "\\Y", new_s); goto next_cell; } if (cell->getPort("\\S").size() != new_s.size()) { - cover_list("opt.opt_const.mux_reduce", "$mux", "$pmux", cell->type.str()); + cover_list("opt.opt_expr.mux_reduce", "$mux", "$pmux", cell->type.str()); log("Optimized away %d select inputs of %s cell `%s' in module `%s'.\n", GetSize(cell->getPort("\\S")) - GetSize(new_s), log_id(cell->type), log_id(cell), log_id(module)); cell->setPort("\\A", new_a); @@ -911,7 +961,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons RTLIL::SigSpec y(RTLIL::const_ ## _t(a.as_const(), dummy_arg, \ cell->parameters["\\A_SIGNED"].as_bool(), false, \ cell->parameters["\\Y_WIDTH"].as_int())); \ - cover("opt.opt_const.const.$" #_t); \ + cover("opt.opt_expr.const.$" #_t); \ replace_cell(assign_map, module, cell, stringf("%s", log_signal(a)), "\\Y", y); \ goto next_cell; \ } \ @@ -926,7 +976,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons cell->parameters["\\A_SIGNED"].as_bool(), \ cell->parameters["\\B_SIGNED"].as_bool(), \ cell->parameters["\\Y_WIDTH"].as_int())); \ - cover("opt.opt_const.const.$" #_t); \ + cover("opt.opt_expr.const.$" #_t); \ replace_cell(assign_map, module, cell, stringf("%s, %s", log_signal(a), log_signal(b)), "\\Y", y); \ goto next_cell; \ } \ @@ -1002,7 +1052,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if (a_val == 0) { - cover("opt.opt_const.mul_shift.zero"); + cover("opt.opt_expr.mul_shift.zero"); log("Replacing multiply-by-zero cell `%s' in module `%s' with zero-driver.\n", cell->name.c_str(), module->name.c_str()); @@ -1018,9 +1068,9 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if (a_val == (1 << i)) { if (swapped_ab) - cover("opt.opt_const.mul_shift.swapped"); + cover("opt.opt_expr.mul_shift.swapped"); else - cover("opt.opt_const.mul_shift.unswapped"); + cover("opt.opt_expr.mul_shift.unswapped"); log("Replacing multiply-by-%d cell `%s' in module `%s' with shift-by-%d.\n", a_val, cell->name.c_str(), module->name.c_str(), i); @@ -1048,6 +1098,75 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } } + if (!keepdc && cell->type.in("$div", "$mod")) + { + bool b_signed = cell->parameters["\\B_SIGNED"].as_bool(); + SigSpec sig_b = assign_map(cell->getPort("\\B")); + SigSpec sig_y = assign_map(cell->getPort("\\Y")); + + if (sig_b.is_fully_def() && sig_b.size() <= 32) + { + int b_val = sig_b.as_int(); + + if (b_val == 0) + { + cover("opt.opt_expr.divmod_zero"); + + log("Replacing divide-by-zero cell `%s' in module `%s' with undef-driver.\n", + cell->name.c_str(), module->name.c_str()); + + module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(State::Sx, sig_y.size()))); + module->remove(cell); + + did_something = true; + goto next_cell; + } + + for (int i = 1; i < (b_signed ? sig_b.size()-1 : sig_b.size()); i++) + if (b_val == (1 << i)) + { + if (cell->type == "$div") + { + cover("opt.opt_expr.div_shift"); + + log("Replacing divide-by-%d cell `%s' in module `%s' with shift-by-%d.\n", + b_val, cell->name.c_str(), module->name.c_str(), i); + + std::vector<RTLIL::SigBit> new_b = RTLIL::SigSpec(i, 6); + + while (GetSize(new_b) > 1 && new_b.back() == RTLIL::State::S0) + new_b.pop_back(); + + cell->type = "$shr"; + cell->parameters["\\B_WIDTH"] = GetSize(new_b); + cell->parameters["\\B_SIGNED"] = false; + cell->setPort("\\B", new_b); + cell->check(); + } + else + { + cover("opt.opt_expr.mod_mask"); + + log("Replacing modulo-by-%d cell `%s' in module `%s' with bitmask.\n", + b_val, cell->name.c_str(), module->name.c_str()); + + std::vector<RTLIL::SigBit> new_b = RTLIL::SigSpec(State::S1, i); + + if (b_signed) + new_b.push_back(State::S0); + + cell->type = "$and"; + cell->parameters["\\B_WIDTH"] = GetSize(new_b); + cell->setPort("\\B", new_b); + cell->check(); + } + + did_something = true; + goto next_cell; + } + } + } + next_cell:; #undef ACTION_DO #undef ACTION_DO_Y @@ -1056,15 +1175,16 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } } -struct OptConstPass : public Pass { - OptConstPass() : Pass("opt_const", "perform const folding") { } +struct OptExprPass : public Pass { + OptExprPass() : Pass("opt_expr", "perform const folding and simple expression rewriting") { } virtual void help() { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" opt_const [options] [selection]\n"); + log(" opt_expr [options] [selection]\n"); log("\n"); log("This pass performs const folding on internal cell types with constant inputs.\n"); + log("It also performs some simple expression rewritring.\n"); log("\n"); log(" -mux_undef\n"); log(" remove 'undef' inputs from $mux, $pmux and $_MUX_ cells\n"); @@ -1100,7 +1220,7 @@ struct OptConstPass : public Pass { bool do_fine = false; bool keepdc = false; - log_header("Executing OPT_CONST pass (perform const folding).\n"); + log_header(design, "Executing OPT_EXPR pass (perform const folding).\n"); log_push(); size_t argidx; @@ -1158,6 +1278,6 @@ struct OptConstPass : public Pass { log_pop(); } -} OptConstPass; +} OptExprPass; PRIVATE_NAMESPACE_END diff --git a/passes/opt/opt_share.cc b/passes/opt/opt_merge.cc index 46752e43..97989d27 100644 --- a/passes/opt/opt_share.cc +++ b/passes/opt/opt_merge.cc @@ -31,7 +31,7 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -struct OptShareWorker +struct OptMergeWorker { RTLIL::Design *design; RTLIL::Module *module; @@ -45,6 +45,29 @@ struct OptShareWorker dict<const RTLIL::Cell*, std::string> cell_hash_cache; #endif + static void sort_pmux_conn(dict<RTLIL::IdString, RTLIL::SigSpec> &conn) + { + SigSpec sig_s = conn.at("\\S"); + SigSpec sig_b = conn.at("\\B"); + + int s_width = GetSize(sig_s); + int width = GetSize(sig_b) / s_width; + + vector<pair<SigBit, SigSpec>> sb_pairs; + for (int i = 0; i < s_width; i++) + sb_pairs.push_back(pair<SigBit, SigSpec>(sig_s[i], sig_b.extract(i*width, width))); + + std::sort(sb_pairs.begin(), sb_pairs.end()); + + conn["\\S"] = SigSpec(); + conn["\\B"] = SigSpec(); + + for (auto &it : sb_pairs) { + conn["\\S"].append(it.first); + conn["\\B"].append(it.second); + } + } + #ifdef USE_CELL_HASH_CACHE std::string int_to_hash_string(unsigned int v) { @@ -91,25 +114,40 @@ struct OptShareWorker assign_map.apply(alt_conn.at("\\A")); alt_conn.at("\\A").sort_and_unify(); conn = &alt_conn; + } else + if (cell->type == "$pmux") { + alt_conn = *conn; + assign_map.apply(alt_conn.at("\\A")); + assign_map.apply(alt_conn.at("\\B")); + assign_map.apply(alt_conn.at("\\S")); + sort_pmux_conn(alt_conn); + conn = &alt_conn; } + vector<string> hash_conn_strings; + for (auto &it : *conn) { if (cell->output(it.first)) continue; RTLIL::SigSpec sig = it.second; assign_map.apply(sig); - hash_string += "C " + it.first.str() + "="; + string s = "C " + it.first.str() + "="; for (auto &chunk : sig.chunks()) { if (chunk.wire) - hash_string += "{" + chunk.wire->name.str() + " " + + s += "{" + chunk.wire->name.str() + " " + int_to_hash_string(chunk.offset) + " " + int_to_hash_string(chunk.width) + "}"; else - hash_string += RTLIL::Const(chunk.data).as_string(); + s += RTLIL::Const(chunk.data).as_string(); } - hash_string += "\n"; + hash_conn_strings.push_back(s + "\n"); } + std::sort(hash_conn_strings.begin(), hash_conn_strings.end()); + + for (auto it : hash_conn_strings) + hash_string += it; + cell_hash_cache[cell] = sha1(hash_string); return cell_hash_cache[cell]; } @@ -171,6 +209,10 @@ struct OptShareWorker if (cell1->type == "$reduce_and" || cell1->type == "$reduce_or" || cell1->type == "$reduce_bool") { conn1["\\A"].sort_and_unify(); conn2["\\A"].sort_and_unify(); + } else + if (cell1->type == "$pmux") { + sort_pmux_conn(conn1); + sort_pmux_conn(conn2); } if (conn1 != conn2) { @@ -212,14 +254,14 @@ struct OptShareWorker } struct CompareCells { - OptShareWorker *that; - CompareCells(OptShareWorker *that) : that(that) {} + OptMergeWorker *that; + CompareCells(OptMergeWorker *that) : that(that) {} bool operator()(const RTLIL::Cell *cell1, const RTLIL::Cell *cell2) const { return that->compare_cells(cell1, cell2); } }; - OptShareWorker(RTLIL::Design *design, RTLIL::Module *module, bool mode_nomux, bool mode_share_all) : + OptMergeWorker(RTLIL::Design *design, RTLIL::Module *module, bool mode_nomux, bool mode_share_all) : design(design), module(module), assign_map(module), mode_share_all(mode_share_all) { total_count = 0; @@ -286,13 +328,13 @@ struct OptShareWorker } }; -struct OptSharePass : public Pass { - OptSharePass() : Pass("opt_share", "consolidate identical cells") { } +struct OptMergePass : public Pass { + OptMergePass() : Pass("opt_merge", "consolidate identical cells") { } virtual void help() { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" opt_share [options] [selection]\n"); + log(" opt_merge [options] [selection]\n"); log("\n"); log("This pass identifies cells with identical type and input signals. Such cells\n"); log("are then merged to one cell.\n"); @@ -306,7 +348,7 @@ struct OptSharePass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing OPT_SHARE pass (detect identical cells).\n"); + log_header(design, "Executing OPT_MERGE pass (detect identical cells).\n"); bool mode_nomux = false; bool mode_share_all = false; @@ -328,7 +370,7 @@ struct OptSharePass : public Pass { int total_count = 0; for (auto module : design->selected_modules()) { - OptShareWorker worker(design, module, mode_nomux, mode_share_all); + OptMergeWorker worker(design, module, mode_nomux, mode_share_all); total_count += worker.total_count; } @@ -336,6 +378,6 @@ struct OptSharePass : public Pass { design->scratchpad_set_bool("opt.did_something", true); log("Removed a total of %d cells.\n", total_count); } -} OptSharePass; +} OptMergePass; PRIVATE_NAMESPACE_END diff --git a/passes/opt/opt_muxtree.cc b/passes/opt/opt_muxtree.cc index 905a0162..f5ddc2af 100644 --- a/passes/opt/opt_muxtree.cc +++ b/passes/opt/opt_muxtree.cc @@ -68,7 +68,7 @@ struct OptMuxtreeWorker OptMuxtreeWorker(RTLIL::Design *design, RTLIL::Module *module) : design(design), module(module), assign_map(module), removed_count(0) { - log("Running muxtree optimizier on module %s..\n", module->name.c_str()); + log("Running muxtree optimizer on module %s..\n", module->name.c_str()); log(" Creating internal representation of mux trees.\n"); @@ -464,7 +464,7 @@ struct OptMuxtreePass : public Pass { } virtual void execute(vector<std::string> args, RTLIL::Design *design) { - log_header("Executing OPT_MUXTREE pass (detect dead branches in mux trees).\n"); + log_header(design, "Executing OPT_MUXTREE pass (detect dead branches in mux trees).\n"); extra_args(args, 1, design); int total_count = 0; diff --git a/passes/opt/opt_reduce.cc b/passes/opt/opt_reduce.cc index 98b7b2e1..eb9d02ad 100644 --- a/passes/opt/opt_reduce.cc +++ b/passes/opt/opt_reduce.cc @@ -354,7 +354,7 @@ struct OptReducePass : public Pass { { bool do_fine = false; - log_header("Executing OPT_REDUCE pass (consolidate $*mux and $reduce_* inputs).\n"); + log_header(design, "Executing OPT_REDUCE pass (consolidate $*mux and $reduce_* inputs).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { diff --git a/passes/opt/opt_rmdff.cc b/passes/opt/opt_rmdff.cc index e1b184af..fa954afa 100644 --- a/passes/opt/opt_rmdff.cc +++ b/passes/opt/opt_rmdff.cc @@ -60,7 +60,7 @@ bool handle_dlatch(RTLIL::Module *mod, RTLIL::Cell *dlatch) return false; delete_dlatch: - log("Removing %s (%s) from module %s.\n", dlatch->name.c_str(), dlatch->type.c_str(), mod->name.c_str()); + log("Removing %s (%s) from module %s.\n", log_id(dlatch), log_id(dlatch->type), log_id(mod)); remove_init_attr(dlatch->getPort("\\Q")); mod->remove(dlatch); return true; @@ -170,7 +170,7 @@ bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff) return false; delete_dff: - log("Removing %s (%s) from module %s.\n", dff->name.c_str(), dff->type.c_str(), mod->name.c_str()); + log("Removing %s (%s) from module %s.\n", log_id(dff), log_id(dff->type), log_id(mod)); remove_init_attr(dff->getPort("\\Q")); mod->remove(dff); return true; @@ -190,73 +190,116 @@ struct OptRmdffPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - int total_count = 0; - log_header("Executing OPT_RMDFF pass (remove dff with constant values).\n"); + int total_count = 0, total_initdrv = 0; + log_header(design, "Executing OPT_RMDFF pass (remove dff with constant values).\n"); extra_args(args, 1, design); - for (auto &mod_it : design->modules_) + for (auto module : design->selected_modules()) { - if (!design->selected(mod_it.second)) - continue; - - assign_map.set(mod_it.second); - dff_init_map.set(mod_it.second); - for (auto &it : mod_it.second->wires_) - if (it.second->attributes.count("\\init") != 0) { - dff_init_map.add(it.second, it.second->attributes.at("\\init")); - for (int i = 0; i < GetSize(it.second); i++) { - SigBit wire_bit(it.second, i), mapped_bit = assign_map(wire_bit); - if (mapped_bit.wire) + pool<SigBit> driven_bits; + dict<SigBit, State> init_bits; + + assign_map.set(module); + dff_init_map.set(module); + + for (auto wire : module->wires()) + { + if (wire->attributes.count("\\init") != 0) { + Const initval = wire->attributes.at("\\init"); + dff_init_map.add(wire, initval); + for (int i = 0; i < GetSize(wire); i++) { + SigBit wire_bit(wire, i), mapped_bit = assign_map(wire_bit); + if (mapped_bit.wire) { init_attributes[mapped_bit].insert(wire_bit); + if (i < GetSize(initval)) + init_bits[mapped_bit] = initval[i]; + } } } + + if (wire->port_input) { + for (auto bit : assign_map(wire)) + driven_bits.insert(bit); + } + } mux_drivers.clear(); std::vector<RTLIL::IdString> dff_list; std::vector<RTLIL::IdString> dlatch_list; - for (auto &it : mod_it.second->cells_) { - if (it.second->type == "$mux" || it.second->type == "$pmux") { - if (it.second->getPort("\\A").size() == it.second->getPort("\\B").size()) - mux_drivers.insert(assign_map(it.second->getPort("\\Y")), it.second); + for (auto cell : module->cells()) + { + for (auto &conn : cell->connections()) + if (cell->output(conn.first) || !cell->known()) + for (auto bit : assign_map(conn.second)) + driven_bits.insert(bit); + + if (cell->type == "$mux" || cell->type == "$pmux") { + if (cell->getPort("\\A").size() == cell->getPort("\\B").size()) + mux_drivers.insert(assign_map(cell->getPort("\\Y")), cell); continue; } - if (!design->selected(mod_it.second, it.second)) + + if (!design->selected(module, cell)) continue; - if (it.second->type == "$_DFF_N_") dff_list.push_back(it.first); - if (it.second->type == "$_DFF_P_") dff_list.push_back(it.first); - if (it.second->type == "$_DFF_NN0_") dff_list.push_back(it.first); - if (it.second->type == "$_DFF_NN1_") dff_list.push_back(it.first); - if (it.second->type == "$_DFF_NP0_") dff_list.push_back(it.first); - if (it.second->type == "$_DFF_NP1_") dff_list.push_back(it.first); - if (it.second->type == "$_DFF_PN0_") dff_list.push_back(it.first); - if (it.second->type == "$_DFF_PN1_") dff_list.push_back(it.first); - if (it.second->type == "$_DFF_PP0_") dff_list.push_back(it.first); - if (it.second->type == "$_DFF_PP1_") dff_list.push_back(it.first); - if (it.second->type == "$dff") dff_list.push_back(it.first); - if (it.second->type == "$adff") dff_list.push_back(it.first); - if (it.second->type == "$dlatch") dlatch_list.push_back(it.first); + + if (cell->type.in("$_DFF_N_", "$_DFF_P_", + "$_DFF_NN0_", "$_DFF_NN1_", "$_DFF_NP0_", "$_DFF_NP1_", + "$_DFF_PN0_", "$_DFF_PN1_", "$_DFF_PP0_", "$_DFF_PP1_", + "$dff", "$adff")) + dff_list.push_back(cell->name); + + if (cell->type == "$dlatch") + dlatch_list.push_back(cell->name); } for (auto &id : dff_list) { - if (mod_it.second->cells_.count(id) > 0 && - handle_dff(mod_it.second, mod_it.second->cells_[id])) + if (module->cell(id) != nullptr && + handle_dff(module, module->cells_[id])) total_count++; } for (auto &id : dlatch_list) { - if (mod_it.second->cells_.count(id) > 0 && - handle_dlatch(mod_it.second, mod_it.second->cells_[id])) + if (module->cell(id) != nullptr && + handle_dlatch(module, module->cells_[id])) total_count++; } + + SigSpec const_init_sigs; + + for (auto bit : init_bits) + if (!driven_bits.count(bit.first)) + const_init_sigs.append(bit.first); + + const_init_sigs.sort_and_unify(); + + for (SigSpec sig : const_init_sigs.chunks()) + { + Const val; + + for (auto bit : sig) + val.bits.push_back(init_bits.at(bit)); + + log("Promoting init spec %s = %s to constant driver in module %s.\n", + log_signal(sig), log_signal(val), log_id(module)); + + module->connect(sig, val); + remove_init_attr(sig); + total_initdrv++; + } } assign_map.clear(); mux_drivers.clear(); - if (total_count) + if (total_count || total_initdrv) design->scratchpad_set_bool("opt.did_something", true); - log("Replaced %d DFF cells.\n", total_count); + + if (total_initdrv) + log("Promoted %d init specs to constant drivers.\n", total_initdrv); + + if (total_count) + log("Replaced %d DFF cells.\n", total_count); } } OptRmdffPass; diff --git a/passes/opt/share.cc b/passes/opt/share.cc index 1d9b1006..22914eaa 100644 --- a/passes/opt/share.cc +++ b/passes/opt/share.cc @@ -793,10 +793,59 @@ struct ShareWorker return true; } - void optimize_activation_patterns(pool<ssc_pair_t> & /* patterns */) + void optimize_activation_patterns(pool<ssc_pair_t> &patterns) { // TODO: Remove patterns that are contained in other patterns - // TODO: Consolidate pairs of patterns that only differ in the value for one signal bit + + dict<SigSpec, pool<Const>> db; + bool did_something = false; + + for (auto const &p : patterns) + { + auto &sig = p.first; + auto &val = p.second; + int len = GetSize(sig); + + for (int i = 0; i < len; i++) + { + auto otherval = val; + + if (otherval.bits[i] == State::S0) + otherval.bits[i] = State::S1; + else if (otherval.bits[i] == State::S1) + otherval.bits[i] = State::S0; + else + continue; + + if (db[sig].count(otherval)) + { + auto newsig = sig; + newsig.remove(i); + + auto newval = val; + newval.bits.erase(newval.bits.begin() + i); + + db[newsig].insert(newval); + db[sig].erase(otherval); + + did_something = true; + goto next_pattern; + } + } + + db[sig].insert(val); + next_pattern:; + } + + if (!did_something) + return; + + patterns.clear(); + for (auto &it : db) + for (auto &val : it.second) + patterns.insert(make_pair(it.first, val)); + + optimize_activation_patterns(patterns); } const pool<ssc_pair_t> &find_cell_activation_patterns(RTLIL::Cell *cell, const char *indent) @@ -1451,7 +1500,7 @@ struct SharePass : public Pass { config.generic_other_ops.insert("$alu"); config.generic_other_ops.insert("$macc"); - log_header("Executing SHARE pass (SAT-based resource sharing).\n"); + log_header(design, "Executing SHARE pass (SAT-based resource sharing).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { diff --git a/passes/opt/wreduce.cc b/passes/opt/wreduce.cc index 4f08da67..07503fbb 100644 --- a/passes/opt/wreduce.cc +++ b/passes/opt/wreduce.cc @@ -366,15 +366,26 @@ struct WreducePass : public Pass { log(" assign y = a + b + c + 1;\n"); log(" endmodule\n"); log("\n"); + log("Options:\n"); + log("\n"); + log(" -memx\n"); + log(" Do not change the width of memory address ports. Use this options in\n"); + log(" flows that use the 'memory_memx' pass.\n"); + log("\n"); } virtual void execute(std::vector<std::string> args, Design *design) { WreduceConfig config; + bool opt_memx = false; - log_header("Executing WREDUCE pass (reducing word size of cells).\n"); + log_header(design, "Executing WREDUCE pass (reducing word size of cells).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-memx") { + opt_memx = true; + continue; + } break; } extra_args(args, argidx, design); @@ -385,6 +396,7 @@ struct WreducePass : public Pass { continue; for (auto c : module->selected_cells()) + { if (c->type.in("$reduce_and", "$reduce_or", "$reduce_xor", "$reduce_xnor", "$reduce_bool", "$lt", "$le", "$eq", "$ne", "$eqx", "$nex", "$ge", "$gt", "$logic_not", "$logic_and", "$logic_or") && GetSize(c->getPort("\\Y")) > 1) { @@ -396,6 +408,23 @@ struct WreducePass : public Pass { module->connect(sig, Const(0, GetSize(sig))); } } + if (!opt_memx && c->type.in("$memrd", "$memwr", "$meminit")) { + IdString memid = c->getParam("\\MEMID").decode_string(); + RTLIL::Memory *mem = module->memories.at(memid); + if (mem->start_offset >= 0) { + int cur_addrbits = c->getParam("\\ABITS").as_int(); + int max_addrbits = ceil_log2(mem->start_offset + mem->size); + if (cur_addrbits > max_addrbits) { + log("Removed top %d address bits (of %d) from memory %s port %s.%s (%s).\n", + cur_addrbits-max_addrbits, cur_addrbits, + c->type == "$memrd" ? "read" : c->type == "$memwr" ? "write" : "init", + log_id(module), log_id(c), log_id(memid)); + c->setParam("\\ABITS", max_addrbits); + c->setPort("\\ADDR", c->getPort("\\ADDR").extract(0, max_addrbits)); + } + } + } + } WreduceWorker worker(&config, module); worker.run(); diff --git a/passes/proc/proc.cc b/passes/proc/proc.cc index 577ff6bf..d5366f26 100644 --- a/passes/proc/proc.cc +++ b/passes/proc/proc.cc @@ -52,12 +52,17 @@ struct ProcPass : public Pass { log(" -global_arst [!]<netname>\n"); log(" This option is passed through to proc_arst.\n"); log("\n"); + log(" -ifx\n"); + log(" This option is passed through to proc_mux. proc_rmdead is not\n"); + log(" executed in -ifx mode.\n"); + log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { std::string global_arst; + bool ifxmode = false; - log_header("Executing PROC pass (convert processes to netlists).\n"); + log_header(design, "Executing PROC pass (convert processes to netlists).\n"); log_push(); size_t argidx; @@ -67,18 +72,23 @@ struct ProcPass : public Pass { global_arst = args[++argidx]; continue; } + if (args[argidx] == "-ifx") { + ifxmode = true; + continue; + } break; } extra_args(args, argidx, design); Pass::call(design, "proc_clean"); - Pass::call(design, "proc_rmdead"); + if (!ifxmode) + Pass::call(design, "proc_rmdead"); Pass::call(design, "proc_init"); if (global_arst.empty()) Pass::call(design, "proc_arst"); else Pass::call(design, "proc_arst -global_arst " + global_arst); - Pass::call(design, "proc_mux"); + Pass::call(design, ifxmode ? "proc_mux -ifx" : "proc_mux"); Pass::call(design, "proc_dlatch"); Pass::call(design, "proc_dff"); Pass::call(design, "proc_clean"); diff --git a/passes/proc/proc_arst.cc b/passes/proc/proc_arst.cc index 1da23728..216b00dd 100644 --- a/passes/proc/proc_arst.cc +++ b/passes/proc/proc_arst.cc @@ -226,7 +226,7 @@ struct ProcArstPass : public Pass { std::string global_arst; bool global_arst_neg = false; - log_header("Executing PROC_ARST pass (detect async resets in processes).\n"); + log_header(design, "Executing PROC_ARST pass (detect async resets in processes).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) diff --git a/passes/proc/proc_clean.cc b/passes/proc/proc_clean.cc index 35801951..7dbabc21 100644 --- a/passes/proc/proc_clean.cc +++ b/passes/proc/proc_clean.cc @@ -156,7 +156,7 @@ struct ProcCleanPass : public Pass { virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { int total_count = 0; - log_header("Executing PROC_CLEAN pass (remove empty switches from decision trees).\n"); + log_header(design, "Executing PROC_CLEAN pass (remove empty switches from decision trees).\n"); extra_args(args, 1, design); diff --git a/passes/proc/proc_dff.cc b/passes/proc/proc_dff.cc index 63713139..f532990c 100644 --- a/passes/proc/proc_dff.cc +++ b/passes/proc/proc_dff.cc @@ -369,7 +369,7 @@ struct ProcDffPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing PROC_DFF pass (convert process syncs to FFs).\n"); + log_header(design, "Executing PROC_DFF pass (convert process syncs to FFs).\n"); extra_args(args, 1, design); diff --git a/passes/proc/proc_dlatch.cc b/passes/proc/proc_dlatch.cc index e37d81dd..6621afd3 100644 --- a/passes/proc/proc_dlatch.cc +++ b/passes/proc/proc_dlatch.cc @@ -33,6 +33,8 @@ struct proc_dlatch_db_t Module *module; SigMap sigmap; + pool<Cell*> generated_dlatches; + dict<Cell*, vector<SigBit>> mux_srcbits; dict<SigBit, pair<Cell*, int>> mux_drivers; dict<SigBit, int> sigusers; @@ -40,10 +42,24 @@ struct proc_dlatch_db_t { for (auto cell : module->cells()) { - if (cell->type.in("$mux", "$pmux")) { + if (cell->type.in("$mux", "$pmux")) + { auto sig_y = sigmap(cell->getPort("\\Y")); for (int i = 0; i < GetSize(sig_y); i++) mux_drivers[sig_y[i]] = pair<Cell*, int>(cell, i); + + pool<SigBit> mux_srcbits_pool; + for (auto bit : sigmap(cell->getPort("\\A"))) + mux_srcbits_pool.insert(bit); + for (auto bit : sigmap(cell->getPort("\\B"))) + mux_srcbits_pool.insert(bit); + + vector<SigBit> mux_srcbits_vec; + for (auto bit : mux_srcbits_pool) + if (bit.wire != nullptr) + mux_srcbits_vec.push_back(bit); + + mux_srcbits[cell].swap(mux_srcbits_vec); } for (auto &conn : cell->connections()) @@ -58,6 +74,42 @@ struct proc_dlatch_db_t sigusers[bit]++; } + bool quickcheck(const SigSpec &haystack, const SigSpec &needle) + { + pool<SigBit> haystack_bits = sigmap(haystack).to_sigbit_pool(); + pool<SigBit> needle_bits = sigmap(needle).to_sigbit_pool(); + + pool<Cell*> cells_queue, cells_visited; + pool<SigBit> bits_queue, bits_visited; + + bits_queue = haystack_bits; + while (!bits_queue.empty()) + { + for (auto &bit : bits_queue) { + auto it = mux_drivers.find(bit); + if (it != mux_drivers.end()) + if (!cells_visited.count(it->second.first)) + cells_queue.insert(it->second.first); + bits_visited.insert(bit); + } + + bits_queue.clear(); + + for (auto c : cells_queue) { + for (auto bit : mux_srcbits[c]) { + if (needle_bits.count(bit)) + return true; + if (!bits_visited.count(bit)) + bits_queue.insert(bit); + } + } + + cells_queue.clear(); + } + + return false; + } + struct rule_node_t { // a node is true if "signal" equals "match" and [any @@ -202,6 +254,84 @@ struct proc_dlatch_db_t rules_sig[n] = and_bits[0]; return and_bits[0]; } + + void fixup_mux(Cell *cell) + { + SigSpec sig_a = cell->getPort("\\A"); + SigSpec sig_b = cell->getPort("\\B"); + SigSpec sig_s = cell->getPort("\\S"); + SigSpec sig_any_valid_b; + + SigSpec sig_new_b, sig_new_s; + for (int i = 0; i < GetSize(sig_s); i++) { + SigSpec b = sig_b.extract(i*GetSize(sig_a), GetSize(sig_a)); + if (!b.is_fully_undef()) { + sig_any_valid_b = b; + sig_new_b.append(b); + sig_new_s.append(sig_s[i]); + } + } + + if (sig_new_s.empty()) { + sig_new_b = sig_a; + sig_new_s = State::S0; + } + + if (sig_a.is_fully_undef() && !sig_any_valid_b.empty()) + cell->setPort("\\A", sig_any_valid_b); + + if (GetSize(sig_new_s) == 1) { + cell->type = "$mux"; + cell->unsetParam("\\S_WIDTH"); + } else { + cell->type = "$pmux"; + cell->setParam("\\S_WIDTH", GetSize(sig_new_s)); + } + + cell->setPort("\\B", sig_new_b); + cell->setPort("\\S", sig_new_s); + } + + void fixup_muxes() + { + pool<Cell*> visited, queue; + dict<Cell*, pool<SigBit>> upstream_cell2net; + dict<SigBit, pool<Cell*>> upstream_net2cell; + + CellTypes ct; + ct.setup_internals(); + + for (auto cell : module->cells()) + for (auto conn : cell->connections()) { + if (cell->input(conn.first)) + for (auto bit : sigmap(conn.second)) + upstream_cell2net[cell].insert(bit); + if (cell->output(conn.first)) + for (auto bit : sigmap(conn.second)) + upstream_net2cell[bit].insert(cell); + } + + queue = generated_dlatches; + while (!queue.empty()) + { + pool<Cell*> next_queue; + + for (auto cell : queue) { + if (cell->type.in("$mux", "$pmux")) + fixup_mux(cell); + for (auto bit : upstream_cell2net[cell]) + for (auto cell : upstream_net2cell[bit]) + next_queue.insert(cell); + visited.insert(cell); + } + + queue.clear(); + for (auto cell : next_queue) { + if (!visited.count(cell) && ct.cell_known(cell->type)) + queue.insert(cell); + } + } + } }; void proc_dlatch(proc_dlatch_db_t &db, RTLIL::Process *proc) @@ -218,9 +348,17 @@ void proc_dlatch(proc_dlatch_db_t &db, RTLIL::Process *proc) continue; } - for (auto ss : sr->actions) { + for (auto ss : sr->actions) + { db.sigmap.apply(ss.first); db.sigmap.apply(ss.second); + + if (!db.quickcheck(ss.second, ss.first)) { + nolatches_bits.first.append(ss.first); + nolatches_bits.second.append(ss.second); + continue; + } + for (int i = 0; i < GetSize(ss.first); i++) latches_out_in[ss.first[i]] = ss.second[i]; } @@ -268,6 +406,8 @@ void proc_dlatch(proc_dlatch_db_t &db, RTLIL::Process *proc) SigSpec rhs = latches_bits.second.extract(offset, width); Cell *cell = db.module->addDlatch(NEW_ID, db.module->Not(NEW_ID, db.make_hold(n)), rhs, lhs); + db.generated_dlatches.insert(cell); + log("Latch inferred for signal `%s.%s' from process `%s.%s': %s\n", db.module->name.c_str(), log_signal(lhs), db.module->name.c_str(), proc->name.c_str(), log_id(cell)); } @@ -292,7 +432,7 @@ struct ProcDlatchPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing PROC_DLATCH pass (convert process syncs to latches).\n"); + log_header(design, "Executing PROC_DLATCH pass (convert process syncs to latches).\n"); extra_args(args, 1, design); @@ -301,6 +441,7 @@ struct ProcDlatchPass : public Pass { for (auto &proc_it : module->processes) if (design->selected(module, proc_it.second)) proc_dlatch(db, proc_it.second); + db.fixup_muxes(); } } } ProcDlatchPass; diff --git a/passes/proc/proc_init.cc b/passes/proc/proc_init.cc index 633d4e58..0c8fb83d 100644 --- a/passes/proc/proc_init.cc +++ b/passes/proc/proc_init.cc @@ -61,13 +61,28 @@ void proc_init(RTLIL::Module *mod, RTLIL::Process *proc) log_cmd_error("Failed to get a constant init value for %s: %s\n", log_signal(lhs), log_signal(rhs)); int offset = 0; - for (auto &lhs_c : lhs.chunks()) { - if (lhs_c.wire != NULL) { - RTLIL::SigSpec value = rhs.extract(offset, lhs_c.width); - if (value.size() != lhs_c.wire->width) - log_cmd_error("Init value is not for the entire wire: %s = %s\n", log_signal(lhs_c), log_signal(value)); - log(" Setting init value: %s = %s\n", log_signal(lhs_c.wire), log_signal(value)); - lhs_c.wire->attributes["\\init"] = value.as_const(); + for (auto &lhs_c : lhs.chunks()) + { + if (lhs_c.wire != nullptr) + { + SigSpec valuesig = rhs.extract(offset, lhs_c.width); + if (!valuesig.is_fully_const()) + log_cmd_error("Non-const initialization value: %s = %s\n", log_signal(lhs_c), log_signal(valuesig)); + + Const value = valuesig.as_const(); + Const &wireinit = lhs_c.wire->attributes["\\init"]; + + while (GetSize(wireinit.bits) < lhs_c.wire->width) + wireinit.bits.push_back(State::Sx); + + for (int i = 0; i < lhs_c.width; i++) { + auto &initbit = wireinit.bits[i + lhs_c.offset]; + if (initbit != State::Sx && initbit != value[i]) + log_cmd_error("Conflicting initialization values for %s.\n", log_signal(lhs_c)); + initbit = value[i]; + } + + log(" Set init value: %s = %s\n", log_signal(lhs_c.wire), log_signal(wireinit)); } offset += lhs_c.width; } @@ -100,7 +115,7 @@ struct ProcInitPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing PROC_INIT pass (extract init attributes).\n"); + log_header(design, "Executing PROC_INIT pass (extract init attributes).\n"); extra_args(args, 1, design); diff --git a/passes/proc/proc_mux.cc b/passes/proc/proc_mux.cc index 943e8c56..57e131ca 100644 --- a/passes/proc/proc_mux.cc +++ b/passes/proc/proc_mux.cc @@ -143,7 +143,7 @@ struct SnippetSwCache } }; -RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SwitchRule *sw) +RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SwitchRule *sw, bool ifxmode) { std::stringstream sstr; sstr << "$procmux$" << (autoidx++); @@ -164,14 +164,14 @@ RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s if (comp.size() == 0) return RTLIL::SigSpec(); - if (sig.size() == 1 && comp == RTLIL::SigSpec(1,1)) + if (sig.size() == 1 && comp == RTLIL::SigSpec(1,1) && !ifxmode) { mod->connect(RTLIL::SigSig(RTLIL::SigSpec(cmp_wire, cmp_wire->width++), sig)); } else { // create compare cell - RTLIL::Cell *eq_cell = mod->addCell(stringf("%s_CMP%d", sstr.str().c_str(), cmp_wire->width), "$eq"); + RTLIL::Cell *eq_cell = mod->addCell(stringf("%s_CMP%d", sstr.str().c_str(), cmp_wire->width), ifxmode ? "$eqx" : "$eq"); eq_cell->attributes = sw->attributes; eq_cell->parameters["\\A_SIGNED"] = RTLIL::Const(0); @@ -211,7 +211,7 @@ RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s return RTLIL::SigSpec(ctrl_wire); } -RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SigSpec when_signal, RTLIL::SigSpec else_signal, RTLIL::Cell *&last_mux_cell, RTLIL::SwitchRule *sw) +RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SigSpec when_signal, RTLIL::SigSpec else_signal, RTLIL::Cell *&last_mux_cell, RTLIL::SwitchRule *sw, bool ifxmode) { log_assert(when_signal.size() == else_signal.size()); @@ -223,7 +223,7 @@ RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s return when_signal; // compare results - RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw); + RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw, ifxmode); if (ctrl_sig.size() == 0) return when_signal; log_assert(ctrl_sig.size() == 1); @@ -245,12 +245,15 @@ RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s return RTLIL::SigSpec(result_wire); } -void append_pmux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SigSpec when_signal, RTLIL::Cell *last_mux_cell, RTLIL::SwitchRule *sw) +void append_pmux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SigSpec when_signal, RTLIL::Cell *last_mux_cell, RTLIL::SwitchRule *sw, bool ifxmode) { log_assert(last_mux_cell != NULL); log_assert(when_signal.size() == last_mux_cell->getPort("\\A").size()); - RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw); + if (when_signal == last_mux_cell->getPort("\\A")) + return; + + RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw, ifxmode); log_assert(ctrl_sig.size() == 1); last_mux_cell->type = "$pmux"; @@ -266,7 +269,7 @@ void append_pmux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::ve } RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, SnippetSwCache &swcache, dict<RTLIL::SwitchRule*, bool, hash_ptr_ops> &swpara, - RTLIL::CaseRule *cs, const RTLIL::SigSpec &sig, const RTLIL::SigSpec &defval) + RTLIL::CaseRule *cs, const RTLIL::SigSpec &sig, const RTLIL::SigSpec &defval, bool ifxmode) { RTLIL::SigSpec result = defval; @@ -329,7 +332,7 @@ RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, SnippetSwCache &swcache, d for (auto pat : cs2->compare) if (!pat.is_fully_const()) extra_group_for_next_case = true; - else + else if (!ifxmode) pool.take(pat); } } @@ -340,18 +343,18 @@ RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, SnippetSwCache &swcache, d for (size_t i = 0; i < sw->cases.size(); i++) { int case_idx = sw->cases.size() - i - 1; RTLIL::CaseRule *cs2 = sw->cases[case_idx]; - RTLIL::SigSpec value = signal_to_mux_tree(mod, swcache, swpara, cs2, sig, initial_val); + RTLIL::SigSpec value = signal_to_mux_tree(mod, swcache, swpara, cs2, sig, initial_val, ifxmode); if (last_mux_cell && pgroups[case_idx] == pgroups[case_idx+1]) - append_pmux(mod, sw->signal, cs2->compare, value, last_mux_cell, sw); + append_pmux(mod, sw->signal, cs2->compare, value, last_mux_cell, sw, ifxmode); else - result = gen_mux(mod, sw->signal, cs2->compare, value, result, last_mux_cell, sw); + result = gen_mux(mod, sw->signal, cs2->compare, value, result, last_mux_cell, sw, ifxmode); } } return result; } -void proc_mux(RTLIL::Module *mod, RTLIL::Process *proc) +void proc_mux(RTLIL::Module *mod, RTLIL::Process *proc, bool ifxmode) { log("Creating decoders for process `%s.%s'.\n", mod->name.c_str(), proc->name.c_str()); @@ -372,7 +375,7 @@ void proc_mux(RTLIL::Module *mod, RTLIL::Process *proc) log("%6d/%d: %s\n", ++cnt, GetSize(sigsnip.snippets), log_signal(sig)); - RTLIL::SigSpec value = signal_to_mux_tree(mod, swcache, swpara, &proc->root_case, sig, RTLIL::SigSpec(RTLIL::State::Sx, sig.size())); + RTLIL::SigSpec value = signal_to_mux_tree(mod, swcache, swpara, &proc->root_case, sig, RTLIL::SigSpec(RTLIL::State::Sx, sig.size()), ifxmode); mod->connect(RTLIL::SigSig(sig, value)); } } @@ -383,23 +386,37 @@ struct ProcMuxPass : public Pass { { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" proc_mux [selection]\n"); + log(" proc_mux [options] [selection]\n"); log("\n"); log("This pass converts the decision trees in processes (originating from if-else\n"); log("and case statements) to trees of multiplexer cells.\n"); log("\n"); + log(" -ifx\n"); + log(" Use Verilog simulation behavior with respect to undef values in\n"); + log(" 'case' expressions and 'if' conditions.\n"); + log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing PROC_MUX pass (convert decision trees to multiplexers).\n"); + bool ifxmode = false; + log_header(design, "Executing PROC_MUX pass (convert decision trees to multiplexers).\n"); - extra_args(args, 1, design); + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-ifx") { + ifxmode = true; + continue; + } + break; + } + extra_args(args, argidx, design); for (auto mod : design->modules()) if (design->selected(mod)) for (auto &proc_it : mod->processes) if (design->selected(mod, proc_it.second)) - proc_mux(mod, proc_it.second); + proc_mux(mod, proc_it.second, ifxmode); } } ProcMuxPass; diff --git a/passes/proc/proc_rmdead.cc b/passes/proc/proc_rmdead.cc index f60d4b30..5672fb47 100644 --- a/passes/proc/proc_rmdead.cc +++ b/passes/proc/proc_rmdead.cc @@ -51,8 +51,8 @@ void proc_rmdead(RTLIL::SwitchRule *sw, int &counter) counter++; continue; } - if (pool.empty()) - sw->cases[i]->compare.clear(); + // if (pool.empty()) + // sw->cases[i]->compare.clear(); } for (auto switch_it : sw->cases[i]->switches) @@ -76,7 +76,7 @@ struct ProcRmdeadPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing PROC_RMDEAD pass (remove dead branches from decision trees).\n"); + log_header(design, "Executing PROC_RMDEAD pass (remove dead branches from decision trees).\n"); extra_args(args, 1, design); diff --git a/passes/sat/Makefile.inc b/passes/sat/Makefile.inc index 4fa6bf0d..0c5f6fc6 100644 --- a/passes/sat/Makefile.inc +++ b/passes/sat/Makefile.inc @@ -4,4 +4,5 @@ OBJS += passes/sat/freduce.o OBJS += passes/sat/eval.o OBJS += passes/sat/miter.o OBJS += passes/sat/expose.o +OBJS += passes/sat/assertpmux.o diff --git a/passes/sat/assertpmux.cc b/passes/sat/assertpmux.cc new file mode 100644 index 00000000..63a90767 --- /dev/null +++ b/passes/sat/assertpmux.cc @@ -0,0 +1,240 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * + * 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 + +struct AssertpmuxWorker +{ + Module *module; + SigMap sigmap; + + bool flag_noinit; + bool flag_always; + + // get<0> ... mux cell + // get<1> ... mux port index + // get<2> ... mux bit index + dict<SigBit, pool<tuple<Cell*, int, int>>> sigbit_muxusers; + + dict<SigBit, SigBit> sigbit_actsignals; + dict<SigSpec, SigBit> sigspec_actsignals; + dict<tuple<Cell*, int>, SigBit> muxport_actsignal; + + AssertpmuxWorker(Module *module, bool flag_noinit, bool flag_always) : + module(module), sigmap(module), flag_noinit(flag_noinit), flag_always(flag_always) + { + for (auto wire : module->wires()) + { + if (wire->port_output) + for (auto bit : sigmap(wire)) + sigbit_actsignals[bit] = State::S1; + } + + for (auto cell : module->cells()) + { + if (cell->type.in("$mux", "$pmux")) + { + int width = cell->getParam("\\WIDTH").as_int(); + int numports = cell->type == "$mux" ? 2 : cell->getParam("\\S_WIDTH").as_int() + 1; + + SigSpec sig_a = sigmap(cell->getPort("\\A")); + SigSpec sig_b = sigmap(cell->getPort("\\B")); + SigSpec sig_s = sigmap(cell->getPort("\\S")); + + for (int i = 0; i < numports; i++) { + SigSpec bits = i == 0 ? sig_a : sig_b.extract(width*(i-1), width); + for (int k = 0; k < width; k++) { + tuple<Cell*, int, int> muxuser(cell, i, k); + sigbit_muxusers[bits[k]].insert(muxuser); + } + } + } + else + { + for (auto &conn : cell->connections()) { + if (!cell->known() || cell->input(conn.first)) + for (auto bit : sigmap(conn.second)) + sigbit_actsignals[bit] = State::S1; + } + } + } + } + + SigBit get_bit_activation(SigBit bit) + { + sigmap.apply(bit); + + if (sigbit_actsignals.count(bit) == 0) + { + SigSpec output; + + for (auto muxuser : sigbit_muxusers.at(bit)) + { + Cell *cell = std::get<0>(muxuser); + int portidx = std::get<1>(muxuser); + int bitidx = std::get<2>(muxuser); + + tuple<Cell*, int> muxport(cell, portidx); + + if (muxport_actsignal.count(muxport) == 0) { + if (portidx == 0) + muxport_actsignal[muxport] = module->LogicNot(NEW_ID, cell->getPort("\\S")); + else + muxport_actsignal[muxport] = cell->getPort("\\S")[portidx-1]; + } + + output.append(module->LogicAnd(NEW_ID, muxport_actsignal.at(muxport), get_bit_activation(cell->getPort("\\Y")[bitidx]))); + } + + output.sort_and_unify(); + + if (GetSize(output) == 0) + output = State::S0; + else if (GetSize(output) > 1) + output = module->ReduceOr(NEW_ID, output); + + sigbit_actsignals[bit] = output.as_bit(); + } + + return sigbit_actsignals.at(bit); + } + + SigBit get_activation(SigSpec sig) + { + sigmap.apply(sig); + sig.sort_and_unify(); + + if (sigspec_actsignals.count(sig) == 0) + { + SigSpec output; + + for (auto bit : sig) + output.append(get_bit_activation(bit)); + + output.sort_and_unify(); + + if (GetSize(output) == 0) + output = State::S0; + else if (GetSize(output) > 1) + output = module->ReduceOr(NEW_ID, output); + + sigspec_actsignals[sig] = output.as_bit(); + } + + return sigspec_actsignals.at(sig); + } + + void run(Cell *pmux) + { + log("Adding assert for $pmux cell %s.%s.\n", log_id(module), log_id(pmux)); + + int swidth = pmux->getParam("\\S_WIDTH").as_int(); + int cntbits = ceil_log2(swidth+1); + + SigSpec sel = pmux->getPort("\\S"); + SigSpec cnt(State::S0, cntbits); + + for (int i = 0; i < swidth; i++) + cnt = module->Add(NEW_ID, cnt, sel[i]); + + SigSpec assert_a = module->Le(NEW_ID, cnt, SigSpec(1, cntbits)); + SigSpec assert_en; + + if (flag_noinit) + assert_en.append(module->LogicNot(NEW_ID, module->Initstate(NEW_ID))); + + if (!flag_always) + assert_en.append(get_activation(pmux->getPort("\\Y"))); + + if (GetSize(assert_en) == 0) + assert_en = State::S1; + + if (GetSize(assert_en) == 2) + assert_en = module->LogicAnd(NEW_ID, assert_en[0], assert_en[1]); + + Cell *assert_cell = module->addAssert(NEW_ID, assert_a, assert_en); + + if (pmux->attributes.count("\\src") != 0) + assert_cell->attributes["\\src"] = pmux->attributes.at("\\src"); + } +}; + +struct AssertpmuxPass : public Pass { + AssertpmuxPass() : Pass("assertpmux", "convert internal signals to module ports") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" assertpmux [options] [selection]\n"); + log("\n"); + log("This command adds asserts to the design that assert that all parallel muxes\n"); + log("($pmux cells) have a maximum of one of their inputs enable at any time.\n"); + log("\n"); + log(" -noinit\n"); + log(" do not enforce the pmux condition during the init state\n"); + log("\n"); + log(" -always\n"); + log(" usually the $pmux condition is only checked when the $pmux output\n"); + log(" is used be the mux tree it drives. this option will deactivate this\n"); + log(" additional constrained and check the $pmux condition always.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + bool flag_noinit = false; + bool flag_always = false; + + log_header(design, "Executing ASSERTPMUX pass (add asserts for $pmux cells).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-noinit") { + flag_noinit = true; + continue; + } + if (args[argidx] == "-always") { + flag_always = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) + { + AssertpmuxWorker worker(module, flag_noinit, flag_always); + vector<Cell*> pmux_cells; + + for (auto cell : module->selected_cells()) + if (cell->type == "$pmux") + pmux_cells.push_back(cell); + + for (auto cell : pmux_cells) + worker.run(cell); + } + + } +} AssertpmuxPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/sat/eval.cc b/passes/sat/eval.cc index 614a1bd3..09f69cc5 100644 --- a/passes/sat/eval.cc +++ b/passes/sat/eval.cc @@ -389,7 +389,7 @@ struct EvalPass : public Pass { std::vector<std::string> shows, tables; bool set_undef = false; - log_header("Executing EVAL pass (evaluate the circuit given an input).\n"); + log_header(design, "Executing EVAL pass (evaluate the circuit given an input).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { diff --git a/passes/sat/expose.cc b/passes/sat/expose.cc index ebdf2ed5..9427547f 100644 --- a/passes/sat/expose.cc +++ b/passes/sat/expose.cc @@ -262,7 +262,7 @@ struct ExposePass : public Pass { bool flag_evert_dff = false; std::string sep = "."; - log_header("Executing EXPOSE pass (exposing internal signals as outputs).\n"); + log_header(design, "Executing EXPOSE pass (exposing internal signals as outputs).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) diff --git a/passes/sat/freduce.cc b/passes/sat/freduce.cc index 373b8048..77263f6a 100644 --- a/passes/sat/freduce.cc +++ b/passes/sat/freduce.cc @@ -798,7 +798,7 @@ struct FreducePass : public Pass { inv_mode = false; dump_prefix = std::string(); - log_header("Executing FREDUCE pass (perform functional reduction).\n"); + log_header(design, "Executing FREDUCE pass (perform functional reduction).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { diff --git a/passes/sat/miter.cc b/passes/sat/miter.cc index 682299ef..4854e19b 100644 --- a/passes/sat/miter.cc +++ b/passes/sat/miter.cc @@ -32,7 +32,7 @@ void create_miter_equiv(struct Pass *that, std::vector<std::string> args, RTLIL: bool flag_make_assert = false; bool flag_flatten = false; - log_header("Executing MITER pass (creating miter circuit).\n"); + log_header(design, "Executing MITER pass (creating miter circuit).\n"); size_t argidx; for (argidx = 2; argidx < args.size(); argidx++) @@ -254,7 +254,7 @@ void create_miter_equiv(struct Pass *that, std::vector<std::string> args, RTLIL: if (flag_flatten) { log_push(); - Pass::call_on_module(design, miter_module, "flatten; opt_const -keepdc -undriven;;"); + Pass::call_on_module(design, miter_module, "flatten; opt_expr -keepdc -undriven;;"); log_pop(); } } @@ -264,7 +264,7 @@ void create_miter_assert(struct Pass *that, std::vector<std::string> args, RTLIL bool flag_make_outputs = false; bool flag_flatten = false; - log_header("Executing MITER pass (creating miter circuit).\n"); + log_header(design, "Executing MITER pass (creating miter circuit).\n"); size_t argidx; for (argidx = 2; argidx < args.size(); argidx++) @@ -327,7 +327,7 @@ void create_miter_assert(struct Pass *that, std::vector<std::string> args, RTLIL if (flag_flatten) { log_push(); - Pass::call_on_module(design, module, "opt_const -keepdc -undriven;;"); + Pass::call_on_module(design, module, "opt_expr -keepdc -undriven;;"); log_pop(); } } @@ -361,7 +361,7 @@ struct MiterPass : public Pass { log(" also create an 'assert' cell that checks if trigger is always low.\n"); log("\n"); log(" -flatten\n"); - log(" call 'flatten; opt_const -keepdc -undriven;;' on the miter circuit.\n"); + log(" call 'flatten; opt_expr -keepdc -undriven;;' on the miter circuit.\n"); log("\n"); log("\n"); log(" miter -assert [options] module [miter_name]\n"); @@ -375,7 +375,7 @@ struct MiterPass : public Pass { log(" keep module output ports.\n"); log("\n"); log(" -flatten\n"); - log(" call 'flatten; opt_const -keepdc -undriven;;' on the miter circuit.\n"); + log(" call 'flatten; opt_expr -keepdc -undriven;;' on the miter circuit.\n"); log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) diff --git a/passes/sat/sat.cc b/passes/sat/sat.cc index 2e9c6d2f..a6ac7afd 100644 --- a/passes/sat/sat.cc +++ b/passes/sat/sat.cc @@ -90,106 +90,16 @@ struct SatHelper log_cmd_error("Bit %d of %s is undef but option -enable_undef is missing!\n", int(i), log_signal(sig)); } - void setup_init() - { - log ("\nSetting up initial state:\n"); - - RTLIL::SigSpec big_lhs, big_rhs; - - for (auto &it : module->wires_) - { - if (it.second->attributes.count("\\init") == 0) - continue; - - RTLIL::SigSpec lhs = sigmap(it.second); - RTLIL::SigSpec rhs = it.second->attributes.at("\\init"); - log_assert(lhs.size() == rhs.size()); - - RTLIL::SigSpec removed_bits; - for (int i = 0; i < lhs.size(); i++) { - RTLIL::SigSpec bit = lhs.extract(i, 1); - if (!satgen.initial_state.check_all(bit)) { - removed_bits.append(bit); - lhs.remove(i, 1); - rhs.remove(i, 1); - i--; - } - } - - if (removed_bits.size()) - log_warning("ignoring initial value on non-register: %s\n", log_signal(removed_bits)); - - if (lhs.size()) { - log("Import set-constraint from init attribute: %s = %s\n", log_signal(lhs), log_signal(rhs)); - big_lhs.remove2(lhs, &big_rhs); - big_lhs.append(lhs); - big_rhs.append(rhs); - } - } - - for (auto &s : sets_init) - { - RTLIL::SigSpec lhs, rhs; - - if (!RTLIL::SigSpec::parse_sel(lhs, design, module, s.first)) - log_cmd_error("Failed to parse lhs set expression `%s'.\n", s.first.c_str()); - if (!RTLIL::SigSpec::parse_rhs(lhs, rhs, module, s.second)) - log_cmd_error("Failed to parse rhs set expression `%s'.\n", s.second.c_str()); - show_signal_pool.add(sigmap(lhs)); - show_signal_pool.add(sigmap(rhs)); - - if (lhs.size() != rhs.size()) - log_cmd_error("Set expression with different lhs and rhs sizes: %s (%s, %d bits) vs. %s (%s, %d bits)\n", - s.first.c_str(), log_signal(lhs), lhs.size(), s.second.c_str(), log_signal(rhs), rhs.size()); - - log("Import set-constraint: %s = %s\n", log_signal(lhs), log_signal(rhs)); - big_lhs.remove2(lhs, &big_rhs); - big_lhs.append(lhs); - big_rhs.append(rhs); - } - - if (!satgen.initial_state.check_all(big_lhs)) { - RTLIL::SigSpec rem = satgen.initial_state.remove(big_lhs); - log_cmd_error("Found -set-init bits that are not part of the initial_state: %s\n", log_signal(rem)); - } - - if (set_init_def) { - RTLIL::SigSpec rem = satgen.initial_state.export_all(); - std::vector<int> undef_rem = satgen.importUndefSigSpec(rem, 1); - ez->assume(ez->NOT(ez->expression(ezSAT::OpOr, undef_rem))); - } - - if (set_init_undef) { - RTLIL::SigSpec rem = satgen.initial_state.export_all(); - rem.remove(big_lhs); - big_lhs.append(rem); - big_rhs.append(RTLIL::SigSpec(RTLIL::State::Sx, rem.size())); - } - - if (set_init_zero) { - RTLIL::SigSpec rem = satgen.initial_state.export_all(); - rem.remove(big_lhs); - big_lhs.append(rem); - big_rhs.append(RTLIL::SigSpec(RTLIL::State::S0, rem.size())); - } - - if (big_lhs.size() == 0) { - log("No constraints for initial state found.\n\n"); - return; - } - - log("Final constraint equation: %s = %s\n\n", log_signal(big_lhs), log_signal(big_rhs)); - check_undef_enabled(big_lhs), check_undef_enabled(big_rhs); - ez->assume(satgen.signals_eq(big_lhs, big_rhs, 1)); - } - - void setup(int timestep = -1) + void setup(int timestep = -1, bool initstate = false) { if (timestep > 0) log ("\nSetting up time step %d:\n", timestep); else log ("\nSetting up SAT problem:\n"); + if (initstate) + satgen.setInitState(timestep); + if (timestep > max_timestep) max_timestep = timestep; @@ -341,6 +251,97 @@ struct SatHelper log("Import constraint from assume cell: %s when %s.\n", log_signal(assumes_a[i]), log_signal(assumes_en[i])); ez->assume(satgen.importAssumes(timestep)); } + + if (initstate) + { + RTLIL::SigSpec big_lhs, big_rhs; + + for (auto &it : module->wires_) + { + if (it.second->attributes.count("\\init") == 0) + continue; + + RTLIL::SigSpec lhs = sigmap(it.second); + RTLIL::SigSpec rhs = it.second->attributes.at("\\init"); + log_assert(lhs.size() == rhs.size()); + + RTLIL::SigSpec removed_bits; + for (int i = 0; i < lhs.size(); i++) { + RTLIL::SigSpec bit = lhs.extract(i, 1); + if (!satgen.initial_state.check_all(bit)) { + removed_bits.append(bit); + lhs.remove(i, 1); + rhs.remove(i, 1); + i--; + } + } + + if (removed_bits.size()) + log_warning("ignoring initial value on non-register: %s\n", log_signal(removed_bits)); + + if (lhs.size()) { + log("Import set-constraint from init attribute: %s = %s\n", log_signal(lhs), log_signal(rhs)); + big_lhs.remove2(lhs, &big_rhs); + big_lhs.append(lhs); + big_rhs.append(rhs); + } + } + + for (auto &s : sets_init) + { + RTLIL::SigSpec lhs, rhs; + + if (!RTLIL::SigSpec::parse_sel(lhs, design, module, s.first)) + log_cmd_error("Failed to parse lhs set expression `%s'.\n", s.first.c_str()); + if (!RTLIL::SigSpec::parse_rhs(lhs, rhs, module, s.second)) + log_cmd_error("Failed to parse rhs set expression `%s'.\n", s.second.c_str()); + show_signal_pool.add(sigmap(lhs)); + show_signal_pool.add(sigmap(rhs)); + + if (lhs.size() != rhs.size()) + log_cmd_error("Set expression with different lhs and rhs sizes: %s (%s, %d bits) vs. %s (%s, %d bits)\n", + s.first.c_str(), log_signal(lhs), lhs.size(), s.second.c_str(), log_signal(rhs), rhs.size()); + + log("Import init set-constraint: %s = %s\n", log_signal(lhs), log_signal(rhs)); + big_lhs.remove2(lhs, &big_rhs); + big_lhs.append(lhs); + big_rhs.append(rhs); + } + + if (!satgen.initial_state.check_all(big_lhs)) { + RTLIL::SigSpec rem = satgen.initial_state.remove(big_lhs); + log_cmd_error("Found -set-init bits that are not part of the initial_state: %s\n", log_signal(rem)); + } + + if (set_init_def) { + RTLIL::SigSpec rem = satgen.initial_state.export_all(); + std::vector<int> undef_rem = satgen.importUndefSigSpec(rem, 1); + ez->assume(ez->NOT(ez->expression(ezSAT::OpOr, undef_rem))); + } + + if (set_init_undef) { + RTLIL::SigSpec rem = satgen.initial_state.export_all(); + rem.remove(big_lhs); + big_lhs.append(rem); + big_rhs.append(RTLIL::SigSpec(RTLIL::State::Sx, rem.size())); + } + + if (set_init_zero) { + RTLIL::SigSpec rem = satgen.initial_state.export_all(); + rem.remove(big_lhs); + big_lhs.append(rem); + big_rhs.append(RTLIL::SigSpec(RTLIL::State::S0, rem.size())); + } + + if (big_lhs.size() == 0) { + log("No constraints for initial state found.\n\n"); + return; + } + + log("Final init constraint equation: %s = %s\n", log_signal(big_lhs), log_signal(big_rhs)); + check_undef_enabled(big_lhs), check_undef_enabled(big_rhs); + ez->assume(satgen.signals_eq(big_lhs, big_rhs, timestep)); + } } int setup_proof(int timestep = -1) @@ -630,11 +631,11 @@ struct SatHelper "---------------------------------------------------------------------------------------------------"; if (last_timestep == -2) { log(max_timestep > 0 ? " Time " : " "); - log("%-*s %10s %10s %*s\n", maxModelName+10, "Signal Name", "Dec", "Hex", maxModelWidth+5, "Bin"); + log("%-*s %11s %9s %*s\n", maxModelName+5, "Signal Name", "Dec", "Hex", maxModelWidth+3, "Bin"); } log(max_timestep > 0 ? " ---- " : " "); - log("%*.*s %10.10s %10.10s %*.*s\n", maxModelName+10, maxModelName+10, - hline, hline, hline, maxModelWidth+5, maxModelWidth+5, hline); + log("%*.*s %11.11s %9.9s %*.*s\n", maxModelName+5, maxModelName+5, + hline, hline, hline, maxModelWidth+3, maxModelWidth+3, hline); last_timestep = info.timestep; } @@ -647,9 +648,9 @@ struct SatHelper log(" "); if (info.width <= 32 && !found_undef) - log("%-*s %10d %10x %*s\n", maxModelName+10, info.description.c_str(), value.as_int(), value.as_int(), maxModelWidth+5, value.as_string().c_str()); + log("%-*s %11d %9x %*s\n", maxModelName+5, info.description.c_str(), value.as_int(), value.as_int(), maxModelWidth+3, value.as_string().c_str()); else - log("%-*s %10s %10s %*s\n", maxModelName+10, info.description.c_str(), "--", "--", maxModelWidth+5, value.as_string().c_str()); + log("%-*s %11s %9s %*s\n", maxModelName+5, info.description.c_str(), "--", "--", maxModelWidth+3, value.as_string().c_str()); } if (last_timestep == -2) @@ -1073,7 +1074,7 @@ struct SatPass : public Pass { int tempinduct_skip = 0, stepsize = 1; std::string vcd_file_name, json_file_name, cnf_file_name; - log_header("Executing SAT pass (solving SAT problems in the circuit).\n"); + log_header(design, "Executing SAT pass (solving SAT problems in the circuit).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { @@ -1377,7 +1378,6 @@ struct SatPass : public Pass { SatHelper basecase(design, module, enable_undef); SatHelper inductstep(design, module, enable_undef); - bool basecase_setup_init = true; basecase.sets = sets; basecase.set_assumes = set_assumes; @@ -1403,7 +1403,7 @@ struct SatPass : public Pass { for (int timestep = 1; timestep <= seq_len; timestep++) if (!tempinduct_inductonly) - basecase.setup(timestep); + basecase.setup(timestep, timestep == 1); inductstep.sets = sets; inductstep.set_assumes = set_assumes; @@ -1436,15 +1436,10 @@ struct SatPass : public Pass { if (!tempinduct_inductonly) { - basecase.setup(seq_len + inductlen); + basecase.setup(seq_len + inductlen, seq_len + inductlen == 1); int property = basecase.setup_proof(seq_len + inductlen); basecase.generate_model(); - if (basecase_setup_init) { - basecase.setup_init(); - basecase_setup_init = false; - } - if (inductlen > 1) basecase.force_unique_state(seq_len + 1, seq_len + inductlen); @@ -1452,6 +1447,7 @@ struct SatPass : public Pass { { log("\n[base case %d] Solving problem with %d variables and %d clauses..\n", inductlen, basecase.ez->numCnfVariables(), basecase.ez->numCnfClauses()); + log_flush(); if (basecase.solve(basecase.ez->NOT(property))) { log("SAT temporal induction proof finished - model found for base case: FAIL!\n"); @@ -1522,6 +1518,7 @@ struct SatPass : public Pass { log("\n[induction step %d] Solving problem with %d variables and %d clauses..\n", inductlen, inductstep.ez->numCnfVariables(), inductstep.ez->numCnfClauses()); + log_flush(); if (!inductstep.solve(inductstep.ez->NOT(property))) { if (inductstep.gotTimeout) @@ -1599,14 +1596,13 @@ struct SatPass : public Pass { } else { std::vector<int> prove_bits; for (int timestep = 1; timestep <= seq_len; timestep++) { - sathelper.setup(timestep); + sathelper.setup(timestep, timestep == 1); if (sathelper.prove.size() || sathelper.prove_x.size() || sathelper.prove_asserts) if (timestep > prove_skip) prove_bits.push_back(sathelper.setup_proof(timestep)); } if (sathelper.prove.size() || sathelper.prove_x.size() || sathelper.prove_asserts) sathelper.ez->assume(sathelper.ez->NOT(sathelper.ez->expression(ezSAT::OpAnd, prove_bits))); - sathelper.setup_init(); } sathelper.generate_model(); @@ -1628,6 +1624,7 @@ struct SatPass : public Pass { rerun_solver: log("\nSolving problem with %d variables and %d clauses..\n", sathelper.ez->numCnfVariables(), sathelper.ez->numCnfClauses()); + log_flush(); if (sathelper.solve()) { diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc index 8112516c..b5024fa9 100644 --- a/passes/techmap/Makefile.inc +++ b/passes/techmap/Makefile.inc @@ -7,6 +7,9 @@ OBJS += passes/techmap/libparse.o ifeq ($(ENABLE_ABC),1) OBJS += passes/techmap/abc.o +ifneq ($(ABCEXTERNAL),) +passes/techmap/abc.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"' +endif endif ifneq ($(SMALL),1) @@ -23,6 +26,11 @@ OBJS += passes/techmap/tribuf.o OBJS += passes/techmap/lut2mux.o OBJS += passes/techmap/nlutmap.o OBJS += passes/techmap/dffsr2dff.o +OBJS += passes/techmap/shregmap.o +OBJS += passes/techmap/deminout.o +OBJS += passes/techmap/insbuf.o +OBJS += passes/techmap/attrmvcp.o +OBJS += passes/techmap/attrmap.o endif GENFILES += passes/techmap/techmap.inc diff --git a/passes/techmap/abc.cc b/passes/techmap/abc.cc index 7da26602..cc79296c 100644 --- a/passes/techmap/abc.cc +++ b/passes/techmap/abc.cc @@ -29,15 +29,17 @@ // Kahn, Arthur B. (1962), "Topological sorting of large networks", Communications of the ACM 5 (11): 558-562, doi:10.1145/368996.369025 // http://en.wikipedia.org/wiki/Topological_sorting -#define ABC_COMMAND_LIB "strash; scorr; ifraig; retime {D}; strash; dch -f; map {D}" -#define ABC_COMMAND_CTR "strash; scorr; ifraig; retime {D}; strash; dch -f; map {D}; buffer; upsize {D}; dnsize {D}; stime -p" -#define ABC_COMMAND_LUT "strash; scorr; ifraig; retime; strash; dch -f; if" -#define ABC_COMMAND_DFL "strash; scorr; ifraig; retime; strash; dch -f; map" - -#define ABC_FAST_COMMAND_LIB "retime {D}; map {D}" -#define ABC_FAST_COMMAND_CTR "retime {D}; map {D}; buffer; upsize {D}; dnsize {D}; stime -p" -#define ABC_FAST_COMMAND_LUT "retime; if" -#define ABC_FAST_COMMAND_DFL "retime; map" +#define ABC_COMMAND_LIB "strash; dc2; scorr; ifraig; retime -o {D}; strash; dch -f; map {D}" +#define ABC_COMMAND_CTR "strash; dc2; scorr; ifraig; retime -o {D}; strash; dch -f; map {D}; buffer; upsize {D}; dnsize {D}; stime -p" +#define ABC_COMMAND_LUT "strash; dc2; scorr; ifraig; retime -o; strash; dch -f; if; mfs" +#define ABC_COMMAND_SOP "strash; dc2; scorr; ifraig; retime -o; strash; dch -f; cover {I} {P}" +#define ABC_COMMAND_DFL "strash; dc2; scorr; ifraig; retime -o; strash; dch -f; map" + +#define ABC_FAST_COMMAND_LIB "retime -o {D}; map {D}" +#define ABC_FAST_COMMAND_CTR "retime -o {D}; map {D}; buffer; upsize {D}; dnsize {D}; stime -p" +#define ABC_FAST_COMMAND_LUT "retime -o; if" +#define ABC_FAST_COMMAND_SOP "retime -o; cover -I {I} -P {P}" +#define ABC_FAST_COMMAND_DFL "retime -o; map" #include "kernel/register.h" #include "kernel/sigtools.h" @@ -593,7 +595,8 @@ struct abc_output_filter void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::string script_file, std::string exe_file, std::string liberty_file, std::string constr_file, bool cleanup, vector<int> lut_costs, bool dff_mode, std::string clk_str, - bool keepff, std::string delay_target, bool fast_mode, const std::vector<RTLIL::Cell*> &cells, bool show_tempdir) + bool keepff, std::string delay_target, std::string sop_inputs, std::string sop_products, bool fast_mode, + const std::vector<RTLIL::Cell*> &cells, bool show_tempdir, bool sop_mode) { module = current_module; map_autoidx = autoidx++; @@ -616,7 +619,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin if (!cleanup) tempdir_name[0] = tempdir_name[4] = '_'; tempdir_name = make_temp_dir(tempdir_name); - log_header("Extracting gate netlist of module `%s' to `%s/input.blif'..\n", + log_header(design, "Extracting gate netlist of module `%s' to `%s/input.blif'..\n", module->name.c_str(), replace_tempdir(tempdir_name, tempdir_name, show_tempdir).c_str()); std::string abc_script = stringf("read_blif %s/input.blif; ", tempdir_name.c_str()); @@ -642,16 +645,30 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin abc_script += script_file[i]; } else abc_script += stringf("source %s", script_file.c_str()); - } else if (!lut_costs.empty()) + } else if (!lut_costs.empty()) { + bool all_luts_cost_same = true; + for (int this_cost : lut_costs) + if (this_cost != lut_costs.front()) + all_luts_cost_same = false; abc_script += fast_mode ? ABC_FAST_COMMAND_LUT : ABC_COMMAND_LUT; - else if (!liberty_file.empty()) + if (all_luts_cost_same && !fast_mode) + abc_script += "; lutpack"; + } else if (!liberty_file.empty()) abc_script += constr_file.empty() ? (fast_mode ? ABC_FAST_COMMAND_LIB : ABC_COMMAND_LIB) : (fast_mode ? ABC_FAST_COMMAND_CTR : ABC_COMMAND_CTR); + else if (sop_mode) + abc_script += fast_mode ? ABC_FAST_COMMAND_SOP : ABC_COMMAND_SOP; else abc_script += fast_mode ? ABC_FAST_COMMAND_DFL : ABC_COMMAND_DFL; for (size_t pos = abc_script.find("{D}"); pos != std::string::npos; pos = abc_script.find("{D}", pos)) abc_script = abc_script.substr(0, pos) + delay_target + abc_script.substr(pos+3); + for (size_t pos = abc_script.find("{I}"); pos != std::string::npos; pos = abc_script.find("{D}", pos)) + abc_script = abc_script.substr(0, pos) + sop_inputs + abc_script.substr(pos+3); + + for (size_t pos = abc_script.find("{P}"); pos != std::string::npos; pos = abc_script.find("{D}", pos)) + abc_script = abc_script.substr(0, pos) + sop_products + abc_script.substr(pos+3); + abc_script += stringf("; write_blif %s/output.blif", tempdir_name.c_str()); abc_script = add_echos_to_abc_cmd(abc_script); @@ -828,7 +845,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin if (count_output > 0) { - log_header("Executing ABC.\n"); + log_header(design, "Executing ABC.\n"); buffer = stringf("%s/stdcells.genlib", tempdir_name.c_str()); f = fopen(buffer.c_str(), "wt"); @@ -892,13 +909,13 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin if (ifs.fail()) log_error("Can't open ABC output file `%s'.\n", buffer.c_str()); - bool builtin_lib = liberty_file.empty() && script_file.empty() && lut_costs.empty(); + bool builtin_lib = liberty_file.empty(); RTLIL::Design *mapped_design = new RTLIL::Design; - parse_blif(mapped_design, ifs, builtin_lib ? "\\DFF" : "\\_dff_"); + parse_blif(mapped_design, ifs, builtin_lib ? "\\DFF" : "\\_dff_", false, sop_mode); ifs.close(); - log_header("Re-integrating ABC results.\n"); + log_header(design, "Re-integrating ABC results.\n"); RTLIL::Module *mapped_mod = mapped_design->modules_["\\netlist"]; if (mapped_mod == NULL) log_error("ABC output file does not contain a module `netlist'.\n"); @@ -910,10 +927,10 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin } std::map<std::string, int> cell_stats; - if (builtin_lib) + for (auto c : mapped_mod->cells()) { - for (auto &it : mapped_mod->cells_) { - RTLIL::Cell *c = it.second; + if (builtin_lib) + { cell_stats[RTLIL::unescape_id(c->type)]++; if (c->type == "\\ZERO" || c->type == "\\ONE") { RTLIL::SigSig conn; @@ -1052,60 +1069,57 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin design->select(module, cell); continue; } - log_abort(); } - } - else - { - for (auto &it : mapped_mod->cells_) - { - RTLIL::Cell *c = it.second; - cell_stats[RTLIL::unescape_id(c->type)]++; - if (c->type == "\\_const0_" || c->type == "\\_const1_") { - RTLIL::SigSig conn; - conn.first = RTLIL::SigSpec(module->wires_[remap_name(c->connections().begin()->second.as_wire()->name)]); - conn.second = RTLIL::SigSpec(c->type == "\\_const0_" ? 0 : 1, 1); - module->connect(conn); - continue; - } - if (c->type == "\\_dff_") { - log_assert(clk_sig.size() == 1); - RTLIL::Cell *cell; - if (en_sig.size() == 0) { - cell = module->addCell(remap_name(c->name), clk_polarity ? "$_DFF_P_" : "$_DFF_N_"); - } else { - log_assert(en_sig.size() == 1); - cell = module->addCell(remap_name(c->name), stringf("$_DFFE_%c%c_", clk_polarity ? 'P' : 'N', en_polarity ? 'P' : 'N')); - cell->setPort("\\E", en_sig); - } - if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx; - cell->setPort("\\D", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\D").as_wire()->name)])); - cell->setPort("\\Q", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Q").as_wire()->name)])); - cell->setPort("\\C", clk_sig); - design->select(module, cell); - continue; - } - if (c->type == "$lut" && GetSize(c->getPort("\\A")) == 1 && c->getParam("\\LUT").as_int() == 2) { - SigSpec my_a = module->wires_[remap_name(c->getPort("\\A").as_wire()->name)]; - SigSpec my_y = module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)]; - module->connect(my_y, my_a); - continue; + + cell_stats[RTLIL::unescape_id(c->type)]++; + + if (c->type == "\\_const0_" || c->type == "\\_const1_") { + RTLIL::SigSig conn; + conn.first = RTLIL::SigSpec(module->wires_[remap_name(c->connections().begin()->second.as_wire()->name)]); + conn.second = RTLIL::SigSpec(c->type == "\\_const0_" ? 0 : 1, 1); + module->connect(conn); + continue; + } + + if (c->type == "\\_dff_") { + log_assert(clk_sig.size() == 1); + RTLIL::Cell *cell; + if (en_sig.size() == 0) { + cell = module->addCell(remap_name(c->name), clk_polarity ? "$_DFF_P_" : "$_DFF_N_"); + } else { + log_assert(en_sig.size() == 1); + cell = module->addCell(remap_name(c->name), stringf("$_DFFE_%c%c_", clk_polarity ? 'P' : 'N', en_polarity ? 'P' : 'N')); + cell->setPort("\\E", en_sig); } - RTLIL::Cell *cell = module->addCell(remap_name(c->name), c->type); if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx; - cell->parameters = c->parameters; - for (auto &conn : c->connections()) { - RTLIL::SigSpec newsig; - for (auto &c : conn.second.chunks()) { - if (c.width == 0) - continue; - log_assert(c.width == 1); - newsig.append(module->wires_[remap_name(c.wire->name)]); - } - cell->setPort(conn.first, newsig); - } + cell->setPort("\\D", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\D").as_wire()->name)])); + cell->setPort("\\Q", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Q").as_wire()->name)])); + cell->setPort("\\C", clk_sig); design->select(module, cell); + continue; + } + + if (c->type == "$lut" && GetSize(c->getPort("\\A")) == 1 && c->getParam("\\LUT").as_int() == 2) { + SigSpec my_a = module->wires_[remap_name(c->getPort("\\A").as_wire()->name)]; + SigSpec my_y = module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)]; + module->connect(my_y, my_a); + continue; + } + + RTLIL::Cell *cell = module->addCell(remap_name(c->name), c->type); + if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx; + cell->parameters = c->parameters; + for (auto &conn : c->connections()) { + RTLIL::SigSpec newsig; + for (auto &c : conn.second.chunks()) { + if (c.width == 0) + continue; + log_assert(c.width == 1); + newsig.append(module->wires_[remap_name(c.wire->name)]); + } + cell->setPort(conn.first, newsig); } + design->select(module, cell); } for (auto conn : mapped_mod->connections()) { @@ -1167,7 +1181,11 @@ struct AbcPass : public Pass { log("library to a target architecture.\n"); log("\n"); log(" -exe <command>\n"); - log(" use the specified command name instead of \"yosys-abc\" to execute ABC.\n"); +#ifdef ABCEXTERNAL + log(" use the specified command instead of \"" ABCEXTERNAL "\" to execute ABC.\n"); +#else + log(" use the specified command instead of \"<yosys-bindir>/yosys-abc\" to execute ABC.\n"); +#endif log(" This can e.g. be used to call a specific version of ABC or a wrapper.\n"); log("\n"); log(" -script <file>\n"); @@ -1186,9 +1204,15 @@ struct AbcPass : public Pass { log(" for -liberty with -constr:\n"); log("%s\n", fold_abc_cmd(ABC_COMMAND_CTR).c_str()); log("\n"); - log(" for -lut:\n"); + log(" for -lut/-luts (only one LUT size):\n"); + log("%s\n", fold_abc_cmd(ABC_COMMAND_LUT "; lutpack").c_str()); + log("\n"); + log(" for -lut/-luts (different LUT sizes):\n"); log("%s\n", fold_abc_cmd(ABC_COMMAND_LUT).c_str()); log("\n"); + log(" for -sop:\n"); + log("%s\n", fold_abc_cmd(ABC_COMMAND_SOP).c_str()); + log("\n"); log(" otherwise:\n"); log("%s\n", fold_abc_cmd(ABC_COMMAND_DFL).c_str()); log("\n"); @@ -1202,9 +1226,12 @@ struct AbcPass : public Pass { log(" for -liberty with -constr:\n"); log("%s\n", fold_abc_cmd(ABC_FAST_COMMAND_CTR).c_str()); log("\n"); - log(" for -lut:\n"); + log(" for -lut/-luts:\n"); log("%s\n", fold_abc_cmd(ABC_FAST_COMMAND_LUT).c_str()); log("\n"); + log(" for -sop:\n"); + log("%s\n", fold_abc_cmd(ABC_FAST_COMMAND_SOP).c_str()); + log("\n"); log(" otherwise:\n"); log("%s\n", fold_abc_cmd(ABC_FAST_COMMAND_DFL).c_str()); log("\n"); @@ -1227,6 +1254,14 @@ struct AbcPass : public Pass { log(" set delay target. the string {D} in the default scripts above is\n"); log(" replaced by this option when used, and an empty string otherwise.\n"); log("\n"); + log(" -I <num>\n"); + log(" maximum number of SOP inputs.\n"); + log(" (replaces {I} in the default scripts above)\n"); + log("\n"); + log(" -P <num>\n"); + log(" maximum number of SOP products.\n"); + log(" (replaces {P} in the default scripts above)\n"); + log("\n"); log(" -lut <width>\n"); log(" generate netlist using luts of (max) the specified width.\n"); log("\n"); @@ -1240,6 +1275,9 @@ struct AbcPass : public Pass { log(" generate netlist using luts. Use the specified costs for luts with 1,\n"); log(" 2, 3, .. inputs.\n"); log("\n"); + log(" -sop\n"); + log(" map to sum-of-product cells and inverters\n"); + log("\n"); // log(" -mux4, -mux8, -mux16\n"); // log(" try to extract 4-input, 8-input, and/or 16-input muxes\n"); // log(" (ignored when used with -liberty or -lut)\n"); @@ -1286,13 +1324,18 @@ struct AbcPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing ABC pass (technology mapping using ABC).\n"); + log_header(design, "Executing ABC pass (technology mapping using ABC).\n"); log_push(); +#ifdef ABCEXTERNAL + std::string exe_file = ABCEXTERNAL; +#else std::string exe_file = proc_self_dirname() + "yosys-abc"; - std::string script_file, liberty_file, constr_file, clk_str, delay_target; +#endif + std::string script_file, liberty_file, constr_file, clk_str; + std::string delay_target, sop_inputs, sop_products; bool fast_mode = false, dff_mode = false, keepff = false, cleanup = true; - bool show_tempdir = false; + bool show_tempdir = false, sop_mode = false; vector<int> lut_costs; markgroups = false; @@ -1302,9 +1345,11 @@ struct AbcPass : public Pass { enabled_gates.clear(); #ifdef _WIN32 +#ifndef ABCEXTERNAL if (!check_file_exists(exe_file + ".exe") && check_file_exists(proc_self_dirname() + "..\\yosys-abc.exe")) exe_file = proc_self_dirname() + "..\\yosys-abc"; #endif +#endif size_t argidx; char pwd [PATH_MAX]; @@ -1340,6 +1385,14 @@ struct AbcPass : public Pass { delay_target = "-D " + args[++argidx]; continue; } + if (arg == "-I" && argidx+1 < args.size()) { + sop_inputs = "-I " + args[++argidx]; + continue; + } + if (arg == "-P" && argidx+1 < args.size()) { + sop_products = "-P " + args[++argidx]; + continue; + } if (arg == "-lut" && argidx+1 < args.size()) { string arg = args[++argidx]; size_t pos = arg.find_first_of(':'); @@ -1374,6 +1427,10 @@ struct AbcPass : public Pass { } continue; } + if (arg == "-sop") { + sop_mode = true; + continue; + } if (arg == "-mux4") { map_mux4 = true; continue; @@ -1447,7 +1504,8 @@ struct AbcPass : public Pass { if (mod->processes.size() > 0) log("Skipping module %s as it contains processes.\n", log_id(mod)); else if (!dff_mode || !clk_str.empty()) - abc_module(design, mod, script_file, exe_file, liberty_file, constr_file, cleanup, lut_costs, dff_mode, clk_str, keepff, delay_target, fast_mode, mod->selected_cells(), show_tempdir); + abc_module(design, mod, script_file, exe_file, liberty_file, constr_file, cleanup, lut_costs, dff_mode, clk_str, keepff, + delay_target, sop_inputs, sop_products, fast_mode, mod->selected_cells(), show_tempdir, sop_mode); else { assign_map.set(mod); @@ -1580,7 +1638,7 @@ struct AbcPass : public Pass { assigned_cells_reverse[cell] = key; } - log_header("Summary of detected clock domains:\n"); + log_header(design, "Summary of detected clock domains:\n"); for (auto &it : assigned_cells) log(" %d cells in clk=%s%s, en=%s%s\n", GetSize(it.second), std::get<0>(it.first) ? "" : "!", log_signal(std::get<1>(it.first)), @@ -1591,8 +1649,8 @@ struct AbcPass : public Pass { clk_sig = assign_map(std::get<1>(it.first)); en_polarity = std::get<2>(it.first); en_sig = assign_map(std::get<3>(it.first)); - abc_module(design, mod, script_file, exe_file, liberty_file, constr_file, cleanup, lut_costs, - !clk_sig.empty(), "$", keepff, delay_target, fast_mode, it.second, show_tempdir); + abc_module(design, mod, script_file, exe_file, liberty_file, constr_file, cleanup, lut_costs, !clk_sig.empty(), "$", + keepff, delay_target, sop_inputs, sop_products, fast_mode, it.second, show_tempdir, sop_mode); assign_map.set(mod); } } diff --git a/passes/techmap/aigmap.cc b/passes/techmap/aigmap.cc index db1c731e..b9ac7ade 100644 --- a/passes/techmap/aigmap.cc +++ b/passes/techmap/aigmap.cc @@ -41,7 +41,7 @@ struct AigmapPass : public Pass { { bool nand_mode = false; - log_header("Executing AIGMAP pass (map logic to AIG).\n"); + log_header(design, "Executing AIGMAP pass (map logic to AIG).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) diff --git a/passes/techmap/alumacc.cc b/passes/techmap/alumacc.cc index 3c7ff4b9..9f6dd02d 100644 --- a/passes/techmap/alumacc.cc +++ b/passes/techmap/alumacc.cc @@ -544,7 +544,7 @@ struct AlumaccPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing ALUMACC pass (create $alu and $macc cells).\n"); + log_header(design, "Executing ALUMACC pass (create $alu and $macc cells).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { diff --git a/passes/techmap/attrmap.cc b/passes/techmap/attrmap.cc new file mode 100644 index 00000000..f715b63e --- /dev/null +++ b/passes/techmap/attrmap.cc @@ -0,0 +1,250 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * + * 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 + +Const make_value(string &value) +{ + if (GetSize(value) >= 2 && value.front() == '"' && value.back() == '"') + return Const(value.substr(1, GetSize(value)-2)); + + SigSpec sig; + SigSpec::parse(sig, nullptr, value); + return sig.as_const(); +} + +bool match_name(string &name, IdString &id, bool ignore_case=false) +{ + string str1 = RTLIL::escape_id(name); + string str2 = id.str(); + + if (ignore_case) + return !strcasecmp(str1.c_str(), str2.c_str()); + + return str1 == str2; +} + +bool match_value(string &value, Const &val, bool ignore_case=false) +{ + if (ignore_case && ((val.flags & RTLIL::CONST_FLAG_STRING) != 0) && GetSize(value) && value.front() == '"' && value.back() == '"') { + string str1 = value.substr(1, GetSize(value)-2); + string str2 = val.decode_string(); + return !strcasecmp(str1.c_str(), str2.c_str()); + } + + return make_value(value) == val; +} + +struct AttrmapAction { + virtual ~AttrmapAction() { } + virtual bool apply(IdString &id, Const &val) = 0; +}; + +struct AttrmapTocase : AttrmapAction { + string name; + virtual bool apply(IdString &id, Const&) { + if (match_name(name, id, true)) + id = RTLIL::escape_id(name); + return true; + } +}; + +struct AttrmapRename : AttrmapAction { + string old_name, new_name; + virtual bool apply(IdString &id, Const&) { + if (match_name(old_name, id)) + id = RTLIL::escape_id(new_name); + return true; + } +}; + +struct AttrmapMap : AttrmapAction { + bool imap; + string old_name, new_name; + string old_value, new_value; + virtual bool apply(IdString &id, Const &val) { + if (match_name(old_name, id) && match_value(old_value, val, true)) { + id = RTLIL::escape_id(new_name); + val = make_value(new_value); + } + return true; + } +}; + +struct AttrmapRemove : AttrmapAction { + string name, value; + virtual bool apply(IdString &id, Const &val) { + return !(match_name(name, id) && match_value(value, val)); + } +}; + +void attrmap_apply(string objname, vector<std::unique_ptr<AttrmapAction>> &actions, dict<RTLIL::IdString, RTLIL::Const> &attributes) +{ + dict<RTLIL::IdString, RTLIL::Const> new_attributes; + + for (auto attr : attributes) + { + auto new_attr = attr; + for (auto &action : actions) + if (!action->apply(new_attr.first, new_attr.second)) + goto delete_this_attr; + + if (new_attr != attr) + log("Changed attribute on %s: %s=%s -> %s=%s\n", objname.c_str(), + log_id(attr.first), log_const(attr.second), log_id(new_attr.first), log_const(new_attr.second)); + + new_attributes[new_attr.first] = new_attr.second; + + if (0) + delete_this_attr: + log("Removed attribute on %s: %s=%s\n", objname.c_str(), log_id(attr.first), log_const(attr.second)); + } + + attributes.swap(new_attributes); +} + +struct AttrmapPass : public Pass { + AttrmapPass() : Pass("attrmap", "renaming attributes") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" attrmap [options] [selection]\n"); + log("\n"); + log("This command renames attributes and/or mapps key/value pairs to\n"); + log("other key/value pairs.\n"); + log("\n"); + log(" -tocase <name>\n"); + log(" Match attribute names case-insensitively and set it to the specified\n"); + log(" name.\n"); + log("\n"); + log(" -rename <old_name> <new_name>\n"); + log(" Rename attributes as specified\n"); + log("\n"); + log(" -map <old_name>=<old_value> <new_name>=<new_value>\n"); + log(" Map key/value pairs as indicated.\n"); + log("\n"); + log(" -imap <old_name>=<old_value> <new_name>=<new_value>\n"); + log(" Like -map, but use case-insensitive match for <old_value> when\n"); + log(" it is a string value.\n"); + log("\n"); + log(" -remove <name>=<value>\n"); + log(" Remove attributes matching this pattern.\n"); + log("\n"); + log(" -modattr\n"); + log(" Operate on module attributes instead of attributes on wires and cells.\n"); + log("\n"); + log("For example, mapping Xilinx-style \"keep\" attributes to Yosys-style:\n"); + log("\n"); + log(" attrmap -tocase keep -imap keep=\"true\" keep=1 \\\n"); + log(" -imap keep=\"false\" keep=0 -remove keep=0\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + log_header(design, "Executing ATTRMAP pass (move or copy attributes).\n"); + + bool modattr_mode = false; + vector<std::unique_ptr<AttrmapAction>> actions; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + std::string arg = args[argidx]; + if (arg == "-tocase" && argidx+1 < args.size()) { + auto action = new AttrmapTocase; + action->name = args[++argidx]; + actions.push_back(std::unique_ptr<AttrmapAction>(action)); + continue; + } + if (arg == "-rename" && argidx+2 < args.size()) { + auto action = new AttrmapRename; + action->old_name = args[++argidx]; + action->new_name = args[++argidx]; + actions.push_back(std::unique_ptr<AttrmapAction>(action)); + continue; + } + if ((arg == "-map" || arg == "-imap") && argidx+2 < args.size()) { + string arg1 = args[++argidx]; + string arg2 = args[++argidx]; + string val1, val2; + size_t p = arg1.find("="); + if (p != string::npos) { + val1 = arg1.substr(p+1); + arg1 = arg1.substr(0, p); + } + p = arg2.find("="); + if (p != string::npos) { + val2 = arg2.substr(p+1); + arg2 = arg2.substr(0, p); + } + auto action = new AttrmapMap; + action->imap = (arg == "-map"); + action->old_name = arg1; + action->new_name = arg2; + action->old_value = val1; + action->new_value = val2; + actions.push_back(std::unique_ptr<AttrmapAction>(action)); + continue; + } + if (arg == "-remove" && argidx+1 < args.size()) { + string arg1 = args[++argidx], val1; + size_t p = arg1.find("="); + if (p != string::npos) { + val1 = arg1.substr(p+1); + arg1 = arg1.substr(0, p); + } + auto action = new AttrmapRemove; + action->name = arg1; + action->value = val1; + actions.push_back(std::unique_ptr<AttrmapAction>(action)); + continue; + } + if (arg == "-modattr") { + modattr_mode = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + if (modattr_mode) + { + for (auto module : design->selected_whole_modules()) + attrmap_apply(stringf("%s", log_id(module)), actions, module->attributes); + } + else + { + for (auto module : design->selected_modules()) + { + for (auto wire : module->selected_wires()) + attrmap_apply(stringf("%s.%s", log_id(module), log_id(wire)), actions, wire->attributes); + + for (auto cell : module->selected_cells()) + attrmap_apply(stringf("%s.%s", log_id(module), log_id(cell)), actions, cell->attributes); + } + } + } +} AttrmapPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/attrmvcp.cc b/passes/techmap/attrmvcp.cc new file mode 100644 index 00000000..50eaf61d --- /dev/null +++ b/passes/techmap/attrmvcp.cc @@ -0,0 +1,139 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * + * 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 + +struct AttrmvcpPass : public Pass { + AttrmvcpPass() : Pass("attrmvcp", "move or copy attributes from wires to driving cells") { } + virtual void help() + { + log("\n"); + log(" attrmvcp [options] [selection]\n"); + log("\n"); + log("Move or copy attributes on wires to the cells driving them.\n"); + log("\n"); + log(" -copy\n"); + log(" By default, attributes are moved. This will only add\n"); + log(" the attribute to the cell, without removing it from\n"); + log(" the wire.\n"); + log("\n"); + log(" -purge\n"); + log(" If no selected cell consumes the attribute, then it is\n"); + log(" left on the wire by default. This option will cause the\n"); + log(" attribute to be removed from the wire, even if no selected\n"); + log(" cell takes it.\n"); + log("\n"); + log(" -driven\n"); + log(" By default, attriburtes are moved to the cell driving the\n"); + log(" wire. With this option set it will be moved to the cell\n"); + log(" driven by the wire instead.\n"); + log("\n"); + log(" -attr <attrname>\n"); + log(" Move or copy this attribute. This option can be used\n"); + log(" multiple times.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + log_header(design, "Executing ATTRMVCP pass (move or copy attributes).\n"); + + bool copy_mode = false; + bool driven_mode = false; + bool purge_mode = false; + pool<IdString> attrnames; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + std::string arg = args[argidx]; + if (arg == "-copy") { + copy_mode = true; + continue; + } + if (arg == "-driven") { + driven_mode = true; + continue; + } + if (arg == "-purge") { + purge_mode = true; + continue; + } + if (arg == "-attr" && argidx+1 < args.size()) { + attrnames.insert(RTLIL::escape_id(args[++argidx])); + continue; + } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) + { + dict<SigBit, pool<Cell*>> net2cells; + SigMap sigmap(module); + + for (auto cell : module->selected_cells()) + for (auto &conn : cell->connections()) + if (driven_mode) { + if (cell->input(conn.first)) + for (auto bit : sigmap(conn.second)) + net2cells[bit].insert(cell); + } else { + if (cell->output(conn.first)) + for (auto bit : sigmap(conn.second)) + net2cells[bit].insert(cell); + } + + for (auto wire : module->selected_wires()) + { + dict<IdString, Const> new_attributes; + + for (auto attr : wire->attributes) + { + bool did_something = false; + + if (!attrnames.count(attr.first)) { + new_attributes[attr.first] = attr.second; + continue; + } + + for (auto bit : sigmap(wire)) + if (net2cells.count(bit)) + for (auto cell : net2cells.at(bit)) { + log("Moving attribute %s=%s from %s.%s to %s.%s.\n", log_id(attr.first), log_const(attr.second), + log_id(module), log_id(wire), log_id(module), log_id(cell)); + cell->attributes[attr.first] = attr.second; + did_something = true; + } + + if (!purge_mode && !did_something) + new_attributes[attr.first] = attr.second; + } + + if (!copy_mode) + wire->attributes.swap(new_attributes); + } + } + } +} AttrmvcpPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/deminout.cc b/passes/techmap/deminout.cc new file mode 100644 index 00000000..ed4e4576 --- /dev/null +++ b/passes/techmap/deminout.cc @@ -0,0 +1,116 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * + * 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 + +struct DeminoutPass : public Pass { + DeminoutPass() : Pass("deminout", "demote inout ports to input or output") { } + virtual void help() + { + log("\n"); + log(" deminout [options] [selection]\n"); + log("\n"); + log("\"Demote\" inout ports to input or output ports, if possible.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + log_header(design, "Executing DEMINOUT pass (demote inout ports to input or output).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + // if (args[argidx] == "-bits") { + // flag_bits = true; + // continue; + // } + break; + } + extra_args(args, argidx, design); + + bool keep_running = true; + + while (keep_running) + { + keep_running = false; + + for (auto module : design->selected_modules()) + { + SigMap sigmap(module); + pool<SigBit> bits_written, bits_used, bits_inout; + dict<SigBit, int> bits_numports; + + for (auto wire : module->wires()) + if (wire->port_id) + for (auto bit : sigmap(wire)) + bits_numports[bit]++; + + for (auto cell : module->cells()) + for (auto &conn : cell->connections()) + { + bool cellport_out = cell->output(conn.first) || !cell->known(); + bool cellport_in = cell->input(conn.first) || !cell->known(); + + if (cellport_out && cellport_in) + for (auto bit : sigmap(conn.second)) + bits_inout.insert(bit); + + if (cellport_out) + for (auto bit : sigmap(conn.second)) + bits_written.insert(bit); + + if (cellport_in) + for (auto bit : sigmap(conn.second)) + bits_used.insert(bit); + } + + for (auto wire : module->selected_wires()) + if (wire->port_input && wire->port_output) + { + bool new_input = false; + bool new_output = false; + + for (auto bit : sigmap(wire)) + { + if (bits_numports[bit] > 1 || bits_inout.count(bit)) + new_input = true, new_output = true; + + if (bits_written.count(bit)) + new_output = true; + else if (bits_used.count(bit)) + new_input = true; + } + + if (new_input != new_output) { + log("Demoting inout port %s.%s to %s.\n", log_id(module), log_id(wire), new_input ? "input" : "output"); + wire->port_input = new_input; + wire->port_output = new_output; + keep_running = true; + } + } + } + } + } +} DeminoutPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/dff2dffe.cc b/passes/techmap/dff2dffe.cc index 51bfaade..1b8920bb 100644 --- a/passes/techmap/dff2dffe.cc +++ b/passes/techmap/dff2dffe.cc @@ -285,7 +285,7 @@ struct Dff2dffePass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing DFF2DFFE pass (transform $dff to $dffe where applicable).\n"); + log_header(design, "Executing DFF2DFFE pass (transform $dff to $dffe where applicable).\n"); bool unmap_mode = false; dict<IdString, IdString> direct_dict; diff --git a/passes/techmap/dffinit.cc b/passes/techmap/dffinit.cc index e0273f43..d737b342 100644 --- a/passes/techmap/dffinit.cc +++ b/passes/techmap/dffinit.cc @@ -41,7 +41,7 @@ struct DffinitPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing DFFINIT pass (set INIT param on FF cells).\n"); + log_header(design, "Executing DFFINIT pass (set INIT param on FF cells).\n"); dict<IdString, dict<IdString, IdString>> ff_types; diff --git a/passes/techmap/dfflibmap.cc b/passes/techmap/dfflibmap.cc index 4d7a1a70..c8104fb7 100644 --- a/passes/techmap/dfflibmap.cc +++ b/passes/techmap/dfflibmap.cc @@ -108,6 +108,7 @@ static void find_cell(LibertyAst *ast, std::string cell_type, bool clkpol, bool LibertyAst *best_cell = NULL; std::map<std::string, char> best_cell_ports; int best_cell_pins = 0; + bool best_cell_noninv = false; double best_cell_area = 0; if (ast->id != "library") @@ -155,6 +156,7 @@ static void find_cell(LibertyAst *ast, std::string cell_type, bool clkpol, bool int num_pins = 0; bool found_output = false; + bool found_noninv_output = false; for (auto pin : cell->children) { if (pin->id != "pin" || pin->args.size() != 1) @@ -175,10 +177,14 @@ static void find_cell(LibertyAst *ast, std::string cell_type, bool clkpol, bool value.erase(pos, 1); if (value == ff->args[0]) { this_cell_ports[pin->args[0]] = cell_next_pol ? 'Q' : 'q'; + if (cell_next_pol) + found_noninv_output = true; found_output = true; } else if (value == ff->args[1]) { this_cell_ports[pin->args[0]] = cell_next_pol ? 'q' : 'Q'; + if (!cell_next_pol) + found_noninv_output = true; found_output = true; } } @@ -187,7 +193,7 @@ static void find_cell(LibertyAst *ast, std::string cell_type, bool clkpol, bool this_cell_ports[pin->args[0]] = 0; } - if (!found_output || (best_cell != NULL && num_pins > best_cell_pins)) + if (!found_output || (best_cell != NULL && (num_pins > best_cell_pins || (best_cell_noninv && !found_noninv_output)))) continue; if (best_cell != NULL && num_pins == best_cell_pins && area > best_cell_area) @@ -196,12 +202,14 @@ static void find_cell(LibertyAst *ast, std::string cell_type, bool clkpol, bool best_cell = cell; best_cell_pins = num_pins; best_cell_area = area; + best_cell_noninv = found_noninv_output; best_cell_ports.swap(this_cell_ports); continue_cell_loop:; } if (best_cell != NULL) { - log(" cell %s (pins=%d, area=%.2f) is a direct match for cell type %s.\n", best_cell->args[0].c_str(), best_cell_pins, best_cell_area, cell_type.c_str()); + log(" cell %s (%sinv, pins=%d, area=%.2f) is a direct match for cell type %s.\n", + best_cell->args[0].c_str(), best_cell_noninv ? "non" : "", best_cell_pins, best_cell_area, cell_type.c_str()); if (prepare_mode) { cell_mappings[cell_type].cell_name = cell_type; cell_mappings[cell_type].ports["C"] = 'C'; @@ -221,6 +229,7 @@ static void find_cell_sr(LibertyAst *ast, std::string cell_type, bool clkpol, bo LibertyAst *best_cell = NULL; std::map<std::string, char> best_cell_ports; int best_cell_pins = 0; + bool best_cell_noninv = false; double best_cell_area = 0; if (ast->id != "library") @@ -260,6 +269,7 @@ static void find_cell_sr(LibertyAst *ast, std::string cell_type, bool clkpol, bo int num_pins = 0; bool found_output = false; + bool found_noninv_output = false; for (auto pin : cell->children) { if (pin->id != "pin" || pin->args.size() != 1) @@ -280,10 +290,14 @@ static void find_cell_sr(LibertyAst *ast, std::string cell_type, bool clkpol, bo value.erase(pos, 1); if (value == ff->args[0]) { this_cell_ports[pin->args[0]] = cell_next_pol ? 'Q' : 'q'; + if (cell_next_pol) + found_noninv_output = true; found_output = true; } else if (value == ff->args[1]) { this_cell_ports[pin->args[0]] = cell_next_pol ? 'q' : 'Q'; + if (!cell_next_pol) + found_noninv_output = true; found_output = true; } } @@ -292,7 +306,7 @@ static void find_cell_sr(LibertyAst *ast, std::string cell_type, bool clkpol, bo this_cell_ports[pin->args[0]] = 0; } - if (!found_output || (best_cell != NULL && num_pins > best_cell_pins)) + if (!found_output || (best_cell != NULL && (num_pins > best_cell_pins || (best_cell_noninv && !found_noninv_output)))) continue; if (best_cell != NULL && num_pins == best_cell_pins && area > best_cell_area) @@ -301,12 +315,14 @@ static void find_cell_sr(LibertyAst *ast, std::string cell_type, bool clkpol, bo best_cell = cell; best_cell_pins = num_pins; best_cell_area = area; + best_cell_noninv = found_noninv_output; best_cell_ports.swap(this_cell_ports); continue_cell_loop:; } if (best_cell != NULL) { - log(" cell %s (pins=%d, area=%.2f) is a direct match for cell type %s.\n", best_cell->args[0].c_str(), best_cell_pins, best_cell_area, cell_type.c_str()); + log(" cell %s (%sinv, pins=%d, area=%.2f) is a direct match for cell type %s.\n", + best_cell->args[0].c_str(), best_cell_noninv ? "non" : "", best_cell_pins, best_cell_area, cell_type.c_str()); if (prepare_mode) { cell_mappings[cell_type].cell_name = cell_type; cell_mappings[cell_type].ports["C"] = 'C'; @@ -531,7 +547,7 @@ struct DfflibmapPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing DFFLIBMAP pass (mapping DFF cells to sequential cells from liberty file).\n"); + log_header(design, "Executing DFFLIBMAP pass (mapping DFF cells to sequential cells from liberty file).\n"); std::string liberty_file; bool prepare_mode = false; diff --git a/passes/techmap/dffsr2dff.cc b/passes/techmap/dffsr2dff.cc index 8dcbb4ed..0d4d5362 100644 --- a/passes/techmap/dffsr2dff.cc +++ b/passes/techmap/dffsr2dff.cc @@ -188,7 +188,7 @@ struct Dffsr2dffPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing DFFSR2DFF pass (mapping DFFSR cells to simpler FFs).\n"); + log_header(design, "Executing DFFSR2DFF pass (mapping DFFSR cells to simpler FFs).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) diff --git a/passes/techmap/extract.cc b/passes/techmap/extract.cc index fc73177c..71e29c60 100644 --- a/passes/techmap/extract.cc +++ b/passes/techmap/extract.cc @@ -442,7 +442,7 @@ struct ExtractPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing EXTRACT pass (map subcircuits to cells).\n"); + log_header(design, "Executing EXTRACT pass (map subcircuits to cells).\n"); log_push(); SubCircuitSolver solver; @@ -627,7 +627,7 @@ struct ExtractPass : public Pass { std::map<std::string, RTLIL::Module*> needle_map, haystack_map; std::vector<RTLIL::Module*> needle_list; - log_header("Creating graphs for SubCircuit library.\n"); + log_header(design, "Creating graphs for SubCircuit library.\n"); if (!mine_mode) for (auto &mod_it : map->modules_) { @@ -654,7 +654,7 @@ struct ExtractPass : public Pass { if (!mine_mode) { std::vector<SubCircuit::Solver::Result> results; - log_header("Running solver from SubCircuit library.\n"); + log_header(design, "Running solver from SubCircuit library.\n"); std::sort(needle_list.begin(), needle_list.end(), compareSortNeedleList); @@ -667,7 +667,7 @@ struct ExtractPass : public Pass { if (results.size() > 0) { - log_header("Substitute SubCircuits with cells.\n"); + log_header(design, "Substitute SubCircuits with cells.\n"); for (int i = 0; i < int(results.size()); i++) { auto &result = results[i]; @@ -688,7 +688,7 @@ struct ExtractPass : public Pass { { std::vector<SubCircuit::Solver::MineResult> results; - log_header("Running miner from SubCircuit library.\n"); + log_header(design, "Running miner from SubCircuit library.\n"); solver.mine(results, mine_cells_min, mine_cells_max, mine_min_freq, mine_limit_mod); map = new RTLIL::Design; diff --git a/passes/techmap/hilomap.cc b/passes/techmap/hilomap.cc index a0bd2f9a..82cecac2 100644 --- a/passes/techmap/hilomap.cc +++ b/passes/techmap/hilomap.cc @@ -76,7 +76,7 @@ struct HilomapPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing HILOMAP pass (mapping to constant drivers).\n"); + log_header(design, "Executing HILOMAP pass (mapping to constant drivers).\n"); hicell_celltype = std::string(); hicell_portname = std::string(); diff --git a/passes/techmap/insbuf.cc b/passes/techmap/insbuf.cc new file mode 100644 index 00000000..aa81468d --- /dev/null +++ b/passes/techmap/insbuf.cc @@ -0,0 +1,94 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * + * 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 + +struct InsbufPass : public Pass { + InsbufPass() : Pass("insbuf", "insert buffer cells for connected wires") { } + virtual void help() + { + log("\n"); + log(" insbuf [options] [selection]\n"); + log("\n"); + log("Insert buffer cells into the design for directly connected wires.\n"); + log("\n"); + log(" -buf <celltype> <in-portname> <out-portname>\n"); + log(" Use the given cell type instead of $_BUF_. (Notice that the next\n"); + log(" call to \"clean\" will remove all $_BUF_ in the design.)\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + log_header(design, "Executing INSBUF pass (insert buffer cells for connected wires).\n"); + + std::string celltype = "$_BUF_", in_portname = "\\A", out_portname = "\\Y"; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + std::string arg = args[argidx]; + if (arg == "-buf" && argidx+3 < args.size()) { + celltype = args[++argidx]; + in_portname = args[++argidx]; + out_portname = args[++argidx]; + continue; + } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) + { + std::vector<RTLIL::SigSig> new_connections; + + for (auto &conn : module->connections()) + { + RTLIL::SigSig new_conn; + + for (int i = 0; i < GetSize(conn.first); i++) + { + SigBit lhs = conn.first[i]; + SigBit rhs = conn.second[i]; + + if (lhs.wire && !design->selected(module, lhs.wire)) { + new_conn.first.append(lhs); + new_conn.second.append(rhs); + continue; + } + + Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(celltype)); + cell->setPort(RTLIL::escape_id(in_portname), rhs); + cell->setPort(RTLIL::escape_id(out_portname), lhs); + log("Added %s.%s: %s -> %s\n", log_id(module), log_id(cell), log_signal(rhs), log_signal(lhs)); + } + + if (GetSize(new_conn.first)) + new_connections.push_back(new_conn); + } + + module->new_connections(new_connections); + } + } +} InsbufPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/iopadmap.cc b/passes/techmap/iopadmap.cc index 9dab40ca..4acbf7c0 100644 --- a/passes/techmap/iopadmap.cc +++ b/passes/techmap/iopadmap.cc @@ -17,9 +17,8 @@ * */ -#include "kernel/register.h" -#include "kernel/rtlil.h" -#include "kernel/log.h" +#include "kernel/yosys.h" +#include "kernel/sigtools.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -48,12 +47,23 @@ struct IopadmapPass : public Pass { log(" Map module input ports to the given cell type with the\n"); log(" given output port name. if a 2nd portname is given, the\n"); log(" signal is passed through the pad call, using the 2nd\n"); - log(" portname as input.\n"); + log(" portname as the port facing the module port.\n"); log("\n"); log(" -outpad <celltype> <portname>[:<portname>]\n"); log(" -inoutpad <celltype> <portname>[:<portname>]\n"); log(" Similar to -inpad, but for output and inout ports.\n"); log("\n"); + log(" -toutpad <celltype> <portname>:<portname>[:<portname>]\n"); + log(" Merges $_TBUF_ cells into the output pad cell. This takes precedence\n"); + log(" over the other -outpad cell. The first portname is the enable input\n"); + log(" of the tristate driver.\n"); + log("\n"); + log(" -tinoutpad <celltype> <portname>:<portname>:<portname>[:<portname>]\n"); + log(" Merges $_TBUF_ cells into the inout pad cell. This takes precedence\n"); + log(" over the other -inoutpad cell. The first portname is the enable input\n"); + log(" of the tristate driver and the 2nd portname is the internal output\n"); + log(" buffering the external signal.\n"); + log("\n"); log(" -widthparam <param_name>\n"); log(" Use the specified parameter name to set the port width.\n"); log("\n"); @@ -65,14 +75,18 @@ struct IopadmapPass : public Pass { log(" are wider. (the default behavior is to create word-wide\n"); log(" buffers using -widthparam to set the word size on the cell.)\n"); log("\n"); + log("Tristate PADS (-toutpad, -tinoutpad) always operate in -bits mode.\n"); + log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing IOPADMAP pass (mapping inputs/outputs to IO-PAD cells).\n"); + log_header(design, "Executing IOPADMAP pass (mapping inputs/outputs to IO-PAD cells).\n"); std::string inpad_celltype, inpad_portname, inpad_portname2; std::string outpad_celltype, outpad_portname, outpad_portname2; std::string inoutpad_celltype, inoutpad_portname, inoutpad_portname2; + std::string toutpad_celltype, toutpad_portname, toutpad_portname2, toutpad_portname3; + std::string tinoutpad_celltype, tinoutpad_portname, tinoutpad_portname2, tinoutpad_portname3, tinoutpad_portname4; std::string widthparam, nameparam; bool flag_bits = false; @@ -98,6 +112,21 @@ struct IopadmapPass : public Pass { split_portname_pair(inoutpad_portname, inoutpad_portname2); continue; } + if (arg == "-toutpad" && argidx+2 < args.size()) { + toutpad_celltype = args[++argidx]; + toutpad_portname = args[++argidx]; + split_portname_pair(toutpad_portname, toutpad_portname2); + split_portname_pair(toutpad_portname2, toutpad_portname3); + continue; + } + if (arg == "-tinoutpad" && argidx+2 < args.size()) { + tinoutpad_celltype = args[++argidx]; + tinoutpad_portname = args[++argidx]; + split_portname_pair(tinoutpad_portname, tinoutpad_portname2); + split_portname_pair(tinoutpad_portname2, tinoutpad_portname3); + split_portname_pair(tinoutpad_portname3, tinoutpad_portname4); + continue; + } if (arg == "-widthparam" && argidx+1 < args.size()) { widthparam = args[++argidx]; continue; @@ -116,12 +145,132 @@ struct IopadmapPass : public Pass { for (auto module : design->selected_modules()) { + dict<IdString, pool<int>> skip_wires; + + if (!toutpad_celltype.empty() || !tinoutpad_celltype.empty()) + { + SigMap sigmap(module); + dict<SigBit, pair<IdString, pool<IdString>>> tbuf_bits; + + for (auto cell : module->cells()) + if (cell->type == "$_TBUF_") { + SigBit bit = sigmap(cell->getPort("\\Y").as_bit()); + tbuf_bits[bit].first = cell->name; + } + + for (auto cell : module->cells()) + for (auto port : cell->connections()) + for (auto bit : sigmap(port.second)) + if (tbuf_bits.count(bit)) + tbuf_bits.at(bit).second.insert(cell->name); + + for (auto wire : module->selected_wires()) + { + if (!wire->port_output) + continue; + + for (int i = 0; i < GetSize(wire); i++) + { + SigBit wire_bit(wire, i); + SigBit mapped_wire_bit = sigmap(wire_bit); + + if (tbuf_bits.count(mapped_wire_bit) == 0) + continue; + + auto &tbuf_cache = tbuf_bits.at(mapped_wire_bit); + Cell *tbuf_cell = module->cell(tbuf_cache.first); + + if (tbuf_cell == nullptr) + continue; + + SigBit en_sig = tbuf_cell->getPort("\\E").as_bit(); + SigBit data_sig = tbuf_cell->getPort("\\A").as_bit(); + + if (wire->port_input && !tinoutpad_celltype.empty()) + { + log("Mapping port %s.%s[%d] using %s.\n", log_id(module), log_id(wire), i, tinoutpad_celltype.c_str()); + + Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(tinoutpad_celltype)); + Wire *owire = module->addWire(NEW_ID); + + cell->setPort(RTLIL::escape_id(tinoutpad_portname), en_sig); + cell->setPort(RTLIL::escape_id(tinoutpad_portname2), owire); + cell->setPort(RTLIL::escape_id(tinoutpad_portname3), data_sig); + cell->setPort(RTLIL::escape_id(tinoutpad_portname4), wire_bit); + cell->attributes["\\keep"] = RTLIL::Const(1); + + for (auto cn : tbuf_cache.second) { + auto c = module->cell(cn); + if (c == nullptr) + continue; + for (auto port : c->connections()) { + SigSpec sig = port.second; + bool newsig = false; + for (auto &bit : sig) + if (sigmap(bit) == mapped_wire_bit) { + bit = owire; + newsig = true; + } + if (newsig) + c->setPort(port.first, sig); + } + } + + + module->remove(tbuf_cell); + skip_wires[wire->name].insert(i); + continue; + } + + if (!wire->port_input && !toutpad_celltype.empty()) + { + log("Mapping port %s.%s[%d] using %s.\n", log_id(module), log_id(wire), i, toutpad_celltype.c_str()); + + Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(toutpad_celltype)); + + cell->setPort(RTLIL::escape_id(toutpad_portname), en_sig); + cell->setPort(RTLIL::escape_id(toutpad_portname2), data_sig); + cell->setPort(RTLIL::escape_id(toutpad_portname3), wire_bit); + cell->attributes["\\keep"] = RTLIL::Const(1); + + for (auto cn : tbuf_cache.second) { + auto c = module->cell(cn); + if (c == nullptr) + continue; + for (auto port : c->connections()) { + SigSpec sig = port.second; + bool newsig = false; + for (auto &bit : sig) + if (sigmap(bit) == mapped_wire_bit) { + bit = data_sig; + newsig = true; + } + if (newsig) + c->setPort(port.first, sig); + } + } + + module->remove(tbuf_cell); + skip_wires[wire->name].insert(i); + continue; + } + } + } + } + for (auto wire : module->selected_wires()) { if (!wire->port_id) continue; std::string celltype, portname, portname2; + pool<int> skip_bit_indices; + + if (skip_wires.count(wire->name)) { + if (!flag_bits) + continue; + skip_bit_indices = skip_wires.at(wire->name); + } if (wire->port_input && !wire->port_output) { if (inpad_celltype.empty()) { @@ -163,12 +312,21 @@ struct IopadmapPass : public Pass { if (!portname2.empty()) { new_wire = module->addWire(NEW_ID, wire); module->swap_names(new_wire, wire); + wire->attributes.clear(); } if (flag_bits) { for (int i = 0; i < wire->width; i++) { + if (skip_bit_indices.count(i)) { + if (wire->port_output) + module->connect(SigSpec(new_wire, i), SigSpec(wire, i)); + else + module->connect(SigSpec(wire, i), SigSpec(new_wire, i)); + continue; + } + RTLIL::Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(celltype)); cell->setPort(RTLIL::escape_id(portname), RTLIL::SigSpec(wire, i)); if (!portname2.empty()) diff --git a/passes/techmap/lut2mux.cc b/passes/techmap/lut2mux.cc index fe55e499..2bb0bd8b 100644 --- a/passes/techmap/lut2mux.cc +++ b/passes/techmap/lut2mux.cc @@ -67,7 +67,7 @@ struct Lut2muxPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing LUT2MUX pass (mapping to constant drivers).\n"); + log_header(design, "Executing LUT2MUX pass (convert $lut to $_MUX_).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) diff --git a/passes/techmap/maccmap.cc b/passes/techmap/maccmap.cc index d5b8fe80..32569d07 100644 --- a/passes/techmap/maccmap.cc +++ b/passes/techmap/maccmap.cc @@ -379,7 +379,7 @@ struct MaccmapPass : public Pass { { bool unmap_mode = false; - log_header("Executing MACCMAP pass (map $macc cells).\n"); + log_header(design, "Executing MACCMAP pass (map $macc cells).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { diff --git a/passes/techmap/muxcover.cc b/passes/techmap/muxcover.cc index 514c3365..1dc64958 100644 --- a/passes/techmap/muxcover.cc +++ b/passes/techmap/muxcover.cc @@ -581,7 +581,7 @@ struct MuxcoverPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing MUXCOVER pass (mapping to wider MUXes).\n"); + log_header(design, "Executing MUXCOVER pass (mapping to wider MUXes).\n"); bool use_mux4 = false; bool use_mux8 = false; diff --git a/passes/techmap/nlutmap.cc b/passes/techmap/nlutmap.cc index 7ece4005..6fcdf82b 100644 --- a/passes/techmap/nlutmap.cc +++ b/passes/techmap/nlutmap.cc @@ -26,6 +26,7 @@ PRIVATE_NAMESPACE_BEGIN struct NlutmapConfig { vector<int> luts; + bool assert_mode = false; }; struct NlutmapWorker @@ -64,7 +65,7 @@ struct NlutmapWorker { vector<int> available_luts = config.luts; - while (!available_luts.empty()) + while (GetSize(available_luts) > 1) { int n_luts = available_luts.back(); int lut_size = GetSize(available_luts); @@ -84,7 +85,7 @@ struct NlutmapWorker if (cell->type != "$lut" || mapped_cells.count(cell)) continue; - if (GetSize(cell->getPort("\\A")) == lut_size) + if (GetSize(cell->getPort("\\A")) == lut_size || lut_size == 2) candidate_ratings[cell] = 0; for (auto &conn : cell->connections()) @@ -116,6 +117,12 @@ struct NlutmapWorker available_luts.back() += n_luts; } + if (config.assert_mode) { + for (auto cell : module->cells()) + if (cell->type == "$lut" && !mapped_cells.count(cell)) + log_error("Insufficient number of LUTs to map all logic cells!\n"); + } + run_abc(0); } }; @@ -135,6 +142,9 @@ struct NlutmapPass : public Pass { log(" The number of LUTs with 1, 2, 3, ... inputs that are\n"); log(" available in the target architecture.\n"); log("\n"); + log(" -assert\n"); + log(" Create an error if not all logic can be mapped\n"); + log("\n"); log("Excess logic that does not fit into the specified LUTs is mapped back\n"); log("to generic logic gates ($_AND_, etc.).\n"); log("\n"); @@ -143,7 +153,7 @@ struct NlutmapPass : public Pass { { NlutmapConfig config; - log_header("Executing NLUTMAP pass (mapping to constant drivers).\n"); + log_header(design, "Executing NLUTMAP pass (mapping to constant drivers).\n"); log_push(); size_t argidx; @@ -156,6 +166,10 @@ struct NlutmapPass : public Pass { config.luts.push_back(atoi(token.c_str())); continue; } + if (args[argidx] == "-assert") { + config.assert_mode = true; + continue; + } break; } extra_args(args, argidx, design); diff --git a/passes/techmap/pmuxtree.cc b/passes/techmap/pmuxtree.cc index 3c12bfd0..c626dbcc 100644 --- a/passes/techmap/pmuxtree.cc +++ b/passes/techmap/pmuxtree.cc @@ -78,7 +78,7 @@ struct PmuxtreePass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing PMUXTREE pass.\n"); + log_header(design, "Executing PMUXTREE pass.\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { diff --git a/passes/techmap/shregmap.cc b/passes/techmap/shregmap.cc new file mode 100644 index 00000000..6936b499 --- /dev/null +++ b/passes/techmap/shregmap.cc @@ -0,0 +1,584 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * + * 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 + +struct ShregmapTech +{ + virtual ~ShregmapTech() { } + virtual bool analyze(vector<int> &taps) = 0; + virtual bool fixup(Cell *cell, dict<int, SigBit> &taps) = 0; +}; + +struct ShregmapOptions +{ + int minlen, maxlen; + int keep_before, keep_after; + bool zinit, init, params, ffe; + dict<IdString, pair<IdString, IdString>> ffcells; + ShregmapTech *tech; + + ShregmapOptions() + { + minlen = 2; + maxlen = 0; + keep_before = 0; + keep_after = 0; + zinit = false; + init = false; + params = false; + ffe = false; + tech = nullptr; + } +}; + +struct ShregmapTechGreenpak4 : ShregmapTech +{ + bool analyze(vector<int> &taps) + { + if (GetSize(taps) > 2 && taps[0] == 0 && taps[2] < 17) { + taps.clear(); + return true; + } + + if (GetSize(taps) > 2) + return false; + + if (taps.back() > 16) return false; + + return true; + } + + bool fixup(Cell *cell, dict<int, SigBit> &taps) + { + auto D = cell->getPort("\\D"); + auto C = cell->getPort("\\C"); + + auto newcell = cell->module->addCell(NEW_ID, "\\GP_SHREG"); + newcell->setPort("\\nRST", State::S1); + newcell->setPort("\\CLK", C); + newcell->setPort("\\IN", D); + + int i = 0; + for (auto tap : taps) { + newcell->setPort(i ? "\\OUTB" : "\\OUTA", tap.second); + newcell->setParam(i ? "\\OUTB_TAP" : "\\OUTA_TAP", tap.first + 1); + i++; + } + + cell->setParam("\\OUTA_INVERT", 0); + return false; + } +}; + +struct ShregmapWorker +{ + Module *module; + SigMap sigmap; + + const ShregmapOptions &opts; + int dff_count, shreg_count; + + pool<Cell*> remove_cells; + pool<SigBit> remove_init; + + dict<SigBit, bool> sigbit_init; + dict<SigBit, Cell*> sigbit_chain_next; + dict<SigBit, Cell*> sigbit_chain_prev; + pool<SigBit> sigbit_with_non_chain_users; + pool<Cell*> chain_start_cells; + + void make_sigbit_chain_next_prev() + { + for (auto wire : module->wires()) + { + if (wire->port_output || wire->get_bool_attribute("\\keep")) { + for (auto bit : sigmap(wire)) + sigbit_with_non_chain_users.insert(bit); + } + + if (wire->attributes.count("\\init")) { + SigSpec initsig = sigmap(wire); + Const initval = wire->attributes.at("\\init"); + for (int i = 0; i < GetSize(initsig) && i < GetSize(initval); i++) + if (initval[i] == State::S0 && !opts.zinit) + sigbit_init[initsig[i]] = false; + else if (initval[i] == State::S1) + sigbit_init[initsig[i]] = true; + } + } + + for (auto cell : module->cells()) + { + if (opts.ffcells.count(cell->type) && !cell->get_bool_attribute("\\keep")) + { + IdString d_port = opts.ffcells.at(cell->type).first; + IdString q_port = opts.ffcells.at(cell->type).second; + + SigBit d_bit = sigmap(cell->getPort(d_port).as_bit()); + SigBit q_bit = sigmap(cell->getPort(q_port).as_bit()); + + if (opts.init || sigbit_init.count(q_bit) == 0) + { + if (sigbit_chain_next.count(d_bit)) { + sigbit_with_non_chain_users.insert(d_bit); + } else + sigbit_chain_next[d_bit] = cell; + + sigbit_chain_prev[q_bit] = cell; + continue; + } + } + + for (auto conn : cell->connections()) + if (cell->input(conn.first)) + for (auto bit : sigmap(conn.second)) + sigbit_with_non_chain_users.insert(bit); + } + } + + void find_chain_start_cells() + { + for (auto it : sigbit_chain_next) + { + if (opts.tech == nullptr && sigbit_with_non_chain_users.count(it.first)) + goto start_cell; + + if (sigbit_chain_prev.count(it.first) != 0) + { + Cell *c1 = sigbit_chain_prev.at(it.first); + Cell *c2 = it.second; + + if (c1->type != c2->type) + goto start_cell; + + if (c1->parameters != c2->parameters) + goto start_cell; + + IdString d_port = opts.ffcells.at(c1->type).first; + IdString q_port = opts.ffcells.at(c1->type).second; + + auto c1_conn = c1->connections(); + auto c2_conn = c1->connections(); + + c1_conn.erase(d_port); + c1_conn.erase(q_port); + + c2_conn.erase(d_port); + c2_conn.erase(q_port); + + if (c1_conn != c2_conn) + goto start_cell; + + continue; + } + + start_cell: + chain_start_cells.insert(it.second); + } + } + + vector<Cell*> create_chain(Cell *start_cell) + { + vector<Cell*> chain; + + Cell *c = start_cell; + while (c != nullptr) + { + chain.push_back(c); + + IdString q_port = opts.ffcells.at(c->type).second; + SigBit q_bit = sigmap(c->getPort(q_port).as_bit()); + + if (sigbit_chain_next.count(q_bit) == 0) + break; + + c = sigbit_chain_next.at(q_bit); + if (chain_start_cells.count(c) != 0) + break; + } + + return chain; + } + + void process_chain(vector<Cell*> &chain) + { + if (GetSize(chain) < opts.keep_before + opts.minlen + opts.keep_after) + return; + + int cursor = opts.keep_before; + while (cursor < GetSize(chain) - opts.keep_after) + { + int depth = GetSize(chain) - opts.keep_after - cursor; + + if (opts.maxlen > 0) + depth = std::min(opts.maxlen, depth); + + Cell *first_cell = chain[cursor]; + IdString q_port = opts.ffcells.at(first_cell->type).second; + dict<int, SigBit> taps_dict; + + if (opts.tech) + { + vector<SigBit> qbits; + vector<int> taps; + + for (int i = 0; i < depth; i++) + { + Cell *cell = chain[cursor+i]; + auto qbit = sigmap(cell->getPort(q_port)); + qbits.push_back(qbit); + + if (sigbit_with_non_chain_users.count(qbit)) + taps.push_back(i); + } + + while (depth > 0) + { + if (taps.empty() || taps.back() < depth-1) + taps.push_back(depth-1); + + if (opts.tech->analyze(taps)) + break; + + taps.pop_back(); + depth--; + } + + depth = 0; + for (auto tap : taps) { + taps_dict[tap] = qbits.at(tap); + log_assert(depth < tap+1); + depth = tap+1; + } + } + + if (depth < 2) { + cursor++; + continue; + } + + Cell *last_cell = chain[cursor+depth-1]; + + log("Converting %s.%s ... %s.%s to a shift register with depth %d.\n", + log_id(module), log_id(first_cell), log_id(module), log_id(last_cell), depth); + + dff_count += depth; + shreg_count += 1; + + string shreg_cell_type_str = "$__SHREG"; + if (opts.params) { + shreg_cell_type_str += "_"; + } else { + if (first_cell->type[1] != '_') + shreg_cell_type_str += "_"; + shreg_cell_type_str += first_cell->type.substr(1); + } + + if (opts.init) { + vector<State> initval; + for (int i = depth-1; i >= 0; i--) { + SigBit bit = sigmap(chain[cursor+i]->getPort(q_port).as_bit()); + if (sigbit_init.count(bit) == 0) + initval.push_back(State::Sx); + else if (sigbit_init.at(bit)) + initval.push_back(State::S1); + else + initval.push_back(State::S0); + remove_init.insert(bit); + } + first_cell->setParam("\\INIT", initval); + } + + if (opts.zinit) + for (int i = depth-1; i >= 0; i--) { + SigBit bit = sigmap(chain[cursor+i]->getPort(q_port).as_bit()); + remove_init.insert(bit); + } + + if (opts.params) + { + int param_clkpol = -1; + int param_enpol = 2; + + if (first_cell->type == "$_DFF_N_") param_clkpol = 0; + if (first_cell->type == "$_DFF_P_") param_clkpol = 1; + + if (first_cell->type == "$_DFFE_NN_") param_clkpol = 0, param_enpol = 0; + if (first_cell->type == "$_DFFE_NP_") param_clkpol = 0, param_enpol = 1; + if (first_cell->type == "$_DFFE_PN_") param_clkpol = 1, param_enpol = 0; + if (first_cell->type == "$_DFFE_PP_") param_clkpol = 1, param_enpol = 1; + + log_assert(param_clkpol >= 0); + first_cell->setParam("\\CLKPOL", param_clkpol); + if (opts.ffe) first_cell->setParam("\\ENPOL", param_enpol); + } + + first_cell->type = shreg_cell_type_str; + first_cell->setPort(q_port, last_cell->getPort(q_port)); + first_cell->setParam("\\DEPTH", depth); + + if (opts.tech != nullptr && !opts.tech->fixup(first_cell, taps_dict)) + remove_cells.insert(first_cell); + + for (int i = 1; i < depth; i++) + remove_cells.insert(chain[cursor+i]); + cursor += depth; + } + } + + void cleanup() + { + for (auto cell : remove_cells) + module->remove(cell); + + for (auto wire : module->wires()) + { + if (wire->attributes.count("\\init") == 0) + continue; + + SigSpec initsig = sigmap(wire); + Const &initval = wire->attributes.at("\\init"); + + for (int i = 0; i < GetSize(initsig) && i < GetSize(initval); i++) + if (remove_init.count(initsig[i])) + initval[i] = State::Sx; + + if (SigSpec(initval).is_fully_undef()) + wire->attributes.erase("\\init"); + } + + remove_cells.clear(); + sigbit_chain_next.clear(); + sigbit_chain_prev.clear(); + chain_start_cells.clear(); + } + + ShregmapWorker(Module *module, const ShregmapOptions &opts) : + module(module), sigmap(module), opts(opts), dff_count(0), shreg_count(0) + { + make_sigbit_chain_next_prev(); + find_chain_start_cells(); + + for (auto c : chain_start_cells) { + vector<Cell*> chain = create_chain(c); + process_chain(chain); + } + + cleanup(); + } +}; + +struct ShregmapPass : public Pass { + ShregmapPass() : Pass("shregmap", "map shift registers") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" shregmap [options] [selection]\n"); + log("\n"); + log("This pass converts chains of $_DFF_[NP]_ gates to target specific shift register\n"); + log("primitives. The generated shift register will be of type $__SHREG_DFF_[NP]_ and\n"); + log("will use the same interface as the original $_DFF_*_ cells. The cell parameter\n"); + log("'DEPTH' will contain the depth of the shift register. Use a target-specific\n"); + log("'techmap' map file to convert those cells to the actual target cells.\n"); + log("\n"); + log(" -minlen N\n"); + log(" minimum length of shift register (default = 2)\n"); + log(" (this is the length after -keep_before and -keep_after)\n"); + log("\n"); + log(" -maxlen N\n"); + log(" maximum length of shift register (default = no limit)\n"); + log(" larger chains will be mapped to multiple shift register instances\n"); + log("\n"); + log(" -keep_before N\n"); + log(" number of DFFs to keep before the shift register (default = 0)\n"); + log("\n"); + log(" -keep_after N\n"); + log(" number of DFFs to keep after the shift register (default = 0)\n"); + log("\n"); + log(" -clkpol pos|neg|any\n"); + log(" limit match to only positive or negative edge clocks. (default = any)\n"); + log("\n"); + log(" -enpol pos|neg|none|any_or_none|any\n"); + log(" limit match to FFs with the specified enable polarity. (default = none)\n"); + log("\n"); + log(" -match <cell_type>[:<d_port_name>:<q_port_name>]\n"); + log(" match the specified cells instead of $_DFF_N_ and $_DFF_P_. If\n"); + log(" ':<d_port_name>:<q_port_name>' is omitted then 'D' and 'Q' is used\n"); + log(" by default. E.g. the option '-clkpol pos' is just an alias for\n"); + log(" '-match $_DFF_P_', which is an alias for '-match $_DFF_P_:D:Q'.\n"); + log("\n"); + log(" -params\n"); + log(" instead of encoding the clock and enable polarity in the cell name by\n"); + log(" deriving from the original cell name, simply name all generated cells\n"); + log(" $__SHREG_ and use CLKPOL and ENPOL parameters. An ENPOL value of 2 is\n"); + log(" used to denote cells without enable input. The ENPOL parameter is\n"); + log(" omitted when '-enpol none' (or no -enpol option) is passed.\n"); + log("\n"); + log(" -zinit\n"); + log(" assume the shift register is automatically zero-initialized, so it\n"); + log(" becomes legal to merge zero initialized FFs into the shift register.\n"); + log("\n"); + log(" -init\n"); + log(" map initialized registers to the shift reg, add an INIT parameter to\n"); + log(" generated cells with the initialization value. (first bit to shift out\n"); + log(" in LSB position)\n"); + log("\n"); + log(" -tech greenpak4\n"); + log(" map to greenpak4 shift registers.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + ShregmapOptions opts; + string clkpol, enpol; + + log_header(design, "Executing SHREGMAP pass (map shift registers).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-clkpol" && argidx+1 < args.size()) { + clkpol = args[++argidx]; + continue; + } + if (args[argidx] == "-enpol" && argidx+1 < args.size()) { + enpol = args[++argidx]; + continue; + } + if (args[argidx] == "-match" && argidx+1 < args.size()) { + vector<string> match_args = split_tokens(args[++argidx], ":"); + if (GetSize(match_args) < 2) + match_args.push_back("D"); + if (GetSize(match_args) < 3) + match_args.push_back("Q"); + IdString id_cell_type(RTLIL::escape_id(match_args[0])); + IdString id_d_port_name(RTLIL::escape_id(match_args[1])); + IdString id_q_port_name(RTLIL::escape_id(match_args[2])); + opts.ffcells[id_cell_type] = make_pair(id_d_port_name, id_q_port_name); + continue; + } + if (args[argidx] == "-minlen" && argidx+1 < args.size()) { + opts.minlen = atoi(args[++argidx].c_str()); + continue; + } + if (args[argidx] == "-maxlen" && argidx+1 < args.size()) { + opts.maxlen = atoi(args[++argidx].c_str()); + continue; + } + if (args[argidx] == "-keep_before" && argidx+1 < args.size()) { + opts.keep_before = atoi(args[++argidx].c_str()); + continue; + } + if (args[argidx] == "-keep_after" && argidx+1 < args.size()) { + opts.keep_after = atoi(args[++argidx].c_str()); + continue; + } + if (args[argidx] == "-tech" && argidx+1 < args.size() && opts.tech == nullptr) { + string tech = args[++argidx]; + if (tech == "greenpak4") { + clkpol = "pos"; + opts.zinit = true; + opts.tech = new ShregmapTechGreenpak4; + } else { + argidx--; + break; + } + continue; + } + if (args[argidx] == "-zinit") { + opts.zinit = true; + continue; + } + if (args[argidx] == "-init") { + opts.init = true; + continue; + } + if (args[argidx] == "-params") { + opts.params = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + if (opts.zinit && opts.init) + log_cmd_error("Options -zinit and -init are exclusive!\n"); + + if (opts.ffcells.empty()) + { + bool clk_pos = clkpol == "" || clkpol == "pos" || clkpol == "any"; + bool clk_neg = clkpol == "" || clkpol == "neg" || clkpol == "any"; + + bool en_none = enpol == "" || enpol == "none" || enpol == "any_or_none"; + bool en_pos = enpol == "pos" || enpol == "any" || enpol == "any_or_none"; + bool en_neg = enpol == "neg" || enpol == "any" || enpol == "any_or_none"; + + if (clk_pos && en_none) + opts.ffcells["$_DFF_P_"] = make_pair(IdString("\\D"), IdString("\\Q")); + if (clk_neg && en_none) + opts.ffcells["$_DFF_N_"] = make_pair(IdString("\\D"), IdString("\\Q")); + + if (clk_pos && en_pos) + opts.ffcells["$_DFFE_PP_"] = make_pair(IdString("\\D"), IdString("\\Q")); + if (clk_pos && en_neg) + opts.ffcells["$_DFFE_PN_"] = make_pair(IdString("\\D"), IdString("\\Q")); + + if (clk_neg && en_pos) + opts.ffcells["$_DFFE_NP_"] = make_pair(IdString("\\D"), IdString("\\Q")); + if (clk_neg && en_neg) + opts.ffcells["$_DFFE_NN_"] = make_pair(IdString("\\D"), IdString("\\Q")); + + if (en_pos || en_neg) + opts.ffe = true; + } + else + { + if (!clkpol.empty()) + log_cmd_error("Options -clkpol and -match are exclusive!\n"); + if (!enpol.empty()) + log_cmd_error("Options -enpol and -match are exclusive!\n"); + if (opts.params) + log_cmd_error("Options -params and -match are exclusive!\n"); + } + + int dff_count = 0; + int shreg_count = 0; + + for (auto module : design->selected_modules()) { + ShregmapWorker worker(module, opts); + dff_count += worker.dff_count; + shreg_count += worker.shreg_count; + } + + log("Converted %d dff cells into %d shift registers.\n", dff_count, shreg_count); + + if (opts.tech != nullptr) { + delete opts.tech; + opts.tech = nullptr; + } + } +} ShregmapPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/simplemap.cc b/passes/techmap/simplemap.cc index f6ac3964..0fb64734 100644 --- a/passes/techmap/simplemap.cc +++ b/passes/techmap/simplemap.cc @@ -321,6 +321,36 @@ void simplemap_lut(RTLIL::Module *module, RTLIL::Cell *cell) module->connect(cell->getPort("\\Y"), lut_data); } +void simplemap_sop(RTLIL::Module *module, RTLIL::Cell *cell) +{ + SigSpec ctrl = cell->getPort("\\A"); + SigSpec table = cell->getParam("\\TABLE"); + + int width = cell->getParam("\\WIDTH").as_int(); + int depth = cell->getParam("\\DEPTH").as_int(); + table.extend_u0(2 * width * depth); + + SigSpec products; + + for (int i = 0; i < depth; i++) { + SigSpec in, pat; + for (int j = 0; j < width; j++) { + if (table[2*i*width + 2*j + 0] == State::S1) { + in.append(ctrl[j]); + pat.append(State::S0); + } + if (table[2*i*width + 2*j + 1] == State::S1) { + in.append(ctrl[j]); + pat.append(State::S1); + } + } + + products.append(GetSize(in) > 0 ? module->Eq(NEW_ID, in, pat) : State::S1); + } + + module->connect(cell->getPort("\\Y"), module->ReduceOr(NEW_ID, products)); +} + void simplemap_slice(RTLIL::Module *module, RTLIL::Cell *cell) { int offset = cell->parameters.at("\\OFFSET").as_int(); @@ -498,6 +528,7 @@ void simplemap_get_mappers(std::map<RTLIL::IdString, void(*)(RTLIL::Module*, RTL mappers["$mux"] = simplemap_mux; mappers["$tribuf"] = simplemap_tribuf; mappers["$lut"] = simplemap_lut; + mappers["$sop"] = simplemap_sop; mappers["$slice"] = simplemap_slice; mappers["$concat"] = simplemap_concat; mappers["$sr"] = simplemap_sr; @@ -543,7 +574,7 @@ struct SimplemapPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing SIMPLEMAP pass (map simple cells to gate primitives).\n"); + log_header(design, "Executing SIMPLEMAP pass (map simple cells to gate primitives).\n"); extra_args(args, 1, design); std::map<RTLIL::IdString, void(*)(RTLIL::Module*, RTLIL::Cell*)> mappers; diff --git a/passes/techmap/techmap.cc b/passes/techmap/techmap.cc index 5334ebfa..b2cc492b 100644 --- a/passes/techmap/techmap.cc +++ b/passes/techmap/techmap.cc @@ -234,8 +234,10 @@ struct TechmapWorker tpl_written_bits.insert(bit); SigMap port_signal_map; + SigSig port_signal_assign; - for (auto &it : cell->connections()) { + for (auto &it : cell->connections()) + { RTLIL::IdString portname = it.first; if (positional_ports.count(portname) > 0) portname = positional_ports.at(portname); @@ -244,16 +246,22 @@ struct TechmapWorker log_error("Can't map port `%s' of cell `%s' to template `%s'!\n", portname.c_str(), cell->name.c_str(), tpl->name.c_str()); continue; } + RTLIL::Wire *w = tpl->wires_.at(portname); - RTLIL::SigSig c; + RTLIL::SigSig c, extra_connect; + if (w->port_output && !w->port_input) { c.first = it.second; c.second = RTLIL::SigSpec(w); apply_prefix(cell->name.str(), c.second, module); + extra_connect.first = c.second; + extra_connect.second = c.first; } else if (!w->port_output && w->port_input) { c.first = RTLIL::SigSpec(w); c.second = it.second; apply_prefix(cell->name.str(), c.first, module); + extra_connect.first = c.first; + extra_connect.second = c.second; } else { SigSpec sig_tpl = w, sig_tpl_pf = w, sig_mod = it.second; apply_prefix(cell->name.str(), sig_tpl_pf, module); @@ -266,28 +274,48 @@ struct TechmapWorker c.second.append(sig_mod[i]); } } + extra_connect.first = sig_tpl_pf; + extra_connect.second = sig_mod; } + if (c.second.size() > c.first.size()) c.second.remove(c.first.size(), c.second.size() - c.first.size()); + if (c.second.size() < c.first.size()) c.second.append(RTLIL::SigSpec(RTLIL::State::S0, c.first.size() - c.second.size())); + log_assert(c.first.size() == c.second.size()); - if (flatten_mode) { + + if (flatten_mode) + { // more conservative approach: // connect internal and external wires + if (sigmaps.count(module) == 0) sigmaps[module].set(module); + if (sigmaps.at(module)(c.first).has_const()) log_error("Mismatch in directionality for cell port %s.%s.%s: %s <= %s\n", log_id(module), log_id(cell), log_id(it.first), log_signal(c.first), log_signal(c.second)); + module->connect(c); - } else { + } + else + { // approach that yields nicer outputs: // replace internal wires that are connected to external wires + if (w->port_output) port_signal_map.add(c.second, c.first); else port_signal_map.add(c.first, c.second); + + for (auto &attr : w->attributes) { + if (attr.first == "\\src") + continue; + module->connect(extra_connect); + break; + } } } @@ -611,7 +639,7 @@ struct TechmapWorker if (techmap_cache.count(key) > 0) { tpl = techmap_cache[key]; } else { - if (cell->parameters.size() != 0) { + if (parameters.size() != 0) { derived_name = tpl->derive(map, dict<RTLIL::IdString, RTLIL::Const>(parameters.begin(), parameters.end())); tpl = map->module(derived_name); log_continue = true; @@ -779,7 +807,7 @@ struct TechmapWorker if (recursive_mode) { if (log_continue) { - log_header("Continuing TECHMAP pass.\n"); + log_header(design, "Continuing TECHMAP pass.\n"); log_continue = false; } while (techmap_module(map, tpl, map, handled_cells, celltypeMap, true)) { } @@ -790,7 +818,7 @@ struct TechmapWorker continue; if (log_continue) { - log_header("Continuing TECHMAP pass.\n"); + log_header(design, "Continuing TECHMAP pass.\n"); log_continue = false; } @@ -833,7 +861,7 @@ struct TechmapWorker } if (log_continue) { - log_header("Continuing TECHMAP pass.\n"); + log_header(design, "Continuing TECHMAP pass.\n"); log_continue = false; } @@ -976,7 +1004,7 @@ struct TechmapPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing TECHMAP pass (map to technology primitives).\n"); + log_header(design, "Executing TECHMAP pass (map to technology primitives).\n"); log_push(); TechmapWorker worker; @@ -1108,7 +1136,7 @@ struct FlattenPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing FLATTEN pass (flatten design).\n"); + log_header(design, "Executing FLATTEN pass (flatten design).\n"); log_push(); extra_args(args, 1, design); diff --git a/passes/techmap/tribuf.cc b/passes/techmap/tribuf.cc index d0564b4e..03629082 100644 --- a/passes/techmap/tribuf.cc +++ b/passes/techmap/tribuf.cc @@ -160,7 +160,7 @@ struct TribufPass : public Pass { { TribufConfig config; - log_header("Executing TRIBUF pass.\n"); + log_header(design, "Executing TRIBUF pass.\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { diff --git a/passes/tests/test_autotb.cc b/passes/tests/test_autotb.cc index bb516fca..cb31056f 100644 --- a/passes/tests/test_autotb.cc +++ b/passes/tests/test_autotb.cc @@ -71,16 +71,21 @@ static std::string idy(std::string str1, std::string str2 = std::string(), std:: return id(str1); } -static void autotest(std::ostream &f, RTLIL::Design *design, int num_iter) +static void autotest(std::ostream &f, RTLIL::Design *design, int num_iter, int seed) { + f << stringf("`ifndef outfile\n"); + f << stringf("\t`define outfile \"/dev/stdout\"\n"); + f << stringf("`endif\n"); + f << stringf("module testbench;\n\n"); - f << stringf("integer i;\n\n"); + f << stringf("integer i;\n"); + f << stringf("integer file;\n\n"); f << stringf("reg [31:0] xorshift128_x = 123456789;\n"); f << stringf("reg [31:0] xorshift128_y = 362436069;\n"); f << stringf("reg [31:0] xorshift128_z = 521288629;\n"); - f << stringf("reg [31:0] xorshift128_w = %u; // <-- seed value\n", int(time(NULL))); + f << stringf("reg [31:0] xorshift128_w = %u; // <-- seed value\n", seed ? seed : int(time(NULL))); f << stringf("reg [31:0] xorshift128_t;\n\n"); f << stringf("task xorshift128;\n"); f << stringf("begin\n"); @@ -150,6 +155,7 @@ static void autotest(std::ostream &f, RTLIL::Design *design, int num_iter) f << stringf("\t%s <= #%d 0;\n", it->first.c_str(), ++delay_counter*2); for (auto it = signal_clk.begin(); it != signal_clk.end(); ++it) f << stringf("\t%s <= #%d 0;\n", it->first.c_str(), ++delay_counter*2); + f << stringf("\t#%d;\n", ((2*delay_counter+99)/100)*100); for (auto it = signal_clk.begin(); it != signal_clk.end(); ++it) { f << stringf("\t#100; %s <= 1;\n", it->first.c_str()); f << stringf("\t#100; %s <= 0;\n", it->first.c_str()); @@ -157,6 +163,7 @@ static void autotest(std::ostream &f, RTLIL::Design *design, int num_iter) delay_counter = 0; for (auto it = signal_in.begin(); it != signal_in.end(); ++it) f << stringf("\t%s <= #%d ~0;\n", it->first.c_str(), ++delay_counter*2); + f << stringf("\t#%d;\n", ((2*delay_counter+99)/100)*100); for (auto it = signal_clk.begin(); it != signal_clk.end(); ++it) { f << stringf("\t#100; %s <= 1;\n", it->first.c_str()); f << stringf("\t#100; %s <= 0;\n", it->first.c_str()); @@ -167,6 +174,7 @@ static void autotest(std::ostream &f, RTLIL::Design *design, int num_iter) continue; f << stringf("\t%s <= #%d 'b%s;\n", it->first.c_str(), ++delay_counter*2, signal_const[it->first].c_str()); } + f << stringf("\t#%d;\n", ((2*delay_counter+99)/100)*100); f << stringf("end\n"); f << stringf("endtask\n\n"); @@ -179,6 +187,7 @@ static void autotest(std::ostream &f, RTLIL::Design *design, int num_iter) f << stringf("\txorshift128;\n"); f << stringf("\t%s <= #%d { xorshift128_x, xorshift128_y, xorshift128_z, xorshift128_w };\n", it->first.c_str(), ++delay_counter*2); } + f << stringf("\t#%d;\n", ((2*delay_counter+99)/100)*100); f << stringf("end\n"); f << stringf("endtask\n\n"); @@ -206,61 +215,70 @@ static void autotest(std::ostream &f, RTLIL::Design *design, int num_iter) f << stringf("task %s;\n", idy(mod->name.str(), "print_status").c_str()); f << stringf("begin\n"); - f << stringf("\t$display(\"#OUT# %%b %%b %%b %%t %%d\", {"); + f << stringf("\t$fdisplay(file, \"#OUT# %%b %%b %%b %%t %%d\", {"); if (signal_in.size()) for (auto it = signal_in.begin(); it != signal_in.end(); it++) { f << stringf("%s %s", it == signal_in.begin() ? "" : ",", it->first.c_str()); int len = it->second; + header2 += ", \""; if (len > 1) header2 += "/", len--; while (len > 1) header2 += "-", len--; if (len > 0) header2 += shorthand, len--; + header2 += "\""; header1.push_back(" " + it->first); - header1.back()[0] = shorthand++; + header1.back()[0] = shorthand; + shorthand = shorthand == 'Z' ? 'A' : shorthand+1; } else { f << stringf(" 1'bx"); - header2 += "#"; + header2 += ", \"#\""; } f << stringf(" }, {"); - header2 += " "; + header2 += ", \" \""; if (signal_clk.size()) { for (auto it = signal_clk.begin(); it != signal_clk.end(); it++) { f << stringf("%s %s", it == signal_clk.begin() ? "" : ",", it->first.c_str()); int len = it->second; + header2 += ", \""; if (len > 1) header2 += "/", len--; while (len > 1) header2 += "-", len--; if (len > 0) header2 += shorthand, len--; + header2 += "\""; header1.push_back(" " + it->first); - header1.back()[0] = shorthand++; + header1.back()[0] = shorthand; + shorthand = shorthand == 'Z' ? 'A' : shorthand+1; } } else { f << stringf(" 1'bx"); - header2 += "#"; + header2 += ", \"#\""; } f << stringf(" }, {"); - header2 += " "; + header2 += ", \" \""; if (signal_out.size()) { for (auto it = signal_out.begin(); it != signal_out.end(); it++) { f << stringf("%s %s", it == signal_out.begin() ? "" : ",", it->first.c_str()); int len = it->second; + header2 += ", \""; if (len > 1) header2 += "/", len--; while (len > 1) header2 += "-", len--; if (len > 0) header2 += shorthand, len--; + header2 += "\""; header1.push_back(" " + it->first); - header1.back()[0] = shorthand++; + header1.back()[0] = shorthand; + shorthand = shorthand == 'Z' ? 'A' : shorthand+1; } } else { f << stringf(" 1'bx"); - header2 += "#"; + header2 += ", \"#\""; } f << stringf(" }, $time, i);\n"); f << stringf("end\n"); @@ -268,17 +286,17 @@ static void autotest(std::ostream &f, RTLIL::Design *design, int num_iter) f << stringf("task %s;\n", idy(mod->name.str(), "print_header").c_str()); f << stringf("begin\n"); - f << stringf("\t$display(\"#OUT#\");\n"); + f << stringf("\t$fdisplay(file, \"#OUT#\");\n"); for (auto &hdr : header1) - f << stringf("\t$display(\"#OUT# %s\");\n", hdr.c_str()); - f << stringf("\t$display(\"#OUT#\");\n"); - f << stringf("\t$display(\"#OUT# %s\");\n", header2.c_str()); + f << stringf("\t$fdisplay(file, \"#OUT# %s\");\n", hdr.c_str()); + f << stringf("\t$fdisplay(file, \"#OUT#\");\n"); + f << stringf("\t$fdisplay(file, {\"#OUT# \"%s});\n", header2.c_str()); f << stringf("end\n"); f << stringf("endtask\n\n"); f << stringf("task %s;\n", idy(mod->name.str(), "test").c_str()); f << stringf("begin\n"); - f << stringf("\t$display(\"#OUT#\\n#OUT# ==== %s ====\");\n", idy(mod->name.str()).c_str()); + f << stringf("\t$fdisplay(file, \"#OUT#\\n#OUT# ==== %s ====\");\n", idy(mod->name.str()).c_str()); f << stringf("\t%s;\n", idy(mod->name.str(), "reset").c_str()); f << stringf("\tfor (i=0; i<%d; i=i+1) begin\n", num_iter); f << stringf("\t\tif (i %% 20 == 0) %s;\n", idy(mod->name.str(), "print_header").c_str()); @@ -293,9 +311,11 @@ static void autotest(std::ostream &f, RTLIL::Design *design, int num_iter) f << stringf("initial begin\n"); f << stringf("\t// $dumpfile(\"testbench.vcd\");\n"); f << stringf("\t// $dumpvars(0, testbench);\n"); + f << stringf("\tfile = $fopen(`outfile);\n"); for (auto it = design->modules_.begin(); it != design->modules_.end(); ++it) if (!it->second->get_bool_attribute("\\gentb_skip")) f << stringf("\t%s;\n", idy(it->first.str(), "test").c_str()); + f << stringf("\t$fclose(file);\n"); f << stringf("\t$finish;\n"); f << stringf("end\n\n"); @@ -332,8 +352,9 @@ struct TestAutotbBackend : public Backend { virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) { int num_iter = 1000; + int seed = 0; - log_header("Executing TEST_AUTOTB backend (auto-generate pseudo-random test benches).\n"); + log_header(design, "Executing TEST_AUTOTB backend (auto-generate pseudo-random test benches).\n"); int argidx; for (argidx = 1; argidx < GetSize(args); argidx++) @@ -342,11 +363,15 @@ struct TestAutotbBackend : public Backend { num_iter = atoi(args[++argidx].c_str()); continue; } + if (args[argidx] == "-seed" && argidx+1 < GetSize(args)) { + seed = atoi(args[++argidx].c_str()); + continue; + } break; } extra_args(f, filename, args, argidx); - autotest(*f, design, num_iter); + autotest(*f, design, num_iter, seed); } } TestAutotbBackend; diff --git a/passes/tests/test_cell.cc b/passes/tests/test_cell.cc index a8fcac9b..049c2053 100644 --- a/passes/tests/test_cell.cc +++ b/passes/tests/test_cell.cc @@ -21,6 +21,7 @@ #include "kernel/yosys.h" #include "kernel/satgen.h" #include "kernel/consteval.h" +#include "kernel/celledges.h" #include "kernel/macc.h" #include <algorithm> @@ -42,6 +43,32 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type, RTLIL::Cell *cell = module->addCell("\\UUT", cell_type); RTLIL::Wire *wire; + if (cell_type == "$mux" || cell_type == "$pmux") + { + int width = 1 + xorshift32(8); + int swidth = cell_type == "$mux" ? 1 : 1 + xorshift32(8); + + wire = module->addWire("\\A"); + wire->width = width; + wire->port_input = true; + cell->setPort("\\A", wire); + + wire = module->addWire("\\B"); + wire->width = width * swidth; + wire->port_input = true; + cell->setPort("\\B", wire); + + wire = module->addWire("\\S"); + wire->width = swidth; + wire->port_input = true; + cell->setPort("\\S", wire); + + wire = module->addWire("\\Y"); + wire->width = width; + wire->port_output = true; + cell->setPort("\\Y", wire); + } + if (cell_type == "$fa") { int width = 1 + xorshift32(8); @@ -164,6 +191,41 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type, cell->setParam("\\LUT", config.as_const()); } + if (cell_type == "$sop") + { + int width = 1 + xorshift32(8); + int depth = 1 + xorshift32(8); + + wire = module->addWire("\\A"); + wire->width = width; + wire->port_input = true; + cell->setPort("\\A", wire); + + wire = module->addWire("\\Y"); + wire->port_output = true; + cell->setPort("\\Y", wire); + + RTLIL::SigSpec config; + for (int i = 0; i < width*depth; i++) + switch (xorshift32(3)) { + case 0: + config.append(RTLIL::S1); + config.append(RTLIL::S0); + break; + case 1: + config.append(RTLIL::S0); + config.append(RTLIL::S1); + break; + case 2: + config.append(RTLIL::S0); + config.append(RTLIL::S0); + break; + } + + cell->setParam("\\DEPTH", depth); + cell->setParam("\\TABLE", config.as_const()); + } + if (cell_type_flags.find('A') != std::string::npos) { wire = module->addWire("\\A"); wire->width = 1 + xorshift32(8); @@ -270,6 +332,91 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type, cell->check(); } +static void run_edges_test(RTLIL::Design *design, bool verbose) +{ + Module *module = *design->modules().begin(); + Cell *cell = *module->cells().begin(); + + ezSatPtr ezptr; + ezSAT &ez = *ezptr.get(); + + SigMap sigmap(module); + SatGen satgen(&ez, &sigmap); + + FwdCellEdgesDatabase edges_db(sigmap); + if (!edges_db.add_edges_from_cell(cell)) + log_error("Creating edge database failed for this cell!\n"); + + dict<SigBit, pool<SigBit>> satgen_db; + + satgen.setContext(&sigmap, "X:"); + satgen.importCell(cell); + + satgen.setContext(&sigmap, "Y:"); + satgen.importCell(cell); + + vector<tuple<SigBit, int, int>> input_db, output_db; + + for (auto &conn : cell->connections()) + { + SigSpec bits = sigmap(conn.second); + + satgen.setContext(&sigmap, "X:"); + std::vector<int> xbits = satgen.importSigSpec(bits); + + satgen.setContext(&sigmap, "Y:"); + std::vector<int> ybits = satgen.importSigSpec(bits); + + for (int i = 0; i < GetSize(bits); i++) + if (cell->input(conn.first)) + input_db.emplace_back(bits[i], xbits[i], ybits[i]); + else + output_db.emplace_back(bits[i], xbits[i], ybits[i]); + } + + if (verbose) + log("\nSAT solving for all edges:\n"); + + for (int i = 0; i < GetSize(input_db); i++) + { + SigBit inbit = std::get<0>(input_db[i]); + + if (verbose) + log(" Testing input signal %s:\n", log_signal(inbit)); + + vector<int> xinbits, yinbits; + for (int k = 0; k < GetSize(input_db); k++) + if (k != i) { + xinbits.push_back(std::get<1>(input_db[k])); + yinbits.push_back(std::get<2>(input_db[k])); + } + + int xyinbit_ok = ez.vec_eq(xinbits, yinbits); + + for (int k = 0; k < GetSize(output_db); k++) + { + SigBit outbit = std::get<0>(output_db[k]); + int xoutbit = std::get<1>(output_db[k]); + int youtbit = std::get<2>(output_db[k]); + + bool is_edge = ez.solve(xyinbit_ok, ez.XOR(xoutbit, youtbit)); + + if (is_edge) + satgen_db[inbit].insert(outbit); + + if (verbose) { + bool is_ref_edge = edges_db.db.count(inbit) && edges_db.db.at(inbit).count(outbit); + log(" %c %s %s\n", is_edge ? 'x' : 'o', log_signal(outbit), is_edge == is_ref_edge ? "OK" : "ERROR"); + } + } + } + + if (satgen_db == edges_db.db) + log("PASS.\n"); + else + log_error("SAT-based edge table does not match the database!\n"); +} + static void run_eval_test(RTLIL::Design *design, bool verbose, bool nosat, std::string uut_name, std::ofstream &vlog_file) { log("Eval testing:%c", verbose ? '\n' : ' '); @@ -534,7 +681,7 @@ struct TestCellPass : public Pass { log(" pass this option to techmap.\n"); log("\n"); log(" -simlib\n"); - log(" use \"techmap -map +/simlib.v -max_iter 2 -autoproc\"\n"); + log(" use \"techmap -D SIMLIB_NOCHECKS -map +/simlib.v -max_iter 2 -autoproc\"\n"); log("\n"); log(" -aigmap\n"); log(" instead of calling \"techmap\", call \"aigmap\"\n"); @@ -555,6 +702,9 @@ struct TestCellPass : public Pass { log(" -noeval\n"); log(" do not check const-eval models\n"); log("\n"); + log(" -edges\n"); + log(" test cell edges db creator against sat-based implementation\n"); + log("\n"); log(" -v\n"); log(" print additional debug information to the console\n"); log("\n"); @@ -574,6 +724,7 @@ struct TestCellPass : public Pass { bool constmode = false; bool nosat = false; bool noeval = false; + bool edges = false; int argidx; for (argidx = 1; argidx < GetSize(args); argidx++) @@ -604,7 +755,7 @@ struct TestCellPass : public Pass { continue; } if (args[argidx] == "-simlib") { - techmap_cmd = "techmap -map +/simlib.v -max_iter 2 -autoproc"; + techmap_cmd = "techmap -D SIMLIB_NOCHECKS -map +/simlib.v -max_iter 2 -autoproc"; continue; } if (args[argidx] == "-aigmap") { @@ -627,6 +778,10 @@ struct TestCellPass : public Pass { noeval = true; continue; } + if (args[argidx] == "-edges") { + edges = true; + continue; + } if (args[argidx] == "-v") { verbose = true; continue; @@ -690,13 +845,18 @@ struct TestCellPass : public Pass { cell_types["$logic_and"] = "ABSY"; cell_types["$logic_or"] = "ABSY"; - // cell_types["$mux"] = "A"; - // cell_types["$pmux"] = "A"; + if (edges) { + cell_types["$mux"] = "*"; + cell_types["$pmux"] = "*"; + } + // cell_types["$slice"] = "A"; // cell_types["$concat"] = "A"; // cell_types["$assert"] = "A"; + // cell_types["$assume"] = "A"; cell_types["$lut"] = "*"; + cell_types["$sop"] = "*"; cell_types["$alu"] = "ABSY"; cell_types["$lcu"] = "*"; cell_types["$macc"] = "*"; @@ -763,6 +923,9 @@ struct TestCellPass : public Pass { create_gold_module(design, cell_type, cell_types.at(cell_type), constmode, muxdiv); if (!write_prefix.empty()) { Pass::call(design, stringf("write_ilang %s_%s_%05d.il", write_prefix.c_str(), cell_type.c_str()+1, i)); + } else if (edges) { + Pass::call(design, "dump gold"); + run_edges_test(design, verbose); } else { Pass::call(design, stringf("copy gold gate; cd gate; %s; cd ..; opt -fast gate", techmap_cmd.c_str())); if (!nosat) |