diff options
Diffstat (limited to 'passes')
171 files changed, 6702 insertions, 3928 deletions
diff --git a/passes/cmds/Makefile.inc b/passes/cmds/Makefile.inc index 53bfd40c6..16a38b511 100644 --- a/passes/cmds/Makefile.inc +++ b/passes/cmds/Makefile.inc @@ -18,6 +18,7 @@ OBJS += passes/cmds/setattr.o OBJS += passes/cmds/copy.o OBJS += passes/cmds/splice.o OBJS += passes/cmds/scc.o +OBJS += passes/cmds/glift.o OBJS += passes/cmds/torder.o OBJS += passes/cmds/logcmd.o OBJS += passes/cmds/tee.o @@ -40,3 +41,5 @@ endif OBJS += passes/cmds/scratchpad.o OBJS += passes/cmds/logger.o OBJS += passes/cmds/printattrs.o +OBJS += passes/cmds/sta.o +OBJS += passes/cmds/clean_zerowidth.o diff --git a/passes/cmds/add.cc b/passes/cmds/add.cc index a2f4a9100..c09517254 100644 --- a/passes/cmds/add.cc +++ b/passes/cmds/add.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/cmds/autoname.cc b/passes/cmds/autoname.cc index 28d4012c4..6019c6153 100644 --- a/passes/cmds/autoname.cc +++ b/passes/cmds/autoname.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -22,25 +22,20 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -int autoname_worker(Module *module) +int autoname_worker(Module *module, const dict<Wire*, int>& wire_score) { dict<Cell*, pair<int, IdString>> proposed_cell_names; dict<Wire*, pair<int, IdString>> proposed_wire_names; - dict<Wire*, int> wire_score; int best_score = -1; - for (auto cell : module->selected_cells()) - for (auto &conn : cell->connections()) - for (auto bit : conn.second) - if (bit.wire != nullptr) - wire_score[bit.wire]++; - for (auto cell : module->selected_cells()) { if (cell->name[0] == '$') { for (auto &conn : cell->connections()) { - string suffix = stringf("_%s_%s", log_id(cell->type), log_id(conn.first)); + string suffix; for (auto bit : conn.second) if (bit.wire != nullptr && bit.wire->name[0] != '$') { + if (suffix.empty()) + suffix = stringf("_%s_%s", log_id(cell->type), log_id(conn.first)); IdString new_name(bit.wire->name.str() + suffix); int score = wire_score.at(bit.wire); if (cell->output(conn.first)) score = 0; @@ -54,9 +49,11 @@ int autoname_worker(Module *module) } } else { for (auto &conn : cell->connections()) { - string suffix = stringf("_%s", log_id(conn.first)); + string suffix; for (auto bit : conn.second) if (bit.wire != nullptr && bit.wire->name[0] == '$' && !bit.wire->port_id) { + if (suffix.empty()) + suffix = stringf("_%s", log_id(conn.first)); IdString new_name(cell->name.str() + suffix); int score = wire_score.at(bit.wire); if (cell->output(conn.first)) score = 0; @@ -118,10 +115,17 @@ struct AutonamePass : public Pass { for (auto module : design->selected_modules()) { + dict<Wire*, int> wire_score; + for (auto cell : module->selected_cells()) + for (auto &conn : cell->connections()) + for (auto bit : conn.second) + if (bit.wire != nullptr) + wire_score[bit.wire]++; + int count = 0, iter = 0; while (1) { iter++; - int n = autoname_worker(module); + int n = autoname_worker(module, wire_score); if (!n) break; count += n; } diff --git a/passes/cmds/blackbox.cc b/passes/cmds/blackbox.cc index 08a635514..43670efaf 100644 --- a/passes/cmds/blackbox.cc +++ b/passes/cmds/blackbox.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -46,10 +46,11 @@ struct BlackboxPass : public Pass { } extra_args(args, argidx, design); - for (auto module : design->selected_whole_modules_warn()) + for (auto module : design->selected_whole_modules_warn(true)) { module->makeblackbox(); module->set_bool_attribute(ID::blackbox); + module->set_bool_attribute(ID::whitebox, false); } } } BlackboxPass; diff --git a/passes/cmds/bugpoint.cc b/passes/cmds/bugpoint.cc index da81e7f09..7b621504d 100644 --- a/passes/cmds/bugpoint.cc +++ b/passes/cmds/bugpoint.cc @@ -38,6 +38,8 @@ struct BugpointPass : public Pass { log("and the same script, repeating these steps while it can find a smaller design that\n"); log("still causes a crash. Once this command finishes, it replaces the current design\n"); log("with the smallest testcase it was able to produce.\n"); + log("In order to save the reduced testcase you must write this out to a file with\n"); + log("another command after `bugpoint` like `write_rtlil` or `write_verilog`.\n"); log("\n"); log(" -script <filename> | -command \"<command>\"\n"); log(" use this script file or command to crash Yosys. required.\n"); @@ -87,9 +89,12 @@ struct BugpointPass : public Pass { log(" -updates\n"); log(" try to remove process updates from syncs.\n"); log("\n"); + log(" -runner \"<prefix>\"\n"); + log(" child process wrapping command, e.g., \"timeout 30\", or valgrind.\n"); + log("\n"); } - bool run_yosys(RTLIL::Design *design, string yosys_cmd, string yosys_arg) + bool run_yosys(RTLIL::Design *design, string runner, string yosys_cmd, string yosys_arg) { design->sort(); @@ -97,7 +102,7 @@ struct BugpointPass : public Pass { RTLIL_BACKEND::dump_design(f, design, /*only_selected=*/false, /*flag_m=*/true, /*flag_n=*/false); f.close(); - string yosys_cmdline = stringf("%s -qq -L bugpoint-case.log %s bugpoint-case.il", yosys_cmd.c_str(), yosys_arg.c_str()); + string yosys_cmdline = stringf("%s %s -qq -L bugpoint-case.log %s bugpoint-case.il", runner.c_str(), yosys_cmd.c_str(), yosys_arg.c_str()); return run_command(yosys_cmdline) == 0; } @@ -270,7 +275,7 @@ struct BugpointPass : public Pass { if (mod->get_blackbox_attribute()) continue; - RTLIL::IdString removed_process; + RTLIL::Process *removed_process = nullptr; for (auto process : mod->processes) { if (process.second->get_bool_attribute(ID::bugpoint_keep)) @@ -279,13 +284,12 @@ struct BugpointPass : public Pass { if (index++ == seed) { log_header(design, "Trying to remove process %s.%s.\n", log_id(mod), log_id(process.first)); - removed_process = process.first; + removed_process = process.second; break; } } - if (!removed_process.empty()) { - delete mod->processes[removed_process]; - mod->processes.erase(removed_process); + if (removed_process) { + mod->remove(removed_process); return design_copy; } } @@ -339,6 +343,23 @@ struct BugpointPass : public Pass { return design_copy; } } + int i = 0; + for (auto it = sy->mem_write_actions.begin(); it != sy->mem_write_actions.end(); ++it, ++i) + { + if (index++ == seed) + { + log_header(design, "Trying to remove sync %s memwr %s %s %s %s in %s.%s.\n", log_signal(sy->signal), log_id(it->memid), log_signal(it->address), log_signal(it->data), log_signal(it->enable), log_id(mod), log_id(pr.first)); + sy->mem_write_actions.erase(it); + // Remove the bit for removed action from other actions' priority masks. + for (auto it2 = sy->mem_write_actions.begin(); it2 != sy->mem_write_actions.end(); ++it2) { + auto &mask = it2->priority_mask; + if (GetSize(mask) > i) { + mask.bits.erase(mask.bits.begin() + i); + } + } + return design_copy; + } + } } } } @@ -356,7 +377,7 @@ struct BugpointPass : public Pass { if (wire->get_bool_attribute(ID::bugpoint_keep)) continue; - if (wire->name.begins_with("$delete_wire")) + if (wire->name.begins_with("$delete_wire") || wire->name.begins_with("$auto$bugpoint")) continue; if (index++ == seed) @@ -377,7 +398,7 @@ struct BugpointPass : public Pass { void execute(std::vector<std::string> args, RTLIL::Design *design) override { - string yosys_cmd = "yosys", yosys_arg, grep; + string yosys_cmd = "yosys", yosys_arg, grep, runner; bool fast = false, clean = false; bool modules = false, ports = false, cells = false, connections = false, processes = false, assigns = false, updates = false, wires = false, has_part = false; @@ -455,6 +476,14 @@ struct BugpointPass : public Pass { has_part = true; continue; } + if (args[argidx] == "-runner" && argidx + 1 < args.size()) { + runner = args[++argidx]; + if (runner.size() && runner.at(0) == '"') { + log_assert(runner.back() == '"'); + runner = runner.substr(1, runner.size() - 2); + } + continue; + } break; } extra_args(args, argidx, design); @@ -478,7 +507,7 @@ struct BugpointPass : public Pass { log_cmd_error("This command only operates on fully selected designs!\n"); RTLIL::Design *crashing_design = clean_design(design, clean); - if (run_yosys(crashing_design, yosys_cmd, yosys_arg)) + if (run_yosys(crashing_design, runner, yosys_cmd, yosys_arg)) log_cmd_error("The provided script file or command and Yosys binary do not crash on this design!\n"); if (!check_logfile(grep)) log_cmd_error("The provided grep string is not found in the log file!\n"); @@ -495,12 +524,12 @@ struct BugpointPass : public Pass { if (clean) { RTLIL::Design *testcase = clean_design(simplified); - crashes = !run_yosys(testcase, yosys_cmd, yosys_arg); + crashes = !run_yosys(testcase, runner, yosys_cmd, yosys_arg); delete testcase; } else { - crashes = !run_yosys(simplified, yosys_cmd, yosys_arg); + crashes = !run_yosys(simplified, runner, yosys_cmd, yosys_arg); } if (crashes && check_logfile(grep)) diff --git a/passes/cmds/check.cc b/passes/cmds/check.cc index 36febb98a..ee0f0a58f 100644 --- a/passes/cmds/check.cc +++ b/passes/cmds/check.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -141,6 +141,14 @@ struct CheckPass : public Pass { for (auto bit : sigmap(action.second)) if (bit.wire) used_wires.insert(bit); } + for (auto memwr : sync->mem_write_actions) { + for (auto bit : sigmap(memwr.address)) + if (bit.wire) used_wires.insert(bit); + for (auto bit : sigmap(memwr.data)) + if (bit.wire) used_wires.insert(bit); + for (auto bit : sigmap(memwr.enable)) + if (bit.wire) used_wires.insert(bit); + } } } diff --git a/passes/cmds/chformal.cc b/passes/cmds/chformal.cc index a1b3fbef7..d813a449c 100644 --- a/passes/cmds/chformal.cc +++ b/passes/cmds/chformal.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/cmds/chtype.cc b/passes/cmds/chtype.cc index b894f334c..6f9ca9a45 100644 --- a/passes/cmds/chtype.cc +++ b/passes/cmds/chtype.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/cmds/clean_zerowidth.cc b/passes/cmds/clean_zerowidth.cc new file mode 100644 index 000000000..bac6b1521 --- /dev/null +++ b/passes/cmds/clean_zerowidth.cc @@ -0,0 +1,210 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2021 Marcelina KoÅ›cielnicka <mwk@0x04.net> + * + * 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/celltypes.h" +#include "kernel/mem.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct CleanZeroWidthPass : public Pass { + CleanZeroWidthPass() : Pass("clean_zerowidth", "clean zero-width connections from the design") { } + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" clean_zerowidth [selection]\n"); + log("\n"); + log("Fixes the selected cells and processes to contain no zero-width connections.\n"); + log("Depending on the cell type, this may be implemented by removing the connection,\n"); + log("widening it to 1-bit, or removing the cell altogether.\n"); + log("\n"); + } + + void clean_case(RTLIL::CaseRule *cs) + { + std::vector<SigSig> new_actions; + for (auto &action : cs->actions) + if (GetSize(action.first) != 0) + new_actions.push_back(action); + std::swap(new_actions, cs->actions); + for (auto sw : cs->switches) + for (auto scs : sw->cases) + clean_case(scs); + } + + void execute(std::vector<std::string> args, RTLIL::Design *design) override + { + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + break; + } + extra_args(args, argidx, design); + + CellTypes ct; + ct.setup(); + + for (auto module : design->selected_modules()) + { + for (auto cell : module->selected_cells()) + { + if (!ct.cell_known(cell->type)) { + // User-defined cell: just prune zero-width connections. + for (auto it: cell->connections()) { + if (GetSize(it.second) == 0) { + cell->unsetPort(it.first); + } + } + } else if (RTLIL::builtin_ff_cell_types().count(cell->type)) { + // Coarse FF cells: remove if WIDTH == 0 (no outputs). + // This will also trigger on fine cells, so use the Q port + // width instead of actual WIDTH parameter. + if (GetSize(cell->getPort(ID::Q)) == 0) { + module->remove(cell); + } + } else if (cell->type.in(ID($pmux), ID($bmux), ID($demux))) { + // Remove altogether if WIDTH is 0, replace with + // a connection if S_WIDTH is 0. + if (cell->getParam(ID::WIDTH).as_int() == 0) { + module->remove(cell); + } + if (cell->getParam(ID::S_WIDTH).as_int() == 0) { + module->connect(cell->getPort(ID::Y), cell->getPort(ID::A)); + module->remove(cell); + } + } else if (cell->type == ID($concat)) { + // If a concat has a zero-width input: replace with direct + // connection to the other input. + if (cell->getParam(ID::A_WIDTH).as_int() == 0) { + module->connect(cell->getPort(ID::Y), cell->getPort(ID::B)); + module->remove(cell); + } else if (cell->getParam(ID::B_WIDTH).as_int() == 0) { + module->connect(cell->getPort(ID::Y), cell->getPort(ID::A)); + module->remove(cell); + } + } else if (cell->type == ID($fsm)) { + // TODO: not supported + } else if (cell->is_mem_cell()) { + // Skip — will be handled below. + } else if (cell->type == ID($lut)) { + // Zero-width LUT is just a const driver. + if (cell->getParam(ID::WIDTH).as_int() == 0) { + module->connect(cell->getPort(ID::Y), cell->getParam(ID::LUT)[0]); + module->remove(cell); + } + } else if (cell->type == ID($sop)) { + // Zero-width SOP is just a const driver. + if (cell->getParam(ID::WIDTH).as_int() == 0) { + // The value is 1 iff DEPTH is non-0. + bool val = cell->getParam(ID::DEPTH).as_int() != 0; + module->connect(cell->getPort(ID::Y), val); + module->remove(cell); + } + } else if (cell->hasParam(ID::WIDTH)) { + // For cells with WIDTH parameter: remove if zero. + if (cell->getParam(ID::WIDTH).as_int() == 0) { + module->remove(cell); + } + } else if (cell->hasParam(ID::Y_WIDTH)) { + // For most operators: remove if Y width is 0, expand + // A and B to 1-bit if their width is 0. + if (cell->getParam(ID::Y_WIDTH).as_int() == 0) { + module->remove(cell); + } else if (cell->type == ID($macc)) { + // TODO: fixing zero-width A and B not supported. + } else { + if (cell->getParam(ID::A_WIDTH).as_int() == 0) { + cell->setPort(ID::A, State::S0); + cell->setParam(ID::A_WIDTH, 1); + } + if (cell->hasParam(ID::B_WIDTH) && cell->getParam(ID::B_WIDTH).as_int() == 0) { + cell->setPort(ID::B, State::S0); + cell->setParam(ID::B_WIDTH, 1); + } + } + } + } + + // NOTE: Zero-width switch signals are left alone for processes, as there + // is no simple way of cleaning them up. + for (auto &it: module->processes) { + if (!design->selected(module, it.second)) + continue; + clean_case(&it.second->root_case); + for (auto sync : it.second->syncs) { + std::vector<int> swizzle; + std::vector<RTLIL::MemWriteAction> new_memwr_actions; + for (int i = 0; i < GetSize(sync->mem_write_actions); i++) { + auto &memwr = sync->mem_write_actions[i]; + if (GetSize(memwr.data) == 0) + continue; + if (GetSize(memwr.address) == 0) + memwr.address = State::S0; + Const priority_mask; + for (auto x : swizzle) { + priority_mask.bits.push_back(memwr.priority_mask.bits[x]); + } + memwr.priority_mask = priority_mask; + swizzle.push_back(i); + new_memwr_actions.push_back(memwr); + } + std::swap(new_memwr_actions, sync->mem_write_actions); + std::vector<SigSig> new_actions; + for (auto &action : sync->actions) + if (GetSize(action.first) != 0) + new_actions.push_back(action); + std::swap(new_actions, sync->actions); + } + } + + for (auto &mem : Mem::get_selected_memories(module)) { + if (mem.width == 0) { + mem.remove(); + continue; + } + for (auto &init : mem.inits) { + if (GetSize(init.addr) == 0) { + init.addr = State::S0; + } + } + for (auto &port : mem.rd_ports) { + if (GetSize(port.addr) == 0) { + port.addr = State::S0; + } + } + for (auto &port : mem.wr_ports) { + if (GetSize(port.addr) == 0) { + port.addr = State::S0; + } + } + mem.emit(); + } + + std::vector<SigSig> new_conns; + for (auto &conn : module->connections()) + if (GetSize(conn.first) != 0) + new_conns.push_back(conn); + module->new_connections(new_conns); + } + } +} CleanZeroWidthPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/cmds/connect.cc b/passes/cmds/connect.cc index 0cc6cbe52..1bd52aab2 100644 --- a/passes/cmds/connect.cc +++ b/passes/cmds/connect.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -60,7 +60,7 @@ struct ConnectPass : public Pass { log("Unconnect all existing drivers for the specified expression.\n"); log("\n"); log("\n"); - log(" connect [-nomap] -port <cell> <port> <expr>\n"); + log(" connect [-nomap] [-assert] -port <cell> <port> <expr>\n"); log("\n"); log("Connect the specified cell port to the specified cell port.\n"); log("\n"); @@ -72,6 +72,9 @@ struct ConnectPass : public Pass { log("The connect command operates in one module only. Either only one module must\n"); log("be selected or an active module must be set using the 'cd' command.\n"); log("\n"); + log("The -assert option verifies that the connection already exists, instead of\n"); + log("making it.\n"); + log("\n"); log("This command does not operate on module with processes.\n"); log("\n"); } @@ -88,7 +91,7 @@ struct ConnectPass : public Pass { if (!module->processes.empty()) log_cmd_error("Found processes in selected module.\n"); - bool flag_nounset = false, flag_nomap = false; + bool flag_nounset = false, flag_nomap = false, flag_assert = false; std::string set_lhs, set_rhs, unset_expr; std::string port_cell, port_port, port_expr; @@ -104,6 +107,10 @@ struct ConnectPass : public Pass { flag_nomap = true; continue; } + if (arg == "-assert") { + flag_assert = true; + continue; + } if (arg == "-set" && argidx+2 < args.size()) { set_lhs = args[++argidx]; set_rhs = args[++argidx]; @@ -126,7 +133,7 @@ struct ConnectPass : public Pass { if (!flag_nomap) for (auto &it : module->connections()) { std::vector<RTLIL::SigBit> lhs = it.first.to_sigbit_vector(); - std::vector<RTLIL::SigBit> rhs = it.first.to_sigbit_vector(); + std::vector<RTLIL::SigBit> rhs = it.second.to_sigbit_vector(); for (size_t i = 0; i < lhs.size(); i++) if (rhs[i].wire != nullptr) sigmap.add(lhs[i], rhs[i]); @@ -137,6 +144,9 @@ struct ConnectPass : public Pass { if (!unset_expr.empty() || !port_cell.empty()) log_cmd_error("Can't use -set together with -unset and/or -port.\n"); + if (flag_assert) + log_cmd_error("The -assert option is only supported with -port.\n"); + RTLIL::SigSpec sig_lhs, sig_rhs; if (!RTLIL::SigSpec::parse_sel(sig_lhs, design, module, set_lhs)) log_cmd_error("Failed to parse set lhs expression `%s'.\n", set_lhs.c_str()); @@ -157,6 +167,9 @@ struct ConnectPass : public Pass { if (!port_cell.empty() || flag_nounset) log_cmd_error("Can't use -unset together with -port and/or -nounset.\n"); + if (flag_assert) + log_cmd_error("The -assert option is only supported with -port.\n"); + RTLIL::SigSpec sig; if (!RTLIL::SigSpec::parse_sel(sig, design, module, unset_expr)) log_cmd_error("Failed to parse unset expression `%s'.\n", unset_expr.c_str()); @@ -177,7 +190,14 @@ struct ConnectPass : public Pass { if (!RTLIL::SigSpec::parse_sel(sig, design, module, port_expr)) log_cmd_error("Failed to parse port expression `%s'.\n", port_expr.c_str()); - module->cell(RTLIL::escape_id(port_cell))->setPort(RTLIL::escape_id(port_port), sigmap(sig)); + if (!flag_assert) { + module->cell(RTLIL::escape_id(port_cell))->setPort(RTLIL::escape_id(port_port), sigmap(sig)); + } else { + SigSpec cur = module->cell(RTLIL::escape_id(port_cell))->getPort(RTLIL::escape_id(port_port)); + if (sigmap(sig) != sigmap(cur)) { + log_cmd_error("Expected connection not present: expected %s, found %s.\n", log_signal(sig), log_signal(cur)); + } + } } else log_cmd_error("Expected -set, -unset, or -port.\n"); diff --git a/passes/cmds/connwrappers.cc b/passes/cmds/connwrappers.cc index 9235dda2b..dbe23ccf1 100644 --- a/passes/cmds/connwrappers.cc +++ b/passes/cmds/connwrappers.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/cmds/copy.cc b/passes/cmds/copy.cc index c351065f3..e3fb3a0e6 100644 --- a/passes/cmds/copy.cc +++ b/passes/cmds/copy.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/cmds/cover.cc b/passes/cmds/cover.cc index 0867e3b4f..1db3e2ca0 100644 --- a/passes/cmds/cover.cc +++ b/passes/cmds/cover.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2014 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2014 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/cmds/delete.cc b/passes/cmds/delete.cc index 684fa37b0..e341f29d6 100644 --- a/passes/cmds/delete.cc +++ b/passes/cmds/delete.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -90,7 +90,7 @@ struct DeletePass : public Pass { pool<RTLIL::Wire*> delete_wires; pool<RTLIL::Cell*> delete_cells; - pool<RTLIL::IdString> delete_procs; + pool<RTLIL::Process*> delete_procs; pool<RTLIL::IdString> delete_mems; for (auto wire : module->selected_wires()) @@ -103,14 +103,14 @@ struct DeletePass : public Pass { for (auto cell : module->cells()) { if (design->selected(module, cell)) delete_cells.insert(cell); - if (cell->type.in(ID($memrd), ID($memwr)) && + if (cell->has_memid() && delete_mems.count(cell->parameters.at(ID::MEMID).decode_string()) != 0) delete_cells.insert(cell); } for (auto &it : module->processes) if (design->selected(module, it.second)) - delete_procs.insert(it.first); + delete_procs.insert(it.second); for (auto &it : delete_mems) { delete module->memories.at(it); @@ -120,10 +120,8 @@ struct DeletePass : public Pass { for (auto &it : delete_cells) module->remove(it); - for (auto &it : delete_procs) { - delete module->processes.at(it); - module->processes.erase(it); - } + for (auto &it : delete_procs) + module->remove(it); module->remove(delete_wires); diff --git a/passes/cmds/design.cc b/passes/cmds/design.cc index 2d7ba1fef..169f7cc4a 100644 --- a/passes/cmds/design.cc +++ b/passes/cmds/design.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/cmds/edgetypes.cc b/passes/cmds/edgetypes.cc index 37c420400..5b53f50cc 100644 --- a/passes/cmds/edgetypes.cc +++ b/passes/cmds/edgetypes.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/cmds/exec.cc b/passes/cmds/exec.cc index 951fa53fc..f00629a02 100644 --- a/passes/cmds/exec.cc +++ b/passes/cmds/exec.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 - 2020 Claire Wolf <claire@symbioticeda.com> + * Copyright (C) 2012 - 2020 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/cmds/glift.cc b/passes/cmds/glift.cc new file mode 100644 index 000000000..b398c3e04 --- /dev/null +++ b/passes/cmds/glift.cc @@ -0,0 +1,599 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2020 Alberto Gonzalez <boqwxp@airmail.cc> + * + * 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/rtlil.h" +#include "kernel/utils.h" +#include "kernel/log.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct GliftWorker { +private: + bool is_top_module = false; + bool opt_create_precise_model = false, opt_create_imprecise_model = false, opt_create_instrumented_model = false; + bool opt_taintconstants = false, opt_keepoutputs = false, opt_simplecostmodel = false, opt_nocostmodel = false; + bool opt_instrumentmore = false; + std::vector<RTLIL::Wire *> new_taint_outputs; + std::vector<std::pair<RTLIL::SigSpec, RTLIL::IdString>> meta_mux_selects; + RTLIL::Module *module = nullptr; + + const RTLIL::IdString cost_model_wire_name = ID(__glift_weight); + const RTLIL::IdString glift_attribute_name = ID(glift); + + + RTLIL::SigSpec get_corresponding_taint_signal(RTLIL::SigSpec sig) { + RTLIL::SigSpec ret; + + //Get the connected wire for the cell port: + log_assert(sig.is_wire() || sig.is_fully_const()); + log_assert(sig.is_wire() || sig.is_fully_const()); + + //Get a SigSpec for the corresponding taint signal for the cell port, creating one if necessary: + if (sig.is_wire()) { + RTLIL::Wire *w = module->wire(sig.as_wire()->name.str() + "_t"); + if (w == nullptr) w = module->addWire(sig.as_wire()->name.str() + "_t", 1); + ret = w; + } + else if (sig.is_fully_const() && opt_taintconstants) + ret = RTLIL::State::S1; + else if (sig.is_fully_const()) + ret = RTLIL::State::S0; + else + log_cmd_error("Cell port SigSpec has unexpected type.\n"); + + //Finally, if the cell port was a module input or output, make sure the corresponding taint signal is marked, too: + if(sig.is_wire() && sig.as_wire()->port_input) + ret.as_wire()->port_input = true; + if(sig.is_wire() && sig.as_wire()->port_output) + new_taint_outputs.push_back(ret.as_wire()); + + return ret; + } + + void add_precise_GLIFT_logic(const RTLIL::Cell *cell, RTLIL::SigSpec &port_a, RTLIL::SigSpec &port_a_taint, RTLIL::SigSpec &port_b, RTLIL::SigSpec &port_b_taint, RTLIL::SigSpec &port_y_taint) { + //AKA AN2_SH2 or OR2_SH2 + bool is_and = cell->type.in(ID($_AND_), ID($_NAND_)); + RTLIL::SigSpec n_port_a = module->LogicNot(cell->name.str() + "_t_1_1", port_a, false, cell->get_src_attribute()); + RTLIL::SigSpec n_port_b = module->LogicNot(cell->name.str() + "_t_1_2", port_b, false, cell->get_src_attribute()); + auto subexpr1 = module->And(cell->name.str() + "_t_1_3", is_and? port_a : n_port_a, port_b_taint, false, cell->get_src_attribute()); + auto subexpr2 = module->And(cell->name.str() + "_t_1_4", is_and? port_b : n_port_b, port_a_taint, false, cell->get_src_attribute()); + auto subexpr3 = module->And(cell->name.str() + "_t_1_5", port_a_taint, port_b_taint, false, cell->get_src_attribute()); + auto subexpr4 = module->Or(cell->name.str() + "_t_1_6", subexpr1, subexpr2, false, cell->get_src_attribute()); + module->addOr(cell->name.str() + "_t_1_7", subexpr4, subexpr3, port_y_taint, false, cell->get_src_attribute()); + } + + void add_imprecise_GLIFT_logic_1(const RTLIL::Cell *cell, RTLIL::SigSpec &port_a, RTLIL::SigSpec &port_a_taint, RTLIL::SigSpec &port_b, RTLIL::SigSpec &port_b_taint, RTLIL::SigSpec &port_y_taint) { + //AKA AN2_SH3 or OR2_SH3 + bool is_and = cell->type.in(ID($_AND_), ID($_NAND_)); + RTLIL::SigSpec n_port_a = module->LogicNot(cell->name.str() + "_t_2_1", port_a, false, cell->get_src_attribute()); + auto subexpr1 = module->And(cell->name.str() + "_t_2_2", is_and? port_b : n_port_a, is_and? port_a_taint : port_b_taint, false, cell->get_src_attribute()); + module->addOr(cell->name.str() + "_t_2_3", is_and? port_b_taint : port_a_taint, subexpr1, port_y_taint, false, cell->get_src_attribute()); + } + + void add_imprecise_GLIFT_logic_2(const RTLIL::Cell *cell, RTLIL::SigSpec &port_a, RTLIL::SigSpec &port_a_taint, RTLIL::SigSpec &port_b, RTLIL::SigSpec &port_b_taint, RTLIL::SigSpec &port_y_taint) { + //AKA AN2_SH4 or OR2_SH4 + bool is_and = cell->type.in(ID($_AND_), ID($_NAND_)); + RTLIL::SigSpec n_port_b = module->LogicNot(cell->name.str() + "_t_3_1", port_b, false, cell->get_src_attribute()); + auto subexpr1 = module->And(cell->name.str() + "_t_3_2", is_and? port_a : n_port_b, is_and? port_b_taint : port_a_taint, false, cell->get_src_attribute()); + module->addOr(cell->name.str() + "_t_3_3", is_and? port_a_taint : port_b_taint, subexpr1, port_y_taint, false, cell->get_src_attribute()); + } + + void add_imprecise_GLIFT_logic_3(const RTLIL::Cell *cell, RTLIL::SigSpec &port_a_taint, RTLIL::SigSpec &port_b_taint, RTLIL::SigSpec &port_y_taint) { + //AKA AN2_SH5 or OR2_SH5 or XR2_SH2 + module->addOr(cell->name.str() + "_t_4_1", port_a_taint, port_b_taint, port_y_taint, false, cell->get_src_attribute()); + } + + void add_imprecise_GLIFT_logic_4(RTLIL::SigSpec &port_a_taint, RTLIL::SigSpec &port_y_taint) { + module->connect(port_y_taint, port_a_taint); + } + + void add_imprecise_GLIFT_logic_5(RTLIL::SigSpec &port_b_taint, RTLIL::SigSpec &port_y_taint) { + module->connect(port_y_taint, port_b_taint); + } + + void add_imprecise_GLIFT_logic_6(RTLIL::SigSpec &port_y_taint) { + module->connect(port_y_taint, RTLIL::Const(1, 1)); + } + + void add_imprecise_GLIFT_logic_7(RTLIL::SigSpec &port_y_taint) { + module->connect(port_y_taint, RTLIL::Const(0, 1)); + } + + void add_precise_GLIFT_mux(const RTLIL::Cell *cell, RTLIL::SigSpec &port_a, RTLIL::SigSpec &port_a_taint, RTLIL::SigSpec &port_b, RTLIL::SigSpec &port_b_taint, RTLIL::SigSpec &port_s, RTLIL::SigSpec &port_s_taint, RTLIL::SigSpec &port_y_taint) { + //S&At | ~S&Bt | ~A&B&St | A&~B&St | At&St | Bt&St + RTLIL::SigSpec n_port_a = module->LogicNot(cell->name.str() + "_t_4_1", port_a, false, cell->get_src_attribute()); + RTLIL::SigSpec n_port_b = module->LogicNot(cell->name.str() + "_t_4_2", port_b, false, cell->get_src_attribute()); + RTLIL::SigSpec n_port_s = module->LogicNot(cell->name.str() + "_t_4_3", port_s, false, cell->get_src_attribute()); + auto subexpr1 = module->And(cell->name.str() + "_t_4_4", port_s, port_a_taint, false, cell->get_src_attribute()); + auto subexpr2 = module->And(cell->name.str() + "_t_4_5", n_port_s, port_b_taint, false, cell->get_src_attribute()); + auto subexpr3 = module->And(cell->name.str() + "_t_4_6", n_port_a, port_b, false, cell->get_src_attribute()); + auto subexpr4 = module->And(cell->name.str() + "_t_4_7", subexpr3, port_s_taint, false, cell->get_src_attribute()); + auto subexpr5 = module->And(cell->name.str() + "_t_4_8", port_a, n_port_b, false, cell->get_src_attribute()); + auto subexpr6 = module->And(cell->name.str() + "_t_4_9", subexpr5, port_s_taint, false, cell->get_src_attribute()); + auto subexpr7 = module->And(cell->name.str() + "_t_4_10", port_a_taint, port_s_taint, false, cell->get_src_attribute()); + auto subexpr8 = module->And(cell->name.str() + "_t_4_11", port_b_taint, port_s_taint, false, cell->get_src_attribute()); + auto subexpr9 = module->Or(cell->name.str() + "_t_4_12", subexpr1, subexpr2, false, cell->get_src_attribute()); + auto subexpr10 = module->Or(cell->name.str() + "_t_4_13", subexpr4, subexpr6, false, cell->get_src_attribute()); + auto subexpr11 = module->Or(cell->name.str() + "_t_4_14", subexpr7, subexpr8, false, cell->get_src_attribute()); + auto subexpr12 = module->Or(cell->name.str() + "_t_4_15", subexpr9, subexpr10, false, cell->get_src_attribute()); + module->addOr(cell->name.str() + "_t_4_16", subexpr11, subexpr12, port_y_taint, false, cell->get_src_attribute()); + } + + RTLIL::SigSpec score_metamux_select(const RTLIL::SigSpec &metamux_select, const RTLIL::IdString celltype) { + log_assert(metamux_select.is_wire()); + + if (opt_simplecostmodel) { + //The complex model is an area model, so a lower score should mean smaller. + //In this case, a nonzero hole metamux select value means less logic. + //Thus we should invert the ReduceOr over the metamux_select signal. + RTLIL::SigSpec pmux_select = module->ReduceOr(metamux_select.as_wire()->name.str() + "_nonzero", metamux_select); + return module->Pmux(NEW_ID, RTLIL::Const(1), RTLIL::Const(0), pmux_select, metamux_select.as_wire()->get_src_attribute()); + } else { + auto select_width = metamux_select.as_wire()->width; + + std::vector<RTLIL::Const> costs; + if (celltype == ID($_AND_) || celltype == ID($_OR_)) { + costs = {5, 2, 2, 1, 0, 0, 0, 0}; + log_assert(select_width == 2 || select_width == 3); + log_assert(opt_instrumentmore || select_width == 2); + log_assert(!opt_instrumentmore || select_width == 3); + } + else if (celltype == ID($_XOR_) || celltype == ID($_XNOR_)) { + costs = {1, 0, 0, 0}; + log_assert(select_width == 2); + } + + std::vector<RTLIL::SigSpec> next_pmux_y_ports, pmux_y_ports(costs.begin(), costs.begin() + exp2(select_width)); + for (auto i = 0; pmux_y_ports.size() > 1; ++i) { + for (auto j = 0; j+1 < GetSize(pmux_y_ports); j += 2) { + next_pmux_y_ports.emplace_back(module->Pmux(stringf("%s_mux_%d_%d", metamux_select.as_wire()->name.c_str(), i, j), pmux_y_ports[j], pmux_y_ports[j+1], metamux_select[GetSize(metamux_select) - 1 - i], metamux_select.as_wire()->get_src_attribute())); + } + if (GetSize(pmux_y_ports) % 2 == 1) + next_pmux_y_ports.push_back(pmux_y_ports[GetSize(pmux_y_ports) - 1]); + pmux_y_ports.swap(next_pmux_y_ports); + next_pmux_y_ports.clear(); + } + + log_assert(pmux_y_ports.size() == 1); + return pmux_y_ports[0]; + } + } + + void create_glift_logic() { + if (module->get_bool_attribute(glift_attribute_name)) + return; + + std::vector<RTLIL::SigSig> connections(module->connections()); + + for(auto &cell : module->cells().to_vector()) { + if (!cell->type.in({ID($_AND_), ID($_NAND_), ID($_OR_), ID($_NOR_), ID($_XOR_), ID($_XNOR_), ID($_MUX_), ID($_NMUX_), ID($_NOT_), ID($anyconst), ID($allconst), ID($assume), ID($assert)}) && module->design->module(cell->type) == nullptr) { + log_cmd_error("Unsupported cell type \"%s\" found. Run `techmap` first.\n", cell->type.c_str()); + } + if (cell->type.in(ID($_AND_), ID($_NAND_), ID($_OR_), ID($_NOR_))) { + const unsigned int A = 0, B = 1, Y = 2; + const unsigned int NUM_PORTS = 3; + RTLIL::SigSpec ports[NUM_PORTS] = {cell->getPort(ID::A), cell->getPort(ID::B), cell->getPort(ID::Y)}; + RTLIL::SigSpec port_taints[NUM_PORTS]; + + if (ports[A].size() != 1 || ports[B].size() != 1 || ports[Y].size() != 1) + log_cmd_error("Multi-bit signal found. Run `splitnets` first.\n"); + for (unsigned int i = 0; i < NUM_PORTS; ++i) + port_taints[i] = get_corresponding_taint_signal(ports[i]); + + if (opt_create_precise_model) + add_precise_GLIFT_logic(cell, ports[A], port_taints[A], ports[B], port_taints[B], port_taints[Y]); + else if (opt_create_imprecise_model) + add_imprecise_GLIFT_logic_3(cell, port_taints[A], port_taints[B], port_taints[Y]); + else if (opt_create_instrumented_model) { + std::vector<RTLIL::SigSpec> taint_version; + int num_versions = opt_instrumentmore? 8 : 4; + + for (auto i = 1; i <= num_versions; ++i) + taint_version.emplace_back(RTLIL::SigSpec(module->addWire(stringf("%s_y%d", cell->name.c_str(), i), 1))); + + for (auto i = 0; i < num_versions; ++i) { + switch(i) { + case 0: add_precise_GLIFT_logic(cell, ports[A], port_taints[A], ports[B], port_taints[B], taint_version[i]); + break; + case 1: add_imprecise_GLIFT_logic_1(cell, ports[A], port_taints[A], ports[B], port_taints[B], taint_version[i]); + break; + case 2: add_imprecise_GLIFT_logic_2(cell, ports[A], port_taints[A], ports[B], port_taints[B], taint_version[i]); + break; + case 3: add_imprecise_GLIFT_logic_3(cell, port_taints[A], port_taints[B], taint_version[i]); + break; + case 4: add_imprecise_GLIFT_logic_4(port_taints[A], taint_version[i]); + break; + case 5: add_imprecise_GLIFT_logic_5(port_taints[B], taint_version[i]); + break; + case 6: add_imprecise_GLIFT_logic_6(taint_version[i]); + break; + case 7: add_imprecise_GLIFT_logic_7(taint_version[i]); + break; + default: log_assert(false); + } + } + + auto select_width = log2(num_versions); + log_assert(exp2(select_width) == num_versions); + RTLIL::SigSpec meta_mux_select(module->addWire(cell->name.str() + "_sel", select_width)); + meta_mux_selects.push_back(make_pair(meta_mux_select, cell->type)); + module->connect(meta_mux_select, module->Anyconst(cell->name.str() + "_hole", select_width, cell->get_src_attribute())); + + std::vector<RTLIL::SigSpec> next_meta_mux_y_ports, meta_mux_y_ports(taint_version); + for (auto i = 0; meta_mux_y_ports.size() > 1; ++i) { + for (auto j = 0; j+1 < GetSize(meta_mux_y_ports); j += 2) { + next_meta_mux_y_ports.emplace_back(module->Mux(stringf("%s_mux_%d_%d", cell->name.c_str(), i, j), meta_mux_y_ports[j], meta_mux_y_ports[j+1], meta_mux_select[GetSize(meta_mux_select) - 1 - i])); + } + if (GetSize(meta_mux_y_ports) % 2 == 1) + next_meta_mux_y_ports.push_back(meta_mux_y_ports[GetSize(meta_mux_y_ports) - 1]); + meta_mux_y_ports.swap(next_meta_mux_y_ports); + next_meta_mux_y_ports.clear(); + } + log_assert(meta_mux_y_ports.size() == 1); + module->connect(port_taints[Y], meta_mux_y_ports[0]); + } + else log_cmd_error("This is a bug (1).\n"); + } + else if (cell->type.in(ID($_XOR_), ID($_XNOR_))) { + const unsigned int A = 0, B = 1, Y = 2; + const unsigned int NUM_PORTS = 3; + RTLIL::SigSpec ports[NUM_PORTS] = {cell->getPort(ID::A), cell->getPort(ID::B), cell->getPort(ID::Y)}; + RTLIL::SigSpec port_taints[NUM_PORTS]; + + if (ports[A].size() != 1 || ports[B].size() != 1 || ports[Y].size() != 1) + log_cmd_error("Multi-bit signal found. Run `splitnets` first.\n"); + for (unsigned int i = 0; i < NUM_PORTS; ++i) + port_taints[i] = get_corresponding_taint_signal(ports[i]); + + if (opt_create_precise_model || opt_create_imprecise_model) + add_imprecise_GLIFT_logic_3(cell, port_taints[A], port_taints[B], port_taints[Y]); + else if (opt_create_instrumented_model) { + std::vector<RTLIL::SigSpec> taint_version; + int num_versions = 4; + auto select_width = log2(num_versions); + log_assert(exp2(select_width) == num_versions); + + for (auto i = 1; i <= num_versions; ++i) + taint_version.emplace_back(RTLIL::SigSpec(module->addWire(stringf("%s_y%d", cell->name.c_str(), i), 1))); + + for (auto i = 0; i < num_versions; ++i) { + switch(i) { + case 0: add_imprecise_GLIFT_logic_3(cell, port_taints[A], port_taints[B], taint_version[i]); + break; + case 1: add_imprecise_GLIFT_logic_4(port_taints[A], taint_version[i]); + break; + case 2: add_imprecise_GLIFT_logic_5(port_taints[B], taint_version[i]); + break; + case 3: add_imprecise_GLIFT_logic_6(taint_version[i]); + break; + default: log_assert(false); + } + } + + RTLIL::SigSpec meta_mux_select(module->addWire(cell->name.str() + "_sel", select_width)); + meta_mux_selects.push_back(make_pair(meta_mux_select, cell->type)); + module->connect(meta_mux_select, module->Anyconst(cell->name.str() + "_hole", select_width, cell->get_src_attribute())); + + std::vector<RTLIL::SigSpec> next_meta_mux_y_ports, meta_mux_y_ports(taint_version); + for (auto i = 0; meta_mux_y_ports.size() > 1; ++i) { + for (auto j = 0; j+1 < GetSize(meta_mux_y_ports); j += 2) { + next_meta_mux_y_ports.emplace_back(module->Mux(stringf("%s_mux_%d_%d", cell->name.c_str(), i, j), meta_mux_y_ports[j], meta_mux_y_ports[j+1], meta_mux_select[GetSize(meta_mux_select) - 1 - i])); + } + if (GetSize(meta_mux_y_ports) % 2 == 1) + next_meta_mux_y_ports.push_back(meta_mux_y_ports[GetSize(meta_mux_y_ports) - 1]); + meta_mux_y_ports.swap(next_meta_mux_y_ports); + next_meta_mux_y_ports.clear(); + } + log_assert(meta_mux_y_ports.size() == 1); + module->connect(port_taints[Y], meta_mux_y_ports[0]); + } + else log_cmd_error("This is a bug (2).\n"); + + } + else if (cell->type.in(ID($_MUX_), ID($_NMUX_))) { + const unsigned int A = 0, B = 1, S = 2, Y = 3; + const unsigned int NUM_PORTS = 4; + RTLIL::SigSpec ports[NUM_PORTS] = {cell->getPort(ID::A), cell->getPort(ID::B), cell->getPort(ID::S), cell->getPort(ID::Y)}; + RTLIL::SigSpec port_taints[NUM_PORTS]; + + if (ports[A].size() != 1 || ports[B].size() != 1 || ports[S].size() != 1 || ports[Y].size() != 1) + log_cmd_error("Multi-bit signal found. Run `splitnets` first.\n"); + for (unsigned int i = 0; i < NUM_PORTS; ++i) + port_taints[i] = get_corresponding_taint_signal(ports[i]); + + add_precise_GLIFT_mux(cell, ports[A], port_taints[A], ports[B], port_taints[B], ports[S], port_taints[S], port_taints[Y]); + } + else if (cell->type.in(ID($_NOT_))) { + const unsigned int A = 0, Y = 1; + const unsigned int NUM_PORTS = 2; + RTLIL::SigSpec ports[NUM_PORTS] = {cell->getPort(ID::A), cell->getPort(ID::Y)}; + RTLIL::SigSpec port_taints[NUM_PORTS]; + + if (ports[A].size() != 1 || ports[Y].size() != 1) + log_cmd_error("Multi-bit signal found. Run `splitnets` first.\n"); + for (unsigned int i = 0; i < NUM_PORTS; ++i) + port_taints[i] = get_corresponding_taint_signal(ports[i]); + + if (cell->type == ID($_NOT_)) { + module->connect(port_taints[Y], port_taints[A]); + } + else log_cmd_error("This is a bug (3).\n"); + } + else if (module->design->module(cell->type) != nullptr) { + //User cell type + //This function is called on modules according to topological order, so we do not need to + //recurse to GLIFT model the child module. However, we need to augment the ports list + //with taint signals and connect the new ports to the corresponding taint signals. + RTLIL::Module *cell_module_def = module->design->module(cell->type); + dict<RTLIL::IdString, RTLIL::SigSpec> orig_ports = cell->connections(); + log("Adding cell %s\n", cell_module_def->name.c_str()); + for (auto &it : orig_ports) { + RTLIL::SigSpec port = it.second; + RTLIL::SigSpec port_taint = get_corresponding_taint_signal(port); + + log_assert(port_taint.is_wire()); + log_assert(std::find(cell_module_def->ports.begin(), cell_module_def->ports.end(), port_taint.as_wire()->name) != cell_module_def->ports.end()); + cell->setPort(port_taint.as_wire()->name, port_taint); + } + } + else log_cmd_error("This is a bug (4).\n"); + } //end foreach cell in cells + + for (auto &conn : connections) { + RTLIL::SigSpec first = get_corresponding_taint_signal(conn.first); + RTLIL::SigSpec second = get_corresponding_taint_signal(conn.second); + + module->connect(first, second); + + if(conn.second.is_wire() && conn.second.as_wire()->port_input) + second.as_wire()->port_input = true; + if(conn.first.is_wire() && conn.first.as_wire()->port_output) + new_taint_outputs.push_back(first.as_wire()); + } //end foreach conn in connections + + //Create a rough model of area by summing the (potentially simplified) "weight" score of each meta-mux select: + if (!opt_nocostmodel) { + std::vector<RTLIL::SigSpec> meta_mux_select_sums; + std::vector<RTLIL::SigSpec> meta_mux_select_sums_buf; + for (auto &it : meta_mux_selects) { + meta_mux_select_sums.emplace_back(score_metamux_select(it.first, it.second)); + } + for (unsigned int i = 0; meta_mux_select_sums.size() > 1; ) { + meta_mux_select_sums_buf.clear(); + for (i = 0; i + 1 < meta_mux_select_sums.size(); i += 2) { + meta_mux_select_sums_buf.push_back(module->Add(meta_mux_select_sums[i].as_wire()->name.str() + "_add", meta_mux_select_sums[i], meta_mux_select_sums[i+1], false)); + } + if (meta_mux_select_sums.size() % 2 == 1) + meta_mux_select_sums_buf.push_back(meta_mux_select_sums[meta_mux_select_sums.size()-1]); + meta_mux_select_sums.swap(meta_mux_select_sums_buf); + } + if (meta_mux_select_sums.size() > 0) { + meta_mux_select_sums[0].as_wire()->set_bool_attribute("\\minimize"); + meta_mux_select_sums[0].as_wire()->set_bool_attribute("\\keep"); + module->rename(meta_mux_select_sums[0].as_wire(), cost_model_wire_name); + } + } + + //Mark new module outputs: + for (auto &port_name : module->ports) { + RTLIL::Wire *port = module->wire(port_name); + log_assert(port != nullptr); + if (is_top_module && port->port_output && !opt_keepoutputs) + port->port_output = false; + } + for (auto &output : new_taint_outputs) + output->port_output = true; + module->fixup_ports(); //we have some new taint signals in the module interface + module->set_bool_attribute(glift_attribute_name, true); + } + +public: + GliftWorker(RTLIL::Module *_module, bool _is_top_module, bool _opt_create_precise_model, bool _opt_create_imprecise_model, bool _opt_create_instrumented_model, bool _opt_taintconstants, bool _opt_keepoutputs, bool _opt_simplecostmodel, bool _opt_nocostmodel, bool _opt_instrumentmore) { + module = _module; + is_top_module = _is_top_module; + opt_create_precise_model = _opt_create_precise_model; + opt_create_imprecise_model = _opt_create_imprecise_model; + opt_create_instrumented_model = _opt_create_instrumented_model; + opt_taintconstants = _opt_taintconstants; + opt_keepoutputs = _opt_keepoutputs; + opt_simplecostmodel = _opt_simplecostmodel; + opt_nocostmodel = _opt_nocostmodel; + opt_instrumentmore = _opt_instrumentmore; + + create_glift_logic(); + } +}; + +struct GliftPass : public Pass { + GliftPass() : Pass("glift", "create GLIFT models and optimization problems") {} + + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" glift <command> [options] [selection]\n"); + log("\n"); + log("Augments the current or specified module with gate-level information flow tracking\n"); + log("(GLIFT) logic using the \"constructive mapping\" approach. Also can set up QBF-SAT\n"); + log("optimization problems in order to optimize GLIFT models or trade off precision and\n"); + log("complexity.\n"); + log("\n"); + log("\n"); + log("Commands:\n"); + log("\n"); + log(" -create-precise-model\n"); + log(" Replaces the current or specified module with one that has corresponding \"taint\"\n"); + log(" inputs, outputs, and internal nets along with precise taint tracking logic.\n"); + log(" For example, precise taint tracking logic for an AND gate is:\n"); + log("\n"); + log(" y_t = a & b_t | b & a_t | a_t & b_t\n"); + log("\n"); + log("\n"); + log(" -create-imprecise-model\n"); + log(" Replaces the current or specified module with one that has corresponding \"taint\"\n"); + log(" inputs, outputs, and internal nets along with imprecise \"All OR\" taint tracking\n"); + log(" logic:\n"); + log("\n"); + log(" y_t = a_t | b_t\n"); + log("\n"); + log("\n"); + log(" -create-instrumented-model\n"); + log(" Replaces the current or specified module with one that has corresponding \"taint\"\n"); + log(" inputs, outputs, and internal nets along with 4 varying-precision versions of taint\n"); + log(" tracking logic. Which version of taint tracking logic is used for a given gate is\n"); + log(" determined by a MUX selected by an $anyconst cell. By default, unless the\n"); + log(" `-no-cost-model` option is provided, an additional wire named `__glift_weight` with\n"); + log(" the `keep` and `minimize` attributes is added to the module along with pmuxes and\n"); + log(" adders to calculate a rough estimate of the number of logic gates in the GLIFT model\n"); + log(" given an assignment for the $anyconst cells. The four versions of taint tracking logic\n"); + log(" for an AND gate are:"); + log("\n"); + log(" y_t = a & b_t | b & a_t | a_t & b_t (like `-create-precise-model`)\n"); + log(" y_t = a_t | a & b_t\n"); + log(" y_t = b_t | b & a_t\n"); + log(" y_t = a_t | b_t (like `-create-imprecise-model`)\n"); + log("\n"); + log("\n"); + log("Options:\n"); + log("\n"); + log(" -taint-constants\n"); + log(" Constant values in the design are labeled as tainted.\n"); + log(" (default: label constants as un-tainted)\n"); + log("\n"); + log(" -keep-outputs\n"); + log(" Do not remove module outputs. Taint tracking outputs will appear in the module ports\n"); + log(" alongside the orignal outputs.\n"); + log(" (default: original module outputs are removed)\n"); + log("\n"); + log(" -simple-cost-model\n"); + log(" Do not model logic area. Instead model the number of non-zero assignments to $anyconsts.\n"); + log(" Taint tracking logic versions vary in their size, but all reduced-precision versions are\n"); + log(" significantly smaller than the fully-precise version. A non-zero $anyconst assignment means\n"); + log(" that reduced-precision taint tracking logic was chosen for some gate.\n"); + log(" Only applicable in combination with `-create-instrumented-model`.\n"); + log(" (default: use a complex model and give that wire the \"keep\" and \"minimize\" attributes)\n"); + log("\n"); + log(" -no-cost-model\n"); + log(" Do not model taint tracking logic area and do not create a `__glift_weight` wire.\n"); + log(" Only applicable in combination with `-create-instrumented-model`.\n"); + log(" (default: model area and give that wire the \"keep\" and \"minimize\" attributes)\n"); + log("\n"); + log(" -instrument-more\n"); + log(" Allow choice from more versions of (even simpler) taint tracking logic. A total\n"); + log(" of 8 versions of taint tracking logic will be added per gate, including the 4\n"); + log(" versions from `-create-instrumented-model` and these additional versions:\n"); + log("\n"); + log(" y_t = a_t\n"); + log(" y_t = b_t\n"); + log(" y_t = 1\n"); + log(" y_t = 0\n"); + log("\n"); + log(" Only applicable in combination with `-create-instrumented-model`.\n"); + log(" (default: do not add more versions of taint tracking logic.\n"); + log("\n"); + } + + void execute(std::vector<std::string> args, RTLIL::Design *design) override + { + bool opt_create_precise_model = false, opt_create_imprecise_model = false, opt_create_instrumented_model = false; + bool opt_taintconstants = false, opt_keepoutputs = false, opt_simplecostmodel = false, opt_nocostmodel = false; + bool opt_instrumentmore = false; + log_header(design, "Executing GLIFT pass (creating and manipulating GLIFT models).\n"); + std::vector<std::string>::size_type argidx; + + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-create-precise-model") { + opt_create_precise_model = true; + continue; + } + if (args[argidx] == "-create-imprecise-model") { + opt_create_imprecise_model = true; + continue; + } + if (args[argidx] == "-create-instrumented-model") { + opt_create_instrumented_model = true; + continue; + } + if (args[argidx] == "-taint-constants") { + opt_taintconstants = true; + continue; + } + if (args[argidx] == "-keep-outputs") { + opt_keepoutputs = true; + continue; + } + if (args[argidx] == "-simple-cost-model") { + opt_simplecostmodel = true; + continue; + } + if (args[argidx] == "-no-cost-model") { + opt_nocostmodel = true; + continue; + } + if (args[argidx] == "-instrument-more") { + opt_instrumentmore = true; + continue; + } + break; + } + if(!opt_create_precise_model && !opt_create_imprecise_model && !opt_create_instrumented_model) + log_cmd_error("No command provided. See help for usage.\n"); + if(static_cast<int>(opt_create_precise_model) + static_cast<int>(opt_create_imprecise_model) + static_cast<int>(opt_create_instrumented_model) != 1) + log_cmd_error("Only one command may be specified. See help for usage.\n"); + if(opt_simplecostmodel && opt_nocostmodel) + log_cmd_error("Only one of `-simple-cost-model` and `-no-cost-model` may be specified. See help for usage.\n"); + if((opt_simplecostmodel || opt_nocostmodel) && !opt_create_instrumented_model) + log_cmd_error("Options `-simple-cost-model` and `-no-cost-model` may only be used with `-create-instrumented-model`. See help for usage.\n"); + extra_args(args, argidx, design); + + if (GetSize(design->selected_modules()) == 0) + log_cmd_error("Can't operate on an empty selection!\n"); + + TopoSort<RTLIL::Module*, IdString::compare_ptr_by_name<RTLIL::Module>> topo_modules; //cribbed from passes/techmap/flatten.cc + auto worklist = design->selected_modules(); + pool<RTLIL::IdString> non_top_modules; + while (!worklist.empty()) { + RTLIL::Module *module = *(worklist.begin()); + worklist.erase(worklist.begin()); + topo_modules.node(module); + + for (auto cell : module->selected_cells()) { + RTLIL::Module *tpl = design->module(cell->type); + if (tpl != nullptr) { + if (topo_modules.database.count(tpl) == 0) + worklist.push_back(tpl); + topo_modules.edge(tpl, module); + non_top_modules.insert(cell->type); + } + } + } + + if (!topo_modules.sort()) + log_cmd_error("Cannot handle recursive module instantiations.\n"); + + for (auto i = 0; i < GetSize(topo_modules.sorted); ++i) { + RTLIL::Module *module = topo_modules.sorted[i]; + GliftWorker(module, !non_top_modules[module->name], opt_create_precise_model, opt_create_imprecise_model, opt_create_instrumented_model, opt_taintconstants, opt_keepoutputs, opt_simplecostmodel, opt_nocostmodel, opt_instrumentmore); + } + } +} GliftPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/cmds/logcmd.cc b/passes/cmds/logcmd.cc index 12c43ecec..f1702400d 100644 --- a/passes/cmds/logcmd.cc +++ b/passes/cmds/logcmd.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * Copyright (C) 2014 Johann Glaser <Johann.Glaser@gmx.at> * * Permission to use, copy, modify, and/or distribute this software for any diff --git a/passes/cmds/logger.cc b/passes/cmds/logger.cc index 6a9ed6036..ec92f1d01 100644 --- a/passes/cmds/logger.cc +++ b/passes/cmds/logger.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2020 Miodrag Milanovic <clifford@clifford.at> + * Copyright (C) 2020 Miodrag Milanovic <micko@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -64,6 +64,11 @@ struct LoggerPass : public Pass { log(" -expect-no-warnings\n"); log(" gives error in case there is at least one warning that is not expected.\n"); log("\n"); + log(" -check-expected\n"); + log(" verifies that the patterns previously set up by -expect have actually\n"); + log(" been met, then clears the expected log list. If this is not called\n"); + log(" manually, the check will happen at yosys exist time instead.\n"); + log("\n"); } void execute(std::vector<std::string> args, RTLIL::Design * design) override @@ -176,6 +181,10 @@ struct LoggerPass : public Pass { log_expect_no_warnings = true; continue; } + if (args[argidx] == "-check-expected") { + log_check_expected(); + continue; + } break; } extra_args(args, argidx, design, false); diff --git a/passes/cmds/ltp.cc b/passes/cmds/ltp.cc index 39ec432c2..22bdaab44 100644 --- a/passes/cmds/ltp.cc +++ b/passes/cmds/ltp.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/cmds/plugin.cc b/passes/cmds/plugin.cc index a94769bcd..3a1ae2850 100644 --- a/passes/cmds/plugin.cc +++ b/passes/cmds/plugin.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2014 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2014 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/cmds/portlist.cc b/passes/cmds/portlist.cc index 97f4bfd99..03048422d 100644 --- a/passes/cmds/portlist.cc +++ b/passes/cmds/portlist.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/cmds/qwp.cc b/passes/cmds/qwp.cc index cf0f6d0de..2da612441 100644 --- a/passes/cmds/qwp.cc +++ b/passes/cmds/qwp.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/cmds/rename.cc b/passes/cmds/rename.cc index f8fe715c8..1d08fc514 100644 --- a/passes/cmds/rename.cc +++ b/passes/cmds/rename.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/cmds/scatter.cc b/passes/cmds/scatter.cc index a70dd3086..017600a46 100644 --- a/passes/cmds/scatter.cc +++ b/passes/cmds/scatter.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/cmds/scc.cc b/passes/cmds/scc.cc index 7aa9a484f..81881832c 100644 --- a/passes/cmds/scc.cc +++ b/passes/cmds/scc.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/cmds/scratchpad.cc b/passes/cmds/scratchpad.cc index 9369f5312..015eb97e7 100644 --- a/passes/cmds/scratchpad.cc +++ b/passes/cmds/scratchpad.cc @@ -1,8 +1,8 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * 2019 Nina Engelhardt <nak@symbioticeda.com> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> + * 2019 N. Engelhardt <nak@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/cmds/select.cc b/passes/cmds/select.cc index b4f3994a2..bb7b78cfe 100644 --- a/passes/cmds/select.cc +++ b/passes/cmds/select.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/cmds/setattr.cc b/passes/cmds/setattr.cc index 3a94209d4..710fa9ab4 100644 --- a/passes/cmds/setattr.cc +++ b/passes/cmds/setattr.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/cmds/setundef.cc b/passes/cmds/setundef.cc index cf8d76619..a078b0b1c 100644 --- a/passes/cmds/setundef.cc +++ b/passes/cmds/setundef.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/cmds/show.cc b/passes/cmds/show.cc index 0c96f8c5d..8f9824f9b 100644 --- a/passes/cmds/show.cc +++ b/passes/cmds/show.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -239,6 +239,19 @@ struct ShowWorker int idx = single_idx_count++; for (int rep, i = int(sig.chunks().size())-1; i >= 0; i -= rep) { const RTLIL::SigChunk &c = sig.chunks().at(i); + int cl, cr; + if (c.wire) { + if (c.wire->upto) { + cr = c.wire->start_offset + (c.wire->width - c.offset - 1); + cl = cr - (c.width - 1); + } else { + cr = c.wire->start_offset + c.offset; + cl = cr + c.width - 1; + } + } else { + cl = c.offset + c.width - 1; + cr = c.offset; + } if (!driver && c.wire == nullptr) { RTLIL::State s1 = c.data.front(); for (auto s2 : c.data) @@ -254,7 +267,7 @@ struct ShowWorker std::string repinfo = rep > 1 ? stringf("%dx ", rep) : ""; if (driver) { log_assert(!net.empty()); - label_string += stringf("<s%d> %d:%d - %s%d:%d |", i, pos, pos-c.width+1, repinfo.c_str(), c.offset+c.width-1, c.offset); + label_string += stringf("<s%d> %d:%d - %s%d:%d |", i, pos, pos-c.width+1, repinfo.c_str(), cl, cr); net_conn_map[net].in.insert(stringf("x%d:s%d", idx, i)); net_conn_map[net].bits = rep*c.width; net_conn_map[net].color = nextColor(c, net_conn_map[net].color); @@ -268,7 +281,7 @@ struct ShowWorker c.data.front() == State::Sz ? 'Z' : '?', pos, pos-rep*c.width+1); } else { - label_string += stringf("<s%d> %s%d:%d - %d:%d |", i, repinfo.c_str(), c.offset+c.width-1, c.offset, pos, pos-rep*c.width+1); + label_string += stringf("<s%d> %s%d:%d - %d:%d |", i, repinfo.c_str(), cl, cr, pos, pos-rep*c.width+1); net_conn_map[net].out.insert(stringf("x%d:s%d", idx, i)); net_conn_map[net].bits = rep*c.width; net_conn_map[net].color = nextColor(c, net_conn_map[net].color); @@ -339,6 +352,11 @@ struct ShowWorker { input_signals.insert(obj->signal); collect_proc_signals(obj->actions, input_signals, output_signals); + for (auto it : obj->mem_write_actions) { + input_signals.insert(it.address); + input_signals.insert(it.data); + input_signals.insert(it.enable); + } } void collect_proc_signals(RTLIL::Process *obj, std::set<RTLIL::SigSpec> &input_signals, std::set<RTLIL::SigSpec> &output_signals) @@ -648,7 +666,7 @@ struct ShowPass : public Pass { log(" (including inout ports) are on the right side.\n"); log("\n"); log(" -pause\n"); - log(" wait for the use to press enter to before returning\n"); + log(" wait for the user to press enter to before returning\n"); log("\n"); log(" -enum\n"); log(" enumerate objects with internal ($-prefixed) names\n"); diff --git a/passes/cmds/splice.cc b/passes/cmds/splice.cc index 0f63b91c5..4ad0d2b25 100644 --- a/passes/cmds/splice.cc +++ b/passes/cmds/splice.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/cmds/splitnets.cc b/passes/cmds/splitnets.cc index fff8a0d3e..927cefca3 100644 --- a/passes/cmds/splitnets.cc +++ b/passes/cmds/splitnets.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/cmds/sta.cc b/passes/cmds/sta.cc new file mode 100644 index 000000000..13e1ee13c --- /dev/null +++ b/passes/cmds/sta.cc @@ -0,0 +1,312 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * (C) 2019 Eddie Hung <eddie@fpgeh.com> + * + * 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" +#include "kernel/timinginfo.h" +#include <deque> + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct StaWorker +{ + Design *design; + Module *module; + SigMap sigmap; + + struct t_data { + Cell* driver; + IdString dst_port, src_port; + vector<tuple<SigBit,int,IdString>> fanouts; + SigBit backtrack; + t_data() : driver(nullptr) {} + }; + dict<SigBit, t_data> data; + std::deque<SigBit> queue; + struct t_endpoint { + Cell *sink; + IdString port; + int required; + t_endpoint() : sink(nullptr), required(0) {} + }; + dict<SigBit, t_endpoint> endpoints; + + int maxarrival; + SigBit maxbit; + + pool<SigBit> driven; + + StaWorker(RTLIL::Module *module) : design(module->design), module(module), sigmap(module), maxarrival(0) + { + TimingInfo timing; + + for (auto cell : module->cells()) + { + Module *inst_module = design->module(cell->type); + if (!inst_module) { + log_warning("Cell type '%s' not recognised! Ignoring.\n", log_id(cell->type)); + continue; + } + + if (!inst_module->get_blackbox_attribute()) { + log_warning("Cell type '%s' is not a black- nor white-box! Ignoring.\n", log_id(cell->type)); + continue; + } + + IdString derived_type = inst_module->derive(design, cell->parameters); + inst_module = design->module(derived_type); + log_assert(inst_module); + + if (!timing.count(derived_type)) { + auto &t = timing.setup_module(inst_module); + if (t.has_inputs && t.comb.empty() && t.arrival.empty() && t.required.empty()) + log_warning("Module '%s' has no timing arcs!\n", log_id(cell->type)); + } + + auto &t = timing.at(derived_type); + if (t.comb.empty() && t.arrival.empty() && t.required.empty()) + continue; + + pool<std::pair<SigBit,TimingInfo::NameBit>> src_bits, dst_bits; + + for (auto &conn : cell->connections()) { + auto rhs = sigmap(conn.second); + for (auto i = 0; i < GetSize(rhs); i++) { + const auto &bit = rhs[i]; + if (!bit.wire) + continue; + TimingInfo::NameBit namebit(conn.first,i); + if (cell->input(conn.first)) { + src_bits.insert(std::make_pair(bit,namebit)); + + auto it = t.required.find(namebit); + if (it == t.required.end()) + continue; + auto r = endpoints.insert(bit); + if (r.second || r.first->second.required < it->second.first) { + r.first->second.sink = cell; + r.first->second.port = conn.first; + r.first->second.required = it->second.first; + } + } + if (cell->output(conn.first)) { + dst_bits.insert(std::make_pair(bit,namebit)); + auto &d = data[bit]; + d.driver = cell; + d.dst_port = conn.first; + driven.insert(bit); + + auto it = t.arrival.find(namebit); + if (it == t.arrival.end()) + continue; + const auto &s = it->second.second; + if (cell->hasPort(s.name)) { + auto s_bit = sigmap(cell->getPort(s.name)[s.offset]); + if (s_bit.wire) + data[s_bit].fanouts.emplace_back(bit,it->second.first,s.name); + } + } + } + } + + for (const auto &s : src_bits) + for (const auto &d : dst_bits) { + auto it = t.comb.find(TimingInfo::BitBit(s.second,d.second)); + if (it == t.comb.end()) + continue; + data[s.first].fanouts.emplace_back(d.first,it->second,s.second.name); + } + } + + for (auto port_name : module->ports) { + auto wire = module->wire(port_name); + if (wire->port_input) { + for (const auto &b : sigmap(wire)) { + queue.emplace_back(b); + driven.insert(b); + } + // All primary inputs to arrive at time zero + wire->set_intvec_attribute(ID::sta_arrival, std::vector<int>(GetSize(wire), 0)); + } + if (wire->port_output) + for (const auto &b : sigmap(wire)) + if (b.wire) + endpoints.insert(b); + } + } + + void run() + { + while (!queue.empty()) { + auto b = queue.front(); + queue.pop_front(); + auto it = data.find(b); + if (it == data.end()) + continue; + const auto& src_arrivals = b.wire->get_intvec_attribute(ID::sta_arrival); + log_assert(GetSize(src_arrivals) == GetSize(b.wire)); + auto src_arrival = src_arrivals[b.offset]; + for (const auto &d : it->second.fanouts) { + const auto &dst_bit = std::get<0>(d); + auto dst_arrivals = dst_bit.wire->get_intvec_attribute(ID::sta_arrival); + if (dst_arrivals.empty()) + dst_arrivals = std::vector<int>(GetSize(dst_bit.wire), -1); + else + log_assert(GetSize(dst_arrivals) == GetSize(dst_bit.wire)); + auto &dst_arrival = dst_arrivals[dst_bit.offset]; + auto new_arrival = src_arrival + std::get<1>(d); + if (dst_arrival < new_arrival) { + auto dst_wire = dst_bit.wire; + dst_arrival = std::max(dst_arrival, new_arrival); + dst_wire->set_intvec_attribute(ID::sta_arrival, dst_arrivals); + queue.emplace_back(dst_bit); + + data[dst_bit].backtrack = b; + data[dst_bit].src_port = std::get<2>(d); + + auto it = endpoints.find(dst_bit); + if (it != endpoints.end()) + new_arrival += it->second.required; + if (new_arrival > maxarrival && driven.count(b)) { + maxarrival = new_arrival; + maxbit = dst_bit; + } + } + } + } + + auto b = maxbit; + if (b == SigBit()) { + log("No timing paths found.\n"); + return; + } + + log("Latest arrival time in '%s' is %d:\n", log_id(module), maxarrival); + auto it = endpoints.find(maxbit); + if (it != endpoints.end() && it->second.sink) + log(" %6d %s (%s.%s)\n", maxarrival, log_id(it->second.sink), log_id(it->second.sink->type), log_id(it->second.port)); + else { + log(" %6d (%s)\n", maxarrival, b.wire->port_output ? "<primary output>" : "<unknown>"); + if (!b.wire->port_output) + log_warning("Critical-path does not terminate in a recognised endpoint.\n"); + } + auto jt = data.find(b); + while (jt != data.end()) { + int arrival = b.wire->get_intvec_attribute(ID::sta_arrival)[b.offset]; + if (jt->second.driver) { + log(" %s\n", log_signal(b)); + log(" %6d %s (%s.%s->%s)\n", arrival, log_id(jt->second.driver), log_id(jt->second.driver->type), log_id(jt->second.src_port), log_id(jt->second.dst_port)); + } + else if (b.wire->port_input) + log(" %6d %s (%s)\n", arrival, log_signal(b), "<primary input>"); + else + log_abort(); + b = jt->second.backtrack; + jt = data.find(b); + } + + std::map<int, unsigned> arrival_histogram; + for (const auto &i : endpoints) { + const auto &b = i.first; + if (!driven.count(b)) + continue; + + if (!b.wire->attributes.count(ID::sta_arrival)) { + log_warning("Endpoint %s.%s has no (* sta_arrival *) value.\n", log_id(module), log_signal(b)); + continue; + } + + auto arrival = b.wire->get_intvec_attribute(ID::sta_arrival)[b.offset]; + if (arrival < 0) { + log_warning("Endpoint %s.%s has no (* sta_arrival *) value.\n", log_id(module), log_signal(b)); + continue; + } + arrival += i.second.required; + arrival_histogram[arrival]++; + } + // Adapted from https://github.com/YosysHQ/nextpnr/blob/affb12cc27ebf409eade062c4c59bb98569d8147/common/timing.cc#L946-L969 + if (arrival_histogram.size() > 0) { + unsigned num_bins = 20; + unsigned bar_width = 60; + auto min_arrival = arrival_histogram.begin()->first; + auto max_arrival = arrival_histogram.rbegin()->first; + auto bin_size = std::max<unsigned>(1, ceil((max_arrival - min_arrival + 1) / float(num_bins))); + std::vector<unsigned> bins(num_bins); + unsigned max_freq = 0; + for (const auto &i : arrival_histogram) { + auto &bin = bins[(i.first - min_arrival) / bin_size]; + bin += i.second; + max_freq = std::max(max_freq, bin); + } + bar_width = std::min(bar_width, max_freq); + + log("\n"); + log("Arrival histogram:\n"); + log(" legend: * represents %d endpoint(s)\n", max_freq / bar_width); + log(" + represents [1,%d) endpoint(s)\n", max_freq / bar_width); + for (int i = num_bins-1; i >= 0; --i) + log("(%6d, %6d] |%s%c\n", min_arrival + bin_size * (i + 1), min_arrival + bin_size * i, + std::string(bins[i] * bar_width / max_freq, '*').c_str(), + (bins[i] * bar_width) % max_freq > 0 ? '+' : ' '); + } + } +}; + +struct StaPass : public Pass { + StaPass() : Pass("sta", "perform static timing analysis") { } + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" sta [options] [selection]\n"); + log("\n"); + log("This command performs static timing analysis on the design. (Only considers\n"); + log("paths within a single module, so the design must be flattened.)\n"); + log("\n"); + } + void execute(std::vector<std::string> args, RTLIL::Design *design) override + { + log_header(design, "Executing STA pass (static timing analysis).\n"); + + /* + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-TODO") { + continue; + } + break; + } + */ + + extra_args(args, 1, design); + + for (Module *module : design->selected_modules()) + { + if (module->has_processes_warn()) + continue; + + StaWorker worker(module); + worker.run(); + } + } +} StaPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/cmds/stat.cc b/passes/cmds/stat.cc index 0d84c73db..fffdda48e 100644 --- a/passes/cmds/stat.cc +++ b/passes/cmds/stat.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -117,10 +117,14 @@ struct statdata_t } else if (cell_type.in(ID($mux), ID($pmux))) cell_type = stringf("%s_%d", cell_type.c_str(), GetSize(cell->getPort(ID::Y))); + else if (cell_type == ID($bmux)) + cell_type = stringf("%s_%d_%d", cell_type.c_str(), GetSize(cell->getPort(ID::Y)), GetSize(cell->getPort(ID::S))); + else if (cell_type == ID($demux)) + cell_type = stringf("%s_%d_%d", cell_type.c_str(), GetSize(cell->getPort(ID::A)), GetSize(cell->getPort(ID::S))); else if (cell_type.in( ID($sr), ID($ff), ID($dff), ID($dffe), ID($dffsr), ID($dffsre), ID($adff), ID($adffe), ID($sdff), ID($sdffe), ID($sdffce), - ID($dlatch), ID($adlatch), ID($dlatchsr))) + ID($aldff), ID($aldffe), ID($dlatch), ID($adlatch), ID($dlatchsr))) cell_type = stringf("%s_%d", cell_type.c_str(), GetSize(cell->getPort(ID::Q))); } diff --git a/passes/cmds/tee.cc b/passes/cmds/tee.cc index 60689fc82..7a1f4a36b 100644 --- a/passes/cmds/tee.cc +++ b/passes/cmds/tee.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2014 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2014 Claire Xenia Wolf <claire@yosyshq.com> * Copyright (C) 2014 Johann Glaser <Johann.Glaser@gmx.at> * * Permission to use, copy, modify, and/or distribute this software for any diff --git a/passes/cmds/torder.cc b/passes/cmds/torder.cc index 30e76081e..1620c0bca 100644 --- a/passes/cmds/torder.cc +++ b/passes/cmds/torder.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -83,7 +83,7 @@ struct TorderPass : public Pass { if (!noautostop && yosys_celltypes.cell_known(cell->type)) { if (conn.first.in(ID::Q, ID::CTRL_OUT, ID::RD_DATA)) continue; - if (cell->type == ID($memrd) && conn.first == ID::DATA) + if (cell->type.in(ID($memrd), ID($memrd_v2)) && conn.first == ID::DATA) continue; } diff --git a/passes/cmds/trace.cc b/passes/cmds/trace.cc index 10742c370..400542776 100644 --- a/passes/cmds/trace.cc +++ b/passes/cmds/trace.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2014 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2014 Claire Xenia Wolf <claire@yosyshq.com> * Copyright (C) 2014 Johann Glaser <Johann.Glaser@gmx.at> * * Permission to use, copy, modify, and/or distribute this software for any diff --git a/passes/cmds/write_file.cc b/passes/cmds/write_file.cc index 3d898a5ef..ea9b3f556 100644 --- a/passes/cmds/write_file.cc +++ b/passes/cmds/write_file.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2014 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2014 Claire Xenia Wolf <claire@yosyshq.com> * Copyright (C) 2014 Johann Glaser <Johann.Glaser@gmx.at> * * Permission to use, copy, modify, and/or distribute this software for any diff --git a/passes/equiv/equiv_add.cc b/passes/equiv/equiv_add.cc index 2abbb59bb..1bcd4a887 100644 --- a/passes/equiv/equiv_add.cc +++ b/passes/equiv/equiv_add.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/equiv/equiv_induct.cc b/passes/equiv/equiv_induct.cc index 37aec50cd..8d882ae83 100644 --- a/passes/equiv/equiv_induct.cc +++ b/passes/equiv/equiv_induct.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -55,7 +55,10 @@ struct EquivInductWorker for (auto cell : cells) { if (!satgen.importCell(cell, step) && !cell_warn_cache.count(cell)) { - log_warning("No SAT model available for cell %s (%s).\n", log_id(cell), log_id(cell->type)); + if (RTLIL::builtin_ff_cell_types().count(cell->type)) + log_warning("No SAT model available for async FF cell %s (%s). Consider running `async2sync` or `clk2fflogic` first.\n", log_id(cell), log_id(cell->type)); + else + log_warning("No SAT model available for cell %s (%s).\n", log_id(cell), log_id(cell->type)); cell_warn_cache.insert(cell); } if (cell->type == ID($equiv)) { diff --git a/passes/equiv/equiv_make.cc b/passes/equiv/equiv_make.cc index 6923ae3d0..7ef2827bf 100644 --- a/passes/equiv/equiv_make.cc +++ b/passes/equiv/equiv_make.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/equiv/equiv_mark.cc b/passes/equiv/equiv_mark.cc index a722b5ed6..97a2a38dd 100644 --- a/passes/equiv/equiv_mark.cc +++ b/passes/equiv/equiv_mark.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/equiv/equiv_miter.cc b/passes/equiv/equiv_miter.cc index e028f806a..6acfe85a9 100644 --- a/passes/equiv/equiv_miter.cc +++ b/passes/equiv/equiv_miter.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/equiv/equiv_purge.cc b/passes/equiv/equiv_purge.cc index a43ecec5a..5b0696d9b 100644 --- a/passes/equiv/equiv_purge.cc +++ b/passes/equiv/equiv_purge.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/equiv/equiv_remove.cc b/passes/equiv/equiv_remove.cc index 89442308b..5d1823e12 100644 --- a/passes/equiv/equiv_remove.cc +++ b/passes/equiv/equiv_remove.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/equiv/equiv_simple.cc b/passes/equiv/equiv_simple.cc index 408c5a793..7621341a7 100644 --- a/passes/equiv/equiv_simple.cc +++ b/passes/equiv/equiv_simple.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -184,8 +184,12 @@ struct EquivSimpleWorker for (auto cell : problem_cells) { auto key = pair<Cell*, int>(cell, step+1); - if (!imported_cells_cache.count(key) && !satgen.importCell(cell, step+1)) - log_cmd_error("No SAT model available for cell %s (%s).\n", log_id(cell), log_id(cell->type)); + if (!imported_cells_cache.count(key) && !satgen.importCell(cell, step+1)) { + if (RTLIL::builtin_ff_cell_types().count(cell->type)) + log_cmd_error("No SAT model available for async FF cell %s (%s). Consider running `async2sync` or `clk2fflogic` first.\n", log_id(cell), log_id(cell->type)); + else + log_cmd_error("No SAT model available for cell %s (%s).\n", log_id(cell), log_id(cell->type)); + } imported_cells_cache.insert(key); } diff --git a/passes/equiv/equiv_status.cc b/passes/equiv/equiv_status.cc index 2db44ea90..b221be27c 100644 --- a/passes/equiv/equiv_status.cc +++ b/passes/equiv/equiv_status.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/equiv/equiv_struct.cc b/passes/equiv/equiv_struct.cc index 9784225db..39604994a 100644 --- a/passes/equiv/equiv_struct.cc +++ b/passes/equiv/equiv_struct.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/fsm/fsm.cc b/passes/fsm/fsm.cc index 21d352407..0c5e624dc 100644 --- a/passes/fsm/fsm.cc +++ b/passes/fsm/fsm.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/fsm/fsm_detect.cc b/passes/fsm/fsm_detect.cc index 97c575ba7..a2d38a0bd 100644 --- a/passes/fsm/fsm_detect.cc +++ b/passes/fsm/fsm_detect.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/fsm/fsm_expand.cc b/passes/fsm/fsm_expand.cc index d6b492af5..239f17f36 100644 --- a/passes/fsm/fsm_expand.cc +++ b/passes/fsm/fsm_expand.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/fsm/fsm_export.cc b/passes/fsm/fsm_export.cc index be6702d7e..65dda1267 100644 --- a/passes/fsm/fsm_export.cc +++ b/passes/fsm/fsm_export.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * Copyright (C) 2012 Martin Schmölzer <martin@schmoelzer.at> * * Permission to use, copy, modify, and/or distribute this software for any diff --git a/passes/fsm/fsm_extract.cc b/passes/fsm/fsm_extract.cc index 082973153..62a9d309e 100644 --- a/passes/fsm/fsm_extract.cc +++ b/passes/fsm/fsm_extract.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/fsm/fsm_info.cc b/passes/fsm/fsm_info.cc index da0982bb9..ff3714021 100644 --- a/passes/fsm/fsm_info.cc +++ b/passes/fsm/fsm_info.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/fsm/fsm_map.cc b/passes/fsm/fsm_map.cc index a30d407f0..df31dbb7a 100644 --- a/passes/fsm/fsm_map.cc +++ b/passes/fsm/fsm_map.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/fsm/fsm_opt.cc b/passes/fsm/fsm_opt.cc index 5fc1fb3bb..f2eb06760 100644 --- a/passes/fsm/fsm_opt.cc +++ b/passes/fsm/fsm_opt.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/fsm/fsm_recode.cc b/passes/fsm/fsm_recode.cc index d4a704270..3dc29b5a0 100644 --- a/passes/fsm/fsm_recode.cc +++ b/passes/fsm/fsm_recode.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/fsm/fsmdata.h b/passes/fsm/fsmdata.h index 47398b558..4ba3b4e4f 100644 --- a/passes/fsm/fsmdata.h +++ b/passes/fsm/fsmdata.h @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/hierarchy/hierarchy.cc b/passes/hierarchy/hierarchy.cc index 3372687e1..440881f19 100644 --- a/passes/hierarchy/hierarchy.cc +++ b/passes/hierarchy/hierarchy.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * Copyright (C) 2018 Ruben Undheim <ruben.undheim@gmail.com> * * Permission to use, copy, modify, and/or distribute this software for any @@ -156,6 +156,289 @@ std::string basic_cell_type(const std::string celltype, int pos[3] = nullptr) { return basicType; } +// A helper struct for expanding a module's interface connections in expand_module +struct IFExpander +{ + IFExpander (RTLIL::Design &design, RTLIL::Module &m) + : module(m), has_interfaces_not_found(false) + { + // Keep track of all derived interfaces available in the current + // module in 'interfaces_in_module': + for (auto cell : module.cells()) { + if(!cell->get_bool_attribute(ID::is_interface)) + continue; + + interfaces_in_module[cell->name] = design.module(cell->type); + } + } + + RTLIL::Module &module; + dict<RTLIL::IdString, RTLIL::Module*> interfaces_in_module; + + bool has_interfaces_not_found; + std::vector<RTLIL::IdString> connections_to_remove; + std::vector<RTLIL::IdString> connections_to_add_name; + std::vector<RTLIL::SigSpec> connections_to_add_signal; + dict<RTLIL::IdString, RTLIL::Module*> interfaces_to_add_to_submodule; + dict<RTLIL::IdString, RTLIL::IdString> modports_used_in_submodule; + + // Reset the per-cell state + void start_cell() + { + has_interfaces_not_found = false; + connections_to_remove.clear(); + connections_to_add_name.clear(); + connections_to_add_signal.clear(); + interfaces_to_add_to_submodule.clear(); + modports_used_in_submodule.clear(); + } + + // Set has_interfaces_not_found if there are pending interfaces that + // haven't been found yet (and might be found in the future). Print a + // warning if we've already gone over all the cells in the module. + void on_missing_interface(RTLIL::IdString interface_name) + { + // If there are cells that haven't yet been processed, maybe + // we'll find this interface in the future. + if (module.get_bool_attribute(ID::cells_not_processed)) { + has_interfaces_not_found = true; + return; + } + + // Otherwise, we have already gone over all cells in this + // module and the interface has still not been found. Warn + // about it and don't set has_interfaces_not_found (to avoid a + // loop). + log_warning("Could not find interface instance for `%s' in `%s'\n", + log_id(interface_name), log_id(&module)); + } + + // Handle an interface connection from the module + void on_interface(RTLIL::Module &submodule, + RTLIL::IdString conn_name, + const RTLIL::SigSpec &conn_signals) + { + // Check if the connected wire is a potential interface in the parent module + std::string interface_name_str = conn_signals.bits()[0].wire->name.str(); + // Strip the prefix '$dummywireforinterface' from the dummy wire to get the name + interface_name_str.replace(0,23,""); + interface_name_str = "\\" + interface_name_str; + RTLIL::IdString interface_name = interface_name_str; + + // If 'interfaces' in the cell have not be been handled yet, we aren't + // ready to derive the sub-module either + if (!module.get_bool_attribute(ID::interfaces_replaced_in_module)) { + on_missing_interface(interface_name); + return; + } + + // Check if the interface instance is present in module. Interface + // instances may either have the plain name or the name appended with + // '_inst_from_top_dummy'. Check for both of them here + int nexactmatch = interfaces_in_module.count(interface_name) > 0; + std::string interface_name_str2 = interface_name_str + "_inst_from_top_dummy"; + RTLIL::IdString interface_name2 = interface_name_str2; + int nmatch2 = interfaces_in_module.count(interface_name2) > 0; + + // If we can't find either name, this is a missing interface. + if (! (nexactmatch || nmatch2)) { + on_missing_interface(interface_name); + return; + } + + if (nexactmatch != 0) // Choose the one with the plain name if it exists + interface_name2 = interface_name; + + RTLIL::Module *mod_replace_ports = interfaces_in_module.at(interface_name2); + + // Go over all wires in interface, and add replacements to lists. + for (auto mod_wire : mod_replace_ports->wires()) { + std::string signal_name1 = conn_name.str() + "." + log_id(mod_wire->name); + std::string signal_name2 = interface_name.str() + "." + log_id(mod_wire); + connections_to_add_name.push_back(RTLIL::IdString(signal_name1)); + if(module.wire(signal_name2) == nullptr) { + log_error("Could not find signal '%s' in '%s'\n", + signal_name2.c_str(), log_id(module.name)); + } + else { + RTLIL::Wire *wire_in_parent = module.wire(signal_name2); + connections_to_add_signal.push_back(wire_in_parent); + } + } + connections_to_remove.push_back(conn_name); + interfaces_to_add_to_submodule[conn_name] = interfaces_in_module.at(interface_name2); + + // Find if the sub-module has set a modport for the current interface + // connection. Add any modports to a dict which will be passed to + // AstModule::derive + string modport_name = submodule.wire(conn_name)->get_string_attribute(ID::interface_modport); + if (!modport_name.empty()) { + modports_used_in_submodule[conn_name] = "\\" + modport_name; + } + } + + // Handle a single connection from the module, making a note to expand + // it if it's an interface connection. + void on_connection(RTLIL::Module &submodule, + RTLIL::IdString conn_name, + const RTLIL::SigSpec &conn_signals) + { + // Check if the connection is present as an interface in the sub-module's port list + const RTLIL::Wire *wire = submodule.wire(conn_name); + if (!wire || !wire->get_bool_attribute(ID::is_interface)) + return; + + // If the connection looks like an interface, handle it. + const auto &bits = conn_signals.bits(); + if (bits.size() == 1 && bits[0].wire->get_bool_attribute(ID::is_interface)) + on_interface(submodule, conn_name, conn_signals); + } + + // Iterate over the connections in a cell, tracking any interface + // connections + void visit_connections(const RTLIL::Cell &cell, + RTLIL::Module &submodule) + { + for (const auto &conn : cell.connections()) { + on_connection(submodule, conn.first, conn.second); + } + } + + // Add/remove connections to the cell as necessary, replacing any SV + // interface port connection with the individual signal connections. + void rewrite_interface_connections(RTLIL::Cell &cell) const + { + for(unsigned int i=0;i<connections_to_add_name.size();i++) { + cell.connections_[connections_to_add_name[i]] = connections_to_add_signal[i]; + } + // Remove the connection for the interface itself: + for(unsigned int i=0;i<connections_to_remove.size();i++) { + cell.connections_.erase(connections_to_remove[i]); + } + } +}; + +// Get a module needed by a cell, either by deriving an abstract module or by +// loading one from a directory in libdirs. +// +// If the module can't be found and check is true then exit with an error +// message. Otherwise, return a pointer to the module if we derived or loaded +// something. or null otherwise (the module should be blackbox or we couldn't +// find it and check is not set). +RTLIL::Module *get_module(RTLIL::Design &design, + RTLIL::Cell &cell, + RTLIL::Module &parent, + bool check, + const std::vector<std::string> &libdirs) +{ + std::string cell_type = cell.type.str(); + RTLIL::Module *abs_mod = design.module("$abstract" + cell_type); + if (abs_mod) { + cell.type = abs_mod->derive(&design, cell.parameters); + cell.parameters.clear(); + RTLIL::Module *mod = design.module(cell.type); + log_assert(mod); + return mod; + } + + // If the cell type starts with '$' and isn't '$abstract', we should + // treat it as a black box and skip. + if (cell_type[0] == '$') + return nullptr; + + for (auto &dir : libdirs) { + static const vector<pair<string, string>> extensions_list = + { + {".v", "verilog"}, + {".sv", "verilog -sv"}, + {".il", "rtlil"} + }; + + for (auto &ext : extensions_list) { + std::string filename = dir + "/" + RTLIL::unescape_id(cell.type) + ext.first; + if (!check_file_exists(filename)) + continue; + + Frontend::frontend_call(&design, NULL, filename, ext.second); + RTLIL::Module *mod = design.module(cell.type); + if (!mod) + log_error("File `%s' from libdir does not declare module `%s'.\n", + filename.c_str(), cell_type.c_str()); + return mod; + } + } + + // We couldn't find the module anywhere. Complain if check is set. + if (check) + log_error("Module `%s' referenced in module `%s' in cell `%s' is not part of the design.\n", + cell_type.c_str(), parent.name.c_str(), cell.name.c_str()); + + return nullptr; +} + +// Try to read an IdString as a numbered connection name ("$123" or similar), +// writing the result to dst. If the string isn't of the right format, ignore +// dst and return false. +bool read_id_num(RTLIL::IdString str, int *dst) +{ + log_assert(dst); + + const char *c_str = str.c_str(); + if (c_str[0] != '$' || !('0' <= c_str[1] && c_str[1] <= '9')) + return false; + + *dst = atoi(c_str + 1); + return true; +} + +// Check that the connections on the cell match those that are defined +// on the type: each named connection should match the name of a port +// and each positional connection should have an index smaller than +// the number of ports. +// +// Also do the same checks on the specified parameters. +void check_cell_connections(const RTLIL::Module &module, RTLIL::Cell &cell, RTLIL::Module &mod) +{ + int id; + for (auto &conn : cell.connections()) { + if (read_id_num(conn.first, &id)) { + if (id <= 0 || id > GetSize(mod.ports)) + log_error("Module `%s' referenced in module `%s' in cell `%s' " + "has only %d ports, requested port %d.\n", + log_id(cell.type), log_id(&module), log_id(&cell), + GetSize(mod.ports), id); + continue; + } + + const RTLIL::Wire* wire = mod.wire(conn.first); + if (!wire || wire->port_id == 0) { + log_error("Module `%s' referenced in module `%s' in cell `%s' " + "does not have a port named '%s'.\n", + log_id(cell.type), log_id(&module), log_id(&cell), + log_id(conn.first)); + } + } + for (auto ¶m : cell.parameters) { + if (read_id_num(param.first, &id)) { + if (id <= 0 || id > GetSize(mod.avail_parameters)) + log_error("Module `%s' referenced in module `%s' in cell `%s' " + "has only %d parameters, requested parameter %d.\n", + log_id(cell.type), log_id(&module), log_id(&cell), + GetSize(mod.avail_parameters), id); + continue; + } + + if (mod.avail_parameters.count(param.first) == 0 && + param.first[0] != '$' && + strchr(param.first.c_str(), '.') == NULL) { + log_error("Module `%s' referenced in module `%s' in cell `%s' " + "does not have a parameter named '%s'.\n", + log_id(cell.type), log_id(&module), log_id(&cell), + log_id(param.first)); + } + } +} + bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check, bool flag_simcheck, std::vector<std::string> &libdirs) { bool did_something = false; @@ -173,23 +456,11 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check } } - // Always keep track of all derived interfaces available in the current module in 'interfaces_in_module': - dict<RTLIL::IdString, RTLIL::Module*> interfaces_in_module; - for (auto cell : module->cells()) - { - if(cell->get_bool_attribute(ID::is_interface)) { - RTLIL::Module *intf_module = design->module(cell->type); - interfaces_in_module[cell->name] = intf_module; - } - } + IFExpander if_expander(*design, *module); for (auto cell : module->cells()) { - bool has_interfaces_not_found = false; - - std::vector<RTLIL::IdString> connections_to_remove; - std::vector<RTLIL::IdString> connections_to_add_name; - std::vector<RTLIL::SigSpec> connections_to_add_signal; + if_expander.start_cell(); if (cell->type.begins_with("$array:")) { int pos[3]; @@ -202,147 +473,32 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check array_cells[cell] = std::pair<int, int>(idx, num); cell->type = cell->type.substr(pos_type + 1); } - dict<RTLIL::IdString, RTLIL::Module*> interfaces_to_add_to_submodule; - dict<RTLIL::IdString, RTLIL::IdString> modports_used_in_submodule; - if (design->module(cell->type) == nullptr) + RTLIL::Module *mod = design->module(cell->type); + if (!mod) { - if (design->module("$abstract" + cell->type.str()) != nullptr) - { - cell->type = design->module("$abstract" + cell->type.str())->derive(design, cell->parameters); - cell->parameters.clear(); - did_something = true; - continue; - } + mod = get_module(*design, *cell, *module, flag_check || flag_simcheck, libdirs); - if (cell->type[0] == '$') - continue; - - for (auto &dir : libdirs) - { - static const vector<pair<string, string>> extensions_list = - { - {".v", "verilog"}, - {".sv", "verilog -sv"}, - {".il", "rtlil"} - }; - - for (auto &ext : extensions_list) - { - filename = dir + "/" + RTLIL::unescape_id(cell->type) + ext.first; - if (check_file_exists(filename)) { - Frontend::frontend_call(design, NULL, filename, ext.second); - goto loaded_module; - } - } - } + // If we still don't have a module, treat the cell as a black box and skip + // it. Otherwise, we either loaded or derived something so should set the + // did_something flag before returning (to ensure we come back and expand + // the thing we just loaded). + if (mod) + did_something = true; - if ((flag_check || flag_simcheck) && cell->type[0] != '$') - log_error("Module `%s' referenced in module `%s' in cell `%s' is not part of the design.\n", - cell->type.c_str(), module->name.c_str(), cell->name.c_str()); continue; + } - loaded_module: - if (design->module(cell->type) == nullptr) - log_error("File `%s' from libdir does not declare module `%s'.\n", filename.c_str(), cell->type.c_str()); - did_something = true; - } else { - - RTLIL::Module *mod = design->module(cell->type); + log_assert(mod); - // Go over all connections and see if any of them are SV interfaces. If they are, then add the replacements to - // some lists, so that the ports for sub-modules can be replaced further down: - for (auto &conn : cell->connections()) { - if(mod->wire(conn.first) != nullptr && mod->wire(conn.first)->get_bool_attribute(ID::is_interface)) { // Check if the connection is present as an interface in the sub-module's port list - if(conn.second.bits().size() == 1 && conn.second.bits()[0].wire->get_bool_attribute(ID::is_interface)) { // Check if the connected wire is a potential interface in the parent module - std::string interface_name_str = conn.second.bits()[0].wire->name.str(); - interface_name_str.replace(0,23,""); // Strip the prefix '$dummywireforinterface' from the dummy wire to get the name - interface_name_str = "\\" + interface_name_str; - RTLIL::IdString interface_name = interface_name_str; - bool not_found_interface = false; - if(module->get_bool_attribute(ID::interfaces_replaced_in_module)) { // If 'interfaces' in the cell have not be been handled yet, there is no need to derive the sub-module either - // Check if the interface instance is present in module: - // Interface instances may either have the plain name or the name appended with '_inst_from_top_dummy'. - // Check for both of them here - int nexactmatch = interfaces_in_module.count(interface_name) > 0; - std::string interface_name_str2 = interface_name_str + "_inst_from_top_dummy"; - RTLIL::IdString interface_name2 = interface_name_str2; - int nmatch2 = interfaces_in_module.count(interface_name2) > 0; - if (nexactmatch > 0 || nmatch2 > 0) { - if (nexactmatch != 0) // Choose the one with the plain name if it exists - interface_name2 = interface_name; - RTLIL::Module *mod_replace_ports = interfaces_in_module.at(interface_name2); - for (auto mod_wire : mod_replace_ports->wires()) { // Go over all wires in interface, and add replacements to lists. - std::string signal_name1 = conn.first.str() + "." + log_id(mod_wire->name); - std::string signal_name2 = interface_name.str() + "." + log_id(mod_wire); - connections_to_add_name.push_back(RTLIL::IdString(signal_name1)); - if(module->wire(signal_name2) == nullptr) { - log_error("Could not find signal '%s' in '%s'\n", signal_name2.c_str(), log_id(module->name)); - } - else { - RTLIL::Wire *wire_in_parent = module->wire(signal_name2); - connections_to_add_signal.push_back(wire_in_parent); - } - } - connections_to_remove.push_back(conn.first); - interfaces_to_add_to_submodule[conn.first] = interfaces_in_module.at(interface_name2); - - // Find if the sub-module has set a modport for the current - // interface connection. Add any modports to a dict which will - // be passed to AstModule::derive - string modport_name = mod->wire(conn.first)->get_string_attribute(ID::interface_modport); - if (!modport_name.empty()) { - modports_used_in_submodule[conn.first] = "\\" + modport_name; - } - } - else not_found_interface = true; - } - else not_found_interface = true; - // If the interface instance has not already been derived in the module, we cannot complete at this stage. Set "has_interfaces_not_found" - // which will delay the expansion of this cell: - if (not_found_interface) { - // If we have already gone over all cells in this module, and the interface has still not been found - flag it as an error: - if(!(module->get_bool_attribute(ID::cells_not_processed))) { - log_warning("Could not find interface instance for `%s' in `%s'\n", log_id(interface_name), log_id(module)); - } - else { - // Only set has_interfaces_not_found if it would be possible to find them, since otherwiser we will end up in an infinite loop: - has_interfaces_not_found = true; - } - } - } - } - } - // + // Go over all connections and check if any of them are SV + // interfaces. + if_expander.visit_connections(*cell, *mod); if (flag_check || flag_simcheck) - { - for (auto &conn : cell->connections()) { - if (conn.first[0] == '$' && '0' <= conn.first[1] && conn.first[1] <= '9') { - int id = atoi(conn.first.c_str()+1); - if (id <= 0 || id > GetSize(mod->ports)) - log_error("Module `%s' referenced in module `%s' in cell `%s' has only %d ports, requested port %d.\n", - log_id(cell->type), log_id(module), log_id(cell), GetSize(mod->ports), id); - } else if (mod->wire(conn.first) == nullptr || mod->wire(conn.first)->port_id == 0) - log_error("Module `%s' referenced in module `%s' in cell `%s' does not have a port named '%s'.\n", - log_id(cell->type), log_id(module), log_id(cell), log_id(conn.first)); - } - for (auto ¶m : cell->parameters) { - if (param.first[0] == '$' && '0' <= param.first[1] && param.first[1] <= '9') { - int id = atoi(param.first.c_str()+1); - if (id <= 0 || id > GetSize(mod->avail_parameters)) - log_error("Module `%s' referenced in module `%s' in cell `%s' has only %d parameters, requested parameter %d.\n", - log_id(cell->type), log_id(module), log_id(cell), GetSize(mod->avail_parameters), id); - } else if (mod->avail_parameters.count(param.first) == 0 && param.first[0] != '$' && strchr(param.first.c_str(), '.') == NULL) - log_error("Module `%s' referenced in module `%s' in cell `%s' does not have a parameter named '%s'.\n", - log_id(cell->type), log_id(module), log_id(cell), log_id(param.first)); - } - - } - } - RTLIL::Module *mod = design->module(cell->type); + check_cell_connections(*module, *cell, *mod); - if (design->module(cell->type)->get_blackbox_attribute()) { + if (mod->get_blackbox_attribute()) { if (flag_simcheck) log_error("Module `%s' referenced in module `%s' in cell `%s' is a blackbox/whitebox module.\n", cell->type.c_str(), module->name.c_str(), cell->name.c_str()); @@ -350,23 +506,18 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check } // If interface instances not yet found, skip cell for now, and say we did something, so that we will return back here: - if(has_interfaces_not_found) { + if(if_expander.has_interfaces_not_found) { did_something = true; // waiting for interfaces to be handled continue; } - // Do the actual replacements of the SV interface port connection with the individual signal connections: - for(unsigned int i=0;i<connections_to_add_name.size();i++) { - cell->connections_[connections_to_add_name[i]] = connections_to_add_signal[i]; - } - // Remove the connection for the interface itself: - for(unsigned int i=0;i<connections_to_remove.size();i++) { - cell->connections_.erase(connections_to_remove[i]); - } + if_expander.rewrite_interface_connections(*cell); // If there are no overridden parameters AND not interfaces, then we can use the existing module instance as the type // for the cell: - if (cell->parameters.size() == 0 && (interfaces_to_add_to_submodule.size() == 0 || !(cell->get_bool_attribute(ID::module_not_derived)))) { + if (cell->parameters.size() == 0 && + (if_expander.interfaces_to_add_to_submodule.size() == 0 || + !(cell->get_bool_attribute(ID::module_not_derived)))) { // If the cell being processed is an the interface instance itself, go down to "handle_interface_instance:", // so that the signals of the interface are added to the parent module. if (mod->get_bool_attribute(ID::is_interface)) { @@ -375,7 +526,10 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check continue; } - cell->type = mod->derive(design, cell->parameters, interfaces_to_add_to_submodule, modports_used_in_submodule); + cell->type = mod->derive(design, + cell->parameters, + if_expander.interfaces_to_add_to_submodule, + if_expander.modports_used_in_submodule); cell->parameters.clear(); did_something = true; @@ -386,7 +540,7 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check if (mod->get_bool_attribute(ID::is_interface) && cell->get_bool_attribute(ID::module_not_derived)) { cell->set_bool_attribute(ID::is_interface); RTLIL::Module *derived_module = design->module(cell->type); - interfaces_in_module[cell->name] = derived_module; + if_expander.interfaces_in_module[cell->name] = derived_module; did_something = true; } // We clear 'module_not_derived' such that we will not rederive the cell again (needed when there are interfaces connected to the cell) @@ -399,11 +553,15 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check // If any interface instances or interface ports were found in the module, we need to rederive it completely: - if ((interfaces_in_module.size() > 0 || has_interface_ports) && !module->get_bool_attribute(ID::interfaces_replaced_in_module)) { - module->reprocess_module(design, interfaces_in_module); + if ((if_expander.interfaces_in_module.size() > 0 || has_interface_ports) && !module->get_bool_attribute(ID::interfaces_replaced_in_module)) { + module->expand_interfaces(design, if_expander.interfaces_in_module); return did_something; } + // Now that modules have been derived, we may want to reprocess this + // module given the additional available context. + if (module->reprocess_if_necessary(design)) + return true; for (auto &it : array_cells) { @@ -952,8 +1110,8 @@ struct HierarchyPass : public Pass { pool<std::pair<IdString, IdString>> params_rename; for (const auto &p : cell->parameters) { - if (p.first[0] == '$' && '0' <= p.first[1] && p.first[1] <= '9') { - int id = atoi(p.first.c_str()+1); + int id; + if (read_id_num(p.first, &id)) { if (id <= 0 || id > GetSize(cell_mod->avail_parameters)) { log(" Failed to map positional parameter %d of cell %s.%s (%s).\n", id, RTLIL::id2cstr(mod->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type)); @@ -980,9 +1138,9 @@ struct HierarchyPass : public Pass { log("Mapping positional arguments of cell %s.%s (%s).\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type)); dict<RTLIL::IdString, RTLIL::SigSpec> new_connections; - for (auto &conn : cell->connections()) - if (conn.first[0] == '$' && '0' <= conn.first[1] && conn.first[1] <= '9') { - int id = atoi(conn.first.c_str()+1); + for (auto &conn : cell->connections()) { + int id; + if (read_id_num(conn.first, &id)) { std::pair<RTLIL::Module*,int> key(design->module(cell->type), id); if (pos_map.count(key) == 0) { log(" Failed to map positional argument %d of cell %s.%s (%s).\n", @@ -992,6 +1150,7 @@ struct HierarchyPass : public Pass { new_connections[pos_map.at(key)] = conn.second; } else new_connections[conn.first] = conn.second; + } cell->connections_ = new_connections; } } diff --git a/passes/hierarchy/submod.cc b/passes/hierarchy/submod.cc index b2826cbff..845dc850f 100644 --- a/passes/hierarchy/submod.cc +++ b/passes/hierarchy/submod.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/hierarchy/uniquify.cc b/passes/hierarchy/uniquify.cc index 3f9443a63..e9322d359 100644 --- a/passes/hierarchy/uniquify.cc +++ b/passes/hierarchy/uniquify.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/memory/Makefile.inc b/passes/memory/Makefile.inc index e468c3a07..5a2c4ecfc 100644 --- a/passes/memory/Makefile.inc +++ b/passes/memory/Makefile.inc @@ -8,4 +8,5 @@ OBJS += passes/memory/memory_bram.o OBJS += passes/memory/memory_map.o OBJS += passes/memory/memory_memx.o OBJS += passes/memory/memory_nordff.o +OBJS += passes/memory/memory_narrow.o diff --git a/passes/memory/memory.cc b/passes/memory/memory.cc index 282517992..bac547c1a 100644 --- a/passes/memory/memory.cc +++ b/passes/memory/memory.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -31,16 +31,19 @@ struct MemoryPass : public Pass { { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" memory [-nomap] [-nordff] [-memx] [-bram <bram_rules>] [selection]\n"); + log(" memory [-nomap] [-nordff] [-nowiden] [-nosat] [-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(" opt_mem\n"); - log(" memory_dff [-nordff] (-memx implies -nordff)\n"); - log(" opt_clean\n"); - log(" memory_share\n"); + log(" opt_mem_priority\n"); + log(" opt_mem_feedback\n"); + log(" memory_dff (skipped if called with -nordff or -memx)\n"); log(" opt_clean\n"); + log(" memory_share [-nowiden] [-nosat]\n"); + log(" opt_mem_widen\n"); log(" memory_memx (when called with -memx)\n"); + log(" opt_clean\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"); @@ -55,6 +58,7 @@ struct MemoryPass : public Pass { bool flag_nordff = false; bool flag_memx = false; string memory_bram_opts; + string memory_share_opts; log_header(design, "Executing MEMORY pass.\n"); log_push(); @@ -74,6 +78,14 @@ struct MemoryPass : public Pass { flag_memx = true; continue; } + if (args[argidx] == "-nowiden") { + memory_share_opts += " -nowiden"; + continue; + } + if (args[argidx] == "-nosat") { + memory_share_opts += " -nosat"; + continue; + } if (argidx+1 < args.size() && args[argidx] == "-bram") { memory_bram_opts += " -rules " + args[++argidx]; continue; @@ -83,9 +95,13 @@ struct MemoryPass : public Pass { extra_args(args, argidx, design); Pass::call(design, "opt_mem"); - Pass::call(design, flag_nordff ? "memory_dff -nordff" : "memory_dff"); + Pass::call(design, "opt_mem_priority"); + Pass::call(design, "opt_mem_feedback"); + if (!flag_nordff) + Pass::call(design, "memory_dff"); Pass::call(design, "opt_clean"); - Pass::call(design, "memory_share"); + Pass::call(design, "memory_share" + memory_share_opts); + Pass::call(design, "opt_mem_widen"); if (flag_memx) Pass::call(design, "memory_memx"); Pass::call(design, "opt_clean"); diff --git a/passes/memory/memory_bram.cc b/passes/memory/memory_bram.cc index c6948fdba..b1f45d5fc 100644 --- a/passes/memory/memory_bram.cc +++ b/passes/memory/memory_bram.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -19,6 +19,7 @@ #include "kernel/yosys.h" #include "kernel/mem.h" +#include "kernel/ffinit.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -29,9 +30,6 @@ struct rules_t int group, index, dupidx; int wrmode, enable, transp, clocks, clkpol; - SigBit sig_clock; - SigSpec sig_addr, sig_data, sig_en; - bool effective_clkpol; bool make_transp; bool make_outreg; int mapped_port; @@ -92,7 +90,6 @@ struct rules_t pi.mapped_port = -1; pi.make_transp = false; pi.make_outreg = false; - pi.effective_clkpol = false; portinfos.push_back(pi); } return portinfos; @@ -401,19 +398,13 @@ struct rules_t } }; -bool replace_memory(Mem &orig_mem, const rules_t &rules, const rules_t::bram_t &bram, const rules_t::match_t &match, dict<string, int> &match_properties, int mode) +bool replace_memory(Mem &mem, const rules_t &rules, FfInitVals *initvals, const rules_t::bram_t &bram, const rules_t::match_t &match, dict<string, int> &match_properties, int mode) { - // We will modify ports — make a copy of the structure. - Mem mem(orig_mem); Module *module = mem.module; auto portinfos = bram.make_portinfos(); int dup_count = 1; - pair<SigBit, bool> make_transp_clk; - bool enable_make_transp = false; - int make_transp_enbits = 0; - dict<int, pair<SigBit, bool>> clock_domains; dict<int, bool> clock_polarities; dict<int, bool> read_transp; @@ -440,35 +431,12 @@ bool replace_memory(Mem &orig_mem, const rules_t &rules, const rules_t::bram_t & log(" Mapping to bram type %s (variant %d):\n", log_id(bram.name), bram.variant); // bram.dump_config(); - bool cell_init = !mem.inits.empty(); - vector<Const> initdata; - - if (cell_init) { - Const initparam = mem.get_init_data(); - initdata.reserve(mem.size); - for (int i=0; i < mem.size; i++) - initdata.push_back(initparam.extract(mem.width*i, mem.width, State::Sx)); - } - + std::vector<int> shuffle_map; if (match.shuffle_enable && bram.dbits >= portinfos.at(match.shuffle_enable - 'A').enable*2 && portinfos.at(match.shuffle_enable - 'A').enable > 0 && !mem.wr_ports.empty()) { int bucket_size = bram.dbits / portinfos.at(match.shuffle_enable - 'A').enable; log(" Shuffle bit order to accommodate enable buckets of size %d..\n", bucket_size); - // extract unshuffled data/enable bits - - std::vector<SigSpec> old_wr_en; - std::vector<SigSpec> old_wr_data; - std::vector<SigSpec> old_rd_data; - - for (auto &port : mem.wr_ports) { - old_wr_en.push_back(port.en); - old_wr_data.push_back(port.data); - } - - for (auto &port : mem.rd_ports) - old_rd_data.push_back(port.data); - // analyze enable structure std::vector<SigSpec> en_order; @@ -483,52 +451,13 @@ bool replace_memory(Mem &orig_mem, const rules_t &rules, const rules_t::bram_t & bits_wr_en[sig].push_back(i); } - // re-create memory ports - - std::vector<SigSpec> new_wr_en(GetSize(old_wr_en)); - std::vector<SigSpec> new_wr_data(GetSize(old_wr_data)); - std::vector<SigSpec> new_rd_data(GetSize(old_rd_data)); - std::vector<std::vector<State>> new_initdata; - std::vector<int> shuffle_map; - - if (cell_init) - new_initdata.resize(mem.size); - for (auto &it : en_order) { - auto &bits = bits_wr_en.at(it); - int buckets = (GetSize(bits) + bucket_size - 1) / bucket_size; - int fillbits = buckets*bucket_size - GetSize(bits); - SigBit fillbit; - - for (int i = 0; i < GetSize(bits); i++) { - for (int j = 0; j < GetSize(mem.wr_ports); j++) { - new_wr_en[j].append(old_wr_en[j][bits[i]]); - new_wr_data[j].append(old_wr_data[j][bits[i]]); - fillbit = old_wr_en[j][bits[i]]; - } - for (int j = 0; j < GetSize(mem.rd_ports); j++) - new_rd_data[j].append(old_rd_data[j][bits[i]]); - if (cell_init) { - for (int j = 0; j < mem.size; j++) - new_initdata[j].push_back(initdata[j][bits[i]]); - } - shuffle_map.push_back(bits[i]); - } + for (auto bit : bits_wr_en.at(it)) + shuffle_map.push_back(bit); - for (int i = 0; i < fillbits; i++) { - for (int j = 0; j < GetSize(mem.wr_ports); j++) { - new_wr_en[j].append(fillbit); - new_wr_data[j].append(State::S0); - } - for (int j = 0; j < GetSize(mem.rd_ports); j++) - new_rd_data[j].append(State::Sx); - if (cell_init) { - for (int j = 0; j < mem.size; j++) - new_initdata[j].push_back(State::Sx); - } + while (GetSize(shuffle_map) % bucket_size) shuffle_map.push_back(-1); - } } log(" Results of bit order shuffling:"); @@ -537,26 +466,15 @@ bool replace_memory(Mem &orig_mem, const rules_t &rules, const rules_t::bram_t & log("\n"); // update mem_*, wr_*, and rd_* variables - - mem.width = GetSize(new_wr_en.front()); - - for (int i = 0; i < GetSize(mem.wr_ports); i++) { - auto &port = mem.wr_ports[i]; - port.en = new_wr_en[i]; - port.data = new_wr_data[i]; - } - - for (int i = 0; i < GetSize(mem.rd_ports); i++) { - auto &port = mem.rd_ports[i]; - port.data = new_rd_data[i]; - } - - if (cell_init) { - for (int i = 0; i < mem.size; i++) - initdata[i] = Const(new_initdata[i]); - } + } else { + for (int i = 0; i < mem.width; i++) + shuffle_map.push_back(i); } + // Align width up to dbits. + while (GetSize(shuffle_map) % bram.dbits) + shuffle_map.push_back(-1); + // assign write ports pair<SigBit, bool> wr_clkdom; for (int cell_port_i = 0, bram_port_i = 0; cell_port_i < GetSize(mem.wr_ports); cell_port_i++) @@ -574,8 +492,6 @@ bool replace_memory(Mem &orig_mem, const rules_t &rules, const rules_t::bram_t & for (; bram_port_i < GetSize(portinfos); bram_port_i++) { auto &pi = portinfos[bram_port_i]; - make_transp_enbits = pi.enable; - make_transp_clk = clkdom; if (pi.wrmode != 1) skip_bram_wport: @@ -601,16 +517,25 @@ bool replace_memory(Mem &orig_mem, const rules_t &rules, const rules_t::bram_t & } } - SigSpec sig_en; - SigBit last_en_bit = State::S1; - for (int i = 0; i < mem.width; i++) { - if (pi.enable && i % (bram.dbits / pi.enable) == 0) { - last_en_bit = port.en[i]; - sig_en.append(last_en_bit); - } - if (last_en_bit != port.en[i]) { - log(" Bram port %c%d has incompatible enable structure.\n", pi.group + 'A', pi.index + 1); - goto skip_bram_wport; + // We need to check enable compatibility of this port, as well as all + // ports that have priority over this one (because they will be involved + // in emulate_priority logic). + + for (int i = 0; i < GetSize(mem.wr_ports); i++) { + auto &oport = mem.wr_ports[i]; + if (i != cell_port_i && !oport.priority_mask[cell_port_i]) + continue; + SigBit last_en_bit = State::S1; + for (int j = 0; j < GetSize(shuffle_map); j++) { + if (shuffle_map[j] == -1) + continue; + SigBit en_bit = oport.en[shuffle_map[j]]; + if (pi.enable && j % (bram.dbits / pi.enable) == 0) + last_en_bit = en_bit; + if (last_en_bit != en_bit) { + log(" Bram port %c%d has incompatible enable structure.\n", pi.group + 'A', pi.index + 1); + goto skip_bram_wport; + } } } @@ -620,14 +545,8 @@ bool replace_memory(Mem &orig_mem, const rules_t &rules, const rules_t::bram_t & if (port.clk_enable) { clock_domains[pi.clocks] = clkdom; clock_polarities[pi.clkpol] = clkdom.second; - pi.sig_clock = clkdom.first; - pi.effective_clkpol = clkdom.second; } - pi.sig_en = sig_en; - pi.sig_addr = port.addr; - pi.sig_data = port.data; - bram_port_i++; goto mapped_wr_port; } @@ -650,10 +569,6 @@ grow_read_ports:; for (auto &pi : portinfos) { if (pi.wrmode == 0) { pi.mapped_port = -1; - pi.sig_clock = SigBit(); - pi.sig_addr = SigSpec(); - pi.sig_data = SigSpec(); - pi.sig_en = SigSpec(); pi.make_outreg = false; pi.make_transp = false; } @@ -685,10 +600,16 @@ grow_read_ports:; for (int cell_port_i = 0; cell_port_i < GetSize(mem.rd_ports); cell_port_i++) { auto &port = mem.rd_ports[cell_port_i]; - bool transp = port.transparent; - - if (mem.wr_ports.empty()) - transp = false; + bool transp = false; + bool non_transp = false; + + if (port.clk_enable) { + for (int i = 0; i < GetSize(mem.wr_ports); i++) + if (port.transparency_mask[i]) + transp = true; + else if (!port.collision_x_mask[i]) + non_transp = true; + } pair<SigBit, bool> clkdom(port.clk, port.clk_polarity); if (!port.clk_enable) @@ -723,21 +644,13 @@ grow_read_ports:; log(" Bram port %c%d.%d has incompatible clock polarity.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1); goto skip_bram_rport; } - if (port.en != State::S1 && pi.enable == 0) { - log(" Bram port %c%d.%d has no read enable input.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1); + if (non_transp && read_transp.count(pi.transp) && read_transp.at(pi.transp)) { + log(" Bram port %c%d.%d has incompatible read transparency.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1); goto skip_bram_rport; } - skip_bram_rport_clkcheck: - if (read_transp.count(pi.transp) && read_transp.at(pi.transp) != transp) { - if (match.make_transp && GetSize(mem.wr_ports) <= 1) { + if (transp && (non_transp || (read_transp.count(pi.transp) && !read_transp.at(pi.transp)))) { + if (match.make_transp) { pi.make_transp = true; - if (pi.clocks != 0) { - if (GetSize(mem.wr_ports) == 1 && wr_clkdom != clkdom) { - log(" Bram port %c%d.%d cannot have soft transparency logic added as read and write clock domains differ.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1); - goto skip_bram_rport; - } - enable_make_transp = true; - } } else { log(" Bram port %c%d.%d has incompatible read transparency.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1); goto skip_bram_rport; @@ -750,22 +663,19 @@ grow_read_ports:; } } + skip_bram_rport_clkcheck: log(" Mapped to bram port %c%d.%d.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1); pi.mapped_port = cell_port_i; - if (port.clk_enable) { + if (pi.clocks) { clock_domains[pi.clocks] = clkdom; clock_polarities[pi.clkpol] = clkdom.second; - if (!pi.make_transp) - read_transp[pi.transp] = transp; - pi.sig_clock = clkdom.first; - pi.sig_en = port.en; - pi.effective_clkpol = clkdom.second; + if (non_transp) + read_transp[pi.transp] = false; + if (transp && !pi.make_transp) + read_transp[pi.transp] = true; } - pi.sig_addr = port.addr; - pi.sig_data = port.data; - if (grow_read_ports_cursor < cell_port_i) { grow_read_ports_cursor = cell_port_i; try_growing_more_read_ports = true; @@ -785,17 +695,19 @@ grow_read_ports:; // update properties and re-check conditions + int dcells = GetSize(shuffle_map) / bram.dbits; + int acells = (mem.size + (1 << bram.abits) - 1) / (1 << bram.abits); if (mode <= 1) { match_properties["dups"] = dup_count; match_properties["waste"] = match_properties["dups"] * match_properties["bwaste"]; - int cells = ((mem.width + bram.dbits - 1) / bram.dbits) * ((mem.size + (1 << bram.abits) - 1) / (1 << bram.abits)); + int cells = dcells * acells; match_properties["efficiency"] = (100 * match_properties["bits"]) / (dup_count * cells * bram.dbits * (1 << bram.abits)); - match_properties["dcells"] = ((mem.width + bram.dbits - 1) / bram.dbits); - match_properties["acells"] = ((mem.size + (1 << bram.abits) - 1) / (1 << bram.abits)); - match_properties["cells"] = match_properties["dcells"] * match_properties["acells"] * match_properties["dups"]; + match_properties["dcells"] = dcells; + match_properties["acells"] = acells; + match_properties["cells"] = cells * dup_count; log(" Updated properties: dups=%d waste=%d efficiency=%d\n", match_properties["dups"], match_properties["waste"], match_properties["efficiency"]); @@ -862,6 +774,90 @@ grow_read_ports:; return true; } + // At this point we are commited to replacing the RAM, and can mutate mem. + + // Apply make_outreg and make_transp where necessary. + for (auto &pi : portinfos) { + if (pi.mapped_port == -1 || pi.wrmode) + continue; + auto &port = mem.rd_ports[pi.mapped_port]; + if (pi.make_outreg) { + mem.extract_rdff(pi.mapped_port, initvals); + } else if (port.clk_enable) { + if (!pi.enable && port.en != State::S1) + mem.emulate_rden(pi.mapped_port, initvals); + else + mem.emulate_reset(pi.mapped_port, true, true, true, initvals); + } + if (pi.make_transp) { + for (int i = 0; i < GetSize(mem.wr_ports); i++) + if (port.transparency_mask[i]) + mem.emulate_transparency(i, pi.mapped_port, initvals); + } + } + + // We don't really support priorities, emulate them. + for (int i = 0; i < GetSize(mem.wr_ports); i++) + for (int j = 0; j < i; j++) + mem.emulate_priority(j, i, initvals); + + // Swizzle the init data. Do this before changing mem.width, so that get_init_data works. + bool cell_init = !mem.inits.empty(); + vector<Const> initdata; + if (cell_init) { + Const initparam = mem.get_init_data(); + initdata.reserve(mem.size); + for (int i = 0; i < mem.size; i++) { + std::vector<State> val; + for (auto idx : shuffle_map) { + if (idx == -1) + val.push_back(State::Sx); + else + val.push_back(initparam[mem.width * i + idx]); + } + initdata.push_back(Const(val)); + } + } + + // Now the big swizzle. + mem.width = GetSize(shuffle_map); + + // Swizzle write ports. + for (auto &port : mem.wr_ports) { + SigSpec new_en, new_data; + SigBit en_bit = State::S1; + for (auto idx : shuffle_map) { + if (idx == -1) { + new_data.append(State::Sx); + } else { + new_data.append(port.data[idx]); + en_bit = port.en[idx]; + } + new_en.append(en_bit); + } + port.en = new_en; + port.data = new_data; + } + + // Swizzle read ports. + for (auto &port : mem.rd_ports) { + SigSpec new_data = module->addWire(NEW_ID, mem.width); + Const new_init_value = Const(State::Sx, mem.width); + Const new_arst_value = Const(State::Sx, mem.width); + Const new_srst_value = Const(State::Sx, mem.width); + for (int i = 0; i < mem.width; i++) + if (shuffle_map[i] != -1) { + module->connect(port.data[shuffle_map[i]], new_data[i]); + new_init_value[i] = port.init_value[shuffle_map[i]]; + new_arst_value[i] = port.arst_value[shuffle_map[i]]; + new_srst_value[i] = port.srst_value[shuffle_map[i]]; + } + port.data = new_data; + port.init_value = new_init_value; + port.arst_value = new_arst_value; + port.srst_value = new_srst_value; + } + // prepare variant parameters dict<IdString, Const> variant_params; @@ -872,21 +868,9 @@ grow_read_ports:; dict<SigSpec, pair<SigSpec, SigSpec>> dout_cache; - for (int grid_d = 0; grid_d*bram.dbits < mem.width; grid_d++) + for (int grid_d = 0; grid_d < dcells; grid_d++) { - SigSpec mktr_wraddr, mktr_wrdata, mktr_wrdata_q; - vector<SigSpec> mktr_wren; - - if (enable_make_transp) { - mktr_wraddr = module->addWire(NEW_ID, bram.abits); - mktr_wrdata = module->addWire(NEW_ID, bram.dbits); - mktr_wrdata_q = module->addWire(NEW_ID, bram.dbits); - module->addDff(NEW_ID, make_transp_clk.first, mktr_wrdata, mktr_wrdata_q, make_transp_clk.second); - for (int grid_a = 0; grid_a*(1 << bram.abits) < mem.size; grid_a++) - mktr_wren.push_back(module->addWire(NEW_ID, make_transp_enbits)); - } - - for (int grid_a = 0; grid_a*(1 << bram.abits) < mem.size; grid_a++) + for (int grid_a = 0; grid_a < acells; grid_a++) for (int dupidx = 0; dupidx < dup_count; dupidx++) { Cell *c = module->addCell(module->uniquify(stringf("%s.%d.%d.%d", mem.memid.c_str(), grid_d, grid_a, dupidx)), bram.name); @@ -896,18 +880,16 @@ grow_read_ports:; c->setParam(vp.first, vp.second); if (cell_init) { - int init_offset = grid_a*(1 << bram.abits); + int init_offset = grid_a*(1 << bram.abits) - mem.start_offset; int init_shift = grid_d*bram.dbits; int init_size = (1 << bram.abits); Const initparam(State::Sx, init_size*bram.dbits); - for (int i = 0; i < init_size; i++) { - State padding = State::Sx; + for (int i = 0; i < init_size; i++) for (int j = 0; j < bram.dbits; j++) - if (init_offset+i < GetSize(initdata) && init_shift+j < GetSize(initdata[init_offset+i])) + if (init_offset+i < GetSize(initdata) && init_offset+i >= 0) initparam[i*bram.dbits+j] = initdata[init_offset+i][init_shift+j]; else - initparam[i*bram.dbits+j] = padding; - } + initparam[i*bram.dbits+j] = State::Sx; c->setParam(ID::INIT, initparam); } @@ -919,101 +901,84 @@ grow_read_ports:; string prefix = stringf("%c%d", pi.group + 'A', pi.index + 1); const char *pf = prefix.c_str(); - if (pi.clocks && (!c->hasPort(stringf("\\CLK%d", (pi.clocks-1) % clocks_max + 1)) || pi.sig_clock.wire)) { - c->setPort(stringf("\\CLK%d", (pi.clocks-1) % clocks_max + 1), pi.sig_clock); - if (pi.clkpol > 1 && pi.sig_clock.wire) - c->setParam(stringf("\\CLKPOL%d", (pi.clkpol-1) % clkpol_max + 1), clock_polarities.at(pi.clkpol)); - if (pi.transp > 1 && pi.sig_clock.wire) - c->setParam(stringf("\\TRANSP%d", (pi.transp-1) % transp_max + 1), read_transp.at(pi.transp)); - } + if (pi.clocks && clock_domains.count(pi.clocks)) + c->setPort(stringf("\\CLK%d", (pi.clocks-1) % clocks_max + 1), clock_domains.at(pi.clocks).first); + if (pi.clkpol > 1 && clock_polarities.count(pi.clkpol)) + c->setParam(stringf("\\CLKPOL%d", (pi.clkpol-1) % clkpol_max + 1), clock_polarities.at(pi.clkpol)); + if (pi.transp > 1 && read_transp.count(pi.transp)) + c->setParam(stringf("\\TRANSP%d", (pi.transp-1) % transp_max + 1), read_transp.at(pi.transp)); SigSpec addr_ok; - if (GetSize(pi.sig_addr) > bram.abits) { - SigSpec extra_addr = pi.sig_addr.extract(bram.abits, GetSize(pi.sig_addr) - bram.abits); + SigSpec sig_addr; + if (pi.mapped_port >= 0) { + if (pi.wrmode == 1) + sig_addr = mem.wr_ports[pi.mapped_port].addr; + else + sig_addr = mem.rd_ports[pi.mapped_port].addr; + } + + if (GetSize(sig_addr) > bram.abits) { + SigSpec extra_addr = sig_addr.extract(bram.abits, GetSize(sig_addr) - bram.abits); SigSpec extra_addr_sel = SigSpec(grid_a, GetSize(extra_addr)); addr_ok = module->Eq(NEW_ID, extra_addr, extra_addr_sel); } - if (pi.enable) - { - SigSpec sig_en = pi.sig_en; + sig_addr.extend_u0(bram.abits); + c->setPort(stringf("\\%sADDR", pf), sig_addr); - if (pi.wrmode == 1) { - sig_en.extend_u0((grid_d+1) * pi.enable); - sig_en = sig_en.extract(grid_d * pi.enable, pi.enable); + if (pi.wrmode == 1) { + if (pi.mapped_port == -1) + { + if (pi.enable) + c->setPort(stringf("\\%sEN", pf), Const(State::S0, pi.enable)); + continue; } - if (!addr_ok.empty()) - sig_en = module->Mux(NEW_ID, SigSpec(0, GetSize(sig_en)), sig_en, addr_ok); - - c->setPort(stringf("\\%sEN", pf), sig_en); - - if (pi.wrmode == 1 && enable_make_transp) - module->connect(mktr_wren[grid_a], sig_en); - } + auto &port = mem.wr_ports[pi.mapped_port]; + SigSpec sig_data = port.data.extract(grid_d * bram.dbits, bram.dbits); + c->setPort(stringf("\\%sDATA", pf), sig_data); - SigSpec sig_addr = pi.sig_addr; - sig_addr.extend_u0(bram.abits); - c->setPort(stringf("\\%sADDR", pf), sig_addr); + if (pi.enable) + { + SigSpec sig_en; + int stride = bram.dbits / pi.enable; + for (int i = 0; i < pi.enable; i++) + sig_en.append(port.en[stride * i + grid_d * bram.dbits]); - if (pi.wrmode == 1 && enable_make_transp && grid_a == 0) - module->connect(mktr_wraddr, sig_addr); + if (!addr_ok.empty()) + sig_en = module->Mux(NEW_ID, SigSpec(0, GetSize(sig_en)), sig_en, addr_ok); - SigSpec sig_data = pi.sig_data; - sig_data.extend_u0((grid_d+1) * bram.dbits); - sig_data = sig_data.extract(grid_d * bram.dbits, bram.dbits); + c->setPort(stringf("\\%sEN", pf), sig_en); - if (pi.wrmode == 1) { - c->setPort(stringf("\\%sDATA", pf), sig_data); - if (enable_make_transp && grid_a == 0) - module->connect(mktr_wrdata, sig_data); + } } else { - SigSpec bram_dout = module->addWire(NEW_ID, bram.dbits); - c->setPort(stringf("\\%sDATA", pf), bram_dout); - if (pi.make_outreg && pi.make_transp) { - log(" Moving output register to address for transparent port %c%d.%d.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1); - SigSpec sig_addr_q = module->addWire(NEW_ID, bram.abits); - module->addDff(NEW_ID, pi.sig_clock, sig_addr, sig_addr_q, pi.effective_clkpol); - c->setPort(stringf("\\%sADDR", pf), sig_addr_q); - } else if (pi.make_outreg) { - SigSpec bram_dout_q = module->addWire(NEW_ID, bram.dbits); - if (!pi.sig_en.empty()) - bram_dout = module->Mux(NEW_ID, bram_dout_q, bram_dout, pi.sig_en); - module->addDff(NEW_ID, pi.sig_clock, bram_dout, bram_dout_q, pi.effective_clkpol); - bram_dout = bram_dout_q; - } else if (pi.make_transp) { - log(" Adding extra logic for transparent port %c%d.%d.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1); - - SigSpec transp_en_d = module->Mux(NEW_ID, SigSpec(0, make_transp_enbits), - mktr_wren[grid_a], module->Eq(NEW_ID, mktr_wraddr, sig_addr)); - - SigSpec transp_en_q = module->addWire(NEW_ID, make_transp_enbits); - module->addDff(NEW_ID, make_transp_clk.first, transp_en_d, transp_en_q, make_transp_clk.second); - - for (int i = 0; i < make_transp_enbits; i++) { - int en_width = bram.dbits / make_transp_enbits; - SigSpec orig_bram_dout = bram_dout.extract(i * en_width, en_width); - SigSpec bypass_dout = mktr_wrdata_q.extract(i * en_width, en_width); - bram_dout.replace(i * en_width, module->Mux(NEW_ID, orig_bram_dout, bypass_dout, transp_en_q[i])); - } + if (pi.mapped_port == -1) + { + if (pi.enable) + c->setPort(stringf("\\%sEN", pf), State::S0); + continue; } + auto &port = mem.rd_ports[pi.mapped_port]; + SigSpec sig_data = port.data.extract(grid_d * bram.dbits, bram.dbits); - for (int i = bram.dbits-1; i >= 0; i--) - if (sig_data[i].wire == nullptr) { - sig_data.remove(i); - bram_dout.remove(i); - } + SigSpec bram_dout = module->addWire(NEW_ID, bram.dbits); + c->setPort(stringf("\\%sDATA", pf), bram_dout); SigSpec addr_ok_q = addr_ok; - if ((pi.clocks || pi.make_outreg) && !addr_ok.empty()) { + if (port.clk_enable && !addr_ok.empty()) { addr_ok_q = module->addWire(NEW_ID); - if (!pi.sig_en.empty()) - addr_ok = module->Mux(NEW_ID, addr_ok_q, addr_ok, pi.sig_en); - module->addDff(NEW_ID, pi.sig_clock, addr_ok, addr_ok_q, pi.effective_clkpol); + module->addDffe(NEW_ID, port.clk, port.en, addr_ok, addr_ok_q, port.clk_polarity); } dout_cache[sig_data].first.append(addr_ok_q); dout_cache[sig_data].second.append(bram_dout); + + if (pi.enable) { + SigSpec sig_en = port.en; + if (!addr_ok.empty()) + sig_en = module->And(NEW_ID, sig_en, addr_ok); + c->setPort(stringf("\\%sEN", pf), sig_en); + } } } } @@ -1037,9 +1002,10 @@ grow_read_ports:; return true; } -void handle_memory(Mem &mem, const rules_t &rules) +void handle_memory(Mem &mem, const rules_t &rules, FfInitVals *initvals) { log("Processing %s.%s:\n", log_id(mem.module), log_id(mem.memid)); + mem.narrow(); bool cell_init = !mem.inits.empty(); @@ -1189,7 +1155,7 @@ void handle_memory(Mem &mem, const rules_t &rules) if (or_next_if_better && i+1 == GetSize(rules.matches) && vi+1 == GetSize(rules.brams.at(match.name))) log_error("Found 'or_next_if_better' in last match rule.\n"); - if (!replace_memory(mem, rules, bram, match, match_properties, 1)) { + if (!replace_memory(mem, rules, initvals, bram, match, match_properties, 1)) { log(" Mapping to bram type %s failed.\n", log_id(match.name)); failed_brams.insert(pair<IdString, int>(bram.name, bram.variant)); goto next_match_rule; @@ -1216,12 +1182,12 @@ void handle_memory(Mem &mem, const rules_t &rules) best_rule_cache.clear(); auto &best_bram = rules.brams.at(rules.matches.at(best_rule.first).name).at(best_rule.second); - if (!replace_memory(mem, rules, best_bram, rules.matches.at(best_rule.first), match_properties, 2)) + if (!replace_memory(mem, rules, initvals, best_bram, rules.matches.at(best_rule.first), match_properties, 2)) log_error("Mapping to bram type %s (variant %d) after pre-selection failed.\n", log_id(best_bram.name), best_bram.variant); return; } - if (!replace_memory(mem, rules, bram, match, match_properties, 0)) { + if (!replace_memory(mem, rules, initvals, bram, match, match_properties, 0)) { log(" Mapping to bram type %s failed.\n", log_id(match.name)); failed_brams.insert(pair<IdString, int>(bram.name, bram.variant)); goto next_match_rule; @@ -1353,9 +1319,12 @@ struct MemoryBramPass : public Pass { } extra_args(args, argidx, design); - for (auto mod : design->selected_modules()) - for (auto &mem : Mem::get_selected_memories(mod)) - handle_memory(mem, rules); + for (auto mod : design->selected_modules()) { + SigMap sigmap(mod); + FfInitVals initvals(&sigmap, mod); + for (auto &mem : Mem::get_selected_memories(mod)) + handle_memory(mem, rules, &initvals); + } } } MemoryBramPass; diff --git a/passes/memory/memory_collect.cc b/passes/memory/memory_collect.cc index ede6ca6a1..bf3bb34f8 100644 --- a/passes/memory/memory_collect.cc +++ b/passes/memory/memory_collect.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/memory/memory_dff.cc b/passes/memory/memory_dff.cc index 4adcb462e..91209d428 100644 --- a/passes/memory/memory_dff.cc +++ b/passes/memory/memory_dff.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -17,415 +17,627 @@ * */ -#include <algorithm> #include "kernel/yosys.h" #include "kernel/sigtools.h" +#include "kernel/modtools.h" #include "kernel/ffinit.h" +#include "kernel/qcsat.h" +#include "kernel/mem.h" +#include "kernel/ff.h" +#include "kernel/ffmerge.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -struct MemoryDffWorker +struct MuxData { + int base_idx; + int size; + bool is_b; + SigSpec sig_s; + std::vector<SigSpec> sig_other; +}; + +struct PortData { + bool relevant; + std::vector<bool> uncollidable_mask; + std::vector<bool> transparency_mask; + std::vector<bool> collision_x_mask; + bool final_transparency; + bool final_collision_x; +}; + +// A helper with some caching for transparency-related SAT queries. +// Bound to a single memory read port in the process of being converted +// from async to sync.. +struct MemQueryCache { - Module *module; - SigMap sigmap; + QuickConeSat &qcsat; + // The memory. + Mem &mem; + // The port, still async at this point. + MemRd &port; + // The virtual FF that will end up merged into this port. + FfData &ff; + // An ezSAT variable that is true when we actually care about the data + // read from memory (ie. the FF has enable on and is not in reset). + int port_ren; + // Some caches. + dict<std::pair<int, SigBit>, bool> cache_can_collide_rdwr; + dict<std::tuple<int, int, SigBit, SigBit>, bool> cache_can_collide_together; + dict<std::tuple<int, SigBit, SigBit, bool>, bool> cache_is_w2rbyp; + dict<std::tuple<SigBit, bool>, bool> cache_impossible_with_ren; + + MemQueryCache(QuickConeSat &qcsat, Mem &mem, MemRd &port, FfData &ff) : qcsat(qcsat), mem(mem), port(port), ff(ff) { + // port_ren is an upper bound on when we care about the value fetched + // from memory this cycle. + int ren = ezSAT::CONST_TRUE; + if (ff.has_ce) { + ren = qcsat.importSigBit(ff.sig_ce); + if (!ff.pol_ce) + ren = qcsat.ez->NOT(ren); + } + if (ff.has_srst) { + int nrst = qcsat.importSigBit(ff.sig_srst); + if (ff.pol_srst) + nrst = qcsat.ez->NOT(nrst); + ren = qcsat.ez->AND(ren, nrst); + } + port_ren = ren; + } - vector<Cell*> dff_cells; - dict<SigBit, SigBit> invbits; - dict<SigBit, int> sigbit_users_count; - dict<SigSpec, Cell*> mux_cells_a, mux_cells_b; - pool<Cell*> forward_merged_dffs, candidate_dffs; - FfInitVals initvals; + // Returns ezSAT variable that is true iff the two addresses are the same. + int addr_eq(SigSpec raddr, SigSpec waddr) { + int abits = std::max(GetSize(raddr), GetSize(waddr)); + raddr.extend_u0(abits); + waddr.extend_u0(abits); + return qcsat.ez->vec_eq(qcsat.importSig(raddr), qcsat.importSig(waddr)); + } - MemoryDffWorker(Module *module) : module(module), sigmap(module) - { - initvals.set(&sigmap, module); + // Returns true if a given write port bit can be active at the same time + // as this read port and at the same address. + bool can_collide_rdwr(int widx, SigBit wen) { + std::pair<int, SigBit> key(widx, wen); + auto it = cache_can_collide_rdwr.find(key); + if (it != cache_can_collide_rdwr.end()) + return it->second; + auto &wport = mem.wr_ports[widx]; + int aeq = addr_eq(port.addr, wport.addr); + int wen_sat = qcsat.importSigBit(wen); + qcsat.prepare(); + bool res = qcsat.ez->solve(aeq, wen_sat, port_ren); + cache_can_collide_rdwr[key] = res; + return res; } - bool find_sig_before_dff(RTLIL::SigSpec &sig, RTLIL::SigSpec &clk, bool &clk_polarity) - { - sigmap.apply(sig); + // Returns true if both given write port bits can be active at the same + // time as this read port and at the same address (three-way collision). + bool can_collide_together(int widx1, int widx2, int bitidx) { + auto &wport1 = mem.wr_ports[widx1]; + auto &wport2 = mem.wr_ports[widx2]; + SigBit wen1 = wport1.en[bitidx]; + SigBit wen2 = wport2.en[bitidx]; + std::tuple<int, int, SigBit, SigBit> key(widx1, widx2, wen1, wen2); + auto it = cache_can_collide_together.find(key); + if (it != cache_can_collide_together.end()) + return it->second; + int aeq1 = addr_eq(port.addr, wport1.addr); + int aeq2 = addr_eq(port.addr, wport2.addr); + int wen1_sat = qcsat.importSigBit(wen1); + int wen2_sat = qcsat.importSigBit(wen2); + qcsat.prepare(); + bool res = qcsat.ez->solve(wen1_sat, wen2_sat, aeq1, aeq2, port_ren); + cache_can_collide_together[key] = res; + return res; + } - dict<SigBit, SigBit> cache; + // Returns true if the given mux selection signal is a valid data-bypass + // signal in soft transparency logic for a given write port bit. + bool is_w2rbyp(int widx, SigBit wen, SigBit sel, bool neg_sel) { + std::tuple<int, SigBit, SigBit, bool> key(widx, wen, sel, neg_sel); + auto it = cache_is_w2rbyp.find(key); + if (it != cache_is_w2rbyp.end()) + return it->second; + auto &wport = mem.wr_ports[widx]; + int aeq = addr_eq(port.addr, wport.addr); + int wen_sat = qcsat.importSigBit(wen); + int sel_expected = qcsat.ez->AND(aeq, wen_sat); + int sel_sat = qcsat.importSigBit(sel); + if (neg_sel) + sel_sat = qcsat.ez->NOT(sel_sat); + qcsat.prepare(); + bool res = !qcsat.ez->solve(port_ren, qcsat.ez->XOR(sel_expected, sel_sat)); + cache_is_w2rbyp[key] = res; + return res; + } - for (auto &bit : sig) - { - if (cache.count(bit)) { - bit = cache[bit]; - continue; - } + // Returns true if the given mux selection signal can never be true + // when this port is active. + bool impossible_with_ren(SigBit sel, bool neg_sel) { + std::tuple<SigBit, bool> key(sel, neg_sel); + auto it = cache_impossible_with_ren.find(key); + if (it != cache_impossible_with_ren.end()) + return it->second; + int sel_sat = qcsat.importSigBit(sel); + if (neg_sel) + sel_sat = qcsat.ez->NOT(sel_sat); + qcsat.prepare(); + bool res = !qcsat.ez->solve(port_ren, sel_sat); + cache_impossible_with_ren[key] = res; + return res; + } - if (bit.wire == NULL) - continue; + // Helper for data_eq: walks up a multiplexer when the value of its + // sel signal is constant under the assumption that this read port + // is active and a given other mux sel signal is true. + bool walk_up_mux_cond(SigBit sel, bool neg_sel, SigBit &bit) { + auto &drivers = qcsat.modwalker.signal_drivers[qcsat.modwalker.sigmap(bit)]; + if (GetSize(drivers) != 1) + return false; + auto driver = *drivers.begin(); + if (!driver.cell->type.in(ID($mux), ID($pmux))) + return false; + log_assert(driver.port == ID::Y); + SigSpec sig_s = driver.cell->getPort(ID::S); + int sel_sat = qcsat.importSigBit(sel); + if (neg_sel) + sel_sat = qcsat.ez->NOT(sel_sat); + bool all_0 = true; + int width = driver.cell->parameters.at(ID::WIDTH).as_int(); + for (int i = 0; i < GetSize(sig_s); i++) { + int sbit = qcsat.importSigBit(sig_s[i]); + qcsat.prepare(); + if (!qcsat.ez->solve(port_ren, sel_sat, qcsat.ez->NOT(sbit))) { + bit = driver.cell->getPort(ID::B)[i * width + driver.offset]; + return true; + } + if (qcsat.ez->solve(port_ren, sel_sat, sbit)) + all_0 = false; + } + if (all_0) { + bit = driver.cell->getPort(ID::A)[driver.offset]; + return true; + } + return false; + } - if (initvals(bit) != State::Sx) - return false; + // Returns true if a given data signal is equivalent to another, under + // the assumption that this read port is active and a given mux sel signal + // is true. Used to match transparency logic data with write port data. + // The walk_up_mux_cond part is necessary because write ports in yosys + // tend to be connected to things like (wen ? wdata : 'x). + bool data_eq(SigBit sel, bool neg_sel, SigBit dbit, SigBit odbit) { + if (qcsat.modwalker.sigmap(dbit) == qcsat.modwalker.sigmap(odbit)) + return true; + while (walk_up_mux_cond(sel, neg_sel, dbit)); + while (walk_up_mux_cond(sel, neg_sel, odbit)); + return qcsat.modwalker.sigmap(dbit) == qcsat.modwalker.sigmap(odbit); + } +}; - for (auto cell : dff_cells) - { - SigSpec this_clk = cell->getPort(ID::CLK); - bool this_clk_polarity = cell->parameters[ID::CLK_POLARITY].as_bool(); +struct MemoryDffWorker +{ + Module *module; + ModWalker modwalker; + FfInitVals initvals; + FfMergeHelper merger; - if (invbits.count(this_clk)) { - this_clk = invbits.at(this_clk); - this_clk_polarity = !this_clk_polarity; - } + MemoryDffWorker(Module *module) : module(module), modwalker(module->design) + { + modwalker.setup(module); + initvals.set(&modwalker.sigmap, module); + merger.set(&initvals, module); + } - if (clk != RTLIL::SigSpec(RTLIL::State::Sx)) { - if (this_clk != clk) + // Starting from the output of an async read port, as long as the data + // signal's only user is a mux data signal, passes through the mux + // and remembers information about it. Conceptually works on every + // bit separately, but coalesces the result when possible. + SigSpec walk_muxes(SigSpec data, std::vector<MuxData> &res) { + bool did_something; + do { + did_something = false; + int prev_idx = -1; + Cell *prev_cell = nullptr; + bool prev_is_b = false; + for (int i = 0; i < GetSize(data); i++) { + SigBit bit = modwalker.sigmap(data[i]); + auto &consumers = modwalker.signal_consumers[bit]; + if (GetSize(consumers) != 1 || modwalker.signal_outputs.count(bit)) + continue; + auto consumer = *consumers.begin(); + bool is_b; + if (consumer.cell->type == ID($mux)) { + if (consumer.port == ID::A) { + is_b = false; + } else if (consumer.port == ID::B) { + is_b = true; + } else { continue; - if (this_clk_polarity != clk_polarity) + } + } else if (consumer.cell->type == ID($pmux)) { + if (consumer.port == ID::A) { + is_b = false; + } else { continue; - } - - RTLIL::SigSpec q_norm = cell->getPort(ID::Q); - sigmap.apply(q_norm); - - RTLIL::SigSpec d = q_norm.extract(bit, &cell->getPort(ID::D)); - if (d.size() != 1) + } + } else { continue; - - if (cell->type == ID($sdffce)) { - SigSpec rval = cell->parameters[ID::SRST_VALUE]; - SigSpec rbit = q_norm.extract(bit, &rval); - if (cell->parameters[ID::SRST_POLARITY].as_bool()) - d = module->Mux(NEW_ID, d, rbit, cell->getPort(ID::SRST)); - else - d = module->Mux(NEW_ID, rbit, d, cell->getPort(ID::SRST)); } - - if (cell->type.in(ID($dffe), ID($sdffe), ID($sdffce))) { - if (cell->parameters[ID::EN_POLARITY].as_bool()) - d = module->Mux(NEW_ID, bit, d, cell->getPort(ID::EN)); - else - d = module->Mux(NEW_ID, d, bit, cell->getPort(ID::EN)); + SigSpec y = consumer.cell->getPort(ID::Y); + int mux_width = GetSize(y); + SigBit ybit = y.extract(consumer.offset); + if (prev_cell != consumer.cell || prev_idx+1 != i || prev_is_b != is_b) { + MuxData md; + md.base_idx = i; + md.size = 0; + md.is_b = is_b; + md.sig_s = consumer.cell->getPort(ID::S); + md.sig_other.resize(GetSize(md.sig_s)); + prev_cell = consumer.cell; + prev_is_b = is_b; + res.push_back(md); } - - if (cell->type.in(ID($sdff), ID($sdffe))) { - SigSpec rval = cell->parameters[ID::SRST_VALUE]; - SigSpec rbit = q_norm.extract(bit, &rval); - if (cell->parameters[ID::SRST_POLARITY].as_bool()) - d = module->Mux(NEW_ID, d, rbit, cell->getPort(ID::SRST)); - else - d = module->Mux(NEW_ID, rbit, d, cell->getPort(ID::SRST)); + auto &md = res.back(); + md.size++; + for (int j = 0; j < GetSize(md.sig_s); j++) { + SigBit obit = consumer.cell->getPort(is_b ? ID::A : ID::B).extract(j * mux_width + consumer.offset); + md.sig_other[j].append(obit); } - - cache[bit] = d; - bit = d; - clk = this_clk; - clk_polarity = this_clk_polarity; - candidate_dffs.insert(cell); - goto replaced_this_bit; + prev_idx = i; + data[i] = ybit; + did_something = true; } - - return false; - replaced_this_bit:; - } - - return true; + } while (did_something); + return data; } - bool find_sig_after_dffe(RTLIL::SigSpec &sig, RTLIL::SigSpec &clk, bool &clk_polarity, RTLIL::SigSpec &en, bool &en_polarity) + // Merges FF and possibly soft transparency logic into an asynchronous + // read port, making it into a synchronous one. + // + // There are three moving parts involved here: + // + // - the async port, which we start from, whose data port is input to... + // - an arbitrary chain of $mux and $pmux cells implementing soft transparency + // logic (ie. bypassing write port's data iff the write port is active and + // writing to the same address as this read port), which in turn feeds... + // - a final FF + // + // The async port and the mux chain are not allowed to have any users that + // are not part of the above. + // + // The algorithm is: + // + // 1. Walk through the muxes. + // 2. Recognize the final FF. + // 3. Knowing the FF's clock and read enable, make a list of write ports + // that we'll run transparency analysis on. + // 4. For every mux bit, recognize it as one of: + // - a transparency bypass mux for some port + // - a bypass mux that feeds 'x instead (this will result in collision + // don't care behavior being recognized) + // - a mux that never selects the other value when read port is active, + // and can thus be skipped (this is necessary because this could + // be a transparency bypass mux for never-colliding port that other + // passes failed to optimize) + // - a mux whose other input is 'x, and can thus be skipped + // 5. When recognizing transparency bypasses, take care to preserve priority + // behavior — when two bypasses are sequential muxes on the chain, they + // effectively have priority over one another, and the transform can + // only be performed when either a) their corresponding write ports + // also have priority, or b) there can never be a three-way collision + // between the two write ports and the read port. + // 6. Check consistency of per-bit transparency masks, merge them into + // per-port transparency masks + // 7. If everything went fine in the previous steps, actually perform + // the merge. + void handle_rd_port(Mem &mem, QuickConeSat &qcsat, int idx) { - sigmap.apply(sig); - - for (auto &bit : sig) - { - if (bit.wire == NULL) - continue; - - for (auto cell : dff_cells) - { - if (forward_merged_dffs.count(cell)) - continue; - if (!cell->type.in(ID($dff), ID($dffe))) - continue; - - SigSpec this_clk = cell->getPort(ID::CLK); - bool this_clk_polarity = cell->parameters[ID::CLK_POLARITY].as_bool(); - SigSpec this_en = State::S1; - bool this_en_polarity = true; - - if (cell->type == ID($dffe)) { - this_en = cell->getPort(ID::EN); - this_en_polarity = cell->parameters[ID::EN_POLARITY].as_bool(); - } - - if (invbits.count(this_clk)) { - this_clk = invbits.at(this_clk); - this_clk_polarity = !this_clk_polarity; - } + auto &port = mem.rd_ports[idx]; + log("Checking read port `%s'[%d] in module `%s': ", mem.memid.c_str(), idx, module->name.c_str()); + + std::vector<MuxData> muxdata; + SigSpec data = walk_muxes(port.data, muxdata); + FfData ff; + pool<std::pair<Cell *, int>> bits; + if (!merger.find_output_ff(data, ff, bits)) { + log("no output FF found.\n"); + return; + } + if (!ff.has_clk) { + log("output latches are not supported.\n"); + return; + } + if (ff.has_aload) { + log("output FF has async load, not supported.\n"); + return; + } + if (ff.has_sr) { + // Latches and FFs with SR are not supported. + log("output FF has both set and reset, not supported.\n"); + return; + } - if (invbits.count(this_en)) { - this_en = invbits.at(this_en); - this_en_polarity = !this_en_polarity; + // Construct cache. + MemQueryCache cache(qcsat, mem, port, ff); + + // Prepare information structure about all ports, recognize port bits + // that can never collide at all and don't need to be checked. + std::vector<PortData> portdata; + for (int i = 0; i < GetSize(mem.wr_ports); i++) { + PortData pd; + auto &wport = mem.wr_ports[i]; + pd.relevant = true; + if (!wport.clk_enable) + pd.relevant = false; + if (wport.clk != ff.sig_clk) + pd.relevant = false; + if (wport.clk_polarity != ff.pol_clk) + pd.relevant = false; + // In theory, we *could* support mismatched width + // ports here. However, it's not worth it — wide + // ports are recognized *after* memory_dff in + // a normal flow. + if (wport.wide_log2 != port.wide_log2) + pd.relevant = false; + pd.uncollidable_mask.resize(GetSize(port.data)); + pd.transparency_mask.resize(GetSize(port.data)); + pd.collision_x_mask.resize(GetSize(port.data)); + if (pd.relevant) { + // If we got this far, this port is potentially + // transparent and/or has undefined collision + // behavior. Now, for every bit, check if it can + // ever collide. + for (int j = 0; j < ff.width; j++) { + if (!cache.can_collide_rdwr(i, wport.en[j])) { + pd.uncollidable_mask[j] = true; + pd.collision_x_mask[j] = true; + } } + } + portdata.push_back(pd); + } - if (clk != RTLIL::SigSpec(RTLIL::State::Sx)) { - if (this_clk != clk) - continue; - if (this_clk_polarity != clk_polarity) - continue; - if (this_en != en) - continue; - if (this_en_polarity != en_polarity) - continue; + // Now inspect the mux chain. + for (auto &md : muxdata) { + // We only mark transparent bits after processing a complete + // mux, so that the transparency priority validation check + // below sees transparency information as of previous mux. + std::vector<std::pair<PortData&, int>> trans_queue; + for (int sel_idx = 0; sel_idx < GetSize(md.sig_s); sel_idx++) { + SigBit sbit = md.sig_s[sel_idx]; + SigSpec &odata = md.sig_other[sel_idx]; + for (int bitidx = md.base_idx; bitidx < md.base_idx+md.size; bitidx++) { + SigBit odbit = odata[bitidx-md.base_idx]; + bool recognized = false; + for (int pi = 0; pi < GetSize(mem.wr_ports); pi++) { + auto &pd = portdata[pi]; + auto &wport = mem.wr_ports[pi]; + if (!pd.relevant) + continue; + if (pd.uncollidable_mask[bitidx]) + continue; + bool match = cache.is_w2rbyp(pi, wport.en[bitidx], sbit, md.is_b); + if (!match) + continue; + // If we got here, we recognized this mux sel + // as valid bypass sel for a given port bit. + if (odbit == State::Sx) { + // 'x, mark collision don't care. + pd.collision_x_mask[bitidx] = true; + pd.transparency_mask[bitidx] = false; + } else if (cache.data_eq(sbit, md.is_b, wport.data[bitidx], odbit)) { + // Correct data value, mark transparency, + // but only after verifying that priority + // is fine. + for (int k = 0; k < GetSize(mem.wr_ports); k++) { + if (portdata[k].transparency_mask[bitidx]) { + if (wport.priority_mask[k]) + continue; + if (!cache.can_collide_together(pi, k, bitidx)) + continue; + log("FF found, but transparency logic priority doesn't match write priority.\n"); + return; + } + } + recognized = true; + trans_queue.push_back({pd, bitidx}); + break; + } else { + log("FF found, but with a mux data input that doesn't seem to correspond to transparency logic.\n"); + return; + } + } + if (!recognized) { + // If we haven't positively identified this as + // a bypass: it's still skippable if the + // data is 'x, or if the sel cannot actually be + // active. + if (odbit == State::Sx) + continue; + if (cache.impossible_with_ren(sbit, md.is_b)) + continue; + log("FF found, but with a mux select that doesn't seem to correspond to transparency logic.\n"); + return; + } } + } + // Done with this mux, now actually apply the transparencies. + for (auto it : trans_queue) { + it.first.transparency_mask[it.second] = true; + it.first.collision_x_mask[it.second] = false; + } + } - RTLIL::SigSpec q_norm = cell->getPort(ID::D); - sigmap.apply(q_norm); - - RTLIL::SigSpec d = q_norm.extract(bit, &cell->getPort(ID::Q)); - if (d.size() != 1) + // Final merging and validation of per-bit masks. + for (int pi = 0; pi < GetSize(mem.wr_ports); pi++) { + auto &pd = portdata[pi]; + if (!pd.relevant) + continue; + bool trans = false; + bool non_trans = false; + for (int i = 0; i < ff.width; i++) { + if (pd.collision_x_mask[i]) continue; - - if (initvals(d) != State::Sx) - return false; - - bit = d; - clk = this_clk; - clk_polarity = this_clk_polarity; - en = this_en; - en_polarity = this_en_polarity; - candidate_dffs.insert(cell); - goto replaced_this_bit; + if (pd.transparency_mask[i]) + trans = true; + else + non_trans = true; } - - return false; - replaced_this_bit:; + if (trans && non_trans) { + log("FF found, but soft transparency logic is inconsistent for port %d.\n", pi); + return; + } + pd.final_transparency = trans; + pd.final_collision_x = !trans && !non_trans; } - return true; + // OK, it worked. + log("merging output FF to cell.\n"); + + merger.remove_output_ff(bits); + if (ff.has_ce && !ff.pol_ce) + ff.sig_ce = module->LogicNot(NEW_ID, ff.sig_ce); + if (ff.has_arst && !ff.pol_arst) + ff.sig_arst = module->LogicNot(NEW_ID, ff.sig_arst); + if (ff.has_srst && !ff.pol_srst) + ff.sig_srst = module->LogicNot(NEW_ID, ff.sig_srst); + port.clk = ff.sig_clk; + port.clk_enable = true; + port.clk_polarity = ff.pol_clk; + if (ff.has_ce) + port.en = ff.sig_ce; + else + port.en = State::S1; + if (ff.has_arst) { + port.arst = ff.sig_arst; + port.arst_value = ff.val_arst; + } else { + port.arst = State::S0; + } + if (ff.has_srst) { + port.srst = ff.sig_srst; + port.srst_value = ff.val_srst; + port.ce_over_srst = ff.ce_over_srst; + } else { + port.srst = State::S0; + } + port.init_value = ff.val_init; + port.data = ff.sig_q; + for (int pi = 0; pi < GetSize(mem.wr_ports); pi++) { + auto &pd = portdata[pi]; + if (!pd.relevant) + continue; + if (pd.final_collision_x) { + log(" Write port %d: don't care on collision.\n", pi); + port.collision_x_mask[pi] = true; + } else if (pd.final_transparency) { + log(" Write port %d: transparent.\n", pi); + port.transparency_mask[pi] = true; + } else { + log(" Write port %d: non-transparent.\n", pi); + } + } + mem.emit(); } - void handle_wr_cell(RTLIL::Cell *cell) + void handle_rd_port_addr(Mem &mem, int idx) { - log("Checking cell `%s' in module `%s': ", cell->name.c_str(), module->name.c_str()); + auto &port = mem.rd_ports[idx]; + log("Checking read port address `%s'[%d] in module `%s': ", mem.memid.c_str(), idx, module->name.c_str()); - RTLIL::SigSpec clk = RTLIL::SigSpec(RTLIL::State::Sx); - bool clk_polarity = 0; - candidate_dffs.clear(); - - RTLIL::SigSpec sig_addr = cell->getPort(ID::ADDR); - if (!find_sig_before_dff(sig_addr, clk, clk_polarity)) { - log("no (compatible) $dff for address input found.\n"); + FfData ff; + pool<std::pair<Cell *, int>> bits; + if (!merger.find_input_ff(port.addr, ff, bits)) { + log("no address FF found.\n"); return; } - - RTLIL::SigSpec sig_data = cell->getPort(ID::DATA); - if (!find_sig_before_dff(sig_data, clk, clk_polarity)) { - log("no (compatible) $dff for data input found.\n"); + if (!ff.has_clk) { + log("address latches are not supported.\n"); return; } - - RTLIL::SigSpec sig_en = cell->getPort(ID::EN); - if (!find_sig_before_dff(sig_en, clk, clk_polarity)) { - log("no (compatible) $dff for enable input found.\n"); + if (ff.has_aload) { + log("address FF has async load, not supported.\n"); return; } - - if (clk != RTLIL::SigSpec(RTLIL::State::Sx)) - { - for (auto cell : candidate_dffs) - forward_merged_dffs.insert(cell); - - cell->setPort(ID::CLK, clk); - cell->setPort(ID::ADDR, sig_addr); - cell->setPort(ID::DATA, sig_data); - cell->setPort(ID::EN, sig_en); - cell->parameters[ID::CLK_ENABLE] = RTLIL::Const(1); - cell->parameters[ID::CLK_POLARITY] = RTLIL::Const(clk_polarity); - - log("merged $dff to cell.\n"); + if (ff.has_sr || ff.has_arst) { + log("address FF has async set and/or reset, not supported.\n"); return; } - - log("no (compatible) $dff found.\n"); - } - - void disconnect_dff(RTLIL::SigSpec sig) - { - sigmap.apply(sig); - sig.sort_and_unify(); - - std::stringstream sstr; - sstr << "$memory_dff_disconnected$" << (autoidx++); - - RTLIL::SigSpec new_sig = module->addWire(sstr.str(), sig.size()); - - for (auto cell : module->cells()) - if (cell->type.in(ID($dff), ID($dffe))) { - RTLIL::SigSpec new_q = cell->getPort(ID::Q); - new_q.replace(sig, new_sig); - cell->setPort(ID::Q, new_q); - } - } - - void handle_rd_cell(RTLIL::Cell *cell) - { - log("Checking cell `%s' in module `%s': ", cell->name.c_str(), module->name.c_str()); - - bool clk_polarity = 0; - bool en_polarity = 0; - - RTLIL::SigSpec clk_data = RTLIL::SigSpec(RTLIL::State::Sx); - RTLIL::SigSpec en_data; - RTLIL::SigSpec sig_data = cell->getPort(ID::DATA); - - for (auto bit : sigmap(sig_data)) - if (sigbit_users_count[bit] > 1) - goto skip_ff_after_read_merging; - - if (mux_cells_a.count(sig_data) || mux_cells_b.count(sig_data)) - { - RTLIL::SigSpec en; - std::vector<RTLIL::SigSpec> check_q; - - do { - bool enable_invert = mux_cells_a.count(sig_data) != 0; - Cell *mux = enable_invert ? mux_cells_a.at(sig_data) : mux_cells_b.at(sig_data); - check_q.push_back(sigmap(mux->getPort(enable_invert ? ID::B : ID::A))); - sig_data = sigmap(mux->getPort(ID::Y)); - en.append(enable_invert ? module->LogicNot(NEW_ID, mux->getPort(ID::S)) : mux->getPort(ID::S)); - } while (mux_cells_a.count(sig_data) || mux_cells_b.count(sig_data)); - - for (auto bit : sig_data) - if (sigbit_users_count[bit] > 1) - goto skip_ff_after_read_merging; - - if (find_sig_after_dffe(sig_data, clk_data, clk_polarity, en_data, en_polarity) && clk_data != RTLIL::SigSpec(RTLIL::State::Sx) && - std::all_of(check_q.begin(), check_q.end(), [&](const SigSpec &cq) {return cq == sig_data; })) - { - if (en_data != State::S1 || !en_polarity) { - if (!en_polarity) - en_data = module->LogicNot(NEW_ID, en_data); - en.append(en_data); - } - disconnect_dff(sig_data); - cell->setPort(ID::CLK, clk_data); - cell->setPort(ID::EN, en.size() > 1 ? module->ReduceAnd(NEW_ID, en) : en); - cell->setPort(ID::DATA, sig_data); - cell->parameters[ID::CLK_ENABLE] = RTLIL::Const(1); - cell->parameters[ID::CLK_POLARITY] = RTLIL::Const(clk_polarity); - cell->parameters[ID::TRANSPARENT] = RTLIL::Const(0); - log("merged data $dff with rd enable to cell.\n"); - return; - } + // Trick part: this transform is invalid if the initial + // value of the FF is fully-defined. However, we + // cannot simply reject FFs with any defined init bit, + // as this is often the result of merging a const bit. + if (ff.val_init.is_fully_def()) { + log("address FF has fully-defined init value, not supported.\n"); + return; } - else - { - if (find_sig_after_dffe(sig_data, clk_data, clk_polarity, en_data, en_polarity) && clk_data != RTLIL::SigSpec(RTLIL::State::Sx)) - { - if (!en_polarity) - en_data = module->LogicNot(NEW_ID, en_data); - disconnect_dff(sig_data); - cell->setPort(ID::CLK, clk_data); - cell->setPort(ID::EN, en_data); - cell->setPort(ID::DATA, sig_data); - cell->parameters[ID::CLK_ENABLE] = RTLIL::Const(1); - cell->parameters[ID::CLK_POLARITY] = RTLIL::Const(clk_polarity); - cell->parameters[ID::TRANSPARENT] = RTLIL::Const(0); - log("merged data $dff to cell.\n"); + for (int i = 0; i < GetSize(mem.wr_ports); i++) { + auto &wport = mem.wr_ports[i]; + if (!wport.clk_enable || wport.clk != ff.sig_clk || wport.clk_polarity != ff.pol_clk) { + log("address FF clock is not compatible with write clock.\n"); return; } } - - skip_ff_after_read_merging:; - RTLIL::SigSpec clk_addr = RTLIL::SigSpec(RTLIL::State::Sx); - RTLIL::SigSpec sig_addr = cell->getPort(ID::ADDR); - if (find_sig_before_dff(sig_addr, clk_addr, clk_polarity) && - clk_addr != RTLIL::SigSpec(RTLIL::State::Sx)) - { - cell->setPort(ID::CLK, clk_addr); - cell->setPort(ID::EN, State::S1); - cell->setPort(ID::ADDR, sig_addr); - cell->parameters[ID::CLK_ENABLE] = RTLIL::Const(1); - cell->parameters[ID::CLK_POLARITY] = RTLIL::Const(clk_polarity); - cell->parameters[ID::TRANSPARENT] = RTLIL::Const(1); - log("merged address $dff to cell.\n"); - return; - } - - log("no (compatible) $dff found.\n"); + // Now we're commited to merge it. + merger.mark_input_ff(bits); + // If the address FF has enable and/or sync reset, unmap it. + ff.unmap_ce_srst(); + port.clk = ff.sig_clk; + port.en = State::S1; + port.addr = ff.sig_d; + port.clk_enable = true; + port.clk_polarity = ff.pol_clk; + for (int i = 0; i < GetSize(mem.wr_ports); i++) + port.transparency_mask[i] = true; + mem.emit(); + log("merged address FF to cell.\n"); } - void run(bool flag_wr_only) + void run() { - for (auto wire : module->wires()) { - if (wire->port_output) - for (auto bit : sigmap(wire)) - sigbit_users_count[bit]++; - } - - for (auto cell : module->cells()) { - if (cell->type.in(ID($dff), ID($dffe), ID($sdff), ID($sdffe), ID($sdffce))) - dff_cells.push_back(cell); - if (cell->type == ID($mux)) { - mux_cells_a[sigmap(cell->getPort(ID::A))] = cell; - mux_cells_b[sigmap(cell->getPort(ID::B))] = cell; + std::vector<Mem> memories = Mem::get_selected_memories(module); + for (auto &mem : memories) { + QuickConeSat qcsat(modwalker); + for (int i = 0; i < GetSize(mem.rd_ports); i++) { + if (!mem.rd_ports[i].clk_enable) + handle_rd_port(mem, qcsat, i); } - if (cell->type.in(ID($not), ID($_NOT_)) || (cell->type == ID($logic_not) && GetSize(cell->getPort(ID::A)) == 1)) { - SigSpec sig_a = cell->getPort(ID::A); - SigSpec sig_y = cell->getPort(ID::Y); - if (cell->type == ID($not)) - sig_a.extend_u0(GetSize(sig_y), cell->getParam(ID::A_SIGNED).as_bool()); - if (cell->type == ID($logic_not)) - sig_y.extend_u0(1); - for (int i = 0; i < GetSize(sig_y); i++) - invbits[sig_y[i]] = sig_a[i]; + } + for (auto &mem : memories) { + for (int i = 0; i < GetSize(mem.rd_ports); i++) { + if (!mem.rd_ports[i].clk_enable) + handle_rd_port_addr(mem, i); } - for (auto &conn : cell->connections()) - if (!cell->known() || cell->input(conn.first)) - for (auto bit : sigmap(conn.second)) - sigbit_users_count[bit]++; } - - for (auto cell : module->selected_cells()) - if (cell->type == ID($memwr) && !cell->parameters[ID::CLK_ENABLE].as_bool()) - handle_wr_cell(cell); - - if (!flag_wr_only) - for (auto cell : module->selected_cells()) - if (cell->type == ID($memrd) && !cell->parameters[ID::CLK_ENABLE].as_bool()) - handle_rd_cell(cell); } }; struct MemoryDffPass : public Pass { - MemoryDffPass() : Pass("memory_dff", "merge input/output DFFs into memories") { } + MemoryDffPass() : Pass("memory_dff", "merge input/output DFFs into memory read ports") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" memory_dff [options] [selection]\n"); log("\n"); - log("This pass detects DFFs at memory ports and merges them into the memory port.\n"); + log("This pass detects DFFs at memory read ports and merges them into the memory port.\n"); log("I.e. it consumes an asynchronous memory port and the flip-flops at its\n"); log("interface and yields a synchronous memory port.\n"); log("\n"); - log(" -nordfff\n"); - log(" do not merge registers on read ports\n"); - log("\n"); } void execute(std::vector<std::string> args, RTLIL::Design *design) override { - bool flag_wr_only = false; - - log_header(design, "Executing MEMORY_DFF pass (merging $dff cells to $memrd and $memwr).\n"); + log_header(design, "Executing MEMORY_DFF pass (merging $dff cells to $memrd).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { - if (args[argidx] == "-nordff" || args[argidx] == "-wr_only") { - flag_wr_only = true; - continue; - } break; } extra_args(args, argidx, design); for (auto mod : design->selected_modules()) { MemoryDffWorker worker(mod); - worker.run(flag_wr_only); + worker.run(); } } } MemoryDffPass; diff --git a/passes/memory/memory_map.cc b/passes/memory/memory_map.cc index 032b8fbbd..ca1ca483d 100644 --- a/passes/memory/memory_map.cc +++ b/passes/memory/memory_map.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -34,10 +34,12 @@ struct MemoryMapWorker RTLIL::Design *design; RTLIL::Module *module; + SigMap sigmap; + FfInitVals initvals; std::map<std::pair<RTLIL::SigSpec, RTLIL::SigSpec>, RTLIL::SigBit> decoder_cache; - MemoryMapWorker(RTLIL::Design *design, RTLIL::Module *module) : design(design), module(module) {} + MemoryMapWorker(RTLIL::Design *design, RTLIL::Module *module) : design(design), module(module), sigmap(module), initvals(&sigmap, module) {} std::string map_case(std::string value) const { @@ -151,11 +153,9 @@ struct MemoryMapWorker continue; } if (!port.clk_enable) { - if (port.addr.is_fully_const()) { - // FIXME: Actually we should check for port.en.is_fully_const() also and - // create a $adff cell with this ports port.en input as reset pin when port.en - // is not a simple static 1. - static_cells_map[port.addr.as_int() - mem.start_offset] = port.data; + if (port.addr.is_fully_const() && port.en.is_fully_ones()) { + for (int sub = 0; sub < (1 << port.wide_log2); sub++) + static_cells_map[port.addr.as_int() + sub] = port.data.extract(sub * mem.width, mem.width); static_ports.insert(i); continue; } @@ -176,22 +176,24 @@ struct MemoryMapWorker log("Mapping memory %s in module %s:\n", mem.memid.c_str(), module->name.c_str()); - std::vector<RTLIL::SigSpec> data_reg_in; - std::vector<RTLIL::SigSpec> data_reg_out; + int abits = ceil_log2(mem.size); + std::vector<RTLIL::SigSpec> data_reg_in(1 << abits); + std::vector<RTLIL::SigSpec> data_reg_out(1 << abits); int count_static = 0; for (int i = 0; i < mem.size; i++) { - if (static_cells_map.count(i) > 0) + int addr = i + mem.start_offset; + int idx = addr & ((1 << abits) - 1); + if (static_cells_map.count(addr) > 0) { - data_reg_in.push_back(RTLIL::SigSpec(RTLIL::State::Sz, mem.width)); - data_reg_out.push_back(static_cells_map[i]); + data_reg_out[idx] = static_cells_map[addr]; count_static++; } else { - RTLIL::Cell *c = module->addCell(genid(mem.memid, "", i), ID($dff)); + RTLIL::Cell *c = module->addCell(genid(mem.memid, "", addr), ID($dff)); c->parameters[ID::WIDTH] = mem.width; if (GetSize(refclock) != 0) { c->parameters[ID::CLK_POLARITY] = RTLIL::Const(refclock_pol); @@ -201,13 +203,13 @@ struct MemoryMapWorker c->setPort(ID::CLK, RTLIL::SigSpec(RTLIL::State::S0)); } - RTLIL::Wire *w_in = module->addWire(genid(mem.memid, "", i, "$d"), mem.width); - data_reg_in.push_back(RTLIL::SigSpec(w_in)); - c->setPort(ID::D, data_reg_in.back()); + RTLIL::Wire *w_in = module->addWire(genid(mem.memid, "", addr, "$d"), mem.width); + data_reg_in[idx] = w_in; + c->setPort(ID::D, w_in); - std::string w_out_name = stringf("%s[%d]", mem.memid.c_str(), i); + std::string w_out_name = stringf("%s[%d]", mem.memid.c_str(), addr); if (module->wires_.count(w_out_name) > 0) - w_out_name = genid(mem.memid, "", i, "$q"); + w_out_name = genid(mem.memid, "", addr, "$q"); RTLIL::Wire *w_out = module->addWire(w_out_name, mem.width); SigSpec w_init = init_data.extract(i*mem.width, mem.width); @@ -215,8 +217,8 @@ struct MemoryMapWorker if (!w_init.is_fully_undef()) w_out->attributes[ID::init] = w_init.as_const(); - data_reg_out.push_back(RTLIL::SigSpec(w_out)); - c->setPort(ID::Q, data_reg_out.back()); + data_reg_out[idx] = w_out; + c->setPort(ID::Q, w_out); } } @@ -224,35 +226,31 @@ struct MemoryMapWorker int count_dff = 0, count_mux = 0, count_wrmux = 0; - int abits = ceil_log2(mem.size); for (int i = 0; i < GetSize(mem.rd_ports); i++) { auto &port = mem.rd_ports[i]; - if (mem.extract_rdff(i)) + if (mem.extract_rdff(i, &initvals)) count_dff++; RTLIL::SigSpec rd_addr = port.addr; rd_addr.extend_u0(abits, false); - if (mem.start_offset) - rd_addr = module->Sub(NEW_ID, rd_addr, SigSpec(mem.start_offset, abits)); - std::vector<RTLIL::SigSpec> rd_signals; rd_signals.push_back(port.data); - for (int j = 0; j < abits; j++) + for (int j = 0; j < abits - port.wide_log2; j++) { std::vector<RTLIL::SigSpec> next_rd_signals; for (size_t k = 0; k < rd_signals.size(); k++) { RTLIL::Cell *c = module->addCell(genid(mem.memid, "$rdmux", i, "", j, "", k), ID($mux)); - c->parameters[ID::WIDTH] = mem.width; + c->parameters[ID::WIDTH] = GetSize(port.data); c->setPort(ID::Y, rd_signals[k]); c->setPort(ID::S, rd_addr.extract(abits-j-1, 1)); count_mux++; - c->setPort(ID::A, module->addWire(genid(mem.memid, "$rdmux", i, "", j, "", k, "$a"), mem.width)); - c->setPort(ID::B, module->addWire(genid(mem.memid, "$rdmux", i, "", j, "", k, "$b"), mem.width)); + c->setPort(ID::A, module->addWire(genid(mem.memid, "$rdmux", i, "", j, "", k, "$a"), GetSize(port.data))); + c->setPort(ID::B, module->addWire(genid(mem.memid, "$rdmux", i, "", j, "", k, "$b"), GetSize(port.data))); next_rd_signals.push_back(c->getPort(ID::A)); next_rd_signals.push_back(c->getPort(ID::B)); @@ -261,37 +259,38 @@ struct MemoryMapWorker next_rd_signals.swap(rd_signals); } - for (int j = 0; j < mem.size; j++) - module->connect(RTLIL::SigSig(rd_signals[j], data_reg_out[j])); + for (int j = 0; j < (1 << abits); j++) + if (data_reg_out[j] != SigSpec()) + module->connect(RTLIL::SigSig(rd_signals[j >> port.wide_log2].extract((j & ((1 << port.wide_log2) - 1)) * mem.width, mem.width), data_reg_out[j])); } log(" read interface: %d $dff and %d $mux cells.\n", count_dff, count_mux); for (int i = 0; i < mem.size; i++) { - if (static_cells_map.count(i) > 0) + int addr = i + mem.start_offset; + int idx = addr & ((1 << abits) - 1); + if (static_cells_map.count(addr) > 0) continue; - RTLIL::SigSpec sig = data_reg_out[i]; + RTLIL::SigSpec sig = data_reg_out[idx]; for (int j = 0; j < GetSize(mem.wr_ports); j++) { auto &port = mem.wr_ports[j]; - RTLIL::SigSpec wr_addr = port.addr; - - if (mem.start_offset) - wr_addr = module->Sub(NEW_ID, wr_addr, SigSpec(mem.start_offset, GetSize(wr_addr))); + RTLIL::SigSpec wr_addr = port.addr.extract_end(port.wide_log2); + RTLIL::Wire *w_seladdr = addr_decode(wr_addr, RTLIL::SigSpec(addr >> port.wide_log2, GetSize(wr_addr))); - RTLIL::Wire *w_seladdr = addr_decode(wr_addr, RTLIL::SigSpec(i, GetSize(wr_addr))); + int sub = addr & ((1 << port.wide_log2) - 1); int wr_offset = 0; - while (wr_offset < port.en.size()) + while (wr_offset < mem.width) { int wr_width = 1; - RTLIL::SigSpec wr_bit = port.en.extract(wr_offset, 1); + RTLIL::SigSpec wr_bit = port.en.extract(wr_offset + sub * mem.width, 1); - while (wr_offset + wr_width < port.en.size()) { - RTLIL::SigSpec next_wr_bit = port.en.extract(wr_offset + wr_width, 1); + while (wr_offset + wr_width < mem.width) { + RTLIL::SigSpec next_wr_bit = port.en.extract(wr_offset + wr_width + sub * mem.width, 1); if (next_wr_bit != wr_bit) break; wr_width++; @@ -301,7 +300,7 @@ struct MemoryMapWorker if (wr_bit != State::S1) { - RTLIL::Cell *c = module->addCell(genid(mem.memid, "$wren", i, "", j, "", wr_offset), ID($and)); + RTLIL::Cell *c = module->addCell(genid(mem.memid, "$wren", addr, "", j, "", wr_offset), ID($and)); c->parameters[ID::A_SIGNED] = RTLIL::Const(0); c->parameters[ID::B_SIGNED] = RTLIL::Const(0); c->parameters[ID::A_WIDTH] = RTLIL::Const(1); @@ -310,17 +309,17 @@ struct MemoryMapWorker c->setPort(ID::A, w); c->setPort(ID::B, wr_bit); - w = module->addWire(genid(mem.memid, "$wren", i, "", j, "", wr_offset, "$y")); + w = module->addWire(genid(mem.memid, "$wren", addr, "", j, "", wr_offset, "$y")); c->setPort(ID::Y, RTLIL::SigSpec(w)); } - RTLIL::Cell *c = module->addCell(genid(mem.memid, "$wrmux", i, "", j, "", wr_offset), ID($mux)); + RTLIL::Cell *c = module->addCell(genid(mem.memid, "$wrmux", addr, "", j, "", wr_offset), ID($mux)); c->parameters[ID::WIDTH] = wr_width; c->setPort(ID::A, sig.extract(wr_offset, wr_width)); - c->setPort(ID::B, port.data.extract(wr_offset, wr_width)); + c->setPort(ID::B, port.data.extract(wr_offset + sub * mem.width, wr_width)); c->setPort(ID::S, RTLIL::SigSpec(w)); - w = module->addWire(genid(mem.memid, "$wrmux", i, "", j, "", wr_offset, "$y"), wr_width); + w = module->addWire(genid(mem.memid, "$wrmux", addr, "", j, "", wr_offset, "$y"), wr_width); c->setPort(ID::Y, w); sig.replace(wr_offset, w); @@ -329,7 +328,7 @@ struct MemoryMapWorker } } - module->connect(RTLIL::SigSig(data_reg_in[i], sig)); + module->connect(RTLIL::SigSig(data_reg_in[idx], sig)); } log(" write interface: %d write mux blocks.\n", count_wrmux); diff --git a/passes/memory/memory_memx.cc b/passes/memory/memory_memx.cc index 02e00cf30..7edc26caa 100644 --- a/passes/memory/memory_memx.cc +++ b/passes/memory/memory_memx.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -17,11 +17,8 @@ * */ -#include "kernel/register.h" -#include "kernel/log.h" -#include <sstream> -#include <set> -#include <stdlib.h> +#include "kernel/yosys.h" +#include "kernel/mem.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -38,53 +35,45 @@ struct MemoryMemxPass : public Pass { log("behavior for out-of-bounds memory reads and writes.\n"); log("\n"); } + + SigSpec make_addr_check(Mem &mem, SigSpec addr) { + int start_addr = mem.start_offset; + int end_addr = mem.start_offset + mem.size; + + addr.extend_u0(32); + + SigSpec res = mem.module->Nex(NEW_ID, mem.module->ReduceXor(NEW_ID, addr), mem.module->ReduceXor(NEW_ID, {addr, State::S1})); + if (start_addr != 0) + res = mem.module->LogicAnd(NEW_ID, res, mem.module->Ge(NEW_ID, addr, start_addr)); + res = mem.module->LogicAnd(NEW_ID, res, mem.module->Lt(NEW_ID, addr, end_addr)); + return res; + } + void execute(std::vector<std::string> args, RTLIL::Design *design) override { 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()) + for (auto &mem : Mem::get_selected_memories(module)) { - vector<Cell*> mem_port_cells; - - for (auto cell : module->selected_cells()) - if (cell->type.in(ID($memrd), ID($memwr))) - mem_port_cells.push_back(cell); - - for (auto cell : mem_port_cells) + for (auto &port : mem.rd_ports) { - IdString memid = cell->getParam(ID::MEMID).decode_string(); - RTLIL::Memory *mem = module->memories.at(memid); + if (port.clk_enable) + log_error("Memory %s.%s has a synchronous read port. Synchronous read ports are not supported by memory_memx!\n", + log_id(module), log_id(mem.memid)); - int lowest_addr = mem->start_offset; - int highest_addr = mem->start_offset + mem->size - 1; - - SigSpec addr = cell->getPort(ID::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 == ID($memrd)) - { - if (cell->getParam(ID::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(ID::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(ID::DATA, raw_rdata); - } + SigSpec addr_ok = make_addr_check(mem, port.addr); + Wire *raw_rdata = module->addWire(NEW_ID, GetSize(port.data)); + module->addMux(NEW_ID, SigSpec(State::Sx, GetSize(port.data)), raw_rdata, addr_ok, port.data); + port.data = raw_rdata; + } - if (cell->type == ID($memwr)) - { - SigSpec en = cell->getPort(ID::EN); - en = module->And(NEW_ID, en, addr_ok.repeat(GetSize(en))); - cell->setPort(ID::EN, en); - } + for (auto &port : mem.wr_ports) { + SigSpec addr_ok = make_addr_check(mem, port.addr); + port.en = module->And(NEW_ID, port.en, addr_ok.repeat(GetSize(port.en))); } + + mem.emit(); } } } MemoryMemxPass; diff --git a/passes/memory/memory_narrow.cc b/passes/memory/memory_narrow.cc new file mode 100644 index 000000000..cf5e43465 --- /dev/null +++ b/passes/memory/memory_narrow.cc @@ -0,0 +1,67 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2020 Marcelina KoÅ›cielnicka <mwk@0x04.net> + * + * 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" +#include "kernel/mem.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct MemoryNarrowPass : public Pass { + MemoryNarrowPass() : Pass("memory_narrow", "split up wide memory ports") { } + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" memory_narrow [options] [selection]\n"); + log("\n"); + log("This pass splits up wide memory ports into several narrow ports.\n"); + log("\n"); + } + void execute(std::vector<std::string> args, RTLIL::Design *design) override + { + log_header(design, "Executing MEMORY_NARROW pass (splitting up wide memory ports).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) { + for (auto &mem : Mem::get_selected_memories(module)) + { + bool wide = false; + for (auto &port : mem.rd_ports) + if (port.wide_log2) + wide = true; + for (auto &port : mem.wr_ports) + if (port.wide_log2) + wide = true; + if (wide) { + mem.narrow(); + mem.emit(); + } + } + } + } +} MemoryNarrowPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/memory/memory_nordff.cc b/passes/memory/memory_nordff.cc index a4fdcfc38..3253c8f60 100644 --- a/passes/memory/memory_nordff.cc +++ b/passes/memory/memory_nordff.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -33,7 +33,7 @@ struct MemoryNordffPass : public Pass { log(" memory_nordff [options] [selection]\n"); log("\n"); log("This pass extracts FFs from memory read ports. This results in a netlist\n"); - log("similar to what one would get from calling memory_dff with -nordff.\n"); + log("similar to what one would get from not calling memory_dff.\n"); log("\n"); } void execute(std::vector<std::string> args, RTLIL::Design *design) override @@ -51,15 +51,22 @@ struct MemoryNordffPass : public Pass { extra_args(args, argidx, design); for (auto module : design->selected_modules()) - for (auto &mem : Mem::get_selected_memories(module)) { - bool changed = false; - for (int i = 0; i < GetSize(mem.rd_ports); i++) - if (mem.extract_rdff(i)) - changed = true; + SigMap sigmap(module); + FfInitVals initvals(&sigmap, module); + for (auto &mem : Mem::get_selected_memories(module)) + { + bool changed = false; + for (int i = 0; i < GetSize(mem.rd_ports); i++) { + if (mem.rd_ports[i].clk_enable) { + mem.extract_rdff(i, &initvals); + changed = true; + } + } - if (changed) - mem.emit(); + if (changed) + mem.emit(); + } } } } MemoryNordffPass; diff --git a/passes/memory/memory_share.cc b/passes/memory/memory_share.cc index 7315aeae1..ceea725d8 100644 --- a/passes/memory/memory_share.cc +++ b/passes/memory/memory_share.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -18,467 +18,260 @@ */ #include "kernel/yosys.h" -#include "kernel/satgen.h" +#include "kernel/qcsat.h" #include "kernel/sigtools.h" #include "kernel/modtools.h" +#include "kernel/mem.h" +#include "kernel/ffinit.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -bool memcells_cmp(RTLIL::Cell *a, RTLIL::Cell *b) -{ - if (a->type == ID($memrd) && b->type == ID($memrd)) - return a->name < b->name; - if (a->type == ID($memrd) || b->type == ID($memrd)) - return (a->type == ID($memrd)) < (b->type == ID($memrd)); - return a->parameters.at(ID::PRIORITY).as_int() < b->parameters.at(ID::PRIORITY).as_int(); -} - struct MemoryShareWorker { RTLIL::Design *design; RTLIL::Module *module; SigMap sigmap, sigmap_xmux; ModWalker modwalker; - CellTypes cone_ct; - - std::map<RTLIL::SigBit, std::pair<RTLIL::Cell*, int>> sig_to_mux; - std::map<pair<std::set<std::map<SigBit, bool>>, SigBit>, SigBit> conditions_logic_cache; - - - // ----------------------------------------------------------------- - // Converting feedbacks to async read ports to proper enable signals - // ----------------------------------------------------------------- - - bool find_data_feedback(const std::set<RTLIL::SigBit> &async_rd_bits, RTLIL::SigBit sig, - std::map<RTLIL::SigBit, bool> &state, std::set<std::map<RTLIL::SigBit, bool>> &conditions) - { - if (async_rd_bits.count(sig)) { - conditions.insert(state); - return true; - } - - if (sig_to_mux.count(sig) == 0) - return false; - - RTLIL::Cell *cell = sig_to_mux.at(sig).first; - int bit_idx = sig_to_mux.at(sig).second; - - std::vector<RTLIL::SigBit> sig_a = sigmap(cell->getPort(ID::A)); - std::vector<RTLIL::SigBit> sig_b = sigmap(cell->getPort(ID::B)); - std::vector<RTLIL::SigBit> sig_s = sigmap(cell->getPort(ID::S)); - std::vector<RTLIL::SigBit> sig_y = sigmap(cell->getPort(ID::Y)); - log_assert(sig_y.at(bit_idx) == sig); - - for (int i = 0; i < int(sig_s.size()); i++) - if (state.count(sig_s[i]) && state.at(sig_s[i]) == true) { - if (find_data_feedback(async_rd_bits, sig_b.at(bit_idx + i*sig_y.size()), state, conditions)) { - RTLIL::SigSpec new_b = cell->getPort(ID::B); - new_b.replace(bit_idx + i*sig_y.size(), RTLIL::State::Sx); - cell->setPort(ID::B, new_b); - } + FfInitVals initvals; + bool flag_widen; + bool flag_sat; + + // -------------------------------------------------- + // Consolidate read ports that read the same address + // (or close enough to be merged to wide ports) + // -------------------------------------------------- + + // A simple function to detect ports that couldn't possibly collide + // because of opposite const address bits (simplistic, but enough + // to fix problems with inferring wide ports). + bool rdwr_can_collide(Mem &mem, int ridx, int widx) { + auto &rport = mem.rd_ports[ridx]; + auto &wport = mem.wr_ports[widx]; + for (int i = std::max(rport.wide_log2, wport.wide_log2); i < GetSize(rport.addr) && i < GetSize(wport.addr); i++) { + if (rport.addr[i] == State::S1 && wport.addr[i] == State::S0) + return false; + if (rport.addr[i] == State::S0 && wport.addr[i] == State::S1) return false; - } - - - for (int i = 0; i < int(sig_s.size()); i++) - { - if (state.count(sig_s[i]) && state.at(sig_s[i]) == false) - continue; - - std::map<RTLIL::SigBit, bool> new_state = state; - new_state[sig_s[i]] = true; - - if (find_data_feedback(async_rd_bits, sig_b.at(bit_idx + i*sig_y.size()), new_state, conditions)) { - RTLIL::SigSpec new_b = cell->getPort(ID::B); - new_b.replace(bit_idx + i*sig_y.size(), RTLIL::State::Sx); - cell->setPort(ID::B, new_b); - } - } - - std::map<RTLIL::SigBit, bool> new_state = state; - for (int i = 0; i < int(sig_s.size()); i++) - new_state[sig_s[i]] = false; - - if (find_data_feedback(async_rd_bits, sig_a.at(bit_idx), new_state, conditions)) { - RTLIL::SigSpec new_a = cell->getPort(ID::A); - new_a.replace(bit_idx, RTLIL::State::Sx); - cell->setPort(ID::A, new_a); } - - return false; + return true; } - RTLIL::SigBit conditions_to_logic(std::set<std::map<RTLIL::SigBit, bool>> &conditions, SigBit olden, int &created_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) { - RTLIL::SigSpec sig1, sig2; - for (auto &it : cond) { - sig1.append(it.first); - sig2.append(it.second ? RTLIL::State::S1 : RTLIL::State::S0); - } - terms.append(module->Ne(NEW_ID, sig1, sig2)); - created_conditions++; + bool merge_rst_value(Mem &mem, Const &res, int wide_log2, const Const &src1, int sub1, const Const &src2, int sub2) { + res = Const(State::Sx, mem.width << wide_log2); + for (int i = 0; i < GetSize(src1); i++) + res[i + sub1 * mem.width] = src1[i]; + for (int i = 0; i < GetSize(src2); i++) { + if (src2[i] == State::Sx) + continue; + auto &dst = res[i + sub2 * mem.width]; + if (dst == src2[i]) + continue; + if (dst != State::Sx) + return false; + dst = src2[i]; } - - if (olden.wire != nullptr || olden != State::S1) - terms.append(olden); - - if (GetSize(terms) == 0) - terms = State::S1; - - if (GetSize(terms) > 1) - terms = module->ReduceAnd(NEW_ID, terms); - - return conditions_logic_cache[key] = terms; + return true; } - void translate_rd_feedback_to_en(std::string memid, std::vector<RTLIL::Cell*> &rd_ports, std::vector<RTLIL::Cell*> &wr_ports) + bool consolidate_rd_by_addr(Mem &mem) { - std::map<RTLIL::SigSpec, std::vector<std::set<RTLIL::SigBit>>> async_rd_bits; - std::map<RTLIL::SigBit, std::set<RTLIL::SigBit>> muxtree_upstream_map; - std::set<RTLIL::SigBit> non_feedback_nets; - - 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 : module->cells()) - { - bool ignore_data_port = false; - - if (cell->type.in(ID($mux), ID($pmux))) - { - std::vector<RTLIL::SigBit> sig_a = sigmap(cell->getPort(ID::A)); - std::vector<RTLIL::SigBit> sig_b = sigmap(cell->getPort(ID::B)); - std::vector<RTLIL::SigBit> sig_s = sigmap(cell->getPort(ID::S)); - std::vector<RTLIL::SigBit> sig_y = sigmap(cell->getPort(ID::Y)); - - non_feedback_nets.insert(sig_s.begin(), sig_s.end()); + if (GetSize(mem.rd_ports) <= 1) + return false; - for (int i = 0; i < int(sig_y.size()); i++) { - muxtree_upstream_map[sig_y[i]].insert(sig_a[i]); - for (int j = 0; j < int(sig_s.size()); j++) - muxtree_upstream_map[sig_y[i]].insert(sig_b[i + j*sig_y.size()]); - } + log("Consolidating read ports of memory %s.%s by address:\n", log_id(module), log_id(mem.memid)); + bool changed = false; + for (int i = 0; i < GetSize(mem.rd_ports); i++) + { + auto &port1 = mem.rd_ports[i]; + if (port1.removed) continue; - } - - if (cell->type.in(ID($memwr), ID($memrd)) && - cell->parameters.at(ID::MEMID).decode_string() == memid) - ignore_data_port = true; - - for (auto conn : cell->connections()) + for (int j = i + 1; j < GetSize(mem.rd_ports); j++) { - if (ignore_data_port && conn.first == ID::DATA) + auto &port2 = mem.rd_ports[j]; + if (port2.removed) continue; - std::vector<RTLIL::SigBit> bits = sigmap(conn.second); - non_feedback_nets.insert(bits.begin(), bits.end()); - } - } - - std::set<RTLIL::SigBit> expand_non_feedback_nets = non_feedback_nets; - while (!expand_non_feedback_nets.empty()) - { - std::set<RTLIL::SigBit> new_expand_non_feedback_nets; - - for (auto &bit : expand_non_feedback_nets) - if (muxtree_upstream_map.count(bit)) - for (auto &new_bit : muxtree_upstream_map.at(bit)) - if (!non_feedback_nets.count(new_bit)) { - non_feedback_nets.insert(new_bit); - new_expand_non_feedback_nets.insert(new_bit); - } - - expand_non_feedback_nets.swap(new_expand_non_feedback_nets); - } - - for (auto cell : rd_ports) - { - if (cell->parameters.at(ID::CLK_ENABLE).as_bool()) - continue; - - RTLIL::SigSpec sig_addr = sigmap(cell->getPort(ID::ADDR)); - std::vector<RTLIL::SigBit> sig_data = sigmap(cell->getPort(ID::DATA)); - - for (int i = 0; i < int(sig_data.size()); i++) - if (non_feedback_nets.count(sig_data[i])) - goto not_pure_feedback_port; - - async_rd_bits[sig_addr].resize(max(async_rd_bits.size(), sig_data.size())); - for (int i = 0; i < int(sig_data.size()); i++) - async_rd_bits[sig_addr][i].insert(sig_data[i]); - - not_pure_feedback_port:; - } - - if (async_rd_bits.empty()) - return; - - log("Populating enable bits on write ports of memory %s.%s with aync read feedback:\n", log_id(module), log_id(memid)); - - for (auto cell : wr_ports) - { - RTLIL::SigSpec sig_addr = sigmap_xmux(cell->getPort(ID::ADDR)); - if (!async_rd_bits.count(sig_addr)) - continue; - - log(" Analyzing write port %s.\n", log_id(cell)); - - std::vector<RTLIL::SigBit> cell_data = cell->getPort(ID::DATA); - std::vector<RTLIL::SigBit> cell_en = cell->getPort(ID::EN); - - int created_conditions = 0; - for (int i = 0; i < int(cell_data.size()); i++) - if (cell_en[i] != RTLIL::SigBit(RTLIL::State::S0)) + if (port1.clk_enable != port2.clk_enable) + continue; + if (port1.clk_enable) { + if (port1.clk != port2.clk) + continue; + if (port1.clk_polarity != port2.clk_polarity) + continue; + } + if (port1.en != port2.en) + continue; + if (port1.arst != port2.arst) + continue; + if (port1.srst != port2.srst) + continue; + if (port1.ce_over_srst != port2.ce_over_srst) + continue; + // If the width of the ports doesn't match, they can still be + // merged by widening the narrow one. Check if the conditions + // hold for that. + int wide_log2 = std::max(port1.wide_log2, port2.wide_log2); + SigSpec addr1 = sigmap_xmux(port1.addr); + SigSpec addr2 = sigmap_xmux(port2.addr); + if (GetSize(addr1) <= wide_log2) + continue; + if (GetSize(addr2) <= wide_log2) + continue; + if (!addr1.extract(0, wide_log2).is_fully_const()) + continue; + if (!addr2.extract(0, wide_log2).is_fully_const()) + continue; + if (addr1.extract_end(wide_log2) != addr2.extract_end(wide_log2)) { + // Incompatible addresses after widening. Last chance — widen both + // ports by one more bit to merge them. + if (!flag_widen) + continue; + wide_log2++; + if (addr1.extract_end(wide_log2) != addr2.extract_end(wide_log2)) + continue; + if (!addr1.extract(0, wide_log2).is_fully_const()) + continue; + if (!addr2.extract(0, wide_log2).is_fully_const()) + continue; + } + // Combine init/reset values. + SigSpec sub1_c = port1.addr.extract(0, wide_log2); + log_assert(sub1_c.is_fully_const()); + int sub1 = sub1_c.as_int(); + SigSpec sub2_c = port2.addr.extract(0, wide_log2); + log_assert(sub2_c.is_fully_const()); + int sub2 = sub2_c.as_int(); + Const init_value, arst_value, srst_value; + if (!merge_rst_value(mem, init_value, wide_log2, port1.init_value, sub1, port2.init_value, sub2)) + continue; + if (!merge_rst_value(mem, arst_value, wide_log2, port1.arst_value, sub1, port2.arst_value, sub2)) + continue; + if (!merge_rst_value(mem, srst_value, wide_log2, port1.srst_value, sub1, port2.srst_value, sub2)) + continue; + // At this point we are committed to the merge. { - std::map<RTLIL::SigBit, bool> state; - std::set<std::map<RTLIL::SigBit, bool>> conditions; - - find_data_feedback(async_rd_bits.at(sig_addr).at(i), cell_data[i], state, conditions); - cell_en[i] = conditions_to_logic(conditions, cell_en[i], created_conditions); + log(" Merging ports %d, %d (address %s).\n", i, j, log_signal(port1.addr)); + port1.addr = addr1; + port2.addr = addr2; + mem.prepare_rd_merge(i, j, &initvals); + mem.widen_prep(wide_log2); + SigSpec new_data = module->addWire(NEW_ID, mem.width << wide_log2); + module->connect(port1.data, new_data.extract(sub1 * mem.width, mem.width << port1.wide_log2)); + module->connect(port2.data, new_data.extract(sub2 * mem.width, mem.width << port2.wide_log2)); + for (int k = 0; k < wide_log2; k++) + port1.addr[k] = State::S0; + port1.init_value = init_value; + port1.arst_value = arst_value; + port1.srst_value = srst_value; + port1.wide_log2 = wide_log2; + port1.data = new_data; + port2.removed = true; + changed = true; } - - if (created_conditions) { - log(" Added enable logic for %d different cases.\n", created_conditions); - cell->setPort(ID::EN, cell_en); } } + + if (changed) + mem.emit(); + + return changed; } // ------------------------------------------------------ // Consolidate write ports that write to the same address + // (or close enough to be merged to wide ports) // ------------------------------------------------------ - RTLIL::SigSpec mask_en_naive(RTLIL::SigSpec do_mask, RTLIL::SigSpec bits, RTLIL::SigSpec mask_bits) + bool consolidate_wr_by_addr(Mem &mem) { - // this is the naive version of the function that does not care about grouping the EN bits. - - RTLIL::SigSpec inv_mask_bits = module->Not(NEW_ID, mask_bits); - RTLIL::SigSpec inv_mask_bits_filtered = module->Mux(NEW_ID, RTLIL::SigSpec(RTLIL::State::S1, bits.size()), inv_mask_bits, do_mask); - RTLIL::SigSpec result = module->And(NEW_ID, inv_mask_bits_filtered, bits); - return result; - } - - RTLIL::SigSpec mask_en_grouped(RTLIL::SigSpec do_mask, RTLIL::SigSpec bits, RTLIL::SigSpec mask_bits) - { - // this version of the function preserves the bit grouping in the EN bits. - - std::vector<RTLIL::SigBit> v_bits = bits; - std::vector<RTLIL::SigBit> v_mask_bits = mask_bits; - - std::map<std::pair<RTLIL::SigBit, RTLIL::SigBit>, std::pair<int, std::vector<int>>> groups; - RTLIL::SigSpec grouped_bits, grouped_mask_bits; - - for (int i = 0; i < bits.size(); i++) { - std::pair<RTLIL::SigBit, RTLIL::SigBit> key(v_bits[i], v_mask_bits[i]); - if (groups.count(key) == 0) { - groups[key].first = grouped_bits.size(); - grouped_bits.append(v_bits[i]); - grouped_mask_bits.append(v_mask_bits[i]); - } - groups[key].second.push_back(i); - } - - std::vector<RTLIL::SigBit> grouped_result = mask_en_naive(do_mask, grouped_bits, grouped_mask_bits); - RTLIL::SigSpec result; - - for (int i = 0; i < bits.size(); i++) { - std::pair<RTLIL::SigBit, RTLIL::SigBit> key(v_bits[i], v_mask_bits[i]); - result.append(grouped_result.at(groups.at(key).first)); - } - - return result; - } - - void merge_en_data(RTLIL::SigSpec &merged_en, RTLIL::SigSpec &merged_data, RTLIL::SigSpec next_en, RTLIL::SigSpec next_data) - { - std::vector<RTLIL::SigBit> v_old_en = merged_en; - std::vector<RTLIL::SigBit> v_next_en = next_en; - - // The new merged_en signal is just the old merged_en signal and next_en OR'ed together. - // But of course we need to preserve the bit grouping.. - - std::map<std::pair<RTLIL::SigBit, RTLIL::SigBit>, int> groups; - std::vector<RTLIL::SigBit> grouped_old_en, grouped_next_en; - RTLIL::SigSpec new_merged_en; - - for (int i = 0; i < int(v_old_en.size()); i++) { - std::pair<RTLIL::SigBit, RTLIL::SigBit> key(v_old_en[i], v_next_en[i]); - if (groups.count(key) == 0) { - groups[key] = grouped_old_en.size(); - grouped_old_en.push_back(key.first); - grouped_next_en.push_back(key.second); - } - } - - std::vector<RTLIL::SigBit> grouped_new_en = module->Or(NEW_ID, grouped_old_en, grouped_next_en); - - for (int i = 0; i < int(v_old_en.size()); i++) { - std::pair<RTLIL::SigBit, RTLIL::SigBit> key(v_old_en[i], v_next_en[i]); - new_merged_en.append(grouped_new_en.at(groups.at(key))); - } - - // Create the new merged_data signal. - - RTLIL::SigSpec new_merged_data(RTLIL::State::Sx, merged_data.size()); - - RTLIL::SigSpec old_data_set = module->And(NEW_ID, merged_en, merged_data); - RTLIL::SigSpec old_data_unset = module->And(NEW_ID, merged_en, module->Not(NEW_ID, merged_data)); - - RTLIL::SigSpec new_data_set = module->And(NEW_ID, next_en, next_data); - RTLIL::SigSpec new_data_unset = module->And(NEW_ID, next_en, module->Not(NEW_ID, next_data)); - - new_merged_data = module->Or(NEW_ID, new_merged_data, old_data_set); - new_merged_data = module->And(NEW_ID, new_merged_data, module->Not(NEW_ID, old_data_unset)); - - new_merged_data = module->Or(NEW_ID, new_merged_data, new_data_set); - new_merged_data = module->And(NEW_ID, new_merged_data, module->Not(NEW_ID, new_data_unset)); - - // Update merged_* signals - - merged_en = new_merged_en; - merged_data = new_merged_data; - } - - void consolidate_wr_by_addr(std::string memid, std::vector<RTLIL::Cell*> &wr_ports) - { - if (wr_ports.size() <= 1) - return; - - log("Consolidating write ports of memory %s.%s by address:\n", log_id(module), log_id(memid)); - - std::map<RTLIL::SigSpec, int> last_port_by_addr; - std::vector<std::vector<bool>> active_bits_on_port; + if (GetSize(mem.wr_ports) <= 1) + return false; - bool cache_clk_enable = false; - bool cache_clk_polarity = false; - RTLIL::SigSpec cache_clk; + log("Consolidating write ports of memory %s.%s by address:\n", log_id(module), log_id(mem.memid)); - for (int i = 0; i < int(wr_ports.size()); i++) + bool changed = false; + for (int i = 0; i < GetSize(mem.wr_ports); i++) { - RTLIL::Cell *cell = wr_ports.at(i); - RTLIL::SigSpec addr = sigmap_xmux(cell->getPort(ID::ADDR)); - - if (cell->parameters.at(ID::CLK_ENABLE).as_bool() != cache_clk_enable || - (cache_clk_enable && (sigmap(cell->getPort(ID::CLK)) != cache_clk || - cell->parameters.at(ID::CLK_POLARITY).as_bool() != cache_clk_polarity))) - { - cache_clk_enable = cell->parameters.at(ID::CLK_ENABLE).as_bool(); - cache_clk_polarity = cell->parameters.at(ID::CLK_POLARITY).as_bool(); - cache_clk = sigmap(cell->getPort(ID::CLK)); - last_port_by_addr.clear(); - - if (cache_clk_enable) - log(" New clock domain: %s %s\n", cache_clk_polarity ? "posedge" : "negedge", log_signal(cache_clk)); - else - log(" New clock domain: unclocked\n"); - } - - log(" Port %d (%s) has addr %s.\n", i, log_id(cell), log_signal(addr)); - - log(" Active bits: "); - std::vector<RTLIL::SigBit> en_bits = sigmap(cell->getPort(ID::EN)); - active_bits_on_port.push_back(std::vector<bool>(en_bits.size())); - for (int k = int(en_bits.size())-1; k >= 0; k--) { - active_bits_on_port[i][k] = en_bits[k].wire != NULL || en_bits[k].data != RTLIL::State::S0; - log("%c", active_bits_on_port[i][k] ? '1' : '0'); - } - log("\n"); - - if (last_port_by_addr.count(addr)) + auto &port1 = mem.wr_ports[i]; + if (port1.removed) + continue; + if (!port1.clk_enable) + continue; + for (int j = i + 1; j < GetSize(mem.wr_ports); j++) { - int last_i = last_port_by_addr.at(addr); - log(" Merging port %d into this one.\n", last_i); - - bool found_overlapping_bits = false; - for (int k = 0; k < int(en_bits.size()); k++) { - if (active_bits_on_port[i][k] && active_bits_on_port[last_i][k]) - found_overlapping_bits = true; - active_bits_on_port[i][k] = active_bits_on_port[i][k] || active_bits_on_port[last_i][k]; - } - - // Force this ports addr input to addr directly (skip don't care muxes) - - cell->setPort(ID::ADDR, addr); - - // If any of the ports between `last_i' and `i' write to the same address, this - // will have priority over whatever `last_i` wrote. So we need to revisit those - // ports and mask the EN bits accordingly. - - RTLIL::SigSpec merged_en = sigmap(wr_ports[last_i]->getPort(ID::EN)); - - for (int j = last_i+1; j < i; j++) - { - if (wr_ports[j] == NULL) + auto &port2 = mem.wr_ports[j]; + if (port2.removed) + continue; + if (!port2.clk_enable) + continue; + if (port1.clk != port2.clk) + continue; + if (port1.clk_polarity != port2.clk_polarity) + continue; + // If the width of the ports doesn't match, they can still be + // merged by widening the narrow one. Check if the conditions + // hold for that. + int wide_log2 = std::max(port1.wide_log2, port2.wide_log2); + SigSpec addr1 = sigmap_xmux(port1.addr); + SigSpec addr2 = sigmap_xmux(port2.addr); + if (GetSize(addr1) <= wide_log2) + continue; + if (GetSize(addr2) <= wide_log2) + continue; + if (!addr1.extract(0, wide_log2).is_fully_const()) + continue; + if (!addr2.extract(0, wide_log2).is_fully_const()) + continue; + if (addr1.extract_end(wide_log2) != addr2.extract_end(wide_log2)) { + // Incompatible addresses after widening. Last chance — widen both + // ports by one more bit to merge them. + if (!flag_widen) + continue; + wide_log2++; + if (addr1.extract_end(wide_log2) != addr2.extract_end(wide_log2)) + continue; + if (!addr1.extract(0, wide_log2).is_fully_const()) + continue; + if (!addr2.extract(0, wide_log2).is_fully_const()) continue; - - for (int k = 0; k < int(en_bits.size()); k++) - if (active_bits_on_port[i][k] && active_bits_on_port[j][k]) - goto found_overlapping_bits_i_j; - - if (0) { - found_overlapping_bits_i_j: - log(" Creating collosion-detect logic for port %d.\n", j); - RTLIL::SigSpec is_same_addr = module->addWire(NEW_ID); - module->addEq(NEW_ID, addr, wr_ports[j]->getPort(ID::ADDR), is_same_addr); - merged_en = mask_en_grouped(is_same_addr, merged_en, sigmap(wr_ports[j]->getPort(ID::EN))); - } } - - // Then we need to merge the (masked) EN and the DATA signals. - - RTLIL::SigSpec merged_data = wr_ports[last_i]->getPort(ID::DATA); - if (found_overlapping_bits) { - log(" Creating logic for merging DATA and EN ports.\n"); - merge_en_data(merged_en, merged_data, sigmap(cell->getPort(ID::EN)), sigmap(cell->getPort(ID::DATA))); - } else { - RTLIL::SigSpec cell_en = sigmap(cell->getPort(ID::EN)); - RTLIL::SigSpec cell_data = sigmap(cell->getPort(ID::DATA)); - for (int k = 0; k < int(en_bits.size()); k++) - if (!active_bits_on_port[last_i][k]) { - merged_en.replace(k, cell_en.extract(k, 1)); - merged_data.replace(k, cell_data.extract(k, 1)); - } + log(" Merging ports %d, %d (address %s).\n", i, j, log_signal(addr1)); + port1.addr = addr1; + port2.addr = addr2; + mem.prepare_wr_merge(i, j, &initvals); + mem.widen_wr_port(i, wide_log2); + mem.widen_wr_port(j, wide_log2); + int pos = 0; + while (pos < GetSize(port1.data)) { + int epos = pos; + while (epos < GetSize(port1.data) && port1.en[epos] == port1.en[pos] && port2.en[epos] == port2.en[pos]) + epos++; + int width = epos - pos; + SigBit new_en; + if (port2.en[pos] == State::S0) { + new_en = port1.en[pos]; + } else if (port1.en[pos] == State::S0) { + port1.data.replace(pos, port2.data.extract(pos, width)); + new_en = port2.en[pos]; + } else { + port1.data.replace(pos, module->Mux(NEW_ID, port1.data.extract(pos, width), port2.data.extract(pos, width), port2.en[pos])); + new_en = module->Or(NEW_ID, port1.en[pos], port2.en[pos]); + } + for (int k = pos; k < epos; k++) + port1.en[k] = new_en; + pos = epos; } - - // Connect the new EN and DATA signals and remove the old write port. - - cell->setPort(ID::EN, merged_en); - cell->setPort(ID::DATA, merged_data); - - module->remove(wr_ports[last_i]); - wr_ports[last_i] = NULL; - - log(" Active bits: "); - std::vector<RTLIL::SigBit> en_bits = sigmap(cell->getPort(ID::EN)); - active_bits_on_port.push_back(std::vector<bool>(en_bits.size())); - for (int k = int(en_bits.size())-1; k >= 0; k--) - log("%c", active_bits_on_port[i][k] ? '1' : '0'); - log("\n"); + changed = true; + port2.removed = true; } - - last_port_by_addr[addr] = i; } - // Clean up `wr_ports': remove all NULL entries - - std::vector<RTLIL::Cell*> wr_ports_with_nulls; - wr_ports_with_nulls.swap(wr_ports); + if (changed) + mem.emit(); - for (auto cell : wr_ports_with_nulls) - if (cell != NULL) - wr_ports.push_back(cell); + return changed; } @@ -486,178 +279,174 @@ struct MemoryShareWorker // Consolidate write ports using sat-based resource sharing // -------------------------------------------------------- - void consolidate_wr_using_sat(std::string memid, std::vector<RTLIL::Cell*> &wr_ports) + void consolidate_wr_using_sat(Mem &mem) { - if (wr_ports.size() <= 1) + if (GetSize(mem.wr_ports) <= 1) return; - ezSatPtr ez; - SatGen satgen(ez.get(), &modwalker.sigmap); + // Get a list of ports that have any chance of being mergeable. - // find list of considered ports and port pairs + pool<int> eligible_ports; - std::set<int> considered_ports; - std::set<int> considered_port_pairs; - - for (int i = 0; i < int(wr_ports.size()); i++) { - std::vector<RTLIL::SigBit> bits = modwalker.sigmap(wr_ports[i]->getPort(ID::EN)); + for (int i = 0; i < GetSize(mem.wr_ports); i++) { + auto &port = mem.wr_ports[i]; + std::vector<RTLIL::SigBit> bits = modwalker.sigmap(port.en); for (auto bit : bits) if (bit == RTLIL::State::S1) goto port_is_always_active; - if (modwalker.has_drivers(bits)) - considered_ports.insert(i); + eligible_ports.insert(i); port_is_always_active:; } - log("Consolidating write ports of memory %s.%s using sat-based resource sharing:\n", log_id(module), log_id(memid)); + if (eligible_ports.size() <= 1) + return; + + log("Consolidating write ports of memory %s.%s using sat-based resource sharing:\n", log_id(module), log_id(mem.memid)); - bool cache_clk_enable = false; - bool cache_clk_polarity = false; - RTLIL::SigSpec cache_clk; + // Group eligible ports by clock domain and width. - for (int i = 0; i < int(wr_ports.size()); i++) + pool<int> checked_ports; + std::vector<std::vector<int>> groups; + for (int i = 0; i < GetSize(mem.wr_ports); i++) { - RTLIL::Cell *cell = wr_ports.at(i); + auto &port1 = mem.wr_ports[i]; + if (!eligible_ports.count(i)) + continue; + if (checked_ports.count(i)) + continue; - if (cell->parameters.at(ID::CLK_ENABLE).as_bool() != cache_clk_enable || - (cache_clk_enable && (sigmap(cell->getPort(ID::CLK)) != cache_clk || - cell->parameters.at(ID::CLK_POLARITY).as_bool() != cache_clk_polarity))) + std::vector<int> group; + group.push_back(i); + + for (int j = i + 1; j < GetSize(mem.wr_ports); j++) { - cache_clk_enable = cell->parameters.at(ID::CLK_ENABLE).as_bool(); - cache_clk_polarity = cell->parameters.at(ID::CLK_POLARITY).as_bool(); - cache_clk = sigmap(cell->getPort(ID::CLK)); + auto &port2 = mem.wr_ports[j]; + if (!eligible_ports.count(j)) + continue; + if (checked_ports.count(j)) + continue; + if (port1.clk_enable != port2.clk_enable) + continue; + if (port1.clk_enable) { + if (port1.clk != port2.clk) + continue; + if (port1.clk_polarity != port2.clk_polarity) + continue; + } + if (port1.wide_log2 != port2.wide_log2) + continue; + group.push_back(j); } - else if (i > 0 && considered_ports.count(i-1) && considered_ports.count(i)) - considered_port_pairs.insert(i); - - if (cache_clk_enable) - log(" Port %d (%s) on %s %s: %s\n", i, log_id(cell), - cache_clk_polarity ? "posedge" : "negedge", log_signal(cache_clk), - considered_ports.count(i) ? "considered" : "not considered"); - else - log(" Port %d (%s) unclocked: %s\n", i, log_id(cell), - considered_ports.count(i) ? "considered" : "not considered"); - } - if (considered_port_pairs.size() < 1) { - log(" No two subsequent ports in same clock domain considered -> nothing to consolidate.\n"); - return; - } - - // create SAT representation of common input cone of all considered EN signals + for (auto j : group) + checked_ports.insert(j); - pool<Wire*> one_hot_wires; - std::set<RTLIL::Cell*> sat_cells; - std::set<RTLIL::SigBit> bits_queue; - std::map<int, int> port_to_sat_variable; + if (group.size() <= 1) + continue; - for (int i = 0; i < int(wr_ports.size()); i++) - if (considered_port_pairs.count(i) || considered_port_pairs.count(i+1)) - { - RTLIL::SigSpec sig = modwalker.sigmap(wr_ports[i]->getPort(ID::EN)); - port_to_sat_variable[i] = ez->expression(ez->OpOr, satgen.importSigSpec(sig)); + groups.push_back(group); + } - std::vector<RTLIL::SigBit> bits = sig; - bits_queue.insert(bits.begin(), bits.end()); + bool changed = false; + for (auto &group : groups) { + auto &some_port = mem.wr_ports[group[0]]; + string ports; + for (auto idx : group) { + if (idx != group[0]) + ports += ", "; + ports += std::to_string(idx); + } + if (!some_port.clk_enable) { + log(" Checking unclocked group, width %d: ports %s.\n", mem.width << some_port.wide_log2, ports.c_str()); + } else { + log(" Checking group clocked with %sedge %s, width %d: ports %s.\n", some_port.clk_polarity ? "pos" : "neg", log_signal(some_port.clk), mem.width << some_port.wide_log2, ports.c_str()); } - while (!bits_queue.empty()) - { - for (auto bit : bits_queue) - if (bit.wire && bit.wire->get_bool_attribute(ID::onehot)) - one_hot_wires.insert(bit.wire); - - pool<ModWalker::PortBit> portbits; - modwalker.get_drivers(portbits, bits_queue); - bits_queue.clear(); - - for (auto &pbit : portbits) - if (sat_cells.count(pbit.cell) == 0 && cone_ct.cell_known(pbit.cell->type)) { - pool<RTLIL::SigBit> &cell_inputs = modwalker.cell_inputs[pbit.cell]; - bits_queue.insert(cell_inputs.begin(), cell_inputs.end()); - sat_cells.insert(pbit.cell); - } - } + // Okay, time to actually run the SAT solver. - for (auto wire : one_hot_wires) { - log(" Adding one-hot constraint for wire %s.\n", log_id(wire)); - vector<int> ez_wire_bits = satgen.importSigSpec(wire); - for (int i : ez_wire_bits) - for (int j : ez_wire_bits) - if (i != j) ez->assume(ez->NOT(i), j); - } + QuickConeSat qcsat(modwalker); - log(" Common input cone for all EN signals: %d cells.\n", int(sat_cells.size())); + // create SAT representation of common input cone of all considered EN signals - for (auto cell : sat_cells) - satgen.importCell(cell); + dict<int, int> port_to_sat_variable; - log(" Size of unconstrained SAT problem: %d variables, %d clauses\n", ez->numCnfVariables(), ez->numCnfClauses()); + for (auto idx : group) + port_to_sat_variable[idx] = qcsat.ez->expression(qcsat.ez->OpOr, qcsat.importSig(mem.wr_ports[idx].en)); - // merge subsequent ports if possible + qcsat.prepare(); - for (int i = 0; i < int(wr_ports.size()); i++) - { - if (!considered_port_pairs.count(i)) - continue; + log(" Common input cone for all EN signals: %d cells.\n", GetSize(qcsat.imported_cells)); - if (ez->solve(port_to_sat_variable.at(i-1), port_to_sat_variable.at(i))) { - log(" According to SAT solver sharing of port %d with port %d is not possible.\n", i-1, i); - continue; - } + log(" Size of unconstrained SAT problem: %d variables, %d clauses\n", qcsat.ez->numCnfVariables(), qcsat.ez->numCnfClauses()); + + // now try merging the ports. + + for (int ii = 0; ii < GetSize(group); ii++) { + int idx1 = group[ii]; + auto &port1 = mem.wr_ports[idx1]; + if (port1.removed) + continue; + for (int jj = ii + 1; jj < GetSize(group); jj++) { + int idx2 = group[jj]; + auto &port2 = mem.wr_ports[idx2]; + if (port2.removed) + continue; - log(" Merging port %d into port %d.\n", i-1, i); - port_to_sat_variable.at(i) = ez->OR(port_to_sat_variable.at(i-1), port_to_sat_variable.at(i)); + if (qcsat.ez->solve(port_to_sat_variable.at(idx1), port_to_sat_variable.at(idx2))) { + log(" According to SAT solver sharing of port %d with port %d is not possible.\n", idx1, idx2); + continue; + } - RTLIL::SigSpec last_addr = wr_ports[i-1]->getPort(ID::ADDR); - RTLIL::SigSpec last_data = wr_ports[i-1]->getPort(ID::DATA); - std::vector<RTLIL::SigBit> last_en = modwalker.sigmap(wr_ports[i-1]->getPort(ID::EN)); + log(" Merging port %d into port %d.\n", idx2, idx1); + mem.prepare_wr_merge(idx1, idx2, &initvals); + port_to_sat_variable.at(idx1) = qcsat.ez->OR(port_to_sat_variable.at(idx1), port_to_sat_variable.at(idx2)); - RTLIL::SigSpec this_addr = wr_ports[i]->getPort(ID::ADDR); - RTLIL::SigSpec this_data = wr_ports[i]->getPort(ID::DATA); - std::vector<RTLIL::SigBit> this_en = modwalker.sigmap(wr_ports[i]->getPort(ID::EN)); + RTLIL::SigSpec last_addr = port1.addr; + RTLIL::SigSpec last_data = port1.data; + std::vector<RTLIL::SigBit> last_en = modwalker.sigmap(port1.en); - RTLIL::SigBit this_en_active = module->ReduceOr(NEW_ID, this_en); + RTLIL::SigSpec this_addr = port2.addr; + RTLIL::SigSpec this_data = port2.data; + std::vector<RTLIL::SigBit> this_en = modwalker.sigmap(port2.en); - if (GetSize(last_addr) < GetSize(this_addr)) - last_addr.extend_u0(GetSize(this_addr)); - else - this_addr.extend_u0(GetSize(last_addr)); + RTLIL::SigBit this_en_active = module->ReduceOr(NEW_ID, this_en); - wr_ports[i]->setParam(ID::ABITS, GetSize(this_addr)); - wr_ports[i]->setPort(ID::ADDR, module->Mux(NEW_ID, last_addr, this_addr, this_en_active)); - wr_ports[i]->setPort(ID::DATA, module->Mux(NEW_ID, last_data, this_data, this_en_active)); + if (GetSize(last_addr) < GetSize(this_addr)) + last_addr.extend_u0(GetSize(this_addr)); + else + this_addr.extend_u0(GetSize(last_addr)); - std::map<std::pair<RTLIL::SigBit, RTLIL::SigBit>, int> groups_en; - RTLIL::SigSpec grouped_last_en, grouped_this_en, en; - RTLIL::Wire *grouped_en = module->addWire(NEW_ID, 0); + SigSpec new_addr = module->Mux(NEW_ID, last_addr.extract_end(port1.wide_log2), this_addr.extract_end(port1.wide_log2), this_en_active); - for (int j = 0; j < int(this_en.size()); j++) { - std::pair<RTLIL::SigBit, RTLIL::SigBit> key(last_en[j], this_en[j]); - if (!groups_en.count(key)) { - grouped_last_en.append(last_en[j]); - grouped_this_en.append(this_en[j]); - groups_en[key] = grouped_en->width; - grouped_en->width++; - } - en.append(RTLIL::SigSpec(grouped_en, groups_en[key])); - } + port1.addr = SigSpec({new_addr, port1.addr.extract(0, port1.wide_log2)}); + port1.data = module->Mux(NEW_ID, last_data, this_data, this_en_active); - module->addMux(NEW_ID, grouped_last_en, grouped_this_en, this_en_active, grouped_en); - wr_ports[i]->setPort(ID::EN, en); + std::map<std::pair<RTLIL::SigBit, RTLIL::SigBit>, int> groups_en; + RTLIL::SigSpec grouped_last_en, grouped_this_en, en; + RTLIL::Wire *grouped_en = module->addWire(NEW_ID, 0); - module->remove(wr_ports[i-1]); - wr_ports[i-1] = NULL; - } + for (int j = 0; j < int(this_en.size()); j++) { + std::pair<RTLIL::SigBit, RTLIL::SigBit> key(last_en[j], this_en[j]); + if (!groups_en.count(key)) { + grouped_last_en.append(last_en[j]); + grouped_this_en.append(this_en[j]); + groups_en[key] = grouped_en->width; + grouped_en->width++; + } + en.append(RTLIL::SigSpec(grouped_en, groups_en[key])); + } - // Clean up `wr_ports': remove all NULL entries + module->addMux(NEW_ID, grouped_last_en, grouped_this_en, this_en_active, grouped_en); + port1.en = en; - std::vector<RTLIL::Cell*> wr_ports_with_nulls; - wr_ports_with_nulls.swap(wr_ports); + port2.removed = true; + changed = true; + } + } + } - for (auto cell : wr_ports_with_nulls) - if (cell != NULL) - wr_ports.push_back(cell); + if (changed) + mem.emit(); } @@ -665,26 +454,19 @@ struct MemoryShareWorker // Setup and run // ------------- - MemoryShareWorker(RTLIL::Design *design) : design(design), modwalker(design) {} + MemoryShareWorker(RTLIL::Design *design, bool flag_widen, bool flag_sat) : design(design), modwalker(design), flag_widen(flag_widen), flag_sat(flag_sat) {} void operator()(RTLIL::Module* module) { - std::map<std::string, std::pair<std::vector<RTLIL::Cell*>, std::vector<RTLIL::Cell*>>> memindex; + std::vector<Mem> memories = Mem::get_selected_memories(module); this->module = module; sigmap.set(module); - sig_to_mux.clear(); - conditions_logic_cache.clear(); + initvals.set(&sigmap, module); sigmap_xmux = sigmap; for (auto cell : module->cells()) { - if (cell->type == ID($memrd)) - memindex[cell->parameters.at(ID::MEMID).decode_string()].first.push_back(cell); - - if (cell->type == ID($memwr)) - memindex[cell->parameters.at(ID::MEMID).decode_string()].second.push_back(cell); - if (cell->type == ID($mux)) { RTLIL::SigSpec sig_a = sigmap_xmux(cell->getPort(ID::A)); @@ -695,40 +477,20 @@ struct MemoryShareWorker else if (sig_b.is_fully_undef()) sigmap_xmux.add(cell->getPort(ID::Y), sig_a); } - - if (cell->type.in(ID($mux), ID($pmux))) - { - std::vector<RTLIL::SigBit> sig_y = sigmap(cell->getPort(ID::Y)); - for (int i = 0; i < int(sig_y.size()); i++) - sig_to_mux[sig_y[i]] = std::pair<RTLIL::Cell*, int>(cell, i); - } } - for (auto &it : memindex) { - std::sort(it.second.first.begin(), it.second.first.end(), memcells_cmp); - std::sort(it.second.second.begin(), it.second.second.end(), memcells_cmp); - translate_rd_feedback_to_en(it.first, it.second.first, it.second.second); - consolidate_wr_by_addr(it.first, it.second.second); + for (auto &mem : memories) { + while (consolidate_rd_by_addr(mem)); + while (consolidate_wr_by_addr(mem)); } - cone_ct.setup_internals(); - cone_ct.cell_types.erase(ID($mul)); - cone_ct.cell_types.erase(ID($mod)); - cone_ct.cell_types.erase(ID($div)); - cone_ct.cell_types.erase(ID($modfloor)); - cone_ct.cell_types.erase(ID($divfloor)); - cone_ct.cell_types.erase(ID($pow)); - cone_ct.cell_types.erase(ID($shl)); - cone_ct.cell_types.erase(ID($shr)); - cone_ct.cell_types.erase(ID($sshl)); - cone_ct.cell_types.erase(ID($sshr)); - cone_ct.cell_types.erase(ID($shift)); - cone_ct.cell_types.erase(ID($shiftx)); - - modwalker.setup(module, &cone_ct); - - for (auto &it : memindex) - consolidate_wr_using_sat(it.first, it.second.second); + if (!flag_sat) + return; + + modwalker.setup(module); + + for (auto &mem : memories) + consolidate_wr_using_sat(mem); } }; @@ -738,22 +500,22 @@ struct MemorySharePass : public Pass { { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" memory_share [selection]\n"); + log(" memory_share [-nosat] [-nowiden] [selection]\n"); log("\n"); log("This pass merges share-able memory ports into single memory ports.\n"); log("\n"); log("The following methods are used to consolidate the number of memory ports:\n"); log("\n"); - log(" - When write ports are connected to async read ports accessing the same\n"); - log(" address, then this feedback path is converted to a write port with\n"); - log(" byte/part enable signals.\n"); - log("\n"); log(" - When multiple write ports access the same address then this is converted\n"); log(" to a single write port with a more complex data and/or enable logic path.\n"); log("\n"); + log(" - When multiple read or write ports access adjacent aligned addresses, they are\n"); + log(" merged to a single wide read or write port. This transformation can be\n"); + log(" disabled with the \"-nowiden\" option.\n"); + log("\n"); log(" - When multiple write ports are never accessed at the same time (a SAT\n"); log(" solver is used to determine this), then the ports are merged into a single\n"); - log(" write port.\n"); + log(" write port. This transformation can be disabled with the \"-nosat\" option.\n"); 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"); @@ -761,9 +523,26 @@ struct MemorySharePass : public Pass { log("\n"); } void execute(std::vector<std::string> args, RTLIL::Design *design) override { + bool flag_widen = true; + bool flag_sat = true; log_header(design, "Executing MEMORY_SHARE pass (consolidating $memrd/$memwr cells).\n"); + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-nosat") + { + flag_sat = false; + continue; + } + if (args[argidx] == "-nowiden") + { + flag_widen = false; + continue; + } + break; + } extra_args(args, 1, design); - MemoryShareWorker msw(design); + MemoryShareWorker msw(design, flag_widen, flag_sat); for (auto module : design->selected_modules()) msw(module); diff --git a/passes/memory/memory_unpack.cc b/passes/memory/memory_unpack.cc index 16b57d9c3..422d6fe15 100644 --- a/passes/memory/memory_unpack.cc +++ b/passes/memory/memory_unpack.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/opt/Makefile.inc b/passes/opt/Makefile.inc index 4ae9b8895..4e52ad8da 100644 --- a/passes/opt/Makefile.inc +++ b/passes/opt/Makefile.inc @@ -2,6 +2,9 @@ OBJS += passes/opt/opt.o OBJS += passes/opt/opt_merge.o OBJS += passes/opt/opt_mem.o +OBJS += passes/opt/opt_mem_feedback.o +OBJS += passes/opt/opt_mem_priority.o +OBJS += passes/opt/opt_mem_widen.o OBJS += passes/opt/opt_muxtree.o OBJS += passes/opt/opt_reduce.o OBJS += passes/opt/opt_dff.o diff --git a/passes/opt/muxpack.cc b/passes/opt/muxpack.cc index aa5f82437..b5e151098 100644 --- a/passes/opt/muxpack.cc +++ b/passes/opt/muxpack.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * 2019 Eddie Hung <eddie@fpgeh.com> * * Permission to use, copy, modify, and/or distribute this software for any diff --git a/passes/opt/opt.cc b/passes/opt/opt.cc index 4b052d9a2..c3e418c07 100644 --- a/passes/opt/opt.cc +++ b/passes/opt/opt.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/opt/opt_clean.cc b/passes/opt/opt_clean.cc index 883374cf6..cb2c261c4 100644 --- a/passes/opt/opt_clean.cc +++ b/passes/opt/opt_clean.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -21,6 +21,7 @@ #include "kernel/sigtools.h" #include "kernel/log.h" #include "kernel/celltypes.h" +#include "kernel/ffinit.h" #include <stdlib.h> #include <stdio.h> #include <set> @@ -101,6 +102,7 @@ void rmunused_module_cells(Module *module, bool verbose) pool<SigBit> used_raw_bits; dict<SigBit, pool<Cell*>> wire2driver; dict<SigBit, vector<string>> driver_driver_logs; + FfInitVals ffinit(&sigmap, module); SigMap raw_sigmap; for (auto &it : module->connections_) { @@ -115,7 +117,7 @@ void rmunused_module_cells(Module *module, bool verbose) } for (Cell *cell : module->cells()) { - if (cell->type.in(ID($memwr), ID($meminit))) { + if (cell->type.in(ID($memwr), ID($memwr_v2), ID($meminit), ID($meminit_v2))) { IdString mem_id = cell->getParam(ID::MEMID).decode_string(); mem2cells[mem_id].insert(cell); } @@ -165,7 +167,7 @@ void rmunused_module_cells(Module *module, bool verbose) for (auto bit : sigmap(it.second)) bits.insert(bit); - if (cell->type == ID($memrd)) { + if (cell->type.in(ID($memrd), ID($memrd_v2))) { IdString mem_id = cell->getParam(ID::MEMID).decode_string(); if (mem_unused.count(mem_id)) { mem_unused.erase(mem_id); @@ -193,6 +195,8 @@ void rmunused_module_cells(Module *module, bool verbose) if (verbose) log_debug(" removing unused `%s' cell `%s'.\n", cell->type.c_str(), cell->name.c_str()); module->design->scratchpad_set_bool("opt.did_something", true); + if (RTLIL::builtin_ff_cell_types().count(cell->type)) + ffinit.remove_init(cell->getPort(ID::Q)); module->remove(cell); count_rm_cells++; } @@ -335,6 +339,7 @@ bool rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos used_signals_nodrivers.add(it2.second); } } + dict<RTLIL::SigBit, RTLIL::State> init_bits; for (auto &it : module->wires_) { RTLIL::Wire *wire = it.second; if (wire->port_id > 0) { @@ -350,6 +355,29 @@ bool rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos assign_map.apply(sig); used_signals.add(sig); } + auto it2 = wire->attributes.find(ID::init); + if (it2 != wire->attributes.end()) { + RTLIL::Const &val = it2->second; + SigSpec sig = assign_map(wire); + for (int i = 0; i < GetSize(val) && i < GetSize(sig); i++) + if (val.bits[i] != State::Sx) + init_bits[sig[i]] = val.bits[i]; + wire->attributes.erase(it2); + } + } + + for (auto wire : module->wires()) { + bool found = false; + Const val(State::Sx, wire->width); + for (int i = 0; i < wire->width; i++) { + auto it = init_bits.find(RTLIL::SigBit(wire, i)); + if (it != init_bits.end()) { + val.bits[i] = it->second; + found = true; + } + } + if (found) + wire->attributes[ID::init] = val; } pool<RTLIL::Wire*> del_wires_queue; diff --git a/passes/opt/opt_demorgan.cc b/passes/opt/opt_demorgan.cc index f0fa86f42..1464c4177 100644 --- a/passes/opt/opt_demorgan.cc +++ b/passes/opt/opt_demorgan.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2017 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2017 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/opt/opt_dff.cc b/passes/opt/opt_dff.cc index a47071a30..73d674c8d 100644 --- a/passes/opt/opt_dff.cc +++ b/passes/opt/opt_dff.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * Copyright (C) 2020 Marcelina KoÅ›cielnicka <mwk@0x04.net> * * Permission to use, copy, modify, and/or distribute this software for any @@ -21,7 +21,8 @@ #include "kernel/log.h" #include "kernel/register.h" #include "kernel/rtlil.h" -#include "kernel/satgen.h" +#include "kernel/qcsat.h" +#include "kernel/modtools.h" #include "kernel/sigtools.h" #include "kernel/ffinit.h" #include "kernel/ff.h" @@ -51,26 +52,20 @@ struct OptDffWorker FfInitVals initvals; dict<SigBit, int> bitusers; dict<SigBit, cell_int_t> bit2mux; - dict<SigBit, RTLIL::Cell*> bit2driver; typedef std::map<RTLIL::SigBit, bool> pattern_t; typedef std::set<pattern_t> patterns_t; typedef std::pair<RTLIL::SigBit, bool> ctrl_t; typedef std::set<ctrl_t> ctrls_t; - ezSatPtr ez; - SatGen satgen; - pool<Cell*> sat_cells; - // Used as a queue. std::vector<Cell *> dff_cells; - OptDffWorker(const OptDffOptions &opt, Module *mod) : opt(opt), module(mod), sigmap(mod), initvals(&sigmap, mod), ez(), satgen(ez.get(), &sigmap) { - // Gathering three kinds of information here for every sigmapped SigBit: + OptDffWorker(const OptDffOptions &opt, Module *mod) : opt(opt), module(mod), sigmap(mod), initvals(&sigmap, mod) { + // Gathering two kinds of information here for every sigmapped SigBit: // // - bitusers: how many users it has (muxes will only be merged into FFs if this is 1, making the FF the only user) // - bit2mux: the mux cell and bit index that drives it, if any - // - bit2driver: the cell driving it, if any for (auto wire : module->wires()) { @@ -88,10 +83,6 @@ struct OptDffWorker for (auto conn : cell->connections()) { bool is_output = cell->output(conn.first); - if (is_output) { - for (auto bit : sigmap(conn.second)) - bit2driver[bit] = cell; - } if (!is_output || !cell->known()) { for (auto bit : sigmap(conn.second)) bitusers[bit]++; @@ -104,20 +95,6 @@ struct OptDffWorker } - std::function<void(Cell*)> sat_import_cell = [&](Cell *c) { - if (!sat_cells.insert(c).second) - return; - if (!satgen.importCell(c)) - return; - for (auto &conn : c->connections()) { - if (!c->input(conn.first)) - continue; - for (auto bit : sigmap(conn.second)) - if (bit2driver.count(bit)) - sat_import_cell(bit2driver.at(bit)); - } - }; - State combine_const(State a, State b) { if (a == State::Sx && !opt.keepdc) return b; @@ -295,7 +272,7 @@ struct OptDffWorker bool changed = false; if (!ff.width) { - module->remove(cell); + ff.remove(); did_something = true; continue; } @@ -318,9 +295,9 @@ struct OptDffWorker if (!ff.pol_clr) { module->connect(ff.sig_q[i], ff.sig_clr[i]); } else if (ff.is_fine) { - module->addNotGate(NEW_ID, ff.sig_q[i], ff.sig_clr[i]); + module->addNotGate(NEW_ID, ff.sig_clr[i], ff.sig_q[i]); } else { - module->addNot(NEW_ID, ff.sig_q[i], ff.sig_clr[i]); + module->addNot(NEW_ID, ff.sig_clr[i], ff.sig_q[i]); } log("Handling always-active SET at position %d on %s (%s) from module %s (changing to combinatorial circuit).\n", i, log_id(cell), log_id(cell->type), log_id(module)); @@ -336,6 +313,7 @@ struct OptDffWorker continue; } ff = ff.slice(keep_bits); + ff.cell = cell; changed = true; } @@ -402,6 +380,68 @@ struct OptDffWorker } } + if (ff.has_aload) { + if (ff.sig_aload == (ff.pol_aload ? State::S0 : State::S1) || (!opt.keepdc && ff.sig_aload == State::Sx)) { + // Always-inactive enable — remove. + log("Removing never-active async load on %s (%s) from module %s.\n", + log_id(cell), log_id(cell->type), log_id(module)); + ff.has_aload = false; + changed = true; + } else if (ff.sig_aload == (ff.pol_aload ? State::S1 : State::S0)) { + // Always-active enable. Make a comb circuit, nuke the FF/latch. + log("Handling always-active async load on %s (%s) from module %s (changing to combinatorial circuit).\n", + log_id(cell), log_id(cell->type), log_id(module)); + ff.remove(); + if (ff.has_sr) { + SigSpec tmp; + if (ff.is_fine) { + if (ff.pol_set) + tmp = module->MuxGate(NEW_ID, ff.sig_ad, State::S1, ff.sig_set); + else + tmp = module->MuxGate(NEW_ID, State::S1, ff.sig_ad, ff.sig_set); + if (ff.pol_clr) + module->addMuxGate(NEW_ID, tmp, State::S0, ff.sig_clr, ff.sig_q); + else + module->addMuxGate(NEW_ID, State::S0, tmp, ff.sig_clr, ff.sig_q); + } else { + if (ff.pol_set) + tmp = module->Or(NEW_ID, ff.sig_ad, ff.sig_set); + else + tmp = module->Or(NEW_ID, ff.sig_ad, module->Not(NEW_ID, ff.sig_set)); + if (ff.pol_clr) + module->addAnd(NEW_ID, tmp, module->Not(NEW_ID, ff.sig_clr), ff.sig_q); + else + module->addAnd(NEW_ID, tmp, ff.sig_clr, ff.sig_q); + } + } else if (ff.has_arst) { + if (ff.is_fine) { + if (ff.pol_arst) + module->addMuxGate(NEW_ID, ff.sig_ad, ff.val_arst[0], ff.sig_arst, ff.sig_q); + else + module->addMuxGate(NEW_ID, ff.val_arst[0], ff.sig_ad, ff.sig_arst, ff.sig_q); + } else { + if (ff.pol_arst) + module->addMux(NEW_ID, ff.sig_ad, ff.val_arst, ff.sig_arst, ff.sig_q); + else + module->addMux(NEW_ID, ff.val_arst, ff.sig_ad, ff.sig_arst, ff.sig_q); + } + } else { + module->connect(ff.sig_q, ff.sig_ad); + } + did_something = true; + continue; + } else if (ff.sig_ad.is_fully_const() && !ff.has_arst && !ff.has_sr) { + log("Changing const-value async load to async reset on %s (%s) from module %s.\n", + log_id(cell), log_id(cell->type), log_id(module)); + ff.has_arst = true; + ff.has_aload = false; + ff.sig_arst = ff.sig_aload; + ff.pol_arst = ff.pol_aload; + ff.val_arst = ff.sig_ad.as_const(); + changed = true; + } + } + if (ff.has_arst) { if (ff.sig_arst == (ff.pol_arst ? State::S0 : State::S1)) { // Always-inactive reset — remove. @@ -413,8 +453,7 @@ struct OptDffWorker // Always-active async reset — change to const driver. log("Handling always-active ARST on %s (%s) from module %s (changing to const driver).\n", log_id(cell), log_id(cell->type), log_id(module)); - initvals.remove_init(ff.sig_q); - module->remove(cell); + ff.remove(); module->connect(ff.sig_q, ff.val_arst); did_something = true; continue; @@ -434,111 +473,63 @@ struct OptDffWorker log_id(cell), log_id(cell->type), log_id(module)); ff.has_srst = false; if (!ff.ce_over_srst) - ff.has_en = false; - ff.sig_d = ff.val_d = ff.val_srst; - ff.d_is_const = true; + ff.has_ce = false; + ff.sig_d = ff.val_srst; changed = true; } } - if (ff.has_en) { - if (ff.sig_en == (ff.pol_en ? State::S0 : State::S1) || (!opt.keepdc && ff.sig_en == State::Sx)) { + if (ff.has_ce) { + if (ff.sig_ce == (ff.pol_ce ? State::S0 : State::S1) || (!opt.keepdc && ff.sig_ce == State::Sx)) { // Always-inactive enable — remove. - if (ff.has_clk && ff.has_srst && !ff.ce_over_srst) { + if (ff.has_srst && !ff.ce_over_srst) { log("Handling never-active EN on %s (%s) from module %s (connecting SRST instead).\n", log_id(cell), log_id(cell->type), log_id(module)); // FF with sync reset — connect the sync reset to D instead. - ff.pol_en = ff.pol_srst; - ff.sig_en = ff.sig_srst; + ff.pol_ce = ff.pol_srst; + ff.sig_ce = ff.sig_srst; ff.has_srst = false; - ff.sig_d = ff.val_d = ff.val_srst; - ff.d_is_const = true; + ff.sig_d = ff.val_srst; changed = true; } else { log("Handling never-active EN on %s (%s) from module %s (removing D path).\n", log_id(cell), log_id(cell->type), log_id(module)); - // The D input path is effectively useless, so remove it (this will be a const-input D latch, SR latch, or a const driver). - ff.has_d = ff.has_en = ff.has_clk = false; + // The D input path is effectively useless, so remove it (this will be a D latch, SR latch, or a const driver). + ff.has_ce = ff.has_clk = ff.has_srst = false; changed = true; } - } else if (ff.sig_en == (ff.pol_en ? State::S1 : State::S0)) { - // Always-active enable. - if (ff.has_clk) { - // For FF, just remove the useless enable. - log("Removing always-active EN on %s (%s) from module %s.\n", - log_id(cell), log_id(cell->type), log_id(module)); - ff.has_en = false; - changed = true; - } else { - // For latches, make a comb circuit, nuke the latch. - log("Handling always-active EN on %s (%s) from module %s (changing to combinatorial circuit).\n", - log_id(cell), log_id(cell->type), log_id(module)); - initvals.remove_init(ff.sig_q); - module->remove(cell); - if (ff.has_sr) { - SigSpec tmp; - if (ff.is_fine) { - if (ff.pol_set) - tmp = module->MuxGate(NEW_ID, ff.sig_d, State::S1, ff.sig_set); - else - tmp = module->MuxGate(NEW_ID, State::S1, ff.sig_d, ff.sig_set); - if (ff.pol_clr) - module->addMuxGate(NEW_ID, tmp, State::S0, ff.sig_clr, ff.sig_q); - else - module->addMuxGate(NEW_ID, State::S0, tmp, ff.sig_clr, ff.sig_q); - } else { - if (ff.pol_set) - tmp = module->Or(NEW_ID, ff.sig_d, ff.sig_set); - else - tmp = module->Or(NEW_ID, ff.sig_d, module->Not(NEW_ID, ff.sig_set)); - if (ff.pol_clr) - module->addAnd(NEW_ID, tmp, module->Not(NEW_ID, ff.sig_clr), ff.sig_q); - else - module->addAnd(NEW_ID, tmp, ff.sig_clr, ff.sig_q); - } - } else if (ff.has_arst) { - if (ff.is_fine) { - if (ff.pol_arst) - module->addMuxGate(NEW_ID, ff.sig_d, ff.val_arst[0], ff.sig_arst, ff.sig_q); - else - module->addMuxGate(NEW_ID, ff.val_arst[0], ff.sig_d, ff.sig_arst, ff.sig_q); - } else { - if (ff.pol_arst) - module->addMux(NEW_ID, ff.sig_d, ff.val_arst, ff.sig_arst, ff.sig_q); - else - module->addMux(NEW_ID, ff.val_arst, ff.sig_d, ff.sig_arst, ff.sig_q); - } - } else { - module->connect(ff.sig_q, ff.sig_d); - } - did_something = true; - continue; - } + } else if (ff.sig_ce == (ff.pol_ce ? State::S1 : State::S0)) { + // Always-active enable. Just remove it. + // For FF, just remove the useless enable. + log("Removing always-active EN on %s (%s) from module %s.\n", + log_id(cell), log_id(cell->type), log_id(module)); + ff.has_ce = false; + changed = true; } } if (ff.has_clk) { if (ff.sig_clk.is_fully_const()) { - // Const clock — the D input path is effectively useless, so remove it (this will be a const-input D latch, SR latch, or a const driver). + // Const clock — the D input path is effectively useless, so remove it (this will be a D latch, SR latch, or a const driver). log("Handling const CLK on %s (%s) from module %s (removing D path).\n", log_id(cell), log_id(cell->type), log_id(module)); - ff.has_d = ff.has_en = ff.has_clk = ff.has_srst = false; + ff.has_ce = ff.has_clk = ff.has_srst = false; changed = true; } } - if (ff.has_d && ff.sig_d == ff.sig_q) { + if ((ff.has_clk || ff.has_gclk) && ff.sig_d == ff.sig_q) { // Q wrapped back to D, can be removed. if (ff.has_clk && ff.has_srst) { // FF with sync reset — connect the sync reset to D instead. log("Handling D = Q on %s (%s) from module %s (conecting SRST instead).\n", log_id(cell), log_id(cell->type), log_id(module)); - if (ff.has_en && ff.ce_over_srst) { - if (!ff.pol_en) { + if (ff.has_ce && ff.ce_over_srst) { + if (!ff.pol_ce) { if (ff.is_fine) - ff.sig_en = module->NotGate(NEW_ID, ff.sig_en); + ff.sig_ce = module->NotGate(NEW_ID, ff.sig_ce); else - ff.sig_en = module->Not(NEW_ID, ff.sig_en); + ff.sig_ce = module->Not(NEW_ID, ff.sig_ce); } if (!ff.pol_srst) { if (ff.is_fine) @@ -547,96 +538,37 @@ struct OptDffWorker ff.sig_srst = module->Not(NEW_ID, ff.sig_srst); } if (ff.is_fine) - ff.sig_en = module->AndGate(NEW_ID, ff.sig_en, ff.sig_srst); + ff.sig_ce = module->AndGate(NEW_ID, ff.sig_ce, ff.sig_srst); else - ff.sig_en = module->And(NEW_ID, ff.sig_en, ff.sig_srst); - ff.pol_en = true; + ff.sig_ce = module->And(NEW_ID, ff.sig_ce, ff.sig_srst); + ff.pol_ce = true; } else { - ff.pol_en = ff.pol_srst; - ff.sig_en = ff.sig_srst; + ff.pol_ce = ff.pol_srst; + ff.sig_ce = ff.sig_srst; } - ff.has_en = true; + ff.has_ce = true; ff.has_srst = false; - ff.sig_d = ff.val_d = ff.val_srst; - ff.d_is_const = true; + ff.sig_d = ff.val_srst; changed = true; } else { // The D input path is effectively useless, so remove it (this will be a const-input D latch, SR latch, or a const driver). log("Handling D = Q on %s (%s) from module %s (removing D path).\n", log_id(cell), log_id(cell->type), log_id(module)); - ff.has_d = ff.has_en = ff.has_clk = false; + ff.has_clk = ff.has_ce = false; changed = true; } } - // Now check if any bit can be replaced by a constant. - pool<int> removed_sigbits; - for (int i = 0; i < ff.width; i++) { - State val = ff.val_init[i]; - if (ff.has_arst) - val = combine_const(val, ff.val_arst[i]); - if (ff.has_srst) - val = combine_const(val, ff.val_srst[i]); - if (ff.has_sr) { - if (ff.sig_clr[i] != (ff.pol_clr ? State::S0 : State::S1)) - val = combine_const(val, State::S0); - if (ff.sig_set[i] != (ff.pol_set ? State::S0 : State::S1)) - val = combine_const(val, State::S1); - } - if (val == State::Sm) - continue; - if (ff.has_d) { - if (!ff.sig_d[i].wire) { - val = combine_const(val, ff.sig_d[i].data); - if (val == State::Sm) - continue; - } else { - if (!opt.sat) - continue; - // For each register bit, try to prove that it cannot change from the initial value. If so, remove it - if (!bit2driver.count(ff.sig_d[i])) - continue; - if (val != State::S0 && val != State::S1) - continue; - - sat_import_cell(bit2driver.at(ff.sig_d[i])); - - int init_sat_pi = satgen.importSigSpec(val).front(); - int q_sat_pi = satgen.importSigBit(ff.sig_q[i]); - int d_sat_pi = satgen.importSigBit(ff.sig_d[i]); - - // Try to find out whether the register bit can change under some circumstances - bool counter_example_found = ez->solve(ez->IFF(q_sat_pi, init_sat_pi), ez->NOT(ez->IFF(d_sat_pi, init_sat_pi))); - - // If the register bit cannot change, we can replace it with a constant - if (counter_example_found) - continue; - } - } - log("Setting constant %d-bit at position %d on %s (%s) from module %s.\n", val ? 1 : 0, - i, log_id(cell), log_id(cell->type), log_id(module)); - - initvals.remove_init(ff.sig_q[i]); - module->connect(ff.sig_q[i], val); - removed_sigbits.insert(i); - } - if (!removed_sigbits.empty()) { - std::vector<int> keep_bits; - for (int i = 0; i < ff.width; i++) - if (!removed_sigbits.count(i)) - keep_bits.push_back(i); - if (keep_bits.empty()) { - module->remove(cell); - did_something = true; - continue; - } - ff = ff.slice(keep_bits); + if (ff.has_aload && !ff.has_clk && ff.sig_ad == ff.sig_q) { + log("Handling AD = Q on %s (%s) from module %s (removing async load path).\n", + log_id(cell), log_id(cell->type), log_id(module)); + ff.has_aload = false; changed = true; } // The cell has been simplified as much as possible already. Now try to spice it up with enables / sync resets. if (ff.has_clk) { - if (!ff.has_arst && !ff.has_sr && (!ff.has_srst || !ff.has_en || ff.ce_over_srst) && !opt.nosdff) { + if (!ff.has_arst && !ff.has_sr && (!ff.has_srst || !ff.has_ce || ff.ce_over_srst) && !opt.nosdff) { // Try to merge sync resets. std::map<ctrls_t, std::vector<int>> groups; std::vector<int> remaining_indices; @@ -697,9 +629,9 @@ struct OptDffWorker new_ff.has_srst = true; new_ff.sig_srst = srst.first; new_ff.pol_srst = srst.second; - if (new_ff.has_en) + if (new_ff.has_ce) new_ff.ce_over_srst = true; - Cell *new_cell = new_ff.emit(module, NEW_ID); + Cell *new_cell = new_ff.emit(); if (new_cell) dff_cells.push_back(new_cell); log("Adding SRST signal on %s (%s) from module %s (D = %s, Q = %s, rval = %s).\n", @@ -712,10 +644,11 @@ struct OptDffWorker continue; } else if (GetSize(remaining_indices) != ff.width) { ff = ff.slice(remaining_indices); + ff.cell = cell; changed = true; } } - if ((!ff.has_srst || !ff.has_en || !ff.ce_over_srst) && !opt.nodffe) { + if ((!ff.has_srst || !ff.has_ce || !ff.ce_over_srst) && !opt.nodffe) { // Try to merge enables. std::map<std::pair<patterns_t, ctrls_t>, std::vector<int>> groups; std::vector<int> remaining_indices; @@ -745,8 +678,8 @@ struct OptDffWorker if (!opt.simple_dffe) patterns = find_muxtree_feedback_patterns(ff.sig_d[i], ff.sig_q[i], pattern_t()); if (!patterns.empty() || !enables.empty()) { - if (ff.has_en) - enables.insert(ctrl_t(ff.sig_en, ff.pol_en)); + if (ff.has_ce) + enables.insert(ctrl_t(ff.sig_ce, ff.pol_ce)); simplify_patterns(patterns); groups[std::make_pair(patterns, enables)].push_back(i); } else @@ -757,11 +690,11 @@ struct OptDffWorker FfData new_ff = ff.slice(it.second); ctrl_t en = make_patterns_logic(it.first.first, it.first.second, ff.is_fine); - new_ff.has_en = true; - new_ff.sig_en = en.first; - new_ff.pol_en = en.second; + new_ff.has_ce = true; + new_ff.sig_ce = en.first; + new_ff.pol_ce = en.second; new_ff.ce_over_srst = false; - Cell *new_cell = new_ff.emit(module, NEW_ID); + Cell *new_cell = new_ff.emit(); if (new_cell) dff_cells.push_back(new_cell); log("Adding EN signal on %s (%s) from module %s (D = %s, Q = %s).\n", @@ -774,6 +707,7 @@ struct OptDffWorker continue; } else if (GetSize(remaining_indices) != ff.width) { ff = ff.slice(remaining_indices); + ff.cell = cell; changed = true; } } @@ -781,9 +715,116 @@ struct OptDffWorker if (changed) { // Rebuild the FF. - IdString name = cell->name; - module->remove(cell); - ff.emit(module, name); + ff.emit(); + did_something = true; + } + } + return did_something; + } + + bool run_constbits() { + ModWalker modwalker(module->design, module); + QuickConeSat qcsat(modwalker); + + // Run as a separate sub-pass, so that we don't mutate (non-FF) cells under ModWalker. + bool did_something = false; + for (auto cell : module->selected_cells()) { + if (!RTLIL::builtin_ff_cell_types().count(cell->type)) + continue; + FfData ff(&initvals, cell); + + // Now check if any bit can be replaced by a constant. + pool<int> removed_sigbits; + for (int i = 0; i < ff.width; i++) { + State val = ff.val_init[i]; + if (ff.has_arst) + val = combine_const(val, ff.val_arst[i]); + if (ff.has_srst) + val = combine_const(val, ff.val_srst[i]); + if (ff.has_sr) { + if (ff.sig_clr[i] != (ff.pol_clr ? State::S0 : State::S1)) + val = combine_const(val, State::S0); + if (ff.sig_set[i] != (ff.pol_set ? State::S0 : State::S1)) + val = combine_const(val, State::S1); + } + if (val == State::Sm) + continue; + if (ff.has_clk || ff.has_gclk) { + if (!ff.sig_d[i].wire) { + val = combine_const(val, ff.sig_d[i].data); + if (val == State::Sm) + continue; + } else { + if (!opt.sat) + continue; + // For each register bit, try to prove that it cannot change from the initial value. If so, remove it + if (!modwalker.has_drivers(ff.sig_d.extract(i))) + continue; + if (val != State::S0 && val != State::S1) + continue; + + int init_sat_pi = qcsat.importSigBit(val); + int q_sat_pi = qcsat.importSigBit(ff.sig_q[i]); + int d_sat_pi = qcsat.importSigBit(ff.sig_d[i]); + + qcsat.prepare(); + + // Try to find out whether the register bit can change under some circumstances + bool counter_example_found = qcsat.ez->solve(qcsat.ez->IFF(q_sat_pi, init_sat_pi), qcsat.ez->NOT(qcsat.ez->IFF(d_sat_pi, init_sat_pi))); + + // If the register bit cannot change, we can replace it with a constant + if (counter_example_found) + continue; + } + } + if (ff.has_aload) { + if (!ff.sig_ad[i].wire) { + val = combine_const(val, ff.sig_ad[i].data); + if (val == State::Sm) + continue; + } else { + if (!opt.sat) + continue; + // For each register bit, try to prove that it cannot change from the initial value. If so, remove it + if (!modwalker.has_drivers(ff.sig_ad.extract(i))) + continue; + if (val != State::S0 && val != State::S1) + continue; + + int init_sat_pi = qcsat.importSigBit(val); + int q_sat_pi = qcsat.importSigBit(ff.sig_q[i]); + int d_sat_pi = qcsat.importSigBit(ff.sig_ad[i]); + + qcsat.prepare(); + + // Try to find out whether the register bit can change under some circumstances + bool counter_example_found = qcsat.ez->solve(qcsat.ez->IFF(q_sat_pi, init_sat_pi), qcsat.ez->NOT(qcsat.ez->IFF(d_sat_pi, init_sat_pi))); + + // If the register bit cannot change, we can replace it with a constant + if (counter_example_found) + continue; + } + } + log("Setting constant %d-bit at position %d on %s (%s) from module %s.\n", val ? 1 : 0, + i, log_id(cell), log_id(cell->type), log_id(module)); + + initvals.remove_init(ff.sig_q[i]); + module->connect(ff.sig_q[i], val); + removed_sigbits.insert(i); + } + if (!removed_sigbits.empty()) { + std::vector<int> keep_bits; + for (int i = 0; i < ff.width; i++) + if (!removed_sigbits.count(i)) + keep_bits.push_back(i); + if (keep_bits.empty()) { + module->remove(cell); + did_something = true; + continue; + } + ff = ff.slice(keep_bits); + ff.cell = cell; + ff.emit(); did_something = true; } } @@ -865,6 +906,8 @@ struct OptDffPass : public Pass { OptDffWorker worker(opt, mod); if (worker.run()) did_something = true; + if (worker.run_constbits()) + did_something = true; } if (did_something) diff --git a/passes/opt/opt_expr.cc b/passes/opt/opt_expr.cc index e36e4419d..be0cd470b 100644 --- a/passes/opt/opt_expr.cc +++ b/passes/opt/opt_expr.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -393,34 +393,8 @@ int get_highest_hot_index(RTLIL::SigSpec signal) return -1; } -// if the signal has only one bit set, return the index of that bit. -// otherwise return -1 -int get_onehot_bit_index(RTLIL::SigSpec signal) -{ - int bit_index = -1; - - for (int i = 0; i < GetSize(signal); i++) - { - if (signal[i] == RTLIL::State::S0) - continue; - - if (signal[i] != RTLIL::State::S1) - return -1; - - if (bit_index != -1) - return -1; - - bit_index = i; - } - - return bit_index; -} - void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool consume_x, bool mux_undef, bool mux_bool, bool do_fine, bool keepdc, bool noclkinv) { - if (!design->selected(module)) - return; - CellTypes ct_combinational; ct_combinational.setup_internals(); ct_combinational.setup_stdcells(); @@ -467,7 +441,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if (!noclkinv) { - if (cell->type.in(ID($dff), ID($dffe), ID($dffsr), ID($dffsre), ID($adff), ID($adffe), ID($sdff), ID($sdffe), ID($sdffce), ID($fsm), ID($memrd), ID($memwr))) + if (cell->type.in(ID($dff), ID($dffe), ID($dffsr), ID($dffsre), ID($adff), ID($adffe), ID($aldff), ID($aldffe), ID($sdff), ID($sdffe), ID($sdffce), ID($fsm), ID($memrd), ID($memrd_v2), ID($memwr), ID($memwr_v2))) handle_polarity_inv(cell, ID::CLK, ID::CLK_POLARITY, assign_map, invert_map); if (cell->type.in(ID($sr), ID($dffsr), ID($dffsre), ID($dlatchsr))) { @@ -478,10 +452,13 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if (cell->type.in(ID($adff), ID($adffe), ID($adlatch))) handle_polarity_inv(cell, ID::ARST, ID::ARST_POLARITY, assign_map, invert_map); + if (cell->type.in(ID($aldff), ID($aldffe))) + handle_polarity_inv(cell, ID::ALOAD, ID::ALOAD_POLARITY, assign_map, invert_map); + if (cell->type.in(ID($sdff), ID($sdffe), ID($sdffce))) handle_polarity_inv(cell, ID::SRST, ID::SRST_POLARITY, assign_map, invert_map); - if (cell->type.in(ID($dffe), ID($adffe), ID($sdffe), ID($sdffce), ID($dffsre), ID($dlatch), ID($adlatch), ID($dlatchsr))) + if (cell->type.in(ID($dffe), ID($adffe), ID($aldffe), ID($sdffe), ID($sdffce), ID($dffsre), ID($dlatch), ID($adlatch), ID($dlatchsr))) handle_polarity_inv(cell, ID::EN, ID::EN_POLARITY, assign_map, invert_map); handle_clkpol_celltype_swap(cell, "$_SR_N?_", "$_SR_P?_", ID::S, assign_map, invert_map); @@ -510,6 +487,13 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons handle_clkpol_celltype_swap(cell, "$_SDFFCE_?N??_", "$_SDFFCE_?P??_", ID::R, assign_map, invert_map); handle_clkpol_celltype_swap(cell, "$_SDFFCE_???N_", "$_SDFFCE_???P_", ID::E, assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_ALDFF_N?_", "$_ALDFF_P?_", ID::C, assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_ALDFF_?N_", "$_ALDFF_?P_", ID::L, assign_map, invert_map); + + handle_clkpol_celltype_swap(cell, "$_ALDFFE_N??_", "$_ALDFFE_P??_", ID::C, assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_ALDFFE_?N?_", "$_ALDFFE_?P?_", ID::L, assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_ALDFFE_??N_", "$_ALDFFE_??P_", ID::E, assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_DFFSR_N??_", "$_DFFSR_P??_", ID::C, assign_map, invert_map); handle_clkpol_celltype_swap(cell, "$_DFFSR_?N?_", "$_DFFSR_?P?_", ID::S, assign_map, invert_map); handle_clkpol_celltype_swap(cell, "$_DFFSR_??N_", "$_DFFSR_??P_", ID::R, assign_map, invert_map); @@ -1526,14 +1510,12 @@ skip_identity: RTLIL::SigSpec sig_b = assign_map(cell->getPort(ID::B)); RTLIL::SigSpec sig_y = assign_map(cell->getPort(ID::Y)); - if (sig_b.is_fully_const() && sig_b.size() <= 32) + if (sig_b.is_fully_const()) std::swap(sig_a, sig_b), std::swap(a_signed, b_signed), swapped_ab = true; - if (sig_a.is_fully_def() && sig_a.size() <= 32) + if (sig_a.is_fully_def()) { - int a_val = sig_a.as_int(); - - if (a_val == 0) + if (sig_a.is_fully_zero()) { cover("opt.opt_expr.mul_shift.zero"); @@ -1547,37 +1529,34 @@ skip_identity: goto next_cell; } - for (int i = 1; i < (a_signed ? sig_a.size()-1 : sig_a.size()); i++) - if (a_val == (1 << i)) - { - if (swapped_ab) - cover("opt.opt_expr.mul_shift.swapped"); - else - cover("opt.opt_expr.mul_shift.unswapped"); + int exp; + if (sig_a.is_onehot(&exp) && !(a_signed && exp == GetSize(sig_a) - 1)) + { + if (swapped_ab) + cover("opt.opt_expr.mul_shift.swapped"); + else + cover("opt.opt_expr.mul_shift.unswapped"); - log_debug("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); + log_debug("Replacing multiply-by-%s cell `%s' in module `%s' with shift-by-%d.\n", + log_signal(sig_a), cell->name.c_str(), module->name.c_str(), exp); - if (!swapped_ab) { - cell->setPort(ID::A, cell->getPort(ID::B)); - cell->parameters.at(ID::A_WIDTH) = cell->parameters.at(ID::B_WIDTH); - cell->parameters.at(ID::A_SIGNED) = cell->parameters.at(ID::B_SIGNED); - } - - std::vector<RTLIL::SigBit> new_b = RTLIL::SigSpec(i, 6); + if (!swapped_ab) { + cell->setPort(ID::A, cell->getPort(ID::B)); + cell->parameters.at(ID::A_WIDTH) = cell->parameters.at(ID::B_WIDTH); + cell->parameters.at(ID::A_SIGNED) = cell->parameters.at(ID::B_SIGNED); + } - while (GetSize(new_b) > 1 && new_b.back() == RTLIL::State::S0) - new_b.pop_back(); + Const new_b = exp; - cell->type = ID($shl); - cell->parameters[ID::B_WIDTH] = GetSize(new_b); - cell->parameters[ID::B_SIGNED] = false; - cell->setPort(ID::B, new_b); - cell->check(); + cell->type = ID($shl); + cell->parameters[ID::B_WIDTH] = GetSize(new_b); + cell->parameters[ID::B_SIGNED] = false; + cell->setPort(ID::B, new_b); + cell->check(); - did_something = true; - goto next_cell; - } + did_something = true; + goto next_cell; + } } sig_a = assign_map(cell->getPort(ID::A)); @@ -1622,7 +1601,7 @@ skip_identity: } } - if (!keepdc && cell->type.in(ID($div), ID($mod), ID($divfloor), ID($modfloor))) + if (cell->type.in(ID($div), ID($mod), ID($divfloor), ID($modfloor))) { bool a_signed = cell->parameters[ID::A_SIGNED].as_bool(); bool b_signed = cell->parameters[ID::B_SIGNED].as_bool(); @@ -1630,11 +1609,9 @@ skip_identity: SigSpec sig_b = assign_map(cell->getPort(ID::B)); SigSpec sig_y = assign_map(cell->getPort(ID::Y)); - if (sig_b.is_fully_def() && sig_b.size() <= 32) + if (sig_b.is_fully_def()) { - int b_val = sig_b.as_int(); - - if (b_val == 0) + if (sig_b.is_fully_zero()) { cover("opt.opt_expr.divmod_zero"); @@ -1648,86 +1625,79 @@ skip_identity: goto next_cell; } - for (int i = 1; i < (b_signed ? sig_b.size()-1 : sig_b.size()); i++) - if (b_val == (1 << i)) + int exp; + if (!keepdc && sig_b.is_onehot(&exp) && !(b_signed && exp == GetSize(sig_b) - 1)) + { + if (cell->type.in(ID($div), ID($divfloor))) { - if (cell->type.in(ID($div), ID($divfloor))) - { - cover("opt.opt_expr.div_shift"); - - bool is_truncating = cell->type == ID($div); - log_debug("Replacing %s-divide-by-%d cell `%s' in module `%s' with shift-by-%d.\n", - is_truncating ? "truncating" : "flooring", - b_val, cell->name.c_str(), module->name.c_str(), i); + cover("opt.opt_expr.div_shift"); - std::vector<RTLIL::SigBit> new_b = RTLIL::SigSpec(i, 6); + bool is_truncating = cell->type == ID($div); + log_debug("Replacing %s-divide-by-%s cell `%s' in module `%s' with shift-by-%d.\n", + is_truncating ? "truncating" : "flooring", + log_signal(sig_b), cell->name.c_str(), module->name.c_str(), exp); - while (GetSize(new_b) > 1 && new_b.back() == RTLIL::State::S0) - new_b.pop_back(); + Const new_b = exp; - cell->type = ID($sshr); - cell->parameters[ID::B_WIDTH] = GetSize(new_b); - cell->parameters[ID::B_SIGNED] = false; - cell->setPort(ID::B, new_b); + cell->type = ID($sshr); + cell->parameters[ID::B_WIDTH] = GetSize(new_b); + cell->parameters[ID::B_SIGNED] = false; + cell->setPort(ID::B, new_b); - // Truncating division is the same as flooring division, except when - // the result is negative and there is a remainder - then trunc = floor + 1 - if (is_truncating && a_signed) { - Wire *flooring = module->addWire(NEW_ID, sig_y.size()); - cell->setPort(ID::Y, flooring); - - Wire *result_neg = module->addWire(NEW_ID); - module->addXor(NEW_ID, sig_a[sig_a.size()-1], sig_b[sig_b.size()-1], result_neg); - Wire *rem_nonzero = module->addWire(NEW_ID); - module->addReduceOr(NEW_ID, sig_a.extract(0, i), rem_nonzero); - Wire *should_add = module->addWire(NEW_ID); - module->addAnd(NEW_ID, result_neg, rem_nonzero, should_add); - module->addAdd(NEW_ID, flooring, should_add, sig_y); - } + // Truncating division is the same as flooring division, except when + // the result is negative and there is a remainder - then trunc = floor + 1 + if (is_truncating && a_signed && GetSize(sig_a) != 0 && exp != 0) { + Wire *flooring = module->addWire(NEW_ID, sig_y.size()); + cell->setPort(ID::Y, flooring); - cell->check(); + SigSpec a_sign = sig_a[sig_a.size()-1]; + SigSpec rem_nonzero = module->ReduceOr(NEW_ID, sig_a.extract(0, exp)); + SigSpec should_add = module->And(NEW_ID, a_sign, rem_nonzero); + module->addAdd(NEW_ID, flooring, should_add, sig_y); } - else if (cell->type.in(ID($mod), ID($modfloor))) + + cell->check(); + } + else if (cell->type.in(ID($mod), ID($modfloor))) + { + cover("opt.opt_expr.mod_mask"); + + bool is_truncating = cell->type == ID($mod); + log_debug("Replacing %s-modulo-by-%s cell `%s' in module `%s' with bitmask.\n", + is_truncating ? "truncating" : "flooring", + log_signal(sig_b), cell->name.c_str(), module->name.c_str()); + + // truncating modulo has the same masked bits as flooring modulo, but + // the sign bits are those of A (except when R=0) + if (is_truncating && a_signed && GetSize(sig_a) != 0 && exp != 0) { - cover("opt.opt_expr.mod_mask"); + module->remove(cell); + SigSpec truncating = sig_a.extract(0, exp); - bool is_truncating = cell->type == ID($mod); - log_debug("Replacing %s-modulo-by-%d cell `%s' in module `%s' with bitmask.\n", - is_truncating ? "truncating" : "flooring", - b_val, cell->name.c_str(), module->name.c_str()); + SigSpec a_sign = sig_a[sig_a.size()-1]; + SigSpec rem_nonzero = module->ReduceOr(NEW_ID, sig_a.extract(0, exp)); + SigSpec extend_bit = module->And(NEW_ID, a_sign, rem_nonzero); - std::vector<RTLIL::SigBit> new_b = RTLIL::SigSpec(State::S1, i); + truncating.append(extend_bit); + module->addPos(NEW_ID, truncating, sig_y, true); + } + else + { + std::vector<RTLIL::SigBit> new_b = RTLIL::SigSpec(State::S1, exp); - if (b_signed) + if (b_signed || exp == 0) new_b.push_back(State::S0); cell->type = ID($and); cell->parameters[ID::B_WIDTH] = GetSize(new_b); cell->setPort(ID::B, new_b); - - // truncating modulo has the same masked bits as flooring modulo, but - // the sign bits are those of A (except when R=0) - if (is_truncating && a_signed) { - Wire *flooring = module->addWire(NEW_ID, sig_y.size()); - cell->setPort(ID::Y, flooring); - SigSpec truncating = SigSpec(flooring).extract(0, i); - - Wire *rem_nonzero = module->addWire(NEW_ID); - module->addReduceOr(NEW_ID, truncating, rem_nonzero); - SigSpec a_sign = sig_a[sig_a.size()-1]; - Wire *extend_bit = module->addWire(NEW_ID); - module->addAnd(NEW_ID, a_sign, rem_nonzero, extend_bit); - - truncating.append(extend_bit); - module->addPos(NEW_ID, truncating, sig_y, true); - } - cell->check(); } - - did_something = true; - goto next_cell; } + + did_something = true; + goto next_cell; + } } } @@ -1957,8 +1927,8 @@ skip_alu_split: replace = true; } - int const_bit_hot = get_onehot_bit_index(const_sig); - if (const_bit_hot >= 0 && const_bit_hot < var_width) + int const_bit_hot; + if (const_sig.is_onehot(&const_bit_hot) && const_bit_hot < var_width) { RTLIL::SigSpec var_high_sig(RTLIL::State::S0, var_width - const_bit_hot); for (int i = const_bit_hot; i < var_width; i++) { @@ -2044,6 +2014,23 @@ skip_alu_split: } } +void replace_const_connections(RTLIL::Module *module) { + SigMap assign_map(module); + for (auto cell : module->selected_cells()) + { + std::vector<std::pair<RTLIL::IdString, SigSpec>> changes; + for (auto &conn : cell->connections()) { + SigSpec mapped = assign_map(conn.second); + if (conn.second != mapped && mapped.is_fully_const()) + changes.push_back({conn.first, mapped}); + } + if (!changes.empty()) + did_something = true; + for (auto &it : changes) + cell->setPort(it.first, it.second); + } +} + struct OptExprPass : public Pass { OptExprPass() : Pass("opt_expr", "perform const folding and simple expression rewriting") { } void help() override @@ -2154,6 +2141,11 @@ struct OptExprPass : public Pass { design->scratchpad_set_bool("opt.did_something", true); } while (did_something); + did_something = false; + replace_const_connections(module); + if (did_something) + design->scratchpad_set_bool("opt.did_something", true); + log_suppressed(); } diff --git a/passes/opt/opt_lut.cc b/passes/opt/opt_lut.cc index 623101016..3b079d964 100644 --- a/passes/opt/opt_lut.cc +++ b/passes/opt/opt_lut.cc @@ -24,16 +24,22 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN +struct dlogic_t { + IdString cell_type; + // LUT input idx -> hard cell's port name + dict<int, IdString> lut_input_port; +}; + struct OptLutWorker { - dict<IdString, dict<int, IdString>> &dlogic; + const std::vector<dlogic_t> &dlogic; RTLIL::Module *module; ModIndex index; SigMap sigmap; pool<RTLIL::Cell*> luts; dict<RTLIL::Cell*, int> luts_arity; - dict<RTLIL::Cell*, pool<RTLIL::Cell*>> luts_dlogics; + dict<RTLIL::Cell*, pool<std::pair<int, RTLIL::Cell*>>> luts_dlogics; dict<RTLIL::Cell*, pool<int>> luts_dlogic_inputs; int eliminated_count = 0, combined_count = 0; @@ -64,7 +70,7 @@ struct OptLutWorker void show_stats_by_arity() { dict<int, int> arity_counts; - dict<IdString, int> dlogic_counts; + std::vector<int> dlogic_counts(dlogic.size()); int max_arity = 0; for (auto lut_arity : luts_arity) @@ -77,7 +83,7 @@ struct OptLutWorker { for (auto &lut_dlogic : lut_dlogics.second) { - dlogic_counts[lut_dlogic->type]++; + dlogic_counts[lut_dlogic.first]++; } } @@ -87,13 +93,13 @@ struct OptLutWorker if (arity_counts[arity]) log(" %d-LUT %16d\n", arity, arity_counts[arity]); } - for (auto &dlogic_count : dlogic_counts) + for (int i = 0; i < GetSize(dlogic); i++) { - log(" with %-12s %4d\n", dlogic_count.first.c_str(), dlogic_count.second); + log(" with %-12s (#%d) %4d\n", dlogic[i].cell_type.c_str(), i, dlogic_counts[i]); } } - OptLutWorker(dict<IdString, dict<int, IdString>> &dlogic, RTLIL::Module *module, int limit) : + OptLutWorker(const std::vector<dlogic_t> &dlogic, RTLIL::Module *module, int limit) : dlogic(dlogic), module(module), index(module), sigmap(module) { log("Discovering LUTs.\n"); @@ -116,20 +122,19 @@ struct OptLutWorker // First, find all dedicated logic we're connected to. This results in an overapproximation // of such connections. - pool<RTLIL::Cell*> lut_all_dlogics; + pool<std::pair<int, RTLIL::Cell*>> lut_all_dlogics; for (int i = 0; i < lut_width; i++) { SigBit bit = lut_input[i]; for (auto &port : index.query_ports(bit)) { - if (dlogic.count(port.cell->type)) + for (int j = 0; j < GetSize(dlogic); j++) { - auto &dlogic_map = dlogic[port.cell->type]; - if (dlogic_map.count(i)) + if (dlogic[j].cell_type == port.cell->type) { - if (port.port == dlogic_map[i]) + if (port.port == dlogic[j].lut_input_port.at(i, IdString())) { - lut_all_dlogics.insert(port.cell); + lut_all_dlogics.insert({j, port.cell}); } } } @@ -143,25 +148,25 @@ struct OptLutWorker // * The connection is illegal. // In either of these cases, we don't need to concern ourselves with preserving the connection // between this LUT and this dedicated logic cell. - pool<RTLIL::Cell*> lut_legal_dlogics; + pool<std::pair<int, RTLIL::Cell*>> lut_legal_dlogics; pool<int> lut_dlogic_inputs; for (auto lut_dlogic : lut_all_dlogics) { - auto &dlogic_map = dlogic[lut_dlogic->type]; + auto &dlogic_map = dlogic[lut_dlogic.first].lut_input_port; bool legal = true; for (auto &dlogic_conn : dlogic_map) { if (lut_width <= dlogic_conn.first) { - log_debug(" LUT has illegal connection to %s cell %s.%s.\n", lut_dlogic->type.c_str(), log_id(module), log_id(lut_dlogic)); + log_debug(" LUT has illegal connection to %s cell %s.%s.\n", lut_dlogic.second->type.c_str(), log_id(module), log_id(lut_dlogic.second)); log_debug(" LUT input A[%d] not present.\n", dlogic_conn.first); legal = false; break; } - if (sigmap(lut_input[dlogic_conn.first]) != sigmap(lut_dlogic->getPort(dlogic_conn.second))) + if (sigmap(lut_input[dlogic_conn.first]) != sigmap(lut_dlogic.second->getPort(dlogic_conn.second))) { - log_debug(" LUT has illegal connection to %s cell %s.%s.\n", lut_dlogic->type.c_str(), log_id(module), log_id(lut_dlogic)); - log_debug(" LUT input A[%d] (wire %s) not connected to %s port %s (wire %s).\n", dlogic_conn.first, log_signal(lut_input[dlogic_conn.first]), lut_dlogic->type.c_str(), dlogic_conn.second.c_str(), log_signal(lut_dlogic->getPort(dlogic_conn.second))); + log_debug(" LUT has illegal connection to %s cell %s.%s.\n", lut_dlogic.second->type.c_str(), log_id(module), log_id(lut_dlogic.second)); + log_debug(" LUT input A[%d] (wire %s) not connected to %s port %s (wire %s).\n", dlogic_conn.first, log_signal(lut_input[dlogic_conn.first]), lut_dlogic.second->type.c_str(), dlogic_conn.second.c_str(), log_signal(lut_dlogic.second->getPort(dlogic_conn.second))); legal = false; break; } @@ -169,7 +174,7 @@ struct OptLutWorker if (legal) { - log_debug(" LUT has legal connection to %s cell %s.%s.\n", lut_dlogic->type.c_str(), log_id(module), log_id(lut_dlogic)); + log_debug(" LUT has legal connection to %s cell %s.%s.\n", lut_dlogic.second->type.c_str(), log_id(module), log_id(lut_dlogic.second)); lut_legal_dlogics.insert(lut_dlogic); for (auto &dlogic_conn : dlogic_map) lut_dlogic_inputs.insert(dlogic_conn.first); @@ -544,7 +549,7 @@ struct OptLutPass : public Pass { { log_header(design, "Executing OPT_LUT pass (optimize LUTs).\n"); - dict<IdString, dict<int, IdString>> dlogic; + std::vector<dlogic_t> dlogic; int limit = -1; size_t argidx; @@ -556,7 +561,8 @@ struct OptLutPass : public Pass { split(tokens, args[++argidx], ':'); if (tokens.size() < 2) log_cmd_error("The -dlogic option requires at least one connection.\n"); - IdString type = "\\" + tokens[0]; + dlogic_t entry; + entry.cell_type = "\\" + tokens[0]; for (auto it = tokens.begin() + 1; it != tokens.end(); ++it) { std::vector<std::string> conn_tokens; split(conn_tokens, *it, '='); @@ -564,8 +570,9 @@ struct OptLutPass : public Pass { log_cmd_error("Invalid format of -dlogic signal mapping.\n"); IdString logic_port = "\\" + conn_tokens[0]; int lut_input = atoi(conn_tokens[1].c_str()); - dlogic[type][lut_input] = logic_port; + entry.lut_input_port[lut_input] = logic_port; } + dlogic.push_back(entry); continue; } if (args[argidx] == "-limit" && argidx + 1 < args.size()) diff --git a/passes/opt/opt_lut_ins.cc b/passes/opt/opt_lut_ins.cc index bb40e1e55..2f7c392b2 100644 --- a/passes/opt/opt_lut_ins.cc +++ b/passes/opt/opt_lut_ins.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -193,6 +193,12 @@ struct OptLutInsPass : public Pass { swz += extra; } } + if (techname == "gowin") { + // Pad the LUT to 1 input, adding consts from the front. + if (new_inputs.empty()) { + new_inputs.insert(new_inputs.begin(), State::S0); + } + } Const new_lut(0, 1 << GetSize(new_inputs)); for (int i = 0; i < GetSize(new_lut); i++) { int lidx = 0; @@ -209,9 +215,9 @@ struct OptLutInsPass : public Pass { } new_lut[i] = lut[lidx]; } - // For ecp5, do not replace with a const driver — the nextpnr + // For ecp5, and gowin do not replace with a const driver — the nextpnr // packer requires a complete set of LUTs for wide LUT muxes. - if (new_inputs.empty() && techname != "ecp5") { + if (new_inputs.empty() && techname != "ecp5" && techname != "gowin") { // const driver. remove_cells.push_back(cell); module->connect(output, new_lut[0]); diff --git a/passes/opt/opt_mem.cc b/passes/opt/opt_mem.cc index 49a0ac51a..edadf2c7b 100644 --- a/passes/opt/opt_mem.cc +++ b/passes/opt/opt_mem.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -51,8 +51,32 @@ struct OptMemPass : public Pass { int total_count = 0; for (auto module : design->selected_modules()) { + SigMap sigmap(module); + FfInitVals initvals(&sigmap, module); for (auto &mem : Mem::get_selected_memories(module)) { + bool changed = false; + for (auto &port : mem.wr_ports) { + if (port.en.is_fully_zero()) { + port.removed = true; + changed = true; + total_count++; + } + } + if (changed) { + mem.emit(); + } + if (mem.wr_ports.empty() && mem.inits.empty()) { + // The whole memory array will contain + // only State::Sx, but the embedded read + // registers could have reset or init values. + // They will probably be optimized away by + // opt_dff later. + for (int i = 0; i < GetSize(mem.rd_ports); i++) { + mem.extract_rdff(i, &initvals); + auto &port = mem.rd_ports[i]; + module->connect(port.data, Const(State::Sx, GetSize(port.data))); + } mem.remove(); total_count++; } diff --git a/passes/opt/opt_mem_feedback.cc b/passes/opt/opt_mem_feedback.cc new file mode 100644 index 000000000..20a2a79ed --- /dev/null +++ b/passes/opt/opt_mem_feedback.cc @@ -0,0 +1,350 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> + * + * 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" +#include "kernel/mem.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +// Describes found feedback path. +struct FeedbackPath { + // Which write port it is. + int wrport_idx; + // Which data bit of that write port it is. + int data_bit_idx; + // Values of all mux select signals that need to be set to select this path. + dict<RTLIL::SigBit, bool> condition; + // The exact feedback bit used (used to match read port). + SigBit feedback_bit; + + FeedbackPath(int wrport_idx, int data_bit_idx, dict<RTLIL::SigBit, bool> condition, SigBit feedback_bit) : wrport_idx(wrport_idx), data_bit_idx(data_bit_idx), condition(condition), feedback_bit(feedback_bit) {} +}; + +struct OptMemFeedbackWorker +{ + RTLIL::Design *design; + RTLIL::Module *module; + SigMap sigmap, sigmap_xmux; + FfInitVals initvals; + + dict<RTLIL::SigBit, std::pair<RTLIL::Cell*, int>> sig_to_mux; + dict<RTLIL::SigBit, int> sig_users_count; + dict<pair<pool<dict<SigBit, bool>>, SigBit>, SigBit> conditions_logic_cache; + + + // ----------------------------------------------------------------- + // Converting feedbacks to async read ports to proper enable signals + // ----------------------------------------------------------------- + + void find_data_feedback(const pool<RTLIL::SigBit> &async_rd_bits, RTLIL::SigBit sig, + const dict<RTLIL::SigBit, bool> &state, + int wrport_idx, int data_bit_idx, + std::vector<FeedbackPath> &paths) + { + if (async_rd_bits.count(sig)) { + paths.push_back(FeedbackPath(wrport_idx, data_bit_idx, state, sig)); + return; + } + + if (sig_users_count[sig] != 1) { + // Only descend into muxes if we're the only user. + return; + } + + if (sig_to_mux.count(sig) == 0) + return; + + RTLIL::Cell *cell = sig_to_mux.at(sig).first; + int bit_idx = sig_to_mux.at(sig).second; + + std::vector<RTLIL::SigBit> sig_a = sigmap(cell->getPort(ID::A)); + std::vector<RTLIL::SigBit> sig_b = sigmap(cell->getPort(ID::B)); + std::vector<RTLIL::SigBit> sig_s = sigmap(cell->getPort(ID::S)); + std::vector<RTLIL::SigBit> sig_y = sigmap(cell->getPort(ID::Y)); + log_assert(sig_y.at(bit_idx) == sig); + + for (int i = 0; i < GetSize(sig_s); i++) + if (state.count(sig_s[i]) && state.at(sig_s[i]) == true) { + find_data_feedback(async_rd_bits, sig_b.at(bit_idx + i*sig_y.size()), state, wrport_idx, data_bit_idx, paths); + return; + } + + + for (int i = 0; i < GetSize(sig_s); i++) + { + if (state.count(sig_s[i]) && state.at(sig_s[i]) == false) + continue; + + dict<RTLIL::SigBit, bool> new_state = state; + new_state[sig_s[i]] = true; + + find_data_feedback(async_rd_bits, sig_b.at(bit_idx + i*sig_y.size()), new_state, wrport_idx, data_bit_idx, paths); + } + + dict<RTLIL::SigBit, bool> new_state = state; + for (auto bit : sig_s) + new_state[bit] = false; + + find_data_feedback(async_rd_bits, sig_a.at(bit_idx), new_state, wrport_idx, data_bit_idx, paths); + } + + RTLIL::SigBit conditions_to_logic(pool<dict<RTLIL::SigBit, bool>> &conditions, SigBit olden) + { + 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) { + RTLIL::SigSpec sig1, sig2; + for (auto &it : cond) { + sig1.append(it.first); + sig2.append(it.second ? RTLIL::State::S1 : RTLIL::State::S0); + } + terms.append(module->Ne(NEW_ID, sig1, sig2)); + } + + if (olden != State::S1) + terms.append(olden); + + if (GetSize(terms) == 0) + terms = State::S1; + + if (GetSize(terms) > 1) + terms = module->ReduceAnd(NEW_ID, terms); + + return conditions_logic_cache[key] = terms; + } + + void translate_rd_feedback_to_en(Mem &mem) + { + // Look for async read ports that may be suitable for feedback paths. + dict<RTLIL::SigSpec, std::vector<pool<RTLIL::SigBit>>> async_rd_bits; + + for (auto &port : mem.rd_ports) + { + if (port.clk_enable) + continue; + + for (int sub = 0; sub < (1 << port.wide_log2); sub++) { + SigSpec addr = sigmap_xmux(port.sub_addr(sub)); + async_rd_bits[addr].resize(mem.width); + for (int i = 0; i < mem.width; i++) + async_rd_bits[addr][i].insert(sigmap(port.data[i + sub * mem.width])); + } + } + + if (async_rd_bits.empty()) + return; + + // Look for actual feedback paths. + std::vector<FeedbackPath> paths; + + for (int i = 0; i < GetSize(mem.wr_ports); i++) + { + auto &port = mem.wr_ports[i]; + + log(" Analyzing %s.%s write port %d.\n", log_id(module), log_id(mem.memid), i); + + for (int sub = 0; sub < (1 << port.wide_log2); sub++) + { + SigSpec addr = sigmap_xmux(port.sub_addr(sub)); + + if (!async_rd_bits.count(addr)) + continue; + + for (int j = 0; j < mem.width; j++) + { + int bit_idx = sub * mem.width + j; + + if (port.en[bit_idx] == State::S0) + continue; + + dict<RTLIL::SigBit, bool> state; + + find_data_feedback(async_rd_bits.at(addr).at(j), sigmap(port.data[bit_idx]), state, i, bit_idx, paths); + } + } + } + + if (paths.empty()) + return; + + // Now determine which read ports are actually used only for + // feedback paths, and can be removed. + + dict<SigBit, int> feedback_users_count; + for (auto &path : paths) + feedback_users_count[path.feedback_bit]++; + + pool<SigBit> feedback_ok; + for (auto &port : mem.rd_ports) + { + if (port.clk_enable) + continue; + + bool ok = true; + for (auto bit : sigmap(port.data)) + if (sig_users_count[bit] != feedback_users_count[bit]) + ok = false; + + if (ok) + { + // This port is going bye-bye. + for (auto bit : sigmap(port.data)) + feedback_ok.insert(bit); + + port.removed = true; + } + } + + if (feedback_ok.empty()) + return; + + // Prepare a feedback condition list grouped by port bits. + + dict<std::pair<int, int>, pool<dict<SigBit, bool>>> portbit_conds; + for (auto &path : paths) + if (feedback_ok.count(path.feedback_bit)) + portbit_conds[std::make_pair(path.wrport_idx, path.data_bit_idx)].insert(path.condition); + + if (portbit_conds.empty()) + return; + + // Okay, let's do it. + + log("Populating enable bits on write ports of memory %s.%s with async read feedback:\n", log_id(module), log_id(mem.memid)); + + // If a write port has a feedback path that we're about to bypass, + // but also has priority over some other write port, the feedback + // path is not necessarily a NOP — it may overwrite the other port. + // Emulate this effect by converting the priority to soft logic + // (this will affect the other port's enable signal). + for (auto &it : portbit_conds) + { + int wrport_idx = it.first.first; + auto &port = mem.wr_ports[wrport_idx]; + + for (int i = 0; i < wrport_idx; i++) + if (port.priority_mask[i]) + mem.emulate_priority(i, wrport_idx, &initvals); + } + + for (auto &it : portbit_conds) + { + int wrport_idx = it.first.first; + int bit = it.first.second; + auto &port = mem.wr_ports[wrport_idx]; + + port.en[bit] = conditions_to_logic(it.second, port.en[bit]); + log(" Port %d bit %d: added enable logic for %d different cases.\n", wrport_idx, bit, GetSize(it.second)); + } + + mem.emit(); + + for (auto bit : feedback_ok) + module->connect(bit, State::Sx); + + design->scratchpad_set_bool("opt.did_something", true); + } + + // ------------- + // Setup and run + // ------------- + + OptMemFeedbackWorker(RTLIL::Design *design) : design(design) {} + + void operator()(RTLIL::Module* module) + { + std::vector<Mem> memories = Mem::get_selected_memories(module); + + this->module = module; + sigmap.set(module); + initvals.set(&sigmap, module); + sig_to_mux.clear(); + conditions_logic_cache.clear(); + + sigmap_xmux = sigmap; + + for (auto wire : module->wires()) { + if (wire->port_output) + for (auto bit : sigmap(wire)) + sig_users_count[bit]++; + } + + for (auto cell : module->cells()) + { + if (cell->type == ID($mux)) + { + RTLIL::SigSpec sig_a = sigmap_xmux(cell->getPort(ID::A)); + RTLIL::SigSpec sig_b = sigmap_xmux(cell->getPort(ID::B)); + + if (sig_a.is_fully_undef()) + sigmap_xmux.add(cell->getPort(ID::Y), sig_b); + else if (sig_b.is_fully_undef()) + sigmap_xmux.add(cell->getPort(ID::Y), sig_a); + } + + if (cell->type.in(ID($mux), ID($pmux))) + { + std::vector<RTLIL::SigBit> sig_y = sigmap(cell->getPort(ID::Y)); + for (int i = 0; i < int(sig_y.size()); i++) + sig_to_mux[sig_y[i]] = std::pair<RTLIL::Cell*, int>(cell, i); + } + + for (auto &conn : cell->connections()) + if (!cell->known() || cell->input(conn.first)) + for (auto bit : sigmap(conn.second)) + sig_users_count[bit]++; + } + + for (auto &mem : memories) + translate_rd_feedback_to_en(mem); + } +}; + +struct OptMemFeedbackPass : public Pass { + OptMemFeedbackPass() : Pass("opt_mem_feedback", "convert memory read-to-write port feedback paths to write enables") { } + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" opt_mem_feedback [selection]\n"); + log("\n"); + log("This pass detects cases where an asynchronous read port is only connected via\n"); + log("a mux tree to a write port with the same address. When such a connection is\n"); + log("found, it is replaced with a new condition on an enable signal, allowing\n"); + log("for removal of the read port.\n"); + log("\n"); + } + void execute(std::vector<std::string> args, RTLIL::Design *design) override { + log_header(design, "Executing OPT_MEM_FEEDBACK pass (finding memory read-to-write feedback paths).\n"); + extra_args(args, 1, design); + OptMemFeedbackWorker worker(design); + + for (auto module : design->selected_modules()) + worker(module); + } +} OptMemFeedbackPass; + +PRIVATE_NAMESPACE_END + diff --git a/passes/opt/opt_mem_priority.cc b/passes/opt/opt_mem_priority.cc new file mode 100644 index 000000000..a9b145bea --- /dev/null +++ b/passes/opt/opt_mem_priority.cc @@ -0,0 +1,109 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2021 Marcelina KoÅ›cielnicka <mwk@0x04.net> + * + * 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/modtools.h" +#include "kernel/qcsat.h" +#include "kernel/mem.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct OptMemPriorityPass : public Pass { + OptMemPriorityPass() : Pass("opt_mem_priority", "remove priority relations between write ports that can never collide") { } + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" opt_mem_priority [selection]\n"); + log("\n"); + log("This pass detects cases where one memory write port has priority over another\n"); + log("even though they can never collide with each other -- ie. there can never be\n"); + log("a situation where a given memory bit is written by both ports at the same\n"); + log("time, for example because of always-different addresses, or mutually exclusive\n"); + log("enable signals. In such cases, the priority relation is removed.\n"); + log("\n"); + } + void execute(std::vector<std::string> args, RTLIL::Design *design) override { + log_header(design, "Executing OPT_MEM_PRIORITY pass (removing unnecessary memory write priority relations).\n"); + extra_args(args, 1, design); + + ModWalker modwalker(design); + + int total_count = 0; + for (auto module : design->selected_modules()) { + modwalker.setup(module); + for (auto &mem : Mem::get_selected_memories(module)) { + bool mem_changed = false; + QuickConeSat qcsat(modwalker); + for (int i = 0; i < GetSize(mem.wr_ports); i++) { + auto &wport1 = mem.wr_ports[i]; + for (int j = 0; j < GetSize(mem.wr_ports); j++) { + auto &wport2 = mem.wr_ports[j]; + if (!wport1.priority_mask[j]) + continue; + // No mixed width support — we could do it, but + // that would complicate code and wouldn't help + // anything since we run this pass before + // wide ports are created in normal flow. + if (wport1.wide_log2 != wport2.wide_log2) + continue; + // Two ports with priority, let's go. + pool<std::pair<SigBit, SigBit>> checked; + SigSpec addr1 = wport1.addr; + SigSpec addr2 = wport2.addr; + int abits = std::max(GetSize(addr1), GetSize(addr2)); + addr1.extend_u0(abits); + addr2.extend_u0(abits); + int addr_eq = qcsat.ez->vec_eq(qcsat.importSig(addr1), qcsat.importSig(addr2)); + bool ok = true; + for (int k = 0; k < GetSize(wport1.data); k++) { + SigBit wen1 = wport1.en[k]; + SigBit wen2 = wport2.en[k]; + if (checked.count({wen1, wen2})) + continue; + int wen1_sat = qcsat.importSigBit(wen1); + int wen2_sat = qcsat.importSigBit(wen2); + qcsat.prepare(); + if (qcsat.ez->solve(wen1_sat, wen2_sat, addr_eq)) { + ok = false; + break; + } + checked.insert({wen1, wen2}); + } + if (ok) { + total_count++; + mem_changed = true; + wport1.priority_mask[j] = false; + } + } + } + if (mem_changed) + mem.emit(); + } + } + + if (total_count) + design->scratchpad_set_bool("opt.did_something", true); + log("Performed a total of %d transformations.\n", total_count); + } +} OptMemPriorityPass; + +PRIVATE_NAMESPACE_END + diff --git a/passes/opt/opt_mem_widen.cc b/passes/opt/opt_mem_widen.cc new file mode 100644 index 000000000..95e01088c --- /dev/null +++ b/passes/opt/opt_mem_widen.cc @@ -0,0 +1,107 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2021 Marcelina KoÅ›cielnicka <mwk@0x04.net> + * + * 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/mem.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct OptMemWidenPass : public Pass { + OptMemWidenPass() : Pass("opt_mem_widen", "optimize memories where all ports are wide") { } + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" opt_mem_widen [options] [selection]\n"); + log("\n"); + log("This pass looks for memories where all ports are wide and adjusts the base\n"); + log("memory width up until that stops being the case.\n"); + log("\n"); + } + void execute(std::vector<std::string> args, RTLIL::Design *design) override + { + log_header(design, "Executing OPT_MEM_WIDEN pass (optimize memories where all ports are wide).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + // if (args[argidx] == "-nomux") { + // mode_nomux = true; + // continue; + // } + break; + } + extra_args(args, argidx, design); + + int total_count = 0; + for (auto module : design->selected_modules()) { + for (auto &mem : Mem::get_selected_memories(module)) { + // If the memory has no read ports, opt_clean will remove it + // instead. + if (mem.rd_ports.empty()) + continue; + int factor_log2 = mem.rd_ports[0].wide_log2; + for (auto &port : mem.rd_ports) + if (port.wide_log2 < factor_log2) + factor_log2 = port.wide_log2; + for (auto &port : mem.wr_ports) + if (port.wide_log2 < factor_log2) + factor_log2 = port.wide_log2; + if (factor_log2 == 0) + continue; + log("Widening base width of memory %s in module %s by factor %d.\n", log_id(mem.memid), log_id(module->name), 1 << factor_log2); + total_count++; + // The inits are too messy to expand one-by-one, for they may + // collide with one another after expansion. Just hit it with + // a hammer. + bool has_init = !mem.inits.empty(); + Const init_data; + if (has_init) { + init_data = mem.get_init_data(); + mem.clear_inits(); + } + mem.width <<= factor_log2; + mem.size >>= factor_log2; + mem.start_offset >>= factor_log2; + if (has_init) { + MemInit new_init; + new_init.addr = mem.start_offset; + new_init.data = init_data; + new_init.en = Const(State::S1, mem.width); + mem.inits.push_back(new_init); + } + for (auto &port : mem.rd_ports) { + port.wide_log2 -= factor_log2; + port.addr = port.addr.extract_end(factor_log2); + } + for (auto &port : mem.wr_ports) { + port.wide_log2 -= factor_log2; + port.addr = port.addr.extract_end(factor_log2); + } + mem.emit(); + } + } + + if (total_count) + design->scratchpad_set_bool("opt.did_something", true); + log("Performed a total of %d transformations.\n", total_count); + } +} OptMemWidenPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/opt/opt_merge.cc b/passes/opt/opt_merge.cc index 9086943dc..115eb97a9 100644 --- a/passes/opt/opt_merge.cc +++ b/passes/opt/opt_merge.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -18,6 +18,7 @@ */ #include "kernel/register.h" +#include "kernel/ffinit.h" #include "kernel/sigtools.h" #include "kernel/log.h" #include "kernel/celltypes.h" @@ -35,7 +36,7 @@ struct OptMergeWorker RTLIL::Design *design; RTLIL::Module *module; SigMap assign_map; - SigMap dff_init_map; + FfInitVals initvals; bool mode_share_all; CellTypes ct; @@ -121,8 +122,7 @@ struct OptMergeWorker if (it.first == ID::Q && RTLIL::builtin_ff_cell_types().count(cell->type)) { // For the 'Q' output of state elements, // use its (* init *) attribute value - for (const auto &b : dff_init_map(it.second)) - sig.append(b.wire ? State::Sx : b); + sig = initvals(it.second); } else continue; @@ -176,12 +176,8 @@ struct OptMergeWorker if (it.first == ID::Q && RTLIL::builtin_ff_cell_types().count(cell1->type)) { // For the 'Q' output of state elements, // use the (* init *) attribute value - auto &sig1 = conn1[it.first]; - for (const auto &b : dff_init_map(it.second)) - sig1.append(b.wire ? State::Sx : b); - auto &sig2 = conn2[it.first]; - for (const auto &b : dff_init_map(cell2->getPort(it.first))) - sig2.append(b.wire ? State::Sx : b); + conn1[it.first] = initvals(it.second); + conn2[it.first] = initvals(cell2->getPort(it.first)); } else { conn1[it.first] = RTLIL::SigSpec(); @@ -247,14 +243,7 @@ struct OptMergeWorker log("Finding identical cells in module `%s'.\n", module->name.c_str()); assign_map.set(module); - dff_init_map.set(module); - for (auto &it : module->wires_) - if (it.second->attributes.count(ID::init) != 0) { - Const initval = it.second->attributes.at(ID::init); - for (int i = 0; i < GetSize(initval) && i < GetSize(it.second); i++) - if (initval[i] == State::S0 || initval[i] == State::S1) - dff_init_map.add(SigBit(it.second, i), initval[i]); - } + initvals.set(&assign_map, module); bool did_something = true; while (did_something) @@ -293,19 +282,12 @@ struct OptMergeWorker RTLIL::SigSpec other_sig = r.first->second->getPort(it.first); log_debug(" Redirecting output %s: %s = %s\n", it.first.c_str(), log_signal(it.second), log_signal(other_sig)); + Const init = initvals(other_sig); + initvals.remove_init(it.second); + initvals.remove_init(other_sig); module->connect(RTLIL::SigSig(it.second, other_sig)); assign_map.add(it.second, other_sig); - - if (it.first == ID::Q && RTLIL::builtin_ff_cell_types().count(cell->type)) { - for (auto c : it.second.chunks()) { - auto jt = c.wire->attributes.find(ID::init); - if (jt == c.wire->attributes.end()) - continue; - for (int i = c.offset; i < c.offset + c.width; i++) - jt->second[i] = State::Sx; - } - dff_init_map.add(it.second, Const(State::Sx, GetSize(it.second))); - } + initvals.set_init(other_sig, init); } } log_debug(" Removing %s cell `%s' from module `%s'.\n", cell->type.c_str(), cell->name.c_str(), module->name.c_str()); diff --git a/passes/opt/opt_muxtree.cc b/passes/opt/opt_muxtree.cc index 67b283e11..100b1b495 100644 --- a/passes/opt/opt_muxtree.cc +++ b/passes/opt/opt_muxtree.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -372,29 +372,28 @@ struct OptMuxtreeWorker int port_idx = 0, port_off = 0; vector<int> bits = sig2bits(sig, false); for (int i = 0; i < GetSize(bits); i++) { - if (bits[i] < 0) - continue; - if (knowledge.known_inactive.at(bits[i])) { - sig[i] = State::S0; - did_something = true; - } else - if (knowledge.known_active.at(bits[i])) { - sig[i] = State::S1; - did_something = true; - } - if (width) { - if (ctrl_bits.count(bits[i])) { - sig[i] = ctrl_bits.at(bits[i]) == port_idx ? State::S1 : State::S0; + if (bits[i] >= 0) { + if (knowledge.known_inactive.at(bits[i])) { + sig[i] = State::S0; + did_something = true; + } else + if (knowledge.known_active.at(bits[i])) { + sig[i] = State::S1; did_something = true; } - if (++port_off == width) - port_idx++, port_off=0; - } else { if (ctrl_bits.count(bits[i])) { - sig[i] = State::S0; + if (width) { + sig[i] = ctrl_bits.at(bits[i]) == port_idx ? State::S1 : State::S0; + } else { + sig[i] = State::S0; + } did_something = true; } } + if (width) { + if (++port_off == width) + port_idx++, port_off=0; + } } if (did_something) { diff --git a/passes/opt/opt_reduce.cc b/passes/opt/opt_reduce.cc index 28de9ceb6..1a7c93fbd 100644 --- a/passes/opt/opt_reduce.cc +++ b/passes/opt/opt_reduce.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -100,7 +100,7 @@ struct OptReduceWorker return; } - void opt_mux(RTLIL::Cell *cell) + void opt_pmux(RTLIL::Cell *cell) { RTLIL::SigSpec sig_a = assign_map(cell->getPort(ID::A)); RTLIL::SigSpec sig_b = assign_map(cell->getPort(ID::B)); @@ -141,20 +141,20 @@ struct OptReduceWorker handled_sig.insert(this_b); } - if (new_sig_s.size() != sig_s.size()) { - log(" New ctrl vector for %s cell %s: %s\n", cell->type.c_str(), cell->name.c_str(), log_signal(new_sig_s)); - did_something = true; - total_count++; - } - if (new_sig_s.size() == 0) { - module->connect(RTLIL::SigSig(cell->getPort(ID::Y), cell->getPort(ID::A))); + module->connect(cell->getPort(ID::Y), cell->getPort(ID::A)); assign_map.add(cell->getPort(ID::Y), cell->getPort(ID::A)); module->remove(cell); + did_something = true; + total_count++; + return; } - else - { + + if (new_sig_s.size() != sig_s.size() || (new_sig_s.size() == 1 && cell->type == ID($pmux))) { + log(" New ctrl vector for %s cell %s: %s\n", cell->type.c_str(), cell->name.c_str(), log_signal(new_sig_s)); + did_something = true; + total_count++; cell->setPort(ID::B, new_sig_b); cell->setPort(ID::S, new_sig_s); if (new_sig_s.size() > 1) { @@ -166,81 +166,347 @@ struct OptReduceWorker } } - void opt_mux_bits(RTLIL::Cell *cell) + void opt_bmux(RTLIL::Cell *cell) { - std::vector<RTLIL::SigBit> sig_a = assign_map(cell->getPort(ID::A)).to_sigbit_vector(); - std::vector<RTLIL::SigBit> sig_b = assign_map(cell->getPort(ID::B)).to_sigbit_vector(); - std::vector<RTLIL::SigBit> sig_y = assign_map(cell->getPort(ID::Y)).to_sigbit_vector(); + RTLIL::SigSpec sig_a = assign_map(cell->getPort(ID::A)); + RTLIL::SigSpec sig_s = assign_map(cell->getPort(ID::S)); + int width = cell->getParam(ID::WIDTH).as_int(); + + RTLIL::SigSpec new_sig_a, new_sig_s; + dict<RTLIL::SigBit, int> handled_bits; + + // 0 and up: index of new_sig_s bit + // -1: const 0 + // -2: const 1 + std::vector<int> swizzle; + + for (int i = 0; i < sig_s.size(); i++) + { + SigBit bit = sig_s[i]; + if (bit == State::S0) { + swizzle.push_back(-1); + } else if (bit == State::S1) { + swizzle.push_back(-2); + } else { + auto it = handled_bits.find(bit); + if (it == handled_bits.end()) { + int new_idx = GetSize(new_sig_s); + new_sig_s.append(bit); + handled_bits[bit] = new_idx; + swizzle.push_back(new_idx); + } else { + swizzle.push_back(it->second); + } + } + } + + for (int i = 0; i < (1 << GetSize(new_sig_s)); i++) { + int idx = 0; + for (int j = 0; j < GetSize(sig_s); j++) { + if (swizzle[j] == -1) { + // const 0. + } else if (swizzle[j] == -2) { + // const 1. + idx |= 1 << j; + } else { + if (i & 1 << swizzle[j]) + idx |= 1 << j; + } + } + new_sig_a.append(sig_a.extract(idx * width, width)); + } + + if (new_sig_s.size() == 0) + { + module->connect(cell->getPort(ID::Y), new_sig_a); + assign_map.add(cell->getPort(ID::Y), new_sig_a); + module->remove(cell); + did_something = true; + total_count++; + return; + } + + if (new_sig_s.size() == 1) + { + cell->type = ID($mux); + cell->setPort(ID::A, new_sig_a.extract(0, width)); + cell->setPort(ID::B, new_sig_a.extract(width, width)); + cell->setPort(ID::S, new_sig_s); + cell->parameters.erase(ID::S_WIDTH); + did_something = true; + total_count++; + return; + } + + if (new_sig_s.size() != sig_s.size()) { + log(" New ctrl vector for %s cell %s: %s\n", cell->type.c_str(), cell->name.c_str(), log_signal(new_sig_s)); + did_something = true; + total_count++; + cell->setPort(ID::A, new_sig_a); + cell->setPort(ID::S, new_sig_s); + cell->parameters[ID::S_WIDTH] = RTLIL::Const(new_sig_s.size()); + } + } + + void opt_demux(RTLIL::Cell *cell) + { + RTLIL::SigSpec sig_y = assign_map(cell->getPort(ID::Y)); + RTLIL::SigSpec sig_s = assign_map(cell->getPort(ID::S)); + int width = cell->getParam(ID::WIDTH).as_int(); + + RTLIL::SigSpec new_sig_y, new_sig_s; + dict<RTLIL::SigBit, int> handled_bits; + + // 0 and up: index of new_sig_s bit + // -1: const 0 + // -2: const 1 + std::vector<int> swizzle; + + for (int i = 0; i < sig_s.size(); i++) + { + SigBit bit = sig_s[i]; + if (bit == State::S0) { + swizzle.push_back(-1); + } else if (bit == State::S1) { + swizzle.push_back(-2); + } else { + auto it = handled_bits.find(bit); + if (it == handled_bits.end()) { + int new_idx = GetSize(new_sig_s); + new_sig_s.append(bit); + handled_bits[bit] = new_idx; + swizzle.push_back(new_idx); + } else { + swizzle.push_back(it->second); + } + } + } + + pool<int> nonzero_idx; + + for (int i = 0; i < (1 << GetSize(new_sig_s)); i++) { + int idx = 0; + for (int j = 0; j < GetSize(sig_s); j++) { + if (swizzle[j] == -1) { + // const 0. + } else if (swizzle[j] == -2) { + // const 1. + idx |= 1 << j; + } else { + if (i & 1 << swizzle[j]) + idx |= 1 << j; + } + } + log_assert(!nonzero_idx.count(idx)); + nonzero_idx.insert(idx); + new_sig_y.append(sig_y.extract(idx * width, width)); + } + + if (new_sig_s.size() == sig_s.size() && sig_s.size() > 0) + return; + + log(" New ctrl vector for %s cell %s: %s\n", cell->type.c_str(), cell->name.c_str(), log_signal(new_sig_s)); + did_something = true; + total_count++; + + for (int i = 0; i < (1 << GetSize(sig_s)); i++) { + if (!nonzero_idx.count(i)) { + SigSpec slice = sig_y.extract(i * width, width); + module->connect(slice, Const(State::S0, width)); + assign_map.add(slice, Const(State::S0, width)); + } + } + + if (new_sig_s.size() == 0) + { + module->connect(new_sig_y, cell->getPort(ID::A)); + assign_map.add(new_sig_y, cell->getPort(ID::A)); + module->remove(cell); + } + else + { + cell->setPort(ID::S, new_sig_s); + cell->setPort(ID::Y, new_sig_y); + cell->parameters[ID::S_WIDTH] = RTLIL::Const(new_sig_s.size()); + } + } + + bool opt_mux_bits(RTLIL::Cell *cell) + { + SigSpec sig_a = assign_map(cell->getPort(ID::A)); + SigSpec sig_b; + SigSpec sig_y = assign_map(cell->getPort(ID::Y)); + int width = GetSize(sig_y); + + if (cell->type != ID($bmux)) + sig_b = assign_map(cell->getPort(ID::B)); - std::vector<RTLIL::SigBit> new_sig_y; RTLIL::SigSig old_sig_conn; - std::vector<std::vector<RTLIL::SigBit>> consolidated_in_tuples; - std::map<std::vector<RTLIL::SigBit>, RTLIL::SigBit> consolidated_in_tuples_map; + dict<SigSpec, SigBit> consolidated_in_tuples; + std::vector<int> swizzle; - for (int i = 0; i < int(sig_y.size()); i++) + for (int i = 0; i < width; i++) { - std::vector<RTLIL::SigBit> in_tuple; + SigSpec in_tuple; bool all_tuple_bits_same = true; - in_tuple.push_back(sig_a.at(i)); - for (int j = i; j < int(sig_b.size()); j += int(sig_a.size())) { - if (sig_b.at(j) != sig_a.at(i)) + in_tuple.append(sig_a[i]); + for (int j = i; j < GetSize(sig_a); j += width) { + in_tuple.append(sig_a[j]); + if (sig_a[j] != in_tuple[0]) + all_tuple_bits_same = false; + } + for (int j = i; j < GetSize(sig_b); j += width) { + in_tuple.append(sig_b[j]); + if (sig_b[j] != in_tuple[0]) all_tuple_bits_same = false; - in_tuple.push_back(sig_b.at(j)); } if (all_tuple_bits_same) { - old_sig_conn.first.append(sig_y.at(i)); - old_sig_conn.second.append(sig_a.at(i)); + old_sig_conn.first.append(sig_y[i]); + old_sig_conn.second.append(sig_a[i]); + continue; } - else if (consolidated_in_tuples_map.count(in_tuple)) + + auto it = consolidated_in_tuples.find(in_tuple); + if (it == consolidated_in_tuples.end()) { - old_sig_conn.first.append(sig_y.at(i)); - old_sig_conn.second.append(consolidated_in_tuples_map.at(in_tuple)); + consolidated_in_tuples[in_tuple] = sig_y[i]; + swizzle.push_back(i); } else { - consolidated_in_tuples_map[in_tuple] = sig_y.at(i); - consolidated_in_tuples.push_back(in_tuple); - new_sig_y.push_back(sig_y.at(i)); + old_sig_conn.first.append(sig_y[i]); + old_sig_conn.second.append(it->second); } } - if (new_sig_y.size() != sig_y.size()) + if (GetSize(swizzle) != width) { log(" Consolidated identical input bits for %s cell %s:\n", cell->type.c_str(), cell->name.c_str()); - log(" Old ports: A=%s, B=%s, Y=%s\n", log_signal(cell->getPort(ID::A)), - log_signal(cell->getPort(ID::B)), log_signal(cell->getPort(ID::Y))); - - cell->setPort(ID::A, RTLIL::SigSpec()); - for (auto &in_tuple : consolidated_in_tuples) { - RTLIL::SigSpec new_a = cell->getPort(ID::A); - new_a.append(in_tuple.at(0)); - cell->setPort(ID::A, new_a); + if (cell->type != ID($bmux)) { + log(" Old ports: A=%s, B=%s, Y=%s\n", log_signal(cell->getPort(ID::A)), + log_signal(cell->getPort(ID::B)), log_signal(cell->getPort(ID::Y))); + } else { + log(" Old ports: A=%s, Y=%s\n", log_signal(cell->getPort(ID::A)), + log_signal(cell->getPort(ID::Y))); } - cell->setPort(ID::B, RTLIL::SigSpec()); - for (int i = 1; i <= cell->getPort(ID::S).size(); i++) - for (auto &in_tuple : consolidated_in_tuples) { - RTLIL::SigSpec new_b = cell->getPort(ID::B); - new_b.append(in_tuple.at(i)); - cell->setPort(ID::B, new_b); + if (swizzle.empty()) { + module->remove(cell); + } else { + SigSpec new_sig_a; + for (int i = 0; i < GetSize(sig_a); i += width) + for (int j: swizzle) + new_sig_a.append(sig_a[i+j]); + cell->setPort(ID::A, new_sig_a); + + if (cell->type != ID($bmux)) { + SigSpec new_sig_b; + for (int i = 0; i < GetSize(sig_b); i += width) + for (int j: swizzle) + new_sig_b.append(sig_b[i+j]); + cell->setPort(ID::B, new_sig_b); } - cell->parameters[ID::WIDTH] = RTLIL::Const(new_sig_y.size()); - cell->setPort(ID::Y, new_sig_y); + SigSpec new_sig_y; + for (int j: swizzle) + new_sig_y.append(sig_y[j]); + cell->setPort(ID::Y, new_sig_y); + + cell->parameters[ID::WIDTH] = RTLIL::Const(GetSize(swizzle)); + + if (cell->type != ID($bmux)) { + log(" New ports: A=%s, B=%s, Y=%s\n", log_signal(cell->getPort(ID::A)), + log_signal(cell->getPort(ID::B)), log_signal(cell->getPort(ID::Y))); + } else { + log(" New ports: A=%s, Y=%s\n", log_signal(cell->getPort(ID::A)), + log_signal(cell->getPort(ID::Y))); + } + } - log(" New ports: A=%s, B=%s, Y=%s\n", log_signal(cell->getPort(ID::A)), - log_signal(cell->getPort(ID::B)), log_signal(cell->getPort(ID::Y))); log(" New connections: %s = %s\n", log_signal(old_sig_conn.first), log_signal(old_sig_conn.second)); + module->connect(old_sig_conn); + + did_something = true; + total_count++; + } + return swizzle.empty(); + } + + bool opt_demux_bits(RTLIL::Cell *cell) { + SigSpec sig_a = assign_map(cell->getPort(ID::A)); + SigSpec sig_y = assign_map(cell->getPort(ID::Y)); + int width = GetSize(sig_a); + + RTLIL::SigSig old_sig_conn; + + dict<SigBit, int> handled_bits; + std::vector<int> swizzle; + + for (int i = 0; i < width; i++) + { + if (sig_a[i] == State::S0) + { + for (int j = i; j < GetSize(sig_y); j += width) + { + old_sig_conn.first.append(sig_y[j]); + old_sig_conn.second.append(State::S0); + } + continue; + } + auto it = handled_bits.find(sig_a[i]); + if (it == handled_bits.end()) + { + handled_bits[sig_a[i]] = i; + swizzle.push_back(i); + } + else + { + for (int j = 0; j < GetSize(sig_y); j += width) + { + old_sig_conn.first.append(sig_y[i+j]); + old_sig_conn.second.append(sig_y[it->second+j]); + } + } + } + + if (GetSize(swizzle) != width) + { + log(" Consolidated identical input bits for %s cell %s:\n", cell->type.c_str(), cell->name.c_str()); + log(" Old ports: A=%s, Y=%s\n", log_signal(cell->getPort(ID::A)), + log_signal(cell->getPort(ID::Y))); + + if (swizzle.empty()) { + module->remove(cell); + } else { + SigSpec new_sig_a; + for (int j: swizzle) + new_sig_a.append(sig_a[j]); + cell->setPort(ID::A, new_sig_a); + + SigSpec new_sig_y; + for (int i = 0; i < GetSize(sig_y); i += width) + for (int j: swizzle) + new_sig_y.append(sig_y[i+j]); + cell->setPort(ID::Y, new_sig_y); + + cell->parameters[ID::WIDTH] = RTLIL::Const(GetSize(swizzle)); + + log(" New ports: A=%s, Y=%s\n", log_signal(cell->getPort(ID::A)), + log_signal(cell->getPort(ID::Y))); + } + + log(" New connections: %s = %s\n", log_signal(old_sig_conn.first), log_signal(old_sig_conn.second)); module->connect(old_sig_conn); did_something = true; total_count++; } + return swizzle.empty(); } OptReduceWorker(RTLIL::Design *design, RTLIL::Module *module, bool do_fine) : @@ -254,9 +520,9 @@ struct OptReduceWorker SigPool mem_wren_sigs; for (auto &cell_it : module->cells_) { RTLIL::Cell *cell = cell_it.second; - if (cell->type == ID($mem)) + if (cell->type.in(ID($mem), ID($mem_v2))) mem_wren_sigs.add(assign_map(cell->getPort(ID::WR_EN))); - if (cell->type == ID($memwr)) + if (cell->type.in(ID($memwr), ID($memwr_v2))) mem_wren_sigs.add(assign_map(cell->getPort(ID::EN))); } for (auto &cell_it : module->cells_) { @@ -309,20 +575,31 @@ struct OptReduceWorker // merge identical inputs on $mux and $pmux cells - std::vector<RTLIL::Cell*> cells; - - for (auto &it : module->cells_) - if ((it.second->type == ID($mux) || it.second->type == ID($pmux)) && design->selected(module, it.second)) - cells.push_back(it.second); - - for (auto cell : cells) + for (auto cell : module->selected_cells()) { + if (!cell->type.in(ID($mux), ID($pmux), ID($bmux), ID($demux))) + continue; + // this optimization is to aggressive for most coarse-grain applications. // but we always want it for multiplexers driving write enable ports. - if (do_fine || mem_wren_sigs.check_any(assign_map(cell->getPort(ID::Y)))) - opt_mux_bits(cell); + if (do_fine || mem_wren_sigs.check_any(assign_map(cell->getPort(ID::Y)))) { + if (cell->type == ID($demux)) { + if (opt_demux_bits(cell)) + continue; + } else { + if (opt_mux_bits(cell)) + continue; + } + } + + if (cell->type.in(ID($mux), ID($pmux))) + opt_pmux(cell); + + if (cell->type == ID($bmux)) + opt_bmux(cell); - opt_mux(cell); + if (cell->type == ID($demux)) + opt_demux(cell); } } diff --git a/passes/opt/opt_share.cc b/passes/opt/opt_share.cc index 62a478673..ba85df975 100644 --- a/passes/opt/opt_share.cc +++ b/passes/opt/opt_share.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * 2019 Bogdan Vukobratovic <bogdan.vukobratovic@gmail.com> * * Permission to use, copy, modify, and/or distribute this software for any diff --git a/passes/opt/pmux2shiftx.cc b/passes/opt/pmux2shiftx.cc index f3b1fd377..90ddf8dd7 100644 --- a/passes/opt/pmux2shiftx.cc +++ b/passes/opt/pmux2shiftx.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/opt/rmports.cc b/passes/opt/rmports.cc index 99a2a61c8..9fa9f5c2d 100644 --- a/passes/opt/rmports.cc +++ b/passes/opt/rmports.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/opt/share.cc b/passes/opt/share.cc index f7848e01d..abef71937 100644 --- a/passes/opt/share.cc +++ b/passes/opt/share.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -18,7 +18,7 @@ */ #include "kernel/yosys.h" -#include "kernel/satgen.h" +#include "kernel/qcsat.h" #include "kernel/sigtools.h" #include "kernel/modtools.h" #include "kernel/utils.h" @@ -58,8 +58,6 @@ struct ShareWorker std::map<RTLIL::Cell*, std::set<RTLIL::Cell*, cell_ptr_cmp>, cell_ptr_cmp> topo_cell_drivers; std::map<RTLIL::SigBit, std::set<RTLIL::Cell*, cell_ptr_cmp>> topo_bit_drivers; - std::vector<std::pair<RTLIL::SigBit, RTLIL::SigBit>> exclusive_ctrls; - // ------------------------------------------------------------------------------ // Find terminal bits -- i.e. bits that do not (exclusively) feed into a mux tree @@ -368,7 +366,7 @@ struct ShareWorker continue; } - if (cell->type == ID($memrd)) { + if (cell->type.in(ID($memrd), ID($memrd_v2))) { if (cell->parameters.at(ID::CLK_ENABLE).as_bool()) continue; if (config.opt_aggressive || !modwalker.sigmap(cell->getPort(ID::ADDR)).is_fully_const()) @@ -401,11 +399,14 @@ struct ShareWorker if (c1->type != c2->type) return false; - if (c1->type == ID($memrd)) + if (c1->type.in(ID($memrd), ID($memrd_v2))) { if (c1->parameters.at(ID::MEMID).decode_string() != c2->parameters.at(ID::MEMID).decode_string()) return false; + if (c1->parameters.at(ID::WIDTH) != c2->parameters.at(ID::WIDTH)) + return false; + return true; } @@ -705,7 +706,7 @@ struct ShareWorker return supercell; } - if (c1->type == ID($memrd)) + if (c1->type.in(ID($memrd), ID($memrd_v2))) { RTLIL::Cell *supercell = module->addCell(NEW_ID, c1); RTLIL::SigSpec addr1 = c1->getPort(ID::ADDR); @@ -1156,7 +1157,6 @@ struct ShareWorker recursion_state.clear(); topo_cell_drivers.clear(); topo_bit_drivers.clear(); - exclusive_ctrls.clear(); terminal_bits.clear(); shareable_cells.clear(); forbidden_controls_cache.clear(); @@ -1171,13 +1171,6 @@ struct ShareWorker log("Found %d cells in module %s that may be considered for resource sharing.\n", GetSize(shareable_cells), log_id(module)); - for (auto cell : module->cells()) - if (cell->type == ID($pmux)) - for (auto bit : cell->getPort(ID::S)) - for (auto other_bit : cell->getPort(ID::S)) - if (bit < other_bit) - exclusive_ctrls.push_back(std::pair<RTLIL::SigBit, RTLIL::SigBit>(bit, other_bit)); - while (!shareable_cells.empty() && config.limit != 0) { RTLIL::Cell *cell = *shareable_cells.begin(); @@ -1256,8 +1249,11 @@ struct ShareWorker optimize_activation_patterns(filtered_cell_activation_patterns); optimize_activation_patterns(filtered_other_cell_activation_patterns); - ezSatPtr ez; - SatGen satgen(ez.get(), &modwalker.sigmap); + QuickConeSat qcsat(modwalker); + if (config.opt_fast) { + qcsat.max_cell_outs = 3; + qcsat.max_cell_count = 100; + } pool<RTLIL::Cell*> sat_cells; std::set<RTLIL::SigBit> bits_queue; @@ -1267,77 +1263,45 @@ struct ShareWorker for (auto &p : filtered_cell_activation_patterns) { log(" Activation pattern for cell %s: %s = %s\n", log_id(cell), log_signal(p.first), log_signal(p.second)); - cell_active.push_back(ez->vec_eq(satgen.importSigSpec(p.first), satgen.importSigSpec(p.second))); + cell_active.push_back(qcsat.ez->vec_eq(qcsat.importSig(p.first), qcsat.importSig(p.second))); all_ctrl_signals.append(p.first); } for (auto &p : filtered_other_cell_activation_patterns) { log(" Activation pattern for cell %s: %s = %s\n", log_id(other_cell), log_signal(p.first), log_signal(p.second)); - other_cell_active.push_back(ez->vec_eq(satgen.importSigSpec(p.first), satgen.importSigSpec(p.second))); + other_cell_active.push_back(qcsat.ez->vec_eq(qcsat.importSig(p.first), qcsat.importSig(p.second))); all_ctrl_signals.append(p.first); } - for (auto &bit : cell_activation_signals.to_sigbit_vector()) - bits_queue.insert(bit); - - for (auto &bit : other_cell_activation_signals.to_sigbit_vector()) - bits_queue.insert(bit); - - while (!bits_queue.empty()) - { - pool<ModWalker::PortBit> portbits; - modwalker.get_drivers(portbits, bits_queue); - bits_queue.clear(); - - for (auto &pbit : portbits) - if (sat_cells.count(pbit.cell) == 0 && cone_ct.cell_known(pbit.cell->type)) { - if (config.opt_fast && modwalker.cell_outputs[pbit.cell].size() >= 4) - continue; - // log(" Adding cell %s (%s) to SAT problem.\n", log_id(pbit.cell), log_id(pbit.cell->type)); - bits_queue.insert(modwalker.cell_inputs[pbit.cell].begin(), modwalker.cell_inputs[pbit.cell].end()); - satgen.importCell(pbit.cell); - sat_cells.insert(pbit.cell); - } - - if (config.opt_fast && sat_cells.size() > 100) - break; - } - - for (auto it : exclusive_ctrls) - if (satgen.importedSigBit(it.first) && satgen.importedSigBit(it.second)) { - log(" Adding exclusive control bits: %s vs. %s\n", log_signal(it.first), log_signal(it.second)); - int sub1 = satgen.importSigBit(it.first); - int sub2 = satgen.importSigBit(it.second); - ez->assume(ez->NOT(ez->AND(sub1, sub2))); - } + qcsat.prepare(); - if (!ez->solve(ez->expression(ez->OpOr, cell_active))) { + int sub1 = qcsat.ez->expression(qcsat.ez->OpOr, cell_active); + if (!qcsat.ez->solve(sub1)) { log(" According to the SAT solver the cell %s is never active. Sharing is pointless, we simply remove it.\n", log_id(cell)); cells_to_remove.insert(cell); break; } - if (!ez->solve(ez->expression(ez->OpOr, other_cell_active))) { + int sub2 = qcsat.ez->expression(qcsat.ez->OpOr, other_cell_active); + if (!qcsat.ez->solve(sub2)) { log(" According to the SAT solver the cell %s is never active. Sharing is pointless, we simply remove it.\n", log_id(other_cell)); cells_to_remove.insert(other_cell); shareable_cells.erase(other_cell); continue; } - ez->non_incremental(); + qcsat.ez->non_incremental(); all_ctrl_signals.sort_and_unify(); - std::vector<int> sat_model = satgen.importSigSpec(all_ctrl_signals); + std::vector<int> sat_model = qcsat.importSig(all_ctrl_signals); std::vector<bool> sat_model_values; - int sub1 = ez->expression(ez->OpOr, cell_active); - int sub2 = ez->expression(ez->OpOr, other_cell_active); - ez->assume(ez->AND(sub1, sub2)); + qcsat.ez->assume(qcsat.ez->AND(sub1, sub2)); log(" Size of SAT problem: %d cells, %d variables, %d clauses\n", - GetSize(sat_cells), ez->numCnfVariables(), ez->numCnfClauses()); + GetSize(sat_cells), qcsat.ez->numCnfVariables(), qcsat.ez->numCnfClauses()); - if (ez->solve(sat_model, sat_model_values)) { + if (qcsat.ez->solve(sat_model, sat_model_values)) { log(" According to the SAT solver this pair of cells can not be shared.\n"); log(" Model from SAT solver: %s = %d'", log_signal(all_ctrl_signals), GetSize(sat_model_values)); for (int i = GetSize(sat_model_values)-1; i >= 0; i--) diff --git a/passes/opt/wreduce.cc b/passes/opt/wreduce.cc index a216f36d4..aaad28ef0 100644 --- a/passes/opt/wreduce.cc +++ b/passes/opt/wreduce.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -558,7 +558,7 @@ struct WreducePass : public Pass { } } - if (!opt_memx && c->type.in(ID($memrd), ID($memwr), ID($meminit))) { + if (!opt_memx && c->type.in(ID($memrd), ID($memrd_v2), ID($memwr), ID($memwr_v2), ID($meminit), ID($meminit_v2))) { IdString memid = c->getParam(ID::MEMID).decode_string(); RTLIL::Memory *mem = module->memories.at(memid); if (mem->start_offset >= 0) { diff --git a/passes/pmgen/Makefile.inc b/passes/pmgen/Makefile.inc index c6bbc386a..a7ef64282 100644 --- a/passes/pmgen/Makefile.inc +++ b/passes/pmgen/Makefile.inc @@ -4,24 +4,31 @@ # -------------------------------------- OBJS += passes/pmgen/test_pmgen.o +GENFILES += passes/pmgen/test_pmgen_pm.h passes/pmgen/test_pmgen.o: passes/pmgen/test_pmgen_pm.h passes/pmgen/ice40_dsp_pm.h passes/pmgen/peepopt_pm.h passes/pmgen/xilinx_srl_pm.h $(eval $(call add_extra_objs,passes/pmgen/test_pmgen_pm.h)) # -------------------------------------- OBJS += passes/pmgen/ice40_dsp.o +GENFILES += passes/pmgen/ice40_dsp_pm.h passes/pmgen/ice40_dsp.o: passes/pmgen/ice40_dsp_pm.h $(eval $(call add_extra_objs,passes/pmgen/ice40_dsp_pm.h)) # -------------------------------------- OBJS += passes/pmgen/ice40_wrapcarry.o +GENFILES += passes/pmgen/ice40_wrapcarry_pm.h passes/pmgen/ice40_wrapcarry.o: passes/pmgen/ice40_wrapcarry_pm.h $(eval $(call add_extra_objs,passes/pmgen/ice40_wrapcarry_pm.h)) # -------------------------------------- OBJS += passes/pmgen/xilinx_dsp.o +GENFILES += passes/pmgen/xilinx_dsp_pm.h +GENFILES += passes/pmgen/xilinx_dsp48a_pm.h +GENFILES += passes/pmgen/xilinx_dsp_CREG_pm.h +GENFILES += passes/pmgen/xilinx_dsp_cascade_pm.h passes/pmgen/xilinx_dsp.o: passes/pmgen/xilinx_dsp_pm.h passes/pmgen/xilinx_dsp48a_pm.h passes/pmgen/xilinx_dsp_CREG_pm.h passes/pmgen/xilinx_dsp_cascade_pm.h $(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp_pm.h)) $(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp48a_pm.h)) @@ -31,6 +38,7 @@ $(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp_cascade_pm.h)) # -------------------------------------- OBJS += passes/pmgen/peepopt.o +GENFILES += passes/pmgen/peepopt_pm.h passes/pmgen/peepopt.o: passes/pmgen/peepopt_pm.h $(eval $(call add_extra_objs,passes/pmgen/peepopt_pm.h)) @@ -43,5 +51,6 @@ passes/pmgen/peepopt_pm.h: passes/pmgen/pmgen.py $(PEEPOPT_PATTERN) # -------------------------------------- OBJS += passes/pmgen/xilinx_srl.o +GENFILES += passes/pmgen/xilinx_srl_pm.h passes/pmgen/xilinx_srl.o: passes/pmgen/xilinx_srl_pm.h $(eval $(call add_extra_objs,passes/pmgen/xilinx_srl_pm.h)) diff --git a/passes/pmgen/generate.h b/passes/pmgen/generate.h index 354583de5..85e208774 100644 --- a/passes/pmgen/generate.h +++ b/passes/pmgen/generate.h @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/pmgen/ice40_dsp.cc b/passes/pmgen/ice40_dsp.cc index c46f5d58f..24134b1fb 100644 --- a/passes/pmgen/ice40_dsp.cc +++ b/passes/pmgen/ice40_dsp.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/pmgen/ice40_dsp.pmg b/passes/pmgen/ice40_dsp.pmg index 7a01cbd51..4de479122 100644 --- a/passes/pmgen/ice40_dsp.pmg +++ b/passes/pmgen/ice40_dsp.pmg @@ -28,9 +28,8 @@ code sigA sigB sigH for (i = GetSize(sig)-1; i > 0; i--) if (sig[i] != sig[i-1]) break; - // Do not remove non-const sign bit - if (sig[i].wire) - ++i; + // Do not remove sign bit + ++i; return sig.extract(0, i); }; sigA = unextend(port(mul, \A)); diff --git a/passes/pmgen/ice40_wrapcarry.cc b/passes/pmgen/ice40_wrapcarry.cc index e234906ad..c936d02dc 100644 --- a/passes/pmgen/ice40_wrapcarry.cc +++ b/passes/pmgen/ice40_wrapcarry.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/pmgen/peepopt.cc b/passes/pmgen/peepopt.cc index a9c62fcf6..9a497c914 100644 --- a/passes/pmgen/peepopt.cc +++ b/passes/pmgen/peepopt.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/pmgen/test_pmgen.cc b/passes/pmgen/test_pmgen.cc index 7b2938ddf..beff59778 100644 --- a/passes/pmgen/test_pmgen.cc +++ b/passes/pmgen/test_pmgen.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/pmgen/xilinx_dsp.cc b/passes/pmgen/xilinx_dsp.cc index cf7703d36..72b4522d8 100644 --- a/passes/pmgen/xilinx_dsp.cc +++ b/passes/pmgen/xilinx_dsp.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * 2019 Eddie Hung <eddie@fpgeh.com> * * Permission to use, copy, modify, and/or distribute this software for any diff --git a/passes/pmgen/xilinx_srl.cc b/passes/pmgen/xilinx_srl.cc index 1410850c7..a66a06586 100644 --- a/passes/pmgen/xilinx_srl.cc +++ b/passes/pmgen/xilinx_srl.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * (C) 2019 Eddie Hung <eddie@fpgeh.com> * * Permission to use, copy, modify, and/or distribute this software for any diff --git a/passes/proc/Makefile.inc b/passes/proc/Makefile.inc index 4b56979f8..50244bf33 100644 --- a/passes/proc/Makefile.inc +++ b/passes/proc/Makefile.inc @@ -8,3 +8,4 @@ OBJS += passes/proc/proc_arst.o OBJS += passes/proc/proc_mux.o OBJS += passes/proc/proc_dlatch.o OBJS += passes/proc/proc_dff.o +OBJS += passes/proc/proc_memwr.o diff --git a/passes/proc/proc.cc b/passes/proc/proc.cc index 09cf0af82..d7aac57b6 100644 --- a/passes/proc/proc.cc +++ b/passes/proc/proc.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -43,7 +43,9 @@ struct ProcPass : public Pass { log(" proc_mux\n"); log(" proc_dlatch\n"); log(" proc_dff\n"); + log(" proc_memwr\n"); log(" proc_clean\n"); + log(" opt_expr -keepdc\n"); log("\n"); log("This replaces the processes in the design with multiplexers,\n"); log("flip-flops and latches.\n"); @@ -60,12 +62,16 @@ struct ProcPass : public Pass { log(" This option is passed through to proc_mux. proc_rmdead is not\n"); log(" executed in -ifx mode.\n"); log("\n"); + log(" -noopt\n"); + log(" Will omit the opt_expr pass.\n"); + log("\n"); } void execute(std::vector<std::string> args, RTLIL::Design *design) override { std::string global_arst; bool ifxmode = false; bool nomux = false; + bool noopt = false; log_header(design, "Executing PROC pass (convert processes to netlists).\n"); log_push(); @@ -85,6 +91,10 @@ struct ProcPass : public Pass { ifxmode = true; continue; } + if (args[argidx] == "-noopt") { + noopt = true; + continue; + } break; } extra_args(args, argidx, design); @@ -102,7 +112,10 @@ struct ProcPass : public Pass { Pass::call(design, ifxmode ? "proc_mux -ifx" : "proc_mux"); Pass::call(design, "proc_dlatch"); Pass::call(design, "proc_dff"); + Pass::call(design, "proc_memwr"); Pass::call(design, "proc_clean"); + if (!noopt) + Pass::call(design, "opt_expr -keepdc"); log_pop(); } diff --git a/passes/proc/proc_arst.cc b/passes/proc/proc_arst.cc index 16db461b2..f01682957 100644 --- a/passes/proc/proc_arst.cc +++ b/passes/proc/proc_arst.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -153,51 +153,93 @@ void eliminate_const(RTLIL::Module *mod, RTLIL::CaseRule *cs, RTLIL::SigSpec con } } +RTLIL::SigSpec apply_reset(RTLIL::Module *mod, RTLIL::Process *proc, RTLIL::SyncRule *sync, SigMap &assign_map, RTLIL::SigSpec root_sig, bool polarity, RTLIL::SigSpec sig, RTLIL::SigSpec log_sig) { + RTLIL::SigSpec rspec = assign_map(sig); + RTLIL::SigSpec rval = RTLIL::SigSpec(RTLIL::State::Sm, rspec.size()); + for (int i = 0; i < GetSize(rspec); i++) + if (rspec[i].wire == NULL) + rval[i] = rspec[i]; + RTLIL::SigSpec last_rval; + for (int count = 0; rval != last_rval; count++) { + last_rval = rval; + apply_const(mod, rspec, rval, &proc->root_case, root_sig, polarity, false); + assign_map.apply(rval); + if (rval.is_fully_const()) + break; + if (count > 100) + log_error("Async reset %s yields endless loop at value %s for signal %s.\n", + log_signal(sync->signal), log_signal(rval), log_signal(log_sig)); + rspec = rval; + } + if (rval.has_marked_bits()) + log_error("Async reset %s yields non-constant value %s for signal %s.\n", + log_signal(sync->signal), log_signal(rval), log_signal(log_sig)); + return rval; +} + void proc_arst(RTLIL::Module *mod, RTLIL::Process *proc, SigMap &assign_map) { -restart_proc_arst: - if (proc->root_case.switches.size() != 1) - return; - - RTLIL::SigSpec root_sig = proc->root_case.switches[0]->signal; + std::vector<RTLIL::SyncRule *> arst_syncs; + std::vector<RTLIL::SyncRule *> edge_syncs; + std::vector<RTLIL::SyncRule *> other_syncs; for (auto &sync : proc->syncs) { - if (sync->type == RTLIL::SyncType::STp || sync->type == RTLIL::SyncType::STn) { + if (sync->type == RTLIL::SyncType::ST0 || sync->type == RTLIL::SyncType::ST1) { + arst_syncs.push_back(sync); + } else if (sync->type == RTLIL::SyncType::STp || sync->type == RTLIL::SyncType::STn) { + edge_syncs.push_back(sync); + } else { + other_syncs.push_back(sync); + } + } + + bool did_something = false; + + while (proc->root_case.switches.size() == 1) { + RTLIL::SigSpec root_sig = proc->root_case.switches[0]->signal; + + bool found = false; + for (auto it = edge_syncs.begin(); it != edge_syncs.end(); ++it) { + auto sync = *it; bool polarity = sync->type == RTLIL::SyncType::STp; if (check_signal(mod, root_sig, sync->signal, polarity)) { - if (proc->syncs.size() == 1) { - log("Found VHDL-style edge-trigger %s in `%s.%s'.\n", log_signal(sync->signal), mod->name.c_str(), proc->name.c_str()); - } else { + if (edge_syncs.size() > 1) { log("Found async reset %s in `%s.%s'.\n", log_signal(sync->signal), mod->name.c_str(), proc->name.c_str()); sync->type = sync->type == RTLIL::SyncType::STp ? RTLIL::SyncType::ST1 : RTLIL::SyncType::ST0; - } - for (auto &action : sync->actions) { - RTLIL::SigSpec rspec = assign_map(action.second); - RTLIL::SigSpec rval = RTLIL::SigSpec(RTLIL::State::Sm, rspec.size()); - for (int i = 0; i < GetSize(rspec); i++) - if (rspec[i].wire == NULL) - rval[i] = rspec[i]; - RTLIL::SigSpec last_rval; - for (int count = 0; rval != last_rval; count++) { - last_rval = rval; - apply_const(mod, rspec, rval, &proc->root_case, root_sig, polarity, false); - assign_map.apply(rval); - if (rval.is_fully_const()) - break; - if (count > 100) - log_error("Async reset %s yields endless loop at value %s for signal %s.\n", - log_signal(sync->signal), log_signal(rval), log_signal(action.first)); - rspec = rval; + arst_syncs.push_back(sync); + edge_syncs.erase(it); + for (auto &action : sync->actions) { + action.second = apply_reset(mod, proc, sync, assign_map, root_sig, polarity, action.second, action.first); + } + for (auto &memwr : sync->mem_write_actions) { + RTLIL::SigSpec en = apply_reset(mod, proc, sync, assign_map, root_sig, polarity, memwr.enable, memwr.enable); + if (!en.is_fully_zero()) { + log_error("Async reset %s causes memory write to %s.\n", + log_signal(sync->signal), log_id(memwr.memid)); + } + apply_reset(mod, proc, sync, assign_map, root_sig, polarity, memwr.address, memwr.address); + apply_reset(mod, proc, sync, assign_map, root_sig, polarity, memwr.data, memwr.data); } - if (rval.has_marked_bits()) - log_error("Async reset %s yields non-constant value %s for signal %s.\n", - log_signal(sync->signal), log_signal(rval), log_signal(action.first)); - action.second = rval; + sync->mem_write_actions.clear(); + eliminate_const(mod, &proc->root_case, root_sig, polarity); + } else { + log("Found VHDL-style edge-trigger %s in `%s.%s'.\n", log_signal(sync->signal), mod->name.c_str(), proc->name.c_str()); + eliminate_const(mod, &proc->root_case, root_sig, !polarity); } - eliminate_const(mod, &proc->root_case, root_sig, polarity); - goto restart_proc_arst; + did_something = true; + found = true; + break; } } + if (!found) + break; + } + + if (did_something) { + proc->syncs.clear(); + proc->syncs.insert(proc->syncs.end(), arst_syncs.begin(), arst_syncs.end()); + proc->syncs.insert(proc->syncs.end(), edge_syncs.begin(), edge_syncs.end()); + proc->syncs.insert(proc->syncs.end(), other_syncs.begin(), other_syncs.end()); } } diff --git a/passes/proc/proc_clean.cc b/passes/proc/proc_clean.cc index 5e78b7316..45872907b 100644 --- a/passes/proc/proc_clean.cc +++ b/passes/proc/proc_clean.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -76,22 +76,33 @@ void proc_clean_switch(RTLIL::SwitchRule *sw, RTLIL::CaseRule *parent, bool &did } else { - bool all_fully_def = true; for (auto cs : sw->cases) - { if (max_depth != 0) proc_clean_case(cs, did_something, count, max_depth-1); - int size = 0; - for (auto cmp : cs->compare) + + bool is_parallel_case = sw->get_bool_attribute(ID::parallel_case); + bool is_full_case = sw->get_bool_attribute(ID::full_case); + + // Empty case removal. The rules are: + // + // - for full_case: only remove cases if *all* cases are empty + // - for parallel_case but not full_case: remove any empty case + // - for non-parallel and non-full case: remove the final case if it's empty + + if (is_full_case) + { + bool all_empty = true; + for (auto cs : sw->cases) + if (!cs->empty()) + all_empty = false; + if (all_empty) { - size += cmp.size(); - if (!cmp.is_fully_def()) - all_fully_def = false; + for (auto cs : sw->cases) + delete cs; + sw->cases.clear(); } - if (sw->signal.size() != size) - all_fully_def = false; } - if (all_fully_def) + else if (is_parallel_case) { for (auto cs = sw->cases.begin(); cs != sw->cases.end();) { @@ -150,7 +161,7 @@ void proc_clean(RTLIL::Module *mod, RTLIL::Process *proc, int &total_count, bool for (size_t j = 0; j < proc->syncs[i]->actions.size(); j++) if (proc->syncs[i]->actions[j].first.size() == 0) proc->syncs[i]->actions.erase(proc->syncs[i]->actions.begin() + (j--)); - if (proc->syncs[i]->actions.size() == 0) { + if (proc->syncs[i]->actions.size() == 0 && proc->syncs[i]->mem_write_actions.size() == 0) { delete proc->syncs[i]; proc->syncs.erase(proc->syncs.begin() + (i--)); } @@ -198,7 +209,7 @@ struct ProcCleanPass : public Pass { extra_args(args, argidx, design); for (auto mod : design->modules()) { - std::vector<RTLIL::IdString> delme; + std::vector<RTLIL::Process *> delme; if (!design->selected(mod)) continue; for (auto &proc_it : mod->processes) { @@ -209,12 +220,11 @@ struct ProcCleanPass : public Pass { proc_it.second->root_case.actions.size() == 0) { if (!quiet) log("Removing empty process `%s.%s'.\n", log_id(mod), proc_it.second->name.c_str()); - delme.push_back(proc_it.first); + delme.push_back(proc_it.second); } } - for (auto &id : delme) { - delete mod->processes[id]; - mod->processes.erase(id); + for (auto proc : delme) { + mod->remove(proc); } } diff --git a/passes/proc/proc_dff.cc b/passes/proc/proc_dff.cc index e320a72a6..234671df5 100644 --- a/passes/proc/proc_dff.cc +++ b/passes/proc/proc_dff.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -143,48 +143,23 @@ void gen_dffsr_complex(RTLIL::Module *mod, RTLIL::SigSpec sig_d, RTLIL::SigSpec cell->type.c_str(), cell->name.c_str(), clk_polarity ? "positive" : "negative"); } -void gen_dffsr(RTLIL::Module *mod, RTLIL::SigSpec sig_in, RTLIL::SigSpec sig_set, RTLIL::SigSpec sig_out, +void gen_aldff(RTLIL::Module *mod, RTLIL::SigSpec sig_in, RTLIL::SigSpec sig_set, RTLIL::SigSpec sig_out, bool clk_polarity, bool set_polarity, RTLIL::SigSpec clk, RTLIL::SigSpec set, RTLIL::Process *proc) { std::stringstream sstr; sstr << "$procdff$" << (autoidx++); - RTLIL::SigSpec sig_set_inv = mod->addWire(NEW_ID, sig_in.size()); - RTLIL::SigSpec sig_sr_set = mod->addWire(NEW_ID, sig_in.size()); - RTLIL::SigSpec sig_sr_clr = mod->addWire(NEW_ID, sig_in.size()); - - RTLIL::Cell *inv_set = mod->addCell(NEW_ID, ID($not)); - inv_set->parameters[ID::A_SIGNED] = RTLIL::Const(0); - inv_set->parameters[ID::A_WIDTH] = RTLIL::Const(sig_in.size()); - inv_set->parameters[ID::Y_WIDTH] = RTLIL::Const(sig_in.size()); - inv_set->setPort(ID::A, sig_set); - inv_set->setPort(ID::Y, sig_set_inv); - - RTLIL::Cell *mux_sr_set = mod->addCell(NEW_ID, ID($mux)); - mux_sr_set->parameters[ID::WIDTH] = RTLIL::Const(sig_in.size()); - mux_sr_set->setPort(set_polarity ? ID::A : ID::B, RTLIL::Const(0, sig_in.size())); - mux_sr_set->setPort(set_polarity ? ID::B : ID::A, sig_set); - mux_sr_set->setPort(ID::Y, sig_sr_set); - mux_sr_set->setPort(ID::S, set); - - RTLIL::Cell *mux_sr_clr = mod->addCell(NEW_ID, ID($mux)); - mux_sr_clr->parameters[ID::WIDTH] = RTLIL::Const(sig_in.size()); - mux_sr_clr->setPort(set_polarity ? ID::A : ID::B, RTLIL::Const(0, sig_in.size())); - mux_sr_clr->setPort(set_polarity ? ID::B : ID::A, sig_set_inv); - mux_sr_clr->setPort(ID::Y, sig_sr_clr); - mux_sr_clr->setPort(ID::S, set); - - RTLIL::Cell *cell = mod->addCell(sstr.str(), ID($dffsr)); + RTLIL::Cell *cell = mod->addCell(sstr.str(), ID($aldff)); cell->attributes = proc->attributes; + cell->parameters[ID::WIDTH] = RTLIL::Const(sig_in.size()); + cell->parameters[ID::ALOAD_POLARITY] = RTLIL::Const(set_polarity, 1); cell->parameters[ID::CLK_POLARITY] = RTLIL::Const(clk_polarity, 1); - cell->parameters[ID::SET_POLARITY] = RTLIL::Const(true, 1); - cell->parameters[ID::CLR_POLARITY] = RTLIL::Const(true, 1); cell->setPort(ID::D, sig_in); cell->setPort(ID::Q, sig_out); + cell->setPort(ID::AD, sig_set); cell->setPort(ID::CLK, clk); - cell->setPort(ID::SET, sig_sr_set); - cell->setPort(ID::CLR, sig_sr_clr); + cell->setPort(ID::ALOAD, set); log(" created %s cell `%s' with %s edge clock and %s level non-const reset.\n", cell->type.c_str(), cell->name.c_str(), clk_polarity ? "positive" : "negative", set_polarity ? "positive" : "negative"); @@ -328,6 +303,10 @@ void proc_dff(RTLIL::Module *mod, RTLIL::Process *proc, ConstEval &ce) ce.assign_map.apply(sig); if (rstval == sig) { + if (sync_level->type == RTLIL::SyncType::ST1) + insig = mod->Mux(NEW_ID, insig, sig, sync_level->signal); + else + insig = mod->Mux(NEW_ID, sig, insig, sync_level->signal); rstval = RTLIL::SigSpec(RTLIL::State::Sz, sig.size()); sync_level = NULL; } @@ -351,7 +330,7 @@ void proc_dff(RTLIL::Module *mod, RTLIL::Process *proc, ConstEval &ce) else if (!rstval.is_fully_const() && !ce.eval(rstval)) { log_warning("Async reset value `%s' is not constant!\n", log_signal(rstval)); - gen_dffsr(mod, insig, rstval, sig_q, + gen_aldff(mod, insig, rstval, sig_q, sync_edge->type == RTLIL::SyncType::STp, sync_level && sync_level->type == RTLIL::SyncType::ST1, sync_edge->signal, sync_level->signal, proc); diff --git a/passes/proc/proc_dlatch.cc b/passes/proc/proc_dlatch.cc index 7b8c05b21..8340e1431 100644 --- a/passes/proc/proc_dlatch.cc +++ b/passes/proc/proc_dlatch.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -342,7 +342,6 @@ struct proc_dlatch_db_t void proc_dlatch(proc_dlatch_db_t &db, RTLIL::Process *proc) { - std::vector<RTLIL::SyncRule*> new_syncs; RTLIL::SigSig latches_bits, nolatches_bits; dict<SigBit, SigBit> latches_out_in; dict<SigBit, int> latches_hold; @@ -351,7 +350,6 @@ void proc_dlatch(proc_dlatch_db_t &db, RTLIL::Process *proc) for (auto sr : proc->syncs) { if (sr->type != RTLIL::SyncType::STa) { - new_syncs.push_back(sr); continue; } @@ -373,8 +371,7 @@ void proc_dlatch(proc_dlatch_db_t &db, RTLIL::Process *proc) for (int i = 0; i < GetSize(ss.first); i++) latches_out_in[ss.first[i]] = ss.second[i]; } - - delete sr; + sr->actions.clear(); } latches_out_in.sort(); @@ -441,8 +438,6 @@ void proc_dlatch(proc_dlatch_db_t &db, RTLIL::Process *proc) offset += width; } - - new_syncs.swap(proc->syncs); } struct ProcDlatchPass : public Pass { diff --git a/passes/proc/proc_init.cc b/passes/proc/proc_init.cc index eb323038d..4da20c395 100644 --- a/passes/proc/proc_init.cc +++ b/passes/proc/proc_init.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -28,12 +28,9 @@ PRIVATE_NAMESPACE_BEGIN void proc_init(RTLIL::Module *mod, SigMap &sigmap, RTLIL::Process *proc) { - bool found_init = false; - for (auto &sync : proc->syncs) if (sync->type == RTLIL::SyncType::STi) { - found_init = true; log("Found init rule in `%s.%s'.\n", mod->name.c_str(), proc->name.c_str()); for (auto &action : sync->actions) @@ -71,17 +68,8 @@ void proc_init(RTLIL::Module *mod, SigMap &sigmap, RTLIL::Process *proc) offset += lhs_c.width; } } + sync->actions.clear(); } - - if (found_init) { - std::vector<RTLIL::SyncRule*> new_syncs; - for (auto &sync : proc->syncs) - if (sync->type == RTLIL::SyncType::STi) - delete sync; - else - new_syncs.push_back(sync); - proc->syncs.swap(new_syncs); - } } struct ProcInitPass : public Pass { diff --git a/passes/proc/proc_memwr.cc b/passes/proc/proc_memwr.cc new file mode 100644 index 000000000..cf10bd4b2 --- /dev/null +++ b/passes/proc/proc_memwr.cc @@ -0,0 +1,121 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2021 Marcelina KoÅ›cielnicka <mwk@0x04.net> + * + * 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/sigtools.h" +#include "kernel/ffinit.h" +#include "kernel/consteval.h" +#include "kernel/log.h" +#include <sstream> +#include <stdlib.h> +#include <stdio.h> + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +void proc_memwr(RTLIL::Module *mod, RTLIL::Process *proc, dict<IdString, int> &next_port_id) +{ + for (auto sr : proc->syncs) + { + std::vector<int> prev_port_ids; + for (auto memwr : sr->mem_write_actions) { + int port_id = next_port_id[memwr.memid]++; + Const priority_mask(State::S0, port_id); + for (int i = 0; i < GetSize(prev_port_ids); i++) + if (memwr.priority_mask[i] == State::S1) + priority_mask[prev_port_ids[i]] = State::S1; + prev_port_ids.push_back(port_id); + + RTLIL::Cell *cell = mod->addCell(NEW_ID, ID($memwr_v2)); + cell->attributes = memwr.attributes; + cell->setParam(ID::MEMID, Const(memwr.memid.str())); + cell->setParam(ID::ABITS, GetSize(memwr.address)); + cell->setParam(ID::WIDTH, GetSize(memwr.data)); + cell->setParam(ID::PORTID, port_id); + cell->setParam(ID::PRIORITY_MASK, priority_mask); + cell->setPort(ID::ADDR, memwr.address); + cell->setPort(ID::DATA, memwr.data); + SigSpec enable = memwr.enable; + for (auto sr2 : proc->syncs) { + if (sr2->type == RTLIL::SyncType::ST0) { + log_assert(sr2->mem_write_actions.empty()); + enable = mod->Mux(NEW_ID, Const(State::S0, GetSize(enable)), enable, sr2->signal); + } else if (sr2->type == RTLIL::SyncType::ST1) { + log_assert(sr2->mem_write_actions.empty()); + enable = mod->Mux(NEW_ID, enable, Const(State::S0, GetSize(enable)), sr2->signal); + } + } + cell->setPort(ID::EN, enable); + if (sr->type == RTLIL::SyncType::STa) { + cell->setPort(ID::CLK, State::Sx); + cell->setParam(ID::CLK_ENABLE, State::S0); + cell->setParam(ID::CLK_POLARITY, State::Sx); + } else if (sr->type == RTLIL::SyncType::STp) { + cell->setPort(ID::CLK, sr->signal); + cell->setParam(ID::CLK_ENABLE, State::S1); + cell->setParam(ID::CLK_POLARITY, State::S1); + } else if (sr->type == RTLIL::SyncType::STn) { + cell->setPort(ID::CLK, sr->signal); + cell->setParam(ID::CLK_ENABLE, State::S1); + cell->setParam(ID::CLK_POLARITY, State::S0); + } else { + log_error("process memory write with unsupported sync type in %s.%s", log_id(mod), log_id(proc)); + } + } + sr->mem_write_actions.clear(); + } +} + +struct ProcMemWrPass : public Pass { + ProcMemWrPass() : Pass("proc_memwr", "extract memory writes from processes") { } + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" proc_memwr [selection]\n"); + log("\n"); + log("This pass converts memory writes in processes into $memwr cells.\n"); + log("\n"); + } + void execute(std::vector<std::string> args, RTLIL::Design *design) override + { + log_header(design, "Executing PROC_MEMWR pass (convert process memory writes to cells).\n"); + + extra_args(args, 1, design); + + for (auto module : design->selected_modules()) { + dict<IdString, int> next_port_id; + for (auto cell : module->cells()) { + if (cell->type.in(ID($memwr), ID($memwr_v2))) { + bool is_compat = cell->type == ID($memwr); + IdString memid = cell->parameters.at(ID::MEMID).decode_string(); + int port_id = cell->parameters.at(is_compat ? ID::PRIORITY : ID::PORTID).as_int(); + if (port_id >= next_port_id[memid]) + next_port_id[memid] = port_id + 1; + } + } + for (auto &proc_it : module->processes) + if (design->selected(module, proc_it.second)) + proc_memwr(module, proc_it.second, next_port_id); + } + } +} ProcMemWrPass; + +PRIVATE_NAMESPACE_END + diff --git a/passes/proc/proc_mux.cc b/passes/proc/proc_mux.cc index d20f34534..b209057fe 100644 --- a/passes/proc/proc_mux.cc +++ b/passes/proc/proc_mux.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/proc/proc_prune.cc b/passes/proc/proc_prune.cc index bd122b91f..9f1080ef6 100644 --- a/passes/proc/proc_prune.cc +++ b/passes/proc/proc_prune.cc @@ -67,51 +67,36 @@ struct PruneWorker } for (auto it = cs->actions.rbegin(); it != cs->actions.rend(); ) { RTLIL::SigSpec lhs = sigmap(it->first); - bool redundant = true; - for (auto &bit : lhs) { + RTLIL::SigSpec rhs = sigmap(it->second); + SigSpec new_lhs, new_rhs; + SigSpec conn_lhs, conn_rhs; + for (int i = 0; i < GetSize(lhs); i++) { + SigBit bit = lhs[i]; if (bit.wire && !assigned[bit]) { - redundant = false; - break; - } - } - bool remove = false; - if (redundant) { - removed_count++; - remove = true; - } else { - if (root) { - bool promotable = true; - for (auto &bit : lhs) { - if (bit.wire && affected[bit] && !assigned[bit]) { - promotable = false; - break; - } - } - if (promotable) { - RTLIL::SigSpec rhs = sigmap(it->second); - RTLIL::SigSig conn; - for (int i = 0; i < GetSize(lhs); i++) { - RTLIL::SigBit lhs_bit = lhs[i]; - if (lhs_bit.wire && !assigned[lhs_bit]) { - conn.first.append(lhs_bit); - conn.second.append(rhs.extract(i)); - } - } - promoted_count++; - module->connect(conn); - remove = true; + if (!affected[bit] && root) { + conn_lhs.append(bit); + conn_rhs.append(rhs[i]); + } else { + new_lhs.append(bit); + new_rhs.append(rhs[i]); } + assigned.insert(bit); + affected.insert(bit); } - for (auto &bit : lhs) - if (bit.wire) - assigned.insert(bit); - for (auto &bit : lhs) - if (bit.wire) - affected.insert(bit); } - if (remove) + if (GetSize(conn_lhs)) { + promoted_count++; + module->connect(conn_lhs, conn_rhs); + } + if (GetSize(new_lhs) == 0) { + if (GetSize(conn_lhs) == 0) + removed_count++; cs->actions.erase((it++).base() - 1); - else it++; + } else { + it->first = new_lhs; + it->second = new_rhs; + it++; + } } return assigned; } diff --git a/passes/proc/proc_rmdead.cc b/passes/proc/proc_rmdead.cc index ee91637ca..2ec11415a 100644 --- a/passes/proc/proc_rmdead.cc +++ b/passes/proc/proc_rmdead.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -28,9 +28,62 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -void proc_rmdead(RTLIL::SwitchRule *sw, int &counter, int &full_case_counter) +static bool can_use_fully_defined_pool(RTLIL::SwitchRule *sw) +{ + if (!GetSize(sw->signal)) + return false; + + for (const RTLIL::SigBit &bit : sw->signal) + if (bit.wire == NULL) + return false; + + for (const RTLIL::CaseRule *cas : sw->cases) + for (const RTLIL::SigSpec &sig : cas->compare) + if (!sig.is_fully_def()) + return false; + + return true; +} + +// This replicates the necessary parts of BitPatternPool's interface, but rather +// than storing remaining patterns, this explicitly stores which fully-defined +// constants have already been matched. +struct FullyDefinedPool +{ + FullyDefinedPool(const RTLIL::SigSpec &signal) + : max_patterns{signal.size() >= 32 ? 0ul : 1ul << signal.size()} + {} + + bool take(RTLIL::SigSpec sig) + { + if (default_reached || patterns.count(sig)) + return false; + patterns.insert(sig); + return true; + } + + void take_all() + { + default_reached = true; + } + + bool empty() + { + return default_reached || + (max_patterns && max_patterns == patterns.size()); + } + + pool<RTLIL::SigSpec> patterns; + bool default_reached = false; + size_t max_patterns; +}; + +void proc_rmdead(RTLIL::SwitchRule *sw, int &counter, int &full_case_counter); + +template <class Pool> +static void proc_rmdead_impl(RTLIL::SwitchRule *sw, int &counter, int &full_case_counter) { - BitPatternPool pool(sw->signal); + Pool pool(sw->signal); for (size_t i = 0; i < sw->cases.size(); i++) { @@ -68,6 +121,14 @@ void proc_rmdead(RTLIL::SwitchRule *sw, int &counter, int &full_case_counter) } } +void proc_rmdead(RTLIL::SwitchRule *sw, int &counter, int &full_case_counter) +{ + if (can_use_fully_defined_pool(sw)) + proc_rmdead_impl<FullyDefinedPool>(sw, counter, full_case_counter); + else + proc_rmdead_impl<BitPatternPool>(sw, counter, full_case_counter); +} + struct ProcRmdeadPass : public Pass { ProcRmdeadPass() : Pass("proc_rmdead", "eliminate dead trees in decision trees") { } void help() override diff --git a/passes/sat/assertpmux.cc b/passes/sat/assertpmux.cc index f31b78804..abdcb2240 100644 --- a/passes/sat/assertpmux.cc +++ b/passes/sat/assertpmux.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/sat/async2sync.cc b/passes/sat/async2sync.cc index 3fa5a614c..46c76eba9 100644 --- a/passes/sat/async2sync.cc +++ b/passes/sat/async2sync.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -41,8 +41,6 @@ struct Async2syncPass : public Pass { log("reset value in the next cycle regardless of the data-in value at the time of\n"); log("the clock edge.\n"); log("\n"); - log("Currently only $adff, $dffsr, and $dlatch cells are supported by this pass.\n"); - log("\n"); } void execute(std::vector<std::string> args, RTLIL::Design *design) override { @@ -74,16 +72,13 @@ struct Async2syncPass : public Pass { FfData ff(&initvals, cell); // Skip for $_FF_ and $ff cells. - if (ff.has_d && !ff.has_clk && !ff.has_en) + if (ff.has_gclk) continue; if (ff.has_clk) { - if (!ff.has_sr && !ff.has_arst) - continue; - if (ff.has_sr) { - ff.unmap_ce_srst(module); + ff.unmap_ce_srst(); log("Replacing %s.%s (%s): SET=%s, CLR=%s, D=%s, Q=%s\n", log_id(module), log_id(cell), log_id(cell->type), @@ -128,8 +123,41 @@ struct Async2syncPass : public Pass { ff.sig_d = new_d; ff.sig_q = new_q; ff.has_sr = false; + } else if (ff.has_aload) { + ff.unmap_ce_srst(); + + log("Replacing %s.%s (%s): ALOAD=%s, AD=%s, D=%s, Q=%s\n", + log_id(module), log_id(cell), log_id(cell->type), + log_signal(ff.sig_aload), log_signal(ff.sig_ad), log_signal(ff.sig_d), log_signal(ff.sig_q)); + + initvals.remove_init(ff.sig_q); + + Wire *new_d = module->addWire(NEW_ID, ff.width); + Wire *new_q = module->addWire(NEW_ID, ff.width); + + if (ff.pol_aload) { + if (!ff.is_fine) { + module->addMux(NEW_ID, new_q, ff.sig_ad, ff.sig_aload, ff.sig_q); + module->addMux(NEW_ID, ff.sig_d, ff.sig_ad, ff.sig_aload, new_d); + } else { + module->addMuxGate(NEW_ID, new_q, ff.sig_ad, ff.sig_aload, ff.sig_q); + module->addMuxGate(NEW_ID, ff.sig_d, ff.sig_ad, ff.sig_aload, new_d); + } + } else { + if (!ff.is_fine) { + module->addMux(NEW_ID, ff.sig_ad, new_q, ff.sig_aload, ff.sig_q); + module->addMux(NEW_ID, ff.sig_ad, ff.sig_d, ff.sig_aload, new_d); + } else { + module->addMuxGate(NEW_ID, ff.sig_ad, new_q, ff.sig_aload, ff.sig_q); + module->addMuxGate(NEW_ID, ff.sig_ad, ff.sig_d, ff.sig_aload, new_d); + } + } + + ff.sig_d = new_d; + ff.sig_q = new_q; + ff.has_aload = false; } else if (ff.has_arst) { - ff.unmap_srst(module); + ff.unmap_srst(); log("Replacing %s.%s (%s): ARST=%s, D=%s, Q=%s\n", log_id(module), log_id(cell), log_id(cell->type), @@ -154,9 +182,12 @@ struct Async2syncPass : public Pass { ff.sig_q = new_q; ff.has_arst = false; ff.has_srst = true; + ff.ce_over_srst = false; ff.val_srst = ff.val_arst; ff.sig_srst = ff.sig_arst; ff.pol_srst = ff.pol_arst; + } else { + continue; } } else @@ -164,25 +195,25 @@ struct Async2syncPass : public Pass { // Latch. log("Replacing %s.%s (%s): EN=%s, D=%s, Q=%s\n", log_id(module), log_id(cell), log_id(cell->type), - log_signal(ff.sig_en), log_signal(ff.sig_d), log_signal(ff.sig_q)); + log_signal(ff.sig_aload), log_signal(ff.sig_ad), log_signal(ff.sig_q)); initvals.remove_init(ff.sig_q); Wire *new_q = module->addWire(NEW_ID, ff.width); Wire *new_d; - if (ff.has_d) { + if (ff.has_aload) { new_d = module->addWire(NEW_ID, ff.width); - if (ff.pol_en) { + if (ff.pol_aload) { if (!ff.is_fine) - module->addMux(NEW_ID, new_q, ff.sig_d, ff.sig_en, new_d); + module->addMux(NEW_ID, new_q, ff.sig_ad, ff.sig_aload, new_d); else - module->addMuxGate(NEW_ID, new_q, ff.sig_d, ff.sig_en, new_d); + module->addMuxGate(NEW_ID, new_q, ff.sig_ad, ff.sig_aload, new_d); } else { if (!ff.is_fine) - module->addMux(NEW_ID, ff.sig_d, new_q, ff.sig_en, new_d); + module->addMux(NEW_ID, ff.sig_ad, new_q, ff.sig_aload, new_d); else - module->addMuxGate(NEW_ID, ff.sig_d, new_q, ff.sig_en, new_d); + module->addMuxGate(NEW_ID, ff.sig_ad, new_q, ff.sig_aload, new_d); } } else { new_d = new_q; @@ -231,15 +262,12 @@ struct Async2syncPass : public Pass { ff.sig_d = new_d; ff.sig_q = new_q; - ff.has_en = false; + ff.has_aload = false; ff.has_arst = false; ff.has_sr = false; - ff.has_d = true; + ff.has_gclk = true; } - - IdString name = cell->name; - module->remove(cell); - ff.emit(module, name); + ff.emit(); } } } diff --git a/passes/sat/clk2fflogic.cc b/passes/sat/clk2fflogic.cc index b9ba5ee3c..f37e07a89 100644 --- a/passes/sat/clk2fflogic.cc +++ b/passes/sat/clk2fflogic.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -151,18 +151,36 @@ struct Clk2fflogicPass : public Pass { if (RTLIL::builtin_ff_cell_types().count(cell->type)) { FfData ff(&initvals, cell); - if (ff.has_d && !ff.has_clk && !ff.has_en) { + if (ff.has_gclk) { // Already a $ff or $_FF_ cell. continue; } + if (ff.has_clk) { + log("Replacing %s.%s (%s): CLK=%s, D=%s, Q=%s\n", + log_id(module), log_id(cell), log_id(cell->type), + log_signal(ff.sig_clk), log_signal(ff.sig_d), log_signal(ff.sig_q)); + } else if (ff.has_aload) { + log("Replacing %s.%s (%s): EN=%s, D=%s, Q=%s\n", + log_id(module), log_id(cell), log_id(cell->type), + log_signal(ff.sig_aload), log_signal(ff.sig_ad), log_signal(ff.sig_q)); + } else { + // $sr. + log("Replacing %s.%s (%s): SET=%s, CLR=%s, Q=%s\n", + log_id(module), log_id(cell), log_id(cell->type), + log_signal(ff.sig_set), log_signal(ff.sig_clr), log_signal(ff.sig_q)); + } + + ff.remove(); + // Strip spaces from signal name, since Yosys IDs can't contain spaces - // Spaces only occur when have a signal that's a slice of a larger bus, + // Spaces only occur when we have a signal that's a slice of a larger bus, // e.g. "\myreg [5:0]", so removing spaces shouldn't result in loss of uniqueness std::string sig_q_str = log_signal(ff.sig_q); sig_q_str.erase(std::remove(sig_q_str.begin(), sig_q_str.end(), ' '), sig_q_str.end()); Wire *past_q = module->addWire(NEW_ID_SUFFIX(stringf("%s#past_q_wire", sig_q_str.c_str())), ff.width); + if (!ff.is_fine) { module->addFf(NEW_ID, ff.sig_q, past_q); } else { @@ -172,7 +190,7 @@ struct Clk2fflogicPass : public Pass { initvals.set_init(past_q, ff.val_init); if (ff.has_clk) { - ff.unmap_ce_srst(module); + ff.unmap_ce_srst(); Wire *past_clk = module->addWire(NEW_ID_SUFFIX(stringf("%s#past_clk#%s", sig_q_str.c_str(), log_signal(ff.sig_clk)))); initvals.set_init(past_clk, ff.pol_clk ? State::S1 : State::S0); @@ -182,10 +200,6 @@ struct Clk2fflogicPass : public Pass { else module->addFfGate(NEW_ID, ff.sig_clk, past_clk); - log("Replacing %s.%s (%s): CLK=%s, D=%s, Q=%s\n", - log_id(module), log_id(cell), log_id(cell->type), - log_signal(ff.sig_clk), log_signal(ff.sig_d), log_signal(ff.sig_q)); - SigSpec clock_edge_pattern; if (ff.pol_clk) { @@ -211,25 +225,17 @@ struct Clk2fflogicPass : public Pass { qval = module->Mux(NEW_ID, past_q, past_d, clock_edge); else qval = module->MuxGate(NEW_ID, past_q, past_d, clock_edge); - } else if (ff.has_d) { - - log("Replacing %s.%s (%s): EN=%s, D=%s, Q=%s\n", - log_id(module), log_id(cell), log_id(cell->type), - log_signal(ff.sig_en), log_signal(ff.sig_d), log_signal(ff.sig_q)); + } else { + qval = past_q; + } - SigSpec sig_en = wrap_async_control(module, ff.sig_en, ff.pol_en); + if (ff.has_aload) { + SigSpec sig_aload = wrap_async_control(module, ff.sig_aload, ff.pol_aload); if (!ff.is_fine) - qval = module->Mux(NEW_ID, past_q, ff.sig_d, sig_en); + qval = module->Mux(NEW_ID, qval, ff.sig_ad, sig_aload); else - qval = module->MuxGate(NEW_ID, past_q, ff.sig_d, sig_en); - } else { - - log("Replacing %s.%s (%s): SET=%s, CLR=%s, Q=%s\n", - log_id(module), log_id(cell), log_id(cell->type), - log_signal(ff.sig_set), log_signal(ff.sig_clr), log_signal(ff.sig_q)); - - qval = past_q; + qval = module->MuxGate(NEW_ID, qval, ff.sig_ad, sig_aload); } if (ff.has_sr) { @@ -254,10 +260,6 @@ struct Clk2fflogicPass : public Pass { } else { module->connect(ff.sig_q, qval); } - - initvals.remove_init(ff.sig_q); - module->remove(cell); - continue; } } } diff --git a/passes/sat/cutpoint.cc b/passes/sat/cutpoint.cc index 6fc267d51..bca6a5ec6 100644 --- a/passes/sat/cutpoint.cc +++ b/passes/sat/cutpoint.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/sat/eval.cc b/passes/sat/eval.cc index 085e7c5b8..05879426e 100644 --- a/passes/sat/eval.cc +++ b/passes/sat/eval.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -18,7 +18,7 @@ */ // [[CITE]] VlogHammer Verilog Regression Test Suite -// http://www.clifford.at/yosys/vloghammer.html +// https://yosyshq.net/yosys/vloghammer.html #include "kernel/register.h" #include "kernel/celltypes.h" diff --git a/passes/sat/expose.cc b/passes/sat/expose.cc index 2c65821cf..e7ec29ee4 100644 --- a/passes/sat/expose.cc +++ b/passes/sat/expose.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/sat/fmcombine.cc b/passes/sat/fmcombine.cc index cb49edac3..e15bdf6a8 100644 --- a/passes/sat/fmcombine.cc +++ b/passes/sat/fmcombine.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/sat/fminit.cc b/passes/sat/fminit.cc index c72e62548..5f4ec0068 100644 --- a/passes/sat/fminit.cc +++ b/passes/sat/fminit.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/sat/freduce.cc b/passes/sat/freduce.cc index f87b85da9..52e80f667 100644 --- a/passes/sat/freduce.cc +++ b/passes/sat/freduce.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/sat/miter.cc b/passes/sat/miter.cc index fe4a819f3..37efadfbd 100644 --- a/passes/sat/miter.cc +++ b/passes/sat/miter.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/sat/mutate.cc b/passes/sat/mutate.cc index 95e0e0944..42eb0c6d0 100644 --- a/passes/sat/mutate.cc +++ b/passes/sat/mutate.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/sat/sat.cc b/passes/sat/sat.cc index 9fdac6147..df2725b3c 100644 --- a/passes/sat/sat.cc +++ b/passes/sat/sat.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/sat/sim.cc b/passes/sat/sim.cc index 3ba66bd33..a7c109374 100644 --- a/passes/sat/sim.cc +++ b/passes/sat/sim.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -21,12 +21,49 @@ #include "kernel/sigtools.h" #include "kernel/celltypes.h" #include "kernel/mem.h" +#include "kernel/fstdata.h" #include <ctime> USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN +enum class SimulationMode { + sim, + cmp, + gold, + gate, +}; + +static const std::map<std::string, int> g_units = +{ + { "", -9 }, // default is ns + { "s", 0 }, + { "ms", -3 }, + { "us", -6 }, + { "ns", -9 }, + { "ps", -12 }, + { "fs", -15 }, + { "as", -18 }, + { "zs", -21 }, +}; + +static double stringToTime(std::string str) +{ + if (str=="END") return -1; + + char *endptr; + long value = strtol(str.c_str(), &endptr, 10); + + if (g_units.find(endptr)==g_units.end()) + log_error("Cannot parse '%s', bad unit '%s'\n", str.c_str(), endptr); + + if (value < 0) + log_error("Time value '%s' must be positive\n", str.c_str()); + + return value * pow(10.0, g_units.at(endptr)); +} + struct SimShared { bool debug = false; @@ -34,6 +71,11 @@ struct SimShared bool writeback = false; bool zinit = false; int rstlen = 1; + FstData *fst = nullptr; + double start_time = 0; + double stop_time = -1; + SimulationMode sim_mode = SimulationMode::sim; + bool cycles_set = false; }; void zinit(State &v) @@ -51,7 +93,8 @@ void zinit(Const &v) struct SimInstance { SimShared *shared; - + + std::string scope; Module *module; Cell *instance; @@ -92,9 +135,11 @@ struct SimInstance std::vector<Mem> memories; dict<Wire*, pair<int, Const>> vcd_database; + dict<Wire*, pair<fstHandle, Const>> fst_database; + dict<Wire*, fstHandle> fst_handles; - SimInstance(SimShared *shared, Module *module, Cell *instance = nullptr, SimInstance *parent = nullptr) : - shared(shared), module(module), instance(instance), parent(parent), sigmap(module) + SimInstance(SimShared *shared, std::string scope, Module *module, Cell *instance = nullptr, SimInstance *parent = nullptr) : + shared(shared), scope(scope), module(module), instance(instance), parent(parent), sigmap(module) { log_assert(module); @@ -116,6 +161,13 @@ struct SimInstance } } + if ((shared->fst) && !(shared->hide_internal && wire->name[0] == '$')) { + fstHandle id = shared->fst->getHandle(scope + "." + RTLIL::unescape_id(wire->name)); + if (id==0 && wire->name.isPublic()) + log_warning("Unable to found wire %s in input file.\n", (scope + "." + RTLIL::unescape_id(wire->name)).c_str()); + fst_handles[wire] = id; + } + if (wire->attributes.count(ID::init)) { Const initval = wire->attributes.at(ID::init); for (int i = 0; i < GetSize(sig) && i < GetSize(initval); i++) @@ -144,7 +196,7 @@ struct SimInstance Module *mod = module->design->module(cell->type); if (mod != nullptr) { - dirty_children.insert(new SimInstance(shared, mod, cell, this)); + dirty_children.insert(new SimInstance(shared, scope + "." + RTLIL::unescape_id(cell->name), mod, cell, this)); } for (auto &port : cell->connections()) { @@ -164,7 +216,7 @@ struct SimInstance ff_database[cell] = ff; } - if (cell->type.in(ID($mem), ID($meminit), ID($memwr), ID($memrd))) + if (cell->is_mem_cell()) { mem_cells[cell] = cell->parameters.at(ID::MEMID).decode_string(); } @@ -271,7 +323,7 @@ struct SimInstance { auto child = children.at(cell); for (auto &conn: cell->connections()) - if (cell->input(conn.first)) { + if (cell->input(conn.first) && GetSize(conn.second)) { Const value = get_state(conn.second); child->set_state(child->module->wire(conn.first), value); } @@ -313,6 +365,12 @@ struct SimInstance return; } + // (A,S -> Y) cells + if (has_a && !has_b && !has_c && !has_d && has_s && has_y) { + set_state(sig_y, CellTypes::eval(cell, get_state(sig_a), get_state(sig_s))); + return; + } + // (A,B,S -> Y) cells if (has_a && has_b && !has_c && !has_d && has_s && has_y) { set_state(sig_y, CellTypes::eval(cell, get_state(sig_a), get_state(sig_b), get_state(sig_s))); @@ -334,7 +392,7 @@ struct SimInstance { auto &port = mem.rd_ports[port_idx]; Const addr = get_state(port.addr); - Const data = Const(State::Sx, mem.width); + Const data = Const(State::Sx, mem.width << port.wide_log2); if (port.clk_enable) log_error("Memory %s.%s has clocked read ports. Run 'memory' with -nordff.\n", log_id(module), log_id(mem.memid)); @@ -342,7 +400,7 @@ struct SimInstance if (addr.is_fully_def()) { int index = addr.as_int() - mem.start_offset; if (index >= 0 && index < mem.size) - data = mdb.data.extract(index*mem.width, mem.width); + data = mdb.data.extract(index*mem.width, mem.width << port.wide_log2); } set_state(port.data, data); @@ -457,7 +515,7 @@ struct SimInstance { int index = addr.as_int() - mem.start_offset; if (index >= 0 && index < mem.size) - for (int i = 0; i < mem.width; i++) + for (int i = 0; i < (mem.width << port.wide_log2); i++) if (enable[i] == State::S1 && mdb.data.bits.at(index*mem.width+i) != data[i]) { mdb.data.bits.at(index*mem.width+i) = data[i]; dirty_memories.insert(mem.memid); @@ -559,6 +617,7 @@ struct SimInstance MemInit minit; minit.addr = mem.mem->start_offset; minit.data = mem.data; + minit.en = Const(State::S1, mem.mem->width); mem.mem->inits.push_back(minit); mem.mem->emit(); } @@ -615,14 +674,125 @@ struct SimInstance for (auto child : children) child.second->write_vcd_step(f); } + + void write_fst_header(struct fstContext *f) + { + fstWriterSetScope(f, FST_ST_VCD_MODULE, stringf("%s",log_id(name())).c_str(), nullptr); + for (auto wire : module->wires()) + { + if (shared->hide_internal && wire->name[0] == '$') + continue; + + fstHandle id = fstWriterCreateVar(f, FST_VT_VCD_WIRE, FST_VD_IMPLICIT, GetSize(wire), + stringf("%s%s", wire->name[0] == '$' ? "\\" : "", log_id(wire)).c_str(), 0); + fst_database[wire] = make_pair(id, Const()); + } + + for (auto child : children) + child.second->write_fst_header(f); + + fstWriterSetUpscope(f); + } + + void write_fst_step(struct fstContext *f) + { + for (auto &it : fst_database) + { + Wire *wire = it.first; + Const value = get_state(wire); + fstHandle id = it.second.first; + + if (it.second.second == value) + continue; + + it.second.second = value; + std::stringstream ss; + for (int i = GetSize(value)-1; i >= 0; i--) { + switch (value[i]) { + case State::S0: ss << "0"; break; + case State::S1: ss << "1"; break; + case State::Sx: ss << "x"; break; + default: ss << "z"; + } + } + fstWriterEmitValueChange(f, id, ss.str().c_str()); + } + + for (auto child : children) + child.second->write_fst_step(f); + } + + void setInitState(uint64_t time) + { + for (auto &it : ff_database) + { + Cell *cell = it.first; + + SigSpec qsig = cell->getPort(ID::Q); + if (qsig.is_wire()) { + IdString name = qsig.as_wire()->name; + fstHandle id = shared->fst->getHandle(scope + "." + RTLIL::unescape_id(name)); + if (id==0 && name.isPublic()) + log_warning("Unable to found wire %s in input file.\n", (scope + "." + RTLIL::unescape_id(name)).c_str()); + if (id!=0) { + Const fst_val = Const::from_string(shared->fst->valueAt(id, time)); + set_state(qsig, fst_val); + } + } + } + for (auto child : children) + child.second->setInitState(time); + } + + bool checkSignals(uint64_t time) + { + bool retVal = false; + for(auto &item : fst_handles) { + if (item.second==0) continue; // Ignore signals not found + Const fst_val = Const::from_string(shared->fst->valueAt(item.second, time)); + Const sim_val = get_state(item.first); + if (sim_val.size()!=fst_val.size()) + log_error("Signal '%s' size is different in gold and gate.\n", log_id(item.first)); + if (shared->sim_mode == SimulationMode::sim) { + // No checks performed when using stimulus + } else if (shared->sim_mode == SimulationMode::gate && !fst_val.is_fully_def()) { // FST data contains X + for(int i=0;i<fst_val.size();i++) { + if (fst_val[i]!=State::Sx && fst_val[i]!=sim_val[i]) { + log_warning("Signal '%s' in file %s in simulation %s\n", log_id(item.first), log_signal(fst_val), log_signal(sim_val)); + retVal = true; + break; + } + } + } else if (shared->sim_mode == SimulationMode::gold && !sim_val.is_fully_def()) { // sim data contains X + for(int i=0;i<sim_val.size();i++) { + if (sim_val[i]!=State::Sx && fst_val[i]!=sim_val[i]) { + log_warning("Signal '%s' in file %s in simulation %s\n", log_id(item.first), log_signal(fst_val), log_signal(sim_val)); + retVal = true; + break; + } + } + } else { + if (fst_val!=sim_val) { + log_warning("Signal '%s' in file %s in simulation '%s'\n", log_id(item.first), log_signal(fst_val), log_signal(sim_val)); + retVal = true; + } + } + } + for (auto child : children) + retVal |= child.second->checkSignals(time); + return retVal; + } }; struct SimWorker : SimShared { SimInstance *top = nullptr; std::ofstream vcdfile; + struct fstContext *fstfile = nullptr; pool<IdString> clock, clockn, reset, resetn; std::string timescale; + std::string sim_filename; + std::string scope; ~SimWorker() { @@ -631,9 +801,6 @@ struct SimWorker : SimShared void write_vcd_header() { - if (!vcdfile.is_open()) - return; - vcdfile << stringf("$version %s $end\n", yosys_version_str); std::time_t t = std::time(nullptr); @@ -653,13 +820,53 @@ struct SimWorker : SimShared void write_vcd_step(int t) { - if (!vcdfile.is_open()) - return; - vcdfile << stringf("#%d\n", t); top->write_vcd_step(vcdfile); } + void write_fst_header() + { + std::time_t t = std::time(nullptr); + fstWriterSetDate(fstfile, asctime(std::localtime(&t))); + fstWriterSetVersion(fstfile, yosys_version_str); + if (!timescale.empty()) + fstWriterSetTimescaleFromString(fstfile, timescale.c_str()); + + fstWriterSetPackType(fstfile, FST_WR_PT_FASTLZ); + fstWriterSetRepackOnClose(fstfile, 1); + + top->write_fst_header(fstfile); + } + + void write_fst_step(int t) + { + fstWriterEmitTimeChange(fstfile, t); + + top->write_fst_step(fstfile); + } + + void write_output_header() + { + if (vcdfile.is_open()) + write_vcd_header(); + if (fstfile) + write_fst_header(); + } + + void write_output_step(int t) + { + if (vcdfile.is_open()) + write_vcd_step(t); + if (fstfile) + write_fst_step(t); + } + + void write_output_end() + { + if (fstfile) + fstWriterClose(fstfile); + } + void update() { while (1) @@ -698,7 +905,7 @@ struct SimWorker : SimShared void run(Module *topmod, int numcycles) { log_assert(top == nullptr); - top = new SimInstance(this, topmod); + top = new SimInstance(this, scope, topmod); if (debug) log("\n===== 0 =====\n"); @@ -713,24 +920,25 @@ struct SimWorker : SimShared update(); - write_vcd_header(); - write_vcd_step(0); + write_output_header(); + write_output_step(0); for (int cycle = 0; cycle < numcycles; cycle++) { if (debug) log("\n===== %d =====\n", 10*cycle + 5); - + else + log("Simulating cycle %d.\n", (cycle*2)+1); set_inports(clock, State::S0); set_inports(clockn, State::S1); update(); - write_vcd_step(10*cycle + 5); + write_output_step(10*cycle + 5); if (debug) log("\n===== %d =====\n", 10*cycle + 10); else - log("Simulating cycle %d.\n", cycle+1); + log("Simulating cycle %d.\n", (cycle*2)+2); set_inports(clock, State::S1); set_inports(clockn, State::S0); @@ -741,11 +949,132 @@ struct SimWorker : SimShared } update(); - write_vcd_step(10*cycle + 10); + write_output_step(10*cycle + 10); } - write_vcd_step(10*numcycles + 2); + write_output_step(10*numcycles + 2); + + write_output_end(); + + if (writeback) { + pool<Module*> wbmods; + top->writeback(wbmods); + } + } + + void run_cosim(Module *topmod, int numcycles) + { + log_assert(top == nullptr); + fst = new FstData(sim_filename); + + if (scope.empty()) + log_error("Scope must be defined for co-simulation.\n"); + + top = new SimInstance(this, scope, topmod); + + std::vector<fstHandle> fst_clock; + + for (auto portname : clock) + { + Wire *w = topmod->wire(portname); + if (!w) + log_error("Can't find port %s on module %s.\n", log_id(portname), log_id(top->module)); + if (!w->port_input) + log_error("Clock port %s on module %s is not input.\n", log_id(portname), log_id(top->module)); + fstHandle id = fst->getHandle(scope + "." + RTLIL::unescape_id(portname)); + if (id==0) + log_error("Can't find port %s.%s in FST.\n", scope.c_str(), log_id(portname)); + fst_clock.push_back(id); + } + for (auto portname : clockn) + { + Wire *w = topmod->wire(portname); + if (!w) + log_error("Can't find port %s on module %s.\n", log_id(portname), log_id(top->module)); + if (!w->port_input) + log_error("Clock port %s on module %s is not input.\n", log_id(portname), log_id(top->module)); + fstHandle id = fst->getHandle(scope + "." + RTLIL::unescape_id(portname)); + if (id==0) + log_error("Can't find port %s.%s in FST.\n", scope.c_str(), log_id(portname)); + fst_clock.push_back(id); + } + if (fst_clock.size()==0) + log_error("No clock signals defined for input file\n"); + + SigMap sigmap(topmod); + std::map<Wire*,fstHandle> inputs; + + for (auto wire : topmod->wires()) { + if (wire->port_input) { + fstHandle id = fst->getHandle(scope + "." + RTLIL::unescape_id(wire->name)); + if (id==0) + log_error("Unable to find required '%s' signal in file\n",(scope + "." + RTLIL::unescape_id(wire->name)).c_str()); + inputs[wire] = id; + } + } + + uint64_t startCount = 0; + uint64_t stopCount = 0; + if (start_time==0) { + if (start_time < fst->getStartTime()) + log_warning("Start time is before simulation file start time\n"); + startCount = fst->getStartTime(); + } else if (start_time==-1) + startCount = fst->getEndTime(); + else { + startCount = start_time / fst->getTimescale(); + if (startCount > fst->getEndTime()) { + startCount = fst->getEndTime(); + log_warning("Start time is after simulation file end time\n"); + } + } + if (stop_time==0) { + if (stop_time < fst->getStartTime()) + log_warning("Stop time is before simulation file start time\n"); + stopCount = fst->getStartTime(); + } else if (stop_time==-1) + stopCount = fst->getEndTime(); + else { + stopCount = stop_time / fst->getTimescale(); + if (stopCount > fst->getEndTime()) { + stopCount = fst->getEndTime(); + log_warning("Stop time is after simulation file end time\n"); + } + } + if (stopCount<startCount) { + log_error("Stop time is before start time\n"); + } + auto samples = fst->getAllEdges(fst_clock, startCount, stopCount); + + // Limit to number of cycles if provided + if (cycles_set && ((size_t)(numcycles *2) < samples.size())) + samples.erase(samples.begin() + (numcycles*2), samples.end()); + + // Add setup time (start time) + if (samples.empty() || samples.front()!=startCount) + samples.insert(samples.begin(), startCount); + + fst->reconstructAllAtTimes(samples); + bool initial = true; + int cycle = 0; + log("Co-simulation from %lu%s to %lu%s\n", (unsigned long)startCount, fst->getTimescaleString(), (unsigned long)stopCount, fst->getTimescaleString()); + for(auto &time : samples) { + log("Co-simulating cycle %d [%lu%s].\n", cycle, (unsigned long)time, fst->getTimescaleString()); + for(auto &item : inputs) { + std::string v = fst->valueAt(item.second, time); + top->set_state(item.first, Const::from_string(v)); + } + if (initial) { + top->setInitState(time); + initial = false; + } + update(); + bool status = top->checkSignals(time); + if (status) + log_error("Signal difference\n"); + cycle++; + } if (writeback) { pool<Module*> wbmods; top->writeback(wbmods); @@ -766,6 +1095,9 @@ struct SimPass : public Pass { log(" -vcd <filename>\n"); log(" write the simulation results to the given VCD file\n"); log("\n"); + log(" -fst <filename>\n"); + log(" write the simulation results to the given FST file\n"); + log("\n"); log(" -clock <portname>\n"); log(" name of top-level clock input\n"); log("\n"); @@ -788,14 +1120,41 @@ struct SimPass : public Pass { log(" include the specified timescale declaration in the vcd\n"); log("\n"); log(" -n <integer>\n"); - log(" number of cycles to simulate (default: 20)\n"); + log(" number of clock cycles to simulate (default: 20)\n"); log("\n"); log(" -a\n"); - log(" include all nets in VCD output, not just those with public names\n"); + log(" use all nets in VCD/FST operations, not just those with public names\n"); log("\n"); log(" -w\n"); log(" writeback mode: use final simulation state as new init state\n"); log("\n"); + log(" -r\n"); + log(" read simulation results file (file formats supported: FST)\n"); + log("\n"); + log(" -scope\n"); + log(" scope of simulation top model\n"); + log("\n"); + log(" -at <time>\n"); + log(" sets start and stop time\n"); + log("\n"); + log(" -start <time>\n"); + log(" start co-simulation in arbitary time (default 0)\n"); + log("\n"); + log(" -stop <time>\n"); + log(" stop co-simulation in arbitary time (default END)\n"); + log("\n"); + log(" -sim\n"); + log(" simulation with stimulus from FST (default)\n"); + log("\n"); + log(" -sim-cmp\n"); + log(" co-simulation expect exact match\n"); + log("\n"); + log(" -sim-gold\n"); + log(" co-simulation, x in simulation can match any value in FST\n"); + log("\n"); + log(" -sim-gate\n"); + log(" co-simulation, x in FST can match any value in simulation\n"); + log("\n"); log(" -d\n"); log(" enable debug output\n"); log("\n"); @@ -804,6 +1163,7 @@ struct SimPass : public Pass { { SimWorker worker; int numcycles = 20; + bool start_set = false, stop_set = false, at_set = false; log_header(design, "Executing SIM pass (simulate the circuit).\n"); @@ -815,8 +1175,15 @@ struct SimPass : public Pass { worker.vcdfile.open(vcd_filename.c_str()); continue; } + if (args[argidx] == "-fst" && argidx+1 < args.size()) { + std::string fst_filename = args[++argidx]; + rewrite_filename(fst_filename); + worker.fstfile = (struct fstContext *)fstWriterCreate(fst_filename.c_str(),1); + continue; + } if (args[argidx] == "-n" && argidx+1 < args.size()) { numcycles = atoi(args[++argidx].c_str()); + worker.cycles_set = true; continue; } if (args[argidx] == "-rstlen" && argidx+1 < args.size()) { @@ -859,9 +1226,55 @@ struct SimPass : public Pass { worker.zinit = true; continue; } + if (args[argidx] == "-r" && argidx+1 < args.size()) { + std::string sim_filename = args[++argidx]; + rewrite_filename(sim_filename); + worker.sim_filename = sim_filename; + continue; + } + if (args[argidx] == "-scope" && argidx+1 < args.size()) { + worker.scope = args[++argidx]; + continue; + } + if (args[argidx] == "-start" && argidx+1 < args.size()) { + worker.start_time = stringToTime(args[++argidx]); + start_set = true; + continue; + } + if (args[argidx] == "-stop" && argidx+1 < args.size()) { + worker.stop_time = stringToTime(args[++argidx]); + stop_set = true; + continue; + } + if (args[argidx] == "-at" && argidx+1 < args.size()) { + worker.start_time = stringToTime(args[++argidx]); + worker.stop_time = worker.start_time; + at_set = true; + continue; + } + if (args[argidx] == "-sim") { + worker.sim_mode = SimulationMode::sim; + continue; + } + if (args[argidx] == "-sim-cmp") { + worker.sim_mode = SimulationMode::cmp; + continue; + } + if (args[argidx] == "-sim-gold") { + worker.sim_mode = SimulationMode::gold; + continue; + } + if (args[argidx] == "-sim-gate") { + worker.sim_mode = SimulationMode::gate; + continue; + } break; } extra_args(args, argidx, design); + if (at_set && (start_set || stop_set || worker.cycles_set)) + log_error("'at' option can only be defined separate of 'start','stop' and 'n'\n"); + if (stop_set && worker.cycles_set) + log_error("'stop' and 'n' can only be used exclusively'\n"); Module *top_mod = nullptr; @@ -877,7 +1290,10 @@ struct SimPass : public Pass { top_mod = mods.front(); } - worker.run(top_mod, numcycles); + if (worker.sim_filename.empty()) + worker.run(top_mod, numcycles); + else + worker.run_cosim(top_mod, numcycles); } } SimPass; diff --git a/passes/sat/supercover.cc b/passes/sat/supercover.cc index aacc044fb..38dbd3cf9 100644 --- a/passes/sat/supercover.cc +++ b/passes/sat/supercover.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc index 035699603..98ccfc303 100644 --- a/passes/techmap/Makefile.inc +++ b/passes/techmap/Makefile.inc @@ -29,6 +29,8 @@ OBJS += passes/techmap/extract_reduce.o OBJS += passes/techmap/alumacc.o OBJS += passes/techmap/dffinit.o OBJS += passes/techmap/pmuxtree.o +OBJS += passes/techmap/bmuxmap.o +OBJS += passes/techmap/demuxmap.o OBJS += passes/techmap/muxcover.o OBJS += passes/techmap/aigmap.o OBJS += passes/techmap/tribuf.o diff --git a/passes/techmap/abc.cc b/passes/techmap/abc.cc index d5286f4e9..80c6282c4 100644 --- a/passes/techmap/abc.cc +++ b/passes/techmap/abc.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -655,8 +655,9 @@ struct abc_output_filter }; void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::string script_file, std::string exe_file, - std::vector<std::string> &liberty_files, std::string constr_file, bool cleanup, vector<int> lut_costs, bool dff_mode, std::string clk_str, - bool keepff, std::string delay_target, std::string sop_inputs, std::string sop_products, std::string lutin_shared, bool fast_mode, + std::vector<std::string> &liberty_files, std::vector<std::string> &genlib_files, std::string constr_file, + bool cleanup, vector<int> lut_costs, bool dff_mode, std::string clk_str, bool keepff, std::string delay_target, + std::string sop_inputs, std::string sop_products, std::string lutin_shared, bool fast_mode, const std::vector<RTLIL::Cell*> &cells, bool show_tempdir, bool sop_mode, bool abc_dress) { module = current_module; @@ -710,8 +711,11 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin std::string abc_script = stringf("read_blif %s/input.blif; ", tempdir_name.c_str()); - if (!liberty_files.empty()) { - for (std::string liberty_file : liberty_files) abc_script += stringf("read_lib -w %s; ", liberty_file.c_str()); + if (!liberty_files.empty() || !genlib_files.empty()) { + for (std::string liberty_file : liberty_files) + abc_script += stringf("read_lib -w %s; ", liberty_file.c_str()); + for (std::string liberty_file : genlib_files) + abc_script += stringf("read_library %s; ", liberty_file.c_str()); if (!constr_file.empty()) abc_script += stringf("read_constr -v %s; ", constr_file.c_str()); } else @@ -739,7 +743,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin abc_script += fast_mode ? ABC_FAST_COMMAND_LUT : ABC_COMMAND_LUT; if (all_luts_cost_same && !fast_mode) abc_script += "; lutpack {S}"; - } else if (!liberty_files.empty()) + } else if (!liberty_files.empty() || !genlib_files.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; @@ -1020,7 +1024,7 @@ 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_files.empty(); + bool builtin_lib = liberty_files.empty() && genlib_files.empty(); RTLIL::Design *mapped_design = new RTLIL::Design; parse_blif(mapped_design, ifs, builtin_lib ? ID(DFF) : ID(_dff_), false, sop_mode); @@ -1302,10 +1306,10 @@ struct AbcPass : public Pass { log("\n"); log(" if no -script parameter is given, the following scripts are used:\n"); log("\n"); - log(" for -liberty without -constr:\n"); + log(" for -liberty/-genlib without -constr:\n"); log("%s\n", fold_abc_cmd(ABC_COMMAND_LIB).c_str()); log("\n"); - log(" for -liberty with -constr:\n"); + log(" for -liberty/-genlib with -constr:\n"); log("%s\n", fold_abc_cmd(ABC_COMMAND_CTR).c_str()); log("\n"); log(" for -lut/-luts (only one LUT size):\n"); @@ -1324,10 +1328,10 @@ struct AbcPass : public Pass { log(" use different default scripts that are slightly faster (at the cost\n"); log(" of output quality):\n"); log("\n"); - log(" for -liberty without -constr:\n"); + log(" for -liberty/-genlib without -constr:\n"); log("%s\n", fold_abc_cmd(ABC_FAST_COMMAND_LIB).c_str()); log("\n"); - log(" for -liberty with -constr:\n"); + log(" for -liberty/-genlib with -constr:\n"); log("%s\n", fold_abc_cmd(ABC_FAST_COMMAND_CTR).c_str()); log("\n"); log(" for -lut/-luts:\n"); @@ -1343,8 +1347,13 @@ struct AbcPass : public Pass { log(" generate netlists for the specified cell library (using the liberty\n"); log(" file format).\n"); log("\n"); + log(" -genlib <file>\n"); + log(" generate netlists for the specified cell library (using the SIS Genlib\n"); + log(" file format).\n"); + log("\n"); log(" -constr <file>\n"); - log(" pass this file with timing constraints to ABC. use with -liberty.\n"); + log(" pass this file with timing constraints to ABC.\n"); + log(" use with -liberty/-genlib.\n"); log("\n"); log(" a constr file contains two lines:\n"); log(" set_driving_cell <cell_name>\n"); @@ -1390,7 +1399,7 @@ struct AbcPass : public Pass { 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"); + // log(" (ignored when used with -liberty/-genlib or -lut)\n"); // log("\n"); log(" -g type1,type2,...\n"); log(" Map to the specified list of gate types. Supported gates types are:\n"); @@ -1446,7 +1455,7 @@ struct AbcPass : public Pass { log(" preserve naming by an equivalence check between the original and post-ABC\n"); log(" netlists (experimental).\n"); log("\n"); - log("When neither -liberty nor -lut is used, the Yosys standard cell library is\n"); + log("When no target cell library is specified the Yosys standard cell library is\n"); log("loaded into ABC before the ABC script is executed.\n"); log("\n"); log("Note that this is a logic optimization pass within Yosys that is calling ABC\n"); @@ -1473,7 +1482,7 @@ struct AbcPass : public Pass { std::string exe_file = yosys_abc_executable; std::string script_file, default_liberty_file, constr_file, clk_str; - std::vector<std::string> liberty_files; + std::vector<std::string> liberty_files, genlib_files; std::string delay_target, sop_inputs, sop_products, lutin_shared = "-S 1"; bool fast_mode = false, dff_mode = false, keepff = false, cleanup = true; bool show_tempdir = false, sop_mode = false; @@ -1556,6 +1565,10 @@ struct AbcPass : public Pass { liberty_files.push_back(args[++argidx]); continue; } + if (arg == "-genlib" && argidx+1 < args.size()) { + genlib_files.push_back(args[++argidx]); + continue; + } if (arg == "-constr" && argidx+1 < args.size()) { constr_file = args[++argidx]; continue; @@ -1645,7 +1658,8 @@ struct AbcPass : public Pass { } extra_args(args, argidx, design); - if (liberty_files.empty() && !default_liberty_file.empty()) liberty_files.push_back(default_liberty_file); + if (genlib_files.empty() && liberty_files.empty() && !default_liberty_file.empty()) + liberty_files.push_back(default_liberty_file); rewrite_filename(script_file); if (!script_file.empty() && !is_absolute_path(script_file) && script_file[0] != '+') @@ -1655,6 +1669,11 @@ struct AbcPass : public Pass { if (!liberty_files[i].empty() && !is_absolute_path(liberty_files[i])) liberty_files[i] = std::string(pwd) + "/" + liberty_files[i]; } + for (int i = 0; i < GetSize(genlib_files); i++) { + rewrite_filename(genlib_files[i]); + if (!genlib_files[i].empty() && !is_absolute_path(genlib_files[i])) + genlib_files[i] = std::string(pwd) + "/" + genlib_files[i]; + } rewrite_filename(constr_file); if (!constr_file.empty() && !is_absolute_path(constr_file)) constr_file = std::string(pwd) + "/" + constr_file; @@ -1818,10 +1837,10 @@ struct AbcPass : public Pass { } } - if (!lut_costs.empty() && !liberty_files.empty()) - log_cmd_error("Got -lut and -liberty! These two options are exclusive.\n"); - if (!constr_file.empty() && liberty_files.empty()) - log_cmd_error("Got -constr but no -liberty!\n"); + if (!lut_costs.empty() && !(liberty_files.empty() && genlib_files.empty())) + log_cmd_error("Got -lut and -liberty/-genlib! These two options are exclusive.\n"); + if (!constr_file.empty() && (liberty_files.empty() && genlib_files.empty())) + log_cmd_error("Got -constr but no -liberty/-genlib!\n"); if (enabled_gates.empty()) { enabled_gates.insert("AND"); @@ -1851,7 +1870,7 @@ struct AbcPass : public Pass { initvals.set(&assign_map, mod); if (!dff_mode || !clk_str.empty()) { - abc_module(design, mod, script_file, exe_file, liberty_files, constr_file, cleanup, lut_costs, dff_mode, clk_str, keepff, + abc_module(design, mod, script_file, exe_file, liberty_files, genlib_files, constr_file, cleanup, lut_costs, dff_mode, clk_str, keepff, delay_target, sop_inputs, sop_products, lutin_shared, fast_mode, mod->selected_cells(), show_tempdir, sop_mode, abc_dress); continue; } @@ -1996,7 +2015,7 @@ 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_files, constr_file, cleanup, lut_costs, !clk_sig.empty(), "$", + abc_module(design, mod, script_file, exe_file, liberty_files, genlib_files, constr_file, cleanup, lut_costs, !clk_sig.empty(), "$", keepff, delay_target, sop_inputs, sop_products, lutin_shared, fast_mode, it.second, show_tempdir, sop_mode, abc_dress); assign_map.set(mod); } diff --git a/passes/techmap/abc9.cc b/passes/techmap/abc9.cc index 56bb15495..1f00fc3e7 100644 --- a/passes/techmap/abc9.cc +++ b/passes/techmap/abc9.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * (C) 2019 Eddie Hung <eddie@fpgeh.com> * * Permission to use, copy, modify, and/or distribute this software for any @@ -283,9 +283,16 @@ struct Abc9Pass : public ScriptPass if (check_label("map")) { if (help_mode) - run("abc9_ops -prep_hier -prep_bypass [-prep_dff -dff]", "(option if -dff)"); + run("abc9_ops -prep_hier [-dff]", "(option if -dff)"); else - run(stringf("abc9_ops -prep_hier -prep_bypass %s", dff_mode ? "-prep_dff -dff" : "")); + run(stringf("abc9_ops -prep_hier %s", dff_mode ? "-dff" : "")); + run("scc -specify -set_attr abc9_scc_id {}"); + if (help_mode) + run("abc9_ops -prep_bypass [-prep_dff]", "(option if -dff)"); + else { + active_design->scratchpad_unset("abc9_ops.prep_bypass.did_something"); + run(stringf("abc9_ops -prep_bypass %s", dff_mode ? "-prep_dff" : "")); + } if (dff_mode) { run("design -copy-to $abc9_map @$abc9_flops", "(only if -dff)"); run("select -unset $abc9_flops", " (only if -dff)"); @@ -330,20 +337,20 @@ struct Abc9Pass : public ScriptPass run("design -stash $abc9_map"); run("design -load $abc9"); run("design -delete $abc9"); + // Insert bypass modules (and perform +/abc9_map.v transformations), except for those cells part of a SCC if (help_mode) run("techmap -wb -max_iter 1 -map %$abc9_map -map +/abc9_map.v [-D DFF]", "(option if -dff)"); else - run(stringf("techmap -wb -max_iter 1 -map %%$abc9_map -map +/abc9_map.v %s", dff_mode ? "-D DFF" : "")); + run(stringf("techmap -wb -max_iter 1 -map %%$abc9_map -map +/abc9_map.v %s a:abc9_scc_id %%n", dff_mode ? "-D DFF" : "")); run("design -delete $abc9_map"); } if (check_label("pre")) { run("read_verilog -icells -lib -specify +/abc9_model.v"); - run("scc -specify -set_attr abc9_scc_id {}"); if (help_mode) - run("abc9_ops -mark_scc -prep_delays -prep_xaiger [-dff]", "(option for -dff)"); + run("abc9_ops -break_scc -prep_delays -prep_xaiger [-dff]", "(option for -dff)"); else - run("abc9_ops -mark_scc -prep_delays -prep_xaiger" + std::string(dff_mode ? " -dff" : "")); + run("abc9_ops -break_scc -prep_delays -prep_xaiger" + std::string(dff_mode ? " -dff" : "")); if (help_mode) run("abc9_ops -prep_lut <maxlut>", "(skip if -lut or -luts)"); else if (!lut_mode) @@ -445,6 +452,9 @@ struct Abc9Pass : public ScriptPass run("design -delete $abc9_unmap"); if (saved_designs.count("$abc9_holes") || help_mode) run("design -delete $abc9_holes"); + if (help_mode || active_design->scratchpad_get_bool("abc9_ops.prep_bypass.did_something")) + run("delete =*_$abc9_byp"); + run("setattr -mod -unset abc9_box_id"); } } } Abc9Pass; diff --git a/passes/techmap/abc9_exe.cc b/passes/techmap/abc9_exe.cc index b916b049d..a66e95e21 100644 --- a/passes/techmap/abc9_exe.cc +++ b/passes/techmap/abc9_exe.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * 2019 Eddie Hung <eddie@fpgeh.com> * * Permission to use, copy, modify, and/or distribute this software for any diff --git a/passes/techmap/abc9_ops.cc b/passes/techmap/abc9_ops.cc index 98d0207c4..29fe74ec7 100644 --- a/passes/techmap/abc9_ops.cc +++ b/passes/techmap/abc9_ops.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * 2019 Eddie Hung <eddie@fpgeh.com> * * Permission to use, copy, modify, and/or distribute this software for any @@ -155,21 +155,6 @@ void prep_hier(RTLIL::Design *design, bool dff_mode) r.first->second = new Design; Design *unmap_design = r.first->second; - static const pool<IdString> seq_types{ - ID($dff), ID($dffsr), ID($adff), - ID($dlatch), ID($dlatchsr), ID($sr), - ID($mem), - ID($_DFF_N_), ID($_DFF_P_), - ID($_DFFSR_NNN_), ID($_DFFSR_NNP_), ID($_DFFSR_NPN_), ID($_DFFSR_NPP_), - ID($_DFFSR_PNN_), ID($_DFFSR_PNP_), ID($_DFFSR_PPN_), ID($_DFFSR_PPP_), - ID($_DFF_N_), ID($_DFF_NN0_), ID($_DFF_NN1_), ID($_DFF_NP0_), ID($_DFF_NP1_), - ID($_DFF_P_), ID($_DFF_PN0_), ID($_DFF_PN1_), ID($_DFF_PP0_), ID($_DFF_PP1_), - ID($_DLATCH_N_), ID($_DLATCH_P_), - ID($_DLATCHSR_NNN_), ID($_DLATCHSR_NNP_), ID($_DLATCHSR_NPN_), ID($_DLATCHSR_NPP_), - ID($_DLATCHSR_PNN_), ID($_DLATCHSR_PNP_), ID($_DLATCHSR_PPN_), ID($_DLATCHSR_PPP_), - ID($_SR_NN_), ID($_SR_NP_), ID($_SR_PN_), ID($_SR_PP_) - }; - for (auto module : design->selected_modules()) for (auto cell : module->cells()) { auto inst_module = design->module(cell->type); @@ -189,16 +174,22 @@ void prep_hier(RTLIL::Design *design, bool dff_mode) derived_type = inst_module->derive(design, cell->parameters); derived_module = design->module(derived_type); } - if (derived_module->get_blackbox_attribute(true /* ignore_wb */)) - continue; if (derived_module->get_bool_attribute(ID::abc9_flop)) { if (!dff_mode) continue; } else { - if (!derived_module->get_bool_attribute(ID::abc9_box) && !derived_module->get_bool_attribute(ID::abc9_bypass)) + if (!derived_module->get_bool_attribute(ID::abc9_box) && !derived_module->get_bool_attribute(ID::abc9_bypass)) { + if (unmap_design->module(derived_type)) { + // If derived_type is present in unmap_design, it means that it was processed previously, but found to be incompatible -- e.g. if + // it contained a non-zero initial state. In this case, continue to replace the cell type/parameters so that it has the same properties + // as a compatible type, yet will be safely unmapped later + cell->type = derived_type; + cell->parameters.clear(); + } continue; + } } if (!unmap_design->module(derived_type)) { @@ -223,7 +214,7 @@ void prep_hier(RTLIL::Design *design, bool dff_mode) } else if (derived_module->get_bool_attribute(ID::abc9_box)) { for (auto derived_cell : derived_module->cells()) - if (seq_types.count(derived_cell->type)) { + if (derived_cell->is_mem_cell() || RTLIL::builtin_ff_cell_types().count(derived_cell->type)) { derived_module->set_bool_attribute(ID::abc9_box, false); derived_module->set_bool_attribute(ID::abc9_bypass); break; @@ -441,6 +432,8 @@ void prep_bypass(RTLIL::Design *design) } } unmap_module->fixup_ports(); + + design->scratchpad_set_bool("abc9_ops.prep_bypass.did_something", true); } } @@ -459,7 +452,14 @@ void prep_dff(RTLIL::Design *design) if (!inst_module->get_bool_attribute(ID::abc9_flop)) continue; log_assert(!inst_module->get_blackbox_attribute(true /* ignore_wb */)); - log_assert(cell->parameters.empty()); + if (!cell->parameters.empty()) + { + // At this stage of the ABC9 flow, cells instantiating (* abc9_flop *) modules must not contain any parameters -- instead it should + // be instantiating the derived module which will have had any parameters constant-propagated. + // This task is expected to be performed by `abc9_ops -prep_hier`, but it looks like it failed to do so for this design. + // Please file a bug report! + log_error("Not expecting parameters on cell '%s' instantiating module '%s' marked (* abc9_flop *)\n", log_id(cell->name), log_id(cell->type)); + } modules_sel.select(inst_module); } } @@ -544,18 +544,31 @@ void prep_dff_unmap(RTLIL::Design *design) } } -void mark_scc(RTLIL::Module *module) +void break_scc(RTLIL::Module *module) { // For every unique SCC found, (arbitrarily) find the first - // cell in the component, and replace its output connections - // with a new wire driven by the old connection but with a - // special (* abc9_keep *) attribute set (which is used by - // write_xaiger to break this wire into PI and POs) + // cell in the component, and interrupt all its output connections + // with the $__ABC9_SCC_BREAKER cell + + // Do not break SCCs which have a cell instantiating an abc9_bypass-able + // module (but which wouldn't have been bypassed) + auto design = module->design; + pool<RTLIL::Cell*> scc_cells; pool<RTLIL::Const> ids_seen; for (auto cell : module->cells()) { auto it = cell->attributes.find(ID::abc9_scc_id); if (it == cell->attributes.end()) continue; + scc_cells.insert(cell); + auto inst_module = design->module(cell->type); + if (inst_module && inst_module->has_attribute(ID::abc9_bypass)) + ids_seen.insert(it->second); + } + + SigSpec I, O; + for (auto cell : scc_cells) { + auto it = cell->attributes.find(ID::abc9_scc_id); + log_assert(it != cell->attributes.end()); auto id = it->second; auto r = ids_seen.insert(id); cell->attributes.erase(it); @@ -565,12 +578,21 @@ void mark_scc(RTLIL::Module *module) if (c.second.is_fully_const()) continue; if (cell->output(c.first)) { Wire *w = module->addWire(NEW_ID, GetSize(c.second)); - w->set_bool_attribute(ID::abc9_keep); - module->connect(w, c.second); + I.append(w); + O.append(c.second); c.second = w; } } } + + if (!I.empty()) + { + auto cell = module->addCell(NEW_ID, ID($__ABC9_SCC_BREAKER)); + log_assert(GetSize(I) == GetSize(O)); + cell->setParam(ID::WIDTH, GetSize(I)); + cell->setPort(ID::I, std::move(I)); + cell->setPort(ID::O, std::move(O)); + } } void prep_delays(RTLIL::Design *design, bool dff_mode) @@ -626,40 +648,38 @@ void prep_delays(RTLIL::Design *design, bool dff_mode) auto inst_module = design->module(cell->type); log_assert(inst_module); - auto &t = timing.at(cell->type).required; - for (auto &conn : cell->connections_) { - auto port_wire = inst_module->wire(conn.first); + for (auto &i : timing.at(cell->type).required) { + auto port_wire = inst_module->wire(i.first.name); if (!port_wire) log_error("Port %s in cell %s (type %s) from module %s does not actually exist", - log_id(conn.first), log_id(cell), log_id(cell->type), log_id(module)); - if (!port_wire->port_input) - continue; - if (conn.second.is_fully_const()) + log_id(i.first.name), log_id(cell), log_id(cell->type), log_id(module)); + log_assert(port_wire->port_input); + + auto d = i.second.first; + if (d == 0) continue; - SigSpec O = module->addWire(NEW_ID, GetSize(conn.second)); - for (int i = 0; i < GetSize(conn.second); i++) { - auto d = t.at(TimingInfo::NameBit(conn.first,i), 0); - if (d == 0) - continue; + auto offset = i.first.offset; + auto O = module->addWire(NEW_ID); + auto rhs = cell->getPort(i.first.name); #ifndef NDEBUG - if (ys_debug(1)) { - static std::set<std::tuple<IdString,IdString,int>> seen; - if (seen.emplace(cell->type, conn.first, i).second) log("%s.%s[%d] abc9_required = %d\n", - log_id(cell->type), log_id(conn.first), i, d); - } + if (ys_debug(1)) { + static pool<std::pair<IdString,TimingInfo::NameBit>> seen; + if (seen.emplace(cell->type, i.first).second) log("%s.%s[%d] abc9_required = %d\n", + log_id(cell->type), log_id(i.first.name), offset, d); + } #endif - auto r = box_cache.insert(d); - if (r.second) { - r.first->second = delay_module->derive(design, {{ID::DELAY, d}}); - log_assert(r.first->second.begins_with("$paramod$__ABC9_DELAY\\DELAY=")); - } - auto box = module->addCell(NEW_ID, r.first->second); - box->setPort(ID::I, conn.second[i]); - box->setPort(ID::O, O[i]); - conn.second[i] = O[i]; + auto r = box_cache.insert(d); + if (r.second) { + r.first->second = delay_module->derive(design, {{ID::DELAY, d}}); + log_assert(r.first->second.begins_with("$paramod$__ABC9_DELAY\\DELAY=")); } + auto box = module->addCell(NEW_ID, r.first->second); + box->setPort(ID::I, rhs[offset]); + box->setPort(ID::O, O); + rhs[offset] = O; + cell->setPort(i.first.name, rhs); } } } @@ -721,10 +741,8 @@ void prep_xaiger(RTLIL::Module *module, bool dff) bit_users[bit].insert(cell->name); if (cell->output(conn.first) && !abc9_flop) - for (const auto &chunk : conn.second.chunks()) - if (!chunk.wire->get_bool_attribute(ID::abc9_keep)) - for (auto b : sigmap(SigSpec(chunk))) - bit_drivers[b].insert(cell->name); + for (auto bit : sigmap(conn.second)) + bit_drivers[bit].insert(cell->name); } toposort.node(cell->name); } @@ -778,7 +796,14 @@ void prep_xaiger(RTLIL::Module *module, bool dff) continue; if (!box_module->get_bool_attribute(ID::abc9_box)) continue; - log_assert(cell->parameters.empty()); + if (!cell->parameters.empty()) + { + // At this stage of the ABC9 flow, cells instantiating (* abc9_box *) modules must not contain any parameters -- instead it should + // be instantiating the derived module which will have had any parameters constant-propagated. + // This task is expected to be performed by `abc9_ops -prep_hier`, but it looks like it failed to do so for this design. + // Please file a bug report! + log_error("Not expecting parameters on cell '%s' instantiating module '%s' marked (* abc9_box *)\n", log_id(cell_name), log_id(cell->type)); + } log_assert(box_module->get_blackbox_attribute()); cell->attributes[ID::abc9_box_seq] = box_count++; @@ -787,7 +812,7 @@ void prep_xaiger(RTLIL::Module *module, bool dff) auto &holes_cell = r.first->second; if (r.second) { if (box_module->get_bool_attribute(ID::whitebox)) { - holes_cell = holes_module->addCell(cell->name, cell->type); + holes_cell = holes_module->addCell(NEW_ID, cell->type); if (box_module->has_processes()) Pass::call_on_module(design, box_module, "proc"); @@ -917,15 +942,8 @@ void prep_box(RTLIL::Design *design) { TimingInfo timing; - std::stringstream ss; int abc9_box_id = 1; - for (auto module : design->modules()) { - auto it = module->attributes.find(ID::abc9_box_id); - if (it == module->attributes.end()) - continue; - abc9_box_id = std::max(abc9_box_id, it->second.as_int()); - } - + std::stringstream ss; dict<IdString,std::vector<IdString>> box_ports; for (auto module : design->modules()) { auto it = module->attributes.find(ID::abc9_box); @@ -986,16 +1004,16 @@ void prep_box(RTLIL::Design *design) log_assert(GetSize(wire) == 1); auto it = t.find(TimingInfo::NameBit(port_name,0)); if (it == t.end()) - // Assume no connectivity if no setup time - ss << "-"; + // Assume that no setup time means zero + ss << 0; else { - ss << it->second; + ss << it->second.first; #ifndef NDEBUG if (ys_debug(1)) { static std::set<std::pair<IdString,IdString>> seen; if (seen.emplace(module->name, port_name).second) log("%s.%s abc9_required = %d\n", log_id(module), - log_id(port_name), it->second); + log_id(port_name), it->second.first); } #endif } @@ -1416,7 +1434,6 @@ void reintegrate(RTLIL::Module *module, bool dff_mode) RTLIL::Wire *mapped_wire = mapped_mod->wire(port); RTLIL::Wire *wire = module->wire(port); log_assert(wire); - wire->attributes.erase(ID::abc9_keep); RTLIL::Wire *remap_wire = module->wire(remap_name(port)); RTLIL::SigSpec signal(wire, remap_wire->start_offset-wire->start_offset, GetSize(remap_wire)); @@ -1579,11 +1596,11 @@ struct Abc9OpsPass : public Pass { log(" insert `$__ABC9_DELAY' blackbox cells into the design to account for\n"); log(" certain required times.\n"); log("\n"); - log(" -mark_scc\n"); + log(" -break_scc\n"); log(" for an arbitrarily chosen cell in each unique SCC of each selected module\n"); - log(" (tagged with an (* abc9_scc_id = <int> *) attribute), temporarily mark all\n"); - log(" wires driven by this cell's outputs with a (* keep *) attribute in order\n"); - log(" to break the SCC. this temporary attribute will be removed on -reintegrate.\n"); + log(" (tagged with an (* abc9_scc_id = <int> *) attribute) interrupt all wires\n"); + log(" driven by this cell's outputs with a temporary $__ABC9_SCC_BREAKER cell\n"); + log(" to break the SCC.\n"); log("\n"); log(" -prep_xaiger\n"); log(" prepare the design for XAIGER output. this includes computing the\n"); @@ -1620,7 +1637,7 @@ struct Abc9OpsPass : public Pass { bool check_mode = false; bool prep_delays_mode = false; - bool mark_scc_mode = false; + bool break_scc_mode = false; bool prep_hier_mode = false; bool prep_bypass_mode = false; bool prep_dff_mode = false, prep_dff_submod_mode = false, prep_dff_unmap_mode = false; @@ -1642,8 +1659,8 @@ struct Abc9OpsPass : public Pass { valid = true; continue; } - if (arg == "-mark_scc") { - mark_scc_mode = true; + if (arg == "-break_scc") { + break_scc_mode = true; valid = true; continue; } @@ -1719,7 +1736,7 @@ struct Abc9OpsPass : public Pass { extra_args(args, argidx, design); if (!valid) - log_cmd_error("At least one of -check, -mark_scc, -prep_{delays,xaiger,dff[123],lut,box}, -write_{lut,box}, -reintegrate must be specified.\n"); + log_cmd_error("At least one of -check, -break_scc, -prep_{delays,xaiger,dff[123],lut,box}, -write_{lut,box}, -reintegrate must be specified.\n"); if (dff_mode && !check_mode && !prep_hier_mode && !prep_delays_mode && !prep_xaiger_mode && !reintegrate_mode) log_cmd_error("'-dff' option is only relevant for -prep_{hier,delay,xaiger} or -reintegrate.\n"); @@ -1756,8 +1773,8 @@ struct Abc9OpsPass : public Pass { write_lut(mod, write_lut_dst); if (!write_box_dst.empty()) write_box(mod, write_box_dst); - if (mark_scc_mode) - mark_scc(mod); + if (break_scc_mode) + break_scc(mod); if (prep_xaiger_mode) prep_xaiger(mod, dff_mode); if (reintegrate_mode) diff --git a/passes/techmap/aigmap.cc b/passes/techmap/aigmap.cc index ce151c7f3..4836ebe34 100644 --- a/passes/techmap/aigmap.cc +++ b/passes/techmap/aigmap.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/techmap/alumacc.cc b/passes/techmap/alumacc.cc index b16e9750e..e4e70004c 100644 --- a/passes/techmap/alumacc.cc +++ b/passes/techmap/alumacc.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/techmap/attrmap.cc b/passes/techmap/attrmap.cc index 8643543c8..96e65ff2e 100644 --- a/passes/techmap/attrmap.cc +++ b/passes/techmap/attrmap.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/techmap/attrmvcp.cc b/passes/techmap/attrmvcp.cc index b3202c587..65b63daf1 100644 --- a/passes/techmap/attrmvcp.cc +++ b/passes/techmap/attrmvcp.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/techmap/bmuxmap.cc b/passes/techmap/bmuxmap.cc new file mode 100644 index 000000000..03673c278 --- /dev/null +++ b/passes/techmap/bmuxmap.cc @@ -0,0 +1,76 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2022 Marcelina KoÅ›cielnicka <mwk@0x04.net> + * + * 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 BmuxmapPass : public Pass { + BmuxmapPass() : Pass("bmuxmap", "transform $bmux cells to trees of $mux cells") { } + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" bmuxmap [selection]\n"); + log("\n"); + log("This pass transforms $bmux cells to trees of $mux cells.\n"); + log("\n"); + } + void execute(std::vector<std::string> args, RTLIL::Design *design) override + { + log_header(design, "Executing BMUXMAP pass.\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) + for (auto cell : module->selected_cells()) + { + if (cell->type != ID($bmux)) + continue; + + SigSpec sel = cell->getPort(ID::S); + SigSpec data = cell->getPort(ID::A); + int width = GetSize(cell->getPort(ID::Y)); + + for (int idx = 0; idx < GetSize(sel); idx++) { + SigSpec new_data = module->addWire(NEW_ID, GetSize(data)/2); + for (int i = 0; i < GetSize(new_data); i += width) { + RTLIL::Cell *mux = module->addMux(NEW_ID, + data.extract(i*2, width), + data.extract(i*2+width, width), + sel[idx], + new_data.extract(i, width)); + mux->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src)); + } + data = new_data; + } + + module->connect(cell->getPort(ID::Y), data); + module->remove(cell); + } + } +} BmuxmapPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/clkbufmap.cc b/passes/techmap/clkbufmap.cc index 1cbd12e3d..a7b96a9c6 100644 --- a/passes/techmap/clkbufmap.cc +++ b/passes/techmap/clkbufmap.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * Copyright (C) 2019 Marcelina KoÅ›cielnicka <mwk@0x04.net> * * Permission to use, copy, modify, and/or distribute this software for any diff --git a/passes/techmap/deminout.cc b/passes/techmap/deminout.cc index 9a23cb90e..5245331f8 100644 --- a/passes/techmap/deminout.cc +++ b/passes/techmap/deminout.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/techmap/demuxmap.cc b/passes/techmap/demuxmap.cc new file mode 100644 index 000000000..292b18bad --- /dev/null +++ b/passes/techmap/demuxmap.cc @@ -0,0 +1,80 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2022 Marcelina KoÅ›cielnicka <mwk@0x04.net> + * + * 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 DemuxmapPass : public Pass { + DemuxmapPass() : Pass("demuxmap", "transform $demux cells to $eq + $mux cells") { } + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" demuxmap [selection]\n"); + log("\n"); + log("This pass transforms $demux cells to a bunch of equality comparisons.\n"); + log("\n"); + } + void execute(std::vector<std::string> args, RTLIL::Design *design) override + { + log_header(design, "Executing DEMUXMAP pass.\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) + for (auto cell : module->selected_cells()) + { + if (cell->type != ID($demux)) + continue; + + SigSpec sel = cell->getPort(ID::S); + SigSpec data = cell->getPort(ID::A); + SigSpec out = cell->getPort(ID::Y); + int width = GetSize(cell->getPort(ID::A)); + + for (int i = 0; i < 1 << GetSize(sel); i++) { + if (width == 1 && data == State::S1) { + RTLIL::Cell *eq_cell = module->addEq(NEW_ID, sel, Const(i, GetSize(sel)), out[i]); + eq_cell->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src)); + } else { + Wire *eq = module->addWire(NEW_ID); + RTLIL::Cell *eq_cell = module->addEq(NEW_ID, sel, Const(i, GetSize(sel)), eq); + eq_cell->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src)); + RTLIL::Cell *mux = module->addMux(NEW_ID, + Const(State::S0, width), + data, + eq, + out.extract(i*width, width)); + mux->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src)); + } + } + + module->remove(cell); + } + } +} DemuxmapPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/dffinit.cc b/passes/techmap/dffinit.cc index 44af043db..9cfe55947 100644 --- a/passes/techmap/dffinit.cc +++ b/passes/techmap/dffinit.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/techmap/dfflegalize.cc b/passes/techmap/dfflegalize.cc index c1e7e557d..1d99caa3a 100644 --- a/passes/techmap/dfflegalize.cc +++ b/passes/techmap/dfflegalize.cc @@ -20,6 +20,7 @@ #include "kernel/yosys.h" #include "kernel/sigtools.h" #include "kernel/ffinit.h" +#include "kernel/ff.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -27,38 +28,42 @@ PRIVATE_NAMESPACE_BEGIN enum FfType { FF_DFF, FF_DFFE, - FF_ADFF0, - FF_ADFF1, - FF_ADFFE0, - FF_ADFFE1, + FF_ADFF, + FF_ADFFE, + FF_ALDFF, + FF_ALDFFE, FF_DFFSR, FF_DFFSRE, - FF_SDFF0, - FF_SDFF1, - FF_SDFFE0, - FF_SDFFE1, - FF_SDFFCE0, - FF_SDFFCE1, + FF_SDFF, + FF_SDFFE, + FF_SDFFCE, + FF_RLATCH, FF_SR, FF_DLATCH, - FF_ADLATCH0, - FF_ADLATCH1, + FF_ADLATCH, FF_DLATCHSR, NUM_FFTYPES, }; enum FfNeg { - NEG_R = 0x1, - NEG_S = 0x2, - NEG_E = 0x4, - NEG_C = 0x8, - NUM_NEG = 0x10, + NEG_CE = 0x1, + NEG_R = 0x2, + NEG_S = 0x4, + NEG_L = 0x8, + NEG_C = 0x10, + NUM_NEG = 0x20, }; enum FfInit { INIT_X = 0x1, INIT_0 = 0x2, INIT_1 = 0x4, + INIT_X_R0 = 0x10, + INIT_0_R0 = 0x20, + INIT_1_R0 = 0x40, + INIT_X_R1 = 0x100, + INIT_0_R1 = 0x200, + INIT_1_R1 = 0x400, }; struct DffLegalizePass : public Pass { @@ -101,6 +106,8 @@ struct DffLegalizePass : public Pass { log("- $_DFFE_[NP][NP]_\n"); log("- $_DFF_[NP][NP][01]_\n"); log("- $_DFFE_[NP][NP][01][NP]_\n"); + log("- $_ALDFF_[NP][NP]_\n"); + log("- $_ALDFFE_[NP][NP][NP]_\n"); log("- $_DFFSR_[NP][NP][NP]_\n"); log("- $_DFFSRE_[NP][NP][NP][NP]_\n"); log("- $_SDFF_[NP][NP][01]_\n"); @@ -151,18 +158,30 @@ struct DffLegalizePass : public Pass { int supported_cells[NUM_FFTYPES]; // Aggregated for all *dff* cells. int supported_dff; + // Aggregated for all *dffe* cells. + int supported_dffe; // Aggregated for all dffsr* cells. int supported_dffsr; - // Aggregated for all adff* cells. - int supported_adff0; - int supported_adff1; + // Aggregated for all aldff cells. + int supported_aldff; + // Aggregated for all aldffe cells. + int supported_aldffe; + // Aggregated for all adff* cells and trivial emulations. + int supported_adff; + // Aggregated for all adffe* cells and trivial emulations. + int supported_adffe; // Aggregated for all sdff* cells. - int supported_sdff0; - int supported_sdff1; + int supported_sdff; // Aggregated for all ways to obtain a SR latch. int supported_sr; + int supported_sr_plain; // Aggregated for all *dlatch* cells. int supported_dlatch; + int supported_dlatch_plain; + // Aggregated for all ways to obtain an R latch. + int supported_rlatch; + // Aggregated for all adlatch cells and trivial emulations. + int supported_adlatch; int mince; int minsrst; @@ -179,736 +198,794 @@ struct DffLegalizePass : public Pass { res |= INIT_1; if (mask & INIT_1) res |= INIT_0; + if (mask & INIT_X_R0) + res |= INIT_X_R1; + if (mask & INIT_0_R0) + res |= INIT_1_R1; + if (mask & INIT_1_R0) + res |= INIT_0_R1; + if (mask & INIT_X_R1) + res |= INIT_X_R0; + if (mask & INIT_0_R1) + res |= INIT_1_R0; + if (mask & INIT_1_R1) + res |= INIT_0_R0; return res; } - void handle_ff(Cell *cell) { - std::string type_str = cell->type.str(); - - FfType ff_type; - int ff_neg = 0; - SigSpec sig_d; - SigSpec sig_q; - SigSpec sig_c; - SigSpec sig_e; - SigSpec sig_r; - SigSpec sig_s; - bool has_srst = false; - - if (cell->hasPort(ID::D)) - sig_d = cell->getPort(ID::D); - if (cell->hasPort(ID::Q)) - sig_q = cell->getPort(ID::Q); - if (cell->hasPort(ID::C)) - sig_c = cell->getPort(ID::C); - if (cell->hasPort(ID::E)) - sig_e = cell->getPort(ID::E); - if (cell->hasPort(ID::R)) - sig_r = cell->getPort(ID::R); - if (cell->hasPort(ID::S)) - sig_s = cell->getPort(ID::S); - - if (type_str.substr(0, 5) == "$_SR_") { - ff_type = FF_SR; - if (type_str[5] == 'N') - ff_neg |= NEG_S; - if (type_str[6] == 'N') - ff_neg |= NEG_R; - } else if (type_str.substr(0, 6) == "$_DFF_" && type_str.size() == 8) { - ff_type = FF_DFF; - if (type_str[6] == 'N') - ff_neg |= NEG_C; - } else if (type_str.substr(0, 7) == "$_DFFE_" && type_str.size() == 10) { - ff_type = FF_DFFE; - if (type_str[7] == 'N') - ff_neg |= NEG_C; - if (type_str[8] == 'N') - ff_neg |= NEG_E; - } else if (type_str.substr(0, 6) == "$_DFF_" && type_str.size() == 10) { - ff_type = type_str[8] == '1' ? FF_ADFF1 : FF_ADFF0; - if (type_str[6] == 'N') - ff_neg |= NEG_C; - if (type_str[7] == 'N') - ff_neg |= NEG_R; - } else if (type_str.substr(0, 7) == "$_DFFE_" && type_str.size() == 12) { - ff_type = type_str[9] == '1' ? FF_ADFFE1 : FF_ADFFE0; - if (type_str[7] == 'N') - ff_neg |= NEG_C; - if (type_str[8] == 'N') - ff_neg |= NEG_R; - if (type_str[10] == 'N') - ff_neg |= NEG_E; - } else if (type_str.substr(0, 8) == "$_DFFSR_" && type_str.size() == 12) { - ff_type = FF_DFFSR; - if (type_str[8] == 'N') - ff_neg |= NEG_C; - if (type_str[9] == 'N') - ff_neg |= NEG_S; - if (type_str[10] == 'N') - ff_neg |= NEG_R; - } else if (type_str.substr(0, 9) == "$_DFFSRE_" && type_str.size() == 14) { - ff_type = FF_DFFSRE; - if (type_str[9] == 'N') - ff_neg |= NEG_C; - if (type_str[10] == 'N') - ff_neg |= NEG_S; - if (type_str[11] == 'N') - ff_neg |= NEG_R; - if (type_str[12] == 'N') - ff_neg |= NEG_E; - } else if (type_str.substr(0, 7) == "$_SDFF_" && type_str.size() == 11) { - ff_type = type_str[9] == '1' ? FF_SDFF1 : FF_SDFF0; - if (type_str[7] == 'N') - ff_neg |= NEG_C; - if (type_str[8] == 'N') - ff_neg |= NEG_R; - has_srst = true; - } else if (type_str.substr(0, 8) == "$_SDFFE_" && type_str.size() == 13) { - ff_type = type_str[10] == '1' ? FF_SDFFE1 : FF_SDFFE0; - if (type_str[8] == 'N') - ff_neg |= NEG_C; - if (type_str[9] == 'N') - ff_neg |= NEG_R; - if (type_str[11] == 'N') - ff_neg |= NEG_E; - has_srst = true; - } else if (type_str.substr(0, 9) == "$_SDFFCE_" && type_str.size() == 14) { - ff_type = type_str[11] == '1' ? FF_SDFFCE1 : FF_SDFFCE0; - if (type_str[9] == 'N') - ff_neg |= NEG_C; - if (type_str[10] == 'N') - ff_neg |= NEG_R; - if (type_str[12] == 'N') - ff_neg |= NEG_E; - has_srst = true; - } else if (type_str.substr(0, 9) == "$_DLATCH_" && type_str.size() == 11) { - ff_type = FF_DLATCH; - if (type_str[9] == 'N') - ff_neg |= NEG_E; - } else if (type_str.substr(0, 9) == "$_DLATCH_" && type_str.size() == 13) { - ff_type = type_str[11] == '1' ? FF_ADLATCH1 : FF_ADLATCH0; - if (type_str[9] == 'N') - ff_neg |= NEG_E; - if (type_str[10] == 'N') - ff_neg |= NEG_R; - } else if (type_str.substr(0, 11) == "$_DLATCHSR_" && type_str.size() == 15) { - ff_type = FF_DLATCHSR; - if (type_str[11] == 'N') - ff_neg |= NEG_E; - if (type_str[12] == 'N') - ff_neg |= NEG_S; - if (type_str[13] == 'N') - ff_neg |= NEG_R; + int get_ff_type(const FfData &ff) { + if (ff.has_clk) { + if (ff.has_sr) { + return ff.has_ce ? FF_DFFSRE : FF_DFFSR; + } else if (ff.has_arst) { + return ff.has_ce ? FF_ADFFE : FF_ADFF; + } else if (ff.has_aload) { + return ff.has_ce ? FF_ALDFFE : FF_ALDFF; + } else if (ff.has_srst) { + if (ff.has_ce) + return ff.ce_over_srst ? FF_SDFFCE : FF_SDFFE; + else + return FF_SDFF; + } else { + return ff.has_ce ? FF_DFFE : FF_DFF; + } } else { - log_warning("Ignoring unknown ff type %s [%s.%s].\n", log_id(cell->type), log_id(cell->module->name), log_id(cell->name)); - return; + if (ff.has_aload) { + if (ff.has_sr) + return FF_DLATCHSR; + else if (ff.has_arst) + return FF_ADLATCH; + else + return FF_DLATCH; + } else { + if (ff.has_sr) { + return FF_SR; + } else if (ff.has_arst) { + return FF_RLATCH; + } else { + log_assert(0); + return 0; + } + } + } + } + + int get_initmask(FfData &ff) { + int res = 0; + if (ff.val_init[0] == State::S0) + res = INIT_0; + else if (ff.val_init[0] == State::S1) + res = INIT_1; + else + res = INIT_X; + if (ff.has_arst) { + if (ff.val_arst[0] == State::S0) + res <<= 4; + else if (ff.val_arst[0] == State::S1) + res <<= 8; + } else if (ff.has_srst) { + if (ff.val_srst[0] == State::S0) + res <<= 4; + else if (ff.val_srst[0] == State::S1) + res <<= 8; + } + return res; + } + + void fail_ff(const FfData &ff, const char *reason) { + log_error("FF %s.%s (type %s) cannot be legalized: %s\n", log_id(ff.module->name), log_id(ff.cell->name), log_id(ff.cell->type), reason); + } + + bool try_flip(FfData &ff, int supported_mask) { + int initmask = get_initmask(ff); + if (supported_mask & initmask) + return true; + if (supported_mask & flip_initmask(initmask)) { + ff.flip_bits({0}); + return true; } + return false; + } - State initval = initvals(sig_q[0]); - - FfInit initmask = INIT_X; - if (initval == State::S0) - initmask = INIT_0; - else if (initval == State::S1) - initmask = INIT_1; - const char *reason; + void emulate_split_init_arst(FfData &ff) { + ff.remove(); - bool kill_ce = mince && GetSize(sig_c) && GetSize(sig_e) && sig_e[0].wire && ce_used[sig_e[0]] < mince; - bool kill_srst = minsrst && has_srst && sig_r[0].wire && srst_used[sig_r[0]] < minsrst; + FfData ff_dff(ff.module, &initvals, NEW_ID); + ff_dff.width = ff.width; + ff_dff.has_aload = ff.has_aload; + ff_dff.sig_aload = ff.sig_aload; + ff_dff.pol_aload = ff.pol_aload; + ff_dff.sig_ad = ff.sig_ad; + ff_dff.has_clk = ff.has_clk; + ff_dff.sig_clk = ff.sig_clk; + ff_dff.pol_clk = ff.pol_clk; + ff_dff.sig_d = ff.sig_d; + ff_dff.has_ce = ff.has_ce; + ff_dff.sig_ce = ff.sig_ce; + ff_dff.pol_ce = ff.pol_ce; + ff_dff.sig_q = ff.module->addWire(NEW_ID, ff.width); + ff_dff.val_init = ff.val_init; + ff_dff.is_fine = ff.is_fine; - while (!(supported_cells[ff_type] & initmask) || kill_ce || kill_srst) { - // Well, cell is not directly supported. Decide how to deal with it. + FfData ff_adff(ff.module, &initvals, NEW_ID); + ff_adff.width = ff.width; + ff_adff.has_aload = ff.has_aload; + ff_adff.sig_aload = ff.sig_aload; + ff_adff.pol_aload = ff.pol_aload; + ff_adff.sig_ad = ff.sig_ad; + ff_adff.has_clk = ff.has_clk; + ff_adff.sig_clk = ff.sig_clk; + ff_adff.pol_clk = ff.pol_clk; + ff_adff.sig_d = ff.sig_d; + ff_adff.has_ce = ff.has_ce; + ff_adff.sig_ce = ff.sig_ce; + ff_adff.pol_ce = ff.pol_ce; + ff_adff.sig_q = ff.module->addWire(NEW_ID, ff.width); + ff_adff.val_init = Const(State::Sx, ff.width); + ff_adff.has_arst = true; + ff_adff.sig_arst = ff.sig_arst; + ff_adff.pol_arst = ff.pol_arst; + ff_adff.val_arst = ff.val_arst; + ff_adff.is_fine = ff.is_fine; - if (ff_type == FF_DFF || ff_type == FF_DFFE) { - if (kill_ce) { - ff_type = FF_DFF; - goto unmap_enable; - } - if (!(supported_dff & initmask)) { - // This init value is not supported at all... - if (supported_dff & flip_initmask(initmask)) { - // The other one is, though. Negate D, Q, and init. -flip_dqi: - if (initval == State::S0) { - initval = State::S1; - initmask = INIT_1; - } else if (initval == State::S1) { - initval = State::S0; - initmask = INIT_0; - } - if (ff_type != FF_SR) - sig_d = cell->module->NotGate(NEW_ID, sig_d[0]); - SigBit new_q = SigSpec(cell->module->addWire(NEW_ID))[0]; - cell->module->addNotGate(NEW_ID, new_q, sig_q[0]); - initvals.remove_init(sig_q[0]); - initvals.set_init(new_q, initval); - sig_q = new_q; - continue; - } - if (!supported_dff) - reason = "dffs are not supported"; - else - reason = "initialized dffs are not supported"; - goto error; - } + FfData ff_sel(ff.module, &initvals, NEW_ID); + ff_sel.width = 1; + ff_sel.sig_q = ff.module->addWire(NEW_ID); + ff_sel.has_arst = true; + ff_sel.sig_arst = ff.sig_arst; + ff_sel.pol_arst = ff.pol_arst; + ff_sel.val_arst = State::S1; + ff_sel.val_init = State::S0; + ff_sel.is_fine = ff.is_fine; - // Some DFF is supported with this init val. Just pick a type. - if (ff_type == FF_DFF) { - // Try adding a set or reset pin. - for (auto new_type: {FF_SDFF0, FF_SDFF1, FF_ADFF0, FF_ADFF1}) - if (supported_cells[new_type] & initmask) { - ff_type = new_type; - sig_r = State::S0; - goto cell_ok; - } - // Try adding both. - if (supported_cells[FF_DFFSR] & initmask) { - ff_type = FF_DFFSR; - sig_r = State::S0; - sig_s = State::S0; - break; - } - // Nope. Will need to add enable and go the DFFE route. - sig_e = State::S1; - if (supported_cells[FF_DFFE] & initmask) { - ff_type = FF_DFFE; - break; - } - } - // Try adding a set or reset pin. - for (auto new_type: {FF_SDFFE0, FF_SDFFE1, FF_SDFFCE0, FF_SDFFCE1, FF_ADFFE0, FF_ADFFE1}) - if (supported_cells[new_type] & initmask) { - ff_type = new_type; - sig_r = State::S0; - goto cell_ok; - } - // Try adding both. - if (supported_cells[FF_DFFSRE] & initmask) { - ff_type = FF_DFFSRE; - sig_r = State::S0; - sig_s = State::S0; - break; - } + if (ff.is_fine) + ff.module->addMuxGate(NEW_ID, ff_dff.sig_q, ff_adff.sig_q, ff_sel.sig_q, ff.sig_q); + else + ff.module->addMux(NEW_ID, ff_dff.sig_q, ff_adff.sig_q, ff_sel.sig_q, ff.sig_q); + + legalize_ff(ff_dff); + legalize_ff(ff_adff); + legalize_ff(ff_sel); + } - // Seems that no DFFs with enable are supported. - // The enable input needs to be unmapped. - // This should not be reached if we started from plain DFF. - log_assert(ff_type == FF_DFFE); - ff_type = FF_DFF; -unmap_enable: - if (ff_neg & NEG_E) - sig_d = cell->module->MuxGate(NEW_ID, sig_d[0], sig_q[0], sig_e[0]); + void emulate_split_set_clr(FfData &ff) { + // No native DFFSR. However, if we can conjure + // a SR latch and ADFF, it can still be emulated. + int initmask = get_initmask(ff); + int flipmask = flip_initmask(initmask); + bool init_clr = true; + bool init_set = true; + State initsel = State::Sx; + int supported_arst = ff.has_clk ? supported_adff : supported_adlatch; + bool init_clr_ok = (supported_arst & initmask << 4) || (supported_arst & flipmask << 8); + bool init_set_ok = (supported_arst & initmask << 8) || (supported_arst & flipmask << 4); + if (init_clr_ok && init_set_ok && supported_sr) { + // OK + } else if (init_clr_ok && (supported_sr & INIT_0)) { + init_set = false; + initsel = State::S0; + } else if (init_set_ok && (supported_sr & INIT_1)) { + init_clr = false; + initsel = State::S1; + } else if (init_clr_ok && (supported_sr & INIT_1)) { + init_set = false; + initsel = State::S0; + } else if (init_set_ok && (supported_sr & INIT_0)) { + init_clr = false; + initsel = State::S1; + } else { + if (ff.has_clk) { + if (!supported_dffsr) + fail_ff(ff, "dffs with async set and reset are not supported"); else - sig_d = cell->module->MuxGate(NEW_ID, sig_q[0], sig_d[0], sig_e[0]); - ff_neg &= ~NEG_E; - sig_e = SigSpec(); - kill_ce = false; - // Now try again as plain DFF. - continue; - } else if (ff_type == FF_ADFF0 || ff_type == FF_ADFF1 || ff_type == FF_ADFFE0 || ff_type == FF_ADFFE1) { - bool has_set = ff_type == FF_ADFF1 || ff_type == FF_ADFFE1; - bool has_en = ff_type == FF_ADFFE0 || ff_type == FF_ADFFE1; - if (kill_ce) { - ff_type = has_set ? FF_ADFF1 : FF_ADFF0; - goto unmap_enable; - } - if (!has_en && (supported_cells[has_set ? FF_ADFFE1 : FF_ADFFE0] & initmask)) { - // Just add enable. - sig_e = State::S1; - ff_type = has_set ? FF_ADFFE1 : FF_ADFFE0; - break; - } - if (supported_cells[has_en ? FF_DFFSRE : FF_DFFSR] & initmask) { -adff_to_dffsr: - // Throw in a set/reset, retry in DFFSR/DFFSRE branch. - if (has_set) { - sig_s = sig_r; - sig_r = State::S0; - if (ff_neg & NEG_R) { - ff_neg &= ~NEG_R; - ff_neg |= NEG_S; - } - } else { - sig_s = State::S0; - } - if (has_en) - ff_type = FF_DFFSRE; - else - ff_type = FF_DFFSR; - continue; - } - if (has_en && (supported_cells[has_set ? FF_ADFF1 : FF_ADFF0] & initmask)) { - // Unmap enable. - ff_type = has_set ? FF_ADFF1 : FF_ADFF0; - goto unmap_enable; - } - if (supported_dffsr & initmask) { - goto adff_to_dffsr; - } - log_assert(!((has_set ? supported_adff1 : supported_adff0) & initmask)); - // Alright, so this particular combination of initval and - // resetval is not natively supported. First, try flipping - // them both to see whether this helps. - int flipmask = flip_initmask(initmask); - if ((has_set ? supported_adff0 : supported_adff1) & flipmask) { - // Checks out, do it. - ff_type = has_en ? (has_set ? FF_ADFFE0 : FF_ADFFE1) : (has_set ? FF_ADFF0 : FF_ADFF1); - goto flip_dqi; - } + fail_ff(ff, "initialized dffs with async set and reset are not supported"); + } else { + if (!supported_cells[FF_DLATCHSR]) + fail_ff(ff, "dlatch with async set and reset are not supported"); + else + fail_ff(ff, "initialized dlatch with async set and reset are not supported"); + } + } - if (!supported_adff0 && !supported_adff1) { - reason = "dffs with async set or reset are not supported"; - goto error; - } - if (!(supported_dff & ~INIT_X)) { - reason = "initialized dffs are not supported"; - goto error; - } - // If we got here, initialized dff is supported, but not this - // particular reset+init combination (nor its negation). - // The only hope left is breaking down to adff + dff + dlatch + mux. - if (!(supported_dlatch & ~INIT_X)) { - reason = "unsupported initial value and async reset value combination"; - goto error; - } + // If we have to unmap enable anyway, do it before breakdown. + if (ff.has_ce && !supported_cells[FF_ADFFE]) + ff.unmap_ce(); - // If we have to unmap enable anyway, do it before breakdown. - if (has_en && !supported_cells[FF_ADFFE0] && !supported_cells[FF_ADFFE1]) { - ff_type = has_set ? FF_ADFF1 : FF_ADFF0; - goto unmap_enable; - } + log_warning("Emulating async set + reset with several FFs and a mux for %s.%s\n", log_id(ff.module->name), log_id(ff.cell->name)); - log_warning("Emulating mismatched async reset and init with several FFs and a mux for %s.%s\n", log_id(cell->module->name), log_id(cell->name)); - initvals.remove_init(sig_q[0]); - Wire *adff_q = cell->module->addWire(NEW_ID); - Wire *dff_q = cell->module->addWire(NEW_ID); - Wire *sel_q = cell->module->addWire(NEW_ID); - initvals.set_init(SigBit(dff_q, 0), initval); - initvals.set_init(SigBit(sel_q, 0), State::S0); - Cell *cell_dff; - Cell *cell_adff; - Cell *cell_sel; - if (!has_en) { - cell_dff = cell->module->addDffGate(NEW_ID, sig_c, sig_d, dff_q, !(ff_neg & NEG_C)); - cell_adff = cell->module->addAdffGate(NEW_ID, sig_c, sig_r, sig_d, adff_q, has_set, !(ff_neg & NEG_C), !(ff_neg & NEG_R)); - } else { - cell_dff = cell->module->addDffeGate(NEW_ID, sig_c, sig_e, sig_d, dff_q, !(ff_neg & NEG_C), !(ff_neg & NEG_E)); - cell_adff = cell->module->addAdffeGate(NEW_ID, sig_c, sig_e, sig_r, sig_d, adff_q, has_set, !(ff_neg & NEG_C), !(ff_neg & NEG_E), !(ff_neg & NEG_R)); - } - cell_sel = cell->module->addDlatchGate(NEW_ID, sig_r, State::S1, sel_q, !(ff_neg & NEG_R)); - cell->module->addMuxGate(NEW_ID, dff_q, adff_q, sel_q, sig_q); - - // Bye, cell. - cell->module->remove(cell); - handle_ff(cell_dff); - handle_ff(cell_adff); - handle_ff(cell_sel); - return; - } else if (ff_type == FF_DFFSR || ff_type == FF_DFFSRE) { - if (kill_ce) { - ff_type = FF_DFFSR; - goto unmap_enable; - } - // First, see if mapping/unmapping enable will help. - if (supported_cells[FF_DFFSRE] & initmask) { - ff_type = FF_DFFSRE; - sig_e = State::S1; - break; - } - if (supported_cells[FF_DFFSR] & initmask) { - ff_type = FF_DFFSR; - goto unmap_enable; - } - if (supported_dffsr & flip_initmask(initmask)) { -flip_dqisr:; - log_warning("Flipping D/Q/init and inserting set/reset fixup to handle init value on %s.%s [%s]\n", log_id(cell->module->name), log_id(cell->name), log_id(cell->type)); - SigSpec new_r; - bool neg_r = (ff_neg & NEG_R); - bool neg_s = (ff_neg & NEG_S); - if (!(ff_neg & NEG_S)) { - if (!(ff_neg & NEG_R)) - new_r = cell->module->AndnotGate(NEW_ID, sig_s, sig_r); - else - new_r = cell->module->AndGate(NEW_ID, sig_s, sig_r); - } else { - if (!(ff_neg & NEG_R)) - new_r = cell->module->OrGate(NEW_ID, sig_s, sig_r); - else - new_r = cell->module->OrnotGate(NEW_ID, sig_s, sig_r); - } - ff_neg &= ~(NEG_R | NEG_S); - if (neg_r) - ff_neg |= NEG_S; - if (neg_s) - ff_neg |= NEG_R; - sig_s = sig_r; - sig_r = new_r; - goto flip_dqi; - } - // No native DFFSR. However, if we can conjure - // a SR latch and ADFF, it can still be emulated. - int flipmask = flip_initmask(initmask); - bool init0 = true; - bool init1 = true; - State initsel = State::Sx; - if (((supported_adff0 & initmask) || (supported_adff1 & flipmask)) && ((supported_adff1 & initmask) || (supported_adff0 & flipmask)) && supported_sr) { - // OK - } else if (((supported_adff0 & initmask) || (supported_adff1 & flipmask)) && (supported_sr & INIT_0)) { - init1 = false; - initsel = State::S0; - } else if (((supported_adff1 & initmask) || (supported_adff0 & flipmask)) && (supported_sr & INIT_1)) { - init0 = false; - initsel = State::S1; - } else if (((supported_adff0 & initmask) || (supported_adff1 & flipmask)) && (supported_sr & INIT_1)) { - init1 = false; - initsel = State::S0; - } else if (((supported_adff1 & initmask) || (supported_adff0 & flipmask)) && (supported_sr & INIT_0)) { - init0 = false; - initsel = State::S1; - } else { - if (!supported_dffsr) - reason = "dffs with async set and reset are not supported"; - else - reason = "initialized dffs with async set and reset are not supported"; - goto error; - } + log_assert(ff.width == 1); + ff.remove(); - // If we have to unmap enable anyway, do it before breakdown. - if (ff_type == FF_DFFSRE && !supported_cells[FF_ADFFE0] && !supported_cells[FF_ADFFE1]) { - ff_type = FF_DFFSR; - goto unmap_enable; - } + FfData ff_clr(ff.module, &initvals, NEW_ID); + ff_clr.width = ff.width; + ff_clr.has_aload = ff.has_aload; + ff_clr.sig_aload = ff.sig_aload; + ff_clr.pol_aload = ff.pol_aload; + ff_clr.sig_ad = ff.sig_ad; + ff_clr.has_clk = ff.has_clk; + ff_clr.sig_clk = ff.sig_clk; + ff_clr.pol_clk = ff.pol_clk; + ff_clr.sig_d = ff.sig_d; + ff_clr.has_ce = ff.has_ce; + ff_clr.sig_ce = ff.sig_ce; + ff_clr.pol_ce = ff.pol_ce; + ff_clr.has_arst = true; + ff_clr.sig_arst = ff.sig_clr; + ff_clr.pol_arst = ff.pol_clr; + ff_clr.val_arst = Const(State::S0, ff.width); + ff_clr.sig_q = ff.module->addWire(NEW_ID, ff.width); + ff_clr.val_init = init_clr ? ff.val_init : Const(State::Sx, ff.width); + ff_clr.is_fine = ff.is_fine; - log_warning("Emulating async set + reset with several FFs and a mux for %s.%s\n", log_id(cell->module->name), log_id(cell->name)); - initvals.remove_init(sig_q[0]); - Wire *adff0_q = cell->module->addWire(NEW_ID); - Wire *adff1_q = cell->module->addWire(NEW_ID); - Wire *sel_q = cell->module->addWire(NEW_ID); - if (init0) - initvals.set_init(SigBit(adff0_q, 0), initval); - if (init1) - initvals.set_init(SigBit(adff1_q, 0), initval); - initvals.set_init(SigBit(sel_q, 0), initsel); - Cell *cell_adff0; - Cell *cell_adff1; - Cell *cell_sel; - if (ff_type == FF_DFFSR) { - cell_adff0 = cell->module->addAdffGate(NEW_ID, sig_c, sig_r, sig_d, adff0_q, false, !(ff_neg & NEG_C), !(ff_neg & NEG_R)); - cell_adff1 = cell->module->addAdffGate(NEW_ID, sig_c, sig_s, sig_d, adff1_q, true, !(ff_neg & NEG_C), !(ff_neg & NEG_S)); - } else { - cell_adff0 = cell->module->addAdffeGate(NEW_ID, sig_c, sig_e, sig_r, sig_d, adff0_q, false, !(ff_neg & NEG_C), !(ff_neg & NEG_E), !(ff_neg & NEG_R)); - cell_adff1 = cell->module->addAdffeGate(NEW_ID, sig_c, sig_e, sig_s, sig_d, adff1_q, true, !(ff_neg & NEG_C), !(ff_neg & NEG_E), !(ff_neg & NEG_S)); - } - cell_sel = cell->module->addSrGate(NEW_ID, sig_s, sig_r, sel_q, !(ff_neg & NEG_S), !(ff_neg & NEG_R)); - cell->module->addMuxGate(NEW_ID, adff0_q, adff1_q, sel_q, sig_q); - - // Bye, cell. - cell->module->remove(cell); - handle_ff(cell_adff0); - handle_ff(cell_adff1); - handle_ff(cell_sel); + FfData ff_set(ff.module, &initvals, NEW_ID); + ff_set.width = ff.width; + ff_set.has_aload = ff.has_aload; + ff_set.sig_aload = ff.sig_aload; + ff_set.pol_aload = ff.pol_aload; + ff_set.sig_ad = ff.sig_ad; + ff_set.has_clk = ff.has_clk; + ff_set.sig_clk = ff.sig_clk; + ff_set.pol_clk = ff.pol_clk; + ff_set.sig_d = ff.sig_d; + ff_set.has_ce = ff.has_ce; + ff_set.sig_ce = ff.sig_ce; + ff_set.pol_ce = ff.pol_ce; + ff_set.has_arst = true; + ff_set.sig_arst = ff.sig_set; + ff_set.pol_arst = ff.pol_set; + ff_set.val_arst = Const(State::S1, ff.width); + ff_set.sig_q = ff.module->addWire(NEW_ID, ff.width); + ff_set.val_init = init_set ? ff.val_init : Const(State::Sx, ff.width); + ff_set.is_fine = ff.is_fine; + + FfData ff_sel(ff.module, &initvals, NEW_ID); + ff_sel.width = ff.width; + ff_sel.has_sr = true; + ff_sel.pol_clr = ff.pol_clr; + ff_sel.pol_set = ff.pol_set; + ff_sel.sig_clr = ff.sig_clr; + ff_sel.sig_set = ff.sig_set; + ff_sel.sig_q = ff.module->addWire(NEW_ID, ff.width); + ff_sel.val_init = Const(initsel, ff.width); + ff_sel.is_fine = ff.is_fine; + + if (!ff.is_fine) + ff.module->addMux(NEW_ID, ff_clr.sig_q, ff_set.sig_q, ff_sel.sig_q, ff.sig_q); + else + ff.module->addMuxGate(NEW_ID, ff_clr.sig_q, ff_set.sig_q, ff_sel.sig_q, ff.sig_q); + + legalize_ff(ff_clr); + legalize_ff(ff_set); + legalize_ff(ff_sel); + } + + void legalize_dff(FfData &ff) { + if (!try_flip(ff, supported_dff)) { + if (!supported_dff) + fail_ff(ff, "D flip-flops are not supported"); + else + fail_ff(ff, "initialized D flip-flops are not supported"); + } + + int initmask = get_initmask(ff); + // Some DFF is supported with this init val. Just pick a type. + if (ff.has_ce && !(supported_dffe & initmask)) { + ff.unmap_ce(); + } + + if (!ff.has_ce) { + if (supported_cells[FF_DFF] & initmask) { + legalize_finish(ff); return; - } else if (ff_type == FF_SR) { - if (supported_cells[FF_ADLATCH0] & initmask || supported_cells[FF_ADLATCH1] & flip_initmask(initmask)) { - // Convert to ADLATCH0. May get converted to ADLATCH1. - ff_type = FF_ADLATCH0; - sig_e = sig_s; - sig_d = State::S1; - if (ff_neg & NEG_S) { - ff_neg &= ~NEG_S; - ff_neg |= NEG_E; - } - continue; - } else if (supported_cells[FF_DLATCHSR] & initmask) { - // Upgrade to DLATCHSR. - ff_type = FF_DLATCHSR; - sig_e = State::S0; - sig_d = State::Sx; - break; - } else if (supported_dffsr & initmask) { - // Upgrade to DFFSR. May get further upgraded to DFFSRE. - ff_type = FF_DFFSR; - sig_c = State::S0; - sig_d = State::Sx; - continue; - } else if (supported_sr & flip_initmask(initmask)) { - goto flip_dqisr; - } else { - if (!supported_sr) - reason = "sr latches are not supported"; - else - reason = "initialized sr latches are not supported"; - goto error; - } - } else if (ff_type == FF_DLATCH) { - if (!(supported_dlatch & initmask)) { - // This init value is not supported at all... - if (supported_dlatch & flip_initmask(initmask)) - goto flip_dqi; - - if ((sig_d == State::S0 && (supported_adff0 & initmask)) || - (sig_d == State::S1 && (supported_adff1 & initmask)) || - (sig_d == State::S0 && (supported_adff1 & flip_initmask(initmask))) || - (sig_d == State::S1 && (supported_adff0 & flip_initmask(initmask))) - ) { - // Special case: const-D dlatch can be converted into adff with const clock. - ff_type = (sig_d == State::S0) ? FF_ADFF0 : FF_ADFF1; - if (ff_neg & NEG_E) { - ff_neg &= ~NEG_E; - ff_neg |= NEG_R; - } - sig_r = sig_e; - sig_d = State::Sx; - sig_c = State::S1; - continue; - } + } + // Try adding a set or reset pin. + if (supported_cells[FF_SDFF] & initmask) { + ff.add_dummy_srst(); + legalize_finish(ff); + return; + } + if (supported_cells[FF_ADFF] & initmask) { + ff.add_dummy_arst(); + legalize_finish(ff); + return; + } + // Try adding async load. + if (supported_cells[FF_ALDFF] & initmask) { + ff.add_dummy_aload(); + legalize_finish(ff); + return; + } + // Try adding both. + if (supported_cells[FF_DFFSR] & initmask) { + ff.add_dummy_sr(); + legalize_finish(ff); + return; + } + // Nope. Will need to add enable and go the DFFE route. + ff.add_dummy_ce(); + } + if (supported_cells[FF_DFFE] & initmask) { + legalize_finish(ff); + return; + } + // Try adding a set or reset pin. + if (supported_cells[FF_SDFFCE] & initmask) { + ff.add_dummy_srst(); + ff.ce_over_srst = true; + legalize_finish(ff); + return; + } + if (supported_cells[FF_SDFFE] & initmask) { + ff.add_dummy_srst(); + legalize_finish(ff); + return; + } + if (supported_cells[FF_ADFFE] & initmask) { + ff.add_dummy_arst(); + legalize_finish(ff); + return; + } + if (supported_cells[FF_ALDFFE] & initmask) { + ff.add_dummy_aload(); + legalize_finish(ff); + return; + } + // Try adding both. + if (supported_cells[FF_DFFSRE] & initmask) { + ff.add_dummy_sr(); + legalize_finish(ff); + return; + } + log_assert(0); + } - if (!supported_dlatch) - reason = "dlatch are not supported"; - else - reason = "initialized dlatch are not supported"; - goto error; - } + void legalize_sdffce(FfData &ff) { + if (!try_flip(ff, supported_cells[FF_SDFFCE] | supported_cells[FF_SDFFE])) { + ff.unmap_srst(); + legalize_dff(ff); + return; + } - // Some DLATCH is supported with this init val. Just pick a type. - if (supported_cells[FF_ADLATCH0] & initmask) { - ff_type = FF_ADLATCH0; - sig_r = State::S0; - break; - } - if (supported_cells[FF_ADLATCH1] & initmask) { - ff_type = FF_ADLATCH1; - sig_r = State::S0; - break; - } - if (supported_cells[FF_DLATCHSR] & initmask) { - ff_type = FF_DLATCHSR; - sig_r = State::S0; - sig_s = State::S0; - break; - } + int initmask = get_initmask(ff); + if (supported_cells[FF_SDFFCE] & initmask) { + // OK + } else if (supported_cells[FF_SDFFE] & initmask) { + ff.convert_ce_over_srst(false); + } else { + log_assert(0); + } + legalize_finish(ff); + } + + void legalize_sdff(FfData &ff) { + if (!try_flip(ff, supported_sdff)) { + ff.unmap_srst(); + legalize_dff(ff); + return; + } + + int initmask = get_initmask(ff); + if (!ff.has_ce) { + if (supported_cells[FF_SDFF] & initmask) { + // OK + } else if (supported_cells[FF_SDFFE] & initmask) { + ff.add_dummy_ce(); + } else if (supported_cells[FF_SDFFCE] & initmask) { + ff.add_dummy_ce(); + ff.ce_over_srst = true; + } else { log_assert(0); - } else if (ff_type == FF_ADLATCH0 || ff_type == FF_ADLATCH1) { - if (supported_cells[FF_DLATCHSR] & initmask) { - if (ff_type == FF_ADLATCH1) { - sig_s = sig_r; - sig_r = State::S0; - if (ff_neg & NEG_R) { - ff_neg &= ~NEG_R; - ff_neg |= NEG_S; - } - } else { - sig_s = State::S0; - } - ff_type = FF_DLATCHSR; - break; - } - FfType flip_type = ff_type == FF_ADLATCH0 ? FF_ADLATCH1 : FF_ADLATCH0; - if ((supported_cells[flip_type] | supported_cells[FF_DLATCHSR]) & flip_initmask(initmask)) { - ff_type = flip_type; - goto flip_dqi; - } + } + } else { + log_assert(!ff.ce_over_srst); + if (supported_cells[FF_SDFFE] & initmask) { + // OK + } else if (supported_cells[FF_SDFFCE] & initmask) { + ff.convert_ce_over_srst(true); + } else if (supported_cells[FF_SDFF] & initmask) { + ff.unmap_ce(); + } else { + log_assert(0); + } + } + legalize_finish(ff); + } - if (!supported_cells[FF_ADLATCH0] && !supported_cells[FF_ADLATCH1] && !supported_cells[FF_DLATCHSR]) { - reason = "dlatch with async set or reset are not supported"; - goto error; - } - if (!(supported_dlatch & ~INIT_X)) { - reason = "initialized dlatch are not supported"; - goto error; - } + void legalize_adff(FfData &ff) { + if (!try_flip(ff, supported_adff)) { + if (!supported_adff) + fail_ff(ff, "dffs with async set or reset are not supported"); + if (!(supported_dff & (INIT_0 | INIT_1))) + fail_ff(ff, "initialized dffs are not supported"); - if (!(supported_dlatch & ~INIT_X)) { - reason = "initialized dlatch are not supported"; - goto error; - } - // If we got here, initialized dlatch is supported, but not this - // particular reset+init combination (nor its negation). - // The only hope left is breaking down to adff + dff + dlatch + mux. - - log_warning("Emulating mismatched async reset and init with several latches and a mux for %s.%s\n", log_id(cell->module->name), log_id(cell->name)); - initvals.remove_init(sig_q[0]); - Wire *adlatch_q = cell->module->addWire(NEW_ID); - Wire *dlatch_q = cell->module->addWire(NEW_ID); - Wire *sel_q = cell->module->addWire(NEW_ID); - initvals.set_init(SigBit(dlatch_q, 0), initval); - initvals.set_init(SigBit(sel_q, 0), State::S0); - Cell *cell_dlatch; - Cell *cell_adlatch; - Cell *cell_sel; - cell_dlatch = cell->module->addDlatchGate(NEW_ID, sig_e, sig_d, dlatch_q, !(ff_neg & NEG_E)); - cell_adlatch = cell->module->addAdlatchGate(NEW_ID, sig_e, sig_r, sig_d, adlatch_q, ff_type == FF_ADLATCH1, !(ff_neg & NEG_E), !(ff_neg & NEG_R)); - cell_sel = cell->module->addDlatchGate(NEW_ID, sig_r, State::S1, sel_q, !(ff_neg & NEG_R)); - cell->module->addMuxGate(NEW_ID, dlatch_q, adlatch_q, sel_q, sig_q); - - // Bye, cell. - cell->module->remove(cell); - handle_ff(cell_dlatch); - handle_ff(cell_adlatch); - handle_ff(cell_sel); + // If we got here, initialized dff is supported, but not this + // particular reset+init combination (nor its negation). + // The only hope left is breaking down to adff + dff + dlatch + mux. + + if (!((supported_rlatch) & (INIT_0_R1 | INIT_1_R0))) + fail_ff(ff, "unsupported initial value and async reset value combination"); + + // If we have to unmap enable anyway, do it before breakdown. + if (ff.has_ce && !supported_cells[FF_ADFFE]) + ff.unmap_ce(); + + if (ff.cell) + log_warning("Emulating mismatched async reset and init with several FFs and a mux for %s.%s\n", log_id(ff.module->name), log_id(ff.cell->name)); + emulate_split_init_arst(ff); + return; + } + + int initmask = get_initmask(ff); + if (ff.has_ce && !(supported_adffe & initmask)) { + ff.unmap_ce(); + } + + if (!ff.has_ce) { + if (supported_cells[FF_ADFF] & initmask) { + legalize_finish(ff); return; - } else if (ff_type == FF_DLATCHSR) { - if (supported_cells[FF_DLATCHSR] & flip_initmask(initmask)) { - goto flip_dqisr; - } - // No native DFFSR. However, if we can conjure - // a SR latch and ADFF, it can still be emulated. - int flipmask = flip_initmask(initmask); - bool init0 = true; - bool init1 = true; - State initsel = State::Sx; - if (((supported_cells[FF_ADLATCH0] & initmask) || (supported_cells[FF_ADLATCH1] & flipmask)) && ((supported_cells[FF_ADLATCH1] & initmask) || (supported_cells[FF_ADLATCH0] & flipmask)) && supported_sr) { - // OK - } else if (((supported_cells[FF_ADLATCH0] & initmask) || (supported_cells[FF_ADLATCH1] & flipmask)) && (supported_sr & INIT_0)) { - init1 = false; - initsel = State::S0; - } else if (((supported_cells[FF_ADLATCH1] & initmask) || (supported_cells[FF_ADLATCH0] & flipmask)) && (supported_sr & INIT_1)) { - init0 = false; - initsel = State::S1; - } else if (((supported_cells[FF_ADLATCH0] & initmask) || (supported_cells[FF_ADLATCH1] & flipmask)) && (supported_sr & INIT_1)) { - init1 = false; - initsel = State::S0; - } else if (((supported_cells[FF_ADLATCH1] & initmask) || (supported_cells[FF_ADLATCH0] & flipmask)) && (supported_sr & INIT_0)) { - init0 = false; - initsel = State::S1; - } else { - if (!supported_cells[FF_DLATCHSR]) - reason = "dlatch with async set and reset are not supported"; - else - reason = "initialized dlatch with async set and reset are not supported"; - goto error; - } + } + // Try converting to async load. + if (supported_cells[FF_ALDFF] & initmask) { + ff.arst_to_aload(); + legalize_finish(ff); + return; + } + // Try convertint to SR. + if (supported_cells[FF_DFFSR] & initmask) { + ff.arst_to_sr(); + legalize_finish(ff); + return; + } + ff.add_dummy_ce(); + } + if (supported_cells[FF_ADFFE] & initmask) { + legalize_finish(ff); + return; + } + // Try converting to async load. + if (supported_cells[FF_ALDFFE] & initmask) { + ff.arst_to_aload(); + legalize_finish(ff); + return; + } + // Try convertint to SR. + if (supported_cells[FF_DFFSRE] & initmask) { + ff.arst_to_sr(); + legalize_finish(ff); + return; + } + log_assert(0); + } + + void legalize_aldff(FfData &ff) { + if (!try_flip(ff, supported_aldff)) { + ff.aload_to_sr(); + emulate_split_set_clr(ff); + return; + } + + int initmask = get_initmask(ff); + if (ff.has_ce && !(supported_aldffe & initmask)) { + ff.unmap_ce(); + } - log_warning("Emulating async set + reset with several latches and a mux for %s.%s\n", log_id(cell->module->name), log_id(cell->name)); - initvals.remove_init(sig_q[0]); - Wire *adlatch0_q = cell->module->addWire(NEW_ID); - Wire *adlatch1_q = cell->module->addWire(NEW_ID); - Wire *sel_q = cell->module->addWire(NEW_ID); - if (init0) - initvals.set_init(SigBit(adlatch0_q, 0), initval); - if (init1) - initvals.set_init(SigBit(adlatch1_q, 0), initval); - initvals.set_init(SigBit(sel_q, 0), initsel); - Cell *cell_adlatch0; - Cell *cell_adlatch1; - Cell *cell_sel; - cell_adlatch0 = cell->module->addAdlatchGate(NEW_ID, sig_e, sig_r, sig_d, adlatch0_q, false, !(ff_neg & NEG_E), !(ff_neg & NEG_R)); - cell_adlatch1 = cell->module->addAdlatchGate(NEW_ID, sig_e, sig_s, sig_d, adlatch1_q, true, !(ff_neg & NEG_E), !(ff_neg & NEG_S)); - cell_sel = cell->module->addSrGate(NEW_ID, sig_s, sig_r, sel_q, !(ff_neg & NEG_S), !(ff_neg & NEG_R)); - cell->module->addMuxGate(NEW_ID, adlatch0_q, adlatch1_q, sel_q, sig_q); - - // Bye, cell. - cell->module->remove(cell); - handle_ff(cell_adlatch0); - handle_ff(cell_adlatch1); - handle_ff(cell_sel); + if (!ff.has_ce) { + if (supported_cells[FF_ALDFF] & initmask) { + legalize_finish(ff); return; - } else if (ff_type == FF_SDFF0 || ff_type == FF_SDFF1 || ff_type == FF_SDFFE0 || ff_type == FF_SDFFE1 || ff_type == FF_SDFFCE0 || ff_type == FF_SDFFCE1) { - bool has_set = ff_type == FF_SDFF1 || ff_type == FF_SDFFE1 || ff_type == FF_SDFFCE1; - bool has_en = ff_type == FF_SDFFE0 || ff_type == FF_SDFFE1; - bool has_ce = ff_type == FF_SDFFCE0 || ff_type == FF_SDFFCE1; - - if (has_en) { - if (kill_ce || kill_srst) { - ff_type = has_set ? FF_SDFF1 : FF_SDFF0; - goto unmap_enable; - } - } else if (has_ce) { - if (kill_ce || kill_srst) - goto unmap_srst; - } else { - log_assert(!kill_ce); - if (kill_srst) - goto unmap_srst; - } + } + if (supported_cells[FF_DFFSR] & initmask) { + ff.aload_to_sr(); + legalize_finish(ff); + return; + } + ff.add_dummy_ce(); + } + if (supported_cells[FF_ALDFFE] & initmask) { + legalize_finish(ff); + return; + } + if (supported_cells[FF_DFFSRE] & initmask) { + ff.aload_to_sr(); + legalize_finish(ff); + return; + } + log_assert(0); + } - if (!has_ce) { - if (!has_en && (supported_cells[has_set ? FF_SDFFE1 : FF_SDFFE0] & initmask)) { - // Just add enable. - sig_e = State::S1; - ff_type = has_set ? FF_SDFFE1 : FF_SDFFE0; - break; - } - if (!has_en && (supported_cells[has_set ? FF_SDFFCE1 : FF_SDFFCE0] & initmask)) { - // Just add enable. - sig_e = State::S1; - ff_type = has_set ? FF_SDFFCE1 : FF_SDFFCE0; - break; - } - if (has_en && (supported_cells[has_set ? FF_SDFFCE1 : FF_SDFFCE0] & initmask)) { - // Convert sdffe to sdffce - if (!(ff_neg & NEG_E)) { - if (!(ff_neg & NEG_R)) - sig_e = cell->module->OrGate(NEW_ID, sig_e, sig_r); - else - sig_e = cell->module->OrnotGate(NEW_ID, sig_e, sig_r); - } else { - if (!(ff_neg & NEG_R)) - sig_e = cell->module->AndnotGate(NEW_ID, sig_e, sig_r); - else - sig_e = cell->module->AndGate(NEW_ID, sig_e, sig_r); - } - ff_type = has_set ? FF_SDFFCE1 : FF_SDFFCE0; - break; - } - if (has_en && (supported_cells[has_set ? FF_SDFF1 : FF_SDFF0] & initmask)) { - // Unmap enable. - ff_type = has_set ? FF_SDFF1 : FF_SDFF0; - goto unmap_enable; - } - log_assert(!((has_set ? supported_sdff1 : supported_sdff0) & initmask)); - } else { - if ((has_set ? supported_sdff1 : supported_sdff0) & initmask) { - // Convert sdffce to sdffe, which may be further converted to sdff. - if (!(ff_neg & NEG_R)) { - if (!(ff_neg & NEG_E)) - sig_r = cell->module->AndGate(NEW_ID, sig_r, sig_e); - else - sig_r = cell->module->AndnotGate(NEW_ID, sig_r, sig_e); - } else { - if (!(ff_neg & NEG_E)) - sig_r = cell->module->OrnotGate(NEW_ID, sig_r, sig_e); - else - sig_r = cell->module->OrGate(NEW_ID, sig_r, sig_e); - } - ff_type = has_set ? FF_SDFFE1 : FF_SDFFE0; - continue; - } + void legalize_dffsr(FfData &ff) { + if (!try_flip(ff, supported_dffsr)) { + emulate_split_set_clr(ff); + return; + } + + int initmask = get_initmask(ff); + if (ff.has_ce && !(supported_cells[FF_DFFSRE] & initmask)) { + ff.unmap_ce(); + } + + if (!ff.has_ce) { + if (supported_cells[FF_DFFSR] & initmask) { + legalize_finish(ff); + return; + } + ff.add_dummy_ce(); + } + + log_assert(supported_cells[FF_DFFSRE] & initmask); + legalize_finish(ff); + } + + void legalize_dlatch(FfData &ff) { + if (!try_flip(ff, supported_dlatch)) { + if (!supported_dlatch) + fail_ff(ff, "D latches are not supported"); + else + fail_ff(ff, "initialized D latches are not supported"); + } + + int initmask = get_initmask(ff); + // Some DLATCH is supported with this init val. Just pick a type. + if (supported_cells[FF_DLATCH] & initmask) { + legalize_finish(ff); + } else if (supported_cells[FF_ADLATCH] & initmask) { + ff.add_dummy_arst(); + legalize_finish(ff); + } else if (supported_cells[FF_DLATCHSR] & initmask) { + ff.add_dummy_sr(); + legalize_finish(ff); + } else if (supported_cells[FF_ALDFF] & initmask) { + ff.add_dummy_clk(); + legalize_finish(ff); + } else if (supported_cells[FF_ALDFFE] & initmask) { + ff.add_dummy_clk(); + ff.add_dummy_ce(); + legalize_finish(ff); + } else if (supported_sr & initmask) { + ff.aload_to_sr(); + legalize_sr(ff); + } else { + log_assert(0); + } + } + + void legalize_adlatch(FfData &ff) { + if (!try_flip(ff, supported_adlatch)) { + if (!supported_adlatch) + fail_ff(ff, "D latches with async set or reset are not supported"); + if (!(supported_dlatch & (INIT_0 | INIT_1))) + fail_ff(ff, "initialized D latches are not supported"); + + // If we got here, initialized dlatch is supported, but not this + // particular reset+init combination (nor its negation). + // The only hope left is breaking down to adlatch + dlatch + dlatch + mux. + + if (ff.cell) + log_warning("Emulating mismatched async reset and init with several latches and a mux for %s.%s\n", log_id(ff.module->name), log_id(ff.cell->name)); + ff.remove(); + + emulate_split_init_arst(ff); + return; + } + int initmask = get_initmask(ff); + if (supported_cells[FF_ADLATCH] & initmask) { + // OK + } else if (supported_cells[FF_DLATCHSR] & initmask) { + ff.arst_to_sr(); + } else { + log_assert(0); + } + legalize_finish(ff); + } + + void legalize_dlatchsr(FfData &ff) { + if (!try_flip(ff, supported_cells[FF_DLATCHSR])) { + emulate_split_set_clr(ff); + return; + } + legalize_finish(ff); + } + + void legalize_rlatch(FfData &ff) { + if (!try_flip(ff, supported_rlatch)) { + if (!supported_dlatch) + fail_ff(ff, "D latches are not supported"); + else + fail_ff(ff, "initialized D latches are not supported"); + } + + int initmask = get_initmask(ff); + if (((supported_dlatch_plain & 7) * 0x111) & initmask) { + ff.arst_to_aload(); + legalize_dlatch(ff); + } else if (supported_sr & initmask) { + ff.arst_to_sr(); + legalize_sr(ff); + } else if (supported_adff & initmask) { + ff.add_dummy_clk(); + legalize_adff(ff); + } else { + log_assert(0); + } + } + + void legalize_sr(FfData &ff) { + if (!try_flip(ff, supported_sr)) { + if (!supported_sr) + fail_ff(ff, "sr latches are not supported"); + else + fail_ff(ff, "initialized sr latches are not supported"); + } + int initmask = get_initmask(ff); + if (supported_cells[FF_SR] & initmask) { + // OK + } else if (supported_cells[FF_DLATCHSR] & initmask) { + // Upgrade to DLATCHSR. + ff.add_dummy_aload(); + } else if (supported_cells[FF_DFFSR] & initmask) { + // Upgrade to DFFSR. + ff.add_dummy_clk(); + } else if (supported_cells[FF_DFFSRE] & initmask) { + // Upgrade to DFFSRE. + ff.add_dummy_clk(); + ff.add_dummy_ce(); + } else if (supported_cells[FF_ADLATCH] & (initmask << 4)) { + ff.has_sr = false; + ff.has_aload = true; + ff.has_arst = true; + ff.pol_arst = ff.pol_clr; + ff.sig_arst = ff.sig_clr; + ff.sig_aload = ff.sig_set; + ff.pol_aload = ff.pol_set; + ff.sig_ad = State::S1; + ff.val_arst = State::S0; + } else if (supported_cells[FF_ADLATCH] & (flip_initmask(initmask) << 8)) { + ff.has_sr = false; + ff.has_aload = true; + ff.has_arst = true; + ff.pol_arst = ff.pol_clr; + ff.sig_arst = ff.sig_clr; + ff.sig_aload = ff.sig_set; + ff.pol_aload = ff.pol_set; + ff.sig_ad = State::S0; + ff.val_arst = State::S1; + ff.remove_init(); + Wire *new_q = ff.module->addWire(NEW_ID); + if (ff.is_fine) + ff.module->addNotGate(NEW_ID, new_q, ff.sig_q); + else + ff.module->addNot(NEW_ID, new_q, ff.sig_q); + ff.sig_q = new_q; + if (ff.val_init == State::S0) + ff.val_init = State::S1; + else if (ff.val_init == State::S1) + ff.val_init = State::S0; + } else { + log_assert(0); + } + legalize_finish(ff); + } + + void fixup_reset_x(FfData &ff, int supported) { + for (int i = 0; i < ff.width; i++) { + int mask; + if (ff.val_init[i] == State::S0) + mask = INIT_0; + else if (ff.val_init[i] == State::S1) + mask = INIT_1; + else + mask = INIT_X; + if (ff.has_arst) { + if (ff.val_arst[i] == State::Sx) { + if (!(supported & (mask << 8))) + ff.val_arst[i] = State::S0; + if (!(supported & (mask << 4))) + ff.val_arst[i] = State::S1; } - // Alright, so this particular combination of initval and - // resetval is not natively supported. First, try flipping - // them both to see whether this helps. - if ((has_set ? supported_sdff0 : supported_sdff1) & flip_initmask(initmask)) { - // Checks out, do it. - ff_type = has_ce ? (has_set ? FF_SDFFCE0 : FF_SDFFCE1) : has_en ? (has_set ? FF_SDFFE0 : FF_SDFFE1) : (has_set ? FF_SDFF0 : FF_SDFF1); - goto flip_dqi; + } + if (ff.has_srst) { + if (ff.val_srst[i] == State::Sx) { + if (!(supported & (mask << 8))) + ff.val_srst[i] = State::S0; + if (!(supported & (mask << 4))) + ff.val_srst[i] = State::S1; } + } + } + } - // Nope. No way to get SDFF* of the right kind, so unmap it. - // For SDFFE, the enable has to be unmapped first. - if (has_en) { - ff_type = has_set ? FF_SDFF1 : FF_SDFF0; - goto unmap_enable; - } -unmap_srst: - if (has_ce) - ff_type = FF_DFFE; - else - ff_type = FF_DFF; - if (ff_neg & NEG_R) - sig_d = cell->module->MuxGate(NEW_ID, has_set ? State::S1 : State::S0, sig_d[0], sig_r[0]); + void legalize_ff(FfData &ff) { + if (ff.has_gclk) + return; + + // TODO: consider supporting coarse as well. + if (!ff.is_fine) + return; + + if (mince && ff.has_ce && ff.sig_ce[0].wire && ce_used[ff.sig_ce[0]] < mince) + ff.unmap_ce(); + if (minsrst && ff.has_srst && ff.sig_srst[0].wire && srst_used[ff.sig_srst[0]] < minsrst) + ff.unmap_srst(); + + if (ff.has_clk) { + if (ff.has_sr) { + legalize_dffsr(ff); + } else if (ff.has_aload) { + legalize_aldff(ff); + } else if (ff.has_arst) { + legalize_adff(ff); + } else if (ff.has_srst) { + if (ff.has_ce && ff.ce_over_srst) + legalize_sdffce(ff); else - sig_d = cell->module->MuxGate(NEW_ID, sig_d[0], has_set ? State::S1 : State::S0, sig_r[0]); - ff_neg &= ~NEG_R; - sig_r = SigSpec(); - kill_srst = false; - continue; + legalize_sdff(ff); + } else { + legalize_dff(ff); + } + } else if (ff.has_aload) { + if (ff.has_sr) { + legalize_dlatchsr(ff); + } else if (ff.has_arst) { + legalize_adlatch(ff); + } else { + legalize_dlatch(ff); + } + } else { + if (ff.has_sr) { + legalize_sr(ff); + } else if (ff.has_arst) { + legalize_rlatch(ff); } else { log_assert(0); } } -cell_ok: + } + + void flip_pol(FfData &ff, SigSpec &sig, bool &pol) { + if (sig == State::S0) { + sig = State::S1; + } else if (sig == State::S1) { + sig = State::S0; + } else if (ff.is_fine) { + sig = ff.module->NotGate(NEW_ID, sig); + } else { + sig = ff.module->Not(NEW_ID, sig); + } + pol = !pol; + } + void legalize_finish(FfData &ff) { + int ff_type = get_ff_type(ff); + int initmask = get_initmask(ff); + log_assert(supported_cells[ff_type] & initmask); + int ff_neg = 0; + if (ff.has_sr) { + if (!ff.pol_clr) + ff_neg |= NEG_R; + if (!ff.pol_set) + ff_neg |= NEG_S; + } + if (ff.has_arst) { + if (!ff.pol_arst) + ff_neg |= NEG_R; + } + if (ff.has_srst) { + if (!ff.pol_srst) + ff_neg |= NEG_R; + } + if (ff.has_aload) { + if (!ff.pol_aload) + ff_neg |= NEG_L; + } + if (ff.has_clk) { + if (!ff.pol_clk) + ff_neg |= NEG_C; + } + if (ff.has_ce) { + if (!ff.pol_ce) + ff_neg |= NEG_CE; + } if (!(supported_cells_neg[ff_type][ff_neg] & initmask)) { // Cell is supported, but not with those polarities. // Will need to add some inverters. @@ -921,182 +998,27 @@ cell_ok: if (supported_cells_neg[ff_type][ff_neg ^ xneg] & initmask) break; log_assert(xneg < NUM_NEG); - if (xneg & NEG_R) - sig_r = cell->module->NotGate(NEW_ID, sig_r[0]); - if (xneg & NEG_S) - sig_s = cell->module->NotGate(NEW_ID, sig_s[0]); - if (xneg & NEG_E) - sig_e = cell->module->NotGate(NEW_ID, sig_e[0]); + if (xneg & NEG_CE) + flip_pol(ff, ff.sig_ce, ff.pol_ce); + if (ff.has_sr) { + if (xneg & NEG_R) + flip_pol(ff, ff.sig_clr, ff.pol_clr); + if (xneg & NEG_S) + flip_pol(ff, ff.sig_set, ff.pol_set); + } + if (ff.has_arst && xneg & NEG_R) + flip_pol(ff, ff.sig_arst, ff.pol_arst); + if (ff.has_srst && xneg & NEG_R) + flip_pol(ff, ff.sig_srst, ff.pol_srst); + if (xneg & NEG_L) + flip_pol(ff, ff.sig_aload, ff.pol_aload); if (xneg & NEG_C) - sig_c = cell->module->NotGate(NEW_ID, sig_c[0]); + flip_pol(ff, ff.sig_clk, ff.pol_clk); ff_neg ^= xneg; } - cell->unsetPort(ID::D); - cell->unsetPort(ID::Q); - cell->unsetPort(ID::C); - cell->unsetPort(ID::E); - cell->unsetPort(ID::S); - cell->unsetPort(ID::R); - switch (ff_type) { - case FF_DFF: - cell->type = IdString(stringf("$_DFF_%c_", - (ff_neg & NEG_C) ? 'N' : 'P' - )); - cell->setPort(ID::D, sig_d); - cell->setPort(ID::Q, sig_q); - cell->setPort(ID::C, sig_c); - break; - case FF_DFFE: - cell->type = IdString(stringf("$_DFFE_%c%c_", - (ff_neg & NEG_C) ? 'N' : 'P', - (ff_neg & NEG_E) ? 'N' : 'P' - )); - cell->setPort(ID::D, sig_d); - cell->setPort(ID::Q, sig_q); - cell->setPort(ID::C, sig_c); - cell->setPort(ID::E, sig_e); - break; - case FF_ADFF0: - case FF_ADFF1: - cell->type = IdString(stringf("$_DFF_%c%c%c_", - (ff_neg & NEG_C) ? 'N' : 'P', - (ff_neg & NEG_R) ? 'N' : 'P', - (ff_type == FF_ADFF1) ? '1' : '0' - )); - cell->setPort(ID::D, sig_d); - cell->setPort(ID::Q, sig_q); - cell->setPort(ID::C, sig_c); - cell->setPort(ID::R, sig_r); - break; - case FF_ADFFE0: - case FF_ADFFE1: - cell->type = IdString(stringf("$_DFFE_%c%c%c%c_", - (ff_neg & NEG_C) ? 'N' : 'P', - (ff_neg & NEG_R) ? 'N' : 'P', - (ff_type == FF_ADFFE1) ? '1' : '0', - (ff_neg & NEG_E) ? 'N' : 'P' - )); - cell->setPort(ID::D, sig_d); - cell->setPort(ID::Q, sig_q); - cell->setPort(ID::C, sig_c); - cell->setPort(ID::E, sig_e); - cell->setPort(ID::R, sig_r); - break; - case FF_DFFSR: - cell->type = IdString(stringf("$_DFFSR_%c%c%c_", - (ff_neg & NEG_C) ? 'N' : 'P', - (ff_neg & NEG_S) ? 'N' : 'P', - (ff_neg & NEG_R) ? 'N' : 'P' - )); - cell->setPort(ID::D, sig_d); - cell->setPort(ID::Q, sig_q); - cell->setPort(ID::C, sig_c); - cell->setPort(ID::S, sig_s); - cell->setPort(ID::R, sig_r); - break; - case FF_DFFSRE: - cell->type = IdString(stringf("$_DFFSRE_%c%c%c%c_", - (ff_neg & NEG_C) ? 'N' : 'P', - (ff_neg & NEG_S) ? 'N' : 'P', - (ff_neg & NEG_R) ? 'N' : 'P', - (ff_neg & NEG_E) ? 'N' : 'P' - )); - cell->setPort(ID::D, sig_d); - cell->setPort(ID::Q, sig_q); - cell->setPort(ID::C, sig_c); - cell->setPort(ID::E, sig_e); - cell->setPort(ID::S, sig_s); - cell->setPort(ID::R, sig_r); - break; - case FF_SDFF0: - case FF_SDFF1: - cell->type = IdString(stringf("$_SDFF_%c%c%c_", - (ff_neg & NEG_C) ? 'N' : 'P', - (ff_neg & NEG_R) ? 'N' : 'P', - (ff_type == FF_SDFF1) ? '1' : '0' - )); - cell->setPort(ID::D, sig_d); - cell->setPort(ID::Q, sig_q); - cell->setPort(ID::C, sig_c); - cell->setPort(ID::R, sig_r); - break; - case FF_SDFFE0: - case FF_SDFFE1: - cell->type = IdString(stringf("$_SDFFE_%c%c%c%c_", - (ff_neg & NEG_C) ? 'N' : 'P', - (ff_neg & NEG_R) ? 'N' : 'P', - (ff_type == FF_SDFFE1) ? '1' : '0', - (ff_neg & NEG_E) ? 'N' : 'P' - )); - cell->setPort(ID::D, sig_d); - cell->setPort(ID::Q, sig_q); - cell->setPort(ID::C, sig_c); - cell->setPort(ID::E, sig_e); - cell->setPort(ID::R, sig_r); - break; - case FF_SDFFCE0: - case FF_SDFFCE1: - cell->type = IdString(stringf("$_SDFFCE_%c%c%c%c_", - (ff_neg & NEG_C) ? 'N' : 'P', - (ff_neg & NEG_R) ? 'N' : 'P', - (ff_type == FF_SDFFCE1) ? '1' : '0', - (ff_neg & NEG_E) ? 'N' : 'P' - )); - cell->setPort(ID::D, sig_d); - cell->setPort(ID::Q, sig_q); - cell->setPort(ID::C, sig_c); - cell->setPort(ID::E, sig_e); - cell->setPort(ID::R, sig_r); - break; - case FF_DLATCH: - cell->type = IdString(stringf("$_DLATCH_%c_", - (ff_neg & NEG_E) ? 'N' : 'P' - )); - cell->setPort(ID::D, sig_d); - cell->setPort(ID::Q, sig_q); - cell->setPort(ID::E, sig_e); - break; - case FF_ADLATCH0: - case FF_ADLATCH1: - cell->type = IdString(stringf("$_DLATCH_%c%c%c_", - (ff_neg & NEG_E) ? 'N' : 'P', - (ff_neg & NEG_R) ? 'N' : 'P', - (ff_type == FF_ADLATCH1) ? '1' : '0' - )); - cell->setPort(ID::D, sig_d); - cell->setPort(ID::Q, sig_q); - cell->setPort(ID::E, sig_e); - cell->setPort(ID::R, sig_r); - break; - case FF_DLATCHSR: - cell->type = IdString(stringf("$_DLATCHSR_%c%c%c_", - (ff_neg & NEG_E) ? 'N' : 'P', - (ff_neg & NEG_S) ? 'N' : 'P', - (ff_neg & NEG_R) ? 'N' : 'P' - )); - cell->setPort(ID::D, sig_d); - cell->setPort(ID::Q, sig_q); - cell->setPort(ID::E, sig_e); - cell->setPort(ID::S, sig_s); - cell->setPort(ID::R, sig_r); - break; - case FF_SR: - cell->type = IdString(stringf("$_SR_%c%c_", - (ff_neg & NEG_S) ? 'N' : 'P', - (ff_neg & NEG_R) ? 'N' : 'P' - )); - cell->setPort(ID::Q, sig_q); - cell->setPort(ID::S, sig_s); - cell->setPort(ID::R, sig_r); - break; - default: - log_assert(0); - } - return; - -error: - log_error("FF %s.%s (type %s) cannot be legalized: %s\n", log_id(cell->module->name), log_id(cell->name), log_id(cell->type), reason); + fixup_reset_x(ff, supported_cells_neg[ff_type][ff_neg]); + ff.emit(); } void execute(std::vector<std::string> args, RTLIL::Design *design) override @@ -1118,79 +1040,83 @@ error: if (args[argidx] == "-cell" && argidx + 2 < args.size()) { std::string celltype = args[++argidx]; std::string inittype = args[++argidx]; - enum FfType ff_type[2] = {NUM_FFTYPES, NUM_FFTYPES}; + enum FfType ff_type; char pol_c = 0; - char pol_e = 0; + char pol_l = 0; char pol_s = 0; char pol_r = 0; + char pol_ce = 0; char srval = 0; if (celltype.substr(0, 5) == "$_SR_" && celltype.size() == 8 && celltype[7] == '_') { - ff_type[0] = FF_SR; + ff_type = FF_SR; pol_s = celltype[5]; pol_r = celltype[6]; } else if (celltype.substr(0, 6) == "$_DFF_" && celltype.size() == 8 && celltype[7] == '_') { - ff_type[0] = FF_DFF; + ff_type = FF_DFF; pol_c = celltype[6]; } else if (celltype.substr(0, 7) == "$_DFFE_" && celltype.size() == 10 && celltype[9] == '_') { - ff_type[0] = FF_DFFE; + ff_type = FF_DFFE; pol_c = celltype[7]; - pol_e = celltype[8]; + pol_ce = celltype[8]; } else if (celltype.substr(0, 6) == "$_DFF_" && celltype.size() == 10 && celltype[9] == '_') { - ff_type[0] = FF_ADFF0; - ff_type[1] = FF_ADFF1; + ff_type = FF_ADFF; pol_c = celltype[6]; pol_r = celltype[7]; srval = celltype[8]; } else if (celltype.substr(0, 7) == "$_DFFE_" && celltype.size() == 12 && celltype[11] == '_') { - ff_type[0] = FF_ADFFE0; - ff_type[1] = FF_ADFFE1; + ff_type = FF_ADFFE; pol_c = celltype[7]; pol_r = celltype[8]; srval = celltype[9]; - pol_e = celltype[10]; + pol_ce = celltype[10]; + } else if (celltype.substr(0, 8) == "$_ALDFF_" && celltype.size() == 11 && celltype[10] == '_') { + ff_type = FF_ALDFF; + pol_c = celltype[8]; + pol_l = celltype[9]; + } else if (celltype.substr(0, 9) == "$_ALDFFE_" && celltype.size() == 13 && celltype[12] == '_') { + ff_type = FF_ALDFFE; + pol_c = celltype[9]; + pol_l = celltype[10]; + pol_ce = celltype[11]; } else if (celltype.substr(0, 8) == "$_DFFSR_" && celltype.size() == 12 && celltype[11] == '_') { - ff_type[0] = FF_DFFSR; + ff_type = FF_DFFSR; pol_c = celltype[8]; pol_s = celltype[9]; pol_r = celltype[10]; } else if (celltype.substr(0, 9) == "$_DFFSRE_" && celltype.size() == 14 && celltype[13] == '_') { - ff_type[0] = FF_DFFSRE; + ff_type = FF_DFFSRE; pol_c = celltype[9]; pol_s = celltype[10]; pol_r = celltype[11]; - pol_e = celltype[12]; + pol_ce = celltype[12]; } else if (celltype.substr(0, 7) == "$_SDFF_" && celltype.size() == 11 && celltype[10] == '_') { - ff_type[0] = FF_SDFF0; - ff_type[1] = FF_SDFF1; + ff_type = FF_SDFF; pol_c = celltype[7]; pol_r = celltype[8]; srval = celltype[9]; } else if (celltype.substr(0, 8) == "$_SDFFE_" && celltype.size() == 13 && celltype[12] == '_') { - ff_type[0] = FF_SDFFE0; - ff_type[1] = FF_SDFFE1; + ff_type = FF_SDFFE; pol_c = celltype[8]; pol_r = celltype[9]; srval = celltype[10]; - pol_e = celltype[11]; + pol_ce = celltype[11]; } else if (celltype.substr(0, 9) == "$_SDFFCE_" && celltype.size() == 14 && celltype[13] == '_') { - ff_type[0] = FF_SDFFCE0; - ff_type[1] = FF_SDFFCE1; + ff_type = FF_SDFFCE; pol_c = celltype[9]; pol_r = celltype[10]; srval = celltype[11]; - pol_e = celltype[12]; + pol_ce = celltype[12]; } else if (celltype.substr(0, 9) == "$_DLATCH_" && celltype.size() == 11 && celltype[10] == '_') { - ff_type[0] = FF_DLATCH; - pol_e = celltype[9]; + ff_type = FF_DLATCH; + pol_l = celltype[9]; } else if (celltype.substr(0, 9) == "$_DLATCH_" && celltype.size() == 13 && celltype[12] == '_') { - ff_type[0] = FF_ADLATCH0; - ff_type[1] = FF_ADLATCH1; - pol_e = celltype[9]; + ff_type = FF_ADLATCH; + pol_l = celltype[9]; pol_r = celltype[10]; srval = celltype[11]; } else if (celltype.substr(0, 11) == "$_DLATCHSR_" && celltype.size() == 15 && celltype[14] == '_') { - ff_type[0] = FF_DLATCHSR; - pol_e = celltype[11]; + ff_type = FF_DLATCHSR; + pol_l = celltype[11]; pol_s = celltype[12]; pol_r = celltype[13]; } else { @@ -1201,9 +1127,10 @@ unrecognized: int match = 0; for (auto pair : { std::make_pair(pol_c, NEG_C), - std::make_pair(pol_e, NEG_E), + std::make_pair(pol_l, NEG_L), std::make_pair(pol_s, NEG_S), std::make_pair(pol_r, NEG_R), + std::make_pair(pol_ce, NEG_CE), }) { if (pair.first == 'N') { mask |= pair.second; @@ -1214,40 +1141,33 @@ unrecognized: goto unrecognized; } } + int initmask; + if (inittype == "x") { + initmask = 0x111; + } else if (inittype == "0") { + initmask = 0x333; + } else if (inittype == "1") { + initmask = 0x555; + } else if (inittype == "r") { + if (srval == 0) + log_error("init type r not valid for cell type %s.\n", celltype.c_str()); + initmask = 0x537; + } else if (inittype == "01") { + initmask = 0x777; + } else { + log_error("unrecognized init type %s for cell type %s.\n", inittype.c_str(), celltype.c_str()); + } if (srval == '0') { - ff_type[1] = NUM_FFTYPES; + initmask &= 0x0ff; } else if (srval == '1') { - ff_type[0] = NUM_FFTYPES; + initmask &= 0xf0f; } else if (srval != 0 && srval != '?') { goto unrecognized; } - for (int i = 0; i < 2; i++) { - if (ff_type[i] == NUM_FFTYPES) - continue; - int initmask; - if (inittype == "x") { - initmask = INIT_X; - } else if (inittype == "0") { - initmask = INIT_X | INIT_0; - } else if (inittype == "1") { - initmask = INIT_X | INIT_1; - } else if (inittype == "r") { - if (srval == 0) - log_error("init type r not valid for cell type %s.\n", celltype.c_str()); - if (i == 0) - initmask = INIT_X | INIT_0; - else - initmask = INIT_X | INIT_1; - } else if (inittype == "01") { - initmask = INIT_X | INIT_0 | INIT_1; - } else { - log_error("unrecognized init type %s for cell type %s.\n", inittype.c_str(), celltype.c_str()); - } - for (int neg = 0; neg < NUM_NEG; neg++) - if ((neg & mask) == match) - supported_cells_neg[ff_type[i]][neg] |= initmask; - supported_cells[ff_type[i]] |= initmask; - } + for (int neg = 0; neg < NUM_NEG; neg++) + if ((neg & mask) == match) + supported_cells_neg[ff_type][neg] |= initmask; + supported_cells[ff_type] |= initmask; continue; } else if (args[argidx] == "-mince" && argidx + 1 < args.size()) { mince = atoi(args[++argidx].c_str()); @@ -1260,13 +1180,21 @@ unrecognized: } extra_args(args, argidx, design); supported_dffsr = supported_cells[FF_DFFSR] | supported_cells[FF_DFFSRE]; - supported_adff0 = supported_cells[FF_ADFF0] | supported_cells[FF_ADFFE0] | supported_dffsr; - supported_adff1 = supported_cells[FF_ADFF1] | supported_cells[FF_ADFFE1] | supported_dffsr; - supported_sdff0 = supported_cells[FF_SDFF0] | supported_cells[FF_SDFFE0] | supported_cells[FF_SDFFCE0]; - supported_sdff1 = supported_cells[FF_SDFF1] | supported_cells[FF_SDFFE1] | supported_cells[FF_SDFFCE1]; - supported_dff = supported_cells[FF_DFF] | supported_cells[FF_DFFE] | supported_dffsr | supported_adff0 | supported_adff1 | supported_sdff0 | supported_sdff1; - supported_sr = supported_dffsr | supported_cells[FF_DLATCHSR] | supported_cells[FF_SR] | supported_cells[FF_ADLATCH0] | flip_initmask(supported_cells[FF_ADLATCH1]); - supported_dlatch = supported_cells[FF_DLATCH] | supported_cells[FF_ADLATCH0] | supported_cells[FF_ADLATCH1] | supported_cells[FF_DLATCHSR]; + supported_aldff = supported_cells[FF_ALDFF] | supported_cells[FF_ALDFFE] | supported_dffsr; + supported_aldffe = supported_cells[FF_ALDFFE] | supported_cells[FF_DFFSRE]; + supported_adff = supported_cells[FF_ADFF] | supported_cells[FF_ADFFE] | supported_dffsr | supported_aldff; + supported_adffe = supported_cells[FF_ADFFE] | supported_cells[FF_ALDFFE] | supported_cells[FF_DFFSRE]; + supported_sdff = supported_cells[FF_SDFF] | supported_cells[FF_SDFFE] | supported_cells[FF_SDFFCE]; + supported_dff = supported_cells[FF_DFF] | supported_cells[FF_DFFE] | supported_adff | supported_sdff; + supported_dffe = supported_cells[FF_DFFE] | supported_cells[FF_DFFSRE] | supported_cells[FF_ALDFFE] | supported_cells[FF_ADFFE] | supported_cells[FF_SDFFE] | supported_cells[FF_SDFFCE]; + supported_sr_plain = supported_dffsr | supported_cells[FF_DLATCHSR] | supported_cells[FF_SR]; + supported_sr = supported_sr_plain; + supported_sr |= (supported_cells[FF_ADLATCH] >> 4 & 7) * 0x111; + supported_sr |= (flip_initmask(supported_cells[FF_ADLATCH]) >> 4 & 7) * 0x111; + supported_dlatch_plain = supported_cells[FF_DLATCH] | supported_cells[FF_ADLATCH] | supported_cells[FF_DLATCHSR] | supported_cells[FF_ALDFF] | supported_cells[FF_ALDFFE]; + supported_dlatch = supported_dlatch_plain | supported_sr_plain; + supported_rlatch = supported_adff | (supported_dlatch & 7) * 0x111; + supported_adlatch = supported_cells[FF_ADLATCH] | supported_cells[FF_DLATCHSR]; for (auto module : design->selected_modules()) { @@ -1281,36 +1209,20 @@ unrecognized: if (!RTLIL::builtin_ff_cell_types().count(cell->type)) continue; - if (cell->hasPort(ID::C) && cell->hasPort(ID::E)) { - SigSpec sig = cell->getPort(ID::E); - // Do not count const enable signals. - if (GetSize(sig) == 1 && sig[0].wire) - ce_used[sig[0]]++; - } - if (cell->type.str().substr(0, 6) == "$_SDFF") { - SigSpec sig = cell->getPort(ID::R); - // Do not count const srst signals. - if (GetSize(sig) == 1 && sig[0].wire) - srst_used[sig[0]]++; - } + FfData ff(&initvals, cell); + if (ff.has_ce && ff.sig_ce[0].wire) + ce_used[ff.sig_ce[0]] += ff.width; + if (ff.has_srst && ff.sig_srst[0].wire) + srst_used[ff.sig_srst[0]] += ff.width; } } - - // First gather FF cells, then iterate over them later. - // We may need to split an FF into several cells. - std::vector<Cell *> ff_cells; - for (auto cell : module->selected_cells()) { - // Early exit for non-FFs. if (!RTLIL::builtin_ff_cell_types().count(cell->type)) continue; - - ff_cells.push_back(cell); + FfData ff(&initvals, cell); + legalize_ff(ff); } - - for (auto cell: ff_cells) - handle_ff(cell); } sigmap.clear(); diff --git a/passes/techmap/dfflibmap.cc b/passes/techmap/dfflibmap.cc index 78a6f1c0d..252baae9a 100644 --- a/passes/techmap/dfflibmap.cc +++ b/passes/techmap/dfflibmap.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/techmap/dffunmap.cc b/passes/techmap/dffunmap.cc index fb107ff75..7312015f1 100644 --- a/passes/techmap/dffunmap.cc +++ b/passes/techmap/dffunmap.cc @@ -84,21 +84,20 @@ struct DffunmapPass : public Pass { continue; if (ce_only) { - if (!ff.has_en) + if (!ff.has_ce) continue; - ff.unmap_ce(mod); + ff.unmap_ce(); } else if (srst_only) { if (!ff.has_srst) continue; - ff.unmap_srst(mod); + ff.unmap_srst(); } else { - if (!ff.has_en && !ff.has_srst) + if (!ff.has_ce && !ff.has_srst) continue; - ff.unmap_ce_srst(mod); + ff.unmap_ce_srst(); } - mod->remove(cell); - ff.emit(mod, name); + ff.emit(); } } } diff --git a/passes/techmap/extract.cc b/passes/techmap/extract.cc index f5966fac0..137d22170 100644 --- a/passes/techmap/extract.cc +++ b/passes/techmap/extract.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -74,6 +74,7 @@ public: param_int(ID::CTRL_IN_WIDTH) param_int(ID::CTRL_OUT_WIDTH) param_int(ID::OFFSET) + param_int(ID::PORTID) param_int(ID::PRIORITY) param_int(ID::RD_PORTS) param_int(ID::SIZE) diff --git a/passes/techmap/extract_counter.cc b/passes/techmap/extract_counter.cc index 56b2ea584..9c814af23 100644 --- a/passes/techmap/extract_counter.cc +++ b/passes/techmap/extract_counter.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2017 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2017 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/techmap/extract_fa.cc b/passes/techmap/extract_fa.cc index 3fcff01c3..117fdd54c 100644 --- a/passes/techmap/extract_fa.cc +++ b/passes/techmap/extract_fa.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -33,7 +33,7 @@ struct ExtractFaConfig int maxbreadth = 6; }; -// http://svn.clifford.at/handicraft/2016/bindec/bindec.c +// http://svn.clairexen.net/handicraft/2016/bindec/bindec.c int bindec(unsigned char v) { int r = v & 1; diff --git a/passes/techmap/extract_reduce.cc b/passes/techmap/extract_reduce.cc index 07b4200cc..892e9a364 100644 --- a/passes/techmap/extract_reduce.cc +++ b/passes/techmap/extract_reduce.cc @@ -152,10 +152,10 @@ struct ExtractReducePass : public Pass log_assert(y.size() == 1); // Should only continue if there is one fanout back into a cell (not to a port) - if (sig_to_sink[y[0]].size() != 1) + if (sig_to_sink[y].size() != 1 || port_sigs.count(y)) break; - x = *sig_to_sink[y[0]].begin(); + x = *sig_to_sink[y].begin(); } sinks.insert(head_cell); @@ -183,13 +183,15 @@ struct ExtractReducePass : public Pass continue; } + auto xy = sigmap(x->getPort(ID::Y)); + //If this signal drives a port, add it to the sinks //(even though it may not be the end of a chain) - if(port_sigs.count(x) && !consumed_cells.count(x)) + if(port_sigs.count(xy) && !consumed_cells.count(x)) sinks.insert(x); //It's a match, search everything out from it - auto& next = sig_to_sink[x]; + auto& next = sig_to_sink[xy]; for(auto z : next) next_loads.insert(z); } @@ -224,89 +226,60 @@ struct ExtractReducePass : public Pass if(consumed_cells.count(head_cell)) continue; - pool<Cell*> cur_supercell; + dict<SigBit, int> sources; + int inner_cells = 0; std::deque<Cell*> bfs_queue = {head_cell}; while (bfs_queue.size()) { Cell* x = bfs_queue.front(); bfs_queue.pop_front(); - cur_supercell.insert(x); + for (auto port: {ID::A, ID::B}) { + auto bit = sigmap(x->getPort(port)[0]); - auto a = sigmap(x->getPort(ID::A)); - log_assert(a.size() == 1); + bool sink_single = sig_to_sink[bit].size() == 1 && !port_sigs.count(bit); - // Must have only one sink unless we're going off chain - // XXX: Check that it is indeed this node? - if( allow_off_chain || (sig_to_sink[a[0]].size() + port_sigs.count(a[0]) == 1) ) - { - Cell* cell_a = sig_to_driver[a[0]]; - if(cell_a && IsRightType(cell_a, gt)) - { - // The cell here is the correct type, and it's definitely driving - // this current cell. - bfs_queue.push_back(cell_a); - } - } + Cell* drv = sig_to_driver[bit]; + bool drv_ok = drv && drv->type == head_cell->type; - auto b = sigmap(x->getPort(ID::B)); - log_assert(b.size() == 1); - - // Must have only one sink - // XXX: Check that it is indeed this node? - if( allow_off_chain || (sig_to_sink[b[0]].size() + port_sigs.count(b[0]) == 1) ) - { - Cell* cell_b = sig_to_driver[b[0]]; - if(cell_b && IsRightType(cell_b, gt)) - { - // The cell here is the correct type, and it's definitely driving only - // this current cell. - bfs_queue.push_back(cell_b); + if (drv_ok && (allow_off_chain || sink_single)) { + inner_cells++; + bfs_queue.push_back(drv); + } else { + sources[bit]++; } } } - log(" Cells:\n"); - for (auto x : cur_supercell) - log(" %s\n", x->name.c_str()); - - if (cur_supercell.size() > 1) + if (inner_cells) { // Worth it to create reduce cell log(" Creating $reduce_* cell!\n"); - pool<SigBit> input_pool; - pool<SigBit> input_pool_intermed; - for (auto x : cur_supercell) - { - input_pool.insert(sigmap(x->getPort(ID::A))[0]); - input_pool.insert(sigmap(x->getPort(ID::B))[0]); - input_pool_intermed.insert(sigmap(x->getPort(ID::Y))[0]); - } - SigSpec input; - for (auto b : input_pool) - if (input_pool_intermed.count(b) == 0) - input.append(b); - SigBit output = sigmap(head_cell->getPort(ID::Y)[0]); - auto new_reduce_cell = module->addCell(NEW_ID, - gt == GateType::And ? ID($reduce_and) : - gt == GateType::Or ? ID($reduce_or) : - gt == GateType::Xor ? ID($reduce_xor) : ""); - new_reduce_cell->setParam(ID::A_SIGNED, 0); - new_reduce_cell->setParam(ID::A_WIDTH, input.size()); - new_reduce_cell->setParam(ID::Y_WIDTH, 1); - new_reduce_cell->setPort(ID::A, input); - new_reduce_cell->setPort(ID::Y, output); - - if(allow_off_chain) - consumed_cells.insert(head_cell); - else - { - for (auto x : cur_supercell) - consumed_cells.insert(x); + SigSpec input; + for (auto it : sources) { + bool cond; + if (head_cell->type == ID($_XOR_)) + cond = it.second & 1; + else + cond = it.second != 0; + if (cond) + input.append(it.first); + } + + if (head_cell->type == ID($_AND_)) { + module->addReduceAnd(NEW_ID, input, output); + } else if (head_cell->type == ID($_OR_)) { + module->addReduceOr(NEW_ID, input, output); + } else if (head_cell->type == ID($_XOR_)) { + module->addReduceXor(NEW_ID, input, output); + } else { + log_assert(false); } + + consumed_cells.insert(head_cell); } } } diff --git a/passes/techmap/extractinv.cc b/passes/techmap/extractinv.cc index 11463380c..48d9600fa 100644 --- a/passes/techmap/extractinv.cc +++ b/passes/techmap/extractinv.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * Copyright (C) 2019 Marcelina KoÅ›cielnicka <mwk@0x04.net> * * Permission to use, copy, modify, and/or distribute this software for any diff --git a/passes/techmap/flatten.cc b/passes/techmap/flatten.cc index f35b7ff60..7e6df5d2c 100644 --- a/passes/techmap/flatten.cc +++ b/passes/techmap/flatten.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -77,7 +77,7 @@ struct FlattenWorker { bool ignore_wb = false; - void flatten_cell(RTLIL::Design *design, RTLIL::Module *module, RTLIL::Cell *cell, RTLIL::Module *tpl, std::vector<RTLIL::Cell*> &new_cells) + void flatten_cell(RTLIL::Design *design, RTLIL::Module *module, RTLIL::Cell *cell, RTLIL::Module *tpl, SigMap &sigmap, std::vector<RTLIL::Cell*> &new_cells) { // Copy the contents of the flattened cell @@ -122,6 +122,9 @@ struct FlattenWorker for (auto &tpl_proc_it : tpl->processes) { RTLIL::Process *new_proc = module->addProcess(map_name(cell, tpl_proc_it.second), tpl_proc_it.second); map_attributes(cell, new_proc, tpl_proc_it.second->name); + for (auto new_proc_sync : new_proc->syncs) + for (auto &memwr_action : new_proc_sync->mem_write_actions) + memwr_action.memid = memory_map.at(memwr_action.memid).str(); auto rewriter = [&](RTLIL::SigSpec &sig) { map_sigspec(wire_map, sig); }; new_proc->rewrite_sigspecs(rewriter); design->select(module, new_proc); @@ -130,10 +133,10 @@ struct FlattenWorker for (auto tpl_cell : tpl->cells()) { RTLIL::Cell *new_cell = module->addCell(map_name(cell, tpl_cell), tpl_cell); map_attributes(cell, new_cell, tpl_cell->name); - if (new_cell->type.in(ID($memrd), ID($memwr), ID($meminit))) { + if (new_cell->has_memid()) { IdString memid = new_cell->getParam(ID::MEMID).decode_string(); new_cell->setParam(ID::MEMID, Const(memory_map.at(memid).str())); - } else if (new_cell->type == ID($mem)) { + } else if (new_cell->is_mem_cell()) { IdString memid = new_cell->getParam(ID::MEMID).decode_string(); new_cell->setParam(ID::MEMID, Const(concat_name(cell, memid).str())); } @@ -162,7 +165,6 @@ struct FlattenWorker for (auto bit : tpl_conn.first) tpl_driven.insert(bit); - SigMap sigmap(module); for (auto &port_it : cell->connections()) { IdString port_name = port_it.first; @@ -215,6 +217,7 @@ struct FlattenWorker log_id(module), log_id(cell), log_id(port_it.first), log_signal(new_conn.first), log_signal(new_conn.second)); module->connect(new_conn); + sigmap.add(new_conn.first, new_conn.second); } module->remove(cell); @@ -225,6 +228,7 @@ struct FlattenWorker if (!design->selected(module) || module->get_blackbox_attribute(ignore_wb)) return; + SigMap sigmap(module); std::vector<RTLIL::Cell*> worklist = module->selected_cells(); while (!worklist.empty()) { @@ -248,7 +252,7 @@ struct FlattenWorker // If a design is fully selected and has a top module defined, topological sorting ensures that all cells // added during flattening are black boxes, and flattening is finished in one pass. However, when flattening // individual modules, this isn't the case, and the newly added cells might have to be flattened further. - flatten_cell(design, module, cell, tpl, worklist); + flatten_cell(design, module, cell, tpl, sigmap, worklist); } } }; diff --git a/passes/techmap/hilomap.cc b/passes/techmap/hilomap.cc index b808a8d8e..c1b947221 100644 --- a/passes/techmap/hilomap.cc +++ b/passes/techmap/hilomap.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/techmap/insbuf.cc b/passes/techmap/insbuf.cc index a3b5b698d..68c22c317 100644 --- a/passes/techmap/insbuf.cc +++ b/passes/techmap/insbuf.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/techmap/iopadmap.cc b/passes/techmap/iopadmap.cc index e8530a034..437ad5156 100644 --- a/passes/techmap/iopadmap.cc +++ b/passes/techmap/iopadmap.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -43,26 +43,28 @@ struct IopadmapPass : public Pass { log("can only map to very simple PAD cells. Use 'techmap' to further map\n"); log("the resulting cells to more sophisticated PAD cells.\n"); log("\n"); - log(" -inpad <celltype> <portname>[:<portname>]\n"); + log(" -inpad <celltype> <in_port>[:<ext_port>]\n"); 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(" signal is passed through the pad cell, using the 2nd\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(" -outpad <celltype> <out_port>[:<ext_port>]\n"); + log(" -inoutpad <celltype> <io_port>[:<ext_port>]\n"); log(" Similar to -inpad, but for output and inout ports.\n"); log("\n"); - log(" -toutpad <celltype> <portname>:<portname>[:<portname>]\n"); + log(" -toutpad <celltype> <oe_port>:<out_port>[:<ext_port>]\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(" of the tristate driver, which can be prefixed with `~` for negative\n"); + log(" polarity enable.\n"); log("\n"); - log(" -tinoutpad <celltype> <portname>:<portname>:<portname>[:<portname>]\n"); + log(" -tinoutpad <celltype> <oe_port>:<in_port>:<out_port>[:<ext_port>]\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(" buffering the external signal. Like with `-toutpad`, the enable can\n"); + log(" be marked as negative polarity by prefixing the name with `~`.\n"); log("\n"); log(" -ignore <celltype> <portname>[:<portname>]*\n"); log(" Skips mapping inputs/outputs that are already connected to given\n"); @@ -106,6 +108,7 @@ struct IopadmapPass : public Pass { std::string inoutpad_celltype, inoutpad_portname_io, inoutpad_portname_pad; std::string toutpad_celltype, toutpad_portname_oe, toutpad_portname_i, toutpad_portname_pad; std::string tinoutpad_celltype, tinoutpad_portname_oe, tinoutpad_portname_o, tinoutpad_portname_i, tinoutpad_portname_pad; + bool toutpad_neg_oe = false, tinoutpad_neg_oe = false; std::string widthparam, nameparam; pool<pair<IdString, IdString>> ignore; bool flag_bits = false; @@ -137,6 +140,10 @@ struct IopadmapPass : public Pass { toutpad_portname_oe = args[++argidx]; split_portname_pair(toutpad_portname_oe, toutpad_portname_i); split_portname_pair(toutpad_portname_i, toutpad_portname_pad); + if (toutpad_portname_oe[0] == '~') { + toutpad_neg_oe = true; + toutpad_portname_oe = toutpad_portname_oe.substr(1); + } continue; } if (arg == "-tinoutpad" && argidx+2 < args.size()) { @@ -145,6 +152,10 @@ struct IopadmapPass : public Pass { split_portname_pair(tinoutpad_portname_oe, tinoutpad_portname_o); split_portname_pair(tinoutpad_portname_o, tinoutpad_portname_i); split_portname_pair(tinoutpad_portname_i, tinoutpad_portname_pad); + if (tinoutpad_portname_oe[0] == '~') { + tinoutpad_neg_oe = true; + tinoutpad_portname_oe = tinoutpad_portname_oe.substr(1); + } continue; } if (arg == "-ignore" && argidx+2 < args.size()) { @@ -318,6 +329,8 @@ struct IopadmapPass : public Pass { module->uniquify(stringf("$iopadmap$%s.%s[%d]", log_id(module), log_id(wire), i)), RTLIL::escape_id(tinoutpad_celltype)); + if (tinoutpad_neg_oe) + en_sig = module->NotGate(NEW_ID, en_sig); cell->setPort(RTLIL::escape_id(tinoutpad_portname_oe), en_sig); cell->attributes[ID::keep] = RTLIL::Const(1); @@ -340,6 +353,8 @@ struct IopadmapPass : public Pass { module->uniquify(stringf("$iopadmap$%s.%s[%d]", log_id(module), log_id(wire), i)), RTLIL::escape_id(toutpad_celltype)); + if (toutpad_neg_oe) + en_sig = module->NotGate(NEW_ID, en_sig); cell->setPort(RTLIL::escape_id(toutpad_portname_oe), en_sig); cell->setPort(RTLIL::escape_id(toutpad_portname_i), data_sig); cell->attributes[ID::keep] = RTLIL::Const(1); diff --git a/passes/techmap/libparse.cc b/passes/techmap/libparse.cc index 349ccc115..3d0ebaea3 100644 --- a/passes/techmap/libparse.cc +++ b/passes/techmap/libparse.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -384,7 +384,7 @@ void LibertyParser::error(const std::string &str) exit(1); } -/**** BEGIN: http://svn.clifford.at/tools/trunk/examples/check.h ****/ +/**** BEGIN: http://svn.clairexen.net/tools/trunk/examples/check.h ****/ #define CHECK_NV(result, check) \ do { \ @@ -405,7 +405,7 @@ void LibertyParser::error(const std::string &str) } \ } while(0) -/**** END: http://svn.clifford.at/tools/trunk/examples/check.h ****/ +/**** END: http://svn.clairexen.net/tools/trunk/examples/check.h ****/ LibertyAst *find_non_null(LibertyAst *node, const char *name) { diff --git a/passes/techmap/libparse.h b/passes/techmap/libparse.h index c9ebd06c5..77e305f0b 100644 --- a/passes/techmap/libparse.h +++ b/passes/techmap/libparse.h @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/techmap/lut2mux.cc b/passes/techmap/lut2mux.cc index f56eff3e5..ef76e0deb 100644 --- a/passes/techmap/lut2mux.cc +++ b/passes/techmap/lut2mux.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/techmap/maccmap.cc b/passes/techmap/maccmap.cc index 43f2d97f5..2235bdef9 100644 --- a/passes/techmap/maccmap.cc +++ b/passes/techmap/maccmap.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/techmap/muxcover.cc b/passes/techmap/muxcover.cc index 24109b579..a90d81985 100644 --- a/passes/techmap/muxcover.cc +++ b/passes/techmap/muxcover.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/techmap/nlutmap.cc b/passes/techmap/nlutmap.cc index e1ebfcad8..016789157 100644 --- a/passes/techmap/nlutmap.cc +++ b/passes/techmap/nlutmap.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/techmap/pmuxtree.cc b/passes/techmap/pmuxtree.cc index b937d3fb0..ff6bb549b 100644 --- a/passes/techmap/pmuxtree.cc +++ b/passes/techmap/pmuxtree.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/techmap/shregmap.cc b/passes/techmap/shregmap.cc index b971068f7..928182970 100644 --- a/passes/techmap/shregmap.cc +++ b/passes/techmap/shregmap.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/techmap/simplemap.cc b/passes/techmap/simplemap.cc index b9d337da4..7d8dba439 100644 --- a/passes/techmap/simplemap.cc +++ b/passes/techmap/simplemap.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -19,6 +19,7 @@ #include "simplemap.h" #include "kernel/sigtools.h" +#include "kernel/ff.h" #include <stdlib.h> #include <stdio.h> #include <string.h> @@ -298,6 +299,30 @@ void simplemap_tribuf(RTLIL::Module *module, RTLIL::Cell *cell) } } +void simplemap_bmux(RTLIL::Module *module, RTLIL::Cell *cell) +{ + SigSpec sel = cell->getPort(ID::S); + SigSpec data = cell->getPort(ID::A); + int width = GetSize(cell->getPort(ID::Y)); + + for (int idx = 0; idx < GetSize(sel); idx++) { + SigSpec new_data = module->addWire(NEW_ID, GetSize(data)/2); + for (int i = 0; i < GetSize(new_data); i += width) { + for (int k = 0; k < width; k++) { + RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_MUX_)); + gate->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src)); + gate->setPort(ID::A, data[i*2+k]); + gate->setPort(ID::B, data[i*2+width+k]); + gate->setPort(ID::S, sel[idx]); + gate->setPort(ID::Y, new_data[i+k]); + } + } + data = new_data; + } + + module->connect(cell->getPort(ID::Y), data); +} + void simplemap_lut(RTLIL::Module *module, RTLIL::Cell *cell) { SigSpec lut_ctrl = cell->getPort(ID::A); @@ -305,7 +330,6 @@ void simplemap_lut(RTLIL::Module *module, RTLIL::Cell *cell) lut_data.extend_u0(1 << cell->getParam(ID::WIDTH).as_int()); for (int idx = 0; GetSize(lut_data) > 1; idx++) { - SigSpec sig_s = lut_ctrl[idx]; SigSpec new_lut_data = module->addWire(NEW_ID, GetSize(lut_data)/2); for (int i = 0; i < GetSize(lut_data); i += 2) { RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_MUX_)); @@ -367,276 +391,13 @@ void simplemap_concat(RTLIL::Module *module, RTLIL::Cell *cell) module->connect(RTLIL::SigSig(sig_y, sig_ab)); } -void simplemap_sr(RTLIL::Module *module, RTLIL::Cell *cell) -{ - int width = cell->parameters.at(ID::WIDTH).as_int(); - char set_pol = cell->parameters.at(ID::SET_POLARITY).as_bool() ? 'P' : 'N'; - char clr_pol = cell->parameters.at(ID::CLR_POLARITY).as_bool() ? 'P' : 'N'; - - RTLIL::SigSpec sig_s = cell->getPort(ID::SET); - RTLIL::SigSpec sig_r = cell->getPort(ID::CLR); - RTLIL::SigSpec sig_q = cell->getPort(ID::Q); - - std::string gate_type = stringf("$_SR_%c%c_", set_pol, clr_pol); - - for (int i = 0; i < width; i++) { - RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type); - gate->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src)); - gate->setPort(ID::S, sig_s[i]); - gate->setPort(ID::R, sig_r[i]); - gate->setPort(ID::Q, sig_q[i]); - } -} - -void simplemap_ff(RTLIL::Module *module, RTLIL::Cell *cell) -{ - int width = cell->parameters.at(ID::WIDTH).as_int(); - - RTLIL::SigSpec sig_d = cell->getPort(ID::D); - RTLIL::SigSpec sig_q = cell->getPort(ID::Q); - - IdString gate_type = ID($_FF_); - - for (int i = 0; i < width; i++) { - RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type); - gate->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src)); - gate->setPort(ID::D, sig_d[i]); - gate->setPort(ID::Q, sig_q[i]); - } -} - -void simplemap_dff(RTLIL::Module *module, RTLIL::Cell *cell) +void simplemap_ff(RTLIL::Module *, RTLIL::Cell *cell) { - int width = cell->parameters.at(ID::WIDTH).as_int(); - char clk_pol = cell->parameters.at(ID::CLK_POLARITY).as_bool() ? 'P' : 'N'; - - RTLIL::SigSpec sig_clk = cell->getPort(ID::CLK); - RTLIL::SigSpec sig_d = cell->getPort(ID::D); - RTLIL::SigSpec sig_q = cell->getPort(ID::Q); - - IdString gate_type = stringf("$_DFF_%c_", clk_pol); - - for (int i = 0; i < width; i++) { - RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type); - gate->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src)); - gate->setPort(ID::C, sig_clk); - gate->setPort(ID::D, sig_d[i]); - gate->setPort(ID::Q, sig_q[i]); - } -} - -void simplemap_dffe(RTLIL::Module *module, RTLIL::Cell *cell) -{ - int width = cell->parameters.at(ID::WIDTH).as_int(); - char clk_pol = cell->parameters.at(ID::CLK_POLARITY).as_bool() ? 'P' : 'N'; - char en_pol = cell->parameters.at(ID::EN_POLARITY).as_bool() ? 'P' : 'N'; - - RTLIL::SigSpec sig_clk = cell->getPort(ID::CLK); - RTLIL::SigSpec sig_en = cell->getPort(ID::EN); - RTLIL::SigSpec sig_d = cell->getPort(ID::D); - RTLIL::SigSpec sig_q = cell->getPort(ID::Q); - - IdString gate_type = stringf("$_DFFE_%c%c_", clk_pol, en_pol); - - for (int i = 0; i < width; i++) { - RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type); - gate->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src)); - gate->setPort(ID::C, sig_clk); - gate->setPort(ID::E, sig_en); - gate->setPort(ID::D, sig_d[i]); - gate->setPort(ID::Q, sig_q[i]); - } -} - -void simplemap_dffsr(RTLIL::Module *module, RTLIL::Cell *cell) -{ - int width = cell->parameters.at(ID::WIDTH).as_int(); - char clk_pol = cell->parameters.at(ID::CLK_POLARITY).as_bool() ? 'P' : 'N'; - char set_pol = cell->parameters.at(ID::SET_POLARITY).as_bool() ? 'P' : 'N'; - char clr_pol = cell->parameters.at(ID::CLR_POLARITY).as_bool() ? 'P' : 'N'; - - RTLIL::SigSpec sig_clk = cell->getPort(ID::CLK); - RTLIL::SigSpec sig_s = cell->getPort(ID::SET); - RTLIL::SigSpec sig_r = cell->getPort(ID::CLR); - RTLIL::SigSpec sig_d = cell->getPort(ID::D); - RTLIL::SigSpec sig_q = cell->getPort(ID::Q); - - IdString gate_type = stringf("$_DFFSR_%c%c%c_", clk_pol, set_pol, clr_pol); - - for (int i = 0; i < width; i++) { - RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type); - gate->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src)); - gate->setPort(ID::C, sig_clk); - gate->setPort(ID::S, sig_s[i]); - gate->setPort(ID::R, sig_r[i]); - gate->setPort(ID::D, sig_d[i]); - gate->setPort(ID::Q, sig_q[i]); - } -} - -void simplemap_dffsre(RTLIL::Module *module, RTLIL::Cell *cell) -{ - int width = cell->parameters.at(ID::WIDTH).as_int(); - char clk_pol = cell->parameters.at(ID::CLK_POLARITY).as_bool() ? 'P' : 'N'; - char set_pol = cell->parameters.at(ID::SET_POLARITY).as_bool() ? 'P' : 'N'; - char clr_pol = cell->parameters.at(ID::CLR_POLARITY).as_bool() ? 'P' : 'N'; - char en_pol = cell->parameters.at(ID::EN_POLARITY).as_bool() ? 'P' : 'N'; - - RTLIL::SigSpec sig_clk = cell->getPort(ID::CLK); - RTLIL::SigSpec sig_s = cell->getPort(ID::SET); - RTLIL::SigSpec sig_r = cell->getPort(ID::CLR); - RTLIL::SigSpec sig_e = cell->getPort(ID::EN); - RTLIL::SigSpec sig_d = cell->getPort(ID::D); - RTLIL::SigSpec sig_q = cell->getPort(ID::Q); - - IdString gate_type = stringf("$_DFFSRE_%c%c%c%c_", clk_pol, set_pol, clr_pol, en_pol); - - for (int i = 0; i < width; i++) { - RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type); - gate->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src)); - gate->setPort(ID::C, sig_clk); - gate->setPort(ID::S, sig_s[i]); - gate->setPort(ID::R, sig_r[i]); - gate->setPort(ID::E, sig_e); - gate->setPort(ID::D, sig_d[i]); - gate->setPort(ID::Q, sig_q[i]); - } -} - -void simplemap_adff_sdff(RTLIL::Module *module, RTLIL::Cell *cell) -{ - int width = cell->parameters.at(ID::WIDTH).as_int(); - bool is_async = cell->type == ID($adff); - char clk_pol = cell->parameters.at(ID::CLK_POLARITY).as_bool() ? 'P' : 'N'; - char rst_pol = cell->parameters.at(is_async ? ID::ARST_POLARITY : ID::SRST_POLARITY).as_bool() ? 'P' : 'N'; - const char *type = is_async ? "DFF" : "SDFF"; - - std::vector<RTLIL::State> rst_val = cell->parameters.at(is_async ? ID::ARST_VALUE : ID::SRST_VALUE).bits; - while (int(rst_val.size()) < width) - rst_val.push_back(RTLIL::State::S0); - - RTLIL::SigSpec sig_clk = cell->getPort(ID::CLK); - RTLIL::SigSpec sig_rst = cell->getPort(is_async ? ID::ARST : ID::SRST); - RTLIL::SigSpec sig_d = cell->getPort(ID::D); - RTLIL::SigSpec sig_q = cell->getPort(ID::Q); - - IdString gate_type_0 = stringf("$_%s_%c%c0_", type, clk_pol, rst_pol); - IdString gate_type_1 = stringf("$_%s_%c%c1_", type, clk_pol, rst_pol); - - for (int i = 0; i < width; i++) { - RTLIL::Cell *gate = module->addCell(NEW_ID, rst_val.at(i) == RTLIL::State::S1 ? gate_type_1 : gate_type_0); - gate->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src)); - gate->setPort(ID::C, sig_clk); - gate->setPort(ID::R, sig_rst); - gate->setPort(ID::D, sig_d[i]); - gate->setPort(ID::Q, sig_q[i]); - } -} - -void simplemap_adffe_sdffe_sdffce(RTLIL::Module *module, RTLIL::Cell *cell) -{ - int width = cell->parameters.at(ID::WIDTH).as_int(); - bool is_async = cell->type == ID($adffe); - char clk_pol = cell->parameters.at(ID::CLK_POLARITY).as_bool() ? 'P' : 'N'; - char rst_pol = cell->parameters.at(is_async ? ID::ARST_POLARITY : ID::SRST_POLARITY).as_bool() ? 'P' : 'N'; - char en_pol = cell->parameters.at(ID::EN_POLARITY).as_bool() ? 'P' : 'N'; - const char *type = is_async ? "DFFE" : cell->type == ID($sdffe) ? "SDFFE" : "SDFFCE"; - - std::vector<RTLIL::State> rst_val = cell->parameters.at(is_async ? ID::ARST_VALUE : ID::SRST_VALUE).bits; - while (int(rst_val.size()) < width) - rst_val.push_back(RTLIL::State::S0); - - RTLIL::SigSpec sig_clk = cell->getPort(ID::CLK); - RTLIL::SigSpec sig_rst = cell->getPort(is_async ? ID::ARST : ID::SRST); - RTLIL::SigSpec sig_e = cell->getPort(ID::EN); - RTLIL::SigSpec sig_d = cell->getPort(ID::D); - RTLIL::SigSpec sig_q = cell->getPort(ID::Q); - - IdString gate_type_0 = stringf("$_%s_%c%c0%c_", type, clk_pol, rst_pol, en_pol); - IdString gate_type_1 = stringf("$_%s_%c%c1%c_", type, clk_pol, rst_pol, en_pol); - - for (int i = 0; i < width; i++) { - RTLIL::Cell *gate = module->addCell(NEW_ID, rst_val.at(i) == RTLIL::State::S1 ? gate_type_1 : gate_type_0); - gate->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src)); - gate->setPort(ID::C, sig_clk); - gate->setPort(ID::R, sig_rst); - gate->setPort(ID::E, sig_e); - gate->setPort(ID::D, sig_d[i]); - gate->setPort(ID::Q, sig_q[i]); - } -} - -void simplemap_dlatch(RTLIL::Module *module, RTLIL::Cell *cell) -{ - int width = cell->parameters.at(ID::WIDTH).as_int(); - char en_pol = cell->parameters.at(ID::EN_POLARITY).as_bool() ? 'P' : 'N'; - - RTLIL::SigSpec sig_en = cell->getPort(ID::EN); - RTLIL::SigSpec sig_d = cell->getPort(ID::D); - RTLIL::SigSpec sig_q = cell->getPort(ID::Q); - - IdString gate_type = stringf("$_DLATCH_%c_", en_pol); - - for (int i = 0; i < width; i++) { - RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type); - gate->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src)); - gate->setPort(ID::E, sig_en); - gate->setPort(ID::D, sig_d[i]); - gate->setPort(ID::Q, sig_q[i]); - } -} - -void simplemap_adlatch(RTLIL::Module *module, RTLIL::Cell *cell) -{ - int width = cell->parameters.at(ID::WIDTH).as_int(); - char en_pol = cell->parameters.at(ID::EN_POLARITY).as_bool() ? 'P' : 'N'; - char rst_pol = cell->parameters.at(ID::ARST_POLARITY).as_bool() ? 'P' : 'N'; - - std::vector<RTLIL::State> rst_val = cell->parameters.at(ID::ARST_VALUE).bits; - while (int(rst_val.size()) < width) - rst_val.push_back(RTLIL::State::S0); - - RTLIL::SigSpec sig_en = cell->getPort(ID::EN); - RTLIL::SigSpec sig_rst = cell->getPort(ID::ARST); - RTLIL::SigSpec sig_d = cell->getPort(ID::D); - RTLIL::SigSpec sig_q = cell->getPort(ID::Q); - - IdString gate_type_0 = stringf("$_DLATCH_%c%c0_", en_pol, rst_pol); - IdString gate_type_1 = stringf("$_DLATCH_%c%c1_", en_pol, rst_pol); - - for (int i = 0; i < width; i++) { - RTLIL::Cell *gate = module->addCell(NEW_ID, rst_val.at(i) == RTLIL::State::S1 ? gate_type_1 : gate_type_0); - gate->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src)); - gate->setPort(ID::E, sig_en); - gate->setPort(ID::R, sig_rst); - gate->setPort(ID::D, sig_d[i]); - gate->setPort(ID::Q, sig_q[i]); - } -} - -void simplemap_dlatchsr(RTLIL::Module *module, RTLIL::Cell *cell) -{ - int width = cell->parameters.at(ID::WIDTH).as_int(); - char en_pol = cell->parameters.at(ID::EN_POLARITY).as_bool() ? 'P' : 'N'; - char set_pol = cell->parameters.at(ID::SET_POLARITY).as_bool() ? 'P' : 'N'; - char clr_pol = cell->parameters.at(ID::CLR_POLARITY).as_bool() ? 'P' : 'N'; - - RTLIL::SigSpec sig_en = cell->getPort(ID::EN); - RTLIL::SigSpec sig_s = cell->getPort(ID::SET); - RTLIL::SigSpec sig_r = cell->getPort(ID::CLR); - RTLIL::SigSpec sig_d = cell->getPort(ID::D); - RTLIL::SigSpec sig_q = cell->getPort(ID::Q); - - IdString gate_type = stringf("$_DLATCHSR_%c%c%c_", en_pol, set_pol, clr_pol); - - for (int i = 0; i < width; i++) { - RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type); - gate->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src)); - gate->setPort(ID::E, sig_en); - gate->setPort(ID::S, sig_s[i]); - gate->setPort(ID::R, sig_r[i]); - gate->setPort(ID::D, sig_d[i]); - gate->setPort(ID::Q, sig_q[i]); + FfData ff(nullptr, cell); + for (int i = 0; i < ff.width; i++) { + FfData fff = ff.slice({i}); + fff.is_fine = true; + fff.emit(); } } @@ -662,24 +423,27 @@ void simplemap_get_mappers(dict<IdString, void(*)(RTLIL::Module*, RTLIL::Cell*)> mappers[ID($nex)] = simplemap_eqne; mappers[ID($mux)] = simplemap_mux; mappers[ID($tribuf)] = simplemap_tribuf; + mappers[ID($bmux)] = simplemap_bmux; mappers[ID($lut)] = simplemap_lut; mappers[ID($sop)] = simplemap_sop; mappers[ID($slice)] = simplemap_slice; mappers[ID($concat)] = simplemap_concat; - mappers[ID($sr)] = simplemap_sr; + mappers[ID($sr)] = simplemap_ff; mappers[ID($ff)] = simplemap_ff; - mappers[ID($dff)] = simplemap_dff; - mappers[ID($dffe)] = simplemap_dffe; - mappers[ID($dffsr)] = simplemap_dffsr; - mappers[ID($dffsre)] = simplemap_dffsre; - mappers[ID($adff)] = simplemap_adff_sdff; - mappers[ID($sdff)] = simplemap_adff_sdff; - mappers[ID($adffe)] = simplemap_adffe_sdffe_sdffce; - mappers[ID($sdffe)] = simplemap_adffe_sdffe_sdffce; - mappers[ID($sdffce)] = simplemap_adffe_sdffe_sdffce; - mappers[ID($dlatch)] = simplemap_dlatch; - mappers[ID($adlatch)] = simplemap_adlatch; - mappers[ID($dlatchsr)] = simplemap_dlatchsr; + mappers[ID($dff)] = simplemap_ff; + mappers[ID($dffe)] = simplemap_ff; + mappers[ID($dffsr)] = simplemap_ff; + mappers[ID($dffsre)] = simplemap_ff; + mappers[ID($adff)] = simplemap_ff; + mappers[ID($sdff)] = simplemap_ff; + mappers[ID($adffe)] = simplemap_ff; + mappers[ID($sdffe)] = simplemap_ff; + mappers[ID($sdffce)] = simplemap_ff; + mappers[ID($aldff)] = simplemap_ff; + mappers[ID($aldffe)] = simplemap_ff; + mappers[ID($dlatch)] = simplemap_ff; + mappers[ID($adlatch)] = simplemap_ff; + mappers[ID($dlatchsr)] = simplemap_ff; } void simplemap(RTLIL::Module *module, RTLIL::Cell *cell) @@ -712,7 +476,7 @@ struct SimplemapPass : public Pass { log(" $not, $pos, $and, $or, $xor, $xnor\n"); log(" $reduce_and, $reduce_or, $reduce_xor, $reduce_xnor, $reduce_bool\n"); log(" $logic_not, $logic_and, $logic_or, $mux, $tribuf\n"); - log(" $sr, $ff, $dff, $dffe, $dffsr, $dffsre, $adff, $adffe, $sdff, $sdffe, $sdffce, $dlatch, $adlatch, $dlatchsr\n"); + log(" $sr, $ff, $dff, $dffe, $dffsr, $dffsre, $adff, $adffe, $aldff, $aldffe, $sdff, $sdffe, $sdffce, $dlatch, $adlatch, $dlatchsr\n"); log("\n"); } void execute(std::vector<std::string> args, RTLIL::Design *design) override diff --git a/passes/techmap/simplemap.h b/passes/techmap/simplemap.h index 5091050a1..c7654f68c 100644 --- a/passes/techmap/simplemap.h +++ b/passes/techmap/simplemap.h @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -34,12 +34,7 @@ extern void simplemap_mux(RTLIL::Module *module, RTLIL::Cell *cell); extern void simplemap_lut(RTLIL::Module *module, RTLIL::Cell *cell); extern void simplemap_slice(RTLIL::Module *module, RTLIL::Cell *cell); extern void simplemap_concat(RTLIL::Module *module, RTLIL::Cell *cell); -extern void simplemap_sr(RTLIL::Module *module, RTLIL::Cell *cell); -extern void simplemap_dff(RTLIL::Module *module, RTLIL::Cell *cell); -extern void simplemap_dffe(RTLIL::Module *module, RTLIL::Cell *cell); -extern void simplemap_dffsr(RTLIL::Module *module, RTLIL::Cell *cell); -extern void simplemap_adff(RTLIL::Module *module, RTLIL::Cell *cell); -extern void simplemap_dlatch(RTLIL::Module *module, RTLIL::Cell *cell); +extern void simplemap_ff(RTLIL::Module *module, RTLIL::Cell *cell); extern void simplemap(RTLIL::Module *module, RTLIL::Cell *cell); extern void simplemap_get_mappers(dict<RTLIL::IdString, void(*)(RTLIL::Module*, RTLIL::Cell*)> &mappers); diff --git a/passes/techmap/techmap.cc b/passes/techmap/techmap.cc index 96843d710..5cd78fe28 100644 --- a/passes/techmap/techmap.cc +++ b/passes/techmap/techmap.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -364,13 +364,11 @@ struct TechmapWorker for (auto &it2 : autopurge_ports) c->unsetPort(it2); - if (c->type.in(ID($memrd), ID($memwr), ID($meminit))) { + if (c->has_memid()) { IdString memid = c->getParam(ID::MEMID).decode_string(); log_assert(memory_renames.count(memid) != 0); c->setParam(ID::MEMID, Const(memory_renames[memid].str())); - } - - if (c->type == ID($mem)) { + } else if (c->is_mem_cell()) { IdString memid = c->getParam(ID::MEMID).decode_string(); apply_prefix(cell->name, memid); c->setParam(ID::MEMID, Const(memid.c_str())); @@ -379,10 +377,12 @@ struct TechmapWorker if (c->attributes.count(ID::src)) c->add_strpool_attribute(ID::src, extra_src_attrs); - if (techmap_replace_cell) + if (techmap_replace_cell) { for (auto attr : cell->attributes) if (!c->attributes.count(attr.first)) c->attributes[attr.first] = attr.second; + c->attributes.erase(ID::reprocess_after); + } } for (auto &it : tpl->connections()) { diff --git a/passes/techmap/tribuf.cc b/passes/techmap/tribuf.cc index 79ddb4bd7..f92b4cdb0 100644 --- a/passes/techmap/tribuf.cc +++ b/passes/techmap/tribuf.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/techmap/zinit.cc b/passes/techmap/zinit.cc index e3b4ae573..cc208c516 100644 --- a/passes/techmap/zinit.cc +++ b/passes/techmap/zinit.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -20,6 +20,7 @@ #include "kernel/yosys.h" #include "kernel/sigtools.h" #include "kernel/ffinit.h" +#include "kernel/ff.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -60,123 +61,25 @@ struct ZinitPass : public Pass { SigMap sigmap(module); FfInitVals initvals(&sigmap, module); - pool<IdString> dff_types = { - // FIXME: It would appear that supporting - // $dffsr/$_DFFSR_* would require a new - // cell type where S has priority over R - ID($ff), ID($dff), ID($dffe), /*ID($dffsr),*/ ID($adff), ID($adffe), - ID($sdff), ID($sdffe), ID($sdffce), - ID($_FF_), ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_), - /*ID($_DFFSR_NNN_), ID($_DFFSR_NNP_), ID($_DFFSR_NPN_), ID($_DFFSR_NPP_), - ID($_DFFSR_PNN_), ID($_DFFSR_PNP_), ID($_DFFSR_PPN_), ID($_DFFSR_PPP_),*/ - ID($_DFF_N_), ID($_DFF_NN0_), ID($_DFF_NN1_), ID($_DFF_NP0_), ID($_DFF_NP1_), - ID($_DFF_P_), ID($_DFF_PN0_), ID($_DFF_PN1_), ID($_DFF_PP0_), ID($_DFF_PP1_), - // Async set/reset - ID($_DFFE_NN0P_), ID($_DFFE_NN1P_), ID($_DFFE_NP0P_), ID($_DFFE_NP1P_), - ID($_DFFE_PN0P_), ID($_DFFE_PN1P_), ID($_DFFE_PP0P_), ID($_DFFE_PP1P_), - ID($_DFFE_NN0N_), ID($_DFFE_NN1N_), ID($_DFFE_NP0N_), ID($_DFFE_NP1N_), - ID($_DFFE_PN0N_), ID($_DFFE_PN1N_), ID($_DFFE_PP0N_), ID($_DFFE_PP1N_), - // Sync set/reset - ID($_SDFF_NN0_), ID($_SDFF_NN1_), ID($_SDFF_NP0_), ID($_SDFF_NP1_), - ID($_SDFF_PN0_), ID($_SDFF_PN1_), ID($_SDFF_PP0_), ID($_SDFF_PP1_), - ID($_SDFFE_NN0P_), ID($_SDFFE_NN1P_), ID($_SDFFE_NP0P_), ID($_SDFFE_NP1P_), - ID($_SDFFE_PN0P_), ID($_SDFFE_PN1P_), ID($_SDFFE_PP0P_), ID($_SDFFE_PP1P_), - ID($_SDFFE_NN0N_), ID($_SDFFE_NN1N_), ID($_SDFFE_NP0N_), ID($_SDFFE_NP1N_), - ID($_SDFFE_PN0N_), ID($_SDFFE_PN1N_), ID($_SDFFE_PP0N_), ID($_SDFFE_PP1N_), - ID($_SDFFCE_NN0P_), ID($_SDFFCE_NN1P_), ID($_SDFFCE_NP0P_), ID($_SDFFCE_NP1P_), - ID($_SDFFCE_PN0P_), ID($_SDFFCE_PN1P_), ID($_SDFFCE_PP0P_), ID($_SDFFCE_PP1P_), - ID($_SDFFCE_NN0N_), ID($_SDFFCE_NN1N_), ID($_SDFFCE_NP0N_), ID($_SDFFCE_NP1N_), - ID($_SDFFCE_PN0N_), ID($_SDFFCE_PN1N_), ID($_SDFFCE_PP0N_), ID($_SDFFCE_PP1N_) - }; - for (auto cell : module->selected_cells()) { - if (!dff_types.count(cell->type)) - continue; - - SigSpec sig_d = sigmap(cell->getPort(ID::D)); - SigSpec sig_q = sigmap(cell->getPort(ID::Q)); - - if (GetSize(sig_d) < 1 || GetSize(sig_q) < 1) + if (!RTLIL::builtin_ff_cell_types().count(cell->type)) continue; - Const initval = initvals(sig_q); - Const newval = initval; - initvals.remove_init(sig_q); - - Wire *initwire = module->addWire(NEW_ID, GetSize(sig_q)); - - for (int i = 0; i < GetSize(initwire); i++) - if (initval[i] == State::S1) - { - sig_d[i] = module->NotGate(NEW_ID, sig_d[i]); - module->addNotGate(NEW_ID, SigSpec(initwire, i), sig_q[i]); - newval[i] = State::S0; - } - else - { - module->connect(sig_q[i], SigSpec(initwire, i)); - if (all_mode) - newval[i] = State::S0; - } - - initvals.set_init(initwire, newval); + FfData ff(&initvals, cell); log("FF init value for cell %s (%s): %s = %s\n", log_id(cell), log_id(cell->type), - log_signal(sig_q), log_signal(initval)); - - cell->setPort(ID::D, sig_d); - cell->setPort(ID::Q, initwire); - - if (cell->type.in(ID($adff), ID($adffe))) { - auto val = cell->getParam(ID::ARST_VALUE); - for (int i = 0; i < GetSize(initwire); i++) - if (initval[i] == State::S1) - val[i] = (val[i] == State::S1 ? State::S0 : State::S1); - cell->setParam(ID::ARST_VALUE, std::move(val)); - } - else if (cell->type.in(ID($sdff), ID($sdffe), ID($sdffce))) { - auto val = cell->getParam(ID::SRST_VALUE); - for (int i = 0; i < GetSize(initwire); i++) - if (initval[i] == State::S1) - val[i] = (val[i] == State::S1 ? State::S0 : State::S1); - cell->setParam(ID::SRST_VALUE, std::move(val)); - } - else if (initval == State::S1) { - std::string t = cell->type.str(); - if (cell->type.in(ID($_DFF_NN0_), ID($_DFF_NN1_), ID($_DFF_NP0_), ID($_DFF_NP1_), - ID($_DFF_PN0_), ID($_DFF_PN1_), ID($_DFF_PP0_), ID($_DFF_PP1_))) - { - t[8] = (t[8] == '0' ? '1' : '0'); - } - else if (cell->type.in(ID($_SDFF_NN0_), ID($_SDFF_NN1_), ID($_SDFF_NP0_), ID($_SDFF_NP1_), - ID($_SDFF_PN0_), ID($_SDFF_PN1_), ID($_SDFF_PP0_), ID($_SDFF_PP1_))) - { - t[9] = (t[9] == '0' ? '1' : '0'); - } - else if (cell->type.in(ID($_DFFE_NN0P_), ID($_DFFE_NN1P_), ID($_DFFE_NP0P_), ID($_DFFE_NP1P_), - ID($_DFFE_PN0P_), ID($_DFFE_PN1P_), ID($_DFFE_PP0P_), ID($_DFFE_PP1P_), - ID($_DFFE_NN0N_), ID($_DFFE_NN1N_), ID($_DFFE_NP0N_), ID($_DFFE_NP1N_), - ID($_DFFE_PN0N_), ID($_DFFE_PN1N_), ID($_DFFE_PP0N_), ID($_DFFE_PP1N_))) - { - t[9] = (t[9] == '0' ? '1' : '0'); - } - else if (cell->type.in(ID($_SDFFE_NN0P_), ID($_SDFFE_NN1P_), ID($_SDFFE_NP0P_), ID($_SDFFE_NP1P_), - ID($_SDFFE_PN0P_), ID($_SDFFE_PN1P_), ID($_SDFFE_PP0P_), ID($_SDFFE_PP1P_), - ID($_SDFFE_NN0N_), ID($_SDFFE_NN1N_), ID($_SDFFE_NP0N_), ID($_SDFFE_NP1N_), - ID($_SDFFE_PN0N_), ID($_SDFFE_PN1N_), ID($_SDFFE_PP0N_), ID($_SDFFE_PP1N_))) - { - t[10] = (t[10] == '0' ? '1' : '0'); - } - else if (cell->type.in(ID($_SDFFCE_NN0P_), ID($_SDFFCE_NN1P_), ID($_SDFFCE_NP0P_), ID($_SDFFCE_NP1P_), - ID($_SDFFCE_PN0P_), ID($_SDFFCE_PN1P_), ID($_SDFFCE_PP0P_), ID($_SDFFCE_PP1P_), - ID($_SDFFCE_NN0N_), ID($_SDFFCE_NN1N_), ID($_SDFFCE_NP0N_), ID($_SDFFCE_NP1N_), - ID($_SDFFCE_PN0N_), ID($_SDFFCE_PN1N_), ID($_SDFFCE_PP0N_), ID($_SDFFCE_PP1N_))) - { - t[11] = (t[11] == '0' ? '1' : '0'); - } - cell->type = t; + log_signal(ff.sig_q), log_signal(ff.val_init)); + + pool<int> bits; + for (int i = 0; i < ff.width; i++) { + if (ff.val_init.bits[i] == State::S1) + bits.insert(i); + else if (ff.val_init.bits[i] != State::S0 && all_mode) + ff.val_init.bits[i] = State::S0; } + ff.flip_bits(bits); + ff.emit(); } } } diff --git a/passes/tests/test_abcloop.cc b/passes/tests/test_abcloop.cc index ac31e36f1..9e7adaab1 100644 --- a/passes/tests/test_abcloop.cc +++ b/passes/tests/test_abcloop.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2014 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2014 Claire Xenia Wolf <claire@yosyshq.com> * Copyright (C) 2014 Johann Glaser <Johann.Glaser@gmx.at> * * Permission to use, copy, modify, and/or distribute this software for any diff --git a/passes/tests/test_autotb.cc b/passes/tests/test_autotb.cc index 4ab46014d..404d1e48d 100644 --- a/passes/tests/test_autotb.cc +++ b/passes/tests/test_autotb.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/passes/tests/test_cell.cc b/passes/tests/test_cell.cc index 616981f32..e21ec452c 100644 --- a/passes/tests/test_cell.cc +++ b/passes/tests/test_cell.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2014 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2014 Claire Xenia Wolf <claire@yosyshq.com> * Copyright (C) 2014 Johann Glaser <Johann.Glaser@gmx.at> * * Permission to use, copy, modify, and/or distribute this software for any @@ -69,6 +69,48 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type, cell->setPort(ID::Y, wire); } + if (cell_type == ID($bmux)) + { + int width = 1 + xorshift32(8); + int swidth = 1 + xorshift32(4); + + wire = module->addWire(ID::A); + wire->width = width << swidth; + wire->port_input = true; + cell->setPort(ID::A, wire); + + wire = module->addWire(ID::S); + wire->width = swidth; + wire->port_input = true; + cell->setPort(ID::S, wire); + + wire = module->addWire(ID::Y); + wire->width = width; + wire->port_output = true; + cell->setPort(ID::Y, wire); + } + + if (cell_type == ID($demux)) + { + int width = 1 + xorshift32(8); + int swidth = 1 + xorshift32(6); + + wire = module->addWire(ID::A); + wire->width = width; + wire->port_input = true; + cell->setPort(ID::A, wire); + + wire = module->addWire(ID::S); + wire->width = swidth; + wire->port_input = true; + cell->setPort(ID::S, wire); + + wire = module->addWire(ID::Y); + wire->width = width << swidth; + wire->port_output = true; + cell->setPort(ID::Y, wire); + } + if (cell_type == ID($fa)) { int width = 1 + xorshift32(8); @@ -855,8 +897,10 @@ struct TestCellPass : public Pass { cell_types[ID($logic_and)] = "ABSY"; cell_types[ID($logic_or)] = "ABSY"; + cell_types[ID($mux)] = "*"; + cell_types[ID($bmux)] = "*"; + cell_types[ID($demux)] = "*"; if (edges) { - cell_types[ID($mux)] = "*"; cell_types[ID($pmux)] = "*"; } |