diff options
Diffstat (limited to 'passes')
110 files changed, 9346 insertions, 1863 deletions
diff --git a/passes/abc/Makefile.inc b/passes/abc/Makefile.inc deleted file mode 100644 index a7c5b02c6..000000000 --- a/passes/abc/Makefile.inc +++ /dev/null @@ -1,6 +0,0 @@ - -ifeq ($(ENABLE_ABC),1) -OBJS += passes/abc/abc.o -OBJS += passes/abc/blifparse.o -endif - diff --git a/passes/abc/blifparse.cc b/passes/abc/blifparse.cc deleted file mode 100644 index db87eec4a..000000000 --- a/passes/abc/blifparse.cc +++ /dev/null @@ -1,244 +0,0 @@ -/* - * yosys -- Yosys Open SYnthesis Suite - * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#include "blifparse.h" - -YOSYS_NAMESPACE_BEGIN - -static bool read_next_line(char *&buffer, size_t &buffer_size, int &line_count, FILE *f) -{ - int buffer_len = 0; - buffer[0] = 0; - - while (1) - { - buffer_len += strlen(buffer + buffer_len); - while (buffer_len > 0 && (buffer[buffer_len-1] == ' ' || buffer[buffer_len-1] == '\t' || - buffer[buffer_len-1] == '\r' || buffer[buffer_len-1] == '\n')) - buffer[--buffer_len] = 0; - - if (buffer_size-buffer_len < 4096) { - buffer_size *= 2; - buffer = (char*)realloc(buffer, buffer_size); - } - - if (buffer_len == 0 || buffer[buffer_len-1] == '\\') { - if (buffer_len > 0 && buffer[buffer_len-1] == '\\') - buffer[--buffer_len] = 0; - line_count++; - if (fgets(buffer+buffer_len, buffer_size-buffer_len, f) == NULL) - return false; - } else - return true; - } -} - -RTLIL::Design *abc_parse_blif(FILE *f, std::string dff_name) -{ - RTLIL::Design *design = new RTLIL::Design; - RTLIL::Module *module = new RTLIL::Module; - - RTLIL::Const *lutptr = NULL; - RTLIL::State lut_default_state = RTLIL::State::Sx; - - module->name = "\\netlist"; - design->add(module); - - size_t buffer_size = 4096; - char *buffer = (char*)malloc(buffer_size); - int line_count = 0; - - while (1) - { - if (!read_next_line(buffer, buffer_size, line_count, f)) - goto error; - - continue_without_read: - if (buffer[0] == '#') - continue; - - if (buffer[0] == '.') - { - if (lutptr) { - for (auto &bit : lutptr->bits) - if (bit == RTLIL::State::Sx) - bit = lut_default_state; - lutptr = NULL; - lut_default_state = RTLIL::State::Sx; - } - - char *cmd = strtok(buffer, " \t\r\n"); - - if (!strcmp(cmd, ".model")) - continue; - - if (!strcmp(cmd, ".end")) { - module->fixup_ports(); - free(buffer); - return design; - } - - if (!strcmp(cmd, ".inputs") || !strcmp(cmd, ".outputs")) { - char *p; - while ((p = strtok(NULL, " \t\r\n")) != NULL) { - RTLIL::Wire *wire = module->addWire(stringf("\\%s", p)); - if (!strcmp(cmd, ".inputs")) - wire->port_input = true; - else - wire->port_output = true; - } - continue; - } - - if (!strcmp(cmd, ".latch")) - { - char *d = strtok(NULL, " \t\r\n"); - char *q = strtok(NULL, " \t\r\n"); - - if (module->wires_.count(RTLIL::escape_id(d)) == 0) - module->addWire(RTLIL::escape_id(d)); - - if (module->wires_.count(RTLIL::escape_id(q)) == 0) - module->addWire(RTLIL::escape_id(q)); - - RTLIL::Cell *cell = module->addCell(NEW_ID, dff_name); - cell->setPort("\\D", module->wires_.at(RTLIL::escape_id(d))); - cell->setPort("\\Q", module->wires_.at(RTLIL::escape_id(q))); - continue; - } - - if (!strcmp(cmd, ".gate")) - { - char *p = strtok(NULL, " \t\r\n"); - if (p == NULL) - goto error; - - IdString celltype = RTLIL::escape_id(p); - RTLIL::Cell *cell = module->addCell(NEW_ID, celltype); - - while ((p = strtok(NULL, " \t\r\n")) != NULL) { - char *q = strchr(p, '='); - if (q == NULL || !q[0] || !q[1]) - goto error; - *(q++) = 0; - if (module->wires_.count(RTLIL::escape_id(q)) == 0) - module->addWire(RTLIL::escape_id(q)); - cell->setPort(RTLIL::escape_id(p), module->wires_.at(RTLIL::escape_id(q))); - } - continue; - } - - if (!strcmp(cmd, ".names")) - { - char *p; - RTLIL::SigSpec input_sig, output_sig; - while ((p = strtok(NULL, " \t\r\n")) != NULL) { - RTLIL::Wire *wire; - if (module->wires_.count(stringf("\\%s", p)) > 0) { - wire = module->wires_.at(stringf("\\%s", p)); - } else { - wire = module->addWire(stringf("\\%s", p)); - } - input_sig.append(wire); - } - output_sig = input_sig.extract(input_sig.size()-1, 1); - input_sig = input_sig.extract(0, input_sig.size()-1); - - if (input_sig.size() == 0) { - RTLIL::State state = RTLIL::State::Sa; - while (1) { - if (!read_next_line(buffer, buffer_size, line_count, f)) - goto error; - for (int i = 0; buffer[i]; i++) { - if (buffer[i] == ' ' || buffer[i] == '\t') - continue; - if (i == 0 && buffer[i] == '.') - goto finished_parsing_constval; - if (buffer[i] == '0') { - if (state == RTLIL::State::S1) - goto error; - state = RTLIL::State::S0; - continue; - } - if (buffer[i] == '1') { - if (state == RTLIL::State::S0) - goto error; - state = RTLIL::State::S1; - continue; - } - goto error; - } - } - finished_parsing_constval: - if (state == RTLIL::State::Sa) - state = RTLIL::State::S1; - module->connect(RTLIL::SigSig(output_sig, state)); - goto continue_without_read; - } - - RTLIL::Cell *cell = module->addCell(NEW_ID, "$lut"); - cell->parameters["\\WIDTH"] = RTLIL::Const(input_sig.size()); - cell->parameters["\\LUT"] = RTLIL::Const(RTLIL::State::Sx, 1 << input_sig.size()); - cell->setPort("\\A", input_sig); - cell->setPort("\\Y", output_sig); - lutptr = &cell->parameters.at("\\LUT"); - lut_default_state = RTLIL::State::Sx; - continue; - } - - goto error; - } - - if (lutptr == NULL) - goto error; - - char *input = strtok(buffer, " \t\r\n"); - char *output = strtok(NULL, " \t\r\n"); - - if (input == NULL || output == NULL || (strcmp(output, "0") && strcmp(output, "1"))) - goto error; - - int input_len = strlen(input); - if (input_len > 8) - goto error; - - for (int i = 0; i < (1 << input_len); i++) { - for (int j = 0; j < input_len; j++) { - char c1 = input[j]; - if (c1 != '-') { - char c2 = (i & (1 << j)) != 0 ? '1' : '0'; - if (c1 != c2) - goto try_next_value; - } - } - lutptr->bits.at(i) = !strcmp(output, "0") ? RTLIL::State::S0 : RTLIL::State::S1; - try_next_value:; - } - - lut_default_state = !strcmp(output, "0") ? RTLIL::State::S1 : RTLIL::State::S0; - } - -error: - log_error("Syntax error in line %d!\n", line_count); - // delete design; - // return NULL; -} - -YOSYS_NAMESPACE_END - diff --git a/passes/abc/blifparse.h b/passes/abc/blifparse.h deleted file mode 100644 index 31f5b2e5c..000000000 --- a/passes/abc/blifparse.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * yosys -- Yosys Open SYnthesis Suite - * - * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#ifndef ABC_BLIFPARSE -#define ABC_BLIFPARSE - -#include "kernel/yosys.h" - -YOSYS_NAMESPACE_BEGIN - -extern RTLIL::Design *abc_parse_blif(FILE *f, std::string dff_name); - -YOSYS_NAMESPACE_END - -#endif diff --git a/passes/cmds/Makefile.inc b/passes/cmds/Makefile.inc index 0e62abbc4..01ada7739 100644 --- a/passes/cmds/Makefile.inc +++ b/passes/cmds/Makefile.inc @@ -14,6 +14,7 @@ OBJS += passes/cmds/setattr.o OBJS += passes/cmds/copy.o OBJS += passes/cmds/splice.o OBJS += passes/cmds/scc.o +OBJS += passes/cmds/torder.o OBJS += passes/cmds/logcmd.o OBJS += passes/cmds/tee.o OBJS += passes/cmds/write_file.o @@ -21,4 +22,7 @@ OBJS += passes/cmds/connwrappers.o OBJS += passes/cmds/cover.o OBJS += passes/cmds/trace.o OBJS += passes/cmds/plugin.o +OBJS += passes/cmds/check.o +OBJS += passes/cmds/qwp.o +OBJS += passes/cmds/edgetypes.o diff --git a/passes/cmds/add.cc b/passes/cmds/add.cc index 054cfc1cb..e698926f9 100644 --- a/passes/cmds/add.cc +++ b/passes/cmds/add.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -150,5 +150,5 @@ struct AddPass : public Pass { } } } AddPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/cmds/check.cc b/passes/cmds/check.cc new file mode 100644 index 000000000..b3622cb19 --- /dev/null +++ b/passes/cmds/check.cc @@ -0,0 +1,162 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include "kernel/sigtools.h" +#include "kernel/celltypes.h" +#include "kernel/utils.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct CheckPass : public Pass { + CheckPass() : Pass("check", "check for obvious problems in the design") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" check [options] [selection]\n"); + log("\n"); + log("This pass identifies the following problems in the current design:\n"); + log("\n"); + log(" - combinatorial loops\n"); + log("\n"); + log(" - two or more conflicting drivers for one wire\n"); + log("\n"); + log(" - used wires that do not have a driver\n"); + log("\n"); + log("When called with -noinit then this command also checks for wires which have\n"); + log("the 'init' attribute set.\n"); + log("\n"); + log("When called with -assert then the command will produce an error if any\n"); + log("problems are found in the current design.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + int counter = 0; + bool noinit = false; + bool assert_mode = false; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-noinit") { + noinit = true; + continue; + } + if (args[argidx] == "-assert") { + assert_mode = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + log_header(design, "Executing CHECK pass (checking for obvious problems).\n"); + + for (auto module : design->selected_whole_modules_warn()) + { + if (module->has_processes_warn()) + continue; + + log("checking module %s..\n", log_id(module)); + + SigMap sigmap(module); + dict<SigBit, vector<string>> wire_drivers; + dict<SigBit, int> wire_drivers_count; + pool<SigBit> used_wires; + TopoSort<string> topo; + + for (auto cell : module->cells()) + for (auto &conn : cell->connections()) { + SigSpec sig = sigmap(conn.second); + bool logic_cell = yosys_celltypes.cell_evaluable(cell->type); + if (cell->input(conn.first)) + for (auto bit : sig) + if (bit.wire) { + if (logic_cell) + topo.edge(stringf("wire %s", log_signal(bit)), + stringf("cell %s (%s)", log_id(cell), log_id(cell->type))); + used_wires.insert(bit); + } + if (cell->output(conn.first)) + for (int i = 0; i < GetSize(sig); i++) { + if (logic_cell) + topo.edge(stringf("cell %s (%s)", log_id(cell), log_id(cell->type)), + stringf("wire %s", log_signal(sig[i]))); + if (sig[i].wire) + wire_drivers[sig[i]].push_back(stringf("port %s[%d] of cell %s (%s)", + log_id(conn.first), i, log_id(cell), log_id(cell->type))); + } + if (!cell->input(conn.first) && cell->output(conn.first)) + for (auto bit : sig) + if (bit.wire) wire_drivers_count[bit]++; + } + + for (auto wire : module->wires()) { + if (wire->port_input) { + SigSpec sig = sigmap(wire); + for (int i = 0; i < GetSize(sig); i++) + wire_drivers[sig[i]].push_back(stringf("module input %s[%d]", log_id(wire), i)); + } + if (wire->port_output) + for (auto bit : sigmap(wire)) + if (bit.wire) used_wires.insert(bit); + if (wire->port_input && !wire->port_output) + for (auto bit : sigmap(wire)) + if (bit.wire) wire_drivers_count[bit]++; + if (noinit && wire->attributes.count("\\init")) { + log_warning("Wire %s.%s has an unprocessed 'init' attribute.\n", log_id(module), log_id(wire)); + counter++; + } + } + + for (auto it : wire_drivers) + if (wire_drivers_count[it.first] > 1) { + string message = stringf("multiple conflicting drivers for %s.%s:\n", log_id(module), log_signal(it.first)); + for (auto str : it.second) + message += stringf(" %s\n", str.c_str()); + log_warning("%s", message.c_str()); + counter++; + } + + for (auto bit : used_wires) + if (!wire_drivers.count(bit)) { + log_warning("Wire %s.%s is used but has no driver.\n", log_id(module), log_signal(bit)); + counter++; + } + + topo.sort(); + for (auto &loop : topo.loops) { + string message = stringf("found logic loop in module %s:\n", log_id(module)); + for (auto &str : loop) + message += stringf(" %s\n", str.c_str()); + log_warning("%s", message.c_str()); + counter++; + } + } + + log("found and reported %d problems.\n", counter); + + if (assert_mode && counter > 0) + log_error("Found %d problems in 'check -assert'.\n", counter); + } +} CheckPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/cmds/connect.cc b/passes/cmds/connect.cc index e17c1b1c3..52611cf44 100644 --- a/passes/cmds/connect.cc +++ b/passes/cmds/connect.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -49,8 +49,8 @@ struct ConnectPass : public Pass { log("\n"); log(" connect [-nomap] [-nounset] -set <lhs-expr> <rhs-expr>\n"); log("\n"); - log("Create a connection. This is equivialent to adding the statement 'assign\n"); - log("<lhs-expr> = <rhs-expr>;' to the verilog input. Per default, all existing\n"); + log("Create a connection. This is equivalent to adding the statement 'assign\n"); + log("<lhs-expr> = <rhs-expr>;' to the Verilog input. Per default, all existing\n"); log("drivers for <lhs-expr> are unconnected. This can be overwritten by using\n"); log("the -nounset option.\n"); log("\n"); @@ -185,5 +185,5 @@ struct ConnectPass : public Pass { log_cmd_error("Expected -set, -unset, or -port.\n"); } } ConnectPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/cmds/connwrappers.cc b/passes/cmds/connwrappers.cc index a65a63644..c9ab226d5 100644 --- a/passes/cmds/connwrappers.cc +++ b/passes/cmds/connwrappers.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -158,8 +158,8 @@ struct ConnwrappersPass : public Pass { log("\n"); log("Wrappers are used in coarse-grain synthesis to wrap cells with smaller ports\n"); log("in wrapper cells with a (larger) constant port size. I.e. the upper bits\n"); - log("of the wrapper outut are signed/unsigned bit extended. This command uses this\n"); - log("knowlege to rewire the inputs of the driven cells to match the output of\n"); + log("of the wrapper output are signed/unsigned bit extended. This command uses this\n"); + log("knowledge to rewire the inputs of the driven cells to match the output of\n"); log("the driving cell.\n"); log("\n"); log(" -signed <cell_type> <port_name> <width_param>\n"); @@ -198,12 +198,12 @@ struct ConnwrappersPass : public Pass { } extra_args(args, argidx, design); - log_header("Executing CONNWRAPPERS pass (connect extended ports of wrapper cells).\n"); + log_header(design, "Executing CONNWRAPPERS pass (connect extended ports of wrapper cells).\n"); for (auto &mod_it : design->modules_) if (design->selected(mod_it.second)) worker.work(design, mod_it.second); } } ConnwrappersPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/cmds/copy.cc b/passes/cmds/copy.cc index 459e5b0e7..fb863512b 100644 --- a/passes/cmds/copy.cc +++ b/passes/cmds/copy.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -55,5 +55,5 @@ struct CopyPass : public Pass { design->add(new_mod); } } CopyPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/cmds/cover.cc b/passes/cmds/cover.cc index 5644066af..1475475c3 100644 --- a/passes/cmds/cover.cc +++ b/passes/cmds/cover.cc @@ -124,7 +124,7 @@ struct CoverPass : public Pass { extra_args(args, argidx, design); if (do_log) { - log_header("Printing code coverage counters.\n"); + log_header(design, "Printing code coverage counters.\n"); log("\n"); } diff --git a/passes/cmds/delete.cc b/passes/cmds/delete.cc index b4362887e..6d51d30e7 100644 --- a/passes/cmds/delete.cc +++ b/passes/cmds/delete.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -140,5 +140,5 @@ struct DeletePass : public Pass { } } } DeletePass; - + PRIVATE_NAMESPACE_END diff --git a/passes/cmds/design.cc b/passes/cmds/design.cc index 9f800c31f..e900e7b9c 100644 --- a/passes/cmds/design.cc +++ b/passes/cmds/design.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -80,7 +80,7 @@ struct DesignPass : public Pass { log("\n"); log(" design -copy-to <name> [-as <new_mod_name>] [selection]\n"); log("\n"); - log("Copy modules from the current design into the soecified one.\n"); + log("Copy modules from the current design into the specified one.\n"); log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) diff --git a/passes/cmds/edgetypes.cc b/passes/cmds/edgetypes.cc new file mode 100644 index 000000000..7b75a009f --- /dev/null +++ b/passes/cmds/edgetypes.cc @@ -0,0 +1,106 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include "kernel/sigtools.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct EdgetypePass : public Pass { + EdgetypePass() : Pass("edgetypes", "list all types of edges in selection") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" edgetypes [options] [selection]\n"); + log("\n"); + log("This command lists all unique types of 'edges' found in the selection. An 'edge'\n"); + log("is a 4-tuple of source and sink cell type and port name.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + // if (args[argidx] == "-ltr") { + // config.ltr = true; + // continue; + // } + break; + } + extra_args(args, argidx, design); + + pool<string> edge_cache; + + for (auto module : design->selected_modules()) + { + SigMap sigmap(module); + dict<SigBit, pool<tuple<IdString, IdString, int>>> bit_sources, bit_sinks; + pool<std::pair<IdString, IdString>> multibit_ports; + + for (auto cell : module->selected_cells()) + for (auto conn : cell->connections()) + { + IdString cell_type = cell->type; + IdString port_name = conn.first; + SigSpec sig = sigmap(conn.second); + + if (GetSize(sig) > 1) + multibit_ports.insert(std::pair<IdString, IdString>(cell_type, port_name)); + + for (int i = 0; i < GetSize(sig); i++) { + if (cell->output(port_name)) + bit_sources[sig[i]].insert(tuple<IdString, IdString, int>(cell_type, port_name, i)); + if (cell->input(port_name)) + bit_sinks[sig[i]].insert(tuple<IdString, IdString, int>(cell_type, port_name, i)); + } + } + + for (auto &it : bit_sources) + for (auto &source : it.second) + for (auto &sink : bit_sinks[it.first]) + { + auto source_cell_type = std::get<0>(source); + auto source_port_name = std::get<1>(source); + auto source_bit_index = std::get<2>(source); + + auto sink_cell_type = std::get<0>(sink); + auto sink_port_name = std::get<1>(sink); + auto sink_bit_index = std::get<2>(sink); + + string source_str = multibit_ports.count(std::pair<IdString, IdString>(source_cell_type, source_port_name)) ? + stringf("%s.%s[%d]", log_id(source_cell_type), log_id(source_port_name), source_bit_index) : + stringf("%s.%s", log_id(source_cell_type), log_id(source_port_name)); + + string sink_str = multibit_ports.count(std::pair<IdString, IdString>(sink_cell_type, sink_port_name)) ? + stringf("%s.%s[%d]", log_id(sink_cell_type), log_id(sink_port_name), sink_bit_index) : + stringf("%s.%s", log_id(sink_cell_type), log_id(sink_port_name)); + + edge_cache.insert(source_str + " " + sink_str); + } + } + + edge_cache.sort(); + for (auto &str : edge_cache) + log("%s\n", str.c_str()); + } +} EdgetypePass; + +PRIVATE_NAMESPACE_END diff --git a/passes/cmds/plugin.cc b/passes/cmds/plugin.cc index f7c65bbde..828c671de 100644 --- a/passes/cmds/plugin.cc +++ b/passes/cmds/plugin.cc @@ -31,19 +31,23 @@ std::map<std::string, std::string> loaded_plugin_aliases; #ifdef YOSYS_ENABLE_PLUGINS void load_plugin(std::string filename, std::vector<std::string> aliases) { + std::string orig_filename = filename; + if (filename.find('/') == std::string::npos) filename = "./" + filename; if (!loaded_plugins.count(filename)) { void *hdl = dlopen(filename.c_str(), RTLD_LAZY|RTLD_LOCAL); + if (hdl == NULL && orig_filename.find('/') == std::string::npos) + hdl = dlopen((proc_share_dirname() + "plugins/" + orig_filename + ".so").c_str(), RTLD_LAZY|RTLD_LOCAL); if (hdl == NULL) log_cmd_error("Can't load module `%s': %s\n", filename.c_str(), dlerror()); - loaded_plugins[filename] = hdl; + loaded_plugins[orig_filename] = hdl; Pass::init_register(); } for (auto &alias : aliases) - loaded_plugin_aliases[alias] = filename; + loaded_plugin_aliases[alias] = orig_filename; } #else void load_plugin(std::string, std::vector<std::string>) @@ -115,7 +119,7 @@ struct PluginPass : public Pass { log("\n"); int max_alias_len = 1; for (auto &it : loaded_plugin_aliases) - max_alias_len = std::max(max_alias_len, GetSize(it.first)); + max_alias_len = max(max_alias_len, GetSize(it.first)); for (auto &it : loaded_plugin_aliases) log("Alias: %-*s %s\n", max_alias_len, it.first.c_str(), it.second.c_str()); } diff --git a/passes/cmds/qwp.cc b/passes/cmds/qwp.cc new file mode 100644 index 000000000..1b800b6df --- /dev/null +++ b/passes/cmds/qwp.cc @@ -0,0 +1,870 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include "kernel/sigtools.h" + +#undef LOG_MATRICES +#undef PYPLOT_EDGES + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +static uint32_t xorshift32_state; + +static double xorshift32() +{ + xorshift32_state ^= xorshift32_state << 13; + xorshift32_state ^= xorshift32_state >> 17; + xorshift32_state ^= xorshift32_state << 5; + return (xorshift32_state % 1000000) / 1e6; +} + +struct QwpConfig +{ + bool ltr; + bool alpha; + bool verbose; + double grid; + + std::ofstream dump_file; + + QwpConfig() { + ltr = false; + alpha = false; + verbose = false; + grid = 1.0 / 16; + } +}; + +struct QwpWorker +{ + QwpConfig &config; + Module *module; + char direction; + + struct Node { + Cell *cell; + bool tied, alt_tied; + + // pos = position in current direction + // alt_pos = position in the other direction + double pos, alt_pos; + + Node() { + cell = nullptr; + tied = false; + pos = xorshift32(); + alt_tied = false; + alt_pos = xorshift32(); + } + + void tie(double v) { + tied = true; + pos = v; + } + + void alt_tie(double v) { + alt_tied = true; + alt_pos = v; + } + + void swap_alt() { + std::swap(tied, alt_tied); + std::swap(pos, alt_pos); + } + + void proj_left(double midpos) { + cell = nullptr; + tie(pos > midpos ? midpos : pos); + } + + void proj_right(double midpos) { + cell = nullptr; + tie(pos < midpos ? midpos : pos); + } + }; + + vector<Node> nodes; + dict<pair<int, int>, double> edges; + dict<Cell*, int> cell_to_node; + + // worker state variables + double midpos; + double radius; + double alt_midpos; + double alt_radius; + + QwpWorker(QwpConfig &config, Module *module, char direction = 'x') : config(config), module(module), direction(direction) + { + log_assert(direction == 'x' || direction == 'y'); + } + + void load_module() + { + log_assert(direction == 'x'); + + SigMap sigmap(module); + dict<SigBit, pool<int>> bits_to_nodes; + + if (config.ltr || config.alpha) + { + dict<Wire*, double> alpha_inputs, alpha_outputs; + + if (config.alpha) + { + dict<string, Wire*> alpha_order; + + for (auto wire : module->wires()) { + if (wire->port_input || wire->port_output) + alpha_order[wire->name.str()] = wire; + } + + alpha_order.sort(); + + for (auto &it : alpha_order) { + if (it.second->port_input) { + int idx = GetSize(alpha_inputs); + alpha_inputs[it.second] = idx + 0.5; + } + if (it.second->port_output) { + int idx = GetSize(alpha_outputs); + alpha_outputs[it.second] = idx + 0.5; + } + } + } + + for (auto wire : module->wires()) + { + if (!wire->port_input && !wire->port_output) + continue; + + int idx = GetSize(nodes); + nodes.push_back(Node()); + + if (config.ltr) { + if (wire->port_input) + nodes[idx].tie(0.0); + else + nodes[idx].tie(1.0); + } + + if (config.alpha) { + if (wire->port_input) + nodes[idx].alt_tie(alpha_inputs.at(wire) / GetSize(alpha_inputs)); + else + nodes[idx].alt_tie(alpha_outputs.at(wire) / GetSize(alpha_outputs)); + } + + for (auto bit : sigmap(wire)) + bits_to_nodes[bit].insert(idx); + } + } + + for (auto cell : module->selected_cells()) + { + log_assert(cell_to_node.count(cell) == 0); + int idx = GetSize(nodes); + nodes.push_back(Node()); + + cell_to_node[cell] = GetSize(nodes); + nodes[idx].cell = cell; + + for (auto &conn : cell->connections()) + for (auto bit : sigmap(conn.second)) + bits_to_nodes[bit].insert(idx); + } + + for (auto &it : bits_to_nodes) + { + if (GetSize(it.second) > 100) + continue; + + for (int idx1 : it.second) + for (int idx2 : it.second) + if (idx1 < idx2) + edges[pair<int, int>(idx1, idx2)] += 1.0 / GetSize(it.second); + } + } + + void solve(bool alt_mode = false) + { + // A := constraint_matrix + // y := constraint_rhs_vector + // + // AA = A' * A + // Ay = A' * y + // + // M := [AA Ay] + + if (config.verbose) + log("> System size: %d^2\n", GetSize(nodes)); + + // Row major order + int N = GetSize(nodes), N1 = N+1; + vector<double> M(N * N1); + + if (config.verbose) + log("> Edge constraints: %d\n", GetSize(edges)); + + // Edge constraints: + // A[i,:] := [ 0 0 .... 0 weight 0 ... 0 -weight 0 ... 0 0], y[i] := 0 + // + // i.e. nonzero columns in A[i,:] at the two node indices. + for (auto &edge : edges) + { + int idx1 = edge.first.first; + int idx2 = edge.first.second; + double weight = edge.second * (1.0 + xorshift32() * 1e-3); + + M[idx1 + idx1*N1] += weight * weight; + M[idx2 + idx2*N1] += weight * weight; + + M[idx1 + idx2*N1] += -weight * weight; + M[idx2 + idx1*N1] += -weight * weight; + } + + if (config.verbose) + log("> Node constraints: %d\n", GetSize(nodes)); + + // Node constraints: + // A[i,:] := [ 0 0 .... 0 weight 0 ... 0 0], y[i] := weight * pos + // + // i.e. nonzero column in A[i,:] at the node index + // + // "tied" nodes have a large weight, pinning them in position. Untied + // nodes have a small weight, giving then a tiny preference to stay at + // the current position, making sure that AA never is singular. + for (int idx = 0; idx < GetSize(nodes); idx++) + { + auto &node = nodes[idx]; + double rhs = (alt_mode ? node.alt_pos : node.pos); + + double weight = 1e-3; + if (alt_mode ? node.alt_tied : node.tied) + weight = 1e3; + weight *= (1.0 + xorshift32() * 1e-3); + + M[idx + idx*N1] += weight * weight; + M[N + idx*N1] += rhs * weight * weight; + } + +#ifdef LOG_MATRICES + log("\n"); + for (int i = 0; i < N; i++) { + for (int j = 0; j < N+1; j++) + log(" %10.2e", M[(N+1)*i + j]); + log("\n"); + } +#endif + + if (config.verbose) + log("> Solving\n"); + + // Solve "AA*x = Ay" + // (least squares fit for "A*x = y") + // + // Using gaussian elimination to get M := [Id x] + + vector<int> pivot_cache; + vector<int> queue; + + for (int i = 0; i < N; i++) + queue.push_back(i); + + // gaussian elimination + for (int i = 0; i < N; i++) + { + if (config.verbose && ((i+1) % (N/15)) == 0) + log("> Solved %d%%: %d/%d\n", (100*(i+1))/N, i+1, N); + + // find best row + int best_row = queue.front(); + int best_row_queue_idx = 0; + double best_row_absval = 0; + + for (int k = 0; k < GetSize(queue); k++) { + int row = queue[k]; + double absval = fabs(M[i + row*N1]); + if (absval > best_row_absval) { + best_row = row; + best_row_queue_idx = k; + best_row_absval = absval; + } + } + + int row = best_row; + pivot_cache.push_back(row); + + queue[best_row_queue_idx] = queue.back(); + queue.pop_back(); + + // normalize row + for (int k = i+1; k < N1; k++) + M[k + row*N1] /= M[i + row*N1]; + M[i + row*N1] = 1.0; + + // elimination + for (int other_row : queue) { + double d = M[i + other_row*N1]; + for (int k = i+1; k < N1; k++) + M[k + other_row*N1] -= d*M[k + row*N1]; + M[i + other_row*N1] = 0.0; + } + } + + if (config.verbose) + log("> Solved\n"); + + log_assert(queue.empty()); + log_assert(GetSize(pivot_cache) == N); + + // back substitution + for (int i = N-1; i >= 0; i--) + for (int j = i+1; j < N; j++) + { + int row = pivot_cache[i]; + int other_row = pivot_cache[j]; + M[N + row*N1] -= M[j + row*N1] * M[N + other_row*N1]; + M[j + row*N1] = 0.0; + } + +#ifdef LOG_MATRICES + log("\n"); + for (int i = 0; i < N; i++) { + for (int j = 0; j < N+1; j++) + log(" %10.2e", M[(N+1)*i + j]); + log("\n"); + } +#endif + + if (config.verbose) + log("> Update nodes\n"); + + // update node positions + for (int i = 0; i < N; i++) + { + double v = M[(N+1)*i + N]; + double c = alt_mode ? alt_midpos : midpos; + double r = alt_mode ? alt_radius : radius; + + if (std::isfinite(v)) { + v = min(v, c+r); + v = max(v, c-r); + } else { + v = c; + } + + if (alt_mode) { + if (!nodes[i].alt_tied) + nodes[i].alt_pos = v; + } else { + if (!nodes[i].tied) + nodes[i].pos = v; + } + } + } + + void log_cell_coordinates(int indent, bool log_all_nodes = false) + { + for (auto &node : nodes) + { + if (node.cell == nullptr && !log_all_nodes) + continue; + + for (int i = 0; i < indent; i++) + log(" "); + + if (direction == 'x') + log("X=%.2f, Y=%.2f", node.pos, node.alt_pos); + else + log("X=%.2f, Y=%.2f", node.alt_pos, node.pos); + + if (node.tied) + log(" [%c-tied]", direction); + + if (node.alt_tied) + log(" [%c-tied]", direction == 'x' ? 'y' : 'x'); + + if (node.cell != nullptr) + log(" %s (%s)", log_id(node.cell), log_id(node.cell->type)); + else + log(" (none)"); + + log("\n"); + } + } + + void dump_svg(const pool<int> *green_nodes = nullptr, double median = -1) + { + double x_center = direction == 'x' ? midpos : alt_midpos; + double y_center = direction == 'y' ? midpos : alt_midpos; + + double x_radius = direction == 'x' ? radius : alt_radius; + double y_radius = direction == 'y' ? radius : alt_radius; + + config.dump_file << stringf("<svg height=\"240\" width=\"470\">\n"); + config.dump_file << stringf("<rect x=\"0\" y=\"0\" width=\"470\" height=\"240\" style=\"fill:rgb(250,250,200);\" />\n"); + config.dump_file << stringf("<rect x=\"20\" y=\"20\" width=\"200\" height=\"200\" style=\"fill:rgb(200,200,200);\" />\n"); + config.dump_file << stringf("<rect x=\"250\" y=\"20\" width=\"200\" height=\"200\" style=\"fill:rgb(200,200,200);\" />\n"); + + double win_x = 250 + 200 * (direction == 'x' ? midpos - radius : alt_midpos - alt_radius); + double win_y = 20 + 200 * (direction == 'y' ? midpos - radius : alt_midpos - alt_radius); + + double win_w = 200 * (direction == 'x' ? 2*radius : 2*alt_radius); + double win_h = 200 * (direction == 'y' ? 2*radius : 2*alt_radius); + + config.dump_file << stringf("<rect x=\"%.2f\" y=\"%.2f\" width=\"%.2f\" height=\"%.2f\" " + "style=\"stroke:rgb(0,0,0);stroke-width:1;fill:none\" />\n", win_x, win_y, win_w, win_h); + + if (median >= 0) + { + double x1 = 20.0, x2 = 220.0, y1 = 20.0, y2 = 220.0; + + if (direction == 'x') + x1 = x2 = 120 + 100 * (median - x_center) / x_radius; + else + y1 = y2 = 120 + 100 * (median - y_center) / y_radius; + + config.dump_file << stringf("<line x1=\"%.2f\" y1=\"%.2f\" x2=\"%.2f\" y2=\"%.2f\" " + "style=\"stroke:rgb(150,0,150);stroke-width:1\" />\n", x1, y1, x2, y2); + } + + for (auto &edge : edges) + { + auto &node1 = nodes[edge.first.first]; + auto &node2 = nodes[edge.first.second]; + + double x1 = direction == 'x' ? node1.pos : node1.alt_pos; + double y1 = direction == 'y' ? node1.pos : node1.alt_pos; + + double x2 = direction == 'x' ? node2.pos : node2.alt_pos; + double y2 = direction == 'y' ? node2.pos : node2.alt_pos; + + x1 = 120 + 100 * (x1 - x_center) / x_radius; + y1 = 120 + 100 * (y1 - y_center) / y_radius; + + x2 = 120 + 100 * (x2 - x_center) / x_radius; + y2 = 120 + 100 * (y2 - y_center) / y_radius; + + config.dump_file << stringf("<line x1=\"%.2f\" y1=\"%.2f\" x2=\"%.2f\" y2=\"%.2f\" " + "style=\"stroke:rgb(0,0,0);stroke-width:1\" />\n", x1, y1, x2, y2); + } + + for (int i = 0; i < GetSize(nodes); i++) + { + auto &node = nodes[i]; + + double x = direction == 'x' ? node.pos : node.alt_pos; + double y = direction == 'y' ? node.pos : node.alt_pos; + + x = 120 + 100 * (x - x_center) / x_radius; + y = 120 + 100 * (y - y_center) / y_radius; + + const char *color = node.cell == nullptr ? "blue" : "red"; + + if (green_nodes != nullptr && green_nodes->count(i)) + color = "green"; + + config.dump_file << stringf("<circle cx=\"%.2f\" cy=\"%.2f\" r=\"3\" fill=\"%s\"/>\n", x, y, color); + } + + config.dump_file << stringf("</svg>\n"); + } + + void run_worker(int indent) + { + int count_cells = 0; + + for (auto &node : nodes) + if (node.cell != nullptr) + count_cells++; + + for (int i = 0; i < indent; i++) + log(" "); + + string range_str; + + if (direction == 'x') + range_str = stringf("X=%.2f:%.2f, Y=%.2f:%.2f", + midpos - radius, midpos + radius, + alt_midpos - alt_radius, alt_midpos + alt_radius); + else + range_str = stringf("X=%.2f:%.2f, Y=%.2f:%.2f", + alt_midpos - alt_radius, alt_midpos + alt_radius, + midpos - radius, midpos + radius); + + log("%c-qwp on %s with %d cells, %d nodes, and %d edges.\n", direction, + range_str.c_str(), count_cells, GetSize(nodes), GetSize(edges)); + + solve(); + solve(true); + + // detect median position and check for break condition + + vector<pair<double, int>> sorted_pos; + for (int i = 0; i < GetSize(nodes); i++) + if (nodes[i].cell != nullptr) + sorted_pos.push_back(pair<double, int>(nodes[i].pos, i)); + + std::sort(sorted_pos.begin(), sorted_pos.end()); + int median_sidx = GetSize(sorted_pos)/2; + double median = sorted_pos[median_sidx].first; + + double left_scale = radius / (median - (midpos - radius)); + double right_scale = radius / ((midpos + radius) - median); + + if (config.dump_file.is_open()) + { + config.dump_file << stringf("<h4>LSQ %c-Solution for %s:</h4>\n", direction, range_str.c_str()); + + pool<int> green_nodes; + for (int i = 0; i < median_sidx; i++) + green_nodes.insert(sorted_pos[i].second); + + dump_svg(&green_nodes, median); + } + + for (auto &node : nodes) + { + double rel_pos = node.pos - median; + if (rel_pos < 0) { + node.pos = midpos + left_scale*rel_pos; + if (std::isfinite(node.pos)) { + node.pos = min(node.pos, midpos); + node.pos = max(node.pos, midpos - radius); + } else + node.pos = midpos - radius/2; + } else { + node.pos = midpos + right_scale*rel_pos; + if (std::isfinite(node.pos)) { + node.pos = max(node.pos, midpos); + node.pos = min(node.pos, midpos + radius); + } else + node.pos = midpos + radius/2; + } + } + + if (GetSize(sorted_pos) < 2 || (2*radius <= config.grid && 2*alt_radius <= config.grid)) { + log_cell_coordinates(indent + 1); + return; + } + + // create child workers + + char child_direction = direction == 'x' ? 'y' : 'x'; + + QwpWorker left_worker(config, module, child_direction); + QwpWorker right_worker(config, module, child_direction); + + // duplicate nodes into child workers + + dict<int, int> left_nodes, right_nodes; + + for (int k = 0; k < GetSize(sorted_pos); k++) + { + int i = sorted_pos[k].second; + + if (k < median_sidx) { + left_nodes[i] = GetSize(left_worker.nodes); + left_worker.nodes.push_back(nodes[i]); + if (left_worker.nodes.back().pos > midpos) + left_worker.nodes.back().pos = midpos; + left_worker.nodes.back().swap_alt(); + } else { + right_nodes[i] = GetSize(right_worker.nodes); + right_worker.nodes.push_back(nodes[i]); + if (right_worker.nodes.back().pos < midpos) + right_worker.nodes.back().pos = midpos; + right_worker.nodes.back().swap_alt(); + } + } + + // duplicate edges into child workers, project nodes as needed + + for (auto &edge : edges) + { + int idx1 = edge.first.first; + int idx2 = edge.first.second; + double weight = edge.second; + + if (nodes[idx1].cell == nullptr && nodes[idx2].cell == nullptr) + continue; + + int left_idx1 = left_nodes.count(idx1) ? left_nodes.at(idx1) : -1; + int left_idx2 = left_nodes.count(idx2) ? left_nodes.at(idx2) : -1; + + int right_idx1 = right_nodes.count(idx1) ? right_nodes.at(idx1) : -1; + int right_idx2 = right_nodes.count(idx2) ? right_nodes.at(idx2) : -1; + + if (left_idx1 >= 0 && left_worker.nodes[left_idx1].cell && left_idx2 < 0) { + left_idx2 = left_nodes[idx2] = GetSize(left_worker.nodes); + left_worker.nodes.push_back(nodes[idx2]); + left_worker.nodes.back().proj_left(midpos); + left_worker.nodes.back().swap_alt(); + } else + if (left_idx2 >= 0 && left_worker.nodes[left_idx2].cell && left_idx1 < 0) { + left_idx1 = left_nodes[idx1] = GetSize(left_worker.nodes); + left_worker.nodes.push_back(nodes[idx1]); + left_worker.nodes.back().proj_left(midpos); + left_worker.nodes.back().swap_alt(); + } + + if (right_idx1 >= 0 && right_worker.nodes[right_idx1].cell && right_idx2 < 0) { + right_idx2 = right_nodes[idx2] = GetSize(right_worker.nodes); + right_worker.nodes.push_back(nodes[idx2]); + right_worker.nodes.back().proj_right(midpos); + right_worker.nodes.back().swap_alt(); + } else + if (right_idx2 >= 0 && right_worker.nodes[right_idx2].cell && right_idx1 < 0) { + right_idx1 = right_nodes[idx1] = GetSize(right_worker.nodes); + right_worker.nodes.push_back(nodes[idx1]); + right_worker.nodes.back().proj_right(midpos); + right_worker.nodes.back().swap_alt(); + } + + if (left_idx1 >= 0 && left_idx2 >= 0) + left_worker.edges[pair<int, int>(left_idx1, left_idx2)] += weight; + + if (right_idx1 >= 0 && right_idx2 >= 0) + right_worker.edges[pair<int, int>(right_idx1, right_idx2)] += weight; + } + + // run child workers + + left_worker.midpos = right_worker.midpos = alt_midpos; + left_worker.radius = right_worker.radius = alt_radius; + + left_worker.alt_midpos = midpos - radius/2; + right_worker.alt_midpos = midpos + radius/2; + left_worker.alt_radius = right_worker.alt_radius = radius/2; + + left_worker.run_worker(indent+1); + right_worker.run_worker(indent+1); + + // re-integrate results + + for (auto &it : left_nodes) + if (left_worker.nodes[it.second].cell != nullptr) { + nodes[it.first].pos = left_worker.nodes[it.second].alt_pos; + nodes[it.first].alt_pos = left_worker.nodes[it.second].pos; + } + + for (auto &it : right_nodes) + if (right_worker.nodes[it.second].cell != nullptr) { + nodes[it.first].pos = right_worker.nodes[it.second].alt_pos; + nodes[it.first].alt_pos = right_worker.nodes[it.second].pos; + } + + if (config.dump_file.is_open()) { + config.dump_file << stringf("<h4>Final %c-Solution for %s:</h4>\n", direction, range_str.c_str()); + dump_svg(); + } + } + + void histogram(const vector<double> &values) + { + if (values.empty()) { + log("no data\n"); + return; + } + + double min_value = values.front(); + double max_value = values.front(); + + for (auto &v : values) { + min_value = min(min_value, v); + max_value = max(max_value, v); + } + + if (fabs(max_value - min_value) < 0.001) { + log("all values in range %f .. %f\n", min_value, max_value); + return; + } + + vector<int> buckets(60); + int max_bucket_val = 0; + + for (auto &v : values) { + int idx = min(int(GetSize(buckets) * (v - min_value) / (max_value - min_value)), GetSize(buckets)-1); + max_bucket_val = max(max_bucket_val, ++buckets.at(idx)); + } + + for (int i = 4; i >= 0; i--) { + for (int k = 0; k < GetSize(buckets); k++) { + int v = 10 * buckets[k] / max_bucket_val; + if (v >= 2*i+1) + log(v == 2*i+1 ? "." : ":"); + else + log(i == 0 ? (buckets[k] > 0 ? "," : "_") : " "); + } + log("\n"); + } + log("%-30f%30f\n", min_value, max_value); + } + + void run() + { + log("\n"); + log("Running qwp on module %s..\n", log_id(module)); + + if (config.dump_file.is_open()) + config.dump_file << stringf("<h3>QWP protocol for module %s:</h3>\n", log_id(module)); + + load_module(); + + midpos = 0.5; + radius = 0.5; + alt_midpos = 0.5; + alt_radius = 0.5; + run_worker(1); + + for (auto &node : nodes) + if (node.cell != nullptr) + node.cell->attributes["\\qwp_position"] = stringf("%f %f", node.pos, node.alt_pos); + + vector<double> edge_lengths; + vector<double> weighted_edge_lengths; + + double total_edge_length = 0; + double total_weighted_edge_length = 0; + + for (auto &edge : edges) + { + auto &node1 = nodes[edge.first.first]; + auto &node2 = nodes[edge.first.second]; + + double distance = sqrt(pow(node1.pos - node2.pos, 2) + pow(node1.alt_pos - node2.alt_pos, 2)); + double weighted_distance = distance * edge.second; + + edge_lengths.push_back(distance); + weighted_edge_lengths.push_back(weighted_distance); + + total_edge_length += distance; + total_weighted_edge_length += weighted_distance; + } + + log("\n"); + log("Summary for module %s:\n", log_id(module)); + log("Number of edges: %d\n", GetSize(edges)); + log("Total edge length: %f\n", total_edge_length); + log("Total weighted edge length: %f\n", total_weighted_edge_length); + + log("\n"); + log("Histogram over edge lengths:\n"); + histogram(edge_lengths); + + log("\n"); + log("Histogram over weighted edge lengths:\n"); + histogram(weighted_edge_lengths); + } +}; + +struct QwpPass : public Pass { + QwpPass() : Pass("qwp", "quadratic wirelength placer") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" qwp [options] [selection]\n"); + log("\n"); + log("This command runs quadratic wirelength placement on the selected modules and\n"); + log("annotates the cells in the design with 'qwp_position' attributes.\n"); + log("\n"); + log(" -ltr\n"); + log(" Add left-to-right constraints: constrain all inputs on the left border\n"); + log(" outputs to the right border.\n"); + log("\n"); + log(" -alpha\n"); + log(" Add constraints for inputs/outputs to be placed in alphanumerical\n"); + log(" order along the y-axis (top-to-bottom).\n"); + log("\n"); + log(" -grid N\n"); + log(" Number of grid divisions in x- and y-direction. (default=16)\n"); + log("\n"); + log(" -dump <html_file_name>\n"); + log(" Dump a protocol of the placement algorithm to the html file.\n"); + log("\n"); + log(" -v\n"); + log(" Verbose solver output for profiling or debugging\n"); + log("\n"); + log("Note: This implementation of a quadratic wirelength placer uses exact\n"); + log("dense matrix operations. It is only a toy-placer for small circuits.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + QwpConfig config; + xorshift32_state = 123456789; + + log_header(design, "Executing QWP pass (quadratic wirelength placer).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-ltr") { + config.ltr = true; + continue; + } + if (args[argidx] == "-alpha") { + config.alpha = true; + continue; + } + if (args[argidx] == "-v") { + config.verbose = true; + continue; + } + if (args[argidx] == "-grid" && argidx+1 < args.size()) { + config.grid = 1.0 / atoi(args[++argidx].c_str()); + continue; + } + if (args[argidx] == "-dump" && argidx+1 < args.size()) { + config.dump_file.open(args[++argidx], std::ofstream::trunc); + continue; + } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) + { + QwpWorker worker(config, module); + worker.run(); + +#ifdef PYPLOT_EDGES + log("\n"); + log("plt.figure(figsize=(10, 10));\n"); + + for (auto &edge : worker.edges) { + log("plt.plot([%.2f, %.2f], [%.2f, %.2f], \"r-\");\n", + worker.nodes[edge.first.first].pos, + worker.nodes[edge.first.second].pos, + worker.nodes[edge.first.first].alt_pos, + worker.nodes[edge.first.second].alt_pos); + } + + for (auto &node : worker.nodes) { + const char *style = node.cell != nullptr ? "ko" : "ks"; + log("plt.plot([%.2f], [%.2f], \"%s\");\n", node.pos, node.alt_pos, style); + } +#endif + } + } +} QwpPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/cmds/rename.cc b/passes/cmds/rename.cc index 17d803e96..6a002869b 100644 --- a/passes/cmds/rename.cc +++ b/passes/cmds/rename.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -76,12 +76,17 @@ struct RenamePass : public Pass { log("Assign private names (the ones with $-prefix) to all selected wires and cells\n"); log("with public names. This ignores all selected ports.\n"); log("\n"); + log(" rename -top new_name\n"); + log("\n"); + log("Rename top module.\n"); + log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { std::string pattern_prefix = "_", pattern_suffix = "_"; bool flag_enumerate = false; bool flag_hide = false; + bool flag_top = false; bool got_mode = false; size_t argidx; @@ -98,6 +103,11 @@ struct RenamePass : public Pass { got_mode = true; continue; } + if (arg == "-top" && !got_mode) { + flag_top = true; + got_mode = true; + continue; + } if (arg == "-pattern" && argidx+1 < args.size() && args[argidx+1].find('%') != std::string::npos) { int pos = args[++argidx].find('%'); pattern_prefix = args[argidx].substr(0, pos); @@ -171,6 +181,21 @@ struct RenamePass : public Pass { } } else + if (flag_top) + { + if (argidx+1 != args.size()) + log_cmd_error("Invalid number of arguments!\n"); + + IdString new_name = RTLIL::escape_id(args[argidx]); + RTLIL::Module *module = design->top_module(); + + if (module == NULL) + log_cmd_error("No top module found!\n"); + + log("Renaming module %s to %s.\n", log_id(module), log_id(new_name)); + design->rename(module, new_name); + } + else { if (argidx+2 != args.size()) log_cmd_error("Invalid number of arguments!\n"); @@ -203,5 +228,5 @@ struct RenamePass : public Pass { } } } RenamePass; - + PRIVATE_NAMESPACE_END diff --git a/passes/cmds/scatter.cc b/passes/cmds/scatter.cc index 1cd55ecb0..f083e1f67 100644 --- a/passes/cmds/scatter.cc +++ b/passes/cmds/scatter.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -69,5 +69,5 @@ struct ScatterPass : public Pass { } } } ScatterPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/cmds/scc.cc b/passes/cmds/scc.cc index f7f50ab2a..bb6d74474 100644 --- a/passes/cmds/scc.cc +++ b/passes/cmds/scc.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -18,7 +18,7 @@ */ // [[CITE]] Tarjan's strongly connected components algorithm -// Tarjan, R. E. (1972), "Depth-first search and linear graph algorithms", SIAM Journal on Computing 1 (2): 146–160, doi:10.1137/0201010 +// Tarjan, R. E. (1972), "Depth-first search and linear graph algorithms", SIAM Journal on Computing 1 (2): 146-160, doi:10.1137/0201010 // http://en.wikipedia.org/wiki/Tarjan's_strongly_connected_components_algorithm #include "kernel/register.h" @@ -69,10 +69,10 @@ struct SccWorker for (auto nextCell : cellToNextCell[cell]) if (cellLabels.count(nextCell) == 0) { run(nextCell, depth+1, maxDepth); - cellLabels[cell].second = std::min(cellLabels[cell].second, cellLabels[nextCell].second); + cellLabels[cell].second = min(cellLabels[cell].second, cellLabels[nextCell].second); } else if (cellsOnStack.count(nextCell) > 0 && (maxDepth < 0 || cellDepth[nextCell] + maxDepth > depth)) { - cellLabels[cell].second = std::min(cellLabels[cell].second, cellLabels[nextCell].second); + cellLabels[cell].second = min(cellLabels[cell].second, cellLabels[nextCell].second); } if (cellLabels[cell].first == cellLabels[cell].second) @@ -100,7 +100,8 @@ struct SccWorker } } - SccWorker(RTLIL::Design *design, RTLIL::Module *module, bool allCellTypes, int maxDepth) : design(design), module(module), sigmap(module) + SccWorker(RTLIL::Design *design, RTLIL::Module *module, bool nofeedbackMode, bool allCellTypes, int maxDepth) : + design(design), module(module), sigmap(module) { if (module->processes.size() > 0) { log("Skipping module %s as it contains processes (run 'proc' pass first).\n", module->name.c_str()); @@ -167,10 +168,22 @@ struct SccWorker labelCounter = 0; cellLabels.clear(); - while (workQueue.size() > 0) { + while (workQueue.size() > 0) + { RTLIL::Cell *cell = *workQueue.begin(); log_assert(cellStack.size() == 0); cellDepth.clear(); + + if (!nofeedbackMode && cellToNextCell[cell].count(cell)) { + log("Found an SCC:"); + std::set<RTLIL::Cell*> scc; + log(" %s", RTLIL::id2cstr(cell->name)); + cell2scc[cell] = sccList.size(); + scc.insert(cell); + sccList.push_back(scc); + log("\n"); + } + run(cell, 0, maxDepth); } @@ -212,10 +225,18 @@ struct SccPass : public Pass { log("This command identifies strongly connected components (aka logic loops) in the\n"); log("design.\n"); log("\n"); + log(" -expect <num>\n"); + log(" expect to find exactly <num> SSCs. A different number of SSCs will\n"); + log(" produce an error.\n"); + log("\n"); log(" -max_depth <num>\n"); - log(" limit to loops not longer than the specified number of cells. This can\n"); - log(" e.g. be useful in identifying local loops in a module that turns out\n"); - log(" to be one gigantic SCC.\n"); + log(" limit to loops not longer than the specified number of cells. This\n"); + log(" can e.g. be useful in identifying small local loops in a module that\n"); + log(" implements one large SCC.\n"); + log("\n"); + log(" -nofeedback\n"); + log(" do not count cells that have their output fed back into one of their\n"); + log(" inputs as single-cell scc.\n"); log("\n"); log(" -all_cell_types\n"); log(" Usually this command only considers internal non-memory cells. With\n"); @@ -239,9 +260,11 @@ struct SccPass : public Pass { std::map<std::string, std::string> setCellAttr, setWireAttr; bool allCellTypes = false; bool selectMode = false; + bool nofeedbackMode = false; int maxDepth = -1; + int expect = -1; - log_header("Executing SCC pass (detecting logic loops).\n"); + log_header(design, "Executing SCC pass (detecting logic loops).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { @@ -249,6 +272,14 @@ struct SccPass : public Pass { maxDepth = atoi(args[++argidx].c_str()); continue; } + if (args[argidx] == "-expect" && argidx+1 < args.size()) { + expect = atoi(args[++argidx].c_str()); + continue; + } + if (args[argidx] == "-nofeedback") { + nofeedbackMode = true; + continue; + } if (args[argidx] == "-all_cell_types") { allCellTypes = true; continue; @@ -282,16 +313,26 @@ struct SccPass : public Pass { log_cmd_error("The -set*_attr options are not implemented at the moment!\n"); RTLIL::Selection newSelection(false); + int scc_counter = 0; for (auto &mod_it : design->modules_) if (design->selected(mod_it.second)) { - SccWorker worker(design, mod_it.second, allCellTypes, maxDepth); + SccWorker worker(design, mod_it.second, nofeedbackMode, allCellTypes, maxDepth); + scc_counter += GetSize(worker.sccList); if (selectMode) worker.select(newSelection); } + if (expect >= 0) { + if (scc_counter == expect) + log("Found and expected %d SCCs.\n", scc_counter); + else + log_error("Found %d SCCs but expected %d.\n", scc_counter, expect); + } else + log("Found %d SCCs.\n", scc_counter); + if (selectMode) { log_assert(origSelectPos >= 0); design->selection_stack[origSelectPos] = newSelection; @@ -299,5 +340,5 @@ struct SccPass : public Pass { } } } SccPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/cmds/select.cc b/passes/cmds/select.cc index 247765f0d..d2e1a2e2b 100644 --- a/passes/cmds/select.cc +++ b/passes/cmds/select.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -180,6 +180,47 @@ static void select_op_neg(RTLIL::Design *design, RTLIL::Selection &lhs) lhs.selected_members.swap(new_sel.selected_members); } +static int my_xorshift32_rng() { + static uint32_t x32 = 314159265; + x32 ^= x32 << 13; + x32 ^= x32 >> 17; + x32 ^= x32 << 5; + return x32 & 0x0fffffff; +} + +static void select_op_random(RTLIL::Design *design, RTLIL::Selection &lhs, int count) +{ + vector<pair<IdString, IdString>> objects; + + for (auto mod : design->modules()) + { + if (!lhs.selected_module(mod->name)) + continue; + + for (auto cell : mod->cells()) { + if (lhs.selected_member(mod->name, cell->name)) + objects.push_back(make_pair(mod->name, cell->name)); + } + + for (auto wire : mod->wires()) { + if (lhs.selected_member(mod->name, wire->name)) + objects.push_back(make_pair(mod->name, wire->name)); + } + } + + lhs = RTLIL::Selection(false); + + while (!objects.empty() && count-- > 0) + { + int idx = my_xorshift32_rng() % GetSize(objects); + lhs.selected_members[objects[idx].first].insert(objects[idx].second); + objects[idx] = objects.back(); + objects.pop_back(); + } + + lhs.optimize(design); +} + static void select_op_submod(RTLIL::Design *design, RTLIL::Selection &lhs) { for (auto &mod_it : design->modules_) @@ -196,6 +237,27 @@ static void select_op_submod(RTLIL::Design *design, RTLIL::Selection &lhs) } } +static void select_op_cells_to_modules(RTLIL::Design *design, RTLIL::Selection &lhs) +{ + RTLIL::Selection new_sel(false); + for (auto &mod_it : design->modules_) + if (lhs.selected_module(mod_it.first)) + for (auto &cell_it : mod_it.second->cells_) + if (lhs.selected_member(mod_it.first, cell_it.first) && design->modules_.count(cell_it.second->type)) + new_sel.selected_modules.insert(cell_it.second->type); + lhs = new_sel; +} + +static void select_op_module_to_cells(RTLIL::Design *design, RTLIL::Selection &lhs) +{ + RTLIL::Selection new_sel(false); + for (auto &mod_it : design->modules_) + for (auto &cell_it : mod_it.second->cells_) + if (design->modules_.count(cell_it.second->type) && lhs.selected_whole_module(cell_it.second->type)) + new_sel.selected_members[mod_it.first].insert(cell_it.first); + lhs = new_sel; +} + static void select_op_fullmod(RTLIL::Design *design, RTLIL::Selection &lhs) { lhs.optimize(design); @@ -365,7 +427,7 @@ static int parse_comma_list(std::set<RTLIL::IdString> &tokens, std::string str, } } -static int select_op_expand(RTLIL::Design *design, RTLIL::Selection &lhs, std::vector<expand_rule_t> &rules, std::set<RTLIL::IdString> &limits, int max_objects, char mode, CellTypes &ct) +static int select_op_expand(RTLIL::Design *design, RTLIL::Selection &lhs, std::vector<expand_rule_t> &rules, std::set<RTLIL::IdString> &limits, int max_objects, char mode, CellTypes &ct, bool eval_only) { int sel_objects = 0; bool is_input, is_output; @@ -401,6 +463,8 @@ static int select_op_expand(RTLIL::Design *design, RTLIL::Selection &lhs, std::v for (auto &conn : cell.second->connections()) { char last_mode = '-'; + if (eval_only && !yosys_celltypes.cell_evaluable(cell.second->type)) + goto exclude_match; for (auto &rule : rules) { last_mode = rule.mode; if (rule.cell_types.size() > 0 && rule.cell_types.count(cell.second->type) == 0) @@ -433,9 +497,10 @@ static int select_op_expand(RTLIL::Design *design, RTLIL::Selection &lhs, std::v return sel_objects; } -static void select_op_expand(RTLIL::Design *design, std::string arg, char mode) +static void select_op_expand(RTLIL::Design *design, std::string arg, char mode, bool eval_only) { - int pos = mode == 'x' ? 2 : 3, levels = 1, rem_objects = -1; + int pos = (mode == 'x' ? 2 : 3) + (eval_only ? 1 : 0); + int levels = 1, rem_objects = -1; std::vector<expand_rule_t> rules; std::set<RTLIL::IdString> limits; @@ -526,7 +591,7 @@ static void select_op_expand(RTLIL::Design *design, std::string arg, char mode) #endif while (levels-- > 0 && rem_objects != 0) { - int num_objects = select_op_expand(design, work_stack.back(), rules, limits, rem_objects, mode, ct); + int num_objects = select_op_expand(design, work_stack.back(), rules, limits, rem_objects, mode, ct, eval_only); if (num_objects == 0) break; rem_objects -= num_objects; @@ -540,7 +605,7 @@ static void select_filter_active_mod(RTLIL::Design *design, RTLIL::Selection &se { if (design->selected_active_module.empty()) return; - + if (sel.full_selection) { sel.full_selection = false; sel.selected_modules.clear(); @@ -610,11 +675,27 @@ static void select_stmt(RTLIL::Design *design, std::string arg) select_op_intersect(design, work_stack[work_stack.size()-2], work_stack[work_stack.size()-1]); work_stack.pop_back(); } else + if (arg.size() >= 2 && arg[0] == '%' && arg[1] == 'R') { + if (work_stack.size() < 1) + log_cmd_error("Must have at least one element on the stack for operator %%R.\n"); + int count = arg.size() > 2 ? atoi(arg.c_str() + 2) : 1; + select_op_random(design, work_stack[work_stack.size()-1], count); + } else if (arg == "%s") { if (work_stack.size() < 1) log_cmd_error("Must have at least one element on the stack for operator %%s.\n"); select_op_submod(design, work_stack[work_stack.size()-1]); } else + if (arg == "%M") { + if (work_stack.size() < 1) + log_cmd_error("Must have at least one element on the stack for operator %%M.\n"); + select_op_cells_to_modules(design, work_stack[work_stack.size()-1]); + } else + if (arg == "%C") { + if (work_stack.size() < 1) + log_cmd_error("Must have at least one element on the stack for operator %%M.\n"); + select_op_module_to_cells(design, work_stack[work_stack.size()-1]); + } else if (arg == "%c") { if (work_stack.size() < 1) log_cmd_error("Must have at least one element on the stack for operator %%c.\n"); @@ -633,17 +714,32 @@ static void select_stmt(RTLIL::Design *design, std::string arg) if (arg == "%x" || (arg.size() > 2 && arg.substr(0, 2) == "%x" && (arg[2] == ':' || arg[2] == '*' || arg[2] == '.' || ('0' <= arg[2] && arg[2] <= '9')))) { if (work_stack.size() < 1) log_cmd_error("Must have at least one element on the stack for operator %%x.\n"); - select_op_expand(design, arg, 'x'); + select_op_expand(design, arg, 'x', false); } else if (arg == "%ci" || (arg.size() > 3 && arg.substr(0, 3) == "%ci" && (arg[3] == ':' || arg[3] == '*' || arg[3] == '.' || ('0' <= arg[3] && arg[3] <= '9')))) { if (work_stack.size() < 1) log_cmd_error("Must have at least one element on the stack for operator %%ci.\n"); - select_op_expand(design, arg, 'i'); + select_op_expand(design, arg, 'i', false); } else if (arg == "%co" || (arg.size() > 3 && arg.substr(0, 3) == "%co" && (arg[3] == ':' || arg[3] == '*' || arg[3] == '.' || ('0' <= arg[3] && arg[3] <= '9')))) { if (work_stack.size() < 1) log_cmd_error("Must have at least one element on the stack for operator %%co.\n"); - select_op_expand(design, arg, 'o'); + select_op_expand(design, arg, 'o', false); + } else + if (arg == "%xe" || (arg.size() > 3 && arg.substr(0, 3) == "%xe" && (arg[3] == ':' || arg[3] == '*' || arg[3] == '.' || ('0' <= arg[3] && arg[3] <= '9')))) { + if (work_stack.size() < 1) + log_cmd_error("Must have at least one element on the stack for operator %%xe.\n"); + select_op_expand(design, arg, 'x', true); + } else + if (arg == "%cie" || (arg.size() > 4 && arg.substr(0, 4) == "%cie" && (arg[4] == ':' || arg[4] == '*' || arg[4] == '.' || ('0' <= arg[4] && arg[4] <= '9')))) { + if (work_stack.size() < 1) + log_cmd_error("Must have at least one element on the stack for operator %%cie.\n"); + select_op_expand(design, arg, 'i', true); + } else + if (arg == "%coe" || (arg.size() > 4 && arg.substr(0, 4) == "%coe" && (arg[4] == ':' || arg[4] == '*' || arg[4] == '.' || ('0' <= arg[4] && arg[4] <= '9')))) { + if (work_stack.size() < 1) + log_cmd_error("Must have at least one element on the stack for operator %%coe.\n"); + select_op_expand(design, arg, 'o', true); } else log_cmd_error("Unknown selection operator '%s'.\n", arg.c_str()); if (work_stack.size() >= 1) @@ -684,7 +780,7 @@ static void select_stmt(RTLIL::Design *design, std::string arg) select_filter_active_mod(design, work_stack.back()); return; } - + sel.full_selection = false; for (auto &mod_it : design->modules_) { @@ -856,7 +952,7 @@ struct SelectPass : public Pass { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" select [ -add | -del | -set <name> ] {-read <filename> | <selection>}\n"); - log(" select [ -assert-none | -assert-any ] {-read <filename> | <selection>}\n"); + log(" select [ <assert_option> ] {-read <filename> | <selection>}\n"); log(" select [ -list | -write <filename> | -count | -clear ]\n"); log(" select -module <modname>\n"); log("\n"); @@ -892,6 +988,14 @@ struct SelectPass : public Pass { log(" do not modify the current selection. instead assert that the given\n"); log(" selection contains exactly N objects.\n"); log("\n"); + log(" -assert-max N\n"); + log(" do not modify the current selection. instead assert that the given\n"); + log(" selection contains less than or exactly N objects.\n"); + log("\n"); + log(" -assert-min N\n"); + log(" do not modify the current selection. instead assert that the given\n"); + log(" selection contains at least N objects.\n"); + log("\n"); log(" -list\n"); log(" list all objects in the current selection\n"); log("\n"); @@ -1012,7 +1116,7 @@ struct SelectPass : public Pass { log(" like %%d but swap the roles of two top sets on the stack\n"); log("\n"); log(" %%c\n"); - log(" create a copy of the top set rom the stack and push it\n"); + log(" create a copy of the top set from the stack and push it\n"); log("\n"); log(" %%x[<num1>|*][.<num2>][:<rule>[:<rule>..]]\n"); log(" expand top set <num1> num times according to the specified rules.\n"); @@ -1029,19 +1133,31 @@ struct SelectPass : public Pass { log("\n"); log(" %%ci[<num1>|*][.<num2>][:<rule>[:<rule>..]]\n"); log(" %%co[<num1>|*][.<num2>][:<rule>[:<rule>..]]\n"); - log(" simmilar to %%x, but only select input (%%ci) or output cones (%%co)\n"); + log(" similar to %%x, but only select input (%%ci) or output cones (%%co)\n"); + log("\n"); + log(" %%xe[...] %%cie[...] %%coe\n"); + log(" like %%x, %%ci, and %%co but only consider combinatorial cells\n"); log("\n"); log(" %%a\n"); log(" expand top set by selecting all wires that are (at least in part)\n"); log(" aliases for selected wires.\n"); log("\n"); log(" %%s\n"); - log(" expand top set by adding all modules of instantiated cells in selected\n"); + log(" expand top set by adding all modules that implement cells in selected\n"); log(" modules\n"); log("\n"); log(" %%m\n"); log(" expand top set by selecting all modules that contain selected objects\n"); log("\n"); + log(" %%M\n"); + log(" select modules that implement selected cells\n"); + log("\n"); + log(" %%C\n"); + log(" select cells that implement selected modules\n"); + log("\n"); + log(" %%R[<num>]\n"); + log(" select <num> random objects from top selection (default 1)\n"); + log("\n"); log("Example: the following command selects all wires that are connected to a\n"); log("'GATE' input of a 'SWITCH' cell:\n"); log("\n"); @@ -1060,6 +1176,8 @@ struct SelectPass : public Pass { bool assert_none = false; bool assert_any = false; int assert_count = -1; + int assert_max = -1; + int assert_min = -1; std::string write_file, read_file; std::string set_name, sel_str; @@ -1089,6 +1207,14 @@ struct SelectPass : public Pass { assert_count = atoi(args[++argidx].c_str()); continue; } + if (arg == "-assert-max" && argidx+1 < args.size()) { + assert_max = atoi(args[++argidx].c_str()); + continue; + } + if (arg == "-assert-min" && argidx+1 < args.size()) { + assert_min = atoi(args[++argidx].c_str()); + continue; + } if (arg == "-clear") { clear_mode = true; continue; @@ -1165,14 +1291,14 @@ struct SelectPass : public Pass { if (none_mode && args.size() != 2) log_cmd_error("Option -none can not be combined with any other options.\n"); - if (add_mode + del_mode + assert_none + assert_any + (assert_count >= 0) > 1) - log_cmd_error("Options -add, -del, -assert-none, -assert-any or -assert-count can not be combined.\n"); + if (add_mode + del_mode + assert_none + assert_any + (assert_count >= 0) + (assert_max >= 0) + (assert_min >= 0) > 1) + log_cmd_error("Options -add, -del, -assert-none, -assert-any, assert-count, -assert-max or -assert-min can not be combined.\n"); - if ((list_mode || !write_file.empty() || count_mode) && (add_mode || del_mode || assert_none || assert_any || assert_count >= 0)) - log_cmd_error("Options -list, -write and -count can not be combined with -add, -del, -assert-none, -assert-any or -assert-count.\n"); + if ((list_mode || !write_file.empty() || count_mode) && (add_mode || del_mode || assert_none || assert_any || assert_count >= 0 || assert_max >= 0 || assert_min >= 0)) + log_cmd_error("Options -list, -write and -count can not be combined with -add, -del, -assert-none, -assert-any, assert-count, -assert-max, or -assert-min.\n"); - if (!set_name.empty() && (list_mode || !write_file.empty() || count_mode || add_mode || del_mode || assert_none || assert_any || assert_count >= 0)) - log_cmd_error("Option -set can not be combined with -list, -write, -count, -add, -del, -assert-none, -assert-any or -assert-count.\n"); + if (!set_name.empty() && (list_mode || !write_file.empty() || count_mode || add_mode || del_mode || assert_none || assert_any || assert_count >= 0 || assert_max >= 0 || assert_min >= 0)) + log_cmd_error("Option -set can not be combined with -list, -write, -count, -add, -del, -assert-none, -assert-any, -assert-count, -assert-max, or -assert-min.\n"); if (work_stack.size() == 0 && got_module) { RTLIL::Selection sel; @@ -1261,8 +1387,9 @@ struct SelectPass : public Pass { { if (work_stack.size() == 0) log_cmd_error("No selection to check.\n"); + work_stack.back().optimize(design); if (!work_stack.back().empty()) - log_error("Assertation failed: selection is not empty:%s\n", sel_str.c_str()); + log_error("Assertion failed: selection is not empty:%s\n", sel_str.c_str()); return; } @@ -1270,12 +1397,13 @@ struct SelectPass : public Pass { { if (work_stack.size() == 0) log_cmd_error("No selection to check.\n"); + work_stack.back().optimize(design); if (work_stack.back().empty()) - log_error("Assertation failed: selection is empty:%s\n", sel_str.c_str()); + log_error("Assertion failed: selection is empty:%s\n", sel_str.c_str()); return; } - if (assert_count >= 0) + if (assert_count >= 0 || assert_max >= 0 || assert_min >= 0) { int total_count = 0; if (work_stack.size() == 0) @@ -1297,9 +1425,15 @@ struct SelectPass : public Pass { if (sel->selected_member(mod_it.first, it.first)) total_count++; } - if (assert_count != total_count) - log_error("Assertation failed: selection contains %d elements instead of the asserted %d:%s\n", + if (assert_count >= 0 && assert_count != total_count) + log_error("Assertion failed: selection contains %d elements instead of the asserted %d:%s\n", total_count, assert_count, sel_str.c_str()); + if (assert_max >= 0 && assert_max < total_count) + log_error("Assertion failed: selection contains %d elements, more than the maximum number %d:%s\n", + total_count, assert_max, sel_str.c_str()); + if (assert_min >= 0 && assert_min > total_count) + log_error("Assertion failed: selection contains %d elements, less than the minimum number %d:%s\n", + total_count, assert_min, sel_str.c_str()); return; } @@ -1328,7 +1462,7 @@ struct SelectPass : public Pass { design->selection_stack.back().optimize(design); } } SelectPass; - + struct CdPass : public Pass { CdPass() : Pass("cd", "a shortcut for 'select -module <name>'") { } virtual void help() @@ -1343,7 +1477,7 @@ struct CdPass : public Pass { log(" cd <cellname>\n"); log("\n"); log("When no module with the specified name is found, but there is a cell\n"); - log("with the specified name in the current module, then this is equivialent\n"); + log("with the specified name in the current module, then this is equivalent\n"); log("to 'cd <celltype>'.\n"); log("\n"); log(" cd ..\n"); @@ -1400,7 +1534,7 @@ static void log_matches(const char *title, Module *module, T list) log(" %s\n", RTLIL::id2cstr(id)); } } - + struct LsPass : public Pass { LsPass() : Pass("ls", "list modules or objects in modules") { } virtual void help() @@ -1444,5 +1578,5 @@ struct LsPass : public Pass { } } } LsPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/cmds/setattr.cc b/passes/cmds/setattr.cc index 9a6d8a038..9b05ae32f 100644 --- a/passes/cmds/setattr.cc +++ b/passes/cmds/setattr.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -32,25 +32,20 @@ struct setunset_t setunset_t(std::string unset_name) : name(RTLIL::escape_id(unset_name)), value(), unset(true) { } - setunset_t(std::string set_name, std::vector<std::string> args, size_t &argidx) : name(RTLIL::escape_id(set_name)), value(), unset(false) + setunset_t(std::string set_name, std::string set_value) : name(RTLIL::escape_id(set_name)), value(), unset(false) { - if (!args[argidx].empty() && args[argidx][0] == '"') { - std::string str = args[argidx++].substr(1); - while (str.size() != 0 && str[str.size()-1] != '"' && argidx < args.size()) - str += args[argidx++]; - if (str.size() != 0 && str[str.size()-1] == '"') - str = str.substr(0, str.size()-1); - value = RTLIL::Const(str); + if (set_value.substr(0, 1) == "\"" && set_value.substr(GetSize(set_value)-1) == "\"") { + value = RTLIL::Const(set_value.substr(1, GetSize(set_value)-2)); } else { RTLIL::SigSpec sig_value; - if (!RTLIL::SigSpec::parse(sig_value, NULL, args[argidx++])) - log_cmd_error("Can't decode value '%s'!\n", args[argidx-1].c_str()); + if (!RTLIL::SigSpec::parse(sig_value, NULL, set_value)) + log_cmd_error("Can't decode value '%s'!\n", set_value.c_str()); value = sig_value.as_const(); } } }; -static void do_setunset(dict<RTLIL::IdString, RTLIL::Const> &attrs, std::vector<setunset_t> &list) +static void do_setunset(dict<RTLIL::IdString, RTLIL::Const> &attrs, const std::vector<setunset_t> &list) { for (auto &item : list) if (item.unset) @@ -84,9 +79,9 @@ struct SetattrPass : public Pass { { std::string arg = args[argidx]; if (arg == "-set" && argidx+2 < args.size()) { - argidx += 2; - setunset_list.push_back(setunset_t(args[argidx-1], args, argidx)); - argidx--; + string set_key = args[++argidx]; + string set_val = args[++argidx]; + setunset_list.push_back(setunset_t(set_key, set_val)); continue; } if (arg == "-unset" && argidx+1 < args.size()) { @@ -132,7 +127,7 @@ struct SetattrPass : public Pass { } } } SetattrPass; - + struct SetparamPass : public Pass { SetparamPass() : Pass("setparam", "set/unset parameters on objects") { } virtual void help() @@ -154,9 +149,9 @@ struct SetparamPass : public Pass { { std::string arg = args[argidx]; if (arg == "-set" && argidx+2 < args.size()) { - argidx += 2; - setunset_list.push_back(setunset_t(args[argidx-1], args, argidx)); - argidx--; + string set_key = args[++argidx]; + string set_val = args[++argidx]; + setunset_list.push_back(setunset_t(set_key, set_val)); continue; } if (arg == "-unset" && argidx+1 < args.size()) { @@ -180,5 +175,87 @@ struct SetparamPass : public Pass { } } } SetparamPass; - + +struct ChparamPass : public Pass { + ChparamPass() : Pass("chparam", "re-evaluate modules with new parameters") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" chparam [ -set name value ]... [selection]\n"); + log("\n"); + log("Re-evaluate the selected modules with new parameters. String values must be\n"); + log("passed in double quotes (\").\n"); + log("\n"); + log("\n"); + log(" chparam -list [selection]\n"); + log("\n"); + log("List the available parameters of the selected modules.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + std::vector<setunset_t> setunset_list; + dict<RTLIL::IdString, RTLIL::Const> new_parameters; + bool list_mode = false; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + std::string arg = args[argidx]; + if (arg == "-set" && argidx+2 < args.size()) { + string set_key = args[++argidx]; + string set_val = args[++argidx]; + setunset_list.push_back(setunset_t(set_key, set_val)); + continue; + } + if (arg == "-list") { + list_mode = true; + continue; + } + break; + } + + for (int i = argidx; i < GetSize(args); i++) + if (design->module("$abstract\\" + args[i]) != nullptr && + design->module(RTLIL::escape_id(args[i])) == nullptr) + args[i] = "$abstract\\" + args[i]; + + extra_args(args, argidx, design); + + do_setunset(new_parameters, setunset_list); + + if (list_mode) { + if (!new_parameters.empty()) + log_cmd_error("The options -set and -list cannot be used together.\n"); + for (auto module : design->selected_modules()) { + log("%s:\n", log_id(module)); + for (auto param : module->avail_parameters) + log(" %s\n", log_id(param)); + } + return; + } + + pool<IdString> modnames, old_modnames; + for (auto module : design->selected_whole_modules_warn()) { + modnames.insert(module->name); + old_modnames.insert(module->name); + } + modnames.sort(); + + for (auto modname : modnames) { + Module *module = design->module(modname); + Module *new_module = design->module(module->derive(design, new_parameters)); + if (module != new_module) { + Module *m = new_module->clone(); + m->name = module->name; + design->remove(module); + design->add(m); + } + if (old_modnames.count(new_module->name) == 0) + design->remove(new_module); + } + } +} ChparamPass; + PRIVATE_NAMESPACE_END diff --git a/passes/cmds/setundef.cc b/passes/cmds/setundef.cc index b9a29b7d2..26b2eb87d 100644 --- a/passes/cmds/setundef.cc +++ b/passes/cmds/setundef.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -79,11 +79,15 @@ struct SetundefPass : public Pass { log(" replace with random bits using the specified integer als seed\n"); log(" value for the random number generator.\n"); log("\n"); + log(" -init\n"); + log(" also create/update init values for flip-flops\n"); + log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { bool got_value = false; bool undriven_mode = false; + bool init_mode = false; SetundefWorker worker; size_t argidx; @@ -103,6 +107,10 @@ struct SetundefPass : public Pass { worker.next_bit_mode = 1; continue; } + if (args[argidx] == "-init") { + init_mode = true; + continue; + } if (args[argidx] == "-random" && !got_value && argidx+1 < args.size()) { got_value = true; worker.next_bit_mode = 2; @@ -118,12 +126,8 @@ struct SetundefPass : public Pass { if (!got_value) log_cmd_error("One of the options -zero, -one, or -random <seed> must be specified.\n"); - for (auto &mod_it : design->modules_) + for (auto module : design->selected_modules()) { - RTLIL::Module *module = mod_it.second; - if (!design->selected(module)) - continue; - if (undriven_mode) { if (!module->processes.empty()) @@ -151,9 +155,89 @@ struct SetundefPass : public Pass { } } + if (init_mode) + { + SigMap sigmap(module); + pool<SigBit> ffbits; + pool<Wire*> initwires; + + pool<IdString> fftypes; + fftypes.insert("$dff"); + fftypes.insert("$dffe"); + fftypes.insert("$dffsr"); + fftypes.insert("$adff"); + + std::vector<char> list_np = {'N', 'P'}, list_01 = {'0', '1'}; + + for (auto c1 : list_np) + fftypes.insert(stringf("$_DFF_%c_", c1)); + + for (auto c1 : list_np) + for (auto c2 : list_np) + fftypes.insert(stringf("$_DFFE_%c%c_", c1, c2)); + + for (auto c1 : list_np) + for (auto c2 : list_np) + for (auto c3 : list_01) + fftypes.insert(stringf("$_DFF_%c%c%c_", c1, c2, c3)); + + for (auto c1 : list_np) + for (auto c2 : list_np) + for (auto c3 : list_np) + fftypes.insert(stringf("$_DFFSR_%c%c%c_", c1, c2, c3)); + + for (auto cell : module->cells()) + { + if (!fftypes.count(cell->type)) + continue; + + for (auto bit : sigmap(cell->getPort("\\Q"))) + ffbits.insert(bit); + } + + for (auto wire : module->wires()) + { + if (!wire->attributes.count("\\init")) + continue; + + for (auto bit : sigmap(wire)) + ffbits.erase(bit); + + initwires.insert(wire); + } + + for (int wire_types = 0; wire_types < 2; wire_types++) + for (auto wire : module->wires()) + { + if (wire->name[0] == (wire_types ? '\\' : '$')) + next_wire: + continue; + + for (auto bit : sigmap(wire)) + if (!ffbits.count(bit)) + goto next_wire; + + for (auto bit : sigmap(wire)) + ffbits.erase(bit); + + initwires.insert(wire); + } + + for (auto wire : initwires) + { + Const &initval = wire->attributes["\\init"]; + + for (int i = 0; i < GetSize(wire); i++) + if (GetSize(initval) <= i) + initval.bits.push_back(worker.next_bit()); + else if (initval.bits[i] == State::Sx) + initval.bits[i] = worker.next_bit(); + } + } + module->rewrite_sigspecs(worker); } } } SetundefPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/cmds/show.cc b/passes/cmds/show.cc index 63da29b94..87504a33f 100644 --- a/passes/cmds/show.cc +++ b/passes/cmds/show.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -41,7 +41,7 @@ struct ShowWorker { CellTypes ct; - std::vector<std::string> dot_escape_store; + vector<shared_str> dot_escape_store; std::map<RTLIL::IdString, int> dot_id2num_store; std::map<RTLIL::IdString, int> autonames; int single_idx_count; @@ -552,7 +552,7 @@ struct ShowWorker continue; if (design->selected_whole_module(module->name)) { if (module->get_bool_attribute("\\blackbox")) { - log("Skipping blackbox module %s.\n", id2cstr(module->name)); + // log("Skipping blackbox module %s.\n", id2cstr(module->name)); continue; } else if (module->cells_.empty() && module->connections().empty() && module->processes.empty()) { @@ -590,6 +590,10 @@ struct ShowPass : public Pass { log(" inputs or outputs. This option can be used multiple times to specify\n"); log(" more than one library.\n"); log("\n"); + log(" note: in most cases it is better to load the library before calling\n"); + log(" show with 'read_verilog -lib <filename>'. it is also possible to\n"); + log(" load liberty files with 'read_liberty -lib <filename>'.\n"); + log("\n"); log(" -prefix <prefix>\n"); log(" generate <prefix>.* instead of ~/.yosys_show.*\n"); log("\n"); @@ -606,7 +610,7 @@ struct ShowPass : public Pass { log(" -colors <seed>\n"); log(" Randomly assign colors to the wires. The integer argument is the seed\n"); log(" for the random number generator. Change the seed value if the colored\n"); - log(" graph still is ambigous. A seed of zero deactivates the coloring.\n"); + log(" graph still is ambiguous. A seed of zero deactivates the coloring.\n"); log("\n"); log(" -colorattr <attribute_name>\n"); log(" Use the specified attribute to assign colors. A unique color is\n"); @@ -616,7 +620,7 @@ struct ShowPass : public Pass { log(" annotate busses with a label indicating the width of the bus.\n"); log("\n"); log(" -signed\n"); - log(" mark ports (A, B) that are declarted as signed (using the [AB]_SIGNED\n"); + log(" mark ports (A, B) that are declared as signed (using the [AB]_SIGNED\n"); log(" cell parameter) with an asterisk next to the port name.\n"); log("\n"); log(" -stretch\n"); @@ -630,7 +634,7 @@ struct ShowPass : public Pass { log(" enumerate objects with internal ($-prefixed) names\n"); log("\n"); log(" -long\n"); - log(" do not abbeviate objects with internal ($-prefixed) names\n"); + log(" do not abbreviate objects with internal ($-prefixed) names\n"); log("\n"); log(" -notitle\n"); log(" do not add the module name as graph title to the dot file\n"); @@ -641,18 +645,26 @@ struct ShowPass : public Pass { log("The generated output files are '~/.yosys_show.dot' and '~/.yosys_show.<format>',\n"); log("unless another prefix is specified using -prefix <prefix>.\n"); log("\n"); + log("Yosys on Windows and YosysJS use different defaults: The output is written\n"); + log("to 'show.dot' in the current directory and new viewer is launched.\n"); + log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Generating Graphviz representation of design.\n"); + log_header(design, "Generating Graphviz representation of design.\n"); log_push(); std::vector<std::pair<std::string, RTLIL::Selection>> color_selections; std::vector<std::pair<std::string, RTLIL::Selection>> label_selections; +#if defined(EMSCRIPTEN) || defined(_WIN32) + std::string format = "dot"; + std::string prefix = "show"; +#else std::string format; - std::string viewer_exe; std::string prefix = stringf("%s/.yosys_show", getenv("HOME") ? getenv("HOME") : "."); +#endif + std::string viewer_exe; std::vector<std::string> libfiles; std::vector<RTLIL::Design*> libs; uint32_t colorSeed = 0; @@ -661,7 +673,7 @@ struct ShowPass : public Pass { bool flag_stretch = false; bool flag_pause = false; bool flag_enum = false; - bool flag_abbeviate = true; + bool flag_abbreviate = true; bool flag_notitle = false; RTLIL::IdString colorattr; @@ -731,12 +743,12 @@ struct ShowPass : public Pass { } if (arg == "-enum") { flag_enum = true; - flag_abbeviate = false; + flag_abbreviate = false; continue; } if (arg == "-long") { flag_enum = false; - flag_abbeviate = false; + flag_abbreviate = false; continue; } if (arg == "-notitle") { @@ -772,7 +784,7 @@ struct ShowPass : public Pass { } if (libs.size() > 0) - log_header("Continuing show pass.\n"); + log_header(design, "Continuing show pass.\n"); std::string dot_file = stringf("%s.dot", prefix.c_str()); std::string out_file = stringf("%s.%s", prefix.c_str(), format.empty() ? "svg" : format.c_str()); @@ -784,7 +796,7 @@ struct ShowPass : public Pass { delete lib; log_cmd_error("Can't open dot file `%s' for writing.\n", dot_file.c_str()); } - ShowWorker worker(f, design, libs, colorSeed, flag_width, flag_signed, flag_stretch, flag_enum, flag_abbeviate, flag_notitle, color_selections, label_selections, colorattr); + ShowWorker worker(f, design, libs, colorSeed, flag_width, flag_signed, flag_stretch, flag_enum, flag_abbreviate, flag_notitle, color_selections, label_selections, colorattr); fclose(f); for (auto lib : libs) @@ -833,5 +845,5 @@ struct ShowPass : public Pass { log_pop(); } } ShowPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/cmds/splice.cc b/passes/cmds/splice.cc index 3e0158c5c..7418ec4d2 100644 --- a/passes/cmds/splice.cc +++ b/passes/cmds/splice.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -36,6 +36,8 @@ struct SpliceWorker bool sel_by_wire; bool sel_any_bit; bool no_outputs; + bool do_wires; + std::set<RTLIL::IdString> ports; std::set<RTLIL::IdString> no_ports; @@ -62,7 +64,7 @@ struct SpliceWorker return sliced_signals_cache.at(sig); int offset = 0; - int p = driven_bits_map.at(sig.extract(0, 1).to_single_sigbit()) - 1; + int p = driven_bits_map.at(sig.extract(0, 1).as_bit()) - 1; while (driven_bits.at(p) != RTLIL::State::Sm) p--, offset++; @@ -209,23 +211,23 @@ struct SpliceWorker std::vector<std::pair<RTLIL::Wire*, RTLIL::SigSpec>> rework_wires; std::vector<Wire*> mod_wires = module->wires(); - for (auto mod : mod_wires) - if (!no_outputs && mod->port_output) { - if (!design->selected(module, mod)) + for (auto wire : mod_wires) + if ((!no_outputs && wire->port_output) || (do_wires && wire->name[0] == '\\')) { + if (!design->selected(module, wire)) continue; - RTLIL::SigSpec sig = sigmap(mod); + RTLIL::SigSpec sig = sigmap(wire); if (driven_chunks.count(sig) > 0) continue; RTLIL::SigSpec new_sig = get_spliced_signal(sig); if (new_sig != sig) - rework_wires.push_back(std::pair<RTLIL::Wire*, RTLIL::SigSpec>(mod, new_sig)); + rework_wires.push_back(std::pair<RTLIL::Wire*, RTLIL::SigSpec>(wire, new_sig)); } else - if (!mod->port_input) { - RTLIL::SigSpec sig = sigmap(mod); + if (!wire->port_input) { + RTLIL::SigSpec sig = sigmap(wire); if (spliced_signals_cache.count(sig) && spliced_signals_cache.at(sig) != sig) - rework_wires.push_back(std::pair<RTLIL::Wire*, RTLIL::SigSpec>(mod, spliced_signals_cache.at(sig))); + rework_wires.push_back(std::pair<RTLIL::Wire*, RTLIL::SigSpec>(wire, spliced_signals_cache.at(sig))); else if (sliced_signals_cache.count(sig) && sliced_signals_cache.at(sig) != sig) - rework_wires.push_back(std::pair<RTLIL::Wire*, RTLIL::SigSpec>(mod, sliced_signals_cache.at(sig))); + rework_wires.push_back(std::pair<RTLIL::Wire*, RTLIL::SigSpec>(wire, sliced_signals_cache.at(sig))); } for (auto &it : rework_wires) @@ -253,7 +255,7 @@ struct SplicePass : public Pass { log("\n"); log("This command adds $slice and $concat cells to the design to make the splicing\n"); log("of multi-bit signals explicit. This for example is useful for coarse grain\n"); - log("synthesis, where dedidacted hardware is needed to splice signals.\n"); + log("synthesis, where dedicated hardware is needed to splice signals.\n"); log("\n"); log(" -sel_by_cell\n"); log(" only select the cell ports to rewire by the cell. if the selection\n"); @@ -268,6 +270,9 @@ struct SplicePass : public Pass { log(" it is sufficient if the driver of any bit of a cell port is selected.\n"); log(" by default all bits must be selected.\n"); log("\n"); + log(" -wires\n"); + log(" also add $slice and $concat cells to drive otherwise unused wires.\n"); + log("\n"); log(" -no_outputs\n"); log(" do not rewire selected module outputs.\n"); log("\n"); @@ -289,6 +294,7 @@ struct SplicePass : public Pass { bool sel_by_wire = false; bool sel_any_bit = false; bool no_outputs = false; + bool do_wires = false; std::set<RTLIL::IdString> ports, no_ports; size_t argidx; @@ -305,6 +311,10 @@ struct SplicePass : public Pass { sel_any_bit = true; continue; } + if (args[argidx] == "-wires") { + do_wires = true; + continue; + } if (args[argidx] == "-no_outputs") { no_outputs = true; continue; @@ -331,7 +341,7 @@ struct SplicePass : public Pass { if (!ports.empty() && !no_ports.empty()) log_cmd_error("The options -port and -no_port are exclusive!\n"); - log_header("Executing SPLICE pass (creating cells for signal splicing).\n"); + log_header(design, "Executing SPLICE pass (creating cells for signal splicing).\n"); for (auto &mod_it : design->modules_) { @@ -348,11 +358,12 @@ struct SplicePass : public Pass { worker.sel_by_wire = sel_by_wire; worker.sel_any_bit = sel_any_bit; worker.no_outputs = no_outputs; + worker.do_wires = do_wires; worker.ports = ports; worker.no_ports = no_ports; worker.run(); } } } SplicePass; - + PRIVATE_NAMESPACE_END diff --git a/passes/cmds/splitnets.cc b/passes/cmds/splitnets.cc index d4e721a5d..14eeb066f 100644 --- a/passes/cmds/splitnets.cc +++ b/passes/cmds/splitnets.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -50,10 +50,23 @@ struct SplitnetsWorker new_wire_name += format.substr(1, 1); RTLIL::Wire *new_wire = module->addWire(module->uniquify(new_wire_name), width); - new_wire->port_id = wire->port_id; + new_wire->port_id = wire->port_id ? wire->port_id + offset : 0; new_wire->port_input = wire->port_input; new_wire->port_output = wire->port_output; + if (wire->attributes.count("\\src")) + new_wire->attributes["\\src"] = wire->attributes.at("\\src"); + + if (wire->attributes.count("\\keep")) + new_wire->attributes["\\keep"] = wire->attributes.at("\\keep"); + + if (wire->attributes.count("\\init")) { + Const old_init = wire->attributes.at("\\init"), new_init; + for (int i = offset; i < offset+width; i++) + new_init.bits.push_back(i < GetSize(old_init) ? old_init.bits.at(i) : State::Sx); + new_wire->attributes["\\init"] = new_init; + } + std::vector<RTLIL::SigBit> sigvec = RTLIL::SigSpec(new_wire).to_sigbit_vector(); splitmap[wire].insert(splitmap[wire].end(), sigvec.begin(), sigvec.end()); } @@ -96,7 +109,7 @@ struct SplitnetsPass : public Pass { bool flag_driver = false; std::string format = "[]:"; - log_header("Executing SPLITNETS pass (splitting up multi-bit signals).\n"); + log_header(design, "Executing SPLITNETS pass (splitting up multi-bit signals).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) @@ -117,14 +130,27 @@ struct SplitnetsPass : public Pass { } extra_args(args, argidx, design); - for (auto &mod_it : design->modules_) - { - RTLIL::Module *module = mod_it.second; - if (!design->selected(module)) - continue; + // module_ports_db[module_name][old_port_name] = new_port_name_list + dict<IdString, dict<IdString, vector<IdString>>> module_ports_db; + for (auto module : design->selected_modules()) + { SplitnetsWorker worker; + if (flag_ports) + { + int normalized_port_factor = 0; + + for (auto wire : module->wires()) + if (wire->port_id != 0) { + normalized_port_factor = max(normalized_port_factor, wire->port_id+1); + normalized_port_factor = max(normalized_port_factor, GetSize(wire)+1); + } + + for (auto wire : module->wires()) + wire->port_id *= normalized_port_factor; + } + if (flag_driver) { CellTypes ct(design); @@ -176,14 +202,69 @@ struct SplitnetsPass : public Pass { module->rewrite_sigspecs(worker); + if (flag_ports) + { + for (auto wire : module->wires()) + { + if (wire->port_id == 0) + continue; + + SigSpec sig(wire); + worker(sig); + + if (sig == wire) + continue; + + vector<IdString> &new_ports = module_ports_db[module->name][wire->name]; + + for (SigSpec c : sig.chunks()) + new_ports.push_back(c.as_wire()->name); + } + } + pool<RTLIL::Wire*> delete_wires; for (auto &it : worker.splitmap) delete_wires.insert(it.first); module->remove(delete_wires); - module->fixup_ports(); + if (flag_ports) + module->fixup_ports(); + } + + if (!module_ports_db.empty()) + { + for (auto module : design->modules()) + for (auto cell : module->cells()) + { + if (module_ports_db.count(cell->type) == 0) + continue; + + for (auto &it : module_ports_db.at(cell->type)) + { + IdString port_id = it.first; + const auto &new_port_ids = it.second; + + if (!cell->hasPort(port_id)) + continue; + + int offset = 0; + SigSpec sig = cell->getPort(port_id); + + for (auto nid : new_port_ids) + { + int nlen = GetSize(design->module(cell->type)->wire(nid)); + if (offset + nlen > GetSize(sig)) + nlen = GetSize(sig) - offset; + if (nlen > 0) + cell->setPort(nid, sig.extract(offset, nlen)); + offset += nlen; + } + + cell->unsetPort(port_id); + } + } } } } SplitnetsPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/cmds/stat.cc b/passes/cmds/stat.cc index d68c57b20..362a0edfc 100644 --- a/passes/cmds/stat.cc +++ b/passes/cmds/stat.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -19,6 +19,8 @@ #include "kernel/register.h" #include "kernel/celltypes.h" +#include "passes/techmap/libparse.h" + #include "kernel/log.h" USING_YOSYS_NAMESPACE @@ -29,17 +31,21 @@ struct statdata_t #define STAT_INT_MEMBERS X(num_wires) X(num_wire_bits) X(num_pub_wires) X(num_pub_wire_bits) \ X(num_memories) X(num_memory_bits) X(num_cells) X(num_processes) + #define STAT_NUMERIC_MEMBERS STAT_INT_MEMBERS X(area) + #define X(_name) int _name; STAT_INT_MEMBERS #undef X + double area; std::map<RTLIL::IdString, int, RTLIL::sort_by_id_str> num_cells_by_type; + std::set<RTLIL::IdString> unknown_cell_area; statdata_t operator+(const statdata_t &other) const { statdata_t sum = other; #define X(_name) sum._name += _name; - STAT_INT_MEMBERS + STAT_NUMERIC_MEMBERS #undef X for (auto &it : num_cells_by_type) sum.num_cells_by_type[it.first] += it.second; @@ -50,7 +56,7 @@ struct statdata_t { statdata_t sum = *this; #define X(_name) sum._name *= other; - STAT_INT_MEMBERS + STAT_NUMERIC_MEMBERS #undef X for (auto &it : sum.num_cells_by_type) it.second *= other; @@ -60,14 +66,14 @@ struct statdata_t statdata_t() { #define X(_name) _name = 0; - STAT_INT_MEMBERS + STAT_NUMERIC_MEMBERS #undef X } - statdata_t(RTLIL::Design *design, RTLIL::Module *mod, bool width_mode) + statdata_t(RTLIL::Design *design, RTLIL::Module *mod, bool width_mode, const dict<IdString, double> &cell_area) { #define X(_name) _name = 0; - STAT_INT_MEMBERS + STAT_NUMERIC_MEMBERS #undef X for (auto &it : mod->wires_) @@ -110,7 +116,7 @@ struct statdata_t int width_a = it.second->hasPort("\\A") ? GetSize(it.second->getPort("\\A")) : 0; int width_b = it.second->hasPort("\\B") ? GetSize(it.second->getPort("\\B")) : 0; int width_y = it.second->hasPort("\\Y") ? GetSize(it.second->getPort("\\Y")) : 0; - cell_type = stringf("%s_%d", cell_type.c_str(), std::max<int>({width_a, width_b, width_y})); + cell_type = stringf("%s_%d", cell_type.c_str(), max<int>({width_a, width_b, width_y})); } else if (cell_type.in("$mux", "$pmux")) cell_type = stringf("%s_%d", cell_type.c_str(), GetSize(it.second->getPort("\\Y"))); @@ -118,6 +124,13 @@ struct statdata_t cell_type = stringf("%s_%d", cell_type.c_str(), GetSize(it.second->getPort("\\Q"))); } + if (!cell_area.empty()) { + if (cell_area.count(cell_type)) + area += cell_area.at(cell_type); + else + unknown_cell_area.insert(cell_type); + } + num_cells++; num_cells_by_type[cell_type]++; } @@ -141,6 +154,17 @@ struct statdata_t log(" Number of cells: %6d\n", num_cells); for (auto &it : num_cells_by_type) log(" %-26s %6d\n", RTLIL::id2cstr(it.first), it.second); + + if (!unknown_cell_area.empty()) { + log("\n"); + for (auto cell_type : unknown_cell_area) + log(" Area for cell type %s is unknown!\n", cell_type.c_str()); + } + + if (area != 0) { + log("\n"); + log(" Chip area for this module: %f\n", area); + } } }; @@ -162,6 +186,26 @@ statdata_t hierarchy_worker(std::map<RTLIL::IdString, statdata_t> &mod_stat, RTL return mod_data; } +void read_liberty_cellarea(dict<IdString, double> &cell_area, string liberty_file) +{ + std::ifstream f; + f.open(liberty_file.c_str()); + if (f.fail()) + log_cmd_error("Can't open liberty file `%s': %s\n", liberty_file.c_str(), strerror(errno)); + LibertyParser libparser(f); + f.close(); + + for (auto cell : libparser.ast->children) + { + if (cell->id != "cell" || cell->args.size() != 1) + continue; + + LibertyAst *ar = cell->find("area"); + if (ar != NULL && !ar->value.empty()) + cell_area["\\" + cell->args[0]] = atof(ar->value.c_str()); + } +} + struct StatPass : public Pass { StatPass() : Pass("stat", "print some statistics") { } virtual void help() @@ -178,6 +222,9 @@ struct StatPass : public Pass { log(" selected and a module has the 'top' attribute set, this module is used\n"); log(" default value for this option.\n"); log("\n"); + log(" -liberty <liberty_file>\n"); + log(" use cell area information from the provided liberty file\n"); + log("\n"); log(" -width\n"); log(" annotate internal cell types with their word width.\n"); log(" e.g. $add_8 for an 8 bit wide $add cell.\n"); @@ -185,11 +232,12 @@ struct StatPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Printing statistics.\n"); + log_header(design, "Printing statistics.\n"); bool width_mode = false; RTLIL::Module *top_mod = NULL; std::map<RTLIL::IdString, statdata_t> mod_stat; + dict<IdString, double> cell_area; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) @@ -198,6 +246,12 @@ struct StatPass : public Pass { width_mode = true; continue; } + if (args[argidx] == "-liberty" && argidx+1 < args.size()) { + string liberty_file = args[++argidx]; + rewrite_filename(liberty_file); + read_liberty_cellarea(cell_area, liberty_file); + continue; + } if (args[argidx] == "-top" && argidx+1 < args.size()) { if (design->modules_.count(RTLIL::escape_id(args[argidx+1])) == 0) log_cmd_error("Can't find module %s.\n", args[argidx+1].c_str()); @@ -208,25 +262,22 @@ struct StatPass : public Pass { } extra_args(args, argidx, design); - for (auto &it : design->modules_) + for (auto mod : design->selected_modules()) { - if (!design->selected_module(it.first)) - continue; - if (!top_mod && design->full_selection()) - if (it.second->get_bool_attribute("\\top")) - top_mod = it.second; + if (mod->get_bool_attribute("\\top")) + top_mod = mod; - statdata_t data(design, it.second, width_mode); - mod_stat[it.first] = data; + statdata_t data(design, mod, width_mode, cell_area); + mod_stat[mod->name] = data; log("\n"); - log("=== %s%s ===\n", RTLIL::id2cstr(it.first), design->selected_whole_module(it.first) ? "" : " (partially selected)"); + log("=== %s%s ===\n", RTLIL::id2cstr(mod->name), design->selected_whole_module(mod->name) ? "" : " (partially selected)"); log("\n"); data.log_data(); } - if (top_mod != NULL) + if (top_mod != NULL && GetSize(mod_stat) > 1) { log("\n"); log("=== design hierarchy ===\n"); @@ -242,5 +293,5 @@ struct StatPass : public Pass { log("\n"); } } StatPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/cmds/torder.cc b/passes/cmds/torder.cc new file mode 100644 index 000000000..56223610d --- /dev/null +++ b/passes/cmds/torder.cc @@ -0,0 +1,123 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include "kernel/celltypes.h" +#include "kernel/sigtools.h" +#include "kernel/utils.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct TorderPass : public Pass { + TorderPass() : Pass("torder", "print cells in topological order") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" torder [options] [selection]\n"); + log("\n"); + log("This command prints the selected cells in topological order.\n"); + log("\n"); + log(" -stop <cell_type> <cell_port>\n"); + log(" do not use the specified cell port in topological sorting\n"); + log("\n"); + log(" -noautostop\n"); + log(" by default Q outputs of internal FF cells and memory read port outputs\n"); + log(" are not used in topological sorting. this option deactivates that.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + bool noautostop = false; + dict<IdString, pool<IdString>> stop_db; + + log_header(design, "Executing TORDER pass (print cells in topological order).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-stop" && argidx+2 < args.size()) { + IdString cell_type = RTLIL::escape_id(args[++argidx]); + IdString cell_port = RTLIL::escape_id(args[++argidx]); + stop_db[cell_type].insert(cell_port); + continue; + } + if (args[argidx] == "-noautostop") { + noautostop = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) + { + log("module %s\n", log_id(module)); + + SigMap sigmap(module); + dict<SigBit, pool<IdString>> bit_drivers, bit_users; + TopoSort<IdString, RTLIL::sort_by_id_str> toposort; + + for (auto cell : module->selected_cells()) + for (auto conn : cell->connections()) + { + if (stop_db.count(cell->type) && stop_db.at(cell->type).count(conn.first)) + continue; + + if (!noautostop && yosys_celltypes.cell_known(cell->type)) { + if (conn.first.in("\\Q", "\\CTRL_OUT", "\\RD_DATA")) + continue; + if (cell->type == "$memrd" && conn.first == "\\DATA") + continue; + } + + if (cell->input(conn.first)) + for (auto bit : sigmap(conn.second)) + bit_users[bit].insert(cell->name); + + if (cell->output(conn.first)) + for (auto bit : sigmap(conn.second)) + bit_drivers[bit].insert(cell->name); + + toposort.node(cell->name); + } + + for (auto &it : bit_users) + if (bit_drivers.count(it.first)) + for (auto driver_cell : bit_drivers.at(it.first)) + for (auto user_cell : it.second) + toposort.edge(driver_cell, user_cell); + + toposort.analyze_loops = true; + toposort.sort(); + + for (auto &it : toposort.loops) { + log(" loop"); + for (auto cell : it) + log(" %s", log_id(cell)); + log("\n"); + } + + for (auto cell : toposort.sorted) + log(" cell %s\n", log_id(cell)); + } + } +} TorderPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/cmds/write_file.cc b/passes/cmds/write_file.cc index 25ec4acc2..b78265933 100644 --- a/passes/cmds/write_file.cc +++ b/passes/cmds/write_file.cc @@ -31,7 +31,7 @@ struct WriteFileFrontend : public Frontend { log("\n"); log(" write_file [options] output_file [input_file]\n"); log("\n"); - log("Write the text fron the input file to the output file.\n"); + log("Write the text from the input file to the output file.\n"); log("\n"); log(" -a\n"); log(" Append to output file (instead of overwriting)\n"); diff --git a/passes/equiv/Makefile.inc b/passes/equiv/Makefile.inc index 548eaca3b..dd7b3be02 100644 --- a/passes/equiv/Makefile.inc +++ b/passes/equiv/Makefile.inc @@ -6,4 +6,7 @@ OBJS += passes/equiv/equiv_status.o OBJS += passes/equiv/equiv_add.o OBJS += passes/equiv/equiv_remove.o OBJS += passes/equiv/equiv_induct.o +OBJS += passes/equiv/equiv_struct.o +OBJS += passes/equiv/equiv_purge.o +OBJS += passes/equiv/equiv_mark.o diff --git a/passes/equiv/equiv_add.cc b/passes/equiv/equiv_add.cc index a6e2f01b4..0494a724f 100644 --- a/passes/equiv/equiv_add.cc +++ b/passes/equiv/equiv_add.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -29,15 +29,19 @@ struct EquivAddPass : public Pass { { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" equiv_add gold_sig gate_sig\n"); + log(" equiv_add [-try] gold_sig gate_sig\n"); log("\n"); log("This command adds an $equiv cell for the specified signals.\n"); log("\n"); + log("\n"); + log(" equiv_add [-try] -cell gold_cell gate_cell\n"); + log("\n"); + log("This command adds $equiv cells for the ports of the specified cells.\n"); + log("\n"); } virtual void execute(std::vector<std::string> args, Design *design) { - if (GetSize(args) != 3) - cmd_error(args, GetSize(args)-1, "Invalid number of arguments."); + bool try_mode = false; if (design->selected_active_module.empty()) log_cmd_error("This command must be executed in module context!\n"); @@ -45,44 +49,128 @@ struct EquivAddPass : public Pass { Module *module = design->module(design->selected_active_module); log_assert(module != nullptr); - SigSpec gold_signal, gate_signal; + if (GetSize(args) > 1 && args[1] == "-try") { + args.erase(args.begin() + 1); + try_mode = true; + } + + if (GetSize(args) == 4 && args[1] == "-cell") + { + Cell *gold_cell = module->cell(RTLIL::escape_id(args[2])); + Cell *gate_cell = module->cell(RTLIL::escape_id(args[3])); + + if (gold_cell == nullptr) { + if (try_mode) { + log_warning("Can't find gold cell '%s'.\n", args[2].c_str()); + return; + } + log_cmd_error("Can't find gold cell '%s'.\n", args[2].c_str()); + } + + if (gate_cell == nullptr) { + if (try_mode) { + log_warning("Can't find gate cell '%s'.\n", args[3].c_str()); + return; + } + log_cmd_error("Can't find gate cell '%s'.\n", args[3].c_str()); + } + + for (auto conn : gold_cell->connections()) + { + auto port = conn.first; + SigSpec gold_sig = gold_cell->getPort(port); + SigSpec gate_sig = gate_cell->getPort(port); + int width = min(GetSize(gold_sig), GetSize(gate_sig)); - if (!SigSpec::parse(gate_signal, module, args[2])) - log_cmd_error("Error in gate signal: %s\n", args[2].c_str()); + if (gold_cell->input(port) && gate_cell->input(port)) + { + SigSpec combined_sig = module->addWire(NEW_ID, width); - if (!SigSpec::parse_rhs(gate_signal, gold_signal, module, args[1])) - log_cmd_error("Error in gold signal: %s\n", args[1].c_str()); + for (int i = 0; i < width; i++) { + module->addEquiv(NEW_ID, gold_sig[i], gate_sig[i], combined_sig[i]); + gold_sig[i] = gate_sig[i] = combined_sig[i]; + } - log_assert(GetSize(gold_signal) == GetSize(gate_signal)); - SigSpec equiv_signal = module->addWire(NEW_ID, GetSize(gold_signal)); + gold_cell->setPort(port, gold_sig); + gate_cell->setPort(port, gate_sig); + continue; + } - SigMap sigmap(module); - sigmap.apply(gold_signal); - sigmap.apply(gate_signal); + if (gold_cell->output(port) && gate_cell->output(port)) + { + SigSpec new_gold_wire = module->addWire(NEW_ID, width); + SigSpec new_gate_wire = module->addWire(NEW_ID, width); + SigSig gg_conn; - dict<SigBit, SigBit> to_equiv_bits; - pool<Cell*> added_equiv_cells; + for (int i = 0; i < width; i++) { + module->addEquiv(NEW_ID, new_gold_wire[i], new_gold_wire[i], gold_sig[i]); + gg_conn.first.append(gate_sig[i]); + gg_conn.second.append(gold_sig[i]); + gold_sig[i] = new_gold_wire[i]; + gate_sig[i] = new_gate_wire[i]; + } - for (int i = 0; i < GetSize(gold_signal); i++) { - Cell *equiv_cell = module->addEquiv(NEW_ID, gold_signal[i], gate_signal[i], equiv_signal[i]); - equiv_cell->set_bool_attribute("\\keep"); - to_equiv_bits[gold_signal[i]] = equiv_signal[i]; - to_equiv_bits[gate_signal[i]] = equiv_signal[i]; - added_equiv_cells.insert(equiv_cell); + module->connect(gg_conn); + gold_cell->setPort(port, gold_sig); + gate_cell->setPort(port, gate_sig); + continue; + } + } } + else + { + if (GetSize(args) != 3) + cmd_error(args, GetSize(args)-1, "Invalid number of arguments."); + + SigSpec gold_signal, gate_signal; + + if (!SigSpec::parse(gate_signal, module, args[2])) { + if (try_mode) { + log_warning("Error in gate signal: %s\n", args[2].c_str()); + return; + } + log_cmd_error("Error in gate signal: %s\n", args[2].c_str()); + } - for (auto cell : module->cells()) - for (auto conn : cell->connections()) - if (!added_equiv_cells.count(cell) && cell->input(conn.first)) { - SigSpec new_sig; - for (auto bit : conn.second) - if (to_equiv_bits.count(sigmap(bit))) - new_sig.append(to_equiv_bits.at(sigmap(bit))); - else - new_sig.append(bit); - if (conn.second != new_sig) - cell->setPort(conn.first, new_sig); + if (!SigSpec::parse_rhs(gate_signal, gold_signal, module, args[1])) { + if (try_mode) { + log_warning("Error in gold signal: %s\n", args[1].c_str()); + return; + } + log_cmd_error("Error in gold signal: %s\n", args[1].c_str()); } + + log_assert(GetSize(gold_signal) == GetSize(gate_signal)); + SigSpec equiv_signal = module->addWire(NEW_ID, GetSize(gold_signal)); + + SigMap sigmap(module); + sigmap.apply(gold_signal); + sigmap.apply(gate_signal); + + dict<SigBit, SigBit> to_equiv_bits; + pool<Cell*> added_equiv_cells; + + for (int i = 0; i < GetSize(gold_signal); i++) { + Cell *equiv_cell = module->addEquiv(NEW_ID, gold_signal[i], gate_signal[i], equiv_signal[i]); + equiv_cell->set_bool_attribute("\\keep"); + to_equiv_bits[gold_signal[i]] = equiv_signal[i]; + to_equiv_bits[gate_signal[i]] = equiv_signal[i]; + added_equiv_cells.insert(equiv_cell); + } + + for (auto cell : module->cells()) + for (auto conn : cell->connections()) + if (!added_equiv_cells.count(cell) && cell->input(conn.first)) { + SigSpec new_sig; + for (auto bit : conn.second) + if (to_equiv_bits.count(sigmap(bit))) + new_sig.append(to_equiv_bits.at(sigmap(bit))); + else + new_sig.append(bit); + if (conn.second != new_sig) + cell->setPort(conn.first, new_sig); + } + } } } EquivAddPass; diff --git a/passes/equiv/equiv_induct.cc b/passes/equiv/equiv_induct.cc index c5b4eda72..c958c3de4 100644 --- a/passes/equiv/equiv_induct.cc +++ b/passes/equiv/equiv_induct.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -32,7 +32,7 @@ struct EquivInductWorker vector<Cell*> cells; pool<Cell*> workset; - ezDefaultSAT ez; + ezSatPtr ez; SatGen satgen; int max_seq; @@ -43,7 +43,8 @@ struct EquivInductWorker SigPool undriven_signals; EquivInductWorker(Module *module, const pool<Cell*> &unproven_equiv_cells, bool model_undef, int max_seq) : module(module), sigmap(module), - cells(module->selected_cells()), workset(unproven_equiv_cells), satgen(&ez, &sigmap), max_seq(max_seq), success_counter(0) + cells(module->selected_cells()), workset(unproven_equiv_cells), + satgen(ez.get(), &sigmap), max_seq(max_seq), success_counter(0) { satgen.model_undef = model_undef; } @@ -58,14 +59,14 @@ struct EquivInductWorker cell_warn_cache.insert(cell); } if (cell->type == "$equiv") { - SigBit bit_a = sigmap(cell->getPort("\\A")).to_single_sigbit(); - SigBit bit_b = sigmap(cell->getPort("\\B")).to_single_sigbit(); + SigBit bit_a = sigmap(cell->getPort("\\A")).as_bit(); + SigBit bit_b = sigmap(cell->getPort("\\B")).as_bit(); if (bit_a != bit_b) { int ez_a = satgen.importSigBit(bit_a, step); int ez_b = satgen.importSigBit(bit_b, step); - int cond = ez.IFF(ez_a, ez_b); + int cond = ez->IFF(ez_a, ez_b); if (satgen.model_undef) - cond = ez.OR(cond, satgen.importUndefSigBit(bit_a, step)); + cond = ez->OR(cond, satgen.importUndefSigBit(bit_a, step)); ez_equal_terms.push_back(cond); } } @@ -73,11 +74,11 @@ struct EquivInductWorker if (satgen.model_undef) { for (auto bit : undriven_signals.export_all()) - ez.assume(ez.NOT(satgen.importUndefSigBit(bit, step))); + ez->assume(ez->NOT(satgen.importUndefSigBit(bit, step))); } log_assert(!ez_step_is_consistent.count(step)); - ez_step_is_consistent[step] = ez.expression(ez.OpAnd, ez_equal_terms); + ez_step_is_consistent[step] = ez->expression(ez->OpAnd, ez_equal_terms); } void run() @@ -101,27 +102,27 @@ struct EquivInductWorker if (satgen.model_undef) { for (auto bit : satgen.initial_state.export_all()) - ez.assume(ez.NOT(satgen.importUndefSigBit(bit, 1))); + ez->assume(ez->NOT(satgen.importUndefSigBit(bit, 1))); log(" Undef modelling: force def on %d initial reg values and %d inputs.\n", GetSize(satgen.initial_state), GetSize(undriven_signals)); } for (int step = 1; step <= max_seq; step++) { - ez.assume(ez_step_is_consistent[step]); + ez->assume(ez_step_is_consistent[step]); - log(" Proving existence of base case for step %d. (%d clauses over %d variables)\n", step, ez.numCnfClauses(), ez.numCnfVariables()); - if (!ez.solve()) { + log(" Proving existence of base case for step %d. (%d clauses over %d variables)\n", step, ez->numCnfClauses(), ez->numCnfVariables()); + if (!ez->solve()) { log(" Proof for base case failed. Circuit inherently diverges!\n"); return; } create_timestep(step+1); - int new_step_not_consistent = ez.NOT(ez_step_is_consistent[step+1]); - ez.bind(new_step_not_consistent); + int new_step_not_consistent = ez->NOT(ez_step_is_consistent[step+1]); + ez->bind(new_step_not_consistent); - log(" Proving induction step %d. (%d clauses over %d variables)\n", step, ez.numCnfClauses(), ez.numCnfVariables()); - if (!ez.solve(new_step_not_consistent)) { + log(" Proving induction step %d. (%d clauses over %d variables)\n", step, ez->numCnfClauses(), ez->numCnfVariables()); + if (!ez->solve(new_step_not_consistent)) { log(" Proof for induction step holds. Entire workset of %d cells proven!\n", GetSize(workset)); for (auto cell : workset) cell->setPort("\\B", cell->getPort("\\A")); @@ -136,19 +137,19 @@ struct EquivInductWorker for (auto cell : workset) { - SigBit bit_a = sigmap(cell->getPort("\\A")).to_single_sigbit(); - SigBit bit_b = sigmap(cell->getPort("\\B")).to_single_sigbit(); + SigBit bit_a = sigmap(cell->getPort("\\A")).as_bit(); + SigBit bit_b = sigmap(cell->getPort("\\B")).as_bit(); log(" Trying to prove $equiv for %s:", log_signal(sigmap(cell->getPort("\\Y")))); int ez_a = satgen.importSigBit(bit_a, max_seq+1); int ez_b = satgen.importSigBit(bit_b, max_seq+1); - int cond = ez.XOR(ez_a, ez_b); + int cond = ez->XOR(ez_a, ez_b); if (satgen.model_undef) - cond = ez.AND(cond, ez.NOT(satgen.importUndefSigBit(bit_a, max_seq+1))); + cond = ez->AND(cond, ez->NOT(satgen.importUndefSigBit(bit_a, max_seq+1))); - if (!ez.solve(cond)) { + if (!ez->solve(cond)) { log(" success!\n"); cell->setPort("\\B", cell->getPort("\\A")); success_counter++; @@ -197,7 +198,7 @@ struct EquivInductPass : public Pass { bool model_undef = false; int max_seq = 4; - log_header("Executing EQUIV_INDUCT pass.\n"); + log_header(design, "Executing EQUIV_INDUCT pass.\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { diff --git a/passes/equiv/equiv_make.cc b/passes/equiv/equiv_make.cc index 5635e7a7e..40ca42621 100644 --- a/passes/equiv/equiv_make.cc +++ b/passes/equiv/equiv_make.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -280,7 +280,7 @@ struct EquivMakeWorker for (auto c : cells_list) for (auto &conn : c->connections()) - if (ct.cell_input(c->type, conn.first)) { + if (!ct.cell_output(c->type, conn.first)) { SigSpec old_sig = assign_map(conn.second); SigSpec new_sig = rd_signal_map(old_sig); if (old_sig != new_sig) { @@ -407,7 +407,7 @@ struct EquivMakePass : public Pass { log(" Do not match cells or signals that match the names in the file.\n"); log("\n"); log(" -encfile <file>\n"); - log(" Match FSM encodings using the desiption from the file.\n"); + log(" Match FSM encodings using the description from the file.\n"); log(" See 'help fsm_recode' for details.\n"); log("\n"); log("Note: The circuit created by this command is not a miter (with something like\n"); @@ -464,7 +464,7 @@ struct EquivMakePass : public Pass { worker.read_blacklists(); worker.read_encfiles(); - log_header("Executing EQUIV_MAKE pass (creating equiv checking module).\n"); + log_header(design, "Executing EQUIV_MAKE pass (creating equiv checking module).\n"); worker.equiv_mod = design->addModule(RTLIL::escape_id(args[argidx+2])); worker.run(); diff --git a/passes/equiv/equiv_mark.cc b/passes/equiv/equiv_mark.cc new file mode 100644 index 000000000..22c501763 --- /dev/null +++ b/passes/equiv/equiv_mark.cc @@ -0,0 +1,239 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include "kernel/sigtools.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct EquivMarkWorker +{ + Module *module; + SigMap sigmap; + + // cache for traversing signal flow graph + dict<SigBit, pool<IdString>> up_bit2cells; + dict<IdString, pool<SigBit>> up_cell2bits; + pool<IdString> edge_cells, equiv_cells; + + // graph traversal state + pool<SigBit> queue, visited; + + // assigned regions + dict<IdString, int> cell_regions; + dict<SigBit, int> bit_regions; + int next_region; + + // merge-find + mfp<int> region_mf; + + EquivMarkWorker(Module *module) : module(module), sigmap(module) + { + for (auto cell : module->cells()) + { + if (cell->type == "$equiv") + equiv_cells.insert(cell->name); + + for (auto &port : cell->connections()) + { + if (cell->input(port.first)) + for (auto bit : sigmap(port.second)) + up_cell2bits[cell->name].insert(bit); + + if (cell->output(port.first)) + for (auto bit : sigmap(port.second)) + up_bit2cells[bit].insert(cell->name); + } + } + + next_region = 0; + } + + void mark() + { + while (!queue.empty()) + { + pool<IdString> cells; + + for (auto &bit : queue) + { + // log_assert(bit_regions.count(bit) == 0); + bit_regions[bit] = next_region; + visited.insert(bit); + + for (auto cell : up_bit2cells[bit]) + if (edge_cells.count(cell) == 0) + cells.insert(cell); + } + + queue.clear(); + + for (auto cell : cells) + { + if (next_region == 0 && equiv_cells.count(cell)) + continue; + + if (cell_regions.count(cell)) { + if (cell_regions.at(cell) != 0) + region_mf.merge(cell_regions.at(cell), next_region); + continue; + } + + cell_regions[cell] = next_region; + + for (auto bit : up_cell2bits[cell]) + if (visited.count(bit) == 0) + queue.insert(bit); + } + } + + next_region++; + } + + void run() + { + log("Running equiv_mark on module %s:\n", log_id(module)); + + // marking region 0 + + for (auto wire : module->wires()) + if (wire->port_id > 0) + for (auto bit : sigmap(wire)) + queue.insert(bit); + + for (auto cell_name : equiv_cells) + { + auto cell = module->cell(cell_name); + + SigSpec sig_a = sigmap(cell->getPort("\\A")); + SigSpec sig_b = sigmap(cell->getPort("\\B")); + + if (sig_a == sig_b) { + for (auto bit : sig_a) + queue.insert(bit); + edge_cells.insert(cell_name); + cell_regions[cell_name] = 0; + } + } + + mark(); + + // marking unsolved regions + + for (auto cell : module->cells()) + { + if (cell_regions.count(cell->name) || cell->type != "$equiv") + continue; + + SigSpec sig_a = sigmap(cell->getPort("\\A")); + SigSpec sig_b = sigmap(cell->getPort("\\B")); + + log_assert(sig_a != sig_b); + + for (auto bit : sig_a) + queue.insert(bit); + + for (auto bit : sig_b) + queue.insert(bit); + + cell_regions[cell->name] = next_region; + mark(); + } + + // setting attributes + + dict<int, int> final_region_map; + int next_final_region = 0; + + dict<int, int> region_cell_count; + dict<int, int> region_wire_count; + + for (int i = 0; i < next_region; i++) { + int r = region_mf.find(i); + if (final_region_map.count(r) == 0) + final_region_map[r] = next_final_region++; + final_region_map[i] = final_region_map[r]; + } + + for (auto cell : module->cells()) + { + if (cell_regions.count(cell->name)) { + int r = final_region_map.at(cell_regions.at(cell->name)); + cell->attributes["\\equiv_region"] = Const(r); + region_cell_count[r]++; + } else + cell->attributes.erase("\\equiv_region"); + } + + for (auto wire : module->wires()) + { + pool<int> regions; + for (auto bit : sigmap(wire)) + if (bit_regions.count(bit)) + regions.insert(region_mf.find(bit_regions.at(bit))); + + if (GetSize(regions) == 1) { + int r = final_region_map.at(*regions.begin()); + wire->attributes["\\equiv_region"] = Const(r); + region_wire_count[r]++; + } else + wire->attributes.erase("\\equiv_region"); + } + + for (int i = 0; i < next_final_region; i++) + log(" region %d: %d cells, %d wires\n", i, region_wire_count[i], region_cell_count[i]); + } +}; + +struct EquivMarkPass : public Pass { + EquivMarkPass() : Pass("equiv_mark", "mark equivalence checking regions") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" equiv_mark [options] [selection]\n"); + log("\n"); + log("This command marks the regions in an equivalence checking module. Region 0 is\n"); + log("the proven part of the circuit. Regions with higher numbers are connected\n"); + log("unproven subcricuits. The integer attribute 'equiv_region' is set on all\n"); + log("wires and cells.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, Design *design) + { + log_header(design, "Executing EQUIV_MARK pass.\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + // if (args[argidx] == "-foobar") { + // continue; + // } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_whole_modules_warn()) { + EquivMarkWorker worker(module); + worker.run(); + } + } +} EquivMarkPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/equiv/equiv_miter.cc b/passes/equiv/equiv_miter.cc index 23b348184..eb2e5a171 100644 --- a/passes/equiv/equiv_miter.cc +++ b/passes/equiv/equiv_miter.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -156,7 +156,7 @@ struct EquivMiterWorker struct RewriteSigSpecWorker { RTLIL::Module * mod; void operator()(SigSpec &sig) { - vector<RTLIL::SigChunk> chunks = sig.chunks(); + vector<SigChunk> chunks = sig.chunks(); for (auto &c : chunks) if (c.wire != NULL) c.wire = mod->wires_.at(c.wire->name); @@ -333,7 +333,7 @@ struct EquivMiterPass : public Pass { found_two_modules: log_cmd_error("Exactly one module must be selected for 'equiv_miter'!\n"); - log_header("Executing EQUIV_MITER pass.\n"); + log_header(design, "Executing EQUIV_MITER pass.\n"); worker.miter_module = design->addModule(worker.miter_name); worker.run(); diff --git a/passes/equiv/equiv_purge.cc b/passes/equiv/equiv_purge.cc new file mode 100644 index 000000000..163b1009b --- /dev/null +++ b/passes/equiv/equiv_purge.cc @@ -0,0 +1,210 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include "kernel/sigtools.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct EquivPurgeWorker +{ + Module *module; + SigMap sigmap; + int name_cnt; + + EquivPurgeWorker(Module *module) : module(module), sigmap(module), name_cnt(0) { } + + SigSpec make_output(SigSpec sig, IdString cellname) + { + if (sig.is_wire()) { + Wire *wire = sig.as_wire(); + if (wire->name[0] == '\\') { + if (!wire->port_output) { + log(" Module output: %s (%s)\n", log_signal(wire), log_id(cellname)); + wire->port_output = true; + } + return wire; + } + } + + while (1) + { + IdString name = stringf("\\equiv_%d", name_cnt++); + if (module->count_id(name)) + continue; + + Wire *wire = module->addWire(name, GetSize(sig)); + wire->port_output = true; + module->connect(wire, sig); + log(" Module output: %s (%s)\n", log_signal(wire), log_id(cellname)); + return wire; + } + } + + SigSpec make_input(SigSpec sig) + { + if (sig.is_wire()) { + Wire *wire = sig.as_wire(); + if (wire->name[0] == '\\') { + if (!wire->port_output) { + log(" Module input: %s\n", log_signal(wire)); + wire->port_input = true; + } + return module->addWire(NEW_ID, GetSize(sig)); + } + } + + while (1) + { + IdString name = stringf("\\equiv_%d", name_cnt++); + if (module->count_id(name)) + continue; + + Wire *wire = module->addWire(name, GetSize(sig)); + wire->port_input = true; + module->connect(sig, wire); + log(" Module input: %s\n", log_signal(wire)); + return module->addWire(NEW_ID, GetSize(sig)); + } + } + + void run() + { + log("Running equiv_purge on module %s:\n", log_id(module)); + + for (auto wire : module->wires()) { + wire->port_input = false; + wire->port_output = false; + } + + pool<SigBit> queue, visited; + + // cache for traversing signal flow graph + dict<SigBit, pool<IdString>> up_bit2cells; + dict<IdString, pool<SigBit>> up_cell2bits; + + for (auto cell : module->cells()) + { + if (cell->type != "$equiv") { + for (auto &port : cell->connections()) { + if (cell->input(port.first)) + for (auto bit : sigmap(port.second)) + up_cell2bits[cell->name].insert(bit); + if (cell->output(port.first)) + for (auto bit : sigmap(port.second)) + up_bit2cells[bit].insert(cell->name); + } + continue; + } + + SigSpec sig_a = sigmap(cell->getPort("\\A")); + SigSpec sig_b = sigmap(cell->getPort("\\B")); + SigSpec sig_y = sigmap(cell->getPort("\\Y")); + + if (sig_a == sig_b) + continue; + + for (auto bit : sig_a) + queue.insert(bit); + + for (auto bit : sig_b) + queue.insert(bit); + + for (auto bit : sig_y) + visited.insert(bit); + + cell->setPort("\\Y", make_output(sig_y, cell->name)); + } + + SigSpec srcsig; + SigMap rewrite_sigmap(module); + + while (!queue.empty()) + { + pool<SigBit> next_queue; + + for (auto bit : queue) + visited.insert(bit); + + for (auto bit : queue) + { + auto &cells = up_bit2cells[bit]; + + if (cells.empty()) { + srcsig.append(bit); + } else { + for (auto cell : cells) + for (auto bit : up_cell2bits[cell]) + if (visited.count(bit) == 0) + next_queue.insert(bit); + } + } + + next_queue.swap(queue); + } + + srcsig.sort_and_unify(); + + for (SigChunk chunk : srcsig.chunks()) + if (chunk.wire != nullptr) + rewrite_sigmap.add(chunk, make_input(chunk)); + + for (auto cell : module->cells()) + if (cell->type == "$equiv") + cell->setPort("\\Y", rewrite_sigmap(sigmap(cell->getPort("\\Y")))); + + module->fixup_ports(); + } +}; + +struct EquivPurgePass : public Pass { + EquivPurgePass() : Pass("equiv_purge", "purge equivalence checking module") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" equiv_purge [options] [selection]\n"); + log("\n"); + log("This command removes the proven part of an equivalence checking module, leaving\n"); + log("only the unproven segments in the design. This will also remove and add module\n"); + log("ports as needed.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, Design *design) + { + log_header(design, "Executing EQUIV_PURGE pass.\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + // if (args[argidx] == "-foobar") { + // continue; + // } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_whole_modules_warn()) { + EquivPurgeWorker worker(module); + worker.run(); + } + } +} EquivPurgePass; + +PRIVATE_NAMESPACE_END diff --git a/passes/equiv/equiv_remove.cc b/passes/equiv/equiv_remove.cc index b6e232f9b..770497a51 100644 --- a/passes/equiv/equiv_remove.cc +++ b/passes/equiv/equiv_remove.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -46,7 +46,7 @@ struct EquivRemovePass : public Pass { bool mode_gate = false; int remove_count = 0; - log_header("Executing EQUIV_REMOVE pass.\n"); + log_header(design, "Executing EQUIV_REMOVE pass.\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { diff --git a/passes/equiv/equiv_simple.cc b/passes/equiv/equiv_simple.cc index 579877e65..49963ed68 100644 --- a/passes/equiv/equiv_simple.cc +++ b/passes/equiv/equiv_simple.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -32,7 +32,7 @@ struct EquivSimpleWorker SigMap &sigmap; dict<SigBit, Cell*> &bit2driver; - ezDefaultSAT ez; + ezSatPtr ez; SatGen satgen; int max_seq; bool verbose; @@ -41,7 +41,7 @@ struct EquivSimpleWorker EquivSimpleWorker(const vector<Cell*> &equiv_cells, SigMap &sigmap, dict<SigBit, Cell*> &bit2driver, int max_seq, bool verbose, bool model_undef) : module(equiv_cells.front()->module), equiv_cells(equiv_cells), equiv_cell(nullptr), - sigmap(sigmap), bit2driver(bit2driver), satgen(&ez, &sigmap), max_seq(max_seq), verbose(verbose) + sigmap(sigmap), bit2driver(bit2driver), satgen(ez.get(), &sigmap), max_seq(max_seq), verbose(verbose) { satgen.model_undef = model_undef; } @@ -89,9 +89,9 @@ struct EquivSimpleWorker bool run_cell() { - SigBit bit_a = sigmap(equiv_cell->getPort("\\A")).to_single_sigbit(); - SigBit bit_b = sigmap(equiv_cell->getPort("\\B")).to_single_sigbit(); - int ez_context = ez.frozen_literal(); + SigBit bit_a = sigmap(equiv_cell->getPort("\\A")).as_bit(); + SigBit bit_b = sigmap(equiv_cell->getPort("\\B")).as_bit(); + int ez_context = ez->frozen_literal(); if (satgen.model_undef) { @@ -99,14 +99,14 @@ struct EquivSimpleWorker int ez_b = satgen.importDefSigBit(bit_b, max_seq+1); int ez_undef_a = satgen.importUndefSigBit(bit_a, max_seq+1); - ez.assume(ez.XOR(ez_a, ez_b), ez_context); - ez.assume(ez.NOT(ez_undef_a), ez_context); + ez->assume(ez->XOR(ez_a, ez_b), ez_context); + ez->assume(ez->NOT(ez_undef_a), ez_context); } else { int ez_a = satgen.importSigBit(bit_a, max_seq+1); int ez_b = satgen.importSigBit(bit_b, max_seq+1); - ez.assume(ez.XOR(ez_a, ez_b), ez_context); + ez->assume(ez->XOR(ez_a, ez_b), ez_context); } pool<SigBit> seed_a = { bit_a }; @@ -168,16 +168,16 @@ struct EquivSimpleWorker if (satgen.model_undef) { for (auto bit : input_bits) - ez.assume(ez.NOT(satgen.importUndefSigBit(bit, step+1))); + ez->assume(ez->NOT(satgen.importUndefSigBit(bit, step+1))); } if (verbose) - log(" Problem size at t=%d: %d literals, %d clauses\n", step, ez.numCnfVariables(), ez.numCnfClauses()); + log(" Problem size at t=%d: %d literals, %d clauses\n", step, ez->numCnfVariables(), ez->numCnfClauses()); - if (!ez.solve(ez_context)) { + if (!ez->solve(ez_context)) { log(verbose ? " Proved equivalence! Marking $equiv cell as proven.\n" : " success!\n"); equiv_cell->setPort("\\B", equiv_cell->getPort("\\A")); - ez.assume(ez.NOT(ez_context)); + ez->assume(ez->NOT(ez_context)); return true; } @@ -224,7 +224,7 @@ struct EquivSimpleWorker if (!verbose) log(" failed.\n"); - ez.assume(ez.NOT(ez_context)); + ez->assume(ez->NOT(ez_context)); return false; } @@ -277,7 +277,7 @@ struct EquivSimplePass : public Pass { int success_counter = 0; int max_seq = 1; - log_header("Executing EQUIV_SIMPLE pass.\n"); + log_header(design, "Executing EQUIV_SIMPLE pass.\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { @@ -314,7 +314,7 @@ struct EquivSimplePass : public Pass { for (auto cell : module->selected_cells()) if (cell->type == "$equiv" && cell->getPort("\\A") != cell->getPort("\\B")) { - auto bit = sigmap(cell->getPort("\\Y").to_single_sigbit()); + auto bit = sigmap(cell->getPort("\\Y").as_bit()); auto bit_group = bit; if (!nogroup && bit_group.wire) bit_group.offset = 0; diff --git a/passes/equiv/equiv_status.cc b/passes/equiv/equiv_status.cc index 8ca1aacd5..7b9230b35 100644 --- a/passes/equiv/equiv_status.cc +++ b/passes/equiv/equiv_status.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -41,7 +41,7 @@ struct EquivStatusPass : public Pass { bool assert_mode = false; int unproven_count = 0; - log_header("Executing EQUIV_STATUS pass.\n"); + log_header(design, "Executing EQUIV_STATUS pass.\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { diff --git a/passes/equiv/equiv_struct.cc b/passes/equiv/equiv_struct.cc new file mode 100644 index 000000000..c4ced6a71 --- /dev/null +++ b/passes/equiv/equiv_struct.cc @@ -0,0 +1,367 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include "kernel/sigtools.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct EquivStructWorker +{ + Module *module; + SigMap sigmap; + SigMap equiv_bits; + bool mode_fwd; + bool mode_icells; + int merge_count; + + const pool<IdString> &fwonly_cells; + + struct merge_key_t + { + IdString type; + vector<pair<IdString, Const>> parameters; + vector<pair<IdString, int>> port_sizes; + vector<tuple<IdString, int, SigBit>> connections; + + bool operator==(const merge_key_t &other) const { + return type == other.type && connections == other.connections && + parameters == other.parameters && port_sizes == other.port_sizes; + } + + unsigned int hash() const { + unsigned int h = mkhash_init; + h = mkhash(h, mkhash(type)); + h = mkhash(h, mkhash(parameters)); + h = mkhash(h, mkhash(connections)); + return h; + } + }; + + dict<merge_key_t, pool<IdString>> merge_cache; + pool<merge_key_t> fwd_merge_cache, bwd_merge_cache; + + void merge_cell_pair(Cell *cell_a, Cell *cell_b) + { + SigMap merged_map; + merge_count++; + + SigSpec inputs_a, inputs_b; + vector<string> input_names; + + for (auto &port_a : cell_a->connections()) + { + SigSpec bits_a = sigmap(port_a.second); + SigSpec bits_b = sigmap(cell_b->getPort(port_a.first)); + + log_assert(GetSize(bits_a) == GetSize(bits_b)); + + if (!cell_a->output(port_a.first)) + for (int i = 0; i < GetSize(bits_a); i++) + if (bits_a[i] != bits_b[i]) { + inputs_a.append(bits_a[i]); + inputs_b.append(bits_b[i]); + input_names.push_back(GetSize(bits_a) == 1 ? port_a.first.str() : + stringf("%s[%d]", log_id(port_a.first), i)); + } + } + + for (int i = 0; i < GetSize(inputs_a); i++) { + SigBit bit_a = inputs_a[i], bit_b = inputs_b[i]; + SigBit bit_y = module->addWire(NEW_ID); + log(" New $equiv for input %s: A: %s, B: %s, Y: %s\n", + input_names[i].c_str(), log_signal(bit_a), log_signal(bit_b), log_signal(bit_y)); + module->addEquiv(NEW_ID, bit_a, bit_b, bit_y); + merged_map.add(bit_a, bit_y); + merged_map.add(bit_b, bit_y); + } + + std::vector<IdString> outport_names, inport_names; + + for (auto &port_a : cell_a->connections()) + if (cell_a->output(port_a.first)) + outport_names.push_back(port_a.first); + else + inport_names.push_back(port_a.first); + + for (auto &pn : inport_names) + cell_a->setPort(pn, merged_map(sigmap(cell_a->getPort(pn)))); + + for (auto &pn : outport_names) { + SigSpec sig_a = cell_a->getPort(pn); + SigSpec sig_b = cell_b->getPort(pn); + module->connect(sig_b, sig_a); + } + + auto merged_attr = cell_b->get_strpool_attribute("\\equiv_merged"); + merged_attr.insert(log_id(cell_b)); + cell_a->add_strpool_attribute("\\equiv_merged", merged_attr); + module->remove(cell_b); + } + + EquivStructWorker(Module *module, bool mode_fwd, bool mode_icells, const pool<IdString> &fwonly_cells, int iter_num) : + module(module), sigmap(module), equiv_bits(module), + mode_fwd(mode_fwd), mode_icells(mode_icells), merge_count(0), fwonly_cells(fwonly_cells) + { + log(" Starting iteration %d.\n", iter_num); + + pool<SigBit> equiv_inputs; + pool<IdString> cells; + + for (auto cell : module->selected_cells()) + if (cell->type == "$equiv") { + SigBit sig_a = sigmap(cell->getPort("\\A").as_bit()); + SigBit sig_b = sigmap(cell->getPort("\\B").as_bit()); + equiv_bits.add(sig_b, sig_a); + equiv_inputs.insert(sig_a); + equiv_inputs.insert(sig_b); + cells.insert(cell->name); + } else { + if (mode_icells || module->design->module(cell->type)) + cells.insert(cell->name); + } + + for (auto cell : module->selected_cells()) + if (cell->type == "$equiv") { + SigBit sig_a = sigmap(cell->getPort("\\A").as_bit()); + SigBit sig_b = sigmap(cell->getPort("\\B").as_bit()); + SigBit sig_y = sigmap(cell->getPort("\\Y").as_bit()); + if (sig_a == sig_b && equiv_inputs.count(sig_y)) { + log(" Purging redundant $equiv cell %s.\n", log_id(cell)); + module->connect(sig_y, sig_a); + module->remove(cell); + merge_count++; + } + } + + if (merge_count > 0) + return; + + for (auto cell_name : cells) + { + merge_key_t key; + vector<tuple<IdString, int, SigBit>> fwd_connections; + + Cell *cell = module->cell(cell_name); + key.type = cell->type; + + for (auto &it : cell->parameters) + key.parameters.push_back(it); + std::sort(key.parameters.begin(), key.parameters.end()); + + for (auto &it : cell->connections()) + key.port_sizes.push_back(make_pair(it.first, GetSize(it.second))); + std::sort(key.port_sizes.begin(), key.port_sizes.end()); + + for (auto &conn : cell->connections()) + { + if (cell->input(conn.first)) { + SigSpec sig = sigmap(conn.second); + for (int i = 0; i < GetSize(sig); i++) + fwd_connections.push_back(make_tuple(conn.first, i, sig[i])); + } + + if (cell->output(conn.first)) { + SigSpec sig = equiv_bits(conn.second); + for (int i = 0; i < GetSize(sig); i++) { + key.connections.clear(); + key.connections.push_back(make_tuple(conn.first, i, sig[i])); + + if (merge_cache.count(key)) + bwd_merge_cache.insert(key); + merge_cache[key].insert(cell_name); + } + } + } + + std::sort(fwd_connections.begin(), fwd_connections.end()); + key.connections.swap(fwd_connections); + + if (merge_cache.count(key)) + fwd_merge_cache.insert(key); + merge_cache[key].insert(cell_name); + } + + for (int phase = 0; phase < 2; phase++) + { + auto &queue = phase ? bwd_merge_cache : fwd_merge_cache; + + for (auto &key : queue) + { + const char *strategy = nullptr; + vector<Cell*> gold_cells, gate_cells, other_cells; + vector<pair<Cell*, Cell*>> cell_pairs; + IdString cells_type; + + for (auto cell_name : merge_cache[key]) { + Cell *c = module->cell(cell_name); + if (c != nullptr) { + string n = cell_name.str(); + cells_type = c->type; + if (GetSize(n) > 5 && n.substr(GetSize(n)-5) == "_gold") + gold_cells.push_back(c); + else if (GetSize(n) > 5 && n.substr(GetSize(n)-5) == "_gate") + gate_cells.push_back(c); + else + other_cells.push_back(c); + } + } + + if (phase && fwonly_cells.count(cells_type)) + continue; + + if (GetSize(gold_cells) > 1 || GetSize(gate_cells) > 1 || GetSize(other_cells) > 1) + { + strategy = "deduplicate"; + for (int i = 0; i+1 < GetSize(gold_cells); i += 2) + cell_pairs.push_back(make_pair(gold_cells[i], gold_cells[i+1])); + for (int i = 0; i+1 < GetSize(gate_cells); i += 2) + cell_pairs.push_back(make_pair(gate_cells[i], gate_cells[i+1])); + for (int i = 0; i+1 < GetSize(other_cells); i += 2) + cell_pairs.push_back(make_pair(other_cells[i], other_cells[i+1])); + goto run_strategy; + } + + if (GetSize(gold_cells) == 1 && GetSize(gate_cells) == 1) + { + strategy = "gold-gate-pairs"; + cell_pairs.push_back(make_pair(gold_cells[0], gate_cells[0])); + goto run_strategy; + } + + if (GetSize(gold_cells) == 1 && GetSize(other_cells) == 1) + { + strategy = "gold-guess"; + cell_pairs.push_back(make_pair(gold_cells[0], other_cells[0])); + goto run_strategy; + } + + if (GetSize(other_cells) == 1 && GetSize(gate_cells) == 1) + { + strategy = "gate-guess"; + cell_pairs.push_back(make_pair(other_cells[0], gate_cells[0])); + goto run_strategy; + } + + log_assert(GetSize(gold_cells) + GetSize(gate_cells) + GetSize(other_cells) < 2); + continue; + + run_strategy: + int total_group_size = GetSize(gold_cells) + GetSize(gate_cells) + GetSize(other_cells); + log(" %s merging %d %s cells (from group of %d) using strategy %s:\n", phase ? "Bwd" : "Fwd", + 2*GetSize(cell_pairs), log_id(cells_type), total_group_size, strategy); + for (auto it : cell_pairs) { + log(" Merging cells %s and %s.\n", log_id(it.first), log_id(it.second)); + merge_cell_pair(it.first, it.second); + } + } + + if (merge_count > 0) + return; + } + + log(" Nothing to merge.\n"); + } +}; + +struct EquivStructPass : public Pass { + EquivStructPass() : Pass("equiv_struct", "structural equivalence checking") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" equiv_struct [options] [selection]\n"); + log("\n"); + log("This command adds additional $equiv cells based on the assumption that the\n"); + log("gold and gate circuit are structurally equivalent. Note that this can introduce\n"); + log("bad $equiv cells in cases where the netlists are not structurally equivalent,\n"); + log("for example when analyzing circuits with cells with commutative inputs. This\n"); + log("command will also de-duplicate gates.\n"); + log("\n"); + log(" -fwd\n"); + log(" by default this command performans forward sweeps until nothing can\n"); + log(" be merged by forwards sweeps, then backward sweeps until forward\n"); + log(" sweeps are effective again. with this option set only forward sweeps\n"); + log(" are performed.\n"); + log("\n"); + log(" -fwonly <cell_type>\n"); + log(" add the specified cell type to the list of cell types that are only\n"); + log(" merged in forward sweeps and never in backward sweeps. $equiv is in\n"); + log(" this list automatically.\n"); + log("\n"); + log(" -icells\n"); + log(" by default, the internal RTL and gate cell types are ignored. add\n"); + log(" this option to also process those cell types with this command.\n"); + log("\n"); + log(" -maxiter <N>\n"); + log(" maximum number of iterations to run before aborting\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, Design *design) + { + pool<IdString> fwonly_cells({ "$equiv" }); + bool mode_icells = false; + bool mode_fwd = false; + int max_iter = -1; + + log_header(design, "Executing EQUIV_STRUCT pass.\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-fwd") { + mode_fwd = true; + continue; + } + if (args[argidx] == "-icells") { + mode_icells = true; + continue; + } + if (args[argidx] == "-fwonly" && argidx+1 < args.size()) { + fwonly_cells.insert(RTLIL::escape_id(args[++argidx])); + continue; + } + if (args[argidx] == "-maxiter" && argidx+1 < args.size()) { + max_iter = atoi(args[++argidx].c_str()); + continue; + } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) { + int module_merge_count = 0; + log("Running equiv_struct on module %s:\n", log_id(module)); + for (int iter = 0;; iter++) { + if (iter == max_iter) { + log(" Reached iteration limit of %d.\n", iter); + break; + } + EquivStructWorker worker(module, mode_fwd, mode_icells, fwonly_cells, iter+1); + if (worker.merge_count == 0) + break; + module_merge_count += worker.merge_count; + } + if (module_merge_count) + log(" Performed a total of %d merges in module %s.\n", module_merge_count, log_id(module)); + } + } +} EquivStructPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/fsm/fsm.cc b/passes/fsm/fsm.cc index e76be40c2..3b537ecd8 100644 --- a/passes/fsm/fsm.cc +++ b/passes/fsm/fsm.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -34,7 +34,7 @@ struct FsmPass : public Pass { log(" fsm [options] [selection]\n"); log("\n"); log("This pass calls all the other fsm_* passes in a useful order. This performs\n"); - log("FSM extraction and optimiziation. It also calls opt_clean as needed:\n"); + log("FSM extraction and optimization. It also calls opt_clean as needed:\n"); log("\n"); log(" fsm_detect unless got option -nodetect\n"); log(" fsm_extract\n"); @@ -59,7 +59,7 @@ struct FsmPass : public Pass { log(" -expand, -norecode, -export, -nomap\n"); log(" enable or disable passes as indicated above\n"); log("\n"); - log(" -encoding tye\n"); + log(" -encoding type\n"); log(" -fm_set_fsm_file file\n"); log(" -encfile file\n"); log(" passed through to fsm_recode pass\n"); @@ -76,7 +76,7 @@ struct FsmPass : public Pass { std::string encfile_opt; std::string encoding_opt; - log_header("Executing FSM pass (extract and optimize FSM).\n"); + log_header(design, "Executing FSM pass (extract and optimize FSM).\n"); log_push(); size_t argidx; @@ -145,5 +145,5 @@ struct FsmPass : public Pass { log_pop(); } } FsmPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/fsm/fsm_detect.cc b/passes/fsm/fsm_detect.cc index c89553c6b..5a240ba60 100644 --- a/passes/fsm/fsm_detect.cc +++ b/passes/fsm/fsm_detect.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -34,7 +34,7 @@ static SigSet<sig2driver_entry_t> sig2driver, sig2user; static std::set<RTLIL::Cell*> muxtree_cells; static SigPool sig_at_port; -static bool check_state_mux_tree(RTLIL::SigSpec old_sig, RTLIL::SigSpec sig, SigPool &recursion_monitor) +static bool check_state_mux_tree(RTLIL::SigSpec old_sig, RTLIL::SigSpec sig, pool<Cell*> &recursion_monitor) { if (sig_at_port.check_any(assign_map(sig))) return false; @@ -42,31 +42,39 @@ static bool check_state_mux_tree(RTLIL::SigSpec old_sig, RTLIL::SigSpec sig, Sig if (sig.is_fully_const() || old_sig == sig) return true; - if (recursion_monitor.check_any(sig)) { - log_warning("logic loop in mux tree at signal %s in module %s.\n", - log_signal(sig), RTLIL::id2cstr(module->name)); - return false; - } - - recursion_monitor.add(sig); - std::set<sig2driver_entry_t> cellport_list; sig2driver.find(sig, cellport_list); - for (auto &cellport : cellport_list) { + for (auto &cellport : cellport_list) + { if ((cellport.first->type != "$mux" && cellport.first->type != "$pmux") || cellport.second != "\\Y") return false; + + if (recursion_monitor.count(cellport.first)) { + log_warning("logic loop in mux tree at signal %s in module %s.\n", + log_signal(sig), RTLIL::id2cstr(module->name)); + return false; + } + + recursion_monitor.insert(cellport.first); + RTLIL::SigSpec sig_a = assign_map(cellport.first->getPort("\\A")); RTLIL::SigSpec sig_b = assign_map(cellport.first->getPort("\\B")); - if (!check_state_mux_tree(old_sig, sig_a, recursion_monitor)) + + if (!check_state_mux_tree(old_sig, sig_a, recursion_monitor)) { + recursion_monitor.erase(cellport.first); return false; + } + for (int i = 0; i < sig_b.size(); i += sig_a.size()) - if (!check_state_mux_tree(old_sig, sig_b.extract(i, sig_a.size()), recursion_monitor)) + if (!check_state_mux_tree(old_sig, sig_b.extract(i, sig_a.size()), recursion_monitor)) { + recursion_monitor.erase(cellport.first); return false; + } + + recursion_monitor.erase(cellport.first); muxtree_cells.insert(cellport.first); } - recursion_monitor.del(sig); - return true; } @@ -81,6 +89,8 @@ static bool check_state_users(RTLIL::SigSpec sig) RTLIL::Cell *cell = cellport.first; if (muxtree_cells.count(cell) > 0) continue; + if (cell->type == "$logic_not" && assign_map(cell->getPort("\\A")) == sig) + continue; if (cellport.second != "\\A" && cellport.second != "\\B") return false; if (!cell->hasPort("\\A") || !cell->hasPort("\\B") || !cell->hasPort("\\Y")) @@ -100,6 +110,8 @@ static bool check_state_users(RTLIL::SigSpec sig) static void detect_fsm(RTLIL::Wire *wire) { + if (wire->attributes.count("\\init") > 0) + return; if (wire->attributes.count("\\fsm_encoding") > 0 || wire->width <= 1) return; if (sig_at_port.check_any(assign_map(RTLIL::SigSpec(wire)))) @@ -111,7 +123,7 @@ static void detect_fsm(RTLIL::Wire *wire) if ((cellport.first->type != "$dff" && cellport.first->type != "$adff") || cellport.second != "\\Q") continue; muxtree_cells.clear(); - SigPool recursion_monitor; + pool<Cell*> recursion_monitor; RTLIL::SigSpec sig_q = assign_map(cellport.first->getPort("\\Q")); RTLIL::SigSpec sig_d = assign_map(cellport.first->getPort("\\D")); if (sig_q == RTLIL::SigSpec(wire) && check_state_mux_tree(sig_q, sig_d, recursion_monitor) && check_state_users(sig_q)) { @@ -142,7 +154,7 @@ struct FsmDetectPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing FSM_DETECT pass (finding FSMs in design).\n"); + log_header(design, "Executing FSM_DETECT pass (finding FSMs in design).\n"); extra_args(args, 1, design); CellTypes ct; @@ -191,5 +203,5 @@ struct FsmDetectPass : public Pass { muxtree_cells.clear(); } } FsmDetectPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/fsm/fsm_expand.cc b/passes/fsm/fsm_expand.cc index a261eb22b..3ded3f377 100644 --- a/passes/fsm/fsm_expand.cc +++ b/passes/fsm/fsm_expand.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -253,12 +253,12 @@ struct FsmExpandPass : public Pass { log("\n"); log("The fsm_extract pass is conservative about the cells that belong to a finite\n"); log("state machine. This pass can be used to merge additional auxiliary gates into\n"); - log("the finate state machine.\n"); + log("the finite state machine.\n"); log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing FSM_EXPAND pass (merging auxiliary logic into FSMs).\n"); + log_header(design, "Executing FSM_EXPAND pass (merging auxiliary logic into FSMs).\n"); extra_args(args, 1, design); for (auto &mod_it : design->modules_) { @@ -275,5 +275,5 @@ struct FsmExpandPass : public Pass { } } } FsmExpandPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/fsm/fsm_export.cc b/passes/fsm/fsm_export.cc index ad9270334..1cbfcfae8 100644 --- a/passes/fsm/fsm_export.cc +++ b/passes/fsm/fsm_export.cc @@ -3,11 +3,11 @@ * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> * Copyright (C) 2012 Martin Schmölzer <martin@schmoelzer.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -152,7 +152,7 @@ struct FsmExportPass : public Pass { bool flag_origenc = false; size_t argidx; - log_header("Executing FSM_EXPORT pass (exporting FSMs in KISS2 file format).\n"); + log_header(design, "Executing FSM_EXPORT pass (exporting FSMs in KISS2 file format).\n"); for (argidx = 1; argidx < args.size(); argidx++) { arg = args[argidx]; diff --git a/passes/fsm/fsm_extract.cc b/passes/fsm/fsm_extract.cc index 68667ef02..95cb498e3 100644 --- a/passes/fsm/fsm_extract.cc +++ b/passes/fsm/fsm_extract.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -56,6 +56,17 @@ static bool find_states(RTLIL::SigSpec sig, const RTLIL::SigSpec &dff_out, RTLIL std::set<sig2driver_entry_t> cellport_list; sig2driver.find(sig, cellport_list); + + if (GetSize(cellport_list) > 1) { + log(" found %d combined drivers for state signal %s.\n", GetSize(cellport_list), log_signal(sig)); + return false; + } + + if (GetSize(cellport_list) < 1) { + log(" found no driver for state signal %s.\n", log_signal(sig)); + return false; + } + for (auto &cellport : cellport_list) { RTLIL::Cell *cell = module->cells_.at(cellport.first); @@ -90,9 +101,11 @@ static bool find_states(RTLIL::SigSpec sig, const RTLIL::SigSpec &dff_out, RTLIL log(" found reset state: %s (guessed from mux tree)\n", log_signal(*reset_state)); } while (0); - if (ctrl.extract(sig_s).size() == 0) { - log(" found ctrl input: %s\n", log_signal(sig_s)); - ctrl.append(sig_s); + for (auto sig_s_bit : sig_s) { + if (ctrl.extract(sig_s_bit).empty()) { + log(" found ctrl input: %s\n", log_signal(sig_s_bit)); + ctrl.append(sig_s_bit); + } } if (!find_states(sig_aa, dff_out, ctrl, states)) @@ -241,7 +254,7 @@ static void extract_fsm(RTLIL::Wire *wire) { log("Extracting FSM `%s' from module `%s'.\n", wire->name.c_str(), module->name.c_str()); - // get input and output signals for state ff + // get input and output signals for state ff RTLIL::SigSpec dff_out = assign_map(RTLIL::SigSpec(wire)); RTLIL::SigSpec dff_in(RTLIL::State::Sm, wire->width); @@ -305,7 +318,9 @@ static void extract_fsm(RTLIL::Wire *wire) for (auto &cellport : cellport_list) { RTLIL::Cell *cell = module->cells_.at(cellport.first); RTLIL::SigSpec sig_a = assign_map(cell->getPort("\\A")); - RTLIL::SigSpec sig_b = assign_map(cell->getPort("\\B")); + RTLIL::SigSpec sig_b; + if (cell->hasPort("\\B")) + sig_b = assign_map(cell->getPort("\\B")); RTLIL::SigSpec sig_y = assign_map(cell->getPort("\\Y")); if (cellport.second == "\\A" && !sig_b.is_fully_const()) continue; @@ -401,7 +416,7 @@ struct FsmExtractPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing FSM_EXTRACT pass (extracting FSM from design).\n"); + log_header(design, "Executing FSM_EXTRACT pass (extracting FSM from design).\n"); extra_args(args, 1, design); CellTypes ct; @@ -458,5 +473,5 @@ struct FsmExtractPass : public Pass { sig2trigger.clear(); } } FsmExtractPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/fsm/fsm_info.cc b/passes/fsm/fsm_info.cc index 4a1f1d9a2..2cc1a7d53 100644 --- a/passes/fsm/fsm_info.cc +++ b/passes/fsm/fsm_info.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -43,7 +43,7 @@ struct FsmInfoPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing FSM_INFO pass (dumping all available information on FSM cells).\n"); + log_header(design, "Executing FSM_INFO pass (dumping all available information on FSM cells).\n"); extra_args(args, 1, design); for (auto &mod_it : design->modules_) @@ -58,5 +58,5 @@ struct FsmInfoPass : public Pass { } } } FsmInfoPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/fsm/fsm_map.cc b/passes/fsm/fsm_map.cc index 155801a3a..5b32ed599 100644 --- a/passes/fsm/fsm_map.cc +++ b/passes/fsm/fsm_map.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -335,7 +335,7 @@ struct FsmMapPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing FSM_MAP pass (mapping FSMs to basic logic).\n"); + log_header(design, "Executing FSM_MAP pass (mapping FSMs to basic logic).\n"); extra_args(args, 1, design); for (auto &mod_it : design->modules_) { @@ -350,5 +350,5 @@ struct FsmMapPass : public Pass { } } } FsmMapPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/fsm/fsm_opt.cc b/passes/fsm/fsm_opt.cc index 4b93d79f9..5b1da44fc 100644 --- a/passes/fsm/fsm_opt.cc +++ b/passes/fsm/fsm_opt.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -75,14 +75,14 @@ struct FsmOpt fsm_data.reset_state = old_to_new_state.at(fsm_data.reset_state); } } - + bool signal_is_unused(RTLIL::SigSpec sig) { - RTLIL::SigBit bit = sig.to_single_sigbit(); + RTLIL::SigBit bit = sig.as_bit(); if (bit.wire == NULL || bit.wire->attributes.count("\\unused_bits") == 0) return false; - + char *str = strdup(bit.wire->attributes["\\unused_bits"].decode_string().c_str()); for (char *tok = strtok(str, " "); tok != NULL; tok = strtok(NULL, " ")) { if (tok[0] && bit.offset == atoi(tok)) { @@ -336,7 +336,7 @@ struct FsmOptPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing FSM_OPT pass (simple optimizations of FSMs).\n"); + log_header(design, "Executing FSM_OPT pass (simple optimizations of FSMs).\n"); extra_args(args, 1, design); for (auto &mod_it : design->modules_) { @@ -347,5 +347,5 @@ struct FsmOptPass : public Pass { } } } FsmOptPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/fsm/fsm_recode.cc b/passes/fsm/fsm_recode.cc index 169968103..5102d8334 100644 --- a/passes/fsm/fsm_recode.cc +++ b/passes/fsm/fsm_recode.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -85,7 +85,7 @@ static void fsm_recode(RTLIL::Cell *cell, RTLIL::Module *module, FILE *fm_set_fs fsm_data.state_bits = fsm_data.state_table.size(); } else if (encoding == "binary") { - int new_num_state_bits = ceil(log2(fsm_data.state_table.size())); + int new_num_state_bits = ceil_log2(fsm_data.state_table.size()); if (fsm_data.state_bits == new_num_state_bits) { log(" existing encoding is already a packed binary encoding.\n"); return; @@ -93,7 +93,7 @@ static void fsm_recode(RTLIL::Cell *cell, RTLIL::Module *module, FILE *fm_set_fs fsm_data.state_bits = new_num_state_bits; } else log_error("FSM encoding `%s' is not supported!\n", encoding.c_str()); - + if (encfile) fprintf(encfile, ".fsm %s %s\n", log_id(module), RTLIL::unescape_id(cell->parameters["\\NAME"].decode_string()).c_str()); @@ -134,7 +134,7 @@ struct FsmRecodePass : public Pass { log("\n"); log("This pass reassign the state encodings for FSM cells. At the moment only\n"); log("one-hot encoding and binary encoding is supported.\n"); - + log(" -encoding <type>\n"); log(" specify the encoding scheme used for FSMs without the\n"); log(" 'fsm_encoding' attribute or with the attribute set to `auto'.\n"); @@ -157,7 +157,7 @@ struct FsmRecodePass : public Pass { FILE *encfile = NULL; std::string default_encoding; - log_header("Executing FSM_RECODE pass (re-assigning FSM state encoding).\n"); + log_header(design, "Executing FSM_RECODE pass (re-assigning FSM state encoding).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { std::string arg = args[argidx]; @@ -193,5 +193,5 @@ struct FsmRecodePass : public Pass { fclose(encfile); } } FsmRecodePass; - + PRIVATE_NAMESPACE_END diff --git a/passes/fsm/fsmdata.h b/passes/fsm/fsmdata.h index 5671d0006..68222769a 100644 --- a/passes/fsm/fsmdata.h +++ b/passes/fsm/fsmdata.h @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -39,7 +39,7 @@ struct FsmData int state_num_log2 = 0; for (int i = state_table.size(); i > 0; i = i >> 1) state_num_log2++; - state_num_log2 = std::max(state_num_log2, 1); + state_num_log2 = max(state_num_log2, 1); cell->parameters["\\STATE_BITS"] = RTLIL::Const(state_bits); cell->parameters["\\STATE_NUM"] = RTLIL::Const(state_table.size()); diff --git a/passes/hierarchy/Makefile.inc b/passes/hierarchy/Makefile.inc index 99aa1e116..1fb669c11 100644 --- a/passes/hierarchy/Makefile.inc +++ b/passes/hierarchy/Makefile.inc @@ -1,4 +1,5 @@ OBJS += passes/hierarchy/hierarchy.o +OBJS += passes/hierarchy/singleton.o OBJS += passes/hierarchy/submod.o diff --git a/passes/hierarchy/hierarchy.cc b/passes/hierarchy/hierarchy.cc index 58b796a62..94b93de5d 100644 --- a/passes/hierarchy/hierarchy.cc +++ b/passes/hierarchy/hierarchy.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -66,7 +66,7 @@ void generate(RTLIL::Design *design, const std::vector<std::string> &celltypes, for (auto &conn : i2.second->connections()) { if (conn.first[0] != '$') portnames.insert(conn.first); - portwidths[conn.first] = std::max(portwidths[conn.first], conn.second.size()); + portwidths[conn.first] = max(portwidths[conn.first], conn.second.size()); } for (auto ¶ : i2.second->parameters) parameters.insert(para.first); @@ -84,8 +84,8 @@ void generate(RTLIL::Design *design, const std::vector<std::string> &celltypes, for (auto &decl : portdecls) if (decl.index > 0) { - portwidths[decl.portname] = std::max(portwidths[decl.portname], 1); - portwidths[decl.portname] = std::max(portwidths[decl.portname], portwidths[stringf("$%d", decl.index)]); + portwidths[decl.portname] = max(portwidths[decl.portname], 1); + portwidths[decl.portname] = max(portwidths[decl.portname], portwidths[stringf("$%d", decl.index)]); log(" port %d: %s [%d:0] %s\n", decl.index, decl.input ? decl.output ? "inout" : "input" : "output", portwidths[decl.portname]-1, RTLIL::id2cstr(decl.portname)); if (indices.count(decl.index) > ports.size()) log_error("Port index (%d) exceeds number of found ports (%d).\n", decl.index, int(ports.size())); @@ -106,7 +106,7 @@ void generate(RTLIL::Design *design, const std::vector<std::string> &celltypes, log_assert(!indices.empty()); indices.erase(d.index); ports[d.index-1] = d; - portwidths[d.portname] = std::max(portwidths[d.portname], 1); + portwidths[d.portname] = max(portwidths[d.portname], 1); log(" port %d: %s [%d:0] %s\n", d.index, d.input ? d.output ? "inout" : "input" : "output", portwidths[d.portname]-1, RTLIL::id2cstr(d.portname)); goto found_matching_decl; } @@ -261,14 +261,14 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check return did_something; } -void hierarchy_worker(RTLIL::Design *design, std::set<RTLIL::Module*> &used, RTLIL::Module *mod, int indent) +void hierarchy_worker(RTLIL::Design *design, std::set<RTLIL::Module*, IdString::compare_ptr_by_name<Module>> &used, RTLIL::Module *mod, int indent) { if (used.count(mod) > 0) return; if (indent == 0) log("Top module: %s\n", mod->name.c_str()); - else + else if (!mod->get_bool_attribute("\\blackbox")) log("Used module: %*s%s\n", indent, "", mod->name.c_str()); used.insert(mod); @@ -285,9 +285,9 @@ void hierarchy_worker(RTLIL::Design *design, std::set<RTLIL::Module*> &used, RTL } } -void hierarchy(RTLIL::Design *design, RTLIL::Module *top, bool purge_lib, bool first_pass) +void hierarchy_clean(RTLIL::Design *design, RTLIL::Module *top, bool purge_lib) { - std::set<RTLIL::Module*> used; + std::set<RTLIL::Module*, IdString::compare_ptr_by_name<Module>> used; hierarchy_worker(design, used, top, 0); std::vector<RTLIL::Module*> del_modules; @@ -295,17 +295,17 @@ void hierarchy(RTLIL::Design *design, RTLIL::Module *top, bool purge_lib, bool f if (used.count(it.second) == 0) del_modules.push_back(it.second); + int del_counter = 0; for (auto mod : del_modules) { - if (first_pass && mod->name.substr(0, 9) == "$abstract") - continue; if (!purge_lib && mod->get_bool_attribute("\\blackbox")) continue; log("Removing unused module `%s'.\n", mod->name.c_str()); design->modules_.erase(mod->name); + del_counter++; delete mod; } - log("Removed %d unused modules.\n", GetSize(del_modules)); + log("Removed %d unused modules.\n", del_counter); } bool set_keep_assert(std::map<RTLIL::Module*, bool> &cache, RTLIL::Module *mod) @@ -313,12 +313,23 @@ bool set_keep_assert(std::map<RTLIL::Module*, bool> &cache, RTLIL::Module *mod) if (cache.count(mod) == 0) for (auto c : mod->cells()) { RTLIL::Module *m = mod->design->module(c->type); - if ((m != nullptr && set_keep_assert(cache, m)) || c->type == "$assert") + if ((m != nullptr && set_keep_assert(cache, m)) || c->type.in("$assert", "$assume")) return cache[mod] = true; } return cache[mod]; } +int find_top_mod_score(Design *design, Module *module, dict<Module*, int> &db) +{ + if (db.count(module) == 0) { + db[module] = 0; + for (auto cell : module->cells()) + if (design->module(cell->type)) + db[module] = max(db[module], find_top_mod_score(design, design->module(cell->type), db) + 1); + } + return db.at(module); +} + struct HierarchyPass : public Pass { HierarchyPass() : Pass("hierarchy", "check, expand and clean up design hierarchy") { } virtual void help() @@ -339,7 +350,7 @@ struct HierarchyPass : public Pass { log("\n"); log(" -purge_lib\n"); log(" by default the hierarchy command will not remove library (blackbox)\n"); - log(" module. use this options to also remove unused blackbox modules.\n"); + log(" modules. use this option to also remove unused blackbox modules.\n"); log("\n"); log(" -libdir <directory>\n"); log(" search for files named <module_name>.v in the specified directory\n"); @@ -363,6 +374,9 @@ struct HierarchyPass : public Pass { log(" specified top module. otherwise a module with the 'top' attribute set\n"); log(" will implicitly be used as top module, if such a module exists.\n"); log("\n"); + log(" -auto-top\n"); + log(" automatically determine the top of the design hierarchy and mark it.\n"); + log("\n"); log("In -generate mode this pass generates blackbox modules for the given cell\n"); log("types (wildcards supported). For this the design is searched for cells that\n"); log("match the given types and then the given port declarations are used to\n"); @@ -372,7 +386,7 @@ struct HierarchyPass : public Pass { log("\n"); log("Input ports are specified with the 'i' prefix, output ports with the 'o'\n"); log("prefix and inout ports with the 'io' prefix. The optional <num> specifies\n"); - log("the position of the port in the parameter list (needed when instanciated\n"); + log("the position of the port in the parameter list (needed when instantiated\n"); log("using positional arguments). When <num> is not specified, the <portname> can\n"); log("also contain wildcard characters.\n"); log("\n"); @@ -382,13 +396,14 @@ struct HierarchyPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing HIERARCHY pass (managing design hierarchy).\n"); + log_header(design, "Executing HIERARCHY pass (managing design hierarchy).\n"); bool flag_check = false; bool purge_lib = false; RTLIL::Module *top_mod = NULL; std::vector<std::string> libdirs; + bool auto_top_mode = false; bool generate_mode = false; bool keep_positionals = false; bool nokeep_asserts = false; @@ -470,6 +485,10 @@ struct HierarchyPass : public Pass { log_cmd_error("Module `%s' not found!\n", args[argidx].c_str()); continue; } + if (args[argidx] == "-auto-top") { + auto_top_mode = true; + continue; + } break; } extra_args(args, argidx, design, false); @@ -481,35 +500,47 @@ struct HierarchyPass : public Pass { log_push(); - if (top_mod == NULL) + if (top_mod == nullptr) for (auto &mod_it : design->modules_) if (mod_it.second->get_bool_attribute("\\top")) top_mod = mod_it.second; - if (top_mod != NULL) - hierarchy(design, top_mod, purge_lib, true); + if (top_mod == nullptr && auto_top_mode) { + log_header(design, "Finding top of design hierarchy..\n"); + dict<Module*, int> db; + for (Module *mod : design->selected_modules()) { + int score = find_top_mod_score(design, mod, db); + log("root of %3d design levels: %-20s\n", score, log_id(mod)); + if (!top_mod || score > db[top_mod]) + top_mod = mod; + } + if (top_mod != nullptr) + log("Automatically selected %s as design top module.\n", log_id(top_mod)); + } bool did_something = true; - bool did_something_once = false; - while (did_something) { + while (did_something) + { did_something = false; - std::vector<RTLIL::IdString> modnames; - modnames.reserve(design->modules_.size()); - for (auto &mod_it : design->modules_) - modnames.push_back(mod_it.first); - for (auto &modname : modnames) { - if (design->modules_.count(modname) == 0) - continue; - if (expand_module(design, design->modules_[modname], flag_check, libdirs)) + + std::set<RTLIL::Module*, IdString::compare_ptr_by_name<Module>> used_modules; + if (top_mod != NULL) { + log_header(design, "Analyzing design hierarchy..\n"); + hierarchy_worker(design, used_modules, top_mod, 0); + } else { + for (auto mod : design->modules()) + used_modules.insert(mod); + } + + for (auto module : used_modules) { + if (expand_module(design, module, flag_check, libdirs)) did_something = true; } - if (did_something) - did_something_once = true; } - if (top_mod != NULL && did_something_once) { - log_header("Re-running hierarchy analysis..\n"); - hierarchy(design, top_mod, purge_lib, false); + if (top_mod != NULL) { + log_header(design, "Analyzing design hierarchy..\n"); + hierarchy_clean(design, top_mod, purge_lib); } if (top_mod != NULL) { @@ -580,5 +611,5 @@ struct HierarchyPass : public Pass { log_pop(); } } HierarchyPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/hierarchy/singleton.cc b/passes/hierarchy/singleton.cc new file mode 100644 index 000000000..03c365fb5 --- /dev/null +++ b/passes/hierarchy/singleton.cc @@ -0,0 +1,101 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct SingletonPass : public Pass { + SingletonPass() : Pass("singleton", "create singleton modules") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" singleton [selection]\n"); + log("\n"); + log("By default, a module that is instantiated by several other modules is only\n"); + log("kept once in the design. This preserves the original modularity of the design\n"); + log("and reduces the overall size of the design in memory. But it prevents certain\n"); + log("optimizations and other operations on the design. This pass creates singleton\n"); + log("modules for all selected cells. The created modules are marked with the\n"); + log("'singleton' attribute.\n"); + log("\n"); + log("This commands only operates on modules that by themself have the 'singleton'\n"); + log("attribute set (the 'top' module is a singleton implicitly).\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + log_header(design, "Executing SINGLETON pass (creating singleton modules).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + // if (args[argidx] == "-check") { + // flag_check = true; + // continue; + // } + } + extra_args(args, argidx, design); + + bool did_something = true; + int singleton_cnt = 0; + + while (did_something) + { + did_something = false; + + for (auto module : design->selected_modules()) + { + if (!module->get_bool_attribute("\\singleton") && !module->get_bool_attribute("\\top")) + continue; + + for (auto cell : module->selected_cells()) + { + auto tmod = design->module(cell->type); + + if (tmod == nullptr) + continue; + + if (tmod->get_bool_attribute("\\blackbox")) + continue; + + if (tmod->get_bool_attribute("\\singleton")) + continue; + + cell->type = module->name.str() + "." + log_id(cell->name); + log("Creating singleton '%s'.\n", log_id(cell->type)); + + auto smod = tmod->clone(); + smod->name = cell->type; + smod->set_bool_attribute("\\singleton"); + design->add(smod); + + did_something = true; + singleton_cnt++; + } + } + } + + log("Created %d singleton modules.\n", singleton_cnt); + } +} SingletonPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/hierarchy/submod.cc b/passes/hierarchy/submod.cc index 8d4012c53..9f312f82d 100644 --- a/passes/hierarchy/submod.cc +++ b/passes/hierarchy/submod.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -32,6 +32,8 @@ struct SubmodWorker CellTypes ct; RTLIL::Design *design; RTLIL::Module *module; + + bool copy_mode; std::string opt_name; struct SubModule @@ -177,21 +179,25 @@ struct SubmodWorker bit.wire = wire_flags[bit.wire].new_wire; } log(" cell %s (%s)\n", new_cell->name.c_str(), new_cell->type.c_str()); - module->remove(cell); + if (!copy_mode) + module->remove(cell); } submod.cells.clear(); - RTLIL::Cell *new_cell = module->addCell(submod.full_name, submod.full_name); - for (auto &it : wire_flags) - { - RTLIL::Wire *old_wire = it.first; - RTLIL::Wire *new_wire = it.second.new_wire; - if (new_wire->port_id > 0) - new_cell->setPort(new_wire->name, RTLIL::SigSpec(old_wire)); + if (!copy_mode) { + RTLIL::Cell *new_cell = module->addCell(submod.full_name, submod.full_name); + for (auto &it : wire_flags) + { + RTLIL::Wire *old_wire = it.first; + RTLIL::Wire *new_wire = it.second.new_wire; + if (new_wire->port_id > 0) + new_cell->setPort(new_wire->name, RTLIL::SigSpec(old_wire)); + } } } - SubmodWorker(RTLIL::Design *design, RTLIL::Module *module, std::string opt_name = std::string()) : design(design), module(module), opt_name(opt_name) + SubmodWorker(RTLIL::Design *design, RTLIL::Module *module, bool copy_mode = false, std::string opt_name = std::string()) : + design(design), module(module), copy_mode(copy_mode), opt_name(opt_name) { if (!design->selected_whole_module(module->name) && opt_name.empty()) return; @@ -266,7 +272,7 @@ struct SubmodPass : public Pass { { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" submod [selection]\n"); + log(" submod [-copy] [selection]\n"); log("\n"); log("This pass identifies all cells with the 'submod' attribute and moves them to\n"); log("a newly created module. The value of the attribute is used as name for the\n"); @@ -279,19 +285,24 @@ struct SubmodPass : public Pass { log("or memories.\n"); log("\n"); log("\n"); - log(" submod -name <name> [selection]\n"); + log(" submod -name <name> [-copy] [selection]\n"); log("\n"); log("As above, but don't use the 'submod' attribute but instead use the selection.\n"); log("Only objects from one module might be selected. The value of the -name option\n"); log("is used as the value of the 'submod' attribute above.\n"); log("\n"); + log("By default the cells are 'moved' from the source module and the source module\n"); + log("will use an instance of the new module after this command is finished. Call\n"); + log("with -copy to not modify the source module.\n"); + log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing SUBMOD pass (moving cells to submodules as requested).\n"); + log_header(design, "Executing SUBMOD pass (moving cells to submodules as requested).\n"); log_push(); std::string opt_name; + bool copy_mode = false; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { @@ -299,6 +310,10 @@ struct SubmodPass : public Pass { opt_name = args[++argidx]; continue; } + if (args[argidx] == "-copy") { + copy_mode = true; + continue; + } break; } extra_args(args, argidx, design); @@ -306,7 +321,7 @@ struct SubmodPass : public Pass { if (opt_name.empty()) { Pass::call(design, "opt_clean"); - log_header("Continuing SUBMOD pass.\n"); + log_header(design, "Continuing SUBMOD pass.\n"); std::set<RTLIL::IdString> handled_modules; @@ -319,7 +334,7 @@ struct SubmodPass : public Pass { queued_modules.push_back(mod_it.first); for (auto &modname : queued_modules) if (design->modules_.count(modname) != 0) { - SubmodWorker worker(design, design->modules_[modname]); + SubmodWorker worker(design, design->modules_[modname], copy_mode); handled_modules.insert(modname); did_something = true; } @@ -341,13 +356,13 @@ struct SubmodPass : public Pass { log("Nothing selected -> do nothing.\n"); else { Pass::call_on_module(design, module, "opt_clean"); - log_header("Continuing SUBMOD pass.\n"); - SubmodWorker worker(design, module, opt_name); + log_header(design, "Continuing SUBMOD pass.\n"); + SubmodWorker worker(design, module, copy_mode, opt_name); } } log_pop(); } } SubmodPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/memory/memory.cc b/passes/memory/memory.cc index 866efae77..e3c627607 100644 --- a/passes/memory/memory.cc +++ b/passes/memory/memory.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -31,11 +31,11 @@ struct MemoryPass : public Pass { { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" memory [-nomap] [-bram <bram_rules>] [selection]\n"); + log(" memory [-nomap] [-nordff] [-bram <bram_rules>] [selection]\n"); log("\n"); log("This pass calls all the other memory_* passes in a useful order:\n"); log("\n"); - log(" memory_dff\n"); + log(" memory_dff [-nordff]\n"); log(" opt_clean\n"); log(" memory_share\n"); log(" opt_clean\n"); @@ -50,9 +50,10 @@ struct MemoryPass : public Pass { virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { bool flag_nomap = false; + bool flag_nordff = false; string memory_bram_opts; - log_header("Executing MEMORY pass.\n"); + log_header(design, "Executing MEMORY pass.\n"); log_push(); size_t argidx; @@ -61,6 +62,10 @@ struct MemoryPass : public Pass { flag_nomap = true; continue; } + if (args[argidx] == "-nordff") { + flag_nordff = true; + continue; + } if (argidx+1 < args.size() && args[argidx] == "-bram") { memory_bram_opts += " -rules " + args[++argidx]; continue; @@ -69,7 +74,7 @@ struct MemoryPass : public Pass { } extra_args(args, argidx, design); - Pass::call(design, "memory_dff"); + Pass::call(design, flag_nordff ? "memory_dff -nordff" : "memory_dff"); Pass::call(design, "opt_clean"); Pass::call(design, "memory_share"); Pass::call(design, "opt_clean"); @@ -84,5 +89,5 @@ struct MemoryPass : public Pass { log_pop(); } } MemoryPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/memory/memory_bram.cc b/passes/memory/memory_bram.cc index 8f4214027..7b5dd08ab 100644 --- a/passes/memory/memory_bram.cc +++ b/passes/memory/memory_bram.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -32,6 +32,7 @@ struct rules_t SigSpec sig_addr, sig_data, sig_en; bool effective_clkpol; bool make_transp; + bool make_outreg; int mapped_port; }; @@ -85,6 +86,7 @@ struct rules_t pi.clkpol = clkpol[i]; pi.mapped_port = -1; pi.make_transp = false; + pi.make_outreg = false; pi.effective_clkpol = false; portinfos.push_back(pi); } @@ -110,15 +112,15 @@ struct rules_t if (ports[i] != other.ports[i]) log_error("Bram %s variants %d and %d have different number of %c-ports.\n", log_id(name), variant, other.variant, 'A'+i); if (wrmode[i] != other.wrmode[i]) - variant_params[stringf("\\CFG_WRMODE_%c", 'A' + i)] = wrmode[1]; + variant_params[stringf("\\CFG_WRMODE_%c", 'A' + i)] = wrmode[i]; if (enable[i] != other.enable[i]) - variant_params[stringf("\\CFG_ENABLE_%c", 'A' + i)] = enable[1]; + variant_params[stringf("\\CFG_ENABLE_%c", 'A' + i)] = enable[i]; if (transp[i] != other.transp[i]) - variant_params[stringf("\\CFG_TRANSP_%c", 'A' + i)] = transp[1]; + variant_params[stringf("\\CFG_TRANSP_%c", 'A' + i)] = transp[i]; if (clocks[i] != other.clocks[i]) - variant_params[stringf("\\CFG_CLOCKS_%c", 'A' + i)] = clocks[1]; + variant_params[stringf("\\CFG_CLOCKS_%c", 'A' + i)] = clocks[i]; if (clkpol[i] != other.clkpol[i]) - variant_params[stringf("\\CFG_CLKPOL_%c", 'A' + i)] = clkpol[1]; + variant_params[stringf("\\CFG_CLKPOL_%c", 'A' + i)] = clkpol[i]; } } }; @@ -126,7 +128,7 @@ struct rules_t struct match_t { IdString name; dict<string, int> min_limits, max_limits; - bool or_next_if_better, make_transp; + bool or_next_if_better, make_transp, make_outreg; char shuffle_enable; }; @@ -277,6 +279,7 @@ struct rules_t data.name = RTLIL::escape_id(tokens[1]); data.or_next_if_better = false; data.make_transp = false; + data.make_outreg = false; data.shuffle_enable = 0; while (next_line()) @@ -309,6 +312,12 @@ struct rules_t continue; } + if (GetSize(tokens) == 1 && tokens[0] == "make_outreg") { + data.make_transp = true; + data.make_outreg = true; + continue; + } + if (GetSize(tokens) == 1 && tokens[0] == "or_next_if_better") { data.or_next_if_better = true; continue; @@ -320,9 +329,7 @@ struct rules_t void parse(string filename) { - if (filename.substr(0, 2) == "+/") - filename = proc_share_dirname() + filename.substr(1); - + rewrite_filename(filename); infile.open(filename); linecount = 0; @@ -380,9 +387,9 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram, if (pi.clkpol > 1) clkpol_wr_ports.insert(pi.clkpol); } - clocks_max = std::max(clocks_max, pi.clocks); - clkpol_max = std::max(clkpol_max, pi.clkpol); - transp_max = std::max(transp_max, pi.transp); + clocks_max = max(clocks_max, pi.clocks); + clkpol_max = max(clkpol_max, pi.clkpol); + transp_max = max(transp_max, pi.transp); } log(" Mapping to bram type %s (variant %d):\n", log_id(bram.name), bram.variant); @@ -393,6 +400,16 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram, int mem_width = cell->getParam("\\WIDTH").as_int(); // int mem_offset = cell->getParam("\\OFFSET").as_int(); + bool cell_init = !SigSpec(cell->getParam("\\INIT")).is_fully_undef(); + vector<Const> initdata; + + if (cell_init) { + Const initparam = cell->getParam("\\INIT"); + initdata.reserve(mem_size); + for (int i=0; i < mem_size; i++) + initdata.push_back(initparam.extract(mem_width*i, mem_width, State::Sx)); + } + int wr_ports = cell->getParam("\\WR_PORTS").as_int(); auto wr_clken = SigSpec(cell->getParam("\\WR_CLK_ENABLE")); auto wr_clkpol = SigSpec(cell->getParam("\\WR_CLK_POLARITY")); @@ -412,11 +429,12 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram, rd_clkpol.extend_u0(rd_ports); rd_transp.extend_u0(rd_ports); + SigSpec rd_en = cell->getPort("\\RD_EN"); SigSpec rd_clk = cell->getPort("\\RD_CLK"); SigSpec rd_data = cell->getPort("\\RD_DATA"); SigSpec rd_addr = cell->getPort("\\RD_ADDR"); - if (match.shuffle_enable && bram.dbits >= portinfos.at(match.shuffle_enable - 'A').enable*2 && portinfos.at(match.shuffle_enable - 'A').enable > 0) + if (match.shuffle_enable && bram.dbits >= portinfos.at(match.shuffle_enable - 'A').enable*2 && portinfos.at(match.shuffle_enable - 'A').enable > 0 && wr_ports > 0) { 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); @@ -588,7 +606,7 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram, mapped_wr_port:; } - // houskeeping stuff for growing more read ports and restarting read port assignments + // housekeeping stuff for growing more read ports and restarting read port assignments int grow_read_ports_cursor = -1; bool try_growing_more_read_ports = false; @@ -656,6 +674,10 @@ grow_read_ports:; if (clken) { if (pi.clocks == 0) { + if (match.make_outreg) { + pi.make_outreg = true; + goto skip_bram_rport_clkcheck; + } log(" Bram port %c%d.%d has incompatible clock type.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1); goto skip_bram_rport; } @@ -667,12 +689,17 @@ 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 (rd_en[cell_port_i] != 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); + goto skip_bram_rport; + } + skip_bram_rport_clkcheck: if (read_transp.count(pi.transp) && read_transp.at(pi.transp) != transp) { if (match.make_transp && wr_ports <= 1) { pi.make_transp = true; enable_make_transp = true; } else { - log(" Bram port %c%d.%d has incompatible read transparancy.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1); + log(" Bram port %c%d.%d has incompatible read transparency.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1); goto skip_bram_rport; } } @@ -691,6 +718,7 @@ grow_read_ports:; clock_polarities[pi.clkpol] = clkdom.second; read_transp[pi.transp] = transp; pi.sig_clock = clkdom.first; + pi.sig_en = rd_en[cell_port_i]; pi.effective_clkpol = clkdom.second; } @@ -789,6 +817,22 @@ grow_read_ports:; for (auto &vp : variant_params) c->setParam(vp.first, vp.second); + if (cell_init) { + int init_offset = grid_a*(1 << bram.abits); + 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 j = 0; j < bram.dbits; j++) + if (init_offset+i < GetSize(initdata) && init_shift+j < GetSize(initdata[init_offset+i])) + initparam[i*bram.dbits+j] = initdata[init_offset+i][init_shift+j]; + else + initparam[i*bram.dbits+j] = padding; + } + c->setParam("\\INIT", initparam); + } + for (auto &pi : portinfos) { if (pi.dupidx != dupidx) @@ -815,8 +859,11 @@ grow_read_ports:; if (pi.enable) { SigSpec sig_en = pi.sig_en; - sig_en.extend_u0((grid_d+1) * pi.enable); - sig_en = sig_en.extract(grid_d * pi.enable, pi.enable); + + 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 (!addr_ok.empty()) sig_en = module->Mux(NEW_ID, SigSpec(0, GetSize(sig_en)), sig_en, addr_ok); @@ -846,6 +893,14 @@ grow_read_ports:; SigSpec bram_dout = module->addWire(NEW_ID, bram.dbits); c->setPort(stringf("\\%sDATA", pf), bram_dout); + 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; + } + if (pi.make_transp) { log(" Adding extra logic for transparent port %c%d.%d.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1); @@ -871,7 +926,7 @@ grow_read_ports:; } SigSpec addr_ok_q = addr_ok; - if (pi.clocks && !addr_ok.empty()) { + if ((pi.clocks || pi.make_outreg) && !addr_ok.empty()) { addr_ok_q = module->addWire(NEW_ID); module->addDff(NEW_ID, pi.sig_clock, addr_ok, addr_ok_q, pi.effective_clkpol); } @@ -905,6 +960,8 @@ void handle_cell(Cell *cell, const rules_t &rules) { log("Processing %s.%s:\n", log_id(cell->module), log_id(cell)); + bool cell_init = !SigSpec(cell->getParam("\\INIT")).is_fully_undef(); + dict<string, int> match_properties; match_properties["words"] = cell->getParam("\\SIZE").as_int(); match_properties["abits"] = cell->getParam("\\ABITS").as_int(); @@ -920,7 +977,7 @@ void handle_cell(Cell *cell, const rules_t &rules) log("\n"); pool<pair<IdString, int>> failed_brams; - dict<pair<int, int>, std::tuple<int, int, int>> best_rule_cache; + dict<pair<int, int>, tuple<int, int, int>> best_rule_cache; for (int i = 0; i < GetSize(rules.matches); i++) { @@ -975,6 +1032,12 @@ void handle_cell(Cell *cell, const rules_t &rules) log(" Metrics for %s: awaste=%d dwaste=%d bwaste=%d waste=%d efficiency=%d\n", log_id(match.name), awaste, dwaste, bwaste, waste, efficiency); + if (cell_init && bram.init == 0) { + log(" Rule #%d for bram type %s (variant %d) rejected: cannot be initialized.\n", + i+1, log_id(bram.name), bram.variant); + goto next_match_rule; + } + for (auto it : match.min_limits) { if (it.first == "waste" || it.first == "dups" || it.first == "acells" || it.first == "dcells" || it.first == "cells") continue; @@ -987,6 +1050,7 @@ void handle_cell(Cell *cell, const rules_t &rules) i+1, log_id(bram.name), bram.variant, it.first.c_str(), it.second); goto next_match_rule; } + for (auto it : match.max_limits) { if (it.first == "acells" || it.first == "dcells" || it.first == "cells") continue; @@ -1014,10 +1078,7 @@ void handle_cell(Cell *cell, const rules_t &rules) } log(" Storing for later selection.\n"); - best_rule_cache[pair<int, int>(i, vi)] = std::tuple<int, int, int>(match_properties["efficiency"], -match_properties["cells"], -match_properties["acells"]); - - if (or_next_if_better) - goto next_match_rule; + best_rule_cache[pair<int, int>(i, vi)] = tuple<int, int, int>(match_properties["efficiency"], -match_properties["cells"], -match_properties["acells"]); next_match_rule: if (or_next_if_better || best_rule_cache.empty()) @@ -1070,14 +1131,14 @@ struct MemoryBramPass : public Pass { log("rules. A block ram description looks like this:\n"); log("\n"); log(" bram RAMB1024X32 # name of BRAM cell\n"); - // log(" init 1 # set to '1' if BRAM can be initialized\n"); + log(" init 1 # set to '1' if BRAM can be initialized\n"); log(" abits 10 # number of address bits\n"); log(" dbits 32 # number of data bits\n"); log(" groups 2 # number of port groups\n"); log(" ports 1 1 # number of ports in each group\n"); log(" wrmode 1 0 # set to '1' if this groups is write ports\n"); - log(" enable 4 0 # number of enable bits (for write ports)\n"); - log(" transp 0 2 # transparatent (for read ports)\n"); + log(" enable 4 1 # number of enable bits\n"); + log(" transp 0 2 # transparent (for read ports)\n"); log(" clocks 1 2 # clock configuration\n"); log(" clkpol 2 2 # clock polarity configuration\n"); log(" endbram\n"); @@ -1095,7 +1156,7 @@ struct MemoryBramPass : public Pass { log("greater than 1 share the same configuration bit.\n"); log("\n"); log("Using the same bram name in different bram blocks will create different variants\n"); - log("of the bram. Verilog configration parameters for the bram are created as needed.\n"); + log("of the bram. Verilog configuration parameters for the bram are created as needed.\n"); log("\n"); log("It is also possible to create variants by repeating statements in the bram block\n"); log("and appending '@<label>' to the individual statements.\n"); @@ -1128,7 +1189,7 @@ struct MemoryBramPass : public Pass { log(" dcells ....... number of cells in 'data-direction'\n"); log(" cells ........ total number of cells (acells*dcells*dups)\n"); log("\n"); - log("The interface for the created bram instances is dervived from the bram\n"); + log("The interface for the created bram instances is derived from the bram\n"); log("description. Use 'techmap' to convert the created bram instances into\n"); log("instances of the actual bram cells of your target architecture.\n"); log("\n"); @@ -1139,6 +1200,9 @@ struct MemoryBramPass : public Pass { log("A match containing the command 'make_transp' will add external circuitry\n"); log("to simulate 'transparent read', if necessary.\n"); log("\n"); + log("A match containing the command 'make_outreg' will add external flip-flops\n"); + log("to implement synchronous read ports, if necessary.\n"); + log("\n"); log("A match containing the command 'shuffle_enable A' will re-organize\n"); log("the data bits to accommodate the enable pattern of port A.\n"); log("\n"); @@ -1147,7 +1211,7 @@ struct MemoryBramPass : public Pass { { rules_t rules; - log_header("Executing MEMORY_BRAM pass (mapping $mem cells to block memories).\n"); + log_header(design, "Executing MEMORY_BRAM pass (mapping $mem cells to block memories).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { diff --git a/passes/memory/memory_collect.cc b/passes/memory/memory_collect.cc index ccc196202..e068ef905 100644 --- a/passes/memory/memory_collect.cc +++ b/passes/memory/memory_collect.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -17,16 +17,13 @@ * */ -#include "kernel/register.h" -#include "kernel/log.h" -#include <sstream> -#include <algorithm> -#include <stdlib.h> +#include "kernel/yosys.h" +#include "kernel/sigtools.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -bool memcells_cmp(RTLIL::Cell *a, RTLIL::Cell *b) +bool memcells_cmp(Cell *a, Cell *b) { if (a->type == "$memrd" && b->type == "$memrd") return a->name < b->name; @@ -35,108 +32,152 @@ bool memcells_cmp(RTLIL::Cell *a, RTLIL::Cell *b) return a->parameters.at("\\PRIORITY").as_int() < b->parameters.at("\\PRIORITY").as_int(); } -void handle_memory(RTLIL::Module *module, RTLIL::Memory *memory) +Cell *handle_memory(Module *module, RTLIL::Memory *memory) { - log("Collecting $memrd and $memwr for memory `%s' in module `%s':\n", + log("Collecting $memrd, $memwr and $meminit for memory `%s' in module `%s':\n", memory->name.c_str(), module->name.c_str()); int addr_bits = 0; - while ((1 << addr_bits) < memory->size) - addr_bits++; + + Const init_data(State::Sx, memory->size * memory->width); + SigMap sigmap(module); int wr_ports = 0; - RTLIL::SigSpec sig_wr_clk; - RTLIL::SigSpec sig_wr_clk_enable; - RTLIL::SigSpec sig_wr_clk_polarity; - RTLIL::SigSpec sig_wr_addr; - RTLIL::SigSpec sig_wr_data; - RTLIL::SigSpec sig_wr_en; + SigSpec sig_wr_clk; + SigSpec sig_wr_clk_enable; + SigSpec sig_wr_clk_polarity; + SigSpec sig_wr_addr; + SigSpec sig_wr_data; + SigSpec sig_wr_en; int rd_ports = 0; - RTLIL::SigSpec sig_rd_clk; - RTLIL::SigSpec sig_rd_clk_enable; - RTLIL::SigSpec sig_rd_clk_polarity; - RTLIL::SigSpec sig_rd_transparent; - RTLIL::SigSpec sig_rd_addr; - RTLIL::SigSpec sig_rd_data; + SigSpec sig_rd_clk; + SigSpec sig_rd_clk_enable; + SigSpec sig_rd_clk_polarity; + SigSpec sig_rd_transparent; + SigSpec sig_rd_addr; + SigSpec sig_rd_data; + SigSpec sig_rd_en; - std::vector<RTLIL::Cell*> del_cells; - std::vector<RTLIL::Cell*> memcells; + std::vector<Cell*> memcells; for (auto &cell_it : module->cells_) { - RTLIL::Cell *cell = cell_it.second; - if ((cell->type == "$memwr" || cell->type == "$memrd") && memory->name == cell->parameters["\\MEMID"].decode_string()) + Cell *cell = cell_it.second; + if (cell->type.in("$memrd", "$memwr", "$meminit") && memory->name == cell->parameters["\\MEMID"].decode_string()) { + addr_bits = max(addr_bits, cell->getParam("\\ABITS").as_int()); memcells.push_back(cell); + } + } + + if (memcells.empty()) { + log(" no cells found. removing memory.\n"); + return nullptr; } std::sort(memcells.begin(), memcells.end(), memcells_cmp); for (auto cell : memcells) { - if (cell->type == "$memwr" && memory->name == cell->parameters["\\MEMID"].decode_string()) + log(" %s (%s)\n", log_id(cell), log_id(cell->type)); + + if (cell->type == "$meminit") { - wr_ports++; - del_cells.push_back(cell); - - RTLIL::SigSpec clk = cell->getPort("\\CLK"); - RTLIL::SigSpec clk_enable = RTLIL::SigSpec(cell->parameters["\\CLK_ENABLE"]); - RTLIL::SigSpec clk_polarity = RTLIL::SigSpec(cell->parameters["\\CLK_POLARITY"]); - RTLIL::SigSpec addr = cell->getPort("\\ADDR"); - RTLIL::SigSpec data = cell->getPort("\\DATA"); - RTLIL::SigSpec en = cell->getPort("\\EN"); - - clk.extend_u0(1, false); - clk_enable.extend_u0(1, false); - clk_polarity.extend_u0(1, false); - addr.extend_u0(addr_bits, false); - data.extend_u0(memory->width, false); - en.extend_u0(memory->width, false); - - sig_wr_clk.append(clk); - sig_wr_clk_enable.append(clk_enable); - sig_wr_clk_polarity.append(clk_polarity); - sig_wr_addr.append(addr); - sig_wr_data.append(data); - sig_wr_en.append(en); + SigSpec addr = sigmap(cell->getPort("\\ADDR")); + SigSpec data = sigmap(cell->getPort("\\DATA")); + + if (!addr.is_fully_const()) + log_error("Non-constant address %s in memory initialization %s.\n", log_signal(addr), log_id(cell)); + if (!data.is_fully_const()) + log_error("Non-constant data %s in memory initialization %s.\n", log_signal(data), log_id(cell)); + + int offset = (addr.as_int() - memory->start_offset) * memory->width; + + if (offset < 0 || offset + GetSize(data) > GetSize(init_data)) + log_warning("Address %s in memory initialization %s is out-of-bounds.\n", log_signal(addr), log_id(cell)); + + for (int i = 0; i < GetSize(data); i++) + if (0 <= i+offset && i+offset < GetSize(init_data)) + init_data.bits[i+offset] = data[i].data; + + continue; } - if (cell->type == "$memrd" && memory->name == cell->parameters["\\MEMID"].decode_string()) + if (cell->type == "$memwr") { - rd_ports++; - del_cells.push_back(cell); - - RTLIL::SigSpec clk = cell->getPort("\\CLK"); - RTLIL::SigSpec clk_enable = RTLIL::SigSpec(cell->parameters["\\CLK_ENABLE"]); - RTLIL::SigSpec clk_polarity = RTLIL::SigSpec(cell->parameters["\\CLK_POLARITY"]); - RTLIL::SigSpec transparent = RTLIL::SigSpec(cell->parameters["\\TRANSPARENT"]); - RTLIL::SigSpec addr = cell->getPort("\\ADDR"); - RTLIL::SigSpec data = cell->getPort("\\DATA"); - - clk.extend_u0(1, false); - clk_enable.extend_u0(1, false); - clk_polarity.extend_u0(1, false); - transparent.extend_u0(1, false); - addr.extend_u0(addr_bits, false); - data.extend_u0(memory->width, false); - - sig_rd_clk.append(clk); - sig_rd_clk_enable.append(clk_enable); - sig_rd_clk_polarity.append(clk_polarity); - sig_rd_transparent.append(transparent); - sig_rd_addr.append(addr); - sig_rd_data.append(data); + SigSpec clk = sigmap(cell->getPort("\\CLK")); + SigSpec clk_enable = SigSpec(cell->parameters["\\CLK_ENABLE"]); + SigSpec clk_polarity = SigSpec(cell->parameters["\\CLK_POLARITY"]); + SigSpec addr = sigmap(cell->getPort("\\ADDR")); + SigSpec data = sigmap(cell->getPort("\\DATA")); + SigSpec en = sigmap(cell->getPort("\\EN")); + + if (!en.is_fully_zero()) + { + clk.extend_u0(1, false); + clk_enable.extend_u0(1, false); + clk_polarity.extend_u0(1, false); + addr.extend_u0(addr_bits, false); + data.extend_u0(memory->width, false); + en.extend_u0(memory->width, false); + + sig_wr_clk.append(clk); + sig_wr_clk_enable.append(clk_enable); + sig_wr_clk_polarity.append(clk_polarity); + sig_wr_addr.append(addr); + sig_wr_data.append(data); + sig_wr_en.append(en); + + wr_ports++; + } + continue; + } + + if (cell->type == "$memrd") + { + SigSpec clk = sigmap(cell->getPort("\\CLK")); + SigSpec clk_enable = SigSpec(cell->parameters["\\CLK_ENABLE"]); + SigSpec clk_polarity = SigSpec(cell->parameters["\\CLK_POLARITY"]); + SigSpec transparent = SigSpec(cell->parameters["\\TRANSPARENT"]); + SigSpec addr = sigmap(cell->getPort("\\ADDR")); + SigSpec data = sigmap(cell->getPort("\\DATA")); + SigSpec en = sigmap(cell->getPort("\\EN")); + + if (!en.is_fully_zero()) + { + clk.extend_u0(1, false); + clk_enable.extend_u0(1, false); + clk_polarity.extend_u0(1, false); + transparent.extend_u0(1, false); + addr.extend_u0(addr_bits, false); + data.extend_u0(memory->width, false); + + sig_rd_clk.append(clk); + sig_rd_clk_enable.append(clk_enable); + sig_rd_clk_polarity.append(clk_polarity); + sig_rd_transparent.append(transparent); + sig_rd_addr.append(addr); + sig_rd_data.append(data); + sig_rd_en.append(en); + + rd_ports++; + } + continue; } } std::stringstream sstr; sstr << "$mem$" << memory->name.str() << "$" << (autoidx++); - RTLIL::Cell *mem = module->addCell(sstr.str(), "$mem"); - mem->parameters["\\MEMID"] = RTLIL::Const(memory->name.str()); - mem->parameters["\\WIDTH"] = RTLIL::Const(memory->width); - mem->parameters["\\OFFSET"] = RTLIL::Const(memory->start_offset); - mem->parameters["\\SIZE"] = RTLIL::Const(memory->size); - mem->parameters["\\ABITS"] = RTLIL::Const(addr_bits); + Cell *mem = module->addCell(sstr.str(), "$mem"); + mem->parameters["\\MEMID"] = Const(memory->name.str()); + mem->parameters["\\WIDTH"] = Const(memory->width); + mem->parameters["\\OFFSET"] = Const(memory->start_offset); + mem->parameters["\\SIZE"] = Const(memory->size); + mem->parameters["\\ABITS"] = Const(addr_bits); + + while (GetSize(init_data) > 1 && init_data.bits.back() == State::Sx && init_data.bits[GetSize(init_data)-2] == State::Sx) + init_data.bits.pop_back(); + mem->parameters["\\INIT"] = init_data; log_assert(sig_wr_clk.size() == wr_ports); log_assert(sig_wr_clk_enable.size() == wr_ports && sig_wr_clk_enable.is_fully_const()); @@ -145,9 +186,9 @@ void handle_memory(RTLIL::Module *module, RTLIL::Memory *memory) log_assert(sig_wr_data.size() == wr_ports * memory->width); log_assert(sig_wr_en.size() == wr_ports * memory->width); - mem->parameters["\\WR_PORTS"] = RTLIL::Const(wr_ports); - mem->parameters["\\WR_CLK_ENABLE"] = wr_ports ? sig_wr_clk_enable.as_const() : RTLIL::Const(0, 0); - mem->parameters["\\WR_CLK_POLARITY"] = wr_ports ? sig_wr_clk_polarity.as_const() : RTLIL::Const(0, 0); + mem->parameters["\\WR_PORTS"] = Const(wr_ports); + mem->parameters["\\WR_CLK_ENABLE"] = wr_ports ? sig_wr_clk_enable.as_const() : Const(0, 1); + mem->parameters["\\WR_CLK_POLARITY"] = wr_ports ? sig_wr_clk_polarity.as_const() : Const(0, 1); mem->setPort("\\WR_CLK", sig_wr_clk); mem->setPort("\\WR_ADDR", sig_wr_addr); @@ -160,30 +201,36 @@ void handle_memory(RTLIL::Module *module, RTLIL::Memory *memory) log_assert(sig_rd_addr.size() == rd_ports * addr_bits); log_assert(sig_rd_data.size() == rd_ports * memory->width); - mem->parameters["\\RD_PORTS"] = RTLIL::Const(rd_ports); - mem->parameters["\\RD_CLK_ENABLE"] = rd_ports ? sig_rd_clk_enable.as_const() : RTLIL::Const(0, 0); - mem->parameters["\\RD_CLK_POLARITY"] = rd_ports ? sig_rd_clk_polarity.as_const() : RTLIL::Const(0, 0); - mem->parameters["\\RD_TRANSPARENT"] = rd_ports ? sig_rd_transparent.as_const() : RTLIL::Const(0, 0); + mem->parameters["\\RD_PORTS"] = Const(rd_ports); + mem->parameters["\\RD_CLK_ENABLE"] = rd_ports ? sig_rd_clk_enable.as_const() : Const(0, 1); + mem->parameters["\\RD_CLK_POLARITY"] = rd_ports ? sig_rd_clk_polarity.as_const() : Const(0, 1); + mem->parameters["\\RD_TRANSPARENT"] = rd_ports ? sig_rd_transparent.as_const() : Const(0, 1); mem->setPort("\\RD_CLK", sig_rd_clk); mem->setPort("\\RD_ADDR", sig_rd_addr); mem->setPort("\\RD_DATA", sig_rd_data); + mem->setPort("\\RD_EN", sig_rd_en); - for (auto c : del_cells) + for (auto c : memcells) module->remove(c); + + return mem; } -static void handle_module(RTLIL::Design *design, RTLIL::Module *module) +static void handle_module(Design *design, Module *module) { - std::vector<RTLIL::IdString> delme; + std::vector<pair<Cell*, IdString>> finqueue; + for (auto &mem_it : module->memories) if (design->selected(module, mem_it.second)) { - handle_memory(module, mem_it.second); - delme.push_back(mem_it.first); + Cell *c = handle_memory(module, mem_it.second); + finqueue.push_back(pair<Cell*, IdString>(c, mem_it.first)); } - for (auto &it : delme) { - delete module->memories.at(it); - module->memories.erase(it); + for (auto &it : finqueue) { + delete module->memories.at(it.second); + module->memories.erase(it.second); + if (it.first) + module->rename(it.first, it.second); } } @@ -200,12 +247,12 @@ struct MemoryCollectPass : public Pass { log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing MEMORY_COLLECT pass (generating $mem cells).\n"); + log_header(design, "Executing MEMORY_COLLECT pass (generating $mem cells).\n"); extra_args(args, 1, design); for (auto &mod_it : design->modules_) if (design->selected(mod_it.second)) handle_module(design, mod_it.second); } } MemoryCollectPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/memory/memory_dff.cc b/passes/memory/memory_dff.cc index d3cc681a2..40691d160 100644 --- a/passes/memory/memory_dff.cc +++ b/passes/memory/memory_dff.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -17,170 +17,251 @@ * */ -#include "kernel/register.h" -#include "kernel/log.h" -#include <stdlib.h> -#include <sstream> +#include "kernel/yosys.h" +#include "kernel/sigtools.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -void normalize_sig(RTLIL::Module *module, RTLIL::SigSpec &sig) +struct MemoryDffWorker { - for (auto &conn : module->connections()) - sig.replace(conn.first, conn.second); -} + Module *module; + SigMap sigmap; -bool find_sig_before_dff(RTLIL::Module *module, std::vector<RTLIL::Cell*> &dff_cells, RTLIL::SigSpec &sig, RTLIL::SigSpec &clk, bool &clk_polarity, bool after = false) -{ - normalize_sig(module, sig); + 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; + + MemoryDffWorker(Module *module) : module(module), sigmap(module) { } - for (auto &bit : sig) + bool find_sig_before_dff(RTLIL::SigSpec &sig, RTLIL::SigSpec &clk, bool &clk_polarity, bool after = false) { - if (bit.wire == NULL) - continue; + sigmap.apply(sig); - for (auto cell : dff_cells) + for (auto &bit : sig) { - if (clk != RTLIL::SigSpec(RTLIL::State::Sx)) { - if (cell->getPort("\\CLK") != clk) - continue; - if (cell->parameters["\\CLK_POLARITY"].as_bool() != clk_polarity) - continue; - } - - RTLIL::SigSpec q_norm = cell->getPort(after ? "\\D" : "\\Q"); - normalize_sig(module, q_norm); - - RTLIL::SigSpec d = q_norm.extract(bit, &cell->getPort(after ? "\\Q" : "\\D")); - if (d.size() != 1) + if (bit.wire == NULL) continue; - bit = d; - clk = cell->getPort("\\CLK"); - clk_polarity = cell->parameters["\\CLK_POLARITY"].as_bool(); - goto replaced_this_bit; - } + for (auto cell : dff_cells) + { + if (after && forward_merged_dffs.count(cell)) + continue; - return false; - replaced_this_bit:; - } + SigSpec this_clk = cell->getPort("\\CLK"); + bool this_clk_polarity = cell->parameters["\\CLK_POLARITY"].as_bool(); - return true; -} + if (invbits.count(this_clk)) { + this_clk = invbits.at(this_clk); + this_clk_polarity = !this_clk_polarity; + } -void handle_wr_cell(RTLIL::Module *module, std::vector<RTLIL::Cell*> &dff_cells, RTLIL::Cell *cell) -{ - log("Checking cell `%s' in module `%s': ", cell->name.c_str(), module->name.c_str()); + if (clk != RTLIL::SigSpec(RTLIL::State::Sx)) { + if (this_clk != clk) + continue; + if (this_clk_polarity != clk_polarity) + continue; + } - RTLIL::SigSpec clk = RTLIL::SigSpec(RTLIL::State::Sx); - bool clk_polarity = 0; + RTLIL::SigSpec q_norm = cell->getPort(after ? "\\D" : "\\Q"); + sigmap.apply(q_norm); - RTLIL::SigSpec sig_addr = cell->getPort("\\ADDR"); - if (!find_sig_before_dff(module, dff_cells, sig_addr, clk, clk_polarity)) { - log("no (compatible) $dff for address input found.\n"); - return; - } + RTLIL::SigSpec d = q_norm.extract(bit, &cell->getPort(after ? "\\Q" : "\\D")); + if (d.size() != 1) + continue; - RTLIL::SigSpec sig_data = cell->getPort("\\DATA"); - if (!find_sig_before_dff(module, dff_cells, sig_data, clk, clk_polarity)) { - log("no (compatible) $dff for data input found.\n"); - return; - } + bit = d; + clk = this_clk; + clk_polarity = this_clk_polarity; + candidate_dffs.insert(cell); + goto replaced_this_bit; + } - RTLIL::SigSpec sig_en = cell->getPort("\\EN"); - if (!find_sig_before_dff(module, dff_cells, sig_en, clk, clk_polarity)) { - log("no (compatible) $dff for enable input found.\n"); - return; - } + return false; + replaced_this_bit:; + } - if (clk != RTLIL::SigSpec(RTLIL::State::Sx)) { - cell->setPort("\\CLK", clk); - cell->setPort("\\ADDR", sig_addr); - cell->setPort("\\DATA", sig_data); - cell->setPort("\\EN", sig_en); - cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1); - cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity); - log("merged $dff to cell.\n"); - return; + return true; } - log("no (compatible) $dff found.\n"); -} + void handle_wr_cell(RTLIL::Cell *cell) + { + log("Checking cell `%s' in module `%s': ", cell->name.c_str(), module->name.c_str()); -void disconnect_dff(RTLIL::Module *module, RTLIL::SigSpec sig) -{ - normalize_sig(module, sig); - sig.sort_and_unify(); + RTLIL::SigSpec clk = RTLIL::SigSpec(RTLIL::State::Sx); + bool clk_polarity = 0; + candidate_dffs.clear(); - std::stringstream sstr; - sstr << "$memory_dff_disconnected$" << (autoidx++); + RTLIL::SigSpec sig_addr = cell->getPort("\\ADDR"); + if (!find_sig_before_dff(sig_addr, clk, clk_polarity)) { + log("no (compatible) $dff for address input found.\n"); + return; + } - RTLIL::SigSpec new_sig = module->addWire(sstr.str(), sig.size()); + RTLIL::SigSpec sig_data = cell->getPort("\\DATA"); + if (!find_sig_before_dff(sig_data, clk, clk_polarity)) { + log("no (compatible) $dff for data input found.\n"); + return; + } - for (auto cell : module->cells()) - if (cell->type == "$dff") { - RTLIL::SigSpec new_q = cell->getPort("\\Q"); - new_q.replace(sig, new_sig); - cell->setPort("\\Q", new_q); + RTLIL::SigSpec sig_en = cell->getPort("\\EN"); + if (!find_sig_before_dff(sig_en, clk, clk_polarity)) { + log("no (compatible) $dff for enable input found.\n"); + return; } -} -void handle_rd_cell(RTLIL::Module *module, std::vector<RTLIL::Cell*> &dff_cells, RTLIL::Cell *cell) -{ - log("Checking cell `%s' in module `%s': ", cell->name.c_str(), module->name.c_str()); + if (clk != RTLIL::SigSpec(RTLIL::State::Sx)) + { + for (auto cell : candidate_dffs) + forward_merged_dffs.insert(cell); + + cell->setPort("\\CLK", clk); + cell->setPort("\\ADDR", sig_addr); + cell->setPort("\\DATA", sig_data); + cell->setPort("\\EN", sig_en); + cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1); + cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity); + + log("merged $dff to cell.\n"); + return; + } - bool clk_polarity = 0; + log("no (compatible) $dff found.\n"); + } - RTLIL::SigSpec clk_data = RTLIL::SigSpec(RTLIL::State::Sx); - RTLIL::SigSpec sig_data = cell->getPort("\\DATA"); - if (find_sig_before_dff(module, dff_cells, sig_data, clk_data, clk_polarity, true) && - clk_data != RTLIL::SigSpec(RTLIL::State::Sx)) + void disconnect_dff(RTLIL::SigSpec sig) { - disconnect_dff(module, sig_data); - cell->setPort("\\CLK", clk_data); - cell->setPort("\\DATA", sig_data); - cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1); - cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity); - cell->parameters["\\TRANSPARENT"] = RTLIL::Const(0); - log("merged data $dff to cell.\n"); - return; + 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 == "$dff") { + RTLIL::SigSpec new_q = cell->getPort("\\Q"); + new_q.replace(sig, new_sig); + cell->setPort("\\Q", new_q); + } } - RTLIL::SigSpec clk_addr = RTLIL::SigSpec(RTLIL::State::Sx); - RTLIL::SigSpec sig_addr = cell->getPort("\\ADDR"); - if (find_sig_before_dff(module, dff_cells, sig_addr, clk_addr, clk_polarity) && - clk_addr != RTLIL::SigSpec(RTLIL::State::Sx)) + void handle_rd_cell(RTLIL::Cell *cell) { - cell->setPort("\\CLK", clk_addr); - cell->setPort("\\ADDR", sig_addr); - cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1); - cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity); - cell->parameters["\\TRANSPARENT"] = RTLIL::Const(1); - log("merged address $dff to cell.\n"); - return; - } + log("Checking cell `%s' in module `%s': ", cell->name.c_str(), module->name.c_str()); - log("no (compatible) $dff found.\n"); -} + bool clk_polarity = 0; -void handle_module(RTLIL::Module *module, bool flag_wr_only) -{ - std::vector<RTLIL::Cell*> dff_cells; + RTLIL::SigSpec clk_data = RTLIL::SigSpec(RTLIL::State::Sx); + RTLIL::SigSpec sig_data = cell->getPort("\\DATA"); + + for (auto bit : sigmap(sig_data)) + if (sigbit_users_count[bit] > 1) + goto skip_ff_after_read_merging; - for (auto cell : module->cells()) - if (cell->type == "$dff") - dff_cells.push_back(cell); + if (mux_cells_a.count(sig_data) || mux_cells_b.count(sig_data)) + { + 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); + SigSpec check_q = sigmap(mux->getPort(enable_invert ? "\\B" : "\\A")); + + sig_data = sigmap(mux->getPort("\\Y")); + for (auto bit : sig_data) + if (sigbit_users_count[bit] > 1) + goto skip_ff_after_read_merging; + + if (find_sig_before_dff(sig_data, clk_data, clk_polarity, true) && clk_data != RTLIL::SigSpec(RTLIL::State::Sx) && sig_data == check_q) + { + disconnect_dff(sig_data); + cell->setPort("\\CLK", clk_data); + cell->setPort("\\EN", enable_invert ? module->LogicNot(NEW_ID, mux->getPort("\\S")) : mux->getPort("\\S")); + cell->setPort("\\DATA", sig_data); + cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1); + cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity); + cell->parameters["\\TRANSPARENT"] = RTLIL::Const(0); + log("merged data $dff with rd enable to cell.\n"); + return; + } + } + else + { + if (find_sig_before_dff(sig_data, clk_data, clk_polarity, true) && clk_data != RTLIL::SigSpec(RTLIL::State::Sx)) + { + disconnect_dff(sig_data); + cell->setPort("\\CLK", clk_data); + cell->setPort("\\EN", State::S1); + cell->setPort("\\DATA", sig_data); + cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1); + cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity); + cell->parameters["\\TRANSPARENT"] = RTLIL::Const(0); + log("merged data $dff to cell.\n"); + return; + } + } - for (auto cell : module->selected_cells()) - if (cell->type == "$memwr" && !cell->parameters["\\CLK_ENABLE"].as_bool()) - handle_wr_cell(module, dff_cells, cell); + skip_ff_after_read_merging:; + RTLIL::SigSpec clk_addr = RTLIL::SigSpec(RTLIL::State::Sx); + RTLIL::SigSpec sig_addr = cell->getPort("\\ADDR"); + if (find_sig_before_dff(sig_addr, clk_addr, clk_polarity) && + clk_addr != RTLIL::SigSpec(RTLIL::State::Sx)) + { + cell->setPort("\\CLK", clk_addr); + cell->setPort("\\EN", State::S1); + cell->setPort("\\ADDR", sig_addr); + cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1); + cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity); + cell->parameters["\\TRANSPARENT"] = RTLIL::Const(1); + log("merged address $dff to cell.\n"); + return; + } + + log("no (compatible) $dff found.\n"); + } + + void run(bool flag_wr_only) + { + 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 == "$dff") + dff_cells.push_back(cell); + if (cell->type == "$mux") { + mux_cells_a[sigmap(cell->getPort("\\A"))] = cell; + mux_cells_b[sigmap(cell->getPort("\\B"))] = cell; + } + if (cell->type == "$not" || cell->type == "$_NOT_" || (cell->type == "$logic_not" && GetSize(cell->getPort("\\A")) == 1)) { + SigSpec sig_a = cell->getPort("\\A"); + SigSpec sig_y = cell->getPort("\\Y"); + if (cell->type == "$not") + sig_a.extend_u0(GetSize(sig_y), cell->getParam("\\A_SIGNED").as_bool()); + if (cell->type == "$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 &conn : cell->connections()) + if (!cell->known() || cell->input(conn.first)) + for (auto bit : sigmap(conn.second)) + sigbit_users_count[bit]++; + } - if (!flag_wr_only) for (auto cell : module->selected_cells()) - if (cell->type == "$memrd" && !cell->parameters["\\CLK_ENABLE"].as_bool()) - handle_rd_cell(module, dff_cells, cell); -} + if (cell->type == "$memwr" && !cell->parameters["\\CLK_ENABLE"].as_bool()) + handle_wr_cell(cell); + + if (!flag_wr_only) + for (auto cell : module->selected_cells()) + if (cell->type == "$memrd" && !cell->parameters["\\CLK_ENABLE"].as_bool()) + handle_rd_cell(cell); + } +}; struct MemoryDffPass : public Pass { MemoryDffPass() : Pass("memory_dff", "merge input/output DFFs into memories") { } @@ -194,7 +275,7 @@ struct MemoryDffPass : public Pass { 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(" -wr_only\n"); + log(" -nordfff\n"); log(" do not merge registers on read ports\n"); log("\n"); } @@ -202,11 +283,11 @@ struct MemoryDffPass : public Pass { { bool flag_wr_only = false; - log_header("Executing MEMORY_DFF pass (merging $dff cells to $memrd and $memwr).\n"); + log_header(design, "Executing MEMORY_DFF pass (merging $dff cells to $memrd and $memwr).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { - if (args[argidx] == "-wr_only") { + if (args[argidx] == "-nordff" || args[argidx] == "-wr_only") { flag_wr_only = true; continue; } @@ -214,9 +295,11 @@ struct MemoryDffPass : public Pass { } extra_args(args, argidx, design); - for (auto mod : design->selected_modules()) - handle_module(mod, flag_wr_only); + for (auto mod : design->selected_modules()) { + MemoryDffWorker worker(mod); + worker.run(flag_wr_only); + } } } MemoryDffPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/memory/memory_map.cc b/passes/memory/memory_map.cc index 4fb10a989..bffeec857 100644 --- a/passes/memory/memory_map.cc +++ b/passes/memory/memory_map.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -37,7 +37,7 @@ struct MemoryMapWorker { std::stringstream sstr; sstr << "$memory" << name.str() << token1; - + if (i >= 0) sstr << "[" << i << "]"; @@ -80,13 +80,20 @@ struct MemoryMapWorker { std::set<int> static_ports; std::map<int, RTLIL::SigSpec> static_cells_map; + + int wr_ports = cell->parameters["\\WR_PORTS"].as_int(); + int rd_ports = cell->parameters["\\RD_PORTS"].as_int(); + int mem_size = cell->parameters["\\SIZE"].as_int(); int mem_width = cell->parameters["\\WIDTH"].as_int(); int mem_offset = cell->parameters["\\OFFSET"].as_int(); int mem_abits = cell->parameters["\\ABITS"].as_int(); + SigSpec init_data = cell->getParam("\\INIT"); + init_data.extend_u0(mem_size*mem_width, true); + // delete unused memory cell - if (cell->parameters["\\RD_PORTS"].as_int() == 0 && cell->parameters["\\WR_PORTS"].as_int() == 0) { + if (wr_ports == 0 && rd_ports == 0) { module->remove(cell); return; } @@ -95,6 +102,8 @@ struct MemoryMapWorker RTLIL::SigSpec clocks = cell->getPort("\\WR_CLK"); RTLIL::Const clocks_pol = cell->parameters["\\WR_CLK_POLARITY"]; RTLIL::Const clocks_en = cell->parameters["\\WR_CLK_ENABLE"]; + clocks_pol.bits.resize(wr_ports); + clocks_en.bits.resize(wr_ports); RTLIL::SigSpec refclock; RTLIL::State refclock_pol = RTLIL::State::Sx; for (int i = 0; i < clocks.size(); i++) { @@ -110,7 +119,7 @@ struct MemoryMapWorker // FIXME: Actually we should check for wr_en.is_fully_const() also and // create a $adff cell with this ports wr_en input as reset pin when wr_en // is not a simple static 1. - static_cells_map[wr_addr.as_int()] = wr_data; + static_cells_map[wr_addr.as_int() - mem_offset] = wr_data; static_ports.insert(i); continue; } @@ -165,7 +174,10 @@ struct MemoryMapWorker w_out_name = genid(cell->name, "", i, "$q"); RTLIL::Wire *w_out = module->addWire(w_out_name, mem_width); - w_out->start_offset = mem_offset; + SigSpec w_init = init_data.extract(i*mem_width, mem_width); + + if (!w_init.is_fully_undef()) + w_out->attributes["\\init"] = w_init.as_const(); data_reg_out.push_back(RTLIL::SigSpec(w_out)); c->setPort("\\Q", data_reg_out.back()); @@ -180,39 +192,51 @@ struct MemoryMapWorker { RTLIL::SigSpec rd_addr = cell->getPort("\\RD_ADDR").extract(i*mem_abits, mem_abits); + if (mem_offset) + rd_addr = module->Sub(NEW_ID, rd_addr, SigSpec(mem_offset, GetSize(rd_addr))); + std::vector<RTLIL::SigSpec> rd_signals; rd_signals.push_back(cell->getPort("\\RD_DATA").extract(i*mem_width, mem_width)); if (cell->parameters["\\RD_CLK_ENABLE"].bits[i] == RTLIL::State::S1) { + RTLIL::Cell *dff_cell = nullptr; + if (cell->parameters["\\RD_TRANSPARENT"].bits[i] == RTLIL::State::S1) { - RTLIL::Cell *c = module->addCell(genid(cell->name, "$rdreg", i), "$dff"); - c->parameters["\\WIDTH"] = RTLIL::Const(mem_abits); - c->parameters["\\CLK_POLARITY"] = RTLIL::Const(cell->parameters["\\RD_CLK_POLARITY"].bits[i]); - c->setPort("\\CLK", cell->getPort("\\RD_CLK").extract(i, 1)); - c->setPort("\\D", rd_addr); + dff_cell = module->addCell(genid(cell->name, "$rdreg", i), "$dff"); + dff_cell->parameters["\\WIDTH"] = RTLIL::Const(mem_abits); + dff_cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(cell->parameters["\\RD_CLK_POLARITY"].bits[i]); + dff_cell->setPort("\\CLK", cell->getPort("\\RD_CLK").extract(i, 1)); + dff_cell->setPort("\\D", rd_addr); count_dff++; RTLIL::Wire *w = module->addWire(genid(cell->name, "$rdreg", i, "$q"), mem_abits); - c->setPort("\\Q", RTLIL::SigSpec(w)); + dff_cell->setPort("\\Q", RTLIL::SigSpec(w)); rd_addr = RTLIL::SigSpec(w); } else { - RTLIL::Cell *c = module->addCell(genid(cell->name, "$rdreg", i), "$dff"); - c->parameters["\\WIDTH"] = cell->parameters["\\WIDTH"]; - c->parameters["\\CLK_POLARITY"] = RTLIL::Const(cell->parameters["\\RD_CLK_POLARITY"].bits[i]); - c->setPort("\\CLK", cell->getPort("\\RD_CLK").extract(i, 1)); - c->setPort("\\Q", rd_signals.back()); + dff_cell = module->addCell(genid(cell->name, "$rdreg", i), "$dff"); + dff_cell->parameters["\\WIDTH"] = cell->parameters["\\WIDTH"]; + dff_cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(cell->parameters["\\RD_CLK_POLARITY"].bits[i]); + dff_cell->setPort("\\CLK", cell->getPort("\\RD_CLK").extract(i, 1)); + dff_cell->setPort("\\Q", rd_signals.back()); count_dff++; RTLIL::Wire *w = module->addWire(genid(cell->name, "$rdreg", i, "$d"), mem_width); rd_signals.clear(); rd_signals.push_back(RTLIL::SigSpec(w)); - c->setPort("\\D", rd_signals.back()); + dff_cell->setPort("\\D", rd_signals.back()); + } + + SigBit en_bit = cell->getPort("\\RD_EN").extract(i); + if (en_bit != State::S1) { + SigSpec new_d = module->Mux(genid(cell->name, "$rdenmux", i), + dff_cell->getPort("\\Q"), dff_cell->getPort("\\D"), en_bit); + dff_cell->setPort("\\D", new_d); } } @@ -256,6 +280,10 @@ struct MemoryMapWorker RTLIL::SigSpec wr_addr = cell->getPort("\\WR_ADDR").extract(j*mem_abits, mem_abits); RTLIL::SigSpec wr_data = cell->getPort("\\WR_DATA").extract(j*mem_width, mem_width); RTLIL::SigSpec wr_en = cell->getPort("\\WR_EN").extract(j*mem_width, mem_width); + + if (mem_offset) + wr_addr = module->Sub(NEW_ID, wr_addr, SigSpec(mem_offset, GetSize(wr_addr))); + RTLIL::Wire *w_seladdr = addr_decode(wr_addr, RTLIL::SigSpec(i, mem_abits)); int wr_offset = 0; @@ -335,11 +363,11 @@ struct MemoryMapPass : public Pass { log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing MEMORY_MAP pass (converting $mem cells to logic and flip-flops).\n"); + log_header(design, "Executing MEMORY_MAP pass (converting $mem cells to logic and flip-flops).\n"); extra_args(args, 1, design); for (auto mod : design->selected_modules()) MemoryMapWorker(design, mod); } } MemoryMapPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/memory/memory_share.cc b/passes/memory/memory_share.cc index a2f89f6d9..f298169de 100644 --- a/passes/memory/memory_share.cc +++ b/passes/memory/memory_share.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -43,7 +43,7 @@ struct MemoryShareWorker CellTypes cone_ct; std::map<RTLIL::SigBit, std::pair<RTLIL::Cell*, int>> sig_to_mux; - std::map<std::set<std::map<RTLIL::SigBit, bool>>, RTLIL::SigBit> conditions_logic_cache; + std::map<pair<std::set<std::map<SigBit, bool>>, SigBit>, SigBit> conditions_logic_cache; // ----------------------------------------------------------------- @@ -79,7 +79,7 @@ struct MemoryShareWorker } return false; } - + for (int i = 0; i < int(sig_s.size()); i++) { @@ -109,10 +109,12 @@ struct MemoryShareWorker return false; } - RTLIL::SigBit conditions_to_logic(std::set<std::map<RTLIL::SigBit, bool>> &conditions, int &created_conditions) + RTLIL::SigBit conditions_to_logic(std::set<std::map<RTLIL::SigBit, bool>> &conditions, SigBit olden, int &created_conditions) { - if (conditions_logic_cache.count(conditions)) - return conditions_logic_cache.at(conditions); + auto key = make_pair(conditions, olden); + + if (conditions_logic_cache.count(key)) + return conditions_logic_cache.at(key); RTLIL::SigSpec terms; for (auto &cond : conditions) { @@ -125,10 +127,16 @@ struct MemoryShareWorker created_conditions++; } - if (terms.size() > 1) + 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[conditions] = terms; + return conditions_logic_cache[key] = terms; } void translate_rd_feedback_to_en(std::string memid, std::vector<RTLIL::Cell*> &rd_ports, std::vector<RTLIL::Cell*> &wr_ports) @@ -137,15 +145,14 @@ struct MemoryShareWorker std::map<RTLIL::SigBit, std::set<RTLIL::SigBit>> muxtree_upstream_map; std::set<RTLIL::SigBit> non_feedback_nets; - for (auto wire_it : module->wires_) - if (wire_it.second->port_output) { - std::vector<RTLIL::SigBit> bits = RTLIL::SigSpec(wire_it.second); + for (auto wire : module->wires()) + if (wire->port_output) { + std::vector<RTLIL::SigBit> bits = sigmap(wire); non_feedback_nets.insert(bits.begin(), bits.end()); } - for (auto cell_it : module->cells_) + for (auto cell : module->cells()) { - RTLIL::Cell *cell = cell_it.second; bool ignore_data_port = false; if (cell->type == "$mux" || cell->type == "$pmux") @@ -170,7 +177,7 @@ struct MemoryShareWorker cell->parameters.at("\\MEMID").decode_string() == memid) ignore_data_port = true; - for (auto conn : cell_it.second->connections()) + for (auto conn : cell->connections()) { if (ignore_data_port && conn.first == "\\DATA") continue; @@ -207,7 +214,7 @@ struct MemoryShareWorker if (non_feedback_nets.count(sig_data[i])) goto not_pure_feedback_port; - async_rd_bits[sig_addr].resize(std::max(async_rd_bits.size(), sig_data.size())); + 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]); @@ -237,13 +244,8 @@ struct MemoryShareWorker std::map<RTLIL::SigBit, bool> state; std::set<std::map<RTLIL::SigBit, bool>> conditions; - if (cell_en[i].wire != NULL) { - state[cell_en[i]] = false; - conditions.insert(state); - } - find_data_feedback(async_rd_bits.at(sig_addr).at(i), cell_data[i], state, conditions); - cell_en[i] = conditions_to_logic(conditions, created_conditions); + cell_en[i] = conditions_to_logic(conditions, cell_en[i], created_conditions); } if (created_conditions) { @@ -489,8 +491,8 @@ struct MemoryShareWorker if (wr_ports.size() <= 1) return; - ezDefaultSAT ez; - SatGen satgen(&ez, &modwalker.sigmap); + ezSatPtr ez; + SatGen satgen(ez.get(), &modwalker.sigmap); // find list of considered ports and port pairs @@ -553,7 +555,7 @@ struct MemoryShareWorker if (considered_port_pairs.count(i) || considered_port_pairs.count(i+1)) { RTLIL::SigSpec sig = modwalker.sigmap(wr_ports[i]->getPort("\\EN")); - port_to_sat_variable[i] = ez.expression(ez.OpOr, satgen.importSigSpec(sig)); + port_to_sat_variable[i] = ez->expression(ez->OpOr, satgen.importSigSpec(sig)); std::vector<RTLIL::SigBit> bits = sig; bits_queue.insert(bits.begin(), bits.end()); @@ -582,7 +584,7 @@ struct MemoryShareWorker 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); + if (i != j) ez->assume(ez->NOT(i), j); } log(" Common input cone for all EN signals: %d cells.\n", int(sat_cells.size())); @@ -590,7 +592,7 @@ struct MemoryShareWorker for (auto cell : sat_cells) satgen.importCell(cell); - log(" Size of unconstrained SAT problem: %d variables, %d clauses\n", ez.numCnfVariables(), ez.numCnfClauses()); + log(" Size of unconstrained SAT problem: %d variables, %d clauses\n", ez->numCnfVariables(), ez->numCnfClauses()); // merge subsequent ports if possible @@ -599,13 +601,13 @@ struct MemoryShareWorker if (!considered_port_pairs.count(i)) continue; - if (ez.solve(port_to_sat_variable.at(i-1), port_to_sat_variable.at(i))) { + 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(" 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)); + port_to_sat_variable.at(i) = ez->OR(port_to_sat_variable.at(i-1), port_to_sat_variable.at(i)); RTLIL::SigSpec last_addr = wr_ports[i-1]->getPort("\\ADDR"); RTLIL::SigSpec last_data = wr_ports[i-1]->getPort("\\DATA"); @@ -663,10 +665,8 @@ struct MemoryShareWorker std::map<std::string, std::pair<std::vector<RTLIL::Cell*>, std::vector<RTLIL::Cell*>>> memindex; sigmap_xmux = sigmap; - for (auto &it : module->cells_) + for (auto cell : module->cells()) { - RTLIL::Cell *cell = it.second; - if (cell->type == "$memrd") memindex[cell->parameters.at("\\MEMID").decode_string()].first.push_back(cell); @@ -743,11 +743,11 @@ struct MemorySharePass : public Pass { log("\n"); log("Note that in addition to the algorithms implemented in this pass, the $memrd\n"); log("and $memwr cells are also subject to generic resource sharing passes (and other\n"); - log("optimizations) such as opt_share.\n"); + log("optimizations) such as \"share\" and \"opt_merge\".\n"); log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing MEMORY_SHARE pass (consolidating $memrc/$memwr cells).\n"); + log_header(design, "Executing MEMORY_SHARE pass (consolidating $memrc/$memwr cells).\n"); extra_args(args, 1, design); for (auto module : design->selected_modules()) MemoryShareWorker(design, module); diff --git a/passes/memory/memory_unpack.cc b/passes/memory/memory_unpack.cc index e650facb4..a0fc31b5e 100644 --- a/passes/memory/memory_unpack.cc +++ b/passes/memory/memory_unpack.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -57,6 +57,7 @@ void handle_memory(RTLIL::Module *module, RTLIL::Cell *memory) cell->parameters["\\CLK_POLARITY"] = RTLIL::SigSpec(memory->parameters.at("\\RD_CLK_POLARITY")).extract(i, 1).as_const(); cell->parameters["\\TRANSPARENT"] = RTLIL::SigSpec(memory->parameters.at("\\RD_TRANSPARENT")).extract(i, 1).as_const(); cell->setPort("\\CLK", memory->getPort("\\RD_CLK").extract(i, 1)); + cell->setPort("\\EN", memory->getPort("\\RD_EN").extract(i, 1)); cell->setPort("\\ADDR", memory->getPort("\\RD_ADDR").extract(i*abits, abits)); cell->setPort("\\DATA", memory->getPort("\\RD_DATA").extract(i*mem->width, mem->width)); } @@ -76,6 +77,41 @@ void handle_memory(RTLIL::Module *module, RTLIL::Cell *memory) cell->setPort("\\DATA", memory->getPort("\\WR_DATA").extract(i*mem->width, mem->width)); } + Const initval = memory->parameters.at("\\INIT"); + RTLIL::Cell *last_init_cell = nullptr; + SigSpec last_init_data; + int last_init_addr=0; + + for (int i = 0; i < GetSize(initval) && i/mem->width < (1 << abits); i += mem->width) { + Const val = initval.extract(i, mem->width, State::Sx); + for (auto bit : val.bits) + if (bit != State::Sx) + goto found_non_undef_initval; + continue; + found_non_undef_initval: + if (last_init_cell && last_init_addr+1 == i/mem->width) { + last_init_cell->parameters["\\WORDS"] = last_init_cell->parameters["\\WORDS"].as_int() + 1; + last_init_data.append(val); + last_init_addr++; + } else { + if (last_init_cell) + last_init_cell->setPort("\\DATA", last_init_data); + RTLIL::Cell *cell = module->addCell(NEW_ID, "$meminit"); + cell->parameters["\\MEMID"] = mem_name.str(); + cell->parameters["\\ABITS"] = memory->parameters.at("\\ABITS"); + cell->parameters["\\WIDTH"] = memory->parameters.at("\\WIDTH"); + cell->parameters["\\WORDS"] = 1; + cell->parameters["\\PRIORITY"] = i/mem->width; + cell->setPort("\\ADDR", SigSpec(i/mem->width, abits)); + last_init_cell = cell; + last_init_addr = i/mem->width; + last_init_data = val; + } + } + + if (last_init_cell) + last_init_cell->setPort("\\DATA", last_init_data); + module->remove(memory); } @@ -102,12 +138,12 @@ struct MemoryUnpackPass : public Pass { log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing MEMORY_UNPACK pass (generating $memrd/$memwr cells form $mem cells).\n"); + log_header(design, "Executing MEMORY_UNPACK pass (generating $memrd/$memwr cells form $mem cells).\n"); extra_args(args, 1, design); for (auto &mod_it : design->modules_) if (design->selected(mod_it.second)) handle_module(design, mod_it.second); } } MemoryUnpackPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/opt/Makefile.inc b/passes/opt/Makefile.inc index 6b075cd9a..a8b1537bb 100644 --- a/passes/opt/Makefile.inc +++ b/passes/opt/Makefile.inc @@ -1,11 +1,11 @@ OBJS += passes/opt/opt.o -OBJS += passes/opt/opt_share.o +OBJS += passes/opt/opt_merge.o OBJS += passes/opt/opt_muxtree.o OBJS += passes/opt/opt_reduce.o OBJS += passes/opt/opt_rmdff.o -OBJS += passes/opt/opt_clean.o -OBJS += passes/opt/opt_const.o +OBJS += passes/opt/opt_clean.o +OBJS += passes/opt/opt_expr.o ifneq ($(SMALL),1) OBJS += passes/opt/share.o diff --git a/passes/opt/opt.cc b/passes/opt/opt.cc index 25419375e..13ea5469b 100644 --- a/passes/opt/opt.cc +++ b/passes/opt/opt.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -37,23 +37,23 @@ struct OptPass : public Pass { log("a series of trivial optimizations and cleanups. This pass executes the other\n"); log("passes in the following order:\n"); log("\n"); - log(" opt_const [-mux_undef] [-mux_bool] [-undriven] [-fine] [-full] [-keepdc]\n"); - log(" opt_share -nomux\n"); + log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc]\n"); + log(" opt_merge [-share_all] -nomux\n"); log("\n"); log(" do\n"); log(" opt_muxtree\n"); log(" opt_reduce [-fine] [-full]\n"); - log(" opt_share\n"); + log(" opt_merge [-share_all]\n"); log(" opt_rmdff\n"); log(" opt_clean [-purge]\n"); - log(" opt_const [-mux_undef] [-mux_bool] [-undriven] [-fine] [-full] [-keepdc]\n"); + log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc]\n"); log(" while <changed design>\n"); log("\n"); log("When called with -fast the following script is used instead:\n"); log("\n"); log(" do\n"); - log(" opt_const [-mux_undef] [-mux_bool] [-undriven] [-fine] [-full] [-keepdc]\n"); - log(" opt_share\n"); + log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc]\n"); + log(" opt_merge [-share_all]\n"); log(" opt_rmdff\n"); log(" opt_clean [-purge]\n"); log(" while <changed design in opt_rmdff>\n"); @@ -66,11 +66,12 @@ struct OptPass : public Pass { virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { std::string opt_clean_args; - std::string opt_const_args; + std::string opt_expr_args; std::string opt_reduce_args; + std::string opt_merge_args; bool fast_mode = false; - log_header("Executing OPT pass (performing simple optimizations).\n"); + log_header(design, "Executing OPT pass (performing simple optimizations).\n"); log_push(); size_t argidx; @@ -80,29 +81,37 @@ struct OptPass : public Pass { continue; } if (args[argidx] == "-mux_undef") { - opt_const_args += " -mux_undef"; + opt_expr_args += " -mux_undef"; continue; } if (args[argidx] == "-mux_bool") { - opt_const_args += " -mux_bool"; + opt_expr_args += " -mux_bool"; continue; } if (args[argidx] == "-undriven") { - opt_const_args += " -undriven"; + opt_expr_args += " -undriven"; + continue; + } + if (args[argidx] == "-clkinv") { + opt_expr_args += " -clkinv"; continue; } if (args[argidx] == "-fine") { - opt_const_args += " -fine"; + opt_expr_args += " -fine"; opt_reduce_args += " -fine"; continue; } if (args[argidx] == "-full") { - opt_const_args += " -full"; + opt_expr_args += " -full"; opt_reduce_args += " -full"; continue; } if (args[argidx] == "-keepdc") { - opt_const_args += " -keepdc"; + opt_expr_args += " -keepdc"; + continue; + } + if (args[argidx] == "-share_all") { + opt_merge_args += " -share_all"; continue; } if (args[argidx] == "-fast") { @@ -116,38 +125,42 @@ struct OptPass : public Pass { if (fast_mode) { while (1) { - Pass::call(design, "opt_const" + opt_const_args); - Pass::call(design, "opt_share"); + Pass::call(design, "opt_expr" + opt_expr_args); + Pass::call(design, "opt_merge" + opt_merge_args); design->scratchpad_unset("opt.did_something"); Pass::call(design, "opt_rmdff"); if (design->scratchpad_get_bool("opt.did_something") == false) break; Pass::call(design, "opt_clean" + opt_clean_args); - log_header("Rerunning OPT passes. (Removed registers in this run.)\n"); + log_header(design, "Rerunning OPT passes. (Removed registers in this run.)\n"); } Pass::call(design, "opt_clean" + opt_clean_args); } else { - Pass::call(design, "opt_const" + opt_const_args); - Pass::call(design, "opt_share -nomux"); + Pass::call(design, "opt_expr" + opt_expr_args); + Pass::call(design, "opt_merge -nomux" + opt_merge_args); while (1) { design->scratchpad_unset("opt.did_something"); Pass::call(design, "opt_muxtree"); Pass::call(design, "opt_reduce" + opt_reduce_args); - Pass::call(design, "opt_share"); + Pass::call(design, "opt_merge" + opt_merge_args); Pass::call(design, "opt_rmdff"); Pass::call(design, "opt_clean" + opt_clean_args); - Pass::call(design, "opt_const" + opt_const_args); + Pass::call(design, "opt_expr" + opt_expr_args); if (design->scratchpad_get_bool("opt.did_something") == false) break; - log_header("Rerunning OPT passes. (Maybe there is more to do..)\n"); + log_header(design, "Rerunning OPT passes. (Maybe there is more to do..)\n"); } } - log_header(fast_mode ? "Finished fast OPT passes.\n" : "Finished OPT passes. (There is nothing left to do.)\n"); + design->optimize(); + design->sort(); + design->check(); + + log_header(design, fast_mode ? "Finished fast OPT passes.\n" : "Finished OPT passes. (There is nothing left to do.)\n"); log_pop(); } } OptPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/opt/opt_clean.cc b/passes/opt/opt_clean.cc index 6a7e6051d..6600ffa25 100644 --- a/passes/opt/opt_clean.cc +++ b/passes/opt/opt_clean.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -30,7 +30,55 @@ PRIVATE_NAMESPACE_BEGIN using RTLIL::id2cstr; -CellTypes ct, ct_reg, ct_all; +struct keep_cache_t +{ + Design *design; + dict<Module*, bool> cache; + + void reset(Design *design = nullptr) + { + this->design = design; + cache.clear(); + } + + bool query(Module *module) + { + log_assert(design != nullptr); + + if (module == nullptr) + return false; + + if (cache.count(module)) + return cache.at(module); + + cache[module] = true; + if (!module->get_bool_attribute("\\keep")) { + bool found_keep = false; + for (auto cell : module->cells()) + if (query(cell)) found_keep = true; + cache[module] = found_keep; + } + + return cache[module]; + } + + bool query(Cell *cell) + { + if (cell->type.in("$memwr", "$meminit", "$assert", "$assume")) + return true; + + if (cell->has_keep_attr()) + return true; + + if (cell->module && cell->module->design) + return query(cell->module->design->module(cell->type)); + + return false; + } +}; + +keep_cache_t keep_cache; +CellTypes ct_reg, ct_all; int count_rm_cells, count_rm_wires; void rmunused_module_cells(Module *module, bool verbose) @@ -42,12 +90,12 @@ void rmunused_module_cells(Module *module, bool verbose) for (auto &it : module->cells_) { Cell *cell = it.second; for (auto &it2 : cell->connections()) { - if (!ct.cell_input(cell->type, it2.first)) + if (!ct_all.cell_known(cell->type) || ct_all.cell_output(cell->type, it2.first)) for (auto bit : sigmap(it2.second)) if (bit.wire != nullptr) wire2driver[bit].insert(cell); } - if (cell->type == "$memwr" || cell->type == "$assert" || cell->has_keep_attr()) + if (keep_cache.query(cell)) queue.insert(cell); else unused.insert(cell); @@ -67,7 +115,7 @@ void rmunused_module_cells(Module *module, bool verbose) pool<SigBit> bits; for (auto cell : queue) for (auto &it : cell->connections()) - if (!ct.cell_output(cell->type, it.first)) + if (!ct_all.cell_known(cell->type) || ct_all.cell_input(cell->type, it.first)) for (auto bit : sigmap(it.second)) bits.insert(bit); @@ -108,6 +156,9 @@ bool compare_signals(RTLIL::SigBit &s1, RTLIL::SigBit &s2, SigPool ®s, SigPoo if (w1->port_input != w2->port_input) return w2->port_input; + if ((w1->port_input && w1->port_output) != (w2->port_input && w2->port_output)) + return !(w2->port_input && w2->port_output); + if (w1->name[0] == '\\' && w2->name[0] == '\\') { if (regs.check_any(s1) != regs.check_any(s2)) return regs.check_any(s2); @@ -129,7 +180,7 @@ bool compare_signals(RTLIL::SigBit &s1, RTLIL::SigBit &s2, SigPool ®s, SigPoo if (attrs1 != attrs2) return attrs2 > attrs1; - return w2->name < w1->name; + return strcmp(w2->name.c_str(), w1->name.c_str()) < 0; } bool check_public_name(RTLIL::IdString id) @@ -159,7 +210,7 @@ void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos for (auto &it2 : cell->connections()) connected_signals.add(it2.second); } - + SigMap assign_map(module); pool<RTLIL::SigSpec> direct_sigs; pool<RTLIL::Wire*> direct_wires; @@ -193,7 +244,7 @@ void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos for (auto &it2 : cell->connections_) { assign_map.apply(it2.second); used_signals.add(it2.second); - if (!ct.cell_output(cell->type, it2.first)) + if (!ct_all.cell_output(cell->type, it2.first)) used_signals_nodrivers.add(it2.second); } } @@ -216,7 +267,7 @@ void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos std::vector<RTLIL::Wire*> maybe_del_wires; for (auto wire : module->wires()) { - if ((!purge_mode && check_public_name(wire->name)) || wire->port_id != 0 || wire->get_bool_attribute("\\keep")) { + if ((!purge_mode && check_public_name(wire->name)) || wire->port_id != 0 || wire->get_bool_attribute("\\keep") || wire->attributes.count("\\init")) { RTLIL::SigSpec s1 = RTLIL::SigSpec(wire), s2 = s1; assign_map.apply(s2); if (!used_signals.check_any(s2) && wire->port_id == 0 && !wire->get_bool_attribute("\\keep")) { @@ -296,8 +347,14 @@ void rmunused_module(RTLIL::Module *module, bool purge_mode, bool verbose) module->connect(y, a); delcells.push_back(cell); } - for (auto cell : delcells) + for (auto cell : delcells) { + if (verbose) + log(" removing buffer cell `%s': %s = %s\n", cell->name.c_str(), + log_signal(cell->getPort("\\Y")), log_signal(cell->getPort("\\A"))); module->remove(cell); + } + if (!delcells.empty()) + module->design->scratchpad_set_bool("opt.did_something", true); rmunused_module_cells(module, verbose); rmunused_module_signals(module, purge_mode, verbose); @@ -326,7 +383,7 @@ struct OptCleanPass : public Pass { { bool purge_mode = false; - log_header("Executing OPT_CLEAN pass (remove unused cells and wires).\n"); + log_header(design, "Executing OPT_CLEAN pass (remove unused cells and wires).\n"); log_push(); size_t argidx; @@ -339,26 +396,30 @@ struct OptCleanPass : public Pass { } extra_args(args, argidx, design); - ct.setup_internals(); - ct.setup_internals_mem(); - ct.setup_stdcells(); - ct.setup_stdcells_mem(); + keep_cache.reset(design); ct_reg.setup_internals_mem(); ct_reg.setup_stdcells_mem(); + ct_all.setup(design); + for (auto module : design->selected_whole_modules_warn()) { if (module->has_processes_warn()) continue; rmunused_module(module, purge_mode, true); } - ct.clear(); + design->optimize(); + design->sort(); + design->check(); + + keep_cache.reset(); ct_reg.clear(); + ct_all.clear(); log_pop(); } } OptCleanPass; - + struct CleanPass : public Pass { CleanPass() : Pass("clean", "remove unused cells and wires") { } virtual void help() @@ -391,10 +452,7 @@ struct CleanPass : public Pass { if (argidx < args.size()) extra_args(args, argidx, design); - ct.setup_internals(); - ct.setup_internals_mem(); - ct.setup_stdcells(); - ct.setup_stdcells_mem(); + keep_cache.reset(design); ct_reg.setup_internals_mem(); ct_reg.setup_stdcells_mem(); @@ -404,13 +462,10 @@ struct CleanPass : public Pass { count_rm_cells = 0; count_rm_wires = 0; - for (auto mod : design->selected_whole_modules()) { - if (mod->has_processes()) + for (auto module : design->selected_whole_modules()) { + if (module->has_processes()) continue; - do { - design->scratchpad_unset("opt.did_something"); - rmunused_module(mod, purge_mode, false); - } while (design->scratchpad_get_bool("opt.did_something")); + rmunused_module(module, purge_mode, false); } if (count_rm_cells > 0 || count_rm_wires > 0) @@ -420,10 +475,10 @@ struct CleanPass : public Pass { design->sort(); design->check(); - ct.clear(); + keep_cache.reset(); ct_reg.clear(); ct_all.clear(); } } CleanPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/opt/opt_const.cc b/passes/opt/opt_expr.cc index 1758a34fa..b62eae285 100644 --- a/passes/opt/opt_const.cc +++ b/passes/opt/opt_expr.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -179,14 +179,87 @@ bool group_cell_inputs(RTLIL::Module *module, RTLIL::Cell *cell, bool commutativ log("\n"); } - cover_list("opt.opt_const.fine.group", "$not", "$pos", "$and", "$or", "$xor", "$xnor", cell->type.str()); + cover_list("opt.opt_expr.fine.group", "$not", "$pos", "$and", "$or", "$xor", "$xnor", cell->type.str()); module->remove(cell); did_something = true; return true; } -void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool consume_x, bool mux_undef, bool mux_bool, bool do_fine, bool keepdc) +void handle_polarity_inv(Cell *cell, IdString port, IdString param, const SigMap &assign_map, const dict<RTLIL::SigSpec, RTLIL::SigSpec> &invert_map) +{ + SigSpec sig = assign_map(cell->getPort(port)); + if (invert_map.count(sig)) { + log("Inverting %s of %s cell `%s' in module `%s': %s -> %s\n", + log_id(port), log_id(cell->type), log_id(cell), log_id(cell->module), + log_signal(sig), log_signal(invert_map.at(sig))); + cell->setPort(port, (invert_map.at(sig))); + cell->setParam(param, !cell->getParam(param).as_bool()); + } +} + +void handle_clkpol_celltype_swap(Cell *cell, string type1, string type2, IdString port, const SigMap &assign_map, const dict<RTLIL::SigSpec, RTLIL::SigSpec> &invert_map) +{ + log_assert(GetSize(type1) == GetSize(type2)); + string cell_type = cell->type.str(); + + if (GetSize(type1) != GetSize(cell_type)) + return; + + for (int i = 0; i < GetSize(type1); i++) { + log_assert((type1[i] == '?') == (type2[i] == '?')); + if (type1[i] == '?') { + if (cell_type[i] != '0' && cell_type[i] != '1' && cell_type[i] != 'N' && cell_type[i] != 'P') + return; + type1[i] = cell_type[i]; + type2[i] = cell_type[i]; + } + } + + if (cell->type.in(type1, type2)) { + SigSpec sig = assign_map(cell->getPort(port)); + if (invert_map.count(sig)) { + log("Inverting %s of %s cell `%s' in module `%s': %s -> %s\n", + log_id(port), log_id(cell->type), log_id(cell), log_id(cell->module), + log_signal(sig), log_signal(invert_map.at(sig))); + cell->setPort(port, (invert_map.at(sig))); + cell->type = cell->type == type1 ? type2 : type1; + } + } +} + +bool is_one_or_minus_one(const Const &value, bool is_signed, bool &is_negative) +{ + bool all_bits_one = true; + bool last_bit_one = true; + + if (GetSize(value.bits) < 1) + return false; + + if (GetSize(value.bits) == 1) { + if (value.bits[0] != State::S1) + return false; + if (is_signed) + is_negative = true; + return true; + } + + for (int i = 0; i < GetSize(value.bits); i++) { + if (value.bits[i] != State::S1) + all_bits_one = false; + if (value.bits[i] != (i ? State::S0 : State::S1)) + last_bit_one = false; + } + + if (all_bits_one && is_signed) { + is_negative = true; + return true; + } + + return last_bit_one; +} + +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 clkinv) { if (!design->selected(module)) return; @@ -207,6 +280,8 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if ((cell->type == "$_NOT_" || cell->type == "$not" || cell->type == "$logic_not") && cell->getPort("\\A").size() == 1 && cell->getPort("\\Y").size() == 1) invert_map[assign_map(cell->getPort("\\Y"))] = assign_map(cell->getPort("\\A")); + if ((cell->type == "$mux" || cell->type == "$_MUX_") && cell->getPort("\\A") == SigSpec(State::S1) && cell->getPort("\\B") == SigSpec(State::S0)) + invert_map[assign_map(cell->getPort("\\Y"))] = assign_map(cell->getPort("\\S")); if (ct_combinational.cell_known(cell->type)) for (auto &conn : cell->connections()) { RTLIL::SigSpec sig = assign_map(conn.second); @@ -229,9 +304,106 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons for (auto cell : cells.sorted) { -#define ACTION_DO(_p_, _s_) do { cover("opt.opt_const.action_" S__LINE__); replace_cell(assign_map, module, cell, input.as_string(), _p_, _s_); goto next_cell; } while (0) +#define ACTION_DO(_p_, _s_) do { cover("opt.opt_expr.action_" S__LINE__); replace_cell(assign_map, module, cell, input.as_string(), _p_, _s_); goto next_cell; } while (0) #define ACTION_DO_Y(_v_) ACTION_DO("\\Y", RTLIL::SigSpec(RTLIL::State::S ## _v_)) + if (clkinv) + { + if (cell->type.in("$dff", "$dffe", "$dffsr", "$adff", "$fsm", "$memrd", "$memwr")) + handle_polarity_inv(cell, "\\CLK", "\\CLK_POLARITY", assign_map, invert_map); + + if (cell->type.in("$sr", "$dffsr", "$dlatchsr")) { + handle_polarity_inv(cell, "\\SET", "\\SET_POLARITY", assign_map, invert_map); + handle_polarity_inv(cell, "\\CLR", "\\CLR_POLARITY", assign_map, invert_map); + } + + if (cell->type.in("$dffe", "$dlatch", "$dlatchsr")) + handle_polarity_inv(cell, "\\EN", "\\EN_POLARITY", assign_map, invert_map); + + handle_clkpol_celltype_swap(cell, "$_SR_N?_", "$_SR_P?_", "\\S", assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_SR_?N_", "$_SR_?P_", "\\R", assign_map, invert_map); + + handle_clkpol_celltype_swap(cell, "$_DFF_N_", "$_DFF_P_", "\\C", assign_map, invert_map); + + handle_clkpol_celltype_swap(cell, "$_DFFE_N?_", "$_DFFE_P?_", "\\C", assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_DFFE_?N_", "$_DFFE_?P_", "\\E", assign_map, invert_map); + + handle_clkpol_celltype_swap(cell, "$_DFF_N??_", "$_DFF_P??_", "\\C", assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_DFF_?N?_", "$_DFF_?P?_", "\\R", assign_map, invert_map); + + handle_clkpol_celltype_swap(cell, "$_DFFSR_N??_", "$_DFFSR_P??_", "\\C", assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_DFFSR_?N?_", "$_DFFSR_?P?_", "\\S", assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_DFFSR_??N_", "$_DFFSR_??P_", "\\R", assign_map, invert_map); + + handle_clkpol_celltype_swap(cell, "$_DLATCH_N_", "$_DLATCH_P_", "\\E", assign_map, invert_map); + + handle_clkpol_celltype_swap(cell, "$_DLATCHSR_N??_", "$_DLATCHSR_P??_", "\\E", assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_DLATCHSR_?N?_", "$_DLATCHSR_?P?_", "\\S", assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_DLATCHSR_??N_", "$_DLATCHSR_??P_", "\\R", assign_map, invert_map); + } + + bool detect_const_and = false; + bool detect_const_or = false; + + if (cell->type.in("$reduce_and", "$_AND_")) + detect_const_and = true; + + if (cell->type.in("$and", "$logic_and") && GetSize(cell->getPort("\\A")) == 1 && GetSize(cell->getPort("\\B")) == 1) + detect_const_and = true; + + if (cell->type.in("$reduce_or", "$reduce_bool", "$_OR_")) + detect_const_or = true; + + if (cell->type.in("$or", "$logic_or") && GetSize(cell->getPort("\\A")) == 1 && GetSize(cell->getPort("\\B")) == 1) + detect_const_or = true; + + if (detect_const_and || detect_const_or) + { + pool<SigBit> input_bits = assign_map(cell->getPort("\\A")).to_sigbit_pool(); + bool found_zero = false, found_one = false, found_inv = false; + + if (cell->hasPort("\\B")) { + vector<SigBit> more_bits = assign_map(cell->getPort("\\B")).to_sigbit_vector(); + input_bits.insert(more_bits.begin(), more_bits.end()); + } + + for (auto bit : input_bits) { + if (bit == State::S0) + found_zero = true; + if (bit == State::S1) + found_one = true; + if (invert_map.count(bit) && input_bits.count(invert_map.at(bit))) + found_inv = true; + } + + if (detect_const_and && (found_zero || found_inv)) { + cover("opt.opt_expr.const_and"); + replace_cell(assign_map, module, cell, "const_and", "\\Y", RTLIL::State::S0); + goto next_cell; + } + + if (detect_const_or && (found_one || found_inv)) { + cover("opt.opt_expr.const_or"); + replace_cell(assign_map, module, cell, "const_or", "\\Y", RTLIL::State::S1); + goto next_cell; + } + } + + if (cell->type.in("$reduce_and", "$reduce_or", "$reduce_bool", "$reduce_xor", "$reduce_xnor", "$neg") && + GetSize(cell->getPort("\\A")) == 1 && GetSize(cell->getPort("\\Y")) == 1) + { + if (cell->type == "$reduce_xnor") { + cover("opt.opt_expr.reduce_xnor_not"); + log("Replacing %s cell `%s' in module `%s' with $not cell.\n", + log_id(cell->type), log_id(cell->name), log_id(module)); + cell->type = "$not"; + } else { + cover("opt.opt_expr.unary_buffer"); + replace_cell(assign_map, module, cell, "unary_buffer", "\\Y", cell->getPort("\\A")); + } + goto next_cell; + } + if (do_fine) { if (cell->type == "$not" || cell->type == "$pos" || @@ -256,7 +428,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } if (new_a != RTLIL::State::Sm && RTLIL::SigSpec(new_a) != sig_a) { - cover("opt.opt_const.fine.$reduce_and"); + cover("opt.opt_expr.fine.$reduce_and"); log("Replacing port A of %s cell `%s' in module `%s' with constant driver: %s -> %s\n", cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_a), log_signal(new_a)); cell->setPort("\\A", sig_a = new_a); @@ -282,7 +454,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } if (new_a != RTLIL::State::Sm && RTLIL::SigSpec(new_a) != sig_a) { - cover_list("opt.opt_const.fine.A", "$logic_not", "$logic_and", "$logic_or", "$reduce_or", "$reduce_bool", cell->type.str()); + cover_list("opt.opt_expr.fine.A", "$logic_not", "$logic_and", "$logic_or", "$reduce_or", "$reduce_bool", cell->type.str()); log("Replacing port A of %s cell `%s' in module `%s' with constant driver: %s -> %s\n", cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_a), log_signal(new_a)); cell->setPort("\\A", sig_a = new_a); @@ -308,7 +480,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } if (new_b != RTLIL::State::Sm && RTLIL::SigSpec(new_b) != sig_b) { - cover_list("opt.opt_const.fine.B", "$logic_and", "$logic_or", cell->type.str()); + cover_list("opt.opt_expr.fine.B", "$logic_and", "$logic_or", cell->type.str()); log("Replacing port B of %s cell `%s' in module `%s' with constant driver: %s -> %s\n", cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_b), log_signal(new_b)); cell->setPort("\\B", sig_b = new_b); @@ -318,18 +490,6 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } } - if (cell->type == "$logic_or" && (assign_map(cell->getPort("\\A")) == RTLIL::State::S1 || assign_map(cell->getPort("\\B")) == RTLIL::State::S1)) { - cover("opt.opt_const.one_high"); - replace_cell(assign_map, module, cell, "one high", "\\Y", RTLIL::State::S1); - goto next_cell; - } - - if (cell->type == "$logic_and" && (assign_map(cell->getPort("\\A")) == RTLIL::State::S0 || assign_map(cell->getPort("\\B")) == RTLIL::State::S0)) { - cover("opt.opt_const.one_low"); - replace_cell(assign_map, module, cell, "one low", "\\Y", RTLIL::State::S0); - goto next_cell; - } - if (cell->type == "$reduce_xor" || cell->type == "$reduce_xnor" || cell->type == "$shift" || cell->type == "$shiftx" || cell->type == "$shl" || cell->type == "$shr" || cell->type == "$sshl" || cell->type == "$sshr" || cell->type == "$lt" || cell->type == "$le" || cell->type == "$ge" || cell->type == "$gt" || @@ -352,7 +512,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if (0) { found_the_x_bit: - cover_list("opt.opt_const.xbit", "$reduce_xor", "$reduce_xnor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", + cover_list("opt.opt_expr.xbit", "$reduce_xor", "$reduce_xnor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", "$lt", "$le", "$ge", "$gt", "$neg", "$add", "$sub", "$mul", "$div", "$mod", "$pow", cell->type.str()); if (cell->type == "$reduce_xor" || cell->type == "$reduce_xnor" || cell->type == "$lt" || cell->type == "$le" || cell->type == "$ge" || cell->type == "$gt") @@ -365,13 +525,13 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if ((cell->type == "$_NOT_" || cell->type == "$not" || cell->type == "$logic_not") && cell->getPort("\\Y").size() == 1 && invert_map.count(assign_map(cell->getPort("\\A"))) != 0) { - cover_list("opt.opt_const.invert.double", "$_NOT_", "$not", "$logic_not", cell->type.str()); + cover_list("opt.opt_expr.invert.double", "$_NOT_", "$not", "$logic_not", cell->type.str()); replace_cell(assign_map, module, cell, "double_invert", "\\Y", invert_map.at(assign_map(cell->getPort("\\A")))); goto next_cell; } if ((cell->type == "$_MUX_" || cell->type == "$mux") && invert_map.count(assign_map(cell->getPort("\\S"))) != 0) { - cover_list("opt.opt_const.invert.muxsel", "$_MUX_", "$mux", cell->type.str()); + cover_list("opt.opt_expr.invert.muxsel", "$_MUX_", "$mux", cell->type.str()); log("Optimizing away select inverter for %s cell `%s' in module `%s'.\n", log_id(cell->type), log_id(cell), log_id(module)); RTLIL::SigSpec tmp = cell->getPort("\\A"); cell->setPort("\\A", cell->getPort("\\B")); @@ -454,7 +614,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if (input.match(" 1")) ACTION_DO("\\Y", input.extract(1, 1)); if (input.match("01 ")) ACTION_DO("\\Y", input.extract(0, 1)); if (input.match("10 ")) { - cover("opt.opt_const.mux_to_inv"); + cover("opt.opt_expr.mux_to_inv"); cell->type = "$_NOT_"; cell->setPort("\\A", input.extract(0, 1)); cell->unsetPort("\\B"); @@ -479,7 +639,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons RTLIL::SigSpec b = cell->getPort("\\B"); if (cell->parameters["\\A_WIDTH"].as_int() != cell->parameters["\\B_WIDTH"].as_int()) { - int width = std::max(cell->parameters["\\A_WIDTH"].as_int(), cell->parameters["\\B_WIDTH"].as_int()); + int width = max(cell->parameters["\\A_WIDTH"].as_int(), cell->parameters["\\B_WIDTH"].as_int()); a.extend_u0(width, cell->parameters["\\A_SIGNED"].as_bool() && cell->parameters["\\B_SIGNED"].as_bool()); b.extend_u0(width, cell->parameters["\\A_SIGNED"].as_bool() && cell->parameters["\\B_SIGNED"].as_bool()); } @@ -489,7 +649,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons log_assert(GetSize(a) == GetSize(b)); for (int i = 0; i < GetSize(a); i++) { if (a[i].wire == NULL && b[i].wire == NULL && a[i] != b[i] && a[i].data <= RTLIL::State::S1 && b[i].data <= RTLIL::State::S1) { - cover_list("opt.opt_const.eqneq.isneq", "$eq", "$ne", "$eqx", "$nex", cell->type.str()); + cover_list("opt.opt_expr.eqneq.isneq", "$eq", "$ne", "$eqx", "$nex", cell->type.str()); RTLIL::SigSpec new_y = RTLIL::SigSpec((cell->type == "$eq" || cell->type == "$eqx") ? RTLIL::State::S0 : RTLIL::State::S1); new_y.extend_u0(cell->parameters["\\Y_WIDTH"].as_int(), false); replace_cell(assign_map, module, cell, "isneq", "\\Y", new_y); @@ -502,7 +662,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } if (new_a.size() == 0) { - cover_list("opt.opt_const.eqneq.empty", "$eq", "$ne", "$eqx", "$nex", cell->type.str()); + cover_list("opt.opt_expr.eqneq.empty", "$eq", "$ne", "$eqx", "$nex", cell->type.str()); RTLIL::SigSpec new_y = RTLIL::SigSpec((cell->type == "$eq" || cell->type == "$eqx") ? RTLIL::State::S1 : RTLIL::State::S0); new_y.extend_u0(cell->parameters["\\Y_WIDTH"].as_int(), false); replace_cell(assign_map, module, cell, "empty", "\\Y", new_y); @@ -510,7 +670,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } if (new_a.size() < a.size() || new_b.size() < b.size()) { - cover_list("opt.opt_const.eqneq.resize", "$eq", "$ne", "$eqx", "$nex", cell->type.str()); + cover_list("opt.opt_expr.eqneq.resize", "$eq", "$ne", "$eqx", "$nex", cell->type.str()); cell->setPort("\\A", new_a); cell->setPort("\\B", new_b); cell->parameters["\\A_WIDTH"] = new_a.size(); @@ -525,7 +685,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons RTLIL::SigSpec b = assign_map(cell->getPort("\\B")); if (a.is_fully_const() && !b.is_fully_const()) { - cover_list("opt.opt_const.eqneq.swapconst", "$eq", "$ne", cell->type.str()); + cover_list("opt.opt_expr.eqneq.swapconst", "$eq", "$ne", cell->type.str()); cell->setPort("\\A", b); cell->setPort("\\B", a); std::swap(a, b); @@ -536,7 +696,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons RTLIL::SigSpec input = b; ACTION_DO("\\Y", cell->getPort("\\A")); } else { - cover_list("opt.opt_const.eqneq.isnot", "$eq", "$ne", cell->type.str()); + cover_list("opt.opt_expr.eqneq.isnot", "$eq", "$ne", cell->type.str()); log("Replacing %s cell `%s' in module `%s' with inverter.\n", log_id(cell->type), log_id(cell), log_id(module)); cell->type = "$not"; cell->parameters.erase("\\B_WIDTH"); @@ -548,6 +708,25 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } } + if ((cell->type == "$eq" || cell->type == "$ne") && + (assign_map(cell->getPort("\\A")).is_fully_zero() || assign_map(cell->getPort("\\B")).is_fully_zero())) + { + cover_list("opt.opt_expr.eqneq.cmpzero", "$eq", "$ne", cell->type.str()); + log("Replacing %s cell `%s' in module `%s' with %s.\n", log_id(cell->type), log_id(cell), + log_id(module), "$eq" ? "$logic_not" : "$reduce_bool"); + cell->type = cell->type == "$eq" ? "$logic_not" : "$reduce_bool"; + if (assign_map(cell->getPort("\\A")).is_fully_zero()) { + cell->setPort("\\A", cell->getPort("\\B")); + cell->setParam("\\A_SIGNED", cell->getParam("\\B_SIGNED")); + cell->setParam("\\A_WIDTH", cell->getParam("\\B_WIDTH")); + } + cell->unsetPort("\\B"); + cell->unsetParam("\\B_SIGNED"); + cell->unsetParam("\\B_WIDTH"); + did_something = true; + goto next_cell; + } + if (cell->type.in("$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx") && assign_map(cell->getPort("\\B")).is_fully_const()) { bool sign_ext = cell->type == "$sshr" && cell->getParam("\\A_SIGNED").as_bool(); @@ -570,7 +749,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons sig_y[i] = sig_a[GetSize(sig_a)-1]; } - cover_list("opt.opt_const.constshift", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", cell->type.str()); + cover_list("opt.opt_expr.constshift", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", cell->type.str()); log("Replacing %s cell `%s' (B=%s, SHR=%d) in module `%s' with fixed wiring: %s\n", log_id(cell->type), log_id(cell), log_signal(assign_map(cell->getPort("\\B"))), shift_bits, log_id(module), log_signal(sig_y)); @@ -586,6 +765,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons { bool identity_wrt_a = false; bool identity_wrt_b = false; + bool arith_inverse = false; if (cell->type == "$add" || cell->type == "$sub" || cell->type == "$or" || cell->type == "$xor") { @@ -612,10 +792,10 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons RTLIL::SigSpec a = assign_map(cell->getPort("\\A")); RTLIL::SigSpec b = assign_map(cell->getPort("\\B")); - if (a.is_fully_const() && a.size() <= 32 && a.as_int() == 1) + if (a.is_fully_const() && is_one_or_minus_one(a.as_const(), cell->getParam("\\A_SIGNED").as_bool(), arith_inverse)) identity_wrt_b = true; - - if (b.is_fully_const() && b.size() <= 32 && b.as_int() == 1) + else + if (b.is_fully_const() && is_one_or_minus_one(b.as_const(), cell->getParam("\\B_SIGNED").as_bool(), arith_inverse)) identity_wrt_a = true; } @@ -630,9 +810,9 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if (identity_wrt_a || identity_wrt_b) { if (identity_wrt_a) - cover_list("opt.opt_const.identwrt.a", "$add", "$sub", "$or", "$xor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", "$mul", "$div", cell->type.str()); + cover_list("opt.opt_expr.identwrt.a", "$add", "$sub", "$or", "$xor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", "$mul", "$div", cell->type.str()); if (identity_wrt_b) - cover_list("opt.opt_const.identwrt.b", "$add", "$sub", "$or", "$xor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", "$mul", "$div", cell->type.str()); + cover_list("opt.opt_expr.identwrt.b", "$add", "$sub", "$or", "$xor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", "$mul", "$div", cell->type.str()); log("Replacing %s cell `%s' in module `%s' with identity for port %c.\n", cell->type.c_str(), cell->name.c_str(), module->name.c_str(), identity_wrt_a ? 'A' : 'B'); @@ -643,7 +823,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons cell->parameters.at("\\A_SIGNED") = cell->parameters.at("\\B_SIGNED"); } - cell->type = "$pos"; + cell->type = arith_inverse ? "$neg" : "$pos"; cell->unsetPort("\\B"); cell->parameters.erase("\\B_WIDTH"); cell->parameters.erase("\\B_SIGNED"); @@ -656,14 +836,14 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if (mux_bool && (cell->type == "$mux" || cell->type == "$_MUX_") && cell->getPort("\\A") == RTLIL::SigSpec(0, 1) && cell->getPort("\\B") == RTLIL::SigSpec(1, 1)) { - cover_list("opt.opt_const.mux_bool", "$mux", "$_MUX_", cell->type.str()); + cover_list("opt.opt_expr.mux_bool", "$mux", "$_MUX_", cell->type.str()); replace_cell(assign_map, module, cell, "mux_bool", "\\Y", cell->getPort("\\S")); goto next_cell; } if (mux_bool && (cell->type == "$mux" || cell->type == "$_MUX_") && cell->getPort("\\A") == RTLIL::SigSpec(1, 1) && cell->getPort("\\B") == RTLIL::SigSpec(0, 1)) { - cover_list("opt.opt_const.mux_invert", "$mux", "$_MUX_", cell->type.str()); + cover_list("opt.opt_expr.mux_invert", "$mux", "$_MUX_", cell->type.str()); log("Replacing %s cell `%s' in module `%s' with inverter.\n", log_id(cell->type), log_id(cell), log_id(module)); cell->setPort("\\A", cell->getPort("\\S")); cell->unsetPort("\\B"); @@ -682,7 +862,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } if (consume_x && mux_bool && (cell->type == "$mux" || cell->type == "$_MUX_") && cell->getPort("\\A") == RTLIL::SigSpec(0, 1)) { - cover_list("opt.opt_const.mux_and", "$mux", "$_MUX_", cell->type.str()); + cover_list("opt.opt_expr.mux_and", "$mux", "$_MUX_", cell->type.str()); log("Replacing %s cell `%s' in module `%s' with and-gate.\n", log_id(cell->type), log_id(cell), log_id(module)); cell->setPort("\\A", cell->getPort("\\S")); cell->unsetPort("\\S"); @@ -702,7 +882,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } if (consume_x && mux_bool && (cell->type == "$mux" || cell->type == "$_MUX_") && cell->getPort("\\B") == RTLIL::SigSpec(1, 1)) { - cover_list("opt.opt_const.mux_or", "$mux", "$_MUX_", cell->type.str()); + cover_list("opt.opt_expr.mux_or", "$mux", "$_MUX_", cell->type.str()); log("Replacing %s cell `%s' in module `%s' with or-gate.\n", log_id(cell->type), log_id(cell), log_id(module)); cell->setPort("\\B", cell->getPort("\\S")); cell->unsetPort("\\S"); @@ -726,7 +906,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons int width = cell->getPort("\\A").size(); if ((cell->getPort("\\A").is_fully_undef() && cell->getPort("\\B").is_fully_undef()) || cell->getPort("\\S").is_fully_undef()) { - cover_list("opt.opt_const.mux_undef", "$mux", "$pmux", cell->type.str()); + cover_list("opt.opt_expr.mux_undef", "$mux", "$pmux", cell->type.str()); replace_cell(assign_map, module, cell, "mux_undef", "\\Y", cell->getPort("\\A")); goto next_cell; } @@ -745,17 +925,17 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons new_s = new_s.extract(0, new_s.size()-1); } if (new_s.size() == 0) { - cover_list("opt.opt_const.mux_empty", "$mux", "$pmux", cell->type.str()); + cover_list("opt.opt_expr.mux_empty", "$mux", "$pmux", cell->type.str()); replace_cell(assign_map, module, cell, "mux_empty", "\\Y", new_a); goto next_cell; } if (new_a == RTLIL::SigSpec(RTLIL::State::S0) && new_b == RTLIL::SigSpec(RTLIL::State::S1)) { - cover_list("opt.opt_const.mux_sel01", "$mux", "$pmux", cell->type.str()); + cover_list("opt.opt_expr.mux_sel01", "$mux", "$pmux", cell->type.str()); replace_cell(assign_map, module, cell, "mux_sel01", "\\Y", new_s); goto next_cell; } if (cell->getPort("\\S").size() != new_s.size()) { - cover_list("opt.opt_const.mux_reduce", "$mux", "$pmux", cell->type.str()); + cover_list("opt.opt_expr.mux_reduce", "$mux", "$pmux", cell->type.str()); log("Optimized away %d select inputs of %s cell `%s' in module `%s'.\n", GetSize(cell->getPort("\\S")) - GetSize(new_s), log_id(cell->type), log_id(cell), log_id(module)); cell->setPort("\\A", new_a); @@ -781,7 +961,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons RTLIL::SigSpec y(RTLIL::const_ ## _t(a.as_const(), dummy_arg, \ cell->parameters["\\A_SIGNED"].as_bool(), false, \ cell->parameters["\\Y_WIDTH"].as_int())); \ - cover("opt.opt_const.const.$" #_t); \ + cover("opt.opt_expr.const.$" #_t); \ replace_cell(assign_map, module, cell, stringf("%s", log_signal(a)), "\\Y", y); \ goto next_cell; \ } \ @@ -796,7 +976,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons cell->parameters["\\A_SIGNED"].as_bool(), \ cell->parameters["\\B_SIGNED"].as_bool(), \ cell->parameters["\\Y_WIDTH"].as_int())); \ - cover("opt.opt_const.const.$" #_t); \ + cover("opt.opt_expr.const.$" #_t); \ replace_cell(assign_map, module, cell, stringf("%s, %s", log_signal(a), log_signal(b)), "\\Y", y); \ goto next_cell; \ } \ @@ -872,7 +1052,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if (a_val == 0) { - cover("opt.opt_const.mul_shift.zero"); + cover("opt.opt_expr.mul_shift.zero"); log("Replacing multiply-by-zero cell `%s' in module `%s' with zero-driver.\n", cell->name.c_str(), module->name.c_str()); @@ -888,9 +1068,9 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if (a_val == (1 << i)) { if (swapped_ab) - cover("opt.opt_const.mul_shift.swapped"); + cover("opt.opt_expr.mul_shift.swapped"); else - cover("opt.opt_const.mul_shift.unswapped"); + cover("opt.opt_expr.mul_shift.unswapped"); log("Replacing multiply-by-%d cell `%s' in module `%s' with shift-by-%d.\n", a_val, cell->name.c_str(), module->name.c_str(), i); @@ -918,6 +1098,75 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } } + if (!keepdc && cell->type.in("$div", "$mod")) + { + bool b_signed = cell->parameters["\\B_SIGNED"].as_bool(); + SigSpec sig_b = assign_map(cell->getPort("\\B")); + SigSpec sig_y = assign_map(cell->getPort("\\Y")); + + if (sig_b.is_fully_def() && sig_b.size() <= 32) + { + int b_val = sig_b.as_int(); + + if (b_val == 0) + { + cover("opt.opt_expr.divmod_zero"); + + log("Replacing divide-by-zero cell `%s' in module `%s' with undef-driver.\n", + cell->name.c_str(), module->name.c_str()); + + module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(State::Sx, sig_y.size()))); + module->remove(cell); + + did_something = true; + goto next_cell; + } + + for (int i = 1; i < (b_signed ? sig_b.size()-1 : sig_b.size()); i++) + if (b_val == (1 << i)) + { + if (cell->type == "$div") + { + cover("opt.opt_expr.div_shift"); + + log("Replacing divide-by-%d cell `%s' in module `%s' with shift-by-%d.\n", + b_val, cell->name.c_str(), module->name.c_str(), i); + + std::vector<RTLIL::SigBit> new_b = RTLIL::SigSpec(i, 6); + + while (GetSize(new_b) > 1 && new_b.back() == RTLIL::State::S0) + new_b.pop_back(); + + cell->type = "$shr"; + cell->parameters["\\B_WIDTH"] = GetSize(new_b); + cell->parameters["\\B_SIGNED"] = false; + cell->setPort("\\B", new_b); + cell->check(); + } + else + { + cover("opt.opt_expr.mod_mask"); + + log("Replacing modulo-by-%d cell `%s' in module `%s' with bitmask.\n", + b_val, cell->name.c_str(), module->name.c_str()); + + std::vector<RTLIL::SigBit> new_b = RTLIL::SigSpec(State::S1, i); + + if (b_signed) + new_b.push_back(State::S0); + + cell->type = "$and"; + cell->parameters["\\B_WIDTH"] = GetSize(new_b); + cell->setPort("\\B", new_b); + cell->check(); + } + + did_something = true; + goto next_cell; + } + } + } + next_cell:; #undef ACTION_DO #undef ACTION_DO_Y @@ -926,15 +1175,16 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } } -struct OptConstPass : public Pass { - OptConstPass() : Pass("opt_const", "perform const folding") { } +struct OptExprPass : public Pass { + OptExprPass() : Pass("opt_expr", "perform const folding and simple expression rewriting") { } virtual void help() { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" opt_const [options] [selection]\n"); + log(" opt_expr [options] [selection]\n"); log("\n"); log("This pass performs const folding on internal cell types with constant inputs.\n"); + log("It also performs some simple expression rewritring.\n"); log("\n"); log(" -mux_undef\n"); log(" remove 'undef' inputs from $mux, $pmux and $_MUX_ cells\n"); @@ -945,6 +1195,9 @@ struct OptConstPass : public Pass { log(" -undriven\n"); log(" replace undriven nets with undef (x) constants\n"); log("\n"); + log(" -clkinv\n"); + log(" optimize clock inverters by changing FF types\n"); + log("\n"); log(" -fine\n"); log(" perform fine-grain optimizations\n"); log("\n"); @@ -963,10 +1216,11 @@ struct OptConstPass : public Pass { bool mux_undef = false; bool mux_bool = false; bool undriven = false; + bool clkinv = false; bool do_fine = false; bool keepdc = false; - log_header("Executing OPT_CONST pass (perform const folding).\n"); + log_header(design, "Executing OPT_EXPR pass (perform const folding).\n"); log_push(); size_t argidx; @@ -983,6 +1237,10 @@ struct OptConstPass : public Pass { undriven = true; continue; } + if (args[argidx] == "-clkinv") { + clkinv = true; + continue; + } if (args[argidx] == "-fine") { do_fine = true; continue; @@ -1010,16 +1268,16 @@ struct OptConstPass : public Pass { do { do { did_something = false; - replace_const_cells(design, module, false, mux_undef, mux_bool, do_fine, keepdc); + replace_const_cells(design, module, false, mux_undef, mux_bool, do_fine, keepdc, clkinv); if (did_something) design->scratchpad_set_bool("opt.did_something", true); } while (did_something); - replace_const_cells(design, module, true, mux_undef, mux_bool, do_fine, keepdc); + replace_const_cells(design, module, true, mux_undef, mux_bool, do_fine, keepdc, clkinv); } while (did_something); } log_pop(); } -} OptConstPass; +} OptExprPass; PRIVATE_NAMESPACE_END diff --git a/passes/opt/opt_share.cc b/passes/opt/opt_merge.cc index cce97d65b..97989d271 100644 --- a/passes/opt/opt_share.cc +++ b/passes/opt/opt_merge.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -31,12 +31,13 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -struct OptShareWorker +struct OptMergeWorker { RTLIL::Design *design; RTLIL::Module *module; SigMap assign_map; SigMap dff_init_map; + bool mode_share_all; CellTypes ct; int total_count; @@ -44,6 +45,29 @@ struct OptShareWorker dict<const RTLIL::Cell*, std::string> cell_hash_cache; #endif + static void sort_pmux_conn(dict<RTLIL::IdString, RTLIL::SigSpec> &conn) + { + SigSpec sig_s = conn.at("\\S"); + SigSpec sig_b = conn.at("\\B"); + + int s_width = GetSize(sig_s); + int width = GetSize(sig_b) / s_width; + + vector<pair<SigBit, SigSpec>> sb_pairs; + for (int i = 0; i < s_width; i++) + sb_pairs.push_back(pair<SigBit, SigSpec>(sig_s[i], sig_b.extract(i*width, width))); + + std::sort(sb_pairs.begin(), sb_pairs.end()); + + conn["\\S"] = SigSpec(); + conn["\\B"] = SigSpec(); + + for (auto &it : sb_pairs) { + conn["\\S"].append(it.first); + conn["\\B"].append(it.second); + } + } + #ifdef USE_CELL_HASH_CACHE std::string int_to_hash_string(unsigned int v) { @@ -90,25 +114,40 @@ struct OptShareWorker assign_map.apply(alt_conn.at("\\A")); alt_conn.at("\\A").sort_and_unify(); conn = &alt_conn; + } else + if (cell->type == "$pmux") { + alt_conn = *conn; + assign_map.apply(alt_conn.at("\\A")); + assign_map.apply(alt_conn.at("\\B")); + assign_map.apply(alt_conn.at("\\S")); + sort_pmux_conn(alt_conn); + conn = &alt_conn; } + vector<string> hash_conn_strings; + for (auto &it : *conn) { - if (ct.cell_output(cell->type, it.first)) + if (cell->output(it.first)) continue; RTLIL::SigSpec sig = it.second; assign_map.apply(sig); - hash_string += "C " + it.first.str() + "="; + string s = "C " + it.first.str() + "="; for (auto &chunk : sig.chunks()) { if (chunk.wire) - hash_string += "{" + chunk.wire->name.str() + " " + + s += "{" + chunk.wire->name.str() + " " + int_to_hash_string(chunk.offset) + " " + int_to_hash_string(chunk.width) + "}"; else - hash_string += RTLIL::Const(chunk.data).as_string(); + s += RTLIL::Const(chunk.data).as_string(); } - hash_string += "\n"; + hash_conn_strings.push_back(s + "\n"); } + std::sort(hash_conn_strings.begin(), hash_conn_strings.end()); + + for (auto it : hash_conn_strings) + hash_string += it; + cell_hash_cache[cell] = sha1(hash_string); return cell_hash_cache[cell]; } @@ -137,14 +176,14 @@ struct OptShareWorker dict<RTLIL::IdString, RTLIL::SigSpec> conn2 = cell2->connections(); for (auto &it : conn1) { - if (ct.cell_output(cell1->type, it.first)) + if (cell1->output(it.first)) it.second = RTLIL::SigSpec(); else assign_map.apply(it.second); } for (auto &it : conn2) { - if (ct.cell_output(cell2->type, it.first)) + if (cell2->output(it.first)) it.second = RTLIL::SigSpec(); else assign_map.apply(it.second); @@ -170,6 +209,10 @@ struct OptShareWorker if (cell1->type == "$reduce_and" || cell1->type == "$reduce_or" || cell1->type == "$reduce_bool") { conn1["\\A"].sort_and_unify(); conn2["\\A"].sort_and_unify(); + } else + if (cell1->type == "$pmux") { + sort_pmux_conn(conn1); + sort_pmux_conn(conn2); } if (conn1 != conn2) { @@ -197,7 +240,7 @@ struct OptShareWorker if (cell1->type != cell2->type) return cell1->type < cell2->type; - if (!ct.cell_known(cell1->type)) + if ((!mode_share_all && !ct.cell_known(cell1->type)) || !cell1->known()) return cell1 < cell2; if (cell1->has_keep_attr() || cell2->has_keep_attr()) @@ -211,15 +254,15 @@ struct OptShareWorker } struct CompareCells { - OptShareWorker *that; - CompareCells(OptShareWorker *that) : that(that) {} + OptMergeWorker *that; + CompareCells(OptMergeWorker *that) : that(that) {} bool operator()(const RTLIL::Cell *cell1, const RTLIL::Cell *cell2) const { return that->compare_cells(cell1, cell2); } }; - OptShareWorker(RTLIL::Design *design, RTLIL::Module *module, bool mode_nomux) : - design(design), module(module), assign_map(module) + OptMergeWorker(RTLIL::Design *design, RTLIL::Module *module, bool mode_nomux, bool mode_share_all) : + design(design), module(module), assign_map(module), mode_share_all(mode_share_all) { total_count = 0; ct.setup_internals(); @@ -249,7 +292,9 @@ struct OptShareWorker std::vector<RTLIL::Cell*> cells; cells.reserve(module->cells_.size()); for (auto &it : module->cells_) { - if (ct.cell_known(it.second->type) && design->selected(module, it.second)) + if (!design->selected(module, it.second)) + continue; + if (ct.cell_known(it.second->type) || (mode_share_all && it.second->known())) cells.push_back(it.second); } @@ -261,7 +306,7 @@ struct OptShareWorker did_something = true; log(" Cell `%s' is identical to cell `%s'.\n", cell->name.c_str(), sharemap[cell]->name.c_str()); for (auto &it : cell->connections()) { - if (ct.cell_output(cell->type, it.first)) { + if (cell->output(it.first)) { RTLIL::SigSpec other_sig = sharemap[cell]->getPort(it.first); log(" Redirecting output %s: %s = %s\n", it.first.c_str(), log_signal(it.second), log_signal(other_sig)); @@ -270,7 +315,9 @@ struct OptShareWorker } } log(" Removing %s cell `%s' from module `%s'.\n", cell->type.c_str(), cell->name.c_str(), module->name.c_str()); +#ifdef USE_CELL_HASH_CACHE cell_hash_cache.erase(cell); +#endif module->remove(cell); total_count++; } else { @@ -281,13 +328,13 @@ struct OptShareWorker } }; -struct OptSharePass : public Pass { - OptSharePass() : Pass("opt_share", "consolidate identical cells") { } +struct OptMergePass : public Pass { + OptMergePass() : Pass("opt_merge", "consolidate identical cells") { } virtual void help() { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" opt_share [-nomux] [selection]\n"); + log(" opt_merge [options] [selection]\n"); log("\n"); log("This pass identifies cells with identical type and input signals. Such cells\n"); log("are then merged to one cell.\n"); @@ -295,12 +342,16 @@ struct OptSharePass : public Pass { log(" -nomux\n"); log(" Do not merge MUX cells.\n"); log("\n"); + log(" -share_all\n"); + log(" Operate on all cell types, not just built-in types.\n"); + log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing OPT_SHARE pass (detect identical cells).\n"); + log_header(design, "Executing OPT_MERGE pass (detect identical cells).\n"); bool mode_nomux = false; + bool mode_share_all = false; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { @@ -309,13 +360,17 @@ struct OptSharePass : public Pass { mode_nomux = true; continue; } + if (arg == "-share_all") { + mode_share_all = true; + continue; + } break; } extra_args(args, argidx, design); int total_count = 0; for (auto module : design->selected_modules()) { - OptShareWorker worker(design, module, mode_nomux); + OptMergeWorker worker(design, module, mode_nomux, mode_share_all); total_count += worker.total_count; } @@ -323,6 +378,6 @@ struct OptSharePass : public Pass { design->scratchpad_set_bool("opt.did_something", true); log("Removed a total of %d cells.\n", total_count); } -} OptSharePass; - +} OptMergePass; + PRIVATE_NAMESPACE_END diff --git a/passes/opt/opt_muxtree.cc b/passes/opt/opt_muxtree.cc index 982870745..f5ddc2af9 100644 --- a/passes/opt/opt_muxtree.cc +++ b/passes/opt/opt_muxtree.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -68,7 +68,7 @@ struct OptMuxtreeWorker OptMuxtreeWorker(RTLIL::Design *design, RTLIL::Module *module) : design(design), module(module), assign_map(module), removed_count(0) { - log("Running muxtree optimizier on module %s..\n", module->name.c_str()); + log("Running muxtree optimizer on module %s..\n", module->name.c_str()); log(" Creating internal representation of mux trees.\n"); @@ -136,7 +136,7 @@ struct OptMuxtreeWorker } } for (auto wire : module->wires()) { - if (wire->port_output) + if (wire->port_output || wire->get_bool_attribute("\\keep")) for (int idx : sig2bits(RTLIL::SigSpec(wire))) bit2info[idx].seen_non_mux = true; } @@ -464,7 +464,7 @@ struct OptMuxtreePass : public Pass { } virtual void execute(vector<std::string> args, RTLIL::Design *design) { - log_header("Executing OPT_MUXTREE pass (detect dead branches in mux trees).\n"); + log_header(design, "Executing OPT_MUXTREE pass (detect dead branches in mux trees).\n"); extra_args(args, 1, design); int total_count = 0; @@ -479,5 +479,5 @@ struct OptMuxtreePass : public Pass { log("Removed %d multiplexer ports.\n", total_count); } } OptMuxtreePass; - + PRIVATE_NAMESPACE_END diff --git a/passes/opt/opt_reduce.cc b/passes/opt/opt_reduce.cc index 5c36eb26b..eb9d02ad5 100644 --- a/passes/opt/opt_reduce.cc +++ b/passes/opt/opt_reduce.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -284,7 +284,7 @@ struct OptReduceWorker did_something = false; // merge trees of reduce_* cells to one single cell and unify input vectors - // (only handle recduce_and and reduce_or for various reasons) + // (only handle reduce_and and reduce_or for various reasons) const char *type_list[] = { "$reduce_or", "$reduce_and" }; for (auto type : type_list) @@ -354,7 +354,7 @@ struct OptReducePass : public Pass { { bool do_fine = false; - log_header("Executing OPT_REDUCE pass (consolidate $*mux and $reduce_* inputs).\n"); + log_header(design, "Executing OPT_REDUCE pass (consolidate $*mux and $reduce_* inputs).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { @@ -384,5 +384,5 @@ struct OptReducePass : public Pass { log("Performed a total of %d changes.\n", total_count); } } OptReducePass; - + PRIVATE_NAMESPACE_END diff --git a/passes/opt/opt_rmdff.cc b/passes/opt/opt_rmdff.cc index 5f52bb8d8..1711d0f45 100644 --- a/passes/opt/opt_rmdff.cc +++ b/passes/opt/opt_rmdff.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -28,6 +28,43 @@ PRIVATE_NAMESPACE_BEGIN SigMap assign_map, dff_init_map; SigSet<RTLIL::Cell*> mux_drivers; +dict<SigBit, pool<SigBit>> init_attributes; + +void remove_init_attr(SigSpec sig) +{ + for (auto bit : assign_map(sig)) + if (init_attributes.count(bit)) + for (auto wbit : init_attributes.at(bit)) + wbit.wire->attributes.at("\\init")[wbit.offset] = State::Sx; +} + +bool handle_dlatch(RTLIL::Module *mod, RTLIL::Cell *dlatch) +{ + SigSpec sig_e = dlatch->getPort("\\EN"); + + if (sig_e == State::S0) + { + RTLIL::Const val_init; + for (auto bit : dff_init_map(dlatch->getPort("\\Q"))) + val_init.bits.push_back(bit.wire == NULL ? bit.data : State::Sx); + mod->connect(dlatch->getPort("\\Q"), val_init); + goto delete_dlatch; + } + + if (sig_e == State::S1) + { + mod->connect(dlatch->getPort("\\Q"), dlatch->getPort("\\D")); + goto delete_dlatch; + } + + return false; + +delete_dlatch: + log("Removing %s (%s) from module %s.\n", dlatch->name.c_str(), dlatch->type.c_str(), mod->name.c_str()); + remove_init_attr(dlatch->getPort("\\Q")); + mod->remove(dlatch); + return true; +} bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff) { @@ -83,60 +120,50 @@ bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff) val_init.bits.push_back(bit.wire == NULL ? bit.data : RTLIL::State::Sx); } - if (dff->type == "$dff" && mux_drivers.has(sig_d) && !has_init) { + if (dff->type == "$dff" && mux_drivers.has(sig_d)) { std::set<RTLIL::Cell*> muxes; mux_drivers.find(sig_d, muxes); for (auto mux : muxes) { RTLIL::SigSpec sig_a = assign_map(mux->getPort("\\A")); RTLIL::SigSpec sig_b = assign_map(mux->getPort("\\B")); - if (sig_a == sig_q && sig_b.is_fully_const()) { - RTLIL::SigSig conn(sig_q, sig_b); - mod->connect(conn); + if (sig_a == sig_q && sig_b.is_fully_const() && (!has_init || val_init == sig_b.as_const())) { + mod->connect(sig_q, sig_b); goto delete_dff; } - if (sig_b == sig_q && sig_a.is_fully_const()) { - RTLIL::SigSig conn(sig_q, sig_a); - mod->connect(conn); + if (sig_b == sig_q && sig_a.is_fully_const() && (!has_init || val_init == sig_a.as_const())) { + mod->connect(sig_q, sig_a); goto delete_dff; } } } - if (sig_c.is_fully_const() && (!sig_r.size() || !has_init)) { + if (sig_c.is_fully_const() && (!sig_r.size() || !has_init || val_init == val_rv)) { if (val_rv.bits.size() == 0) val_rv = val_init; - RTLIL::SigSig conn(sig_q, val_rv); - mod->connect(conn); + mod->connect(sig_q, val_rv); goto delete_dff; } - if (sig_d.is_fully_undef() && sig_r.size() && !has_init) { - RTLIL::SigSig conn(sig_q, val_rv); - mod->connect(conn); + if (sig_d.is_fully_undef() && sig_r.size() && (!has_init || val_init == val_rv)) { + mod->connect(sig_q, val_rv); goto delete_dff; } if (sig_d.is_fully_undef() && !sig_r.size() && has_init) { - RTLIL::SigSig conn(sig_q, val_init); - mod->connect(conn); + mod->connect(sig_q, val_init); goto delete_dff; } - if (sig_d.is_fully_const() && !sig_r.size() && !has_init) { - RTLIL::SigSig conn(sig_q, sig_d); - mod->connect(conn); + if (sig_d.is_fully_const() && (!sig_r.size() || val_rv == sig_d.as_const()) && (!has_init || val_init == sig_d.as_const())) { + mod->connect(sig_q, sig_d); goto delete_dff; } - if (sig_d == sig_q && !(sig_r.size() && has_init)) { - if (sig_r.size()) { - RTLIL::SigSig conn(sig_q, val_rv); - mod->connect(conn); - } - if (has_init) { - RTLIL::SigSig conn(sig_q, val_init); - mod->connect(conn); - } + if (sig_d == sig_q && (!sig_r.size() || !has_init || val_init == val_rv)) { + if (sig_r.size()) + mod->connect(sig_q, val_rv); + if (has_init) + mod->connect(sig_q, val_init); goto delete_dff; } @@ -144,6 +171,7 @@ bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff) delete_dff: log("Removing %s (%s) from module %s.\n", dff->name.c_str(), dff->type.c_str(), mod->name.c_str()); + remove_init_attr(dff->getPort("\\Q")); mod->remove(dff); return true; } @@ -163,7 +191,7 @@ struct OptRmdffPass : public Pass { virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { int total_count = 0; - log_header("Executing OPT_RMDFF pass (remove dff with constant values).\n"); + log_header(design, "Executing OPT_RMDFF pass (remove dff with constant values).\n"); extra_args(args, 1, design); @@ -175,11 +203,18 @@ struct OptRmdffPass : public Pass { assign_map.set(mod_it.second); dff_init_map.set(mod_it.second); for (auto &it : mod_it.second->wires_) - if (it.second->attributes.count("\\init") != 0) + if (it.second->attributes.count("\\init") != 0) { dff_init_map.add(it.second, it.second->attributes.at("\\init")); + for (int i = 0; i < GetSize(it.second); i++) { + SigBit wire_bit(it.second, i), mapped_bit = assign_map(wire_bit); + if (mapped_bit.wire) + init_attributes[mapped_bit].insert(wire_bit); + } + } mux_drivers.clear(); std::vector<RTLIL::IdString> dff_list; + std::vector<RTLIL::IdString> dlatch_list; for (auto &it : mod_it.second->cells_) { if (it.second->type == "$mux" || it.second->type == "$pmux") { if (it.second->getPort("\\A").size() == it.second->getPort("\\B").size()) @@ -200,6 +235,7 @@ struct OptRmdffPass : public Pass { if (it.second->type == "$_DFF_PP1_") dff_list.push_back(it.first); if (it.second->type == "$dff") dff_list.push_back(it.first); if (it.second->type == "$adff") dff_list.push_back(it.first); + if (it.second->type == "$dlatch") dlatch_list.push_back(it.first); } for (auto &id : dff_list) { @@ -207,6 +243,12 @@ struct OptRmdffPass : public Pass { handle_dff(mod_it.second, mod_it.second->cells_[id])) total_count++; } + + for (auto &id : dlatch_list) { + if (mod_it.second->cells_.count(id) > 0 && + handle_dlatch(mod_it.second, mod_it.second->cells_[id])) + total_count++; + } } assign_map.clear(); @@ -217,5 +259,5 @@ struct OptRmdffPass : public Pass { log("Replaced %d DFF cells.\n", total_count); } } OptRmdffPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/opt/share.cc b/passes/opt/share.cc index 3133cb2a6..22914eaa7 100644 --- a/passes/opt/share.cc +++ b/passes/opt/share.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -113,8 +113,8 @@ struct ShareWorker static int bits_macc_port(const Macc::port_t &p, int width) { if (GetSize(p.in_a) == 0 || GetSize(p.in_b) == 0) - return std::min(std::max(GetSize(p.in_a), GetSize(p.in_b)), width); - return std::min(GetSize(p.in_a), width) * std::min(GetSize(p.in_b), width) / 2; + return min(max(GetSize(p.in_a), GetSize(p.in_b)), width); + return min(GetSize(p.in_a), width) * min(GetSize(p.in_b), width) / 2; } static int bits_macc(const Macc &m, int width) @@ -224,13 +224,13 @@ struct ShareWorker supermacc->ports.push_back(p); } - int score = 1000 + abs(GetSize(p1.in_a) - GetSize(p2.in_a)) * std::max(abs(GetSize(p1.in_b) - GetSize(p2.in_b)), 1); + int score = 1000 + abs(GetSize(p1.in_a) - GetSize(p2.in_a)) * max(abs(GetSize(p1.in_b) - GetSize(p2.in_b)), 1); - for (int i = 0; i < std::min(GetSize(p1.in_a), GetSize(p2.in_a)); i++) + for (int i = 0; i < min(GetSize(p1.in_a), GetSize(p2.in_a)); i++) if (p1.in_a[i] == p2.in_a[i] && score > 0) score--; - for (int i = 0; i < std::min(GetSize(p1.in_b), GetSize(p2.in_b)); i++) + for (int i = 0; i < min(GetSize(p1.in_b), GetSize(p2.in_b)); i++) if (p1.in_b[i] == p2.in_b[i] && score > 0) score--; @@ -243,7 +243,7 @@ struct ShareWorker Macc m1(c1), m2(c2), supermacc; int w1 = GetSize(c1->getPort("\\Y")), w2 = GetSize(c2->getPort("\\Y")); - int width = std::max(w1, w2); + int width = max(w1, w2); m1.optimize(w1); m2.optimize(w2); @@ -369,7 +369,9 @@ struct ShareWorker } if (cell->type == "$memrd") { - if (!cell->parameters.at("\\CLK_ENABLE").as_bool()) + if (cell->parameters.at("\\CLK_ENABLE").as_bool()) + continue; + if (config.opt_aggressive || !modwalker.sigmap(cell->getPort("\\ADDR")).is_fully_const()) shareable_cells.insert(cell); continue; } @@ -387,7 +389,7 @@ struct ShareWorker } if (generic_ops.count(cell->type)) { - if (config.opt_aggressive || cell->parameters.at("\\Y_WIDTH").as_int() >= 10) + if (config.opt_aggressive) shareable_cells.insert(cell); continue; } @@ -417,8 +419,8 @@ struct ShareWorker int a2_width = c2->parameters.at("\\A_WIDTH").as_int(); int y2_width = c2->parameters.at("\\Y_WIDTH").as_int(); - if (std::max(a1_width, a2_width) > 2 * std::min(a1_width, a2_width)) return false; - if (std::max(y1_width, y2_width) > 2 * std::min(y1_width, y2_width)) return false; + if (max(a1_width, a2_width) > 2 * min(a1_width, a2_width)) return false; + if (max(y1_width, y2_width) > 2 * min(y1_width, y2_width)) return false; } return true; @@ -436,9 +438,9 @@ struct ShareWorker int b2_width = c2->parameters.at("\\B_WIDTH").as_int(); int y2_width = c2->parameters.at("\\Y_WIDTH").as_int(); - if (std::max(a1_width, a2_width) > 2 * std::min(a1_width, a2_width)) return false; - if (std::max(b1_width, b2_width) > 2 * std::min(b1_width, b2_width)) return false; - if (std::max(y1_width, y2_width) > 2 * std::min(y1_width, y2_width)) return false; + if (max(a1_width, a2_width) > 2 * min(a1_width, a2_width)) return false; + if (max(b1_width, b2_width) > 2 * min(b1_width, b2_width)) return false; + if (max(y1_width, y2_width) > 2 * min(y1_width, y2_width)) return false; } return true; @@ -456,15 +458,15 @@ struct ShareWorker int b2_width = c2->parameters.at("\\B_WIDTH").as_int(); int y2_width = c2->parameters.at("\\Y_WIDTH").as_int(); - int min1_width = std::min(a1_width, b1_width); - int max1_width = std::max(a1_width, b1_width); + int min1_width = min(a1_width, b1_width); + int max1_width = max(a1_width, b1_width); - int min2_width = std::min(a2_width, b2_width); - int max2_width = std::max(a2_width, b2_width); + int min2_width = min(a2_width, b2_width); + int max2_width = max(a2_width, b2_width); - if (std::max(min1_width, min2_width) > 2 * std::min(min1_width, min2_width)) return false; - if (std::max(max1_width, max2_width) > 2 * std::min(max1_width, max2_width)) return false; - if (std::max(y1_width, y2_width) > 2 * std::min(y1_width, y2_width)) return false; + if (max(min1_width, min2_width) > 2 * min(min1_width, min2_width)) return false; + if (max(max1_width, max2_width) > 2 * min(max1_width, max2_width)) return false; + if (max(y1_width, y2_width) > 2 * min(y1_width, y2_width)) return false; } return true; @@ -473,7 +475,7 @@ struct ShareWorker if (c1->type == "$macc") { if (!config.opt_aggressive) - if (share_macc(c1, c2) > 2 * std::min(bits_macc(c1), bits_macc(c2))) return false; + if (share_macc(c1, c2) > 2 * min(bits_macc(c1), bits_macc(c2))) return false; return true; } @@ -530,8 +532,8 @@ struct ShareWorker RTLIL::SigSpec a2 = c2->getPort("\\A"); RTLIL::SigSpec y2 = c2->getPort("\\Y"); - int a_width = std::max(a1.size(), a2.size()); - int y_width = std::max(y1.size(), y2.size()); + int a_width = max(a1.size(), a2.size()); + int y_width = max(y1.size(), y2.size()); a1.extend_u0(a_width, a_signed); a2.extend_u0(a_width, a_signed); @@ -561,11 +563,11 @@ struct ShareWorker if (config.generic_cbin_ops.count(c1->type)) { - int score_unflipped = std::max(c1->parameters.at("\\A_WIDTH").as_int(), c2->parameters.at("\\A_WIDTH").as_int()) + - std::max(c1->parameters.at("\\B_WIDTH").as_int(), c2->parameters.at("\\B_WIDTH").as_int()); + int score_unflipped = max(c1->parameters.at("\\A_WIDTH").as_int(), c2->parameters.at("\\A_WIDTH").as_int()) + + max(c1->parameters.at("\\B_WIDTH").as_int(), c2->parameters.at("\\B_WIDTH").as_int()); - int score_flipped = std::max(c1->parameters.at("\\A_WIDTH").as_int(), c2->parameters.at("\\B_WIDTH").as_int()) + - std::max(c1->parameters.at("\\B_WIDTH").as_int(), c2->parameters.at("\\A_WIDTH").as_int()); + int score_flipped = max(c1->parameters.at("\\A_WIDTH").as_int(), c2->parameters.at("\\B_WIDTH").as_int()) + + max(c1->parameters.at("\\B_WIDTH").as_int(), c2->parameters.at("\\A_WIDTH").as_int()); if (score_flipped < score_unflipped) { @@ -628,13 +630,13 @@ struct ShareWorker RTLIL::SigSpec b2 = c2->getPort("\\B"); RTLIL::SigSpec y2 = c2->getPort("\\Y"); - int a_width = std::max(a1.size(), a2.size()); - int b_width = std::max(b1.size(), b2.size()); - int y_width = std::max(y1.size(), y2.size()); + int a_width = max(a1.size(), a2.size()); + int b_width = max(b1.size(), b2.size()); + int y_width = max(y1.size(), y2.size()); if (c1->type == "$shr" && a_signed) { - a_width = std::max(y_width, a_width); + a_width = max(y_width, a_width); if (a1.size() < y1.size()) a1.extend_u0(y1.size(), true); if (a2.size() < y2.size()) a2.extend_u0(y2.size(), true); @@ -706,6 +708,10 @@ struct ShareWorker if (c1->type == "$memrd") { RTLIL::Cell *supercell = module->addCell(NEW_ID, c1); + RTLIL::SigSpec addr1 = c1->getPort("\\ADDR"); + RTLIL::SigSpec addr2 = c2->getPort("\\ADDR"); + if (addr1 != addr2) + supercell->setPort("\\ADDR", module->Mux(NEW_ID, addr2, addr1, act)); supercell_aux.insert(module->addPos(NEW_ID, supercell->getPort("\\DATA"), c2->getPort("\\DATA"))); supercell_aux.insert(supercell); return supercell; @@ -787,10 +793,59 @@ struct ShareWorker return true; } - void optimize_activation_patterns(pool<ssc_pair_t> & /* patterns */) + void optimize_activation_patterns(pool<ssc_pair_t> &patterns) { // TODO: Remove patterns that are contained in other patterns - // TODO: Consolidate pairs of patterns that only differ in the value for one signal bit + + dict<SigSpec, pool<Const>> db; + bool did_something = false; + + for (auto const &p : patterns) + { + auto &sig = p.first; + auto &val = p.second; + int len = GetSize(sig); + + for (int i = 0; i < len; i++) + { + auto otherval = val; + + if (otherval.bits[i] == State::S0) + otherval.bits[i] = State::S1; + else if (otherval.bits[i] == State::S1) + otherval.bits[i] = State::S0; + else + continue; + + if (db[sig].count(otherval)) + { + auto newsig = sig; + newsig.remove(i); + + auto newval = val; + newval.bits.erase(newval.bits.begin() + i); + + db[newsig].insert(newval); + db[sig].erase(otherval); + + did_something = true; + goto next_pattern; + } + } + + db[sig].insert(val); + next_pattern:; + } + + if (!did_something) + return; + + patterns.clear(); + for (auto &it : db) + for (auto &val : it.second) + patterns.insert(make_pair(it.first, val)); + + optimize_activation_patterns(patterns); } const pool<ssc_pair_t> &find_cell_activation_patterns(RTLIL::Cell *cell, const char *indent) @@ -1105,7 +1160,7 @@ struct ShareWorker RTLIL::Cell *cell = *shareable_cells.begin(); shareable_cells.erase(cell); - log(" Analyzing resource sharing options for %s:\n", log_id(cell)); + log(" Analyzing resource sharing options for %s (%s):\n", log_id(cell), log_id(cell->type)); const pool<ssc_pair_t> &cell_activation_patterns = find_cell_activation_patterns(cell, " "); RTLIL::SigSpec cell_activation_signals = bits_from_activation_patterns(cell_activation_patterns); @@ -1138,7 +1193,7 @@ struct ShareWorker for (auto other_cell : candidates) { - log(" Analyzing resource sharing with %s:\n", log_id(other_cell)); + log(" Analyzing resource sharing with %s (%s):\n", log_id(other_cell), log_id(other_cell->type)); const pool<ssc_pair_t> &other_cell_activation_patterns = find_cell_activation_patterns(other_cell, " "); RTLIL::SigSpec other_cell_activation_signals = bits_from_activation_patterns(other_cell_activation_patterns); @@ -1178,8 +1233,8 @@ struct ShareWorker optimize_activation_patterns(filtered_cell_activation_patterns); optimize_activation_patterns(filtered_other_cell_activation_patterns); - ezDefaultSAT ez; - SatGen satgen(&ez, &modwalker.sigmap); + ezSatPtr ez; + SatGen satgen(ez.get(), &modwalker.sigmap); pool<RTLIL::Cell*> sat_cells; std::set<RTLIL::SigBit> bits_queue; @@ -1189,13 +1244,13 @@ 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(ez->vec_eq(satgen.importSigSpec(p.first), satgen.importSigSpec(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(ez->vec_eq(satgen.importSigSpec(p.first), satgen.importSigSpec(p.second))); all_ctrl_signals.append(p.first); } @@ -1230,36 +1285,36 @@ struct ShareWorker 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))); + ez->assume(ez->NOT(ez->AND(sub1, sub2))); } - if (!ez.solve(ez.expression(ez.OpOr, cell_active))) { + if (!ez->solve(ez->expression(ez->OpOr, cell_active))) { 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))) { + if (!ez->solve(ez->expression(ez->OpOr, other_cell_active))) { 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(); + ez->non_incremental(); all_ctrl_signals.sort_and_unify(); std::vector<int> sat_model = satgen.importSigSpec(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)); + int sub1 = ez->expression(ez->OpOr, cell_active); + int sub2 = ez->expression(ez->OpOr, other_cell_active); + ez->assume(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), ez->numCnfVariables(), ez->numCnfClauses()); - if (ez.solve(sat_model, sat_model_values)) { + if (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--) @@ -1391,7 +1446,7 @@ struct SharePass : public Pass { log("\n"); log(" -fast\n"); log(" Only consider the simple part of the control logic in SAT solving, resulting\n"); - log(" in much easier SAT problems at the cost of maybe missing some oportunities\n"); + log(" in much easier SAT problems at the cost of maybe missing some opportunities\n"); log(" for resource sharing.\n"); log("\n"); log(" -limit N\n"); @@ -1445,7 +1500,7 @@ struct SharePass : public Pass { config.generic_other_ops.insert("$alu"); config.generic_other_ops.insert("$macc"); - log_header("Executing SHARE pass (SAT-based resource sharing).\n"); + log_header(design, "Executing SHARE pass (SAT-based resource sharing).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { diff --git a/passes/opt/wreduce.cc b/passes/opt/wreduce.cc index 1609a8be7..333541eab 100644 --- a/passes/opt/wreduce.cc +++ b/passes/opt/wreduce.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -37,7 +37,7 @@ struct WreduceConfig "$and", "$or", "$xor", "$xnor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", "$lt", "$le", "$eq", "$ne", "$eqx", "$nex", "$ge", "$gt", - "$add", "$sub", // "$mul", "$div", "$mod", "$pow", + "$add", "$sub", "$mul", // "$div", "$mod", "$pow", "$mux", "$pmux" }); } @@ -51,6 +51,7 @@ struct WreduceWorker std::set<Cell*, IdString::compare_ptr_by_name<Cell>> work_queue_cells; std::set<SigBit> work_queue_bits; + pool<SigBit> keep_bits; WreduceWorker(WreduceConfig *config, Module *module) : config(config), module(module), mi(module) { } @@ -65,10 +66,13 @@ struct WreduceWorker SigSpec sig_y = mi.sigmap(cell->getPort("\\Y")); std::vector<SigBit> bits_removed; + if (sig_y.has_const()) + return; + for (int i = GetSize(sig_y)-1; i >= 0; i--) { auto info = mi.query(sig_y[i]); - if (!info->is_output && GetSize(info->ports) <= 1) { + if (!info->is_output && GetSize(info->ports) <= 1 && !keep_bits.count(mi.sigmap(sig_y[i]))) { bits_removed.push_back(Sx); continue; } @@ -172,6 +176,11 @@ struct WreduceWorker if (cell->type.in("$mux", "$pmux")) return run_cell_mux(cell); + SigSpec sig = mi.sigmap(cell->getPort("\\Y")); + + if (sig.has_const()) + return; + // Reduce size of ports A and B based on constant input bits and size of output port @@ -179,8 +188,8 @@ struct WreduceWorker int max_port_b_size = cell->hasPort("\\B") ? GetSize(cell->getPort("\\B")) : -1; if (cell->type.in("$not", "$pos", "$neg", "$and", "$or", "$xor", "$add", "$sub")) { - max_port_a_size = std::min(max_port_a_size, GetSize(cell->getPort("\\Y"))); - max_port_b_size = std::min(max_port_b_size, GetSize(cell->getPort("\\Y"))); + max_port_a_size = min(max_port_a_size, GetSize(sig)); + max_port_b_size = min(max_port_b_size, GetSize(sig)); } bool port_a_signed = false; @@ -192,10 +201,33 @@ struct WreduceWorker if (max_port_b_size >= 0) run_reduce_inport(cell, 'B', max_port_b_size, port_b_signed, did_something); + if (cell->hasPort("\\A") && cell->hasPort("\\B") && port_a_signed && port_b_signed) { + SigSpec sig_a = mi.sigmap(cell->getPort("\\A")), sig_b = mi.sigmap(cell->getPort("\\B")); + if (GetSize(sig_a) > 0 && sig_a[GetSize(sig_a)-1] == State::S0 && + GetSize(sig_b) > 0 && sig_b[GetSize(sig_b)-1] == State::S0) { + log("Converting cell %s.%s (%s) from signed to unsigned.\n", + log_id(module), log_id(cell), log_id(cell->type)); + cell->setParam("\\A_SIGNED", 0); + cell->setParam("\\B_SIGNED", 0); + port_a_signed = false; + port_b_signed = false; + did_something = true; + } + } - // Reduce size of port Y based on sizes for A and B and unused bits in Y + if (cell->hasPort("\\A") && !cell->hasPort("\\B") && port_a_signed) { + SigSpec sig_a = mi.sigmap(cell->getPort("\\A")); + if (GetSize(sig_a) > 0 && sig_a[GetSize(sig_a)-1] == State::S0) { + log("Converting cell %s.%s (%s) from signed to unsigned.\n", + log_id(module), log_id(cell), log_id(cell->type)); + cell->setParam("\\A_SIGNED", 0); + port_a_signed = false; + did_something = true; + } + } - SigSpec sig = mi.sigmap(cell->getPort("\\Y")); + + // Reduce size of port Y based on sizes for A and B and unused bits in Y int bits_removed = 0; if (port_a_signed && cell->type == "$shr") { @@ -221,7 +253,7 @@ struct WreduceWorker if (cell->hasPort("\\A")) a_size = GetSize(cell->getPort("\\A")); if (cell->hasPort("\\B")) b_size = GetSize(cell->getPort("\\B")); - int max_y_size = std::max(a_size, b_size); + int max_y_size = max(a_size, b_size); if (cell->type == "$add") max_y_size++; @@ -265,6 +297,11 @@ struct WreduceWorker void run() { + for (auto w : module->wires()) + if (w->get_bool_attribute("\\keep")) + for (auto bit : mi.sigmap(w)) + keep_bits.insert(bit); + for (auto c : module->selected_cells()) work_queue_cells.insert(c); @@ -281,6 +318,10 @@ struct WreduceWorker work_queue_cells.insert(port.cell); } + pool<SigSpec> complete_wires; + for (auto w : module->wires()) + complete_wires.insert(mi.sigmap(w)); + for (auto w : module->selected_wires()) { int unused_top_bits = 0; @@ -296,19 +337,22 @@ struct WreduceWorker unused_top_bits++; } - if (0 < unused_top_bits && unused_top_bits < GetSize(w)) { - log("Removed top %d bits (of %d) from wire %s.%s.\n", unused_top_bits, GetSize(w), log_id(module), log_id(w)); - Wire *nw = module->addWire(NEW_ID, w); - nw->width = GetSize(w) - unused_top_bits; - module->connect(nw, SigSpec(w).extract(0, GetSize(nw))); - module->swap_names(w, nw); - } + if (unused_top_bits == 0 || unused_top_bits == GetSize(w)) + continue; + + if (complete_wires[mi.sigmap(w).extract(0, GetSize(w) - unused_top_bits)]) + continue; + + log("Removed top %d bits (of %d) from wire %s.%s.\n", unused_top_bits, GetSize(w), log_id(module), log_id(w)); + Wire *nw = module->addWire(NEW_ID, GetSize(w) - unused_top_bits); + module->connect(nw, SigSpec(w).extract(0, GetSize(nw))); + module->swap_names(w, nw); } } }; struct WreducePass : public Pass { - WreducePass() : Pass("wreduce", "reduce the word size of operations is possible") { } + WreducePass() : Pass("wreduce", "reduce the word size of operations if possible") { } virtual void help() { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| @@ -327,7 +371,7 @@ struct WreducePass : public Pass { { WreduceConfig config; - log_header("Executing WREDUCE pass (reducing word size of cells).\n"); + log_header(design, "Executing WREDUCE pass (reducing word size of cells).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { @@ -340,6 +384,19 @@ struct WreducePass : public Pass { if (module->has_processes_warn()) continue; + for (auto c : module->selected_cells()) + if (c->type.in("$reduce_and", "$reduce_or", "$reduce_xor", "$reduce_xnor", "$reduce_bool", + "$lt", "$le", "$eq", "$ne", "$eqx", "$nex", "$ge", "$gt", + "$logic_not", "$logic_and", "$logic_or") && GetSize(c->getPort("\\Y")) > 1) { + SigSpec sig = c->getPort("\\Y"); + if (!sig.has_const()) { + c->setPort("\\Y", sig[0]); + c->setParam("\\Y_WIDTH", 1); + sig.remove(0); + module->connect(sig, Const(0, GetSize(sig))); + } + } + WreduceWorker worker(&config, module); worker.run(); } diff --git a/passes/proc/Makefile.inc b/passes/proc/Makefile.inc index dfbc78eae..397fe46a1 100644 --- a/passes/proc/Makefile.inc +++ b/passes/proc/Makefile.inc @@ -5,5 +5,6 @@ OBJS += passes/proc/proc_rmdead.o OBJS += passes/proc/proc_init.o OBJS += passes/proc/proc_arst.o OBJS += passes/proc/proc_mux.o +OBJS += passes/proc/proc_dlatch.o OBJS += passes/proc/proc_dff.o diff --git a/passes/proc/proc.cc b/passes/proc/proc.cc index 30efbab4b..d5366f266 100644 --- a/passes/proc/proc.cc +++ b/passes/proc/proc.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -40,22 +40,29 @@ struct ProcPass : public Pass { log(" proc_init\n"); log(" proc_arst\n"); log(" proc_mux\n"); + log(" proc_dlatch\n"); log(" proc_dff\n"); log(" proc_clean\n"); log("\n"); - log("This replaces the processes in the design with multiplexers and flip-flops.\n"); + log("This replaces the processes in the design with multiplexers,\n"); + log("flip-flops and latches.\n"); log("\n"); log("The following options are supported:\n"); log("\n"); log(" -global_arst [!]<netname>\n"); log(" This option is passed through to proc_arst.\n"); log("\n"); + log(" -ifx\n"); + log(" This option is passed through to proc_mux. proc_rmdead is not\n"); + log(" executed in -ifx mode.\n"); + log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { std::string global_arst; + bool ifxmode = false; - log_header("Executing PROC pass (convert processes to netlists).\n"); + log_header(design, "Executing PROC pass (convert processes to netlists).\n"); log_push(); size_t argidx; @@ -65,23 +72,29 @@ struct ProcPass : public Pass { global_arst = args[++argidx]; continue; } + if (args[argidx] == "-ifx") { + ifxmode = true; + continue; + } break; } extra_args(args, argidx, design); Pass::call(design, "proc_clean"); - Pass::call(design, "proc_rmdead"); + if (!ifxmode) + Pass::call(design, "proc_rmdead"); Pass::call(design, "proc_init"); if (global_arst.empty()) Pass::call(design, "proc_arst"); else Pass::call(design, "proc_arst -global_arst " + global_arst); - Pass::call(design, "proc_mux"); + Pass::call(design, ifxmode ? "proc_mux -ifx" : "proc_mux"); + Pass::call(design, "proc_dlatch"); Pass::call(design, "proc_dff"); Pass::call(design, "proc_clean"); log_pop(); } } ProcPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/proc/proc_arst.cc b/passes/proc/proc_arst.cc index 27c6b3bcf..216b00ddd 100644 --- a/passes/proc/proc_arst.cc +++ b/passes/proc/proc_arst.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -226,7 +226,7 @@ struct ProcArstPass : public Pass { std::string global_arst; bool global_arst_neg = false; - log_header("Executing PROC_ARST pass (detect async resets in processes).\n"); + log_header(design, "Executing PROC_ARST pass (detect async resets in processes).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) @@ -244,6 +244,7 @@ struct ProcArstPass : public Pass { } extra_args(args, argidx, design); + pool<Wire*> delete_initattr_wires; for (auto mod : design->modules()) if (design->selected(mod)) { @@ -265,6 +266,7 @@ struct ProcArstPass : public Pass { value.extend_u0(chunk.wire->width, false); arst_sig.append(chunk); arst_val.append(value.extract(chunk.offset, chunk.width)); + delete_initattr_wires.insert(chunk.wire); } if (arst_sig.size()) { log("Added global reset to process %s: %s <- %s\n", @@ -281,7 +283,10 @@ struct ProcArstPass : public Pass { } } } + + for (auto wire : delete_initattr_wires) + wire->attributes.erase("\\init"); } } ProcArstPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/proc/proc_clean.cc b/passes/proc/proc_clean.cc index 82716cd06..7dbabc211 100644 --- a/passes/proc/proc_clean.cc +++ b/passes/proc/proc_clean.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -156,7 +156,7 @@ struct ProcCleanPass : public Pass { virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { int total_count = 0; - log_header("Executing PROC_CLEAN pass (remove empty switches from decision trees).\n"); + log_header(design, "Executing PROC_CLEAN pass (remove empty switches from decision trees).\n"); extra_args(args, 1, design); @@ -183,5 +183,5 @@ struct ProcCleanPass : public Pass { log("Cleaned up %d empty switch%s.\n", total_count, total_count == 1 ? "" : "es"); } } ProcCleanPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/proc/proc_dff.cc b/passes/proc/proc_dff.cc index 76842da6b..f532990c2 100644 --- a/passes/proc/proc_dff.cc +++ b/passes/proc/proc_dff.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -369,7 +369,7 @@ struct ProcDffPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing PROC_DFF pass (convert process syncs to FFs).\n"); + log_header(design, "Executing PROC_DFF pass (convert process syncs to FFs).\n"); extra_args(args, 1, design); @@ -382,5 +382,5 @@ struct ProcDffPass : public Pass { } } } ProcDffPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/proc/proc_dlatch.cc b/passes/proc/proc_dlatch.cc new file mode 100644 index 000000000..6621afd33 --- /dev/null +++ b/passes/proc/proc_dlatch.cc @@ -0,0 +1,449 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/register.h" +#include "kernel/sigtools.h" +#include "kernel/consteval.h" +#include "kernel/log.h" +#include <sstream> +#include <stdlib.h> +#include <stdio.h> + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct proc_dlatch_db_t +{ + Module *module; + SigMap sigmap; + + pool<Cell*> generated_dlatches; + dict<Cell*, vector<SigBit>> mux_srcbits; + dict<SigBit, pair<Cell*, int>> mux_drivers; + dict<SigBit, int> sigusers; + + proc_dlatch_db_t(Module *module) : module(module), sigmap(module) + { + for (auto cell : module->cells()) + { + if (cell->type.in("$mux", "$pmux")) + { + auto sig_y = sigmap(cell->getPort("\\Y")); + for (int i = 0; i < GetSize(sig_y); i++) + mux_drivers[sig_y[i]] = pair<Cell*, int>(cell, i); + + pool<SigBit> mux_srcbits_pool; + for (auto bit : sigmap(cell->getPort("\\A"))) + mux_srcbits_pool.insert(bit); + for (auto bit : sigmap(cell->getPort("\\B"))) + mux_srcbits_pool.insert(bit); + + vector<SigBit> mux_srcbits_vec; + for (auto bit : mux_srcbits_pool) + if (bit.wire != nullptr) + mux_srcbits_vec.push_back(bit); + + mux_srcbits[cell].swap(mux_srcbits_vec); + } + + for (auto &conn : cell->connections()) + if (!cell->known() || cell->input(conn.first)) + for (auto bit : sigmap(conn.second)) + sigusers[bit]++; + } + + for (auto wire : module->wires()) + if (wire->port_input) + for (auto bit : sigmap(wire)) + sigusers[bit]++; + } + + bool quickcheck(const SigSpec &haystack, const SigSpec &needle) + { + pool<SigBit> haystack_bits = sigmap(haystack).to_sigbit_pool(); + pool<SigBit> needle_bits = sigmap(needle).to_sigbit_pool(); + + pool<Cell*> cells_queue, cells_visited; + pool<SigBit> bits_queue, bits_visited; + + bits_queue = haystack_bits; + while (!bits_queue.empty()) + { + for (auto &bit : bits_queue) { + auto it = mux_drivers.find(bit); + if (it != mux_drivers.end()) + if (!cells_visited.count(it->second.first)) + cells_queue.insert(it->second.first); + bits_visited.insert(bit); + } + + bits_queue.clear(); + + for (auto c : cells_queue) { + for (auto bit : mux_srcbits[c]) { + if (needle_bits.count(bit)) + return true; + if (!bits_visited.count(bit)) + bits_queue.insert(bit); + } + } + + cells_queue.clear(); + } + + return false; + } + + struct rule_node_t + { + // a node is true if "signal" equals "match" and [any + // of the child nodes is true or "children" is empty] + SigBit signal, match; + vector<int> children; + + bool operator==(const rule_node_t &other) const { + return signal == other.signal && match == other.match && children == other.children; + } + + unsigned int hash() const { + unsigned int h = mkhash_init; + mkhash(h, signal.hash()); + mkhash(h, match.hash()); + for (auto i : children) mkhash(h, i); + return h; + } + }; + + enum tf_node_types_t : int { + true_node = 1, + false_node = 2 + }; + + idict<rule_node_t, 3> rules_db; + dict<int, SigBit> rules_sig; + + int make_leaf(SigBit signal, SigBit match) + { + rule_node_t node; + node.signal = signal; + node.match = match; + return rules_db(node); + } + + int make_inner(SigBit signal, SigBit match, int child) + { + rule_node_t node; + node.signal = signal; + node.match = match; + node.children.push_back(child); + return rules_db(node); + } + + int make_inner(const pool<int> &children) + { + rule_node_t node; + node.signal = State::S0; + node.match = State::S0; + node.children = vector<int>(children.begin(), children.end()); + std::sort(node.children.begin(), node.children.end()); + return rules_db(node); + } + + int find_mux_feedback(SigBit haystack, SigBit needle, bool set_undef) + { + if (sigusers[haystack] > 1) + set_undef = false; + + if (haystack == needle) + return true_node; + + auto it = mux_drivers.find(haystack); + if (it == mux_drivers.end()) + return false_node; + + Cell *cell = it->second.first; + int index = it->second.second; + + SigSpec sig_a = sigmap(cell->getPort("\\A")); + SigSpec sig_b = sigmap(cell->getPort("\\B")); + SigSpec sig_s = sigmap(cell->getPort("\\S")); + int width = GetSize(sig_a); + + pool<int> children; + + int n = find_mux_feedback(sig_a[index], needle, set_undef); + if (n != false_node) { + if (set_undef && sig_a[index] == needle) { + SigSpec sig = cell->getPort("\\A"); + sig[index] = State::Sx; + cell->setPort("\\A", sig); + } + for (int i = 0; i < GetSize(sig_s); i++) + n = make_inner(sig_s[i], State::S0, n); + children.insert(n); + } + + for (int i = 0; i < GetSize(sig_s); i++) { + n = find_mux_feedback(sig_b[i*width + index], needle, set_undef); + if (n != false_node) { + if (set_undef && sig_b[i*width + index] == needle) { + SigSpec sig = cell->getPort("\\B"); + sig[i*width + index] = State::Sx; + cell->setPort("\\B", sig); + } + children.insert(make_inner(sig_s[i], State::S1, n)); + } + } + + if (children.empty()) + return false_node; + + return make_inner(children); + } + + SigBit make_hold(int n) + { + if (n == true_node) + return State::S1; + + if (n == false_node) + return State::S0; + + if (rules_sig.count(n)) + return rules_sig.at(n); + + const rule_node_t &rule = rules_db[n]; + SigSpec and_bits; + + if (rule.signal != rule.match) { + if (rule.match == State::S1) + and_bits.append(rule.signal); + else if (rule.match == State::S0) + and_bits.append(module->Not(NEW_ID, rule.signal)); + else + and_bits.append(module->Eq(NEW_ID, rule.signal, rule.match)); + } + + if (!rule.children.empty()) { + SigSpec or_bits; + for (int k : rule.children) + or_bits.append(make_hold(k)); + and_bits.append(module->ReduceOr(NEW_ID, or_bits)); + } + + if (GetSize(and_bits) == 2) + and_bits = module->And(NEW_ID, and_bits[0], and_bits[1]); + log_assert(GetSize(and_bits) == 1); + + rules_sig[n] = and_bits[0]; + return and_bits[0]; + } + + void fixup_mux(Cell *cell) + { + SigSpec sig_a = cell->getPort("\\A"); + SigSpec sig_b = cell->getPort("\\B"); + SigSpec sig_s = cell->getPort("\\S"); + SigSpec sig_any_valid_b; + + SigSpec sig_new_b, sig_new_s; + for (int i = 0; i < GetSize(sig_s); i++) { + SigSpec b = sig_b.extract(i*GetSize(sig_a), GetSize(sig_a)); + if (!b.is_fully_undef()) { + sig_any_valid_b = b; + sig_new_b.append(b); + sig_new_s.append(sig_s[i]); + } + } + + if (sig_new_s.empty()) { + sig_new_b = sig_a; + sig_new_s = State::S0; + } + + if (sig_a.is_fully_undef() && !sig_any_valid_b.empty()) + cell->setPort("\\A", sig_any_valid_b); + + if (GetSize(sig_new_s) == 1) { + cell->type = "$mux"; + cell->unsetParam("\\S_WIDTH"); + } else { + cell->type = "$pmux"; + cell->setParam("\\S_WIDTH", GetSize(sig_new_s)); + } + + cell->setPort("\\B", sig_new_b); + cell->setPort("\\S", sig_new_s); + } + + void fixup_muxes() + { + pool<Cell*> visited, queue; + dict<Cell*, pool<SigBit>> upstream_cell2net; + dict<SigBit, pool<Cell*>> upstream_net2cell; + + CellTypes ct; + ct.setup_internals(); + + for (auto cell : module->cells()) + for (auto conn : cell->connections()) { + if (cell->input(conn.first)) + for (auto bit : sigmap(conn.second)) + upstream_cell2net[cell].insert(bit); + if (cell->output(conn.first)) + for (auto bit : sigmap(conn.second)) + upstream_net2cell[bit].insert(cell); + } + + queue = generated_dlatches; + while (!queue.empty()) + { + pool<Cell*> next_queue; + + for (auto cell : queue) { + if (cell->type.in("$mux", "$pmux")) + fixup_mux(cell); + for (auto bit : upstream_cell2net[cell]) + for (auto cell : upstream_net2cell[bit]) + next_queue.insert(cell); + visited.insert(cell); + } + + queue.clear(); + for (auto cell : next_queue) { + if (!visited.count(cell) && ct.cell_known(cell->type)) + queue.insert(cell); + } + } + } +}; + +void proc_dlatch(proc_dlatch_db_t &db, RTLIL::Process *proc) +{ + std::vector<RTLIL::SyncRule*> new_syncs; + RTLIL::SigSig latches_bits, nolatches_bits; + dict<SigBit, SigBit> latches_out_in; + dict<SigBit, int> latches_hold; + + for (auto sr : proc->syncs) + { + if (sr->type != RTLIL::SyncType::STa) { + new_syncs.push_back(sr); + continue; + } + + for (auto ss : sr->actions) + { + db.sigmap.apply(ss.first); + db.sigmap.apply(ss.second); + + if (!db.quickcheck(ss.second, ss.first)) { + nolatches_bits.first.append(ss.first); + nolatches_bits.second.append(ss.second); + continue; + } + + for (int i = 0; i < GetSize(ss.first); i++) + latches_out_in[ss.first[i]] = ss.second[i]; + } + + delete sr; + } + + latches_out_in.sort(); + for (auto &it : latches_out_in) { + int n = db.find_mux_feedback(it.second, it.first, true); + if (n == db.false_node) { + nolatches_bits.first.append(it.first); + nolatches_bits.second.append(it.second); + } else { + latches_bits.first.append(it.first); + latches_bits.second.append(it.second); + latches_hold[it.first] = n; + } + } + + int offset = 0; + for (auto chunk : nolatches_bits.first.chunks()) { + SigSpec lhs = chunk, rhs = nolatches_bits.second.extract(offset, chunk.width); + log("No latch inferred for signal `%s.%s' from process `%s.%s'.\n", + db.module->name.c_str(), log_signal(lhs), db.module->name.c_str(), proc->name.c_str()); + db.module->connect(lhs, rhs); + offset += chunk.width; + } + + offset = 0; + while (offset < GetSize(latches_bits.first)) + { + int width = 1; + int n = latches_hold[latches_bits.first[offset]]; + Wire *w = latches_bits.first[offset].wire; + + if (w != nullptr) + { + while (offset+width < GetSize(latches_bits.first) && + n == latches_hold[latches_bits.first[offset+width]] && + w == latches_bits.first[offset+width].wire) + width++; + + SigSpec lhs = latches_bits.first.extract(offset, width); + SigSpec rhs = latches_bits.second.extract(offset, width); + + Cell *cell = db.module->addDlatch(NEW_ID, db.module->Not(NEW_ID, db.make_hold(n)), rhs, lhs); + db.generated_dlatches.insert(cell); + + log("Latch inferred for signal `%s.%s' from process `%s.%s': %s\n", + db.module->name.c_str(), log_signal(lhs), db.module->name.c_str(), proc->name.c_str(), log_id(cell)); + } + + offset += width; + } + + new_syncs.swap(proc->syncs); +} + +struct ProcDlatchPass : public Pass { + ProcDlatchPass() : Pass("proc_dlatch", "extract latches from processes") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" proc_dlatch [selection]\n"); + log("\n"); + log("This pass identifies latches in the processes and converts them to\n"); + log("d-type latches.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + log_header(design, "Executing PROC_DLATCH pass (convert process syncs to latches).\n"); + + extra_args(args, 1, design); + + for (auto module : design->selected_modules()) { + proc_dlatch_db_t db(module); + for (auto &proc_it : module->processes) + if (design->selected(module, proc_it.second)) + proc_dlatch(db, proc_it.second); + db.fixup_muxes(); + } + } +} ProcDlatchPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/proc/proc_init.cc b/passes/proc/proc_init.cc index dff68159f..0c8fb83dc 100644 --- a/passes/proc/proc_init.cc +++ b/passes/proc/proc_init.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -61,13 +61,28 @@ void proc_init(RTLIL::Module *mod, RTLIL::Process *proc) log_cmd_error("Failed to get a constant init value for %s: %s\n", log_signal(lhs), log_signal(rhs)); int offset = 0; - for (auto &lhs_c : lhs.chunks()) { - if (lhs_c.wire != NULL) { - RTLIL::SigSpec value = rhs.extract(offset, lhs_c.width); - if (value.size() != lhs_c.wire->width) - log_cmd_error("Init value is not for the entire wire: %s = %s\n", log_signal(lhs_c), log_signal(value)); - log(" Setting init value: %s = %s\n", log_signal(lhs_c.wire), log_signal(value)); - lhs_c.wire->attributes["\\init"] = value.as_const(); + for (auto &lhs_c : lhs.chunks()) + { + if (lhs_c.wire != nullptr) + { + SigSpec valuesig = rhs.extract(offset, lhs_c.width); + if (!valuesig.is_fully_const()) + log_cmd_error("Non-const initialization value: %s = %s\n", log_signal(lhs_c), log_signal(valuesig)); + + Const value = valuesig.as_const(); + Const &wireinit = lhs_c.wire->attributes["\\init"]; + + while (GetSize(wireinit.bits) < lhs_c.wire->width) + wireinit.bits.push_back(State::Sx); + + for (int i = 0; i < lhs_c.width; i++) { + auto &initbit = wireinit.bits[i + lhs_c.offset]; + if (initbit != State::Sx && initbit != value[i]) + log_cmd_error("Conflicting initialization values for %s.\n", log_signal(lhs_c)); + initbit = value[i]; + } + + log(" Set init value: %s = %s\n", log_signal(lhs_c.wire), log_signal(wireinit)); } offset += lhs_c.width; } @@ -93,14 +108,14 @@ struct ProcInitPass : public Pass { log("\n"); log(" proc_init [selection]\n"); log("\n"); - log("This pass extracts the 'init' actions from processes (generated from verilog\n"); + log("This pass extracts the 'init' actions from processes (generated from Verilog\n"); log("'initial' blocks) and sets the initial value to the 'init' attribute on the\n"); log("respective wire.\n"); log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing PROC_INIT pass (extract init attributes).\n"); + log_header(design, "Executing PROC_INIT pass (extract init attributes).\n"); extra_args(args, 1, design); @@ -111,5 +126,5 @@ struct ProcInitPass : public Pass { proc_init(mod, proc_it.second); } } ProcInitPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/proc/proc_mux.cc b/passes/proc/proc_mux.cc index 4aa1aab54..57e131ca5 100644 --- a/passes/proc/proc_mux.cc +++ b/passes/proc/proc_mux.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -27,37 +27,123 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -RTLIL::SigSpec find_any_lvalue(const RTLIL::CaseRule *cs) +struct SigSnippets { - for (auto &action : cs->actions) { - if (action.first.size()) - return action.first; - } + idict<SigSpec> sigidx; + dict<SigBit, int> bit2snippet; + pool<int> snippets; - for (auto sw : cs->switches) - for (auto cs2 : sw->cases) { - RTLIL::SigSpec sig = find_any_lvalue(cs2); - if (sig.size()) - return sig; + void insert(SigSpec sig) + { + if (sig.empty()) + return; + + int key = sigidx(sig); + if (snippets.count(key)) + return; + + SigSpec new_sig; + + for (int i = 0; i < GetSize(sig); i++) + { + int other_key = bit2snippet.at(sig[i], -1); + + if (other_key < 0) { + new_sig.append(sig[i]); + continue; + } + + if (!new_sig.empty()) { + int new_key = sigidx(new_sig); + snippets.insert(new_key); + for (auto bit : new_sig) + bit2snippet[bit] = new_key; + new_sig = SigSpec(); + } + + SigSpec other_sig = sigidx[other_key]; + int k = 0, n = 1; + + while (other_sig[k] != sig[i]) { + k++; + log_assert(k < GetSize(other_sig)); + } + + while (i+n < GetSize(sig) && k+n < GetSize(other_sig) && sig[i+n] == other_sig[k+n]) + n++; + + SigSpec sig1 = other_sig.extract(0, k); + SigSpec sig2 = other_sig.extract(k, n); + SigSpec sig3 = other_sig.extract(k+n, GetSize(other_sig)-k-n); + + for (auto bit : other_sig) + bit2snippet.erase(bit); + snippets.erase(other_key); + + insert(sig1); + insert(sig2); + insert(sig3); + + i += n-1; + } + + if (!new_sig.empty()) { + int new_key = sigidx(new_sig); + snippets.insert(new_key); + for (auto bit : new_sig) + bit2snippet[bit] = new_key; + } } - return RTLIL::SigSpec(); -} + void insert(const RTLIL::CaseRule *cs) + { + for (auto &action : cs->actions) + insert(action.first); -void extract_core_signal(const RTLIL::CaseRule *cs, RTLIL::SigSpec &sig) + for (auto sw : cs->switches) + for (auto cs2 : sw->cases) + insert(cs2); + } +}; + +struct SnippetSwCache { - for (auto &action : cs->actions) { - RTLIL::SigSpec lvalue = action.first.extract(sig); - if (lvalue.size()) - sig = lvalue; + dict<RTLIL::SwitchRule*, pool<int>, hash_ptr_ops> cache; + const SigSnippets *snippets; + int current_snippet; + + bool check(RTLIL::SwitchRule *sw) + { + return cache[sw].count(current_snippet) != 0; } - for (auto sw : cs->switches) - for (auto cs2 : sw->cases) - extract_core_signal(cs2, sig); -} + void insert(const RTLIL::CaseRule *cs, vector<RTLIL::SwitchRule*> &sw_stack) + { + for (auto &action : cs->actions) + for (auto bit : action.first) { + int sn = snippets->bit2snippet.at(bit, -1); + if (sn < 0) + continue; + for (auto sw : sw_stack) + cache[sw].insert(sn); + } + + for (auto sw : cs->switches) { + sw_stack.push_back(sw); + for (auto cs2 : sw->cases) + insert(cs2, sw_stack); + sw_stack.pop_back(); + } + } -RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SwitchRule *sw) + void insert(const RTLIL::CaseRule *cs) + { + vector<RTLIL::SwitchRule*> sw_stack; + insert(cs, sw_stack); + } +}; + +RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SwitchRule *sw, bool ifxmode) { std::stringstream sstr; sstr << "$procmux$" << (autoidx++); @@ -78,14 +164,14 @@ RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s if (comp.size() == 0) return RTLIL::SigSpec(); - if (sig.size() == 1 && comp == RTLIL::SigSpec(1,1)) + if (sig.size() == 1 && comp == RTLIL::SigSpec(1,1) && !ifxmode) { mod->connect(RTLIL::SigSig(RTLIL::SigSpec(cmp_wire, cmp_wire->width++), sig)); } else { // create compare cell - RTLIL::Cell *eq_cell = mod->addCell(stringf("%s_CMP%d", sstr.str().c_str(), cmp_wire->width), "$eq"); + RTLIL::Cell *eq_cell = mod->addCell(stringf("%s_CMP%d", sstr.str().c_str(), cmp_wire->width), ifxmode ? "$eqx" : "$eq"); eq_cell->attributes = sw->attributes; eq_cell->parameters["\\A_SIGNED"] = RTLIL::Const(0); @@ -125,7 +211,7 @@ RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s return RTLIL::SigSpec(ctrl_wire); } -RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SigSpec when_signal, RTLIL::SigSpec else_signal, RTLIL::Cell *&last_mux_cell, RTLIL::SwitchRule *sw) +RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SigSpec when_signal, RTLIL::SigSpec else_signal, RTLIL::Cell *&last_mux_cell, RTLIL::SwitchRule *sw, bool ifxmode) { log_assert(when_signal.size() == else_signal.size()); @@ -137,7 +223,7 @@ RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s return when_signal; // compare results - RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw); + RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw, ifxmode); if (ctrl_sig.size() == 0) return when_signal; log_assert(ctrl_sig.size() == 1); @@ -159,12 +245,15 @@ RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s return RTLIL::SigSpec(result_wire); } -void append_pmux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SigSpec when_signal, RTLIL::Cell *last_mux_cell, RTLIL::SwitchRule *sw) +void append_pmux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SigSpec when_signal, RTLIL::Cell *last_mux_cell, RTLIL::SwitchRule *sw, bool ifxmode) { log_assert(last_mux_cell != NULL); log_assert(when_signal.size() == last_mux_cell->getPort("\\A").size()); - RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw); + if (when_signal == last_mux_cell->getPort("\\A")) + return; + + RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw, ifxmode); log_assert(ctrl_sig.size() == 1); last_mux_cell->type = "$pmux"; @@ -179,7 +268,8 @@ void append_pmux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::ve last_mux_cell->parameters["\\S_WIDTH"] = last_mux_cell->getPort("\\S").size(); } -RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, RTLIL::CaseRule *cs, const RTLIL::SigSpec &sig, const RTLIL::SigSpec &defval) +RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, SnippetSwCache &swcache, dict<RTLIL::SwitchRule*, bool, hash_ptr_ops> &swpara, + RTLIL::CaseRule *cs, const RTLIL::SigSpec &sig, const RTLIL::SigSpec &defval, bool ifxmode) { RTLIL::SigSpec result = defval; @@ -190,9 +280,37 @@ RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, RTLIL::CaseRule *cs, const for (auto sw : cs->switches) { + if (!swcache.check(sw)) + continue; + // detect groups of parallel cases std::vector<int> pgroups(sw->cases.size()); + bool is_simple_parallel_case = true; + if (!sw->get_bool_attribute("\\parallel_case")) { + if (!swpara.count(sw)) { + pool<Const> case_values; + for (size_t i = 0; i < sw->cases.size(); i++) { + RTLIL::CaseRule *cs2 = sw->cases[i]; + for (auto pat : cs2->compare) { + if (!pat.is_fully_def()) + goto not_simple_parallel_case; + Const cpat = pat.as_const(); + if (case_values.count(cpat)) + goto not_simple_parallel_case; + case_values.insert(cpat); + } + } + if (0) + not_simple_parallel_case: + is_simple_parallel_case = false; + swpara[sw] = is_simple_parallel_case; + } else { + is_simple_parallel_case = swpara.at(sw); + } + } + + if (!is_simple_parallel_case) { BitPatternPool pool(sw->signal.size()); bool extra_group_for_next_case = false; for (size_t i = 0; i < sw->cases.size(); i++) { @@ -214,7 +332,7 @@ RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, RTLIL::CaseRule *cs, const for (auto pat : cs2->compare) if (!pat.is_fully_const()) extra_group_for_next_case = true; - else + else if (!ifxmode) pool.take(pat); } } @@ -225,37 +343,39 @@ RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, RTLIL::CaseRule *cs, const for (size_t i = 0; i < sw->cases.size(); i++) { int case_idx = sw->cases.size() - i - 1; RTLIL::CaseRule *cs2 = sw->cases[case_idx]; - RTLIL::SigSpec value = signal_to_mux_tree(mod, cs2, sig, initial_val); + RTLIL::SigSpec value = signal_to_mux_tree(mod, swcache, swpara, cs2, sig, initial_val, ifxmode); if (last_mux_cell && pgroups[case_idx] == pgroups[case_idx+1]) - append_pmux(mod, sw->signal, cs2->compare, value, last_mux_cell, sw); + append_pmux(mod, sw->signal, cs2->compare, value, last_mux_cell, sw, ifxmode); else - result = gen_mux(mod, sw->signal, cs2->compare, value, result, last_mux_cell, sw); + result = gen_mux(mod, sw->signal, cs2->compare, value, result, last_mux_cell, sw, ifxmode); } } return result; } -void proc_mux(RTLIL::Module *mod, RTLIL::Process *proc) +void proc_mux(RTLIL::Module *mod, RTLIL::Process *proc, bool ifxmode) { - bool first = true; - while (1) - { - RTLIL::SigSpec sig = find_any_lvalue(&proc->root_case); + log("Creating decoders for process `%s.%s'.\n", mod->name.c_str(), proc->name.c_str()); - if (sig.size() == 0) - break; + SigSnippets sigsnip; + sigsnip.insert(&proc->root_case); - if (first) { - log("Creating decoders for process `%s.%s'.\n", mod->name.c_str(), proc->name.c_str()); - first = false; - } + SnippetSwCache swcache; + swcache.snippets = &sigsnip; + swcache.insert(&proc->root_case); - extract_core_signal(&proc->root_case, sig); + dict<RTLIL::SwitchRule*, bool, hash_ptr_ops> swpara; - log(" creating decoder for signal `%s'.\n", log_signal(sig)); + int cnt = 0; + for (int idx : sigsnip.snippets) + { + swcache.current_snippet = idx; + RTLIL::SigSpec sig = sigsnip.sigidx[idx]; - RTLIL::SigSpec value = signal_to_mux_tree(mod, &proc->root_case, sig, RTLIL::SigSpec(RTLIL::State::Sx, sig.size())); + log("%6d/%d: %s\n", ++cnt, GetSize(sigsnip.snippets), log_signal(sig)); + + RTLIL::SigSpec value = signal_to_mux_tree(mod, swcache, swpara, &proc->root_case, sig, RTLIL::SigSpec(RTLIL::State::Sx, sig.size()), ifxmode); mod->connect(RTLIL::SigSig(sig, value)); } } @@ -266,24 +386,38 @@ struct ProcMuxPass : public Pass { { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" proc_mux [selection]\n"); + log(" proc_mux [options] [selection]\n"); log("\n"); log("This pass converts the decision trees in processes (originating from if-else\n"); log("and case statements) to trees of multiplexer cells.\n"); log("\n"); + log(" -ifx\n"); + log(" Use Verilog simulation behavior with respect to undef values in\n"); + log(" 'case' expressions and 'if' conditions.\n"); + log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing PROC_MUX pass (convert decision trees to multiplexers).\n"); + bool ifxmode = false; + log_header(design, "Executing PROC_MUX pass (convert decision trees to multiplexers).\n"); - extra_args(args, 1, design); + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-ifx") { + ifxmode = true; + continue; + } + break; + } + extra_args(args, argidx, design); for (auto mod : design->modules()) if (design->selected(mod)) for (auto &proc_it : mod->processes) if (design->selected(mod, proc_it.second)) - proc_mux(mod, proc_it.second); + proc_mux(mod, proc_it.second, ifxmode); } } ProcMuxPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/proc/proc_rmdead.cc b/passes/proc/proc_rmdead.cc index 427e0d567..5672fb475 100644 --- a/passes/proc/proc_rmdead.cc +++ b/passes/proc/proc_rmdead.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -51,8 +51,8 @@ void proc_rmdead(RTLIL::SwitchRule *sw, int &counter) counter++; continue; } - if (pool.empty()) - sw->cases[i]->compare.clear(); + // if (pool.empty()) + // sw->cases[i]->compare.clear(); } for (auto switch_it : sw->cases[i]->switches) @@ -76,7 +76,7 @@ struct ProcRmdeadPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing PROC_RMDEAD pass (remove dead branches from decision trees).\n"); + log_header(design, "Executing PROC_RMDEAD pass (remove dead branches from decision trees).\n"); extra_args(args, 1, design); @@ -100,5 +100,5 @@ struct ProcRmdeadPass : public Pass { log("Removed a total of %d dead cases.\n", total_counter); } } ProcRmdeadPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/sat/eval.cc b/passes/sat/eval.cc index 62534ec0b..09f69cc5c 100644 --- a/passes/sat/eval.cc +++ b/passes/sat/eval.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -87,7 +87,7 @@ struct BruteForceEquivChecker BruteForceEquivChecker(RTLIL::Module *mod1, RTLIL::Module *mod2, bool ignore_x_mod1) : mod1(mod1), mod2(mod2), counter(0), errors(0), ignore_x_mod1(ignore_x_mod1) { - log("Checking for equivialence (brute-force): %s vs %s\n", mod1->name.c_str(), mod2->name.c_str()); + log("Checking for equivalence (brute-force): %s vs %s\n", mod1->name.c_str(), mod2->name.c_str()); for (auto &w : mod1->wires_) { RTLIL::Wire *wire1 = w.second; @@ -143,16 +143,16 @@ struct VlogHammerReporter { log("Verifying SAT model (%s)..\n", model_undef ? "with undef" : "without undef"); - ezDefaultSAT ez; + ezSatPtr ez; SigMap sigmap(module); - SatGen satgen(&ez, &sigmap); + SatGen satgen(ez.get(), &sigmap); satgen.model_undef = model_undef; for (auto &c : module->cells_) if (!satgen.importCell(c.second)) log_error("Failed to import cell %s (type %s) to SAT database.\n", RTLIL::id2cstr(c.first), RTLIL::id2cstr(c.second->type)); - ez.assume(satgen.signals_eq(recorded_set_vars, recorded_set_vals)); + ez->assume(satgen.signals_eq(recorded_set_vars, recorded_set_vals)); std::vector<int> y_vec = satgen.importDefSigSpec(module->wires_.at("\\y")); std::vector<bool> y_values; @@ -163,9 +163,9 @@ struct VlogHammerReporter } log(" Created SAT problem with %d variables and %d clauses.\n", - ez.numCnfVariables(), ez.numCnfClauses()); + ez->numCnfVariables(), ez->numCnfClauses()); - if (!ez.solve(y_vec, y_values)) + if (!ez->solve(y_vec, y_values)) log_error("Failed to find solution to SAT problem.\n"); for (int i = 0; i < expected_y.size(); i++) { @@ -204,7 +204,7 @@ struct VlogHammerReporter if (y_undef.at(i)) { log(" Toggling undef bit %d to test undef gating.\n", i); - if (!ez.solve(y_vec, y_values, ez.IFF(y_vec.at(i), y_values.at(i) ? ez.CONST_FALSE : ez.CONST_TRUE))) + if (!ez->solve(y_vec, y_values, ez->IFF(y_vec.at(i), y_values.at(i) ? ez->CONST_FALSE : ez->CONST_TRUE))) log_error("Failed to find solution with toggled bit!\n"); cmp_vars.push_back(y_vec.at(expected_y.size() + i)); @@ -220,15 +220,15 @@ struct VlogHammerReporter } log(" Testing if SAT solution is unique.\n"); - ez.assume(ez.vec_ne(cmp_vars, ez.vec_const(cmp_vals))); - if (ez.solve(y_vec, y_values)) + ez->assume(ez->vec_ne(cmp_vars, ez->vec_const(cmp_vals))); + if (ez->solve(y_vec, y_values)) log_error("Found two distinct solutions to SAT problem.\n"); } else { log(" Testing if SAT solution is unique.\n"); - ez.assume(ez.vec_ne(y_vec, ez.vec_const(y_values))); - if (ez.solve(y_vec, y_values)) + ez->assume(ez->vec_ne(y_vec, ez->vec_const(y_values))); + if (ez->solve(y_vec, y_values)) log_error("Found two distinct solutions to SAT problem.\n"); } @@ -389,7 +389,7 @@ struct EvalPass : public Pass { std::vector<std::string> shows, tables; bool set_undef = false; - log_header("Executing EVAL pass (evaluate the circuit given an input).\n"); + log_header(design, "Executing EVAL pass (evaluate the circuit given an input).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { @@ -421,7 +421,7 @@ struct EvalPass : public Pass { log_error("Can't find module `%s'!\n", mod2_name.c_str()); BruteForceEquivChecker checker(design->modules_.at(mod1_name), design->modules_.at(mod2_name), args[argidx-2] == "-brute_force_equiv_checker_x"); if (checker.errors > 0) - log_cmd_error("Modules are not equivialent!\n"); + log_cmd_error("Modules are not equivalent!\n"); log("Verified %s = %s (using brute-force check on %d cases).\n", mod1_name.c_str(), mod2_name.c_str(), checker.counter); return; @@ -448,7 +448,7 @@ struct EvalPass : public Pass { RTLIL::id2cstr(module->name), RTLIL::id2cstr(mod_it.first)); module = mod_it.second; } - if (module == NULL) + if (module == NULL) log_cmd_error("Can't perform EVAL on an empty selection!\n"); ConstEval ce(module); @@ -568,7 +568,7 @@ struct EvalPass : public Pass { if (tab_column_width.size() < row.size()) tab_column_width.resize(row.size()); for (size_t i = 0; i < row.size(); i++) - tab_column_width[i] = std::max(tab_column_width[i], int(row[i].size())); + tab_column_width[i] = max(tab_column_width[i], int(row[i].size())); } log("\n"); @@ -594,10 +594,10 @@ struct EvalPass : public Pass { log("\n"); if (undef.size() > 0) { undef.sort_and_unify(); - log("Assumend undef (x) value for the following singals: %s\n\n", log_signal(undef)); + log("Assumed undef (x) value for the following signals: %s\n\n", log_signal(undef)); } } } } EvalPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/sat/expose.cc b/passes/sat/expose.cc index b012bc6a4..9427547f3 100644 --- a/passes/sat/expose.cc +++ b/passes/sat/expose.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -116,7 +116,7 @@ void create_dff_dq_map(std::map<RTLIL::IdString, dff_map_info_t> &map, RTLIL::De info.cell = it.second; if (info.cell->type == "$dff") { - info.bit_clk = sigmap(info.cell->getPort("\\CLK")).to_single_sigbit(); + info.bit_clk = sigmap(info.cell->getPort("\\CLK")).as_bit(); info.clk_polarity = info.cell->parameters.at("\\CLK_POLARITY").as_bool(); std::vector<RTLIL::SigBit> sig_d = sigmap(info.cell->getPort("\\D")).to_sigbit_vector(); std::vector<RTLIL::SigBit> sig_q = sigmap(info.cell->getPort("\\Q")).to_sigbit_vector(); @@ -128,8 +128,8 @@ void create_dff_dq_map(std::map<RTLIL::IdString, dff_map_info_t> &map, RTLIL::De } if (info.cell->type == "$adff") { - info.bit_clk = sigmap(info.cell->getPort("\\CLK")).to_single_sigbit(); - info.bit_arst = sigmap(info.cell->getPort("\\ARST")).to_single_sigbit(); + info.bit_clk = sigmap(info.cell->getPort("\\CLK")).as_bit(); + info.bit_arst = sigmap(info.cell->getPort("\\ARST")).as_bit(); info.clk_polarity = info.cell->parameters.at("\\CLK_POLARITY").as_bool(); info.arst_polarity = info.cell->parameters.at("\\ARST_POLARITY").as_bool(); std::vector<RTLIL::SigBit> sig_d = sigmap(info.cell->getPort("\\D")).to_sigbit_vector(); @@ -144,21 +144,21 @@ void create_dff_dq_map(std::map<RTLIL::IdString, dff_map_info_t> &map, RTLIL::De } if (info.cell->type == "$_DFF_N_" || info.cell->type == "$_DFF_P_") { - info.bit_clk = sigmap(info.cell->getPort("\\C")).to_single_sigbit(); + info.bit_clk = sigmap(info.cell->getPort("\\C")).as_bit(); info.clk_polarity = info.cell->type == "$_DFF_P_"; - info.bit_d = sigmap(info.cell->getPort("\\D")).to_single_sigbit(); - bit_info[sigmap(info.cell->getPort("\\Q")).to_single_sigbit()] = info; + info.bit_d = sigmap(info.cell->getPort("\\D")).as_bit(); + bit_info[sigmap(info.cell->getPort("\\Q")).as_bit()] = info; continue; } if (info.cell->type.size() == 10 && info.cell->type.substr(0, 6) == "$_DFF_") { - info.bit_clk = sigmap(info.cell->getPort("\\C")).to_single_sigbit(); - info.bit_arst = sigmap(info.cell->getPort("\\R")).to_single_sigbit(); + info.bit_clk = sigmap(info.cell->getPort("\\C")).as_bit(); + info.bit_arst = sigmap(info.cell->getPort("\\R")).as_bit(); info.clk_polarity = info.cell->type[6] == 'P'; info.arst_polarity = info.cell->type[7] == 'P'; info.arst_value = info.cell->type[0] == '1' ? RTLIL::State::S1 : RTLIL::State::S0; - info.bit_d = sigmap(info.cell->getPort("\\D")).to_single_sigbit(); - bit_info[sigmap(info.cell->getPort("\\Q")).to_single_sigbit()] = info; + info.bit_d = sigmap(info.cell->getPort("\\D")).as_bit(); + bit_info[sigmap(info.cell->getPort("\\Q")).as_bit()] = info; continue; } } @@ -237,8 +237,8 @@ struct ExposePass : public Pass { log(" signal path at that wire.\n"); log("\n"); log(" -shared\n"); - log(" only expose those signals that are shared ammong the selected modules.\n"); - log(" this is useful for preparing modules for equivialence checking.\n"); + log(" only expose those signals that are shared among the selected modules.\n"); + log(" this is useful for preparing modules for equivalence checking.\n"); log("\n"); log(" -evert\n"); log(" also turn connections to instances of other modules to additional\n"); @@ -262,7 +262,7 @@ struct ExposePass : public Pass { bool flag_evert_dff = false; std::string sep = "."; - log_header("Executing EXPOSE pass (exposing internal signals as outputs).\n"); + log_header(design, "Executing EXPOSE pass (exposing internal signals as outputs).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) @@ -646,5 +646,5 @@ struct ExposePass : public Pass { } } } ExposePass; - + PRIVATE_NAMESPACE_END diff --git a/passes/sat/freduce.cc b/passes/sat/freduce.cc index fbca35861..77263f6a2 100644 --- a/passes/sat/freduce.cc +++ b/passes/sat/freduce.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -73,7 +73,7 @@ struct FindReducedInputs SigMap &sigmap; drivers_t &drivers; - ezDefaultSAT ez; + ezSatPtr ez; std::set<RTLIL::Cell*> ez_cells; SatGen satgen; @@ -81,7 +81,7 @@ struct FindReducedInputs std::vector<int> sat_pi_uniq_bitvec; FindReducedInputs(SigMap &sigmap, drivers_t &drivers) : - sigmap(sigmap), drivers(drivers), satgen(&ez, &sigmap) + sigmap(sigmap), drivers(drivers), satgen(ez.get(), &sigmap) { satgen.model_undef = true; } @@ -104,30 +104,30 @@ struct FindReducedInputs satgen.setContext(&sigmap, "A"); int sat_a = satgen.importSigSpec(bit).front(); - ez.assume(ez.NOT(satgen.importUndefSigSpec(bit).front())); + ez->assume(ez->NOT(satgen.importUndefSigSpec(bit).front())); satgen.setContext(&sigmap, "B"); int sat_b = satgen.importSigSpec(bit).front(); - ez.assume(ez.NOT(satgen.importUndefSigSpec(bit).front())); + ez->assume(ez->NOT(satgen.importUndefSigSpec(bit).front())); int idx = sat_pi.size(); size_t idx_bits = get_bits(idx); if (sat_pi_uniq_bitvec.size() != idx_bits) { - sat_pi_uniq_bitvec.push_back(ez.frozen_literal(stringf("uniq_%d", int(idx_bits)-1))); + sat_pi_uniq_bitvec.push_back(ez->frozen_literal(stringf("uniq_%d", int(idx_bits)-1))); for (auto &it : sat_pi) - ez.assume(ez.OR(ez.NOT(it.second), ez.NOT(sat_pi_uniq_bitvec.back()))); + ez->assume(ez->OR(ez->NOT(it.second), ez->NOT(sat_pi_uniq_bitvec.back()))); } log_assert(sat_pi_uniq_bitvec.size() == idx_bits); - sat_pi[bit] = ez.frozen_literal(stringf("p, falsei_%s", log_signal(bit))); - ez.assume(ez.IFF(ez.XOR(sat_a, sat_b), sat_pi[bit])); + sat_pi[bit] = ez->frozen_literal(stringf("p, falsei_%s", log_signal(bit))); + ez->assume(ez->IFF(ez->XOR(sat_a, sat_b), sat_pi[bit])); for (size_t i = 0; i < idx_bits; i++) if ((idx & (1 << i)) == 0) - ez.assume(ez.OR(ez.NOT(sat_pi[bit]), ez.NOT(sat_pi_uniq_bitvec[i]))); + ez->assume(ez->OR(ez->NOT(sat_pi[bit]), ez->NOT(sat_pi_uniq_bitvec[i]))); else - ez.assume(ez.OR(ez.NOT(sat_pi[bit]), sat_pi_uniq_bitvec[i])); + ez->assume(ez->OR(ez->NOT(sat_pi[bit]), sat_pi_uniq_bitvec[i])); } void register_cone_worker(std::set<RTLIL::SigBit> &pi, std::set<RTLIL::SigBit> &sigdone, RTLIL::SigBit out) @@ -201,7 +201,7 @@ struct FindReducedInputs model_expr.push_back(sat_pi.at(pi[i])); } - if (!ez.solve(model_expr, model, ez.expression(ezSAT::OpOr, model_expr), ez.XOR(output_a, output_b), ez.NOT(output_undef_a), ez.NOT(output_undef_b))) + if (!ez->solve(model_expr, model, ez->expression(ezSAT::OpOr, model_expr), ez->XOR(output_a, output_b), ez->NOT(output_undef_a), ez->NOT(output_undef_b))) break; int found_count = 0; @@ -229,8 +229,9 @@ struct PerformReduction SigMap &sigmap; drivers_t &drivers; std::set<std::pair<RTLIL::SigBit, RTLIL::SigBit>> &inv_pairs; + pool<SigBit> recursion_guard; - ezDefaultSAT ez; + ezSatPtr ez; SatGen satgen; std::vector<int> sat_pi, sat_out, sat_def; @@ -246,6 +247,15 @@ struct PerformReduction if (sigdepth.count(out) != 0) return sigdepth.at(out); + if (recursion_guard.count(out)) { + string loop_signals; + for (auto loop_bit : recursion_guard) + loop_signals += string(" ") + log_signal(loop_bit); + log_error("Found logic loop:%s\n", loop_signals.c_str()); + } + + recursion_guard.insert(out); + if (drivers.count(out) != 0) { std::pair<RTLIL::Cell*, std::set<RTLIL::SigBit>> &drv = drivers.at(out); if (celldone.count(drv.first) == 0) { @@ -255,20 +265,21 @@ struct PerformReduction } int max_child_depth = 0; for (auto &bit : drv.second) - max_child_depth = std::max(register_cone_worker(celldone, sigdepth, bit), max_child_depth); + max_child_depth = max(register_cone_worker(celldone, sigdepth, bit), max_child_depth); sigdepth[out] = max_child_depth + 1; } else { pi_bits.push_back(out); sat_pi.push_back(satgen.importSigSpec(out).front()); - ez.assume(ez.NOT(satgen.importUndefSigSpec(out).front())); + ez->assume(ez->NOT(satgen.importUndefSigSpec(out).front())); sigdepth[out] = 0; } + recursion_guard.erase(out); return sigdepth.at(out); } PerformReduction(SigMap &sigmap, drivers_t &drivers, std::set<std::pair<RTLIL::SigBit, RTLIL::SigBit>> &inv_pairs, std::vector<RTLIL::SigBit> &bits, int cone_size) : - sigmap(sigmap), drivers(drivers), inv_pairs(inv_pairs), satgen(&ez, &sigmap), out_bits(bits), cone_size(cone_size) + sigmap(sigmap), drivers(drivers), inv_pairs(inv_pairs), satgen(ez.get(), &sigmap), out_bits(bits), cone_size(cone_size) { satgen.model_undef = true; @@ -278,15 +289,15 @@ struct PerformReduction for (auto &bit : bits) { out_depth.push_back(register_cone_worker(celldone, sigdepth, bit)); sat_out.push_back(satgen.importSigSpec(bit).front()); - sat_def.push_back(ez.NOT(satgen.importUndefSigSpec(bit).front())); + sat_def.push_back(ez->NOT(satgen.importUndefSigSpec(bit).front())); } if (inv_mode && cone_size > 0) { - if (!ez.solve(sat_out, out_inverted, ez.expression(ezSAT::OpAnd, sat_def))) + if (!ez->solve(sat_out, out_inverted, ez->expression(ezSAT::OpAnd, sat_def))) log_error("Solving for initial model failed!\n"); for (size_t i = 0; i < sat_out.size(); i++) if (out_inverted.at(i)) - sat_out[i] = ez.NOT(sat_out[i]); + sat_out[i] = ez->NOT(sat_out[i]); } else out_inverted = std::vector<bool>(sat_out.size(), false); } @@ -296,8 +307,8 @@ struct PerformReduction if (verbose_level == 1) log(" Finding const value for %s.\n", log_signal(out_bits[idx])); - bool can_be_set = ez.solve(ez.AND(sat_out[idx], sat_def[idx])); - bool can_be_clr = ez.solve(ez.AND(ez.NOT(sat_out[idx]), sat_def[idx])); + bool can_be_set = ez->solve(ez->AND(sat_out[idx], sat_def[idx])); + bool can_be_clr = ez->solve(ez->AND(ez->NOT(sat_out[idx]), sat_def[idx])); log_assert(!can_be_set || !can_be_clr); RTLIL::SigBit value(RTLIL::State::Sx); @@ -355,8 +366,8 @@ struct PerformReduction std::vector<int> sat_set_list, sat_clr_list; for (int idx : bucket) { - sat_set_list.push_back(ez.AND(sat_out[idx], sat_def[idx])); - sat_clr_list.push_back(ez.AND(ez.NOT(sat_out[idx]), sat_def[idx])); + sat_set_list.push_back(ez->AND(sat_out[idx], sat_def[idx])); + sat_clr_list.push_back(ez->AND(ez->NOT(sat_out[idx]), sat_def[idx])); } std::vector<int> modelVars = sat_out; @@ -366,7 +377,7 @@ struct PerformReduction if (verbose_level >= 2) modelVars.insert(modelVars.end(), sat_pi.begin(), sat_pi.end()); - if (ez.solve(modelVars, model, ez.expression(ezSAT::OpOr, sat_set_list), ez.expression(ezSAT::OpOr, sat_clr_list))) + if (ez->solve(modelVars, model, ez->expression(ezSAT::OpOr, sat_set_list), ez->expression(ezSAT::OpOr, sat_clr_list))) { int iter_count = 1; @@ -379,13 +390,13 @@ struct PerformReduction for (int idx : bucket) if (!model[sat_out.size() + idx]) { - sat_set_list.push_back(ez.AND(sat_out[idx], sat_def[idx])); - sat_clr_list.push_back(ez.AND(ez.NOT(sat_out[idx]), sat_def[idx])); + sat_set_list.push_back(ez->AND(sat_out[idx], sat_def[idx])); + sat_clr_list.push_back(ez->AND(ez->NOT(sat_out[idx]), sat_def[idx])); } else { sat_def_list.push_back(sat_def[idx]); } - if (!ez.solve(modelVars, model, ez.expression(ezSAT::OpOr, sat_set_list), ez.expression(ezSAT::OpOr, sat_clr_list), ez.expression(ezSAT::OpAnd, sat_def_list))) + if (!ez->solve(modelVars, model, ez->expression(ezSAT::OpOr, sat_set_list), ez->expression(ezSAT::OpOr, sat_clr_list), ez->expression(ezSAT::OpAnd, sat_def_list))) break; iter_count++; } @@ -431,7 +442,7 @@ struct PerformReduction for (int idx2 : bucket) if (idx != idx2) sat_def_list.push_back(sat_def[idx2]); - if (ez.solve(ez.NOT(sat_def[idx]), ez.expression(ezSAT::OpOr, sat_def_list))) + if (ez->solve(ez->NOT(sat_def[idx]), ez->expression(ezSAT::OpOr, sat_def_list))) undef_slaves.push_back(idx); } @@ -446,7 +457,7 @@ struct PerformReduction out_depth[idx] = std::numeric_limits<int>::max(); if (verbose_level >= 1) { - log("%s Found %d equivialent signals:", indt, int(bucket.size())); + log("%s Found %d equivalent signals:", indt, int(bucket.size())); for (int idx : bucket) log("%s%s%s", idx == bucket.front() ? " " : ", ", out_inverted[idx] ? "~" : "", log_signal(out_bits[idx])); log("\n"); @@ -495,7 +506,7 @@ struct PerformReduction std::vector<RTLIL::SigBit> r_sigbits; for (int idx : r) r_sigbits.push_back(out_bits[idx]); - log(" Found group of %d equivialent signals: %s\n", int(r.size()), log_signal(r_sigbits)); + log(" Found group of %d equivalent signals: %s\n", int(r.size()), log_signal(r_sigbits)); } std::vector<int> undef_slaves; @@ -505,7 +516,7 @@ struct PerformReduction for (int idx2 : r) if (idx != idx2) sat_def_list.push_back(sat_def[idx2]); - if (ez.solve(ez.NOT(sat_def[idx]), ez.expression(ezSAT::OpOr, sat_def_list))) + if (ez->solve(ez->NOT(sat_def[idx]), ez->expression(ezSAT::OpOr, sat_def_list))) undef_slaves.push_back(idx); } @@ -681,7 +692,7 @@ struct FreduceWorker if (!dump_prefix.empty()) dump(); - log(" Rewiring %d equivialent groups:\n", int(equiv.size())); + log(" Rewiring %d equivalent groups:\n", int(equiv.size())); int rewired_sigbits = 0; for (auto &grp : equiv) { @@ -755,7 +766,7 @@ struct FreducePass : public Pass { log(" freduce [options] [selection]\n"); log("\n"); log("This pass performs functional reduction in the circuit. I.e. if two nodes are\n"); - log("equivialent, they are merged to one node and one of the redundant drivers is\n"); + log("equivalent, they are merged to one node and one of the redundant drivers is\n"); log("disconnected. A subsequent call to 'clean' will remove the redundant drivers.\n"); log("\n"); log(" -v, -vv\n"); @@ -773,7 +784,7 @@ struct FreducePass : public Pass { log(" operation. this is mostly used for debugging the freduce command.\n"); log("\n"); log("This pass is undef-aware, i.e. it considers don't-care values for detecting\n"); - log("equivialent nodes.\n"); + log("equivalent nodes.\n"); log("\n"); log("All selected wires are considered for rewiring. The selected cells cover the\n"); log("circuit that is analyzed.\n"); @@ -787,7 +798,7 @@ struct FreducePass : public Pass { inv_mode = false; dump_prefix = std::string(); - log_header("Executing FREDUCE pass (perform functional reduction).\n"); + log_header(design, "Executing FREDUCE pass (perform functional reduction).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { @@ -825,5 +836,5 @@ struct FreducePass : public Pass { log("Rewired a total of %d signal bits.\n", bitcount); } } FreducePass; - + PRIVATE_NAMESPACE_END diff --git a/passes/sat/miter.cc b/passes/sat/miter.cc index 9853cd0c6..4854e19bf 100644 --- a/passes/sat/miter.cc +++ b/passes/sat/miter.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -32,7 +32,7 @@ void create_miter_equiv(struct Pass *that, std::vector<std::string> args, RTLIL: bool flag_make_assert = false; bool flag_flatten = false; - log_header("Executing MITER pass (creating miter circuit).\n"); + log_header(design, "Executing MITER pass (creating miter circuit).\n"); size_t argidx; for (argidx = 2; argidx < args.size(); argidx++) @@ -61,7 +61,7 @@ void create_miter_equiv(struct Pass *that, std::vector<std::string> args, RTLIL: } if (argidx+3 != args.size() || args[argidx].substr(0, 1) == "-") that->cmd_error(args, argidx, "command argument error"); - + RTLIL::IdString gold_name = RTLIL::escape_id(args[argidx++]); RTLIL::IdString gate_name = RTLIL::escape_id(args[argidx++]); RTLIL::IdString miter_name = RTLIL::escape_id(args[argidx++]); @@ -71,7 +71,7 @@ void create_miter_equiv(struct Pass *that, std::vector<std::string> args, RTLIL: if (design->modules_.count(gate_name) == 0) log_cmd_error("Can't find gate module %s!\n", gate_name.c_str()); if (design->modules_.count(miter_name) != 0) - log_cmd_error("There is already a module %s!\n", gate_name.c_str()); + log_cmd_error("There is already a module %s!\n", miter_name.c_str()); RTLIL::Module *gold_module = design->modules_.at(gold_name); RTLIL::Module *gate_module = design->modules_.at(gate_name); @@ -254,7 +254,80 @@ void create_miter_equiv(struct Pass *that, std::vector<std::string> args, RTLIL: if (flag_flatten) { log_push(); - Pass::call_on_module(design, miter_module, "flatten; opt_const -keepdc -undriven;;"); + Pass::call_on_module(design, miter_module, "flatten; opt_expr -keepdc -undriven;;"); + log_pop(); + } +} + +void create_miter_assert(struct Pass *that, std::vector<std::string> args, RTLIL::Design *design) +{ + bool flag_make_outputs = false; + bool flag_flatten = false; + + log_header(design, "Executing MITER pass (creating miter circuit).\n"); + + size_t argidx; + for (argidx = 2; argidx < args.size(); argidx++) + { + if (args[argidx] == "-make_outputs") { + flag_make_outputs = true; + continue; + } + if (args[argidx] == "-flatten") { + flag_flatten = true; + continue; + } + break; + } + if ((argidx+1 != args.size() && argidx+2 != args.size()) || args[argidx].substr(0, 1) == "-") + that->cmd_error(args, argidx, "command argument error"); + + IdString module_name = RTLIL::escape_id(args[argidx++]); + IdString miter_name = argidx < args.size() ? RTLIL::escape_id(args[argidx++]) : ""; + + if (design->modules_.count(module_name) == 0) + log_cmd_error("Can't find module %s!\n", module_name.c_str()); + if (!miter_name.empty() && design->modules_.count(miter_name) != 0) + log_cmd_error("There is already a module %s!\n", miter_name.c_str()); + + Module *module = design->module(module_name); + + if (!miter_name.empty()) { + module = module->clone(); + module->name = miter_name; + design->add(module); + } + + if (!flag_make_outputs) + for (auto wire : module->wires()) + wire->port_output = false; + + Wire *trigger = module->addWire("\\trigger"); + trigger->port_output = true; + module->fixup_ports(); + + if (flag_flatten) { + log_push(); + Pass::call_on_module(design, module, "flatten;;"); + log_pop(); + } + + SigSpec or_signals; + vector<Cell*> cell_list = module->cells(); + for (auto cell : cell_list) { + if (cell->type == "$assert") { + SigBit is_active = module->Nex(NEW_ID, cell->getPort("\\A"), State::S1); + SigBit is_enabled = module->Eqx(NEW_ID, cell->getPort("\\EN"), State::S1); + or_signals.append(module->And(NEW_ID, is_active, is_enabled)); + module->remove(cell); + } + } + + module->addReduceOr(NEW_ID, or_signals, trigger); + + if (flag_flatten) { + log_push(); + Pass::call_on_module(design, module, "opt_expr -keepdc -undriven;;"); log_pop(); } } @@ -267,7 +340,7 @@ struct MiterPass : public Pass { log("\n"); log(" miter -equiv [options] gold_name gate_name miter_name\n"); log("\n"); - log("Creates a miter circuit for equivialence checking. The gold- and gate- modules\n"); + log("Creates a miter circuit for equivalence checking. The gold- and gate- modules\n"); log("must have the same interfaces. The miter circuit will have all inputs of the\n"); log("two source modules, prefixed with 'in_'. The miter circuit has a 'trigger'\n"); log("output that goes high if an output mismatch between the two source modules is\n"); @@ -288,7 +361,21 @@ struct MiterPass : public Pass { log(" also create an 'assert' cell that checks if trigger is always low.\n"); log("\n"); log(" -flatten\n"); - log(" call 'flatten; opt_const -keepdc -undriven;;' on the miter circuit.\n"); + log(" call 'flatten; opt_expr -keepdc -undriven;;' on the miter circuit.\n"); + log("\n"); + log("\n"); + log(" miter -assert [options] module [miter_name]\n"); + log("\n"); + log("Creates a miter circuit for property checking. All input ports are kept,\n"); + log("output ports are discarded. An additional output 'trigger' is created that\n"); + log("goes high when an assert is violated. Without a miter_name, the existing\n"); + log("module is modified.\n"); + log("\n"); + log(" -make_outputs\n"); + log(" keep module output ports.\n"); + log("\n"); + log(" -flatten\n"); + log(" call 'flatten; opt_expr -keepdc -undriven;;' on the miter circuit.\n"); log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) @@ -298,8 +385,13 @@ struct MiterPass : public Pass { return; } + if (args.size() > 1 && args[1] == "-assert") { + create_miter_assert(this, args, design); + return; + } + log_cmd_error("Missing mode parameter!\n"); } } MiterPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/sat/sat.cc b/passes/sat/sat.cc index 1aae421f0..c3cb435d1 100644 --- a/passes/sat/sat.cc +++ b/passes/sat/sat.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -41,16 +41,17 @@ struct SatHelper RTLIL::Design *design; RTLIL::Module *module; - ezDefaultSAT ez; SigMap sigmap; CellTypes ct; + + ezSatPtr ez; SatGen satgen; // additional constraints std::vector<std::pair<std::string, std::string>> sets, prove, prove_x, sets_init; std::map<int, std::vector<std::pair<std::string, std::string>>> sets_at; std::map<int, std::vector<std::string>> unsets_at; - bool prove_asserts; + bool prove_asserts, set_assumes; // undef constraints bool enable_undef, set_init_def, set_init_undef, set_init_zero, ignore_unknown_cells; @@ -65,7 +66,7 @@ struct SatHelper bool gotTimeout; SatHelper(RTLIL::Design *design, RTLIL::Module *module, bool enable_undef) : - design(design), module(module), sigmap(module), ct(design), satgen(&ez, &sigmap) + design(design), module(module), sigmap(module), ct(design), satgen(ez.get(), &sigmap) { this->enable_undef = enable_undef; satgen.model_undef = enable_undef; @@ -155,7 +156,7 @@ struct SatHelper if (set_init_def) { RTLIL::SigSpec rem = satgen.initial_state.export_all(); std::vector<int> undef_rem = satgen.importUndefSigSpec(rem, 1); - ez.assume(ez.NOT(ez.expression(ezSAT::OpOr, undef_rem))); + ez->assume(ez->NOT(ez->expression(ezSAT::OpOr, undef_rem))); } if (set_init_undef) { @@ -179,7 +180,7 @@ struct SatHelper log("Final constraint equation: %s = %s\n\n", log_signal(big_lhs), log_signal(big_rhs)); check_undef_enabled(big_lhs), check_undef_enabled(big_rhs); - ez.assume(satgen.signals_eq(big_lhs, big_rhs, 1)); + ez->assume(satgen.signals_eq(big_lhs, big_rhs, 1)); } void setup(int timestep = -1) @@ -250,7 +251,7 @@ struct SatHelper log("Final constraint equation: %s = %s\n", log_signal(big_lhs), log_signal(big_rhs)); check_undef_enabled(big_lhs), check_undef_enabled(big_rhs); - ez.assume(satgen.signals_eq(big_lhs, big_rhs, timestep)); + ez->assume(satgen.signals_eq(big_lhs, big_rhs, timestep)); // 0 = sets_def // 1 = sets_any_undef @@ -310,28 +311,36 @@ struct SatHelper log("Import %s constraint for this timestep: %s\n", t == 0 ? "def" : t == 1 ? "any_undef" : "all_undef", log_signal(sig)); std::vector<int> undef_sig = satgen.importUndefSigSpec(sig, timestep); if (t == 0) - ez.assume(ez.NOT(ez.expression(ezSAT::OpOr, undef_sig))); + ez->assume(ez->NOT(ez->expression(ezSAT::OpOr, undef_sig))); if (t == 1) - ez.assume(ez.expression(ezSAT::OpOr, undef_sig)); + ez->assume(ez->expression(ezSAT::OpOr, undef_sig)); if (t == 2) - ez.assume(ez.expression(ezSAT::OpAnd, undef_sig)); + ez->assume(ez->expression(ezSAT::OpAnd, undef_sig)); } int import_cell_counter = 0; - for (auto &c : module->cells_) - if (design->selected(module, c.second)) { - // log("Import cell: %s\n", RTLIL::id2cstr(c.first)); - if (satgen.importCell(c.second, timestep)) { - for (auto &p : c.second->connections()) - if (ct.cell_output(c.second->type, p.first)) - show_drivers.insert(sigmap(p.second), c.second); + for (auto cell : module->cells()) + if (design->selected(module, cell)) { + // log("Import cell: %s\n", RTLIL::id2cstr(cell->name)); + if (satgen.importCell(cell, timestep)) { + for (auto &p : cell->connections()) + if (ct.cell_output(cell->type, p.first)) + show_drivers.insert(sigmap(p.second), cell); import_cell_counter++; } else if (ignore_unknown_cells) - log_warning("Failed to import cell %s (type %s) to SAT database.\n", RTLIL::id2cstr(c.first), RTLIL::id2cstr(c.second->type)); + log_warning("Failed to import cell %s (type %s) to SAT database.\n", RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type)); else - log_error("Failed to import cell %s (type %s) to SAT database.\n", RTLIL::id2cstr(c.first), RTLIL::id2cstr(c.second->type)); + log_error("Failed to import cell %s (type %s) to SAT database.\n", RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type)); } log("Imported %d cells to SAT database.\n", import_cell_counter); + + if (set_assumes) { + RTLIL::SigSpec assumes_a, assumes_en; + satgen.getAssumes(assumes_a, assumes_en, timestep); + for (int i = 0; i < GetSize(assumes_a); i++) + log("Import constraint from assume cell: %s when %s.\n", log_signal(assumes_a[i]), log_signal(assumes_en[i])); + ez->assume(satgen.importAssumes(timestep)); + } } int setup_proof(int timestep = -1) @@ -401,7 +410,7 @@ struct SatHelper std::vector<int> undef_rhs = satgen.importUndefSigSpec(big_rhs, timestep); for (size_t i = 0; i < value_lhs.size(); i++) - prove_bits.push_back(ez.OR(undef_lhs.at(i), ez.AND(ez.NOT(undef_rhs.at(i)), ez.NOT(ez.XOR(value_lhs.at(i), value_rhs.at(i)))))); + prove_bits.push_back(ez->OR(undef_lhs.at(i), ez->AND(ez->NOT(undef_rhs.at(i)), ez->NOT(ez->XOR(value_lhs.at(i), value_rhs.at(i)))))); } if (prove_asserts) { @@ -412,22 +421,22 @@ struct SatHelper prove_bits.push_back(satgen.importAsserts(timestep)); } - return ez.expression(ezSAT::OpAnd, prove_bits); + return ez->expression(ezSAT::OpAnd, prove_bits); } void force_unique_state(int timestep_from, int timestep_to) { RTLIL::SigSpec state_signals = satgen.initial_state.export_all(); for (int i = timestep_from; i < timestep_to; i++) - ez.assume(ez.NOT(satgen.signals_eq(state_signals, state_signals, i, timestep_to))); + ez->assume(ez->NOT(satgen.signals_eq(state_signals, state_signals, i, timestep_to))); } bool solve(const std::vector<int> &assumptions) { log_assert(gotTimeout == false); - ez.setSolverTimeout(timeout); - bool success = ez.solve(modelExpressions, modelValues, assumptions); - if (ez.getSolverTimoutStatus()) + ez->setSolverTimeout(timeout); + bool success = ez->solve(modelExpressions, modelValues, assumptions); + if (ez->getSolverTimoutStatus()) gotTimeout = true; return success; } @@ -435,9 +444,9 @@ struct SatHelper bool solve(int a = 0, int b = 0, int c = 0, int d = 0, int e = 0, int f = 0) { log_assert(gotTimeout == false); - ez.setSolverTimeout(timeout); - bool success = ez.solve(modelExpressions, modelValues, a, b, c, d, e, f); - if (ez.getSolverTimoutStatus()) + ez->setSolverTimeout(timeout); + bool success = ez->solve(modelExpressions, modelValues, a, b, c, d, e, f); + if (ez->getSolverTimoutStatus()) gotTimeout = true; return success; } @@ -478,7 +487,7 @@ struct SatHelper maybe_undef.push_back(modelExpressions.at(modelExpressions.size()/2 + i)); backupValues.swap(modelValues); - if (!solve(ez.expression(ezSAT::OpAnd, must_undef), ez.expression(ezSAT::OpOr, maybe_undef))) + if (!solve(ez->expression(ezSAT::OpAnd, must_undef), ez->expression(ezSAT::OpOr, maybe_undef))) break; } @@ -597,8 +606,8 @@ struct SatHelper int maxModelWidth = 10; for (auto &info : modelInfo) { - maxModelName = std::max(maxModelName, int(info.description.size())); - maxModelWidth = std::max(maxModelWidth, info.width); + maxModelName = max(maxModelName, int(info.description.size())); + maxModelWidth = max(maxModelWidth, info.width); } log("\n"); @@ -621,11 +630,11 @@ struct SatHelper "---------------------------------------------------------------------------------------------------"; if (last_timestep == -2) { log(max_timestep > 0 ? " Time " : " "); - log("%-*s %10s %10s %*s\n", maxModelName+10, "Signal Name", "Dec", "Hex", maxModelWidth+5, "Bin"); + log("%-*s %11s %9s %*s\n", maxModelName+5, "Signal Name", "Dec", "Hex", maxModelWidth+3, "Bin"); } log(max_timestep > 0 ? " ---- " : " "); - log("%*.*s %10.10s %10.10s %*.*s\n", maxModelName+10, maxModelName+10, - hline, hline, hline, maxModelWidth+5, maxModelWidth+5, hline); + log("%*.*s %11.11s %9.9s %*.*s\n", maxModelName+5, maxModelName+5, + hline, hline, hline, maxModelWidth+3, maxModelWidth+3, hline); last_timestep = info.timestep; } @@ -638,9 +647,9 @@ struct SatHelper log(" "); if (info.width <= 32 && !found_undef) - log("%-*s %10d %10x %*s\n", maxModelName+10, info.description.c_str(), value.as_int(), value.as_int(), maxModelWidth+5, value.as_string().c_str()); + log("%-*s %11d %9x %*s\n", maxModelName+5, info.description.c_str(), value.as_int(), value.as_int(), maxModelWidth+3, value.as_string().c_str()); else - log("%-*s %10s %10s %*s\n", maxModelName+10, info.description.c_str(), "--", "--", maxModelWidth+5, value.as_string().c_str()); + log("%-*s %11s %9s %*s\n", maxModelName+5, info.description.c_str(), "--", "--", maxModelWidth+3, value.as_string().c_str()); } if (last_timestep == -2) @@ -671,7 +680,7 @@ struct SatHelper fprintf(f, " %s\n", stime); fprintf(f, "$end\n"); fprintf(f, "$version\n"); - fprintf(f, " Generated by %s\n", yosys_version_str); + fprintf(f, " Generated by %s\n", yosys_version_str); fprintf(f, "$end\n"); fprintf(f, "$comment\n"); fprintf(f, " Generated from SAT problem in module %s (declared at %s)\n", @@ -750,6 +759,80 @@ struct SatHelper fclose(f); } + void dump_model_to_json(std::string json_file_name) + { + FILE *f = fopen(json_file_name.c_str(), "w"); + if (!f) + log_cmd_error("Can't open output file `%s' for writing: %s\n", json_file_name.c_str(), strerror(errno)); + + log("Dumping SAT model to WaveJSON file '%s'.\n", json_file_name.c_str()); + + int mintime = 1, maxtime = 0, maxwidth = 0;; + dict<string, pair<int, dict<int, Const>>> wavedata; + + for (auto &info : modelInfo) + { + Const value; + for (int i = 0; i < info.width; i++) { + value.bits.push_back(modelValues.at(info.offset+i) ? RTLIL::State::S1 : RTLIL::State::S0); + if (enable_undef && modelValues.at(modelExpressions.size()/2 + info.offset + i)) + value.bits.back() = RTLIL::State::Sx; + } + + wavedata[info.description].first = info.width; + wavedata[info.description].second[info.timestep] = value; + mintime = min(mintime, info.timestep); + maxtime = max(maxtime, info.timestep); + maxwidth = max(maxwidth, info.width); + } + + fprintf(f, "{ \"signal\": ["); + bool fist_wavedata = true; + for (auto &wd : wavedata) + { + fprintf(f, "%s", fist_wavedata ? "\n" : ",\n"); + fist_wavedata = false; + + vector<string> data; + string name = wd.first.c_str(); + while (name.substr(0, 1) == "\\") + name = name.substr(1); + + fprintf(f, " { \"name\": \"%s\", \"wave\": \"", name.c_str()); + for (int i = mintime; i <= maxtime; i++) { + if (wd.second.second.count(i)) { + string this_data = wd.second.second[i].as_string(); + char ch = '='; + if (wd.second.first == 1) + ch = this_data[0]; + if (!data.empty() && data.back() == this_data) { + fprintf(f, "."); + } else { + data.push_back(this_data); + fprintf(f, "%c", ch); + } + } else { + data.push_back(""); + fprintf(f, "4"); + } + } + if (wd.second.first != 1) { + fprintf(f, "\", \"data\": ["); + for (int i = 0; i < GetSize(data); i++) + fprintf(f, "%s\"%s\"", i ? ", " : "", data[i].c_str()); + fprintf(f, "] }"); + } else { + fprintf(f, "\" }"); + } + } + fprintf(f, "\n ],\n"); + fprintf(f, " \"config\": {\n"); + fprintf(f, " \"hscale\": %.2f\n", maxwidth / 4.0); + fprintf(f, " }\n"); + fprintf(f, "}\n"); + fclose(f); + } + void invalidate_model(bool max_undef) { std::vector<int> clause; @@ -758,12 +841,12 @@ struct SatHelper int bit = modelExpressions.at(i), bit_undef = modelExpressions.at(modelExpressions.size()/2 + i); bool val = modelValues.at(i), val_undef = modelValues.at(modelExpressions.size()/2 + i); if (!max_undef || !val_undef) - clause.push_back(val_undef ? ez.NOT(bit_undef) : val ? ez.NOT(bit) : bit); + clause.push_back(val_undef ? ez->NOT(bit_undef) : val ? ez->NOT(bit) : bit); } } else for (size_t i = 0; i < modelExpressions.size(); i++) - clause.push_back(modelValues.at(i) ? ez.NOT(modelExpressions.at(i)) : modelExpressions.at(i)); - ez.assume(ez.expression(ezSAT::OpOr, clause)); + clause.push_back(modelValues.at(i) ? ez->NOT(modelExpressions.at(i)) : modelExpressions.at(i)); + ez->assume(ez->expression(ezSAT::OpOr, clause)); } }; @@ -853,6 +936,9 @@ struct SatPass : public Pass { log(" -show-inputs, -show-outputs, -show-ports\n"); log(" add all module (input/output) ports to the list of shown signals\n"); log("\n"); + log(" -show-regs, -show-public, -show-all\n"); + log(" show all registers, show signals with 'public' names, show all signals\n"); + log("\n"); log(" -ignore_div_by_zero\n"); log(" ignore all solutions that involve a division by zero\n"); log("\n"); @@ -865,11 +951,17 @@ struct SatPass : public Pass { log(" set up a sequential problem with <N> time steps. The steps will\n"); log(" be numbered from 1 to N.\n"); log("\n"); + log(" note: for large <N> it can be significantly faster to use\n"); + log(" -tempinduct-baseonly -maxsteps <N> instead of -seq <N>.\n"); + log("\n"); log(" -set-at <N> <signal> <value>\n"); log(" -unset-at <N> <signal>\n"); log(" set or unset the specified signal to the specified value in the\n"); log(" given timestep. this has priority over a -set for the same signal.\n"); log("\n"); + log(" -set-assumes\n"); + log(" set all assumptions provided via $assume cells\n"); + log("\n"); log(" -set-def-at <N> <signal>\n"); log(" -set-any-undef-at <N> <signal>\n"); log(" -set-all-undef-at <N> <signal>\n"); @@ -890,6 +982,9 @@ struct SatPass : public Pass { log(" -dump_vcd <vcd-file-name>\n"); log(" dump SAT model (counter example in proof) to VCD file\n"); log("\n"); + log(" -dump_json <json-file-name>\n"); + log(" dump SAT model (counter example in proof) to a WaveJSON file.\n"); + log("\n"); log(" -dump_cnf <cnf-file-name>\n"); log(" dump CNF of SAT problem (in DIMACS format). in temporal induction\n"); log(" proofs this is the CNF of the first induction step.\n"); @@ -898,7 +993,7 @@ struct SatPass : public Pass { log("is passed, a temporal induction proof is performed.\n"); log("\n"); log(" -tempinduct\n"); - log(" Perform a temporal induction proof. In a temporalinduction proof it is\n"); + log(" Perform a temporal induction proof. In a temporal induction proof it is\n"); log(" proven that the condition holds forever after the number of time steps\n"); log(" specified using -seq.\n"); log("\n"); @@ -906,12 +1001,26 @@ struct SatPass : public Pass { log(" Perform a temporal induction proof. Assume an initial state with all\n"); log(" registers set to defined values for the induction step.\n"); log("\n"); + log(" -tempinduct-baseonly\n"); + log(" Run only the basecase half of temporal induction (requires -maxsteps)\n"); + log("\n"); + log(" -tempinduct-inductonly\n"); + log(" Run only the induction half of temporal induction\n"); + log("\n"); + log(" -tempinduct-skip <N>\n"); + log(" Skip the first <N> steps of the induction proof.\n"); + log("\n"); + log(" note: this will assume that the base case holds for <N> steps.\n"); + log(" this must be proven independently with \"-tempinduct-baseonly\n"); + log(" -maxsteps <N>\". Use -initsteps if you just want to set a\n"); + log(" minimal induction length.\n"); + log("\n"); log(" -prove <signal> <value>\n"); log(" Attempt to proof that <signal> is always <value>.\n"); log("\n"); log(" -prove-x <signal> <value>\n"); log(" Like -prove, but an undef (x) bit in the lhs matches any value on\n"); - log(" the right hand side. Useful for equivialence checking.\n"); + log(" the right hand side. Useful for equivalence checking.\n"); log("\n"); log(" -prove-asserts\n"); log(" Prove that all asserts in the design hold.\n"); @@ -924,6 +1033,13 @@ struct SatPass : public Pass { log("\n"); log(" -initsteps <N>\n"); log(" Set initial length for the induction.\n"); + log(" This will speed up the search of the right induction length\n"); + log(" for deep induction proofs.\n"); + log("\n"); + log(" -stepsize <N>\n"); + log(" Increase the size of the induction proof in steps of <N>.\n"); + log(" This will speed up the search of the right induction length\n"); + log(" for deep induction proofs.\n"); log("\n"); log(" -timeout <N>\n"); log(" Maximum number of seconds a single SAT instance may take.\n"); @@ -951,10 +1067,13 @@ struct SatPass : public Pass { bool verify = false, fail_on_timeout = false, enable_undef = false, set_def_inputs = false; bool ignore_div_by_zero = false, set_init_undef = false, set_init_zero = false, max_undef = false; bool tempinduct = false, prove_asserts = false, show_inputs = false, show_outputs = false; + bool show_regs = false, show_public = false, show_all = false; bool ignore_unknown_cells = false, falsify = false, tempinduct_def = false, set_init_def = false; - std::string vcd_file_name, cnf_file_name; + bool tempinduct_baseonly = false, tempinduct_inductonly = false, set_assumes = false; + int tempinduct_skip = 0, stepsize = 1; + std::string vcd_file_name, json_file_name, cnf_file_name; - log_header("Executing SAT pass (solving SAT problems in the circuit).\n"); + log_header(design, "Executing SAT pass (solving SAT problems in the circuit).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { @@ -996,6 +1115,10 @@ struct SatPass : public Pass { initsteps = atoi(args[++argidx].c_str()); continue; } + if (args[argidx] == "-stepsize" && argidx+1 < args.size()) { + stepsize = max(1, atoi(args[++argidx].c_str())); + continue; + } if (args[argidx] == "-ignore_div_by_zero") { ignore_div_by_zero = true; continue; @@ -1035,6 +1158,10 @@ struct SatPass : public Pass { enable_undef = true; continue; } + if (args[argidx] == "-set-assumes") { + set_assumes = true; + continue; + } if (args[argidx] == "-tempinduct") { tempinduct = true; continue; @@ -1044,6 +1171,20 @@ struct SatPass : public Pass { tempinduct_def = true; continue; } + if (args[argidx] == "-tempinduct-baseonly") { + tempinduct = true; + tempinduct_baseonly = true; + continue; + } + if (args[argidx] == "-tempinduct-inductonly") { + tempinduct = true; + tempinduct_inductonly = true; + continue; + } + if (args[argidx] == "-tempinduct-skip" && argidx+1 < args.size()) { + tempinduct_skip = atoi(args[++argidx].c_str()); + continue; + } if (args[argidx] == "-prove" && argidx+2 < args.size()) { std::string lhs = args[++argidx]; std::string rhs = args[++argidx]; @@ -1135,6 +1276,18 @@ struct SatPass : public Pass { show_outputs = true; continue; } + if (args[argidx] == "-show-regs") { + show_regs = true; + continue; + } + if (args[argidx] == "-show-public") { + show_public = true; + continue; + } + if (args[argidx] == "-show-all") { + show_all = true; + continue; + } if (args[argidx] == "-ignore_unknown_cells") { ignore_unknown_cells = true; continue; @@ -1143,6 +1296,10 @@ struct SatPass : public Pass { vcd_file_name = args[++argidx]; continue; } + if (args[argidx] == "-dump_json" && argidx+1 < args.size()) { + json_file_name = args[++argidx]; + continue; + } if (args[argidx] == "-dump_cnf" && argidx+1 < args.size()) { cnf_file_name = args[++argidx]; continue; @@ -1152,14 +1309,12 @@ struct SatPass : public Pass { extra_args(args, argidx, design); RTLIL::Module *module = NULL; - for (auto &mod_it : design->modules_) - if (design->selected(mod_it.second)) { - if (module) - log_cmd_error("Only one module must be selected for the SAT pass! (selected: %s and %s)\n", - RTLIL::id2cstr(module->name), RTLIL::id2cstr(mod_it.first)); - module = mod_it.second; - } - if (module == NULL) + for (auto mod : design->selected_modules()) { + if (module) + log_cmd_error("Only one module must be selected for the SAT pass! (selected: %s and %s)\n", log_id(module), log_id(mod)); + module = mod; + } + if (module == NULL) log_cmd_error("Can't perform SAT on an empty selection!\n"); if (!prove.size() && !prove_x.size() && !prove_asserts && tempinduct) @@ -1192,6 +1347,29 @@ struct SatPass : public Pass { shows.push_back(it.second->name.str()); } + if (show_regs) { + pool<Wire*> reg_wires; + for (auto cell : module->cells()) { + if (cell->type == "$dff" || cell->type.substr(0, 6) == "$_DFF_") + for (auto bit : cell->getPort("\\Q")) + if (bit.wire) + reg_wires.insert(bit.wire); + } + for (auto wire : reg_wires) + shows.push_back(wire->name.str()); + } + + if (show_public) { + for (auto wire : module->wires()) + if (wire->name[0] == '\\') + shows.push_back(wire->name.str()); + } + + if (show_all) { + for (auto wire : module->wires()) + shows.push_back(wire->name.str()); + } + if (tempinduct) { if (loopcount > 0 || max_undef) @@ -1199,8 +1377,10 @@ struct SatPass : public Pass { SatHelper basecase(design, module, enable_undef); SatHelper inductstep(design, module, enable_undef); + bool basecase_setup_init = true; basecase.sets = sets; + basecase.set_assumes = set_assumes; basecase.prove = prove; basecase.prove_x = prove_x; basecase.prove_asserts = prove_asserts; @@ -1222,10 +1402,11 @@ struct SatPass : public Pass { basecase.ignore_unknown_cells = ignore_unknown_cells; for (int timestep = 1; timestep <= seq_len; timestep++) - basecase.setup(timestep); - basecase.setup_init(); + if (!tempinduct_inductonly) + basecase.setup(timestep); inductstep.sets = sets; + inductstep.set_assumes = set_assumes; inductstep.prove = prove; inductstep.prove_x = prove_x; inductstep.prove_asserts = prove_asserts; @@ -1237,12 +1418,14 @@ struct SatPass : public Pass { inductstep.satgen.ignore_div_by_zero = ignore_div_by_zero; inductstep.ignore_unknown_cells = ignore_unknown_cells; - inductstep.setup(1); - inductstep.ez.assume(inductstep.setup_proof(1)); + if (!tempinduct_baseonly) { + inductstep.setup(1); + inductstep.ez->assume(inductstep.setup_proof(1)); + } if (tempinduct_def) { std::vector<int> undef_state = inductstep.satgen.importUndefSigSpec(inductstep.satgen.initial_state.export_all(), 1); - inductstep.ez.assume(inductstep.ez.NOT(inductstep.ez.expression(ezSAT::OpOr, undef_state))); + inductstep.ez->assume(inductstep.ez->NOT(inductstep.ez->expression(ezSAT::OpOr, undef_state))); } for (int inductlen = 1; inductlen <= maxsteps || maxsteps == 0; inductlen++) @@ -1251,81 +1434,120 @@ struct SatPass : public Pass { // phase 1: proving base case - basecase.setup(seq_len + inductlen); - int property = basecase.setup_proof(seq_len + inductlen); - basecase.generate_model(); - - if (inductlen > 1) - basecase.force_unique_state(seq_len + 1, seq_len + inductlen); + if (!tempinduct_inductonly) + { + basecase.setup(seq_len + inductlen); + int property = basecase.setup_proof(seq_len + inductlen); + basecase.generate_model(); - log("\n[base case] Solving problem with %d variables and %d clauses..\n", - basecase.ez.numCnfVariables(), basecase.ez.numCnfClauses()); + if (basecase_setup_init) { + basecase.setup_init(); + basecase_setup_init = false; + } - if (basecase.solve(basecase.ez.NOT(property))) { - log("SAT temporal induction proof finished - model found for base case: FAIL!\n"); - print_proof_failed(); - basecase.print_model(); - if(!vcd_file_name.empty()) - basecase.dump_model_to_vcd(vcd_file_name); - goto tip_failed; - } + if (inductlen > 1) + basecase.force_unique_state(seq_len + 1, seq_len + inductlen); - if (basecase.gotTimeout) - goto timeout; + if (tempinduct_skip < inductlen) + { + log("\n[base case %d] Solving problem with %d variables and %d clauses..\n", + inductlen, basecase.ez->numCnfVariables(), basecase.ez->numCnfClauses()); + + if (basecase.solve(basecase.ez->NOT(property))) { + log("SAT temporal induction proof finished - model found for base case: FAIL!\n"); + print_proof_failed(); + basecase.print_model(); + if(!vcd_file_name.empty()) + basecase.dump_model_to_vcd(vcd_file_name); + if(!json_file_name.empty()) + basecase.dump_model_to_json(json_file_name); + goto tip_failed; + } + + if (basecase.gotTimeout) + goto timeout; - log("Base case for induction length %d proven.\n", inductlen); - basecase.ez.assume(property); + log("Base case for induction length %d proven.\n", inductlen); + } + else + { + log("\n[base case %d] Skipping prove for this step (-tempinduct-skip %d).", + inductlen, tempinduct_skip); + log("\n[base case %d] Problem size so far: %d variables and %d clauses.\n", + inductlen, basecase.ez->numCnfVariables(), basecase.ez->numCnfClauses()); + } + basecase.ez->assume(property); + } // phase 2: proving induction step - inductstep.setup(inductlen + 1); - property = inductstep.setup_proof(inductlen + 1); - inductstep.generate_model(); - - if (inductlen > 1) - inductstep.force_unique_state(1, inductlen + 1); - - if (inductlen < initsteps) - { - log("\n[induction step] Skipping problem with %d variables and %d clauses (below initsteps).\n", - inductstep.ez.numCnfVariables(), inductstep.ez.numCnfClauses()); - inductstep.ez.assume(property); - } - else + if (!tempinduct_baseonly) { - if (!cnf_file_name.empty()) - { - FILE *f = fopen(cnf_file_name.c_str(), "w"); - if (!f) - log_cmd_error("Can't open output file `%s' for writing: %s\n", cnf_file_name.c_str(), strerror(errno)); + inductstep.setup(inductlen + 1); + int property = inductstep.setup_proof(inductlen + 1); + inductstep.generate_model(); - log("Dumping CNF to file `%s'.\n", cnf_file_name.c_str()); - cnf_file_name.clear(); + if (inductlen > 1) + inductstep.force_unique_state(1, inductlen + 1); - inductstep.ez.printDIMACS(f, false); - fclose(f); + if (inductlen <= tempinduct_skip || inductlen <= initsteps || inductlen % stepsize != 0) + { + if (inductlen < tempinduct_skip) + log("\n[induction step %d] Skipping prove for this step (-tempinduct-skip %d).", + inductlen, tempinduct_skip); + if (inductlen < initsteps) + log("\n[induction step %d] Skipping prove for this step (-initsteps %d).", + inductlen, tempinduct_skip); + if (inductlen % stepsize != 0) + log("\n[induction step %d] Skipping prove for this step (-stepsize %d).", + inductlen, stepsize); + log("\n[induction step %d] Problem size so far: %d variables and %d clauses.\n", + inductlen, inductstep.ez->numCnfVariables(), inductstep.ez->numCnfClauses()); + inductstep.ez->assume(property); } - - log("\n[induction step] Solving problem with %d variables and %d clauses..\n", - inductstep.ez.numCnfVariables(), inductstep.ez.numCnfClauses()); - - if (!inductstep.solve(inductstep.ez.NOT(property))) { - if (inductstep.gotTimeout) - goto timeout; - log("Induction step proven: SUCCESS!\n"); - print_qed(); - goto tip_success; + else + { + if (!cnf_file_name.empty()) + { + FILE *f = fopen(cnf_file_name.c_str(), "w"); + if (!f) + log_cmd_error("Can't open output file `%s' for writing: %s\n", cnf_file_name.c_str(), strerror(errno)); + + log("Dumping CNF to file `%s'.\n", cnf_file_name.c_str()); + cnf_file_name.clear(); + + inductstep.ez->printDIMACS(f, false); + fclose(f); + } + + log("\n[induction step %d] Solving problem with %d variables and %d clauses..\n", + inductlen, inductstep.ez->numCnfVariables(), inductstep.ez->numCnfClauses()); + + if (!inductstep.solve(inductstep.ez->NOT(property))) { + if (inductstep.gotTimeout) + goto timeout; + log("Induction step proven: SUCCESS!\n"); + print_qed(); + goto tip_success; + } + + log("Induction step failed. Incrementing induction length.\n"); + inductstep.ez->assume(property); + inductstep.print_model(); } - - log("Induction step failed. Incrementing induction length.\n"); - inductstep.ez.assume(property); - inductstep.print_model(); } } + if (tempinduct_baseonly) { + log("\nReached maximum number of time steps -> proved base case for %d steps: SUCCESS!\n", maxsteps); + goto tip_success; + } + log("\nReached maximum number of time steps -> proof failed.\n"); if(!vcd_file_name.empty()) inductstep.dump_model_to_vcd(vcd_file_name); + if(!json_file_name.empty()) + inductstep.dump_model_to_json(json_file_name); print_proof_failed(); tip_failed: @@ -1349,6 +1571,7 @@ struct SatPass : public Pass { SatHelper sathelper(design, module, enable_undef); sathelper.sets = sets; + sathelper.set_assumes = set_assumes; sathelper.prove = prove; sathelper.prove_x = prove_x; sathelper.prove_asserts = prove_asserts; @@ -1372,7 +1595,7 @@ struct SatPass : public Pass { if (seq_len == 0) { sathelper.setup(); if (sathelper.prove.size() || sathelper.prove_x.size() || sathelper.prove_asserts) - sathelper.ez.assume(sathelper.ez.NOT(sathelper.setup_proof())); + sathelper.ez->assume(sathelper.ez->NOT(sathelper.setup_proof())); } else { std::vector<int> prove_bits; for (int timestep = 1; timestep <= seq_len; timestep++) { @@ -1382,7 +1605,7 @@ struct SatPass : public Pass { prove_bits.push_back(sathelper.setup_proof(timestep)); } if (sathelper.prove.size() || sathelper.prove_x.size() || sathelper.prove_asserts) - sathelper.ez.assume(sathelper.ez.NOT(sathelper.ez.expression(ezSAT::OpAnd, prove_bits))); + sathelper.ez->assume(sathelper.ez->NOT(sathelper.ez->expression(ezSAT::OpAnd, prove_bits))); sathelper.setup_init(); } sathelper.generate_model(); @@ -1396,7 +1619,7 @@ struct SatPass : public Pass { log("Dumping CNF to file `%s'.\n", cnf_file_name.c_str()); cnf_file_name.clear(); - sathelper.ez.printDIMACS(f, false); + sathelper.ez->printDIMACS(f, false); fclose(f); } @@ -1404,7 +1627,7 @@ struct SatPass : public Pass { rerun_solver: log("\nSolving problem with %d variables and %d clauses..\n", - sathelper.ez.numCnfVariables(), sathelper.ez.numCnfClauses()); + sathelper.ez->numCnfVariables(), sathelper.ez->numCnfClauses()); if (sathelper.solve()) { @@ -1424,6 +1647,8 @@ struct SatPass : public Pass { if(!vcd_file_name.empty()) sathelper.dump_model_to_vcd(vcd_file_name); + if(!json_file_name.empty()) + sathelper.dump_model_to_json(json_file_name); if (loopcount != 0) { loopcount--, rerun_counter++; @@ -1487,5 +1712,5 @@ struct SatPass : public Pass { } } } SatPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc index d8a433164..96fa0d92a 100644 --- a/passes/techmap/Makefile.inc +++ b/passes/techmap/Makefile.inc @@ -5,17 +5,35 @@ OBJS += passes/techmap/dfflibmap.o OBJS += passes/techmap/maccmap.o OBJS += passes/techmap/libparse.o +ifeq ($(ENABLE_ABC),1) +OBJS += passes/techmap/abc.o +ifneq ($(ABCEXTERNAL),) +passes/techmap/abc.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"' +endif +endif + ifneq ($(SMALL),1) OBJS += passes/techmap/iopadmap.o OBJS += passes/techmap/hilomap.o OBJS += passes/techmap/extract.o OBJS += passes/techmap/alumacc.o OBJS += passes/techmap/dff2dffe.o +OBJS += passes/techmap/dffinit.o +OBJS += passes/techmap/pmuxtree.o +OBJS += passes/techmap/muxcover.o +OBJS += passes/techmap/aigmap.o +OBJS += passes/techmap/tribuf.o +OBJS += passes/techmap/lut2mux.o +OBJS += passes/techmap/nlutmap.o +OBJS += passes/techmap/dffsr2dff.o +OBJS += passes/techmap/shregmap.o +OBJS += passes/techmap/deminout.o endif GENFILES += passes/techmap/techmap.inc passes/techmap/techmap.inc: techlibs/common/techmap.v + $(Q) mkdir -p $(dir $@) $(P) echo "// autogenerated from $<" > $@.new $(Q) echo "static char stdcells_code[] = {" >> $@.new $(Q) od -v -td1 -An $< | $(SED) -e 's/[0-9][0-9]*/&,/g' >> $@.new @@ -24,9 +42,12 @@ passes/techmap/techmap.inc: techlibs/common/techmap.v passes/techmap/techmap.o: passes/techmap/techmap.inc +ifneq ($(CONFIG),emcc) TARGETS += yosys-filterlib$(EXE) EXTRA_OBJS += passes/techmap/filterlib.o yosys-filterlib$(EXE): passes/techmap/filterlib.o - $(P) $(CXX) -o yosys-filterlib$(EXE) $(LDFLAGS) $^ $(LDLIBS) + $(Q) mkdir -p $(dir $@) + $(P) $(LD) -o yosys-filterlib$(EXE) $(LDFLAGS) $^ $(LDLIBS) +endif diff --git a/passes/abc/abc.cc b/passes/techmap/abc.cc index 69da710f2..cc79296c5 100644 --- a/passes/abc/abc.cc +++ b/passes/techmap/abc.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -26,18 +26,20 @@ // http://www.ece.cmu.edu/~ee760/760docs/blif.pdf // [[CITE]] Kahn's Topological sorting algorithm -// Kahn, Arthur B. (1962), "Topological sorting of large networks", Communications of the ACM 5 (11): 558–562, doi:10.1145/368996.369025 +// Kahn, Arthur B. (1962), "Topological sorting of large networks", Communications of the ACM 5 (11): 558-562, doi:10.1145/368996.369025 // http://en.wikipedia.org/wiki/Topological_sorting -#define ABC_COMMAND_LIB "strash; scorr; ifraig; retime {D}; strash; dch -f; map {D}" -#define ABC_COMMAND_CTR "strash; scorr; ifraig; retime {D}; strash; dch -f; map {D}; buffer; upsize {D}; dnsize {D}; stime -p" -#define ABC_COMMAND_LUT "strash; scorr; ifraig; retime; strash; dch -f; if" -#define ABC_COMMAND_DFL "strash; scorr; ifraig; retime; strash; dch -f; map" +#define ABC_COMMAND_LIB "strash; dc2; scorr; ifraig; retime -o {D}; strash; dch -f; map {D}" +#define ABC_COMMAND_CTR "strash; dc2; scorr; ifraig; retime -o {D}; strash; dch -f; map {D}; buffer; upsize {D}; dnsize {D}; stime -p" +#define ABC_COMMAND_LUT "strash; dc2; scorr; ifraig; retime -o; strash; dch -f; if; mfs" +#define ABC_COMMAND_SOP "strash; dc2; scorr; ifraig; retime -o; strash; dch -f; cover {I} {P}" +#define ABC_COMMAND_DFL "strash; dc2; scorr; ifraig; retime -o; strash; dch -f; map" -#define ABC_FAST_COMMAND_LIB "retime {D}; map {D}" -#define ABC_FAST_COMMAND_CTR "retime {D}; map {D}; buffer; upsize {D}; dnsize {D}; stime -p" -#define ABC_FAST_COMMAND_LUT "retime; if" -#define ABC_FAST_COMMAND_DFL "retime; map" +#define ABC_FAST_COMMAND_LIB "retime -o {D}; map {D}" +#define ABC_FAST_COMMAND_CTR "retime -o {D}; map {D}; buffer; upsize {D}; dnsize {D}; stime -p" +#define ABC_FAST_COMMAND_LUT "retime -o; if" +#define ABC_FAST_COMMAND_SOP "retime -o; cover -I {I} -P {P}" +#define ABC_FAST_COMMAND_DFL "retime -o; map" #include "kernel/register.h" #include "kernel/sigtools.h" @@ -56,7 +58,7 @@ # include <dirent.h> #endif -#include "blifparse.h" +#include "frontends/blif/blifparse.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -90,12 +92,17 @@ struct gate_t RTLIL::SigBit bit; }; +bool map_mux4; +bool map_mux8; +bool map_mux16; + bool markgroups; int map_autoidx; SigMap assign_map; RTLIL::Module *module; std::vector<gate_t> signal_list; std::map<RTLIL::SigBit, int> signal_map; +pool<std::string> enabled_gates; bool clk_polarity, en_polarity; RTLIL::SigSpec clk_sig, en_sig; @@ -587,8 +594,9 @@ struct abc_output_filter }; void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::string script_file, std::string exe_file, - std::string liberty_file, std::string constr_file, bool cleanup, int lut_mode, int lut_mode2, bool dff_mode, std::string clk_str, - bool keepff, std::string delay_target, bool fast_mode, const std::vector<RTLIL::Cell*> &cells, bool show_tempdir) + std::string liberty_file, std::string constr_file, bool cleanup, vector<int> lut_costs, bool dff_mode, std::string clk_str, + bool keepff, std::string delay_target, std::string sop_inputs, std::string sop_products, bool fast_mode, + const std::vector<RTLIL::Cell*> &cells, bool show_tempdir, bool sop_mode) { module = current_module; map_autoidx = autoidx++; @@ -611,7 +619,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin if (!cleanup) tempdir_name[0] = tempdir_name[4] = '_'; tempdir_name = make_temp_dir(tempdir_name); - log_header("Extracting gate netlist of module `%s' to `%s/input.blif'..\n", + log_header(design, "Extracting gate netlist of module `%s' to `%s/input.blif'..\n", module->name.c_str(), replace_tempdir(tempdir_name, tempdir_name, show_tempdir).c_str()); std::string abc_script = stringf("read_blif %s/input.blif; ", tempdir_name.c_str()); @@ -621,7 +629,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin if (!constr_file.empty()) abc_script += stringf("read_constr -v %s; ", constr_file.c_str()); } else - if (lut_mode) + if (!lut_costs.empty()) abc_script += stringf("read_lut %s/lutdefs.txt; ", tempdir_name.c_str()); else abc_script += stringf("read_library %s/stdcells.genlib; ", tempdir_name.c_str()); @@ -637,16 +645,30 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin abc_script += script_file[i]; } else abc_script += stringf("source %s", script_file.c_str()); - } else if (lut_mode) + } else if (!lut_costs.empty()) { + bool all_luts_cost_same = true; + for (int this_cost : lut_costs) + if (this_cost != lut_costs.front()) + all_luts_cost_same = false; abc_script += fast_mode ? ABC_FAST_COMMAND_LUT : ABC_COMMAND_LUT; - else if (!liberty_file.empty()) + if (all_luts_cost_same && !fast_mode) + abc_script += "; lutpack"; + } else if (!liberty_file.empty()) abc_script += constr_file.empty() ? (fast_mode ? ABC_FAST_COMMAND_LIB : ABC_COMMAND_LIB) : (fast_mode ? ABC_FAST_COMMAND_CTR : ABC_COMMAND_CTR); + else if (sop_mode) + abc_script += fast_mode ? ABC_FAST_COMMAND_SOP : ABC_COMMAND_SOP; else abc_script += fast_mode ? ABC_FAST_COMMAND_DFL : ABC_COMMAND_DFL; for (size_t pos = abc_script.find("{D}"); pos != std::string::npos; pos = abc_script.find("{D}", pos)) abc_script = abc_script.substr(0, pos) + delay_target + abc_script.substr(pos+3); + for (size_t pos = abc_script.find("{I}"); pos != std::string::npos; pos = abc_script.find("{D}", pos)) + abc_script = abc_script.substr(0, pos) + sop_inputs + abc_script.substr(pos+3); + + for (size_t pos = abc_script.find("{P}"); pos != std::string::npos; pos = abc_script.find("{D}", pos)) + abc_script = abc_script.substr(0, pos) + sop_products + abc_script.substr(pos+3); + abc_script += stringf("; write_blif %s/output.blif", tempdir_name.c_str()); abc_script = add_echos_to_abc_cmd(abc_script); @@ -823,7 +845,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin if (count_output > 0) { - log_header("Executing ABC.\n"); + log_header(design, "Executing ABC.\n"); buffer = stringf("%s/stdcells.genlib", tempdir_name.c_str()); f = fopen(buffer.c_str(), "wt"); @@ -833,28 +855,43 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin fprintf(f, "GATE ONE 1 Y=CONST1;\n"); fprintf(f, "GATE BUF %d Y=A; PIN * NONINV 1 999 1 0 1 0\n", get_cell_cost("$_BUF_")); fprintf(f, "GATE NOT %d Y=!A; PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_NOT_")); - fprintf(f, "GATE AND %d Y=A*B; PIN * NONINV 1 999 1 0 1 0\n", get_cell_cost("$_AND_")); - fprintf(f, "GATE NAND %d Y=!(A*B); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_NAND_")); - fprintf(f, "GATE OR %d Y=A+B; PIN * NONINV 1 999 1 0 1 0\n", get_cell_cost("$_OR_")); - fprintf(f, "GATE NOR %d Y=!(A+B); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_NOR_")); - fprintf(f, "GATE XOR %d Y=(A*!B)+(!A*B); PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_XOR_")); - fprintf(f, "GATE XNOR %d Y=(A*B)+(!A*!B); PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_XNOR_")); - fprintf(f, "GATE AOI3 %d Y=!((A*B)+C); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_AOI3_")); - fprintf(f, "GATE OAI3 %d Y=!((A+B)*C); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_OAI3_")); - fprintf(f, "GATE AOI4 %d Y=!((A*B)+(C*D)); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_AOI4_")); - fprintf(f, "GATE OAI4 %d Y=!((A+B)*(C+D)); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_OAI4_")); - fprintf(f, "GATE MUX %d Y=(A*B)+(S*B)+(!S*A); PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_MUX_")); + if (enabled_gates.empty() || enabled_gates.count("AND")) + fprintf(f, "GATE AND %d Y=A*B; PIN * NONINV 1 999 1 0 1 0\n", get_cell_cost("$_AND_")); + if (enabled_gates.empty() || enabled_gates.count("NAND")) + fprintf(f, "GATE NAND %d Y=!(A*B); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_NAND_")); + if (enabled_gates.empty() || enabled_gates.count("OR")) + fprintf(f, "GATE OR %d Y=A+B; PIN * NONINV 1 999 1 0 1 0\n", get_cell_cost("$_OR_")); + if (enabled_gates.empty() || enabled_gates.count("NOR")) + fprintf(f, "GATE NOR %d Y=!(A+B); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_NOR_")); + if (enabled_gates.empty() || enabled_gates.count("XOR")) + fprintf(f, "GATE XOR %d Y=(A*!B)+(!A*B); PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_XOR_")); + if (enabled_gates.empty() || enabled_gates.count("XNOR")) + fprintf(f, "GATE XNOR %d Y=(A*B)+(!A*!B); PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_XNOR_")); + if (enabled_gates.empty() || enabled_gates.count("AOI3")) + fprintf(f, "GATE AOI3 %d Y=!((A*B)+C); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_AOI3_")); + if (enabled_gates.empty() || enabled_gates.count("OAI3")) + fprintf(f, "GATE OAI3 %d Y=!((A+B)*C); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_OAI3_")); + if (enabled_gates.empty() || enabled_gates.count("AOI4")) + fprintf(f, "GATE AOI4 %d Y=!((A*B)+(C*D)); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_AOI4_")); + if (enabled_gates.empty() || enabled_gates.count("OAI4")) + fprintf(f, "GATE OAI4 %d Y=!((A+B)*(C+D)); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_OAI4_")); + if (enabled_gates.empty() || enabled_gates.count("MUX")) + fprintf(f, "GATE MUX %d Y=(A*B)+(S*B)+(!S*A); PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_MUX_")); + if (map_mux4) + fprintf(f, "GATE MUX4 %d Y=(!S*!T*A)+(S*!T*B)+(!S*T*C)+(S*T*D); PIN * UNKNOWN 1 999 1 0 1 0\n", 2*get_cell_cost("$_MUX_")); + if (map_mux8) + fprintf(f, "GATE MUX8 %d Y=(!S*!T*!U*A)+(S*!T*!U*B)+(!S*T*!U*C)+(S*T*!U*D)+(!S*!T*U*E)+(S*!T*U*F)+(!S*T*U*G)+(S*T*U*H); PIN * UNKNOWN 1 999 1 0 1 0\n", 4*get_cell_cost("$_MUX_")); + if (map_mux16) + fprintf(f, "GATE MUX16 %d Y=(!S*!T*!U*!V*A)+(S*!T*!U*!V*B)+(!S*T*!U*!V*C)+(S*T*!U*!V*D)+(!S*!T*U*!V*E)+(S*!T*U*!V*F)+(!S*T*U*!V*G)+(S*T*U*!V*H)+(!S*!T*!U*V*I)+(S*!T*!U*V*J)+(!S*T*!U*V*K)+(S*T*!U*V*L)+(!S*!T*U*V*M)+(S*!T*U*V*N)+(!S*T*U*V*O)+(S*T*U*V*P); PIN * UNKNOWN 1 999 1 0 1 0\n", 8*get_cell_cost("$_MUX_")); fclose(f); - if (lut_mode) { + if (!lut_costs.empty()) { buffer = stringf("%s/lutdefs.txt", tempdir_name.c_str()); f = fopen(buffer.c_str(), "wt"); if (f == NULL) log_error("Opening %s for writing failed: %s\n", buffer.c_str(), strerror(errno)); - for (int i = 0; i < lut_mode; i++) - fprintf(f, "%d 1.00 1.00\n", i+1); - for (int i = lut_mode; i < lut_mode2; i++) - fprintf(f, "%d %d.00 1.00\n", i+1, 2 << (i - lut_mode)); + for (int i = 0; i < GetSize(lut_costs); i++) + fprintf(f, "%d %d.00 1.00\n", i+1, lut_costs.at(i)); fclose(f); } @@ -867,16 +904,18 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin log_error("ABC: execution of command \"%s\" failed: return code %d.\n", buffer.c_str(), ret); buffer = stringf("%s/%s", tempdir_name.c_str(), "output.blif"); - f = fopen(buffer.c_str(), "rt"); - if (f == NULL) + std::ifstream ifs; + ifs.open(buffer); + if (ifs.fail()) log_error("Can't open ABC output file `%s'.\n", buffer.c_str()); - bool builtin_lib = liberty_file.empty() && script_file.empty() && !lut_mode; - RTLIL::Design *mapped_design = abc_parse_blif(f, builtin_lib ? "\\DFF" : "\\_dff_"); + bool builtin_lib = liberty_file.empty(); + RTLIL::Design *mapped_design = new RTLIL::Design; + parse_blif(mapped_design, ifs, builtin_lib ? "\\DFF" : "\\_dff_", false, sop_mode); - fclose(f); + ifs.close(); - log_header("Re-integrating ABC results.\n"); + log_header(design, "Re-integrating ABC results.\n"); RTLIL::Module *mapped_mod = mapped_design->modules_["\\netlist"]; if (mapped_mod == NULL) log_error("ABC output file does not contain a module `netlist'.\n"); @@ -888,10 +927,10 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin } std::map<std::string, int> cell_stats; - if (builtin_lib) + for (auto c : mapped_mod->cells()) { - for (auto &it : mapped_mod->cells_) { - RTLIL::Cell *c = it.second; + if (builtin_lib) + { cell_stats[RTLIL::unescape_id(c->type)]++; if (c->type == "\\ZERO" || c->type == "\\ONE") { RTLIL::SigSig conn; @@ -934,61 +973,86 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin design->select(module, cell); continue; } - if (c->type == "\\AOI3" || c->type == "\\OAI3") { - RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_" + c->type.substr(1) + "_"); + if (c->type == "\\MUX4") { + RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_MUX4_"); if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx; cell->setPort("\\A", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\A").as_wire()->name)])); cell->setPort("\\B", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\B").as_wire()->name)])); cell->setPort("\\C", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\C").as_wire()->name)])); + cell->setPort("\\D", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\D").as_wire()->name)])); + cell->setPort("\\S", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\S").as_wire()->name)])); + cell->setPort("\\T", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\T").as_wire()->name)])); cell->setPort("\\Y", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)])); design->select(module, cell); continue; } - if (c->type == "\\AOI4" || c->type == "\\OAI4") { - RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_" + c->type.substr(1) + "_"); + if (c->type == "\\MUX8") { + RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_MUX8_"); if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx; cell->setPort("\\A", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\A").as_wire()->name)])); cell->setPort("\\B", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\B").as_wire()->name)])); cell->setPort("\\C", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\C").as_wire()->name)])); cell->setPort("\\D", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\D").as_wire()->name)])); + cell->setPort("\\E", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\E").as_wire()->name)])); + cell->setPort("\\F", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\F").as_wire()->name)])); + cell->setPort("\\G", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\G").as_wire()->name)])); + cell->setPort("\\H", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\H").as_wire()->name)])); + cell->setPort("\\S", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\S").as_wire()->name)])); + cell->setPort("\\T", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\T").as_wire()->name)])); + cell->setPort("\\U", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\U").as_wire()->name)])); cell->setPort("\\Y", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)])); design->select(module, cell); continue; } - if (c->type == "\\DFF") { - log_assert(clk_sig.size() == 1); - RTLIL::Cell *cell; - if (en_sig.size() == 0) { - cell = module->addCell(remap_name(c->name), clk_polarity ? "$_DFF_P_" : "$_DFF_N_"); - } else { - log_assert(en_sig.size() == 1); - cell = module->addCell(remap_name(c->name), stringf("$_DFFE_%c%c_", clk_polarity ? 'P' : 'N', en_polarity ? 'P' : 'N')); - cell->setPort("\\E", en_sig); - } + if (c->type == "\\MUX16") { + RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_MUX16_"); if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx; + cell->setPort("\\A", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\A").as_wire()->name)])); + cell->setPort("\\B", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\B").as_wire()->name)])); + cell->setPort("\\C", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\C").as_wire()->name)])); cell->setPort("\\D", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\D").as_wire()->name)])); - cell->setPort("\\Q", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Q").as_wire()->name)])); - cell->setPort("\\C", clk_sig); + cell->setPort("\\E", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\E").as_wire()->name)])); + cell->setPort("\\F", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\F").as_wire()->name)])); + cell->setPort("\\G", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\G").as_wire()->name)])); + cell->setPort("\\H", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\H").as_wire()->name)])); + cell->setPort("\\I", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\I").as_wire()->name)])); + cell->setPort("\\J", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\J").as_wire()->name)])); + cell->setPort("\\K", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\K").as_wire()->name)])); + cell->setPort("\\L", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\L").as_wire()->name)])); + cell->setPort("\\M", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\M").as_wire()->name)])); + cell->setPort("\\N", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\N").as_wire()->name)])); + cell->setPort("\\O", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\O").as_wire()->name)])); + cell->setPort("\\P", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\P").as_wire()->name)])); + cell->setPort("\\S", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\S").as_wire()->name)])); + cell->setPort("\\T", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\T").as_wire()->name)])); + cell->setPort("\\U", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\U").as_wire()->name)])); + cell->setPort("\\V", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\V").as_wire()->name)])); + cell->setPort("\\Y", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)])); design->select(module, cell); continue; } - log_abort(); - } - } - else - { - for (auto &it : mapped_mod->cells_) - { - RTLIL::Cell *c = it.second; - cell_stats[RTLIL::unescape_id(c->type)]++; - if (c->type == "\\_const0_" || c->type == "\\_const1_") { - RTLIL::SigSig conn; - conn.first = RTLIL::SigSpec(module->wires_[remap_name(c->connections().begin()->second.as_wire()->name)]); - conn.second = RTLIL::SigSpec(c->type == "\\_const0_" ? 0 : 1, 1); - module->connect(conn); + if (c->type == "\\AOI3" || c->type == "\\OAI3") { + RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_" + c->type.substr(1) + "_"); + if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx; + cell->setPort("\\A", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\A").as_wire()->name)])); + cell->setPort("\\B", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\B").as_wire()->name)])); + cell->setPort("\\C", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\C").as_wire()->name)])); + cell->setPort("\\Y", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)])); + design->select(module, cell); + continue; + } + if (c->type == "\\AOI4" || c->type == "\\OAI4") { + RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_" + c->type.substr(1) + "_"); + if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx; + cell->setPort("\\A", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\A").as_wire()->name)])); + cell->setPort("\\B", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\B").as_wire()->name)])); + cell->setPort("\\C", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\C").as_wire()->name)])); + cell->setPort("\\D", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\D").as_wire()->name)])); + cell->setPort("\\Y", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)])); + design->select(module, cell); continue; } - if (c->type == "\\_dff_") { + if (c->type == "\\DFF") { log_assert(clk_sig.size() == 1); RTLIL::Cell *cell; if (en_sig.size() == 0) { @@ -1005,21 +1069,57 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin design->select(module, cell); continue; } - RTLIL::Cell *cell = module->addCell(remap_name(c->name), c->type); - if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx; - cell->parameters = c->parameters; - for (auto &conn : c->connections()) { - RTLIL::SigSpec newsig; - for (auto &c : conn.second.chunks()) { - if (c.width == 0) - continue; - log_assert(c.width == 1); - newsig.append(module->wires_[remap_name(c.wire->name)]); - } - cell->setPort(conn.first, newsig); + } + + cell_stats[RTLIL::unescape_id(c->type)]++; + + if (c->type == "\\_const0_" || c->type == "\\_const1_") { + RTLIL::SigSig conn; + conn.first = RTLIL::SigSpec(module->wires_[remap_name(c->connections().begin()->second.as_wire()->name)]); + conn.second = RTLIL::SigSpec(c->type == "\\_const0_" ? 0 : 1, 1); + module->connect(conn); + continue; + } + + if (c->type == "\\_dff_") { + log_assert(clk_sig.size() == 1); + RTLIL::Cell *cell; + if (en_sig.size() == 0) { + cell = module->addCell(remap_name(c->name), clk_polarity ? "$_DFF_P_" : "$_DFF_N_"); + } else { + log_assert(en_sig.size() == 1); + cell = module->addCell(remap_name(c->name), stringf("$_DFFE_%c%c_", clk_polarity ? 'P' : 'N', en_polarity ? 'P' : 'N')); + cell->setPort("\\E", en_sig); } + if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx; + cell->setPort("\\D", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\D").as_wire()->name)])); + cell->setPort("\\Q", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Q").as_wire()->name)])); + cell->setPort("\\C", clk_sig); design->select(module, cell); + continue; + } + + if (c->type == "$lut" && GetSize(c->getPort("\\A")) == 1 && c->getParam("\\LUT").as_int() == 2) { + SigSpec my_a = module->wires_[remap_name(c->getPort("\\A").as_wire()->name)]; + SigSpec my_y = module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)]; + module->connect(my_y, my_a); + continue; + } + + RTLIL::Cell *cell = module->addCell(remap_name(c->name), c->type); + if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx; + cell->parameters = c->parameters; + for (auto &conn : c->connections()) { + RTLIL::SigSpec newsig; + for (auto &c : conn.second.chunks()) { + if (c.width == 0) + continue; + log_assert(c.width == 1); + newsig.append(module->wires_[remap_name(c.wire->name)]); + } + cell->setPort(conn.first, newsig); } + design->select(module, cell); } for (auto conn : mapped_mod->connections()) { @@ -1081,14 +1181,18 @@ struct AbcPass : public Pass { log("library to a target architecture.\n"); log("\n"); log(" -exe <command>\n"); - log(" use the specified command name instead of \"yosys-abc\" to execute ABC.\n"); +#ifdef ABCEXTERNAL + log(" use the specified command instead of \"" ABCEXTERNAL "\" to execute ABC.\n"); +#else + log(" use the specified command instead of \"<yosys-bindir>/yosys-abc\" to execute ABC.\n"); +#endif log(" This can e.g. be used to call a specific version of ABC or a wrapper.\n"); log("\n"); log(" -script <file>\n"); log(" use the specified ABC script file instead of the default script.\n"); log("\n"); log(" if <file> starts with a plus sign (+), then the rest of the filename\n"); - log(" string is interprated as the command string to be passed to ABC. the\n"); + log(" string is interpreted as the command string to be passed to ABC. The\n"); log(" leading plus sign is removed and all commas (,) in the string are\n"); log(" replaced with blanks before the string is passed to ABC.\n"); log("\n"); @@ -1100,9 +1204,15 @@ struct AbcPass : public Pass { log(" for -liberty with -constr:\n"); log("%s\n", fold_abc_cmd(ABC_COMMAND_CTR).c_str()); log("\n"); - log(" for -lut:\n"); + log(" for -lut/-luts (only one LUT size):\n"); + log("%s\n", fold_abc_cmd(ABC_COMMAND_LUT "; lutpack").c_str()); + log("\n"); + log(" for -lut/-luts (different LUT sizes):\n"); log("%s\n", fold_abc_cmd(ABC_COMMAND_LUT).c_str()); log("\n"); + log(" for -sop:\n"); + log("%s\n", fold_abc_cmd(ABC_COMMAND_SOP).c_str()); + log("\n"); log(" otherwise:\n"); log("%s\n", fold_abc_cmd(ABC_COMMAND_DFL).c_str()); log("\n"); @@ -1116,9 +1226,12 @@ struct AbcPass : public Pass { log(" for -liberty with -constr:\n"); log("%s\n", fold_abc_cmd(ABC_FAST_COMMAND_CTR).c_str()); log("\n"); - log(" for -lut:\n"); + log(" for -lut/-luts:\n"); log("%s\n", fold_abc_cmd(ABC_FAST_COMMAND_LUT).c_str()); log("\n"); + log(" for -sop:\n"); + log("%s\n", fold_abc_cmd(ABC_FAST_COMMAND_SOP).c_str()); + log("\n"); log(" otherwise:\n"); log("%s\n", fold_abc_cmd(ABC_FAST_COMMAND_DFL).c_str()); log("\n"); @@ -1141,6 +1254,14 @@ struct AbcPass : public Pass { log(" set delay target. the string {D} in the default scripts above is\n"); log(" replaced by this option when used, and an empty string otherwise.\n"); log("\n"); + log(" -I <num>\n"); + log(" maximum number of SOP inputs.\n"); + log(" (replaces {I} in the default scripts above)\n"); + log("\n"); + log(" -P <num>\n"); + log(" maximum number of SOP products.\n"); + log(" (replaces {P} in the default scripts above)\n"); + log("\n"); log(" -lut <width>\n"); log(" generate netlist using luts of (max) the specified width.\n"); log("\n"); @@ -1150,6 +1271,22 @@ struct AbcPass : public Pass { log(" the area cost doubles with each additional input bit. the delay cost\n"); log(" is still constant for all lut widths.\n"); log("\n"); + log(" -luts <cost1>,<cost2>,<cost3>,<sizeN>:<cost4-N>,..\n"); + log(" generate netlist using luts. Use the specified costs for luts with 1,\n"); + log(" 2, 3, .. inputs.\n"); + log("\n"); + log(" -sop\n"); + log(" map to sum-of-product cells and inverters\n"); + log("\n"); + // log(" -mux4, -mux8, -mux16\n"); + // log(" try to extract 4-input, 8-input, and/or 16-input muxes\n"); + // log(" (ignored when used with -liberty or -lut)\n"); + // log("\n"); + log(" -g type1,type2,...\n"); + log(" Map the the specified list of gate types. Supported gates types are:\n"); + log(" AND, NAND, OR, NOR, XOR, XNOR, MUX, AOI3, OAI3, AOI4, OAI4.\n"); + log(" (The NOT gate is always added to this list automatically.)\n"); + log("\n"); log(" -dff\n"); log(" also pass $_DFF_?_ and $_DFFE_??_ cells through ABC. modules with many\n"); log(" clock domains are automatically partitioned in clock domains and each\n"); @@ -1161,7 +1298,7 @@ struct AbcPass : public Pass { log("\n"); log(" -keepff\n"); log(" set the \"keep\" attribute on flip-flop output wires. (and thus preserve\n"); - log(" them, for example for equivialence checking.)\n"); + log(" them, for example for equivalence checking.)\n"); log("\n"); log(" -nocleanup\n"); log(" when this option is used, the temporary files created by this pass\n"); @@ -1187,20 +1324,32 @@ struct AbcPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing ABC pass (technology mapping using ABC).\n"); + log_header(design, "Executing ABC pass (technology mapping using ABC).\n"); log_push(); +#ifdef ABCEXTERNAL + std::string exe_file = ABCEXTERNAL; +#else std::string exe_file = proc_self_dirname() + "yosys-abc"; - std::string script_file, liberty_file, constr_file, clk_str, delay_target; +#endif + std::string script_file, liberty_file, constr_file, clk_str; + std::string delay_target, sop_inputs, sop_products; bool fast_mode = false, dff_mode = false, keepff = false, cleanup = true; - bool show_tempdir = false; - int lut_mode = 0, lut_mode2 = 0; + bool show_tempdir = false, sop_mode = false; + vector<int> lut_costs; markgroups = false; + map_mux4 = false; + map_mux8 = false; + map_mux16 = false; + enabled_gates.clear(); + #ifdef _WIN32 +#ifndef ABCEXTERNAL if (!check_file_exists(exe_file + ".exe") && check_file_exists(proc_self_dirname() + "..\\yosys-abc.exe")) exe_file = proc_self_dirname() + "..\\yosys-abc"; #endif +#endif size_t argidx; char pwd [PATH_MAX]; @@ -1216,19 +1365,19 @@ struct AbcPass : public Pass { } if (arg == "-script" && argidx+1 < args.size()) { script_file = args[++argidx]; - if (!script_file.empty() && script_file[0] != '/' && script_file[0] != '+') + if (!script_file.empty() && !is_absolute_path(script_file) && script_file[0] != '+') script_file = std::string(pwd) + "/" + script_file; continue; } if (arg == "-liberty" && argidx+1 < args.size()) { liberty_file = args[++argidx]; - if (!liberty_file.empty() && liberty_file[0] != '/') + if (!liberty_file.empty() && !is_absolute_path(liberty_file)) liberty_file = std::string(pwd) + "/" + liberty_file; continue; } if (arg == "-constr" && argidx+1 < args.size()) { constr_file = args[++argidx]; - if (!constr_file.empty() && constr_file[0] != '/') + if (!constr_file.empty() && !is_absolute_path(constr_file)) constr_file = std::string(pwd) + "/" + constr_file; continue; } @@ -1236,9 +1385,18 @@ struct AbcPass : public Pass { delay_target = "-D " + args[++argidx]; continue; } + if (arg == "-I" && argidx+1 < args.size()) { + sop_inputs = "-I " + args[++argidx]; + continue; + } + if (arg == "-P" && argidx+1 < args.size()) { + sop_products = "-P " + args[++argidx]; + continue; + } if (arg == "-lut" && argidx+1 < args.size()) { string arg = args[++argidx]; size_t pos = arg.find_first_of(':'); + int lut_mode = 0, lut_mode2 = 0; if (pos != string::npos) { lut_mode = atoi(arg.substr(0, pos).c_str()); lut_mode2 = atoi(arg.substr(pos+1).c_str()); @@ -1246,6 +1404,62 @@ struct AbcPass : public Pass { lut_mode = atoi(arg.c_str()); lut_mode2 = lut_mode; } + lut_costs.clear(); + for (int i = 0; i < lut_mode; i++) + lut_costs.push_back(1); + for (int i = lut_mode; i < lut_mode2; i++) + lut_costs.push_back(2 << (i - lut_mode)); + continue; + } + if (arg == "-luts" && argidx+1 < args.size()) { + lut_costs.clear(); + for (auto &tok : split_tokens(args[++argidx], ",")) { + auto parts = split_tokens(tok, ":"); + if (GetSize(parts) == 0 && !lut_costs.empty()) + lut_costs.push_back(lut_costs.back()); + else if (GetSize(parts) == 1) + lut_costs.push_back(atoi(parts.at(0).c_str())); + else if (GetSize(parts) == 2) + while (GetSize(lut_costs) < atoi(parts.at(0).c_str())) + lut_costs.push_back(atoi(parts.at(1).c_str())); + else + log_cmd_error("Invalid -luts syntax.\n"); + } + continue; + } + if (arg == "-sop") { + sop_mode = true; + continue; + } + if (arg == "-mux4") { + map_mux4 = true; + continue; + } + if (arg == "-mux8") { + map_mux8 = true; + continue; + } + if (arg == "-mux16") { + map_mux16 = true; + continue; + } + if (arg == "-g" && argidx+1 < args.size()) { + for (auto g : split_tokens(args[++argidx], ",")) { + if (g == "AND") goto ok_gate; + if (g == "NAND") goto ok_gate; + if (g == "OR") goto ok_gate; + if (g == "NOR") goto ok_gate; + if (g == "XOR") goto ok_gate; + if (g == "XNOR") goto ok_gate; + if (g == "MUX") goto ok_gate; + if (g == "AOI3") goto ok_gate; + if (g == "OAI3") goto ok_gate; + if (g == "AOI4") goto ok_gate; + if (g == "OAI4") goto ok_gate; + cmd_error(args, argidx, stringf("Unsupported gate type: %s", g.c_str())); + ok_gate: + enabled_gates.insert(g); + } continue; } if (arg == "-fast") { @@ -1281,7 +1495,7 @@ struct AbcPass : public Pass { } extra_args(args, argidx, design); - if (lut_mode != 0 && !liberty_file.empty()) + if (!lut_costs.empty() && !liberty_file.empty()) log_cmd_error("Got -lut and -liberty! This two options are exclusive.\n"); if (!constr_file.empty() && liberty_file.empty()) log_cmd_error("Got -constr but no -liberty!\n"); @@ -1290,7 +1504,8 @@ struct AbcPass : public Pass { if (mod->processes.size() > 0) log("Skipping module %s as it contains processes.\n", log_id(mod)); else if (!dff_mode || !clk_str.empty()) - abc_module(design, mod, script_file, exe_file, liberty_file, constr_file, cleanup, lut_mode, lut_mode2, dff_mode, clk_str, keepff, delay_target, fast_mode, mod->selected_cells(), show_tempdir); + abc_module(design, mod, script_file, exe_file, liberty_file, constr_file, cleanup, lut_costs, dff_mode, clk_str, keepff, + delay_target, sop_inputs, sop_products, fast_mode, mod->selected_cells(), show_tempdir, sop_mode); else { assign_map.set(mod); @@ -1303,7 +1518,7 @@ struct AbcPass : public Pass { std::set<RTLIL::Cell*> expand_queue_up, next_expand_queue_up; std::set<RTLIL::Cell*> expand_queue_down, next_expand_queue_down; - typedef std::tuple<bool, RTLIL::SigSpec, bool, RTLIL::SigSpec> clkdomain_t; + typedef tuple<bool, RTLIL::SigSpec, bool, RTLIL::SigSpec> clkdomain_t; std::map<clkdomain_t, std::vector<RTLIL::Cell*>> assigned_cells; std::map<RTLIL::Cell*, clkdomain_t> assigned_cells_reverse; @@ -1423,7 +1638,7 @@ struct AbcPass : public Pass { assigned_cells_reverse[cell] = key; } - log_header("Summary of detected clock domains:\n"); + log_header(design, "Summary of detected clock domains:\n"); for (auto &it : assigned_cells) log(" %d cells in clk=%s%s, en=%s%s\n", GetSize(it.second), std::get<0>(it.first) ? "" : "!", log_signal(std::get<1>(it.first)), @@ -1434,8 +1649,8 @@ struct AbcPass : public Pass { clk_sig = assign_map(std::get<1>(it.first)); en_polarity = std::get<2>(it.first); en_sig = assign_map(std::get<3>(it.first)); - abc_module(design, mod, script_file, exe_file, liberty_file, constr_file, cleanup, lut_mode, lut_mode2, - !clk_sig.empty(), "$", keepff, delay_target, fast_mode, it.second, show_tempdir); + abc_module(design, mod, script_file, exe_file, liberty_file, constr_file, cleanup, lut_costs, !clk_sig.empty(), "$", + keepff, delay_target, sop_inputs, sop_products, fast_mode, it.second, show_tempdir, sop_mode); assign_map.set(mod); } } @@ -1447,5 +1662,5 @@ struct AbcPass : public Pass { log_pop(); } } AbcPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/techmap/aigmap.cc b/passes/techmap/aigmap.cc new file mode 100644 index 000000000..b9ac7aded --- /dev/null +++ b/passes/techmap/aigmap.cc @@ -0,0 +1,149 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include "kernel/cellaigs.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct AigmapPass : public Pass { + AigmapPass() : Pass("aigmap", "map logic to and-inverter-graph circuit") { } + virtual void help() + { + log("\n"); + log(" aigmap [options] [selection]\n"); + log("\n"); + log("Replace all logic cells with circuits made of only $_AND_ and\n"); + log("$_NOT_ cells.\n"); + log("\n"); + log(" -nand\n"); + log(" Enable creation of $_NAND_ cells\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + bool nand_mode = false; + + log_header(design, "Executing AIGMAP pass (map logic to AIG).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-nand") { + nand_mode = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) + { + vector<Cell*> replaced_cells; + int not_replaced_count = 0; + dict<IdString, int> stat_replaced; + dict<IdString, int> stat_not_replaced; + int orig_num_cells = GetSize(module->cells()); + + for (auto cell : module->selected_cells()) + { + Aig aig(cell); + + if (cell->type == "$_AND_" || cell->type == "$_NOT_") + aig.name.clear(); + + if (nand_mode && cell->type == "$_NAND_") + aig.name.clear(); + + if (aig.name.empty()) { + not_replaced_count++; + stat_not_replaced[cell->type]++; + continue; + } + + vector<SigBit> sigs; + dict<pair<int, int>, SigBit> and_cache; + + for (int node_idx = 0; node_idx < GetSize(aig.nodes); node_idx++) + { + SigBit bit; + auto &node = aig.nodes[node_idx]; + + if (node.portbit >= 0) { + bit = cell->getPort(node.portname)[node.portbit]; + } else if (node.left_parent < 0 && node.right_parent < 0) { + bit = node.inverter ? State::S1 : State::S0; + goto skip_inverter; + } else { + SigBit A = sigs.at(node.left_parent); + SigBit B = sigs.at(node.right_parent); + if (nand_mode && node.inverter) { + bit = module->NandGate(NEW_ID, A, B); + goto skip_inverter; + } else { + pair<int, int> key(node.left_parent, node.right_parent); + if (and_cache.count(key)) + bit = and_cache.at(key); + else + bit = module->AndGate(NEW_ID, A, B); + } + } + + if (node.inverter) + bit = module->NotGate(NEW_ID, bit); + + skip_inverter: + for (auto &op : node.outports) + module->connect(cell->getPort(op.first)[op.second], bit); + + sigs.push_back(bit); + } + + replaced_cells.push_back(cell); + stat_replaced[cell->type]++; + } + + if (not_replaced_count == 0 && replaced_cells.empty()) + continue; + + log("Module %s: replaced %d cells with %d new cells, skipped %d cells.\n", log_id(module), + GetSize(replaced_cells), GetSize(module->cells()) - orig_num_cells, not_replaced_count); + + if (!stat_replaced.empty()) { + stat_replaced.sort(); + log(" replaced %d cell types:\n", GetSize(stat_replaced)); + for (auto &it : stat_replaced) + log("%8d %s\n", it.second, log_id(it.first)); + } + + if (!stat_not_replaced.empty()) { + stat_not_replaced.sort(); + log(" not replaced %d cell types:\n", GetSize(stat_not_replaced)); + for (auto &it : stat_not_replaced) + log("%8d %s\n", it.second, log_id(it.first)); + } + + for (auto cell : replaced_cells) + module->remove(cell); + } + } +} AigmapPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/alumacc.cc b/passes/techmap/alumacc.cc index dcffed94d..9f6dd02d0 100644 --- a/passes/techmap/alumacc.cc +++ b/passes/techmap/alumacc.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -40,7 +40,7 @@ struct AlumaccWorker { std::vector<RTLIL::Cell*> cells; RTLIL::SigSpec a, b, c, y; - std::vector<std::tuple<bool, bool, bool, bool, RTLIL::SigSpec>> cmp; + std::vector<tuple<bool, bool, bool, bool, RTLIL::SigSpec>> cmp; bool is_signed, invert_b; RTLIL::Cell *alu_cell; @@ -98,9 +98,9 @@ struct AlumaccWorker } }; - std::map<RTLIL::SigBit, int> bit_users; - std::map<RTLIL::SigSpec, maccnode_t*> sig_macc; - std::map<RTLIL::SigSig, std::set<alunode_t*>> sig_alu; + dict<RTLIL::SigBit, int> bit_users; + dict<RTLIL::SigSpec, maccnode_t*> sig_macc; + dict<RTLIL::SigSig, pool<alunode_t*, hash_ptr_ops>> sig_alu; int macc_counter, alu_counter; AlumaccWorker(RTLIL::Module *module) : module(module), sigmap(module) @@ -138,7 +138,7 @@ struct AlumaccWorker n->users = 0; for (auto bit : n->y) - n->users = std::max(n->users, bit_users.at(bit) - 1); + n->users = max(n->users, bit_users.at(bit) - 1); if (cell->type.in("$pos", "$neg")) { @@ -215,7 +215,7 @@ struct AlumaccWorker { while (1) { - std::set<maccnode_t*> delete_nodes; + pool<maccnode_t*, hash_ptr_ops> delete_nodes; for (auto &it : sig_macc) { @@ -267,7 +267,7 @@ struct AlumaccWorker void macc_to_alu() { - std::set<maccnode_t*> delete_nodes; + pool<maccnode_t*, hash_ptr_ops> delete_nodes; for (auto &it : sig_macc) { @@ -409,7 +409,7 @@ struct AlumaccWorker n->a = A; n->b = B; n->c = RTLIL::S1; - n->y = module->addWire(NEW_ID, std::max(GetSize(A), GetSize(B))); + n->y = module->addWire(NEW_ID, max(GetSize(A), GetSize(B))); n->is_signed = is_signed; n->invert_b = true; sig_alu[RTLIL::SigSig(A, B)].insert(n); @@ -544,7 +544,7 @@ struct AlumaccPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing ALUMACC pass (create $alu and $macc cells).\n"); + log_header(design, "Executing ALUMACC pass (create $alu and $macc cells).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { @@ -563,5 +563,5 @@ struct AlumaccPass : public Pass { } } } AlumaccPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/techmap/deminout.cc b/passes/techmap/deminout.cc new file mode 100644 index 000000000..ed4e45762 --- /dev/null +++ b/passes/techmap/deminout.cc @@ -0,0 +1,116 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include "kernel/sigtools.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct DeminoutPass : public Pass { + DeminoutPass() : Pass("deminout", "demote inout ports to input or output") { } + virtual void help() + { + log("\n"); + log(" deminout [options] [selection]\n"); + log("\n"); + log("\"Demote\" inout ports to input or output ports, if possible.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + log_header(design, "Executing DEMINOUT pass (demote inout ports to input or output).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + // if (args[argidx] == "-bits") { + // flag_bits = true; + // continue; + // } + break; + } + extra_args(args, argidx, design); + + bool keep_running = true; + + while (keep_running) + { + keep_running = false; + + for (auto module : design->selected_modules()) + { + SigMap sigmap(module); + pool<SigBit> bits_written, bits_used, bits_inout; + dict<SigBit, int> bits_numports; + + for (auto wire : module->wires()) + if (wire->port_id) + for (auto bit : sigmap(wire)) + bits_numports[bit]++; + + for (auto cell : module->cells()) + for (auto &conn : cell->connections()) + { + bool cellport_out = cell->output(conn.first) || !cell->known(); + bool cellport_in = cell->input(conn.first) || !cell->known(); + + if (cellport_out && cellport_in) + for (auto bit : sigmap(conn.second)) + bits_inout.insert(bit); + + if (cellport_out) + for (auto bit : sigmap(conn.second)) + bits_written.insert(bit); + + if (cellport_in) + for (auto bit : sigmap(conn.second)) + bits_used.insert(bit); + } + + for (auto wire : module->selected_wires()) + if (wire->port_input && wire->port_output) + { + bool new_input = false; + bool new_output = false; + + for (auto bit : sigmap(wire)) + { + if (bits_numports[bit] > 1 || bits_inout.count(bit)) + new_input = true, new_output = true; + + if (bits_written.count(bit)) + new_output = true; + else if (bits_used.count(bit)) + new_input = true; + } + + if (new_input != new_output) { + log("Demoting inout port %s.%s to %s.\n", log_id(module), log_id(wire), new_input ? "input" : "output"); + wire->port_input = new_input; + wire->port_output = new_output; + keep_running = true; + } + } + } + } + } +} DeminoutPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/dff2dffe.cc b/passes/techmap/dff2dffe.cc index 17549bd06..1b8920bb7 100644 --- a/passes/techmap/dff2dffe.cc +++ b/passes/techmap/dff2dffe.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -27,12 +27,12 @@ PRIVATE_NAMESPACE_BEGIN struct Dff2dffeWorker { + const dict<IdString, IdString> &direct_dict; + RTLIL::Module *module; SigMap sigmap; CellTypes ct; - RTLIL::IdString direct_to; - typedef std::pair<RTLIL::Cell*, int> cell_int_t; std::map<RTLIL::SigBit, cell_int_t> bit2mux; std::vector<RTLIL::Cell*> dff_cells; @@ -42,8 +42,8 @@ struct Dff2dffeWorker typedef std::set<pattern_t> patterns_t; - Dff2dffeWorker(RTLIL::Module *module, RTLIL::IdString direct_from, RTLIL::IdString direct_to) : - module(module), sigmap(module), ct(module->design), direct_to(direct_to) + Dff2dffeWorker(RTLIL::Module *module, const dict<IdString, IdString> &direct_dict) : + direct_dict(direct_dict), module(module), sigmap(module), ct(module->design) { for (auto wire : module->wires()) { if (wire->port_output) @@ -57,11 +57,11 @@ struct Dff2dffeWorker for (int i = 0; i < GetSize(sig_y); i++) bit2mux[sig_y[i]] = cell_int_t(cell, i); } - if (direct_to.empty()) { + if (direct_dict.empty()) { if (cell->type == "$dff" || cell->type == "$_DFF_N_" || cell->type == "$_DFF_P_") dff_cells.push_back(cell); } else { - if (cell->type == direct_from) + if (direct_dict.count(cell->type)) dff_cells.push_back(cell); } for (auto conn : cell->connections()) { @@ -206,10 +206,10 @@ struct Dff2dffeWorker new_sig_d.append(sig_d[i]); new_sig_q.append(sig_q[i]); } - if (!direct_to.empty()) { - log(" converting %s cell %s to %s for %s -> %s.\n", log_id(dff_cell->type), log_id(dff_cell), log_id(direct_to), log_signal(new_sig_d), log_signal(new_sig_q)); + if (!direct_dict.empty()) { + log(" converting %s cell %s to %s for %s -> %s.\n", log_id(dff_cell->type), log_id(dff_cell), log_id(direct_dict.at(dff_cell->type)), log_signal(new_sig_d), log_signal(new_sig_q)); dff_cell->setPort("\\E", make_patterns_logic(it.first, true)); - dff_cell->type = direct_to; + dff_cell->type = direct_dict.at(dff_cell->type); } else if (dff_cell->type == "$dff") { RTLIL::Cell *new_cell = module->addDffe(NEW_ID, dff_cell->getPort("\\CLK"), make_patterns_logic(it.first, false), @@ -222,7 +222,7 @@ struct Dff2dffeWorker } } - if (!direct_to.empty()) + if (!direct_dict.empty()) return; if (remaining_indices.empty()) { @@ -243,9 +243,11 @@ struct Dff2dffeWorker void run() { - log("Transforming $dff to $dffe cells in module %s:\n", log_id(module)); - for (auto dff_cell : dff_cells) + log("Transforming FF to FF+Enable cells in module %s:\n", log_id(module)); + for (auto dff_cell : dff_cells) { + // log("Handling candidate %s:\n", log_id(dff_cell)); handle_dff_cell(dff_cell); + } } }; @@ -255,7 +257,7 @@ struct Dff2dffePass : public Pass { { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" dff2dffe [selection]\n"); + log(" dff2dffe [options] [selection]\n"); log("\n"); log("This pass transforms $dff cells driven by a tree of multiplexers with one or\n"); log("more feedback paths to $dffe cells. It also works on gate-level cells such as\n"); @@ -271,16 +273,22 @@ struct Dff2dffePass : public Pass { log(" <external_gate_type> is the cell type name for a cell with an\n"); log(" identical interface to the <internal_gate_type>, except it\n"); log(" also has an high-active enable port 'E'.\n"); - log(" Usually <external_gate_type> is an intemediate cell type\n"); + log(" Usually <external_gate_type> is an intermediate cell type\n"); log(" that is then translated to the final type using 'techmap'.\n"); log("\n"); + log(" -direct-match <pattern>\n"); + log(" like -direct for all DFF cell types matching the expression.\n"); + log(" this will use $__DFFE_* as <external_gate_type> matching the\n"); + log(" internal gate type $_DFF_*_, except for $_DFF_[NP]_, which is\n"); + log(" converted to $_DFFE_[NP]_.\n"); + log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing DFF2DFFE pass (transform $dff to $dffe where applicable).\n"); + log_header(design, "Executing DFF2DFFE pass (transform $dff to $dffe where applicable).\n"); bool unmap_mode = false; - RTLIL::IdString direct_from, direct_to; + dict<IdString, IdString> direct_dict; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { @@ -289,14 +297,38 @@ struct Dff2dffePass : public Pass { continue; } if (args[argidx] == "-direct" && argidx + 2 < args.size()) { - direct_from = RTLIL::escape_id(args[++argidx]); - direct_to = RTLIL::escape_id(args[++argidx]); + string direct_from = RTLIL::escape_id(args[++argidx]); + string direct_to = RTLIL::escape_id(args[++argidx]); + direct_dict[direct_from] = direct_to; + continue; + } + if (args[argidx] == "-direct-match" && argidx + 1 < args.size()) { + bool found_match = false; + const char *pattern = args[++argidx].c_str(); + if (patmatch(pattern, "$_DFF_P_" )) found_match = true, direct_dict["$_DFF_P_" ] = "$_DFFE_PP_"; + if (patmatch(pattern, "$_DFF_N_" )) found_match = true, direct_dict["$_DFF_N_" ] = "$_DFFE_NP_"; + if (patmatch(pattern, "$_DFF_NN0_")) found_match = true, direct_dict["$_DFF_NN0_"] = "$__DFFE_NN0"; + if (patmatch(pattern, "$_DFF_NN1_")) found_match = true, direct_dict["$_DFF_NN1_"] = "$__DFFE_NN1"; + if (patmatch(pattern, "$_DFF_NP0_")) found_match = true, direct_dict["$_DFF_NP0_"] = "$__DFFE_NP0"; + if (patmatch(pattern, "$_DFF_NP1_")) found_match = true, direct_dict["$_DFF_NP1_"] = "$__DFFE_NP1"; + if (patmatch(pattern, "$_DFF_PN0_")) found_match = true, direct_dict["$_DFF_PN0_"] = "$__DFFE_PN0"; + if (patmatch(pattern, "$_DFF_PN1_")) found_match = true, direct_dict["$_DFF_PN1_"] = "$__DFFE_PN1"; + if (patmatch(pattern, "$_DFF_PP0_")) found_match = true, direct_dict["$_DFF_PP0_"] = "$__DFFE_PP0"; + if (patmatch(pattern, "$_DFF_PP1_")) found_match = true, direct_dict["$_DFF_PP1_"] = "$__DFFE_PP1"; + if (!found_match) + log_cmd_error("No cell types matched pattern '%s'.\n", pattern); continue; } break; } extra_args(args, argidx, design); + if (!direct_dict.empty()) { + log("Selected cell types for direct conversion:\n"); + for (auto &it : direct_dict) + log(" %s -> %s\n", log_id(it.first), log_id(it.second)); + } + for (auto mod : design->selected_modules()) if (!mod->has_processes_warn()) { @@ -328,10 +360,10 @@ struct Dff2dffePass : public Pass { continue; } - Dff2dffeWorker worker(mod, direct_from, direct_to); + Dff2dffeWorker worker(mod, direct_dict); worker.run(); } } } Dff2dffePass; - + PRIVATE_NAMESPACE_END diff --git a/passes/techmap/dffinit.cc b/passes/techmap/dffinit.cc new file mode 100644 index 000000000..d737b3424 --- /dev/null +++ b/passes/techmap/dffinit.cc @@ -0,0 +1,135 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include "kernel/sigtools.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct DffinitPass : public Pass { + DffinitPass() : Pass("dffinit", "set INIT param on FF cells") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" dffinit [options] [selection]\n"); + log("\n"); + log("This pass sets an FF cell parameter to the the initial value of the net it\n"); + log("drives. (This is primarily used in FPGA flows.)\n"); + log("\n"); + log(" -ff <cell_name> <output_port> <init_param>\n"); + log(" operate on the specified cell type. this option can be used\n"); + log(" multiple times.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + log_header(design, "Executing DFFINIT pass (set INIT param on FF cells).\n"); + + dict<IdString, dict<IdString, IdString>> ff_types; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-ff" && argidx+3 < args.size()) { + IdString cell_name = RTLIL::escape_id(args[++argidx]); + IdString output_port = RTLIL::escape_id(args[++argidx]); + IdString init_param = RTLIL::escape_id(args[++argidx]); + ff_types[cell_name][output_port] = init_param; + continue; + } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) + { + SigMap sigmap(module); + dict<SigBit, State> init_bits; + pool<SigBit> cleanup_bits; + pool<SigBit> used_bits; + + for (auto wire : module->selected_wires()) { + if (wire->attributes.count("\\init")) { + Const value = wire->attributes.at("\\init"); + for (int i = 0; i < min(GetSize(value), GetSize(wire)); i++) + init_bits[sigmap(SigBit(wire, i))] = value[i]; + } + if (wire->port_output) + for (auto bit : sigmap(wire)) + used_bits.insert(bit); + } + + for (auto cell : module->selected_cells()) + { + for (auto it : cell->connections()) + if (!cell->known() || cell->input(it.first)) + for (auto bit : sigmap(it.second)) + used_bits.insert(bit); + + if (ff_types.count(cell->type) == 0) + continue; + + for (auto &it : ff_types[cell->type]) + { + if (!cell->hasPort(it.first)) + continue; + + SigSpec sig = sigmap(cell->getPort(it.first)); + Const value; + + if (cell->hasParam(it.second)) + value = cell->getParam(it.second); + + for (int i = 0; i < GetSize(sig); i++) { + if (init_bits.count(sig[i]) == 0) + continue; + while (GetSize(value.bits) <= i) + value.bits.push_back(State::S0); + value.bits[i] = init_bits.at(sig[i]); + cleanup_bits.insert(sig[i]); + } + + log("Setting %s.%s.%s (port=%s, net=%s) to %s.\n", log_id(module), log_id(cell), log_id(it.second), + log_id(it.first), log_signal(sig), log_signal(value)); + cell->setParam(it.second, value); + } + } + + for (auto wire : module->selected_wires()) + if (wire->attributes.count("\\init")) { + Const &value = wire->attributes.at("\\init"); + bool do_cleanup = true; + for (int i = 0; i < min(GetSize(value), GetSize(wire)); i++) { + SigBit bit = sigmap(SigBit(wire, i)); + if (cleanup_bits.count(bit) || !used_bits.count(bit)) + value[i] = State::Sx; + else if (value[i] != State::Sx) + do_cleanup = false; + } + if (do_cleanup) { + log("Removing init attribute from wire %s.%s.\n", log_id(module), log_id(wire)); + wire->attributes.erase("\\init"); + } + } + } + } +} DffinitPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/dfflibmap.cc b/passes/techmap/dfflibmap.cc index b0318a0b3..c8104fb7e 100644 --- a/passes/techmap/dfflibmap.cc +++ b/passes/techmap/dfflibmap.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -17,8 +17,8 @@ * */ -#include "kernel/register.h" -#include "kernel/log.h" +#include "kernel/yosys.h" +#include "kernel/sigtools.h" #include "libparse.h" #include <string.h> #include <errno.h> @@ -80,7 +80,7 @@ static bool parse_pin(LibertyAst *cell, LibertyAst *attr, std::string &pin_name, { if (cell == NULL || attr == NULL || attr->value.empty()) return false; - + std::string value = attr->value; for (size_t pos = value.find_first_of("\" \t()"); pos != std::string::npos; pos = value.find_first_of("\" \t()")) @@ -108,6 +108,7 @@ static void find_cell(LibertyAst *ast, std::string cell_type, bool clkpol, bool LibertyAst *best_cell = NULL; std::map<std::string, char> best_cell_ports; int best_cell_pins = 0; + bool best_cell_noninv = false; double best_cell_area = 0; if (ast->id != "library") @@ -118,6 +119,10 @@ static void find_cell(LibertyAst *ast, std::string cell_type, bool clkpol, bool if (cell->id != "cell" || cell->args.size() != 1) continue; + LibertyAst *dn = cell->find("dont_use"); + if (dn != NULL && dn->value == "true") + continue; + LibertyAst *ff = cell->find("ff"); if (ff == NULL) continue; @@ -151,6 +156,7 @@ static void find_cell(LibertyAst *ast, std::string cell_type, bool clkpol, bool int num_pins = 0; bool found_output = false; + bool found_noninv_output = false; for (auto pin : cell->children) { if (pin->id != "pin" || pin->args.size() != 1) @@ -169,8 +175,16 @@ static void find_cell(LibertyAst *ast, std::string cell_type, bool clkpol, bool std::string value = func->value; for (size_t pos = value.find_first_of("\" \t"); pos != std::string::npos; pos = value.find_first_of("\" \t")) value.erase(pos, 1); - if ((cell_next_pol == true && value == ff->args[0]) || (cell_next_pol == false && value == ff->args[1])) { - this_cell_ports[pin->args[0]] = 'Q'; + if (value == ff->args[0]) { + this_cell_ports[pin->args[0]] = cell_next_pol ? 'Q' : 'q'; + if (cell_next_pol) + found_noninv_output = true; + found_output = true; + } else + if (value == ff->args[1]) { + this_cell_ports[pin->args[0]] = cell_next_pol ? 'q' : 'Q'; + if (!cell_next_pol) + found_noninv_output = true; found_output = true; } } @@ -179,7 +193,7 @@ static void find_cell(LibertyAst *ast, std::string cell_type, bool clkpol, bool this_cell_ports[pin->args[0]] = 0; } - if (!found_output || (best_cell != NULL && num_pins > best_cell_pins)) + if (!found_output || (best_cell != NULL && (num_pins > best_cell_pins || (best_cell_noninv && !found_noninv_output)))) continue; if (best_cell != NULL && num_pins == best_cell_pins && area > best_cell_area) @@ -188,12 +202,14 @@ static void find_cell(LibertyAst *ast, std::string cell_type, bool clkpol, bool best_cell = cell; best_cell_pins = num_pins; best_cell_area = area; + best_cell_noninv = found_noninv_output; best_cell_ports.swap(this_cell_ports); continue_cell_loop:; } if (best_cell != NULL) { - log(" cell %s (pins=%d, area=%.2f) is a direct match for cell type %s.\n", best_cell->args[0].c_str(), best_cell_pins, best_cell_area, cell_type.c_str()); + log(" cell %s (%sinv, pins=%d, area=%.2f) is a direct match for cell type %s.\n", + best_cell->args[0].c_str(), best_cell_noninv ? "non" : "", best_cell_pins, best_cell_area, cell_type.c_str()); if (prepare_mode) { cell_mappings[cell_type].cell_name = cell_type; cell_mappings[cell_type].ports["C"] = 'C'; @@ -213,6 +229,7 @@ static void find_cell_sr(LibertyAst *ast, std::string cell_type, bool clkpol, bo LibertyAst *best_cell = NULL; std::map<std::string, char> best_cell_ports; int best_cell_pins = 0; + bool best_cell_noninv = false; double best_cell_area = 0; if (ast->id != "library") @@ -252,6 +269,7 @@ static void find_cell_sr(LibertyAst *ast, std::string cell_type, bool clkpol, bo int num_pins = 0; bool found_output = false; + bool found_noninv_output = false; for (auto pin : cell->children) { if (pin->id != "pin" || pin->args.size() != 1) @@ -270,8 +288,16 @@ static void find_cell_sr(LibertyAst *ast, std::string cell_type, bool clkpol, bo std::string value = func->value; for (size_t pos = value.find_first_of("\" \t"); pos != std::string::npos; pos = value.find_first_of("\" \t")) value.erase(pos, 1); - if ((cell_next_pol == true && value == ff->args[0]) || (cell_next_pol == false && value == ff->args[1])) { - this_cell_ports[pin->args[0]] = 'Q'; + if (value == ff->args[0]) { + this_cell_ports[pin->args[0]] = cell_next_pol ? 'Q' : 'q'; + if (cell_next_pol) + found_noninv_output = true; + found_output = true; + } else + if (value == ff->args[1]) { + this_cell_ports[pin->args[0]] = cell_next_pol ? 'q' : 'Q'; + if (!cell_next_pol) + found_noninv_output = true; found_output = true; } } @@ -280,7 +306,7 @@ static void find_cell_sr(LibertyAst *ast, std::string cell_type, bool clkpol, bo this_cell_ports[pin->args[0]] = 0; } - if (!found_output || (best_cell != NULL && num_pins > best_cell_pins)) + if (!found_output || (best_cell != NULL && (num_pins > best_cell_pins || (best_cell_noninv && !found_noninv_output)))) continue; if (best_cell != NULL && num_pins == best_cell_pins && area > best_cell_area) @@ -289,12 +315,14 @@ static void find_cell_sr(LibertyAst *ast, std::string cell_type, bool clkpol, bo best_cell = cell; best_cell_pins = num_pins; best_cell_area = area; + best_cell_noninv = found_noninv_output; best_cell_ports.swap(this_cell_ports); continue_cell_loop:; } if (best_cell != NULL) { - log(" cell %s (pins=%d, area=%.2f) is a direct match for cell type %s.\n", best_cell->args[0].c_str(), best_cell_pins, best_cell_area, cell_type.c_str()); + log(" cell %s (%sinv, pins=%d, area=%.2f) is a direct match for cell type %s.\n", + best_cell->args[0].c_str(), best_cell_noninv ? "non" : "", best_cell_pins, best_cell_area, cell_type.c_str()); if (prepare_mode) { cell_mappings[cell_type].cell_name = cell_type; cell_mappings[cell_type].ports["C"] = 'C'; @@ -406,14 +434,42 @@ static void map_sr_to_arst(const char *from, const char *to) } } +static void map_adff_to_dff(const char *from, const char *to) +{ + if (!cell_mappings.count(from) || cell_mappings.count(to) > 0) + return; + + char from_clk_pol YS_ATTRIBUTE(unused) = from[6]; + char from_rst_pol = from[7]; + char to_clk_pol YS_ATTRIBUTE(unused) = to[6]; + + log_assert(from_clk_pol == to_clk_pol); + + log(" create mapping for %s from mapping for %s.\n", to, from); + cell_mappings[to].cell_name = cell_mappings[from].cell_name; + cell_mappings[to].ports = cell_mappings[from].ports; + + for (auto &it : cell_mappings[to].ports) { + if (it.second == 'S' || it.second == 'R') + it.second = from_rst_pol == 'P' ? '0' : '1'; + if (it.second == 's' || it.second == 'r') + it.second = from_rst_pol == 'P' ? '1' : '0'; + } +} + static void dfflibmap(RTLIL::Design *design, RTLIL::Module *module, bool prepare_mode) { log("Mapping DFF cells in module `%s':\n", module->name.c_str()); + dict<SigBit, pool<Cell*>> notmap; + SigMap sigmap(module); + std::vector<RTLIL::Cell*> cell_list; for (auto &it : module->cells_) { if (design->selected(module, it.second) && cell_mappings.count(it.second->type) > 0) cell_list.push_back(it.second); + if (it.second->type == "$_NOT_") + notmap[sigmap(it.second->getPort("\\A"))].insert(it.second); } std::map<std::string, int> stats; @@ -427,6 +483,12 @@ static void dfflibmap(RTLIL::Design *design, RTLIL::Module *module, bool prepare cell_mapping &cm = cell_mappings[cell_type]; RTLIL::Cell *new_cell = module->addCell(cell_name, prepare_mode ? cm.cell_name : "\\" + cm.cell_name); + bool has_q = false, has_qn = false; + for (auto &port : cm.ports) { + if (port.second == 'Q') has_q = true; + if (port.second == 'q') has_qn = true; + } + for (auto &port : cm.ports) { RTLIL::SigSpec sig; if ('A' <= port.second && port.second <= 'Z') { @@ -435,7 +497,14 @@ static void dfflibmap(RTLIL::Design *design, RTLIL::Module *module, bool prepare if (port.second == 'q') { RTLIL::SigSpec old_sig = cell_connections[std::string("\\") + char(port.second - ('a' - 'A'))]; sig = module->addWire(NEW_ID, GetSize(old_sig)); - module->addNotGate(NEW_ID, sig, old_sig); + if (has_q && has_qn) { + for (auto &it : notmap[sigmap(old_sig)]) { + module->connect(it->getPort("\\Y"), sig); + it->setPort("\\Y", module->addWire(NEW_ID, GetSize(old_sig))); + } + } else { + module->addNotGate(NEW_ID, sig, old_sig); + } } else if ('a' <= port.second && port.second <= 'z') { sig = cell_connections[std::string("\\") + char(port.second - ('a' - 'A'))]; @@ -444,7 +513,9 @@ static void dfflibmap(RTLIL::Design *design, RTLIL::Module *module, bool prepare if (port.second == '0' || port.second == '1') { sig = RTLIL::SigSpec(port.second == '0' ? 0 : 1, 1); } else - if (port.second != 0) + if (port.second == 0) { + sig = module->addWire(NEW_ID); + } else log_abort(); new_cell->setPort("\\" + port.first, sig); } @@ -476,7 +547,7 @@ struct DfflibmapPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing DFFLIBMAP pass (mapping DFF cells to sequential cells from liberty file).\n"); + log_header(design, "Executing DFFLIBMAP pass (mapping DFF cells to sequential cells from liberty file).\n"); std::string liberty_file; bool prepare_mode = false; @@ -487,6 +558,7 @@ struct DfflibmapPass : public Pass { std::string arg = args[argidx]; if (arg == "-liberty" && argidx+1 < args.size()) { liberty_file = args[++argidx]; + rewrite_filename(liberty_file); continue; } if (arg == "-prepare") { @@ -558,7 +630,16 @@ struct DfflibmapPass : public Pass { map_sr_to_arst("$_DFFSR_PNN_", "$_DFF_PN1_"); map_sr_to_arst("$_DFFSR_PPP_", "$_DFF_PP0_"); map_sr_to_arst("$_DFFSR_PPP_", "$_DFF_PP1_"); - + + map_adff_to_dff("$_DFF_NN0_", "$_DFF_N_"); + map_adff_to_dff("$_DFF_NN1_", "$_DFF_N_"); + map_adff_to_dff("$_DFF_NP0_", "$_DFF_N_"); + map_adff_to_dff("$_DFF_NP1_", "$_DFF_N_"); + map_adff_to_dff("$_DFF_PN0_", "$_DFF_P_"); + map_adff_to_dff("$_DFF_PN1_", "$_DFF_P_"); + map_adff_to_dff("$_DFF_PP0_", "$_DFF_P_"); + map_adff_to_dff("$_DFF_PP1_", "$_DFF_P_"); + log(" final dff cell mappings:\n"); logmap_all(); @@ -569,5 +650,5 @@ struct DfflibmapPass : public Pass { cell_mappings.clear(); } } DfflibmapPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/techmap/dffsr2dff.cc b/passes/techmap/dffsr2dff.cc new file mode 100644 index 000000000..0d4d53627 --- /dev/null +++ b/passes/techmap/dffsr2dff.cc @@ -0,0 +1,213 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include "kernel/sigtools.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +void dffsr_worker(SigMap &sigmap, Module *module, Cell *cell) +{ + if (cell->type == "$dffsr") + { + int width = cell->getParam("\\WIDTH").as_int(); + bool setpol = cell->getParam("\\SET_POLARITY").as_bool(); + bool clrpol = cell->getParam("\\CLR_POLARITY").as_bool(); + + SigBit setunused = setpol ? State::S0 : State::S1; + SigBit clrunused = clrpol ? State::S0 : State::S1; + + SigSpec setsig = sigmap(cell->getPort("\\SET")); + SigSpec clrsig = sigmap(cell->getPort("\\CLR")); + + Const reset_val; + SigSpec setctrl, clrctrl; + + for (int i = 0; i < width; i++) + { + SigBit setbit = setsig[i], clrbit = clrsig[i]; + + if (setbit == setunused) { + clrctrl.append(clrbit); + reset_val.bits.push_back(State::S0); + continue; + } + + if (clrbit == clrunused) { + setctrl.append(setbit); + reset_val.bits.push_back(State::S1); + continue; + } + + return; + } + + setctrl.sort_and_unify(); + clrctrl.sort_and_unify(); + + if (GetSize(setctrl) > 1 || GetSize(clrctrl) > 1) + return; + + if (GetSize(setctrl) == 0 && GetSize(clrctrl) == 0) + return; + + if (GetSize(setctrl) == 1 && GetSize(clrctrl) == 1) { + if (setpol != clrpol) + return; + if (setctrl != clrctrl) + return; + } + + log("Converting %s cell %s.%s to $adff.\n", log_id(cell->type), log_id(module), log_id(cell)); + + if (GetSize(setctrl) == 1) { + cell->setPort("\\ARST", setctrl); + cell->setParam("\\ARST_POLARITY", setpol); + } else { + cell->setPort("\\ARST", clrctrl); + cell->setParam("\\ARST_POLARITY", clrpol); + } + + cell->type = "$adff"; + cell->unsetPort("\\SET"); + cell->unsetPort("\\CLR"); + cell->setParam("\\ARST_VALUE", reset_val); + cell->unsetParam("\\SET_POLARITY"); + cell->unsetParam("\\CLR_POLARITY"); + + return; + } + + if (cell->type.in("$_DFFSR_NNN_", "$_DFFSR_NNP_", "$_DFFSR_NPN_", "$_DFFSR_NPP_", + "$_DFFSR_PNN_", "$_DFFSR_PNP_", "$_DFFSR_PPN_", "$_DFFSR_PPP_")) + { + char clkpol = cell->type.c_str()[8]; + char setpol = cell->type.c_str()[9]; + char clrpol = cell->type.c_str()[10]; + + SigBit setbit = sigmap(cell->getPort("\\S")); + SigBit clrbit = sigmap(cell->getPort("\\R")); + + SigBit setunused = setpol == 'P' ? State::S0 : State::S1; + SigBit clrunused = clrpol == 'P' ? State::S0 : State::S1; + + IdString oldtype = cell->type; + + if (setbit == setunused) { + cell->type = stringf("$_DFF_%c%c0_", clkpol, clrpol); + cell->unsetPort("\\S"); + goto converted_gate; + } + + if (clrbit == clrunused) { + cell->type = stringf("$_DFF_%c%c1_", clkpol, setpol); + cell->setPort("\\R", cell->getPort("\\S")); + cell->unsetPort("\\S"); + goto converted_gate; + } + + return; + + converted_gate: + log("Converting %s cell %s.%s to %s.\n", log_id(oldtype), log_id(module), log_id(cell), log_id(cell->type)); + return; + } +} + +void adff_worker(SigMap &sigmap, Module *module, Cell *cell) +{ + if (cell->type == "$adff") + { + bool rstpol = cell->getParam("\\ARST_POLARITY").as_bool(); + SigBit rstunused = rstpol ? State::S0 : State::S1; + SigSpec rstsig = sigmap(cell->getPort("\\ARST")); + + if (rstsig != rstunused) + return; + + log("Converting %s cell %s.%s to $dff.\n", log_id(cell->type), log_id(module), log_id(cell)); + + cell->type = "$dff"; + cell->unsetPort("\\ARST"); + cell->unsetParam("\\ARST_VALUE"); + cell->unsetParam("\\ARST_POLARITY"); + + return; + } + + if (cell->type.in("$_DFF_NN0_", "$_DFF_NN1_", "$_DFF_NP0_", "$_DFF_NP1_", + "$_DFF_PN0_", "$_DFF_PN1_", "$_DFF_PP0_", "$_DFF_PP1_")) + { + char clkpol = cell->type.c_str()[6]; + char rstpol = cell->type.c_str()[7]; + + SigBit rstbit = sigmap(cell->getPort("\\R")); + SigBit rstunused = rstpol == 'P' ? State::S0 : State::S1; + + if (rstbit != rstunused) + return; + + IdString newtype = stringf("$_DFF_%c_", clkpol); + log("Converting %s cell %s.%s to %s.\n", log_id(cell->type), log_id(module), log_id(cell), log_id(newtype)); + + cell->type = newtype; + cell->unsetPort("\\R"); + + return; + } +} + +struct Dffsr2dffPass : public Pass { + Dffsr2dffPass() : Pass("dffsr2dff", "convert DFFSR cells to simpler FF cell types") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" dffsr2dff [options] [selection]\n"); + log("\n"); + log("This pass converts DFFSR cells ($dffsr, $_DFFSR_???_) and ADFF cells ($adff,\n"); + log("$_DFF_???_) to simpler FF cell types when any of the set/reset inputs is unused.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + log_header(design, "Executing DFFSR2DFF pass (mapping DFFSR cells to simpler FFs).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + // if (args[argidx] == "-v") { + // continue; + // } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) { + SigMap sigmap(module); + for (auto cell : module->selected_cells()) { + dffsr_worker(sigmap, module, cell); + adff_worker(sigmap, module, cell); + } + } + } +} Dffsr2dffPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/extract.cc b/passes/techmap/extract.cc index ff99040e1..71e29c60b 100644 --- a/passes/techmap/extract.cc +++ b/passes/techmap/extract.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -130,7 +130,7 @@ public: RTLIL::SigSpec needleSig = conn.second; RTLIL::SigSpec haystackSig = haystackCell->getPort(portMapping.at(conn.first.str())); - for (int i = 0; i < std::min(needleSig.size(), haystackSig.size()); i++) { + for (int i = 0; i < min(needleSig.size(), haystackSig.size()); i++) { RTLIL::Wire *needleWire = needleSig[i].wire, *haystackWire = haystackSig[i].wire; if (needleWire != lastNeedleWire || haystackWire != lastHaystackWire) if (!compareAttributes(wire_attr, needleWire ? needleWire->attributes : emptyAttr, haystackWire ? haystackWire->attributes : emptyAttr)) @@ -361,7 +361,7 @@ struct ExtractPass : public Pass { log("\n"); log("This pass looks for subcircuits that are isomorphic to any of the modules\n"); log("in the given map file and replaces them with instances of this modules. The\n"); - log("map file can be a verilog source file (*.v) or an ilang file (*.il).\n"); + log("map file can be a Verilog source file (*.v) or an ilang file (*.il).\n"); log("\n"); log(" -map <map_file>\n"); log(" use the modules in this file as reference. This option can be used\n"); @@ -390,11 +390,11 @@ struct ExtractPass : public Pass { log(" match. This option can be used multiple times.\n"); log("\n"); log(" -swap <needle_type> <port1>,<port2>[,...]\n"); - log(" Register a set of swapable ports for a needle cell type.\n"); + log(" Register a set of swappable ports for a needle cell type.\n"); log(" This option can be used multiple times.\n"); log("\n"); log(" -perm <needle_type> <port1>,<port2>[,...] <portA>,<portB>[,...]\n"); - log(" Register a valid permutation of swapable ports for a needle\n"); + log(" Register a valid permutation of swappable ports for a needle\n"); log(" cell type. This option can be used multiple times.\n"); log("\n"); log(" -cell_attr <attribute_name>\n"); @@ -409,7 +409,7 @@ struct ExtractPass : public Pass { log(" -ignore_param <cell_type> <parameter_name>\n"); log(" Do not use this parameter when matching cells.\n"); log("\n"); - log("This pass does not operate on modules with uprocessed processes in it.\n"); + log("This pass does not operate on modules with unprocessed processes in it.\n"); log("(I.e. the 'proc' pass should be used first to convert processes to netlists.)\n"); log("\n"); log("This pass can also be used for mining for frequent subcircuits. In this mode\n"); @@ -442,7 +442,7 @@ struct ExtractPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing EXTRACT pass (map subcircuits to cells).\n"); + log_header(design, "Executing EXTRACT pass (map subcircuits to cells).\n"); log_push(); SubCircuitSolver solver; @@ -607,6 +607,7 @@ struct ExtractPass : public Pass { else { std::ifstream f; + rewrite_filename(filename); f.open(filename.c_str()); if (f.fail()) { delete map; @@ -626,7 +627,7 @@ struct ExtractPass : public Pass { std::map<std::string, RTLIL::Module*> needle_map, haystack_map; std::vector<RTLIL::Module*> needle_list; - log_header("Creating graphs for SubCircuit library.\n"); + log_header(design, "Creating graphs for SubCircuit library.\n"); if (!mine_mode) for (auto &mod_it : map->modules_) { @@ -649,11 +650,11 @@ struct ExtractPass : public Pass { haystack_map[graph_name] = mod_it.second; } } - + if (!mine_mode) { std::vector<SubCircuit::Solver::Result> results; - log_header("Running solver from SubCircuit library.\n"); + log_header(design, "Running solver from SubCircuit library.\n"); std::sort(needle_list.begin(), needle_list.end(), compareSortNeedleList); @@ -666,7 +667,7 @@ struct ExtractPass : public Pass { if (results.size() > 0) { - log_header("Substitute SubCircuits with cells.\n"); + log_header(design, "Substitute SubCircuits with cells.\n"); for (int i = 0; i < int(results.size()); i++) { auto &result = results[i]; @@ -687,7 +688,7 @@ struct ExtractPass : public Pass { { std::vector<SubCircuit::Solver::MineResult> results; - log_header("Running miner from SubCircuit library.\n"); + log_header(design, "Running miner from SubCircuit library.\n"); solver.mine(results, mine_cells_min, mine_cells_max, mine_min_freq, mine_limit_mod); map = new RTLIL::Design; @@ -736,7 +737,7 @@ struct ExtractPass : public Pass { RTLIL::Cell *newCell = newMod->addCell(cell->name, cell->type); newCell->parameters = cell->parameters; for (auto &conn : cell->connections()) { - std::vector<RTLIL::SigChunk> chunks = sigmap(conn.second); + std::vector<SigChunk> chunks = sigmap(conn.second); for (auto &chunk : chunks) if (chunk.wire != NULL) chunk.wire = newMod->wires_.at(chunk.wire->name); @@ -746,6 +747,7 @@ struct ExtractPass : public Pass { } std::ofstream f; + rewrite_filename(mine_outfile); f.open(mine_outfile.c_str(), std::ofstream::trunc); if (f.fail()) log_error("Can't open output file `%s'.\n", mine_outfile.c_str()); @@ -757,5 +759,5 @@ struct ExtractPass : public Pass { log_pop(); } } ExtractPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/techmap/hilomap.cc b/passes/techmap/hilomap.cc index 9a14ffa3c..82cecac26 100644 --- a/passes/techmap/hilomap.cc +++ b/passes/techmap/hilomap.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -76,7 +76,7 @@ struct HilomapPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing HILOMAP pass (mapping to constant drivers).\n"); + log_header(design, "Executing HILOMAP pass (mapping to constant drivers).\n"); hicell_celltype = std::string(); hicell_portname = std::string(); @@ -119,5 +119,5 @@ struct HilomapPass : public Pass { } } } HilomapPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/techmap/iopadmap.cc b/passes/techmap/iopadmap.cc index 75d02c828..4acbf7c0d 100644 --- a/passes/techmap/iopadmap.cc +++ b/passes/techmap/iopadmap.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -17,9 +17,8 @@ * */ -#include "kernel/register.h" -#include "kernel/rtlil.h" -#include "kernel/log.h" +#include "kernel/yosys.h" +#include "kernel/sigtools.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -45,15 +44,26 @@ struct IopadmapPass : public Pass { log("the resulting cells to more sophisticated PAD cells.\n"); log("\n"); log(" -inpad <celltype> <portname>[:<portname>]\n"); - log(" Map module input ports to the given cell type with\n"); - log(" the given port name. if a 2nd portname is given, the\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(" portname as output.\n"); + log(" portname as the port facing the module port.\n"); log("\n"); log(" -outpad <celltype> <portname>[:<portname>]\n"); log(" -inoutpad <celltype> <portname>[:<portname>]\n"); log(" Similar to -inpad, but for output and inout ports.\n"); log("\n"); + log(" -toutpad <celltype> <portname>:<portname>[:<portname>]\n"); + log(" Merges $_TBUF_ cells into the output pad cell. This takes precedence\n"); + log(" over the other -outpad cell. The first portname is the enable input\n"); + log(" of the tristate driver.\n"); + log("\n"); + log(" -tinoutpad <celltype> <portname>:<portname>:<portname>[:<portname>]\n"); + log(" Merges $_TBUF_ cells into the inout pad cell. This takes precedence\n"); + log(" over the other -inoutpad cell. The first portname is the enable input\n"); + log(" of the tristate driver and the 2nd portname is the internal output\n"); + log(" buffering the external signal.\n"); + log("\n"); log(" -widthparam <param_name>\n"); log(" Use the specified parameter name to set the port width.\n"); log("\n"); @@ -65,14 +75,18 @@ struct IopadmapPass : public Pass { log(" are wider. (the default behavior is to create word-wide\n"); log(" buffers using -widthparam to set the word size on the cell.)\n"); log("\n"); + log("Tristate PADS (-toutpad, -tinoutpad) always operate in -bits mode.\n"); + log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing IOPADMAP pass (mapping inputs/outputs to IO-PAD cells).\n"); + log_header(design, "Executing IOPADMAP pass (mapping inputs/outputs to IO-PAD cells).\n"); std::string inpad_celltype, inpad_portname, inpad_portname2; std::string outpad_celltype, outpad_portname, outpad_portname2; std::string inoutpad_celltype, inoutpad_portname, inoutpad_portname2; + std::string toutpad_celltype, toutpad_portname, toutpad_portname2, toutpad_portname3; + std::string tinoutpad_celltype, tinoutpad_portname, tinoutpad_portname2, tinoutpad_portname3, tinoutpad_portname4; std::string widthparam, nameparam; bool flag_bits = false; @@ -98,6 +112,21 @@ struct IopadmapPass : public Pass { split_portname_pair(inoutpad_portname, inoutpad_portname2); continue; } + if (arg == "-toutpad" && argidx+2 < args.size()) { + toutpad_celltype = args[++argidx]; + toutpad_portname = args[++argidx]; + split_portname_pair(toutpad_portname, toutpad_portname2); + split_portname_pair(toutpad_portname2, toutpad_portname3); + continue; + } + if (arg == "-tinoutpad" && argidx+2 < args.size()) { + tinoutpad_celltype = args[++argidx]; + tinoutpad_portname = args[++argidx]; + split_portname_pair(tinoutpad_portname, tinoutpad_portname2); + split_portname_pair(tinoutpad_portname2, tinoutpad_portname3); + split_portname_pair(tinoutpad_portname3, tinoutpad_portname4); + continue; + } if (arg == "-widthparam" && argidx+1 < args.size()) { widthparam = args[++argidx]; continue; @@ -114,21 +143,134 @@ struct IopadmapPass : public Pass { } extra_args(args, argidx, design); - for (auto &it : design->modules_) + for (auto module : design->selected_modules()) { - RTLIL::Module *module = it.second; - - if (!design->selected(module) || module->get_bool_attribute("\\blackbox")) - continue; + dict<IdString, pool<int>> skip_wires; - for (auto &it2 : module->wires_) + if (!toutpad_celltype.empty() || !tinoutpad_celltype.empty()) { - RTLIL::Wire *wire = it2.second; + SigMap sigmap(module); + dict<SigBit, pair<IdString, pool<IdString>>> tbuf_bits; + + for (auto cell : module->cells()) + if (cell->type == "$_TBUF_") { + SigBit bit = sigmap(cell->getPort("\\Y").as_bit()); + tbuf_bits[bit].first = cell->name; + } + + for (auto cell : module->cells()) + for (auto port : cell->connections()) + for (auto bit : sigmap(port.second)) + if (tbuf_bits.count(bit)) + tbuf_bits.at(bit).second.insert(cell->name); + + for (auto wire : module->selected_wires()) + { + if (!wire->port_output) + continue; + + for (int i = 0; i < GetSize(wire); i++) + { + SigBit wire_bit(wire, i); + SigBit mapped_wire_bit = sigmap(wire_bit); + + if (tbuf_bits.count(mapped_wire_bit) == 0) + continue; + + auto &tbuf_cache = tbuf_bits.at(mapped_wire_bit); + Cell *tbuf_cell = module->cell(tbuf_cache.first); + + if (tbuf_cell == nullptr) + continue; - if (!wire->port_id || !design->selected(module, wire)) + SigBit en_sig = tbuf_cell->getPort("\\E").as_bit(); + SigBit data_sig = tbuf_cell->getPort("\\A").as_bit(); + + if (wire->port_input && !tinoutpad_celltype.empty()) + { + log("Mapping port %s.%s[%d] using %s.\n", log_id(module), log_id(wire), i, tinoutpad_celltype.c_str()); + + Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(tinoutpad_celltype)); + Wire *owire = module->addWire(NEW_ID); + + cell->setPort(RTLIL::escape_id(tinoutpad_portname), en_sig); + cell->setPort(RTLIL::escape_id(tinoutpad_portname2), owire); + cell->setPort(RTLIL::escape_id(tinoutpad_portname3), data_sig); + cell->setPort(RTLIL::escape_id(tinoutpad_portname4), wire_bit); + cell->attributes["\\keep"] = RTLIL::Const(1); + + for (auto cn : tbuf_cache.second) { + auto c = module->cell(cn); + if (c == nullptr) + continue; + for (auto port : c->connections()) { + SigSpec sig = port.second; + bool newsig = false; + for (auto &bit : sig) + if (sigmap(bit) == mapped_wire_bit) { + bit = owire; + newsig = true; + } + if (newsig) + c->setPort(port.first, sig); + } + } + + + module->remove(tbuf_cell); + skip_wires[wire->name].insert(i); + continue; + } + + if (!wire->port_input && !toutpad_celltype.empty()) + { + log("Mapping port %s.%s[%d] using %s.\n", log_id(module), log_id(wire), i, toutpad_celltype.c_str()); + + Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(toutpad_celltype)); + + cell->setPort(RTLIL::escape_id(toutpad_portname), en_sig); + cell->setPort(RTLIL::escape_id(toutpad_portname2), data_sig); + cell->setPort(RTLIL::escape_id(toutpad_portname3), wire_bit); + cell->attributes["\\keep"] = RTLIL::Const(1); + + for (auto cn : tbuf_cache.second) { + auto c = module->cell(cn); + if (c == nullptr) + continue; + for (auto port : c->connections()) { + SigSpec sig = port.second; + bool newsig = false; + for (auto &bit : sig) + if (sigmap(bit) == mapped_wire_bit) { + bit = data_sig; + newsig = true; + } + if (newsig) + c->setPort(port.first, sig); + } + } + + module->remove(tbuf_cell); + skip_wires[wire->name].insert(i); + continue; + } + } + } + } + + for (auto wire : module->selected_wires()) + { + if (!wire->port_id) continue; std::string celltype, portname, portname2; + pool<int> skip_bit_indices; + + if (skip_wires.count(wire->name)) { + if (!flag_bits) + continue; + skip_bit_indices = skip_wires.at(wire->name); + } if (wire->port_input && !wire->port_output) { if (inpad_celltype.empty()) { @@ -170,12 +312,21 @@ struct IopadmapPass : public Pass { if (!portname2.empty()) { new_wire = module->addWire(NEW_ID, wire); module->swap_names(new_wire, wire); + wire->attributes.clear(); } if (flag_bits) { for (int i = 0; i < wire->width; i++) { + if (skip_bit_indices.count(i)) { + if (wire->port_output) + module->connect(SigSpec(new_wire, i), SigSpec(wire, i)); + else + module->connect(SigSpec(wire, i), SigSpec(new_wire, i)); + continue; + } + RTLIL::Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(celltype)); cell->setPort(RTLIL::escape_id(portname), RTLIL::SigSpec(wire, i)); if (!portname2.empty()) @@ -209,5 +360,5 @@ struct IopadmapPass : public Pass { } } } IopadmapPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/techmap/libparse.cc b/passes/techmap/libparse.cc index 50d31ab5a..d5254c029 100644 --- a/passes/techmap/libparse.cc +++ b/passes/techmap/libparse.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -43,8 +43,6 @@ LibertyAst::~LibertyAst() LibertyAst *LibertyAst::find(std::string name) { - if (this == NULL) - return NULL; for (auto child : children) if (child->id == name) return child; @@ -107,14 +105,14 @@ int LibertyParser::lexer(std::string &str) } if (c == '"') { - str = c; + str = ""; while (1) { c = f.get(); if (c == '\n') line++; - str += c; if (c == '"') break; + str += c; } // fprintf(stderr, "LEX: string >>%s<<\n", str.c_str()); return 'v'; @@ -175,7 +173,7 @@ LibertyAst *LibertyParser::parse() if (tok == '}' || tok < 0) return NULL; - + if (tok != 'v') error(); diff --git a/passes/techmap/libparse.h b/passes/techmap/libparse.h index e947bd8cd..cf6325570 100644 --- a/passes/techmap/libparse.h +++ b/passes/techmap/libparse.h @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR diff --git a/passes/techmap/lut2mux.cc b/passes/techmap/lut2mux.cc new file mode 100644 index 000000000..2bb0bd8b4 --- /dev/null +++ b/passes/techmap/lut2mux.cc @@ -0,0 +1,93 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include "kernel/sigtools.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +int lut2mux(Cell *cell) +{ + SigSpec sig_a = cell->getPort("\\A"); + SigSpec sig_y = cell->getPort("\\Y"); + Const lut = cell->getParam("\\LUT"); + int count = 1; + + if (GetSize(sig_a) == 1) + { + cell->module->addMuxGate(NEW_ID, lut[0], lut[1], sig_a, sig_y); + } + else + { + SigSpec sig_a_hi = sig_a[GetSize(sig_a)-1]; + SigSpec sig_a_lo = sig_a.extract(0, GetSize(sig_a)-1); + SigSpec sig_y1 = cell->module->addWire(NEW_ID); + SigSpec sig_y2 = cell->module->addWire(NEW_ID); + + Const lut1 = lut.extract(0, GetSize(lut)/2); + Const lut2 = lut.extract(GetSize(lut)/2, GetSize(lut)/2); + + count += lut2mux(cell->module->addLut(NEW_ID, sig_a_lo, sig_y1, lut1)); + count += lut2mux(cell->module->addLut(NEW_ID, sig_a_lo, sig_y2, lut2)); + + cell->module->addMuxGate(NEW_ID, sig_y1, sig_y2, sig_a_hi, sig_y); + } + + cell->module->remove(cell); + return count; +} + +struct Lut2muxPass : public Pass { + Lut2muxPass() : Pass("lut2mux", "convert $lut to $_MUX_") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" lut2mux [options] [selection]\n"); + log("\n"); + log("This pass converts $lut cells to $_MUX_ gates.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + log_header(design, "Executing LUT2MUX pass (convert $lut to $_MUX_).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + // if (args[argidx] == "-v") { + // continue; + // } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) + for (auto cell : module->selected_cells()) { + if (cell->type == "$lut") { + IdString cell_name = cell->name; + int count = lut2mux(cell); + log("Converted %s.%s to %d MUX cells.\n", log_id(module), log_id(cell_name), count); + } + } + } +} Lut2muxPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/maccmap.cc b/passes/techmap/maccmap.cc index ffbd6289d..32569d076 100644 --- a/passes/techmap/maccmap.cc +++ b/passes/techmap/maccmap.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -134,7 +134,7 @@ struct MaccmapWorker } return retval; #else - return std::max(n - 1, 0); + return max(n - 1, 0); #endif } @@ -371,15 +371,15 @@ struct MaccmapPass : public Pass { log("\n"); log(" maccmap [-unmap] [selection]\n"); log("\n"); - log("This pass maps $macc cells to yosys gate primitives. When the -unmap option is\n"); - log("used then the $macc cell is mapped to $and, $sub, etc. cells instead.\n"); + log("This pass maps $macc cells to yosys $fa and $alu cells. When the -unmap option\n"); + log("is used then the $macc cell is mapped to $add, $sub, etc. cells instead.\n"); log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { bool unmap_mode = false; - log_header("Executing MACCMAP pass (map $macc cells).\n"); + log_header(design, "Executing MACCMAP pass (map $macc cells).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { @@ -400,5 +400,5 @@ struct MaccmapPass : public Pass { } } } MaccmapPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/techmap/muxcover.cc b/passes/techmap/muxcover.cc new file mode 100644 index 000000000..1dc649587 --- /dev/null +++ b/passes/techmap/muxcover.cc @@ -0,0 +1,632 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include "kernel/sigtools.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +#define COST_MUX2 100 +#define COST_MUX4 220 +#define COST_MUX8 460 +#define COST_MUX16 940 + +struct MuxcoverWorker +{ + Module *module; + SigMap sigmap; + + struct newmux_t + { + int cost; + vector<SigBit> inputs, selects; + newmux_t() : cost(0) {} + }; + + struct tree_t + { + SigBit root; + dict<SigBit, Cell*> muxes; + dict<SigBit, newmux_t> newmuxes; + }; + + vector<tree_t> tree_list; + + dict<tuple<SigBit, SigBit, SigBit>, tuple<SigBit, pool<SigBit>, bool>> decode_mux_cache; + dict<SigBit, tuple<SigBit, SigBit, SigBit>> decode_mux_reverse_cache; + int decode_mux_counter; + + bool use_mux4; + bool use_mux8; + bool use_mux16; + bool nodecode; + + MuxcoverWorker(Module *module) : module(module), sigmap(module) + { + use_mux4 = false; + use_mux8 = false; + use_mux16 = false; + nodecode = false; + decode_mux_counter = 0; + } + + void treeify() + { + pool<SigBit> roots; + pool<SigBit> used_once; + dict<SigBit, Cell*> sig_to_mux; + + for (auto wire : module->wires()) { + if (!wire->port_output) + continue; + for (auto bit : sigmap(wire)) + roots.insert(bit); + } + + for (auto cell : module->cells()) { + for (auto conn : cell->connections()) { + if (!cell->input(conn.first)) + continue; + for (auto bit : sigmap(conn.second)) { + if (used_once.count(bit) || cell->type != "$_MUX_" || conn.first == "\\S") + roots.insert(bit); + used_once.insert(bit); + } + } + if (cell->type == "$_MUX_") + sig_to_mux[sigmap(cell->getPort("\\Y"))] = cell; + } + + log(" Treeifying %d MUXes:\n", GetSize(sig_to_mux)); + + roots.sort(); + for (auto rootsig : roots) + { + tree_t tree; + tree.root = rootsig; + + pool<SigBit> wavefront; + wavefront.insert(rootsig); + + while (!wavefront.empty()) { + SigBit bit = wavefront.pop(); + if (sig_to_mux.count(bit) && (bit == rootsig || !roots.count(bit))) { + Cell *c = sig_to_mux.at(bit); + tree.muxes[bit] = c; + wavefront.insert(sigmap(c->getPort("\\A"))); + wavefront.insert(sigmap(c->getPort("\\B"))); + } + } + + if (!tree.muxes.empty()) { + log(" Found tree with %d MUXes at root %s.\n", GetSize(tree.muxes), log_signal(tree.root)); + tree_list.push_back(tree); + } + } + + log(" Finished treeification: Found %d trees.\n", GetSize(tree_list)); + } + + bool follow_muxtree(SigBit &ret_bit, tree_t &tree, SigBit bit, const char *path) + { + if (*path) { + if (tree.muxes.count(bit) == 0) + return false; + char port_name[3] = {'\\', *path, 0}; + return follow_muxtree(ret_bit, tree, sigmap(tree.muxes.at(bit)->getPort(port_name)), path+1); + } else { + ret_bit = bit; + return true; + } + } + + int prepare_decode_mux(SigBit &A, SigBit B, SigBit sel, SigBit bit) + { + if (A == B) + return 0; + + tuple<SigBit, SigBit, SigBit> key(A, B, sel); + if (decode_mux_cache.count(key) == 0) { + auto &entry = decode_mux_cache[key]; + std::get<0>(entry) = module->addWire(NEW_ID); + std::get<2>(entry) = false; + decode_mux_reverse_cache[std::get<0>(entry)] = key; + } + + auto &entry = decode_mux_cache[key]; + A = std::get<0>(entry); + std::get<1>(entry).insert(bit); + + if (std::get<2>(entry)) + return 0; + + return COST_MUX2 / GetSize(std::get<1>(entry)); + } + + void implement_decode_mux(SigBit ctrl_bit) + { + if (decode_mux_reverse_cache.count(ctrl_bit) == 0) + return; + + auto &key = decode_mux_reverse_cache.at(ctrl_bit); + auto &entry = decode_mux_cache[key]; + + if (std::get<2>(entry)) + return; + + implement_decode_mux(std::get<0>(key)); + implement_decode_mux(std::get<1>(key)); + + module->addMuxGate(NEW_ID, std::get<0>(key), std::get<1>(key), std::get<2>(key), ctrl_bit); + std::get<2>(entry) = true; + decode_mux_counter++; + } + + int find_best_cover(tree_t &tree, SigBit bit) + { + if (tree.newmuxes.count(bit)) { + return tree.newmuxes.at(bit).cost; + } + + SigBit A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P; + SigBit S1, S2, S3, S4, S5, S6, S7, S8; + SigBit T1, T2, T3, T4; + SigBit U1, U2; + SigBit V1; + + newmux_t best_mux; + bool ok = true; + + // 2-Input MUX + + ok = ok && follow_muxtree(A, tree, bit, "A"); + ok = ok && follow_muxtree(B, tree, bit, "B"); + + ok = ok && follow_muxtree(S1, tree, bit, "S"); + + if (ok) + { + newmux_t mux; + + mux.inputs.push_back(A); + mux.inputs.push_back(B); + mux.selects.push_back(S1); + + mux.cost += COST_MUX2; + mux.cost += find_best_cover(tree, A); + mux.cost += find_best_cover(tree, B); + + best_mux = mux; + } + + // 4-Input MUX + + if (use_mux4) + { + ok = ok && follow_muxtree(A, tree, bit, "AA"); + ok = ok && follow_muxtree(B, tree, bit, "AB"); + ok = ok && follow_muxtree(C, tree, bit, "BA"); + ok = ok && follow_muxtree(D, tree, bit, "BB"); + + ok = ok && follow_muxtree(S1, tree, bit, "AS"); + ok = ok && follow_muxtree(S2, tree, bit, "BS"); + + if (nodecode) + ok = ok && S1 == S2; + + ok = ok && follow_muxtree(T1, tree, bit, "S"); + + if (ok) + { + newmux_t mux; + + mux.inputs.push_back(A); + mux.inputs.push_back(B); + mux.inputs.push_back(C); + mux.inputs.push_back(D); + + mux.cost += prepare_decode_mux(S1, S2, T1, bit); + + mux.selects.push_back(S1); + mux.selects.push_back(T1); + + mux.cost += COST_MUX4; + mux.cost += find_best_cover(tree, A); + mux.cost += find_best_cover(tree, B); + mux.cost += find_best_cover(tree, C); + mux.cost += find_best_cover(tree, D); + + if (best_mux.cost > mux.cost) + best_mux = mux; + } + } + + // 8-Input MUX + + if (use_mux8) + { + ok = ok && follow_muxtree(A, tree, bit, "AAA"); + ok = ok && follow_muxtree(B, tree, bit, "AAB"); + ok = ok && follow_muxtree(C, tree, bit, "ABA"); + ok = ok && follow_muxtree(D, tree, bit, "ABB"); + ok = ok && follow_muxtree(E, tree, bit, "BAA"); + ok = ok && follow_muxtree(F, tree, bit, "BAB"); + ok = ok && follow_muxtree(G, tree, bit, "BBA"); + ok = ok && follow_muxtree(H, tree, bit, "BBB"); + + ok = ok && follow_muxtree(S1, tree, bit, "AAS"); + ok = ok && follow_muxtree(S2, tree, bit, "ABS"); + ok = ok && follow_muxtree(S3, tree, bit, "BAS"); + ok = ok && follow_muxtree(S4, tree, bit, "BBS"); + + if (nodecode) + ok = ok && S1 == S2 && S2 == S3 && S3 == S4; + + ok = ok && follow_muxtree(T1, tree, bit, "AS"); + ok = ok && follow_muxtree(T2, tree, bit, "BS"); + + if (nodecode) + ok = ok && T1 == T2; + + ok = ok && follow_muxtree(U1, tree, bit, "S"); + + if (ok) + { + newmux_t mux; + + mux.inputs.push_back(A); + mux.inputs.push_back(B); + mux.inputs.push_back(C); + mux.inputs.push_back(D); + mux.inputs.push_back(E); + mux.inputs.push_back(F); + mux.inputs.push_back(G); + mux.inputs.push_back(H); + + mux.cost += prepare_decode_mux(S1, S2, T1, bit); + mux.cost += prepare_decode_mux(S3, S4, T2, bit); + mux.cost += prepare_decode_mux(S1, S3, U1, bit); + + mux.cost += prepare_decode_mux(T1, T2, U1, bit); + + mux.selects.push_back(S1); + mux.selects.push_back(T1); + mux.selects.push_back(U1); + + mux.cost += COST_MUX8; + mux.cost += find_best_cover(tree, A); + mux.cost += find_best_cover(tree, B); + mux.cost += find_best_cover(tree, C); + mux.cost += find_best_cover(tree, D); + mux.cost += find_best_cover(tree, E); + mux.cost += find_best_cover(tree, F); + mux.cost += find_best_cover(tree, G); + mux.cost += find_best_cover(tree, H); + + if (best_mux.cost > mux.cost) + best_mux = mux; + } + } + + // 16-Input MUX + + if (use_mux16) + { + ok = ok && follow_muxtree(A, tree, bit, "AAAA"); + ok = ok && follow_muxtree(B, tree, bit, "AAAB"); + ok = ok && follow_muxtree(C, tree, bit, "AABA"); + ok = ok && follow_muxtree(D, tree, bit, "AABB"); + ok = ok && follow_muxtree(E, tree, bit, "ABAA"); + ok = ok && follow_muxtree(F, tree, bit, "ABAB"); + ok = ok && follow_muxtree(G, tree, bit, "ABBA"); + ok = ok && follow_muxtree(H, tree, bit, "ABBB"); + ok = ok && follow_muxtree(I, tree, bit, "BAAA"); + ok = ok && follow_muxtree(J, tree, bit, "BAAB"); + ok = ok && follow_muxtree(K, tree, bit, "BABA"); + ok = ok && follow_muxtree(L, tree, bit, "BABB"); + ok = ok && follow_muxtree(M, tree, bit, "BBAA"); + ok = ok && follow_muxtree(N, tree, bit, "BBAB"); + ok = ok && follow_muxtree(O, tree, bit, "BBBA"); + ok = ok && follow_muxtree(P, tree, bit, "BBBB"); + + ok = ok && follow_muxtree(S1, tree, bit, "AAAS"); + ok = ok && follow_muxtree(S2, tree, bit, "AABS"); + ok = ok && follow_muxtree(S3, tree, bit, "ABAS"); + ok = ok && follow_muxtree(S4, tree, bit, "ABBS"); + ok = ok && follow_muxtree(S5, tree, bit, "BAAS"); + ok = ok && follow_muxtree(S6, tree, bit, "BABS"); + ok = ok && follow_muxtree(S7, tree, bit, "BBAS"); + ok = ok && follow_muxtree(S8, tree, bit, "BBBS"); + + if (nodecode) + ok = ok && S1 == S2 && S2 == S3 && S3 == S4 && S4 == S5 && S5 == S6 && S6 == S7 && S7 == S8; + + ok = ok && follow_muxtree(T1, tree, bit, "AAS"); + ok = ok && follow_muxtree(T2, tree, bit, "ABS"); + ok = ok && follow_muxtree(T3, tree, bit, "BAS"); + ok = ok && follow_muxtree(T4, tree, bit, "BBS"); + + if (nodecode) + ok = ok && T1 == T2 && T2 == T3 && T3 == T4; + + ok = ok && follow_muxtree(U1, tree, bit, "AS"); + ok = ok && follow_muxtree(U2, tree, bit, "BS"); + + if (nodecode) + ok = ok && U1 == U2; + + ok = ok && follow_muxtree(V1, tree, bit, "S"); + + if (ok) + { + newmux_t mux; + + mux.inputs.push_back(A); + mux.inputs.push_back(B); + mux.inputs.push_back(C); + mux.inputs.push_back(D); + mux.inputs.push_back(E); + mux.inputs.push_back(F); + mux.inputs.push_back(G); + mux.inputs.push_back(H); + mux.inputs.push_back(I); + mux.inputs.push_back(J); + mux.inputs.push_back(K); + mux.inputs.push_back(L); + mux.inputs.push_back(M); + mux.inputs.push_back(N); + mux.inputs.push_back(O); + mux.inputs.push_back(P); + + mux.cost += prepare_decode_mux(S1, S2, T1, bit); + mux.cost += prepare_decode_mux(S3, S4, T2, bit); + mux.cost += prepare_decode_mux(S5, S6, T3, bit); + mux.cost += prepare_decode_mux(S7, S8, T4, bit); + mux.cost += prepare_decode_mux(S1, S3, U1, bit); + mux.cost += prepare_decode_mux(S5, S7, U2, bit); + mux.cost += prepare_decode_mux(S1, S5, V1, bit); + + mux.cost += prepare_decode_mux(T1, T2, U1, bit); + mux.cost += prepare_decode_mux(T3, T4, U2, bit); + mux.cost += prepare_decode_mux(T1, T3, V1, bit); + + mux.cost += prepare_decode_mux(U1, U2, V1, bit); + + mux.selects.push_back(S1); + mux.selects.push_back(T1); + mux.selects.push_back(U1); + mux.selects.push_back(V1); + + mux.cost += COST_MUX16; + mux.cost += find_best_cover(tree, A); + mux.cost += find_best_cover(tree, B); + mux.cost += find_best_cover(tree, C); + mux.cost += find_best_cover(tree, D); + mux.cost += find_best_cover(tree, E); + mux.cost += find_best_cover(tree, F); + mux.cost += find_best_cover(tree, G); + mux.cost += find_best_cover(tree, H); + mux.cost += find_best_cover(tree, I); + mux.cost += find_best_cover(tree, J); + mux.cost += find_best_cover(tree, K); + mux.cost += find_best_cover(tree, L); + mux.cost += find_best_cover(tree, M); + mux.cost += find_best_cover(tree, N); + mux.cost += find_best_cover(tree, O); + mux.cost += find_best_cover(tree, P); + + if (best_mux.cost > mux.cost) + best_mux = mux; + } + } + + tree.newmuxes[bit] = best_mux; + return best_mux.cost; + } + + void implement_best_cover(tree_t &tree, SigBit bit, int count_muxes_by_type[4]) + { + newmux_t mux = tree.newmuxes.at(bit); + + for (auto inbit : mux.inputs) + implement_best_cover(tree, inbit, count_muxes_by_type); + + for (auto selbit : mux.selects) + implement_decode_mux(selbit); + + if (GetSize(mux.inputs) == 0) + return; + + if (GetSize(mux.inputs) == 2) { + count_muxes_by_type[0]++; + Cell *cell = module->addCell(NEW_ID, "$_MUX_"); + cell->setPort("\\A", mux.inputs[0]); + cell->setPort("\\B", mux.inputs[1]); + cell->setPort("\\S", mux.selects[0]); + cell->setPort("\\Y", bit); + return; + } + + if (GetSize(mux.inputs) == 4) { + count_muxes_by_type[1]++; + Cell *cell = module->addCell(NEW_ID, "$_MUX4_"); + cell->setPort("\\A", mux.inputs[0]); + cell->setPort("\\B", mux.inputs[1]); + cell->setPort("\\C", mux.inputs[2]); + cell->setPort("\\D", mux.inputs[3]); + cell->setPort("\\S", mux.selects[0]); + cell->setPort("\\T", mux.selects[1]); + cell->setPort("\\Y", bit); + return; + } + + if (GetSize(mux.inputs) == 8) { + count_muxes_by_type[2]++; + Cell *cell = module->addCell(NEW_ID, "$_MUX8_"); + cell->setPort("\\A", mux.inputs[0]); + cell->setPort("\\B", mux.inputs[1]); + cell->setPort("\\C", mux.inputs[2]); + cell->setPort("\\D", mux.inputs[3]); + cell->setPort("\\E", mux.inputs[4]); + cell->setPort("\\F", mux.inputs[5]); + cell->setPort("\\G", mux.inputs[6]); + cell->setPort("\\H", mux.inputs[7]); + cell->setPort("\\S", mux.selects[0]); + cell->setPort("\\T", mux.selects[1]); + cell->setPort("\\U", mux.selects[2]); + cell->setPort("\\Y", bit); + return; + } + + if (GetSize(mux.inputs) == 16) { + count_muxes_by_type[3]++; + Cell *cell = module->addCell(NEW_ID, "$_MUX16_"); + cell->setPort("\\A", mux.inputs[0]); + cell->setPort("\\B", mux.inputs[1]); + cell->setPort("\\C", mux.inputs[2]); + cell->setPort("\\D", mux.inputs[3]); + cell->setPort("\\E", mux.inputs[4]); + cell->setPort("\\F", mux.inputs[5]); + cell->setPort("\\G", mux.inputs[6]); + cell->setPort("\\H", mux.inputs[7]); + cell->setPort("\\I", mux.inputs[8]); + cell->setPort("\\J", mux.inputs[9]); + cell->setPort("\\K", mux.inputs[10]); + cell->setPort("\\L", mux.inputs[11]); + cell->setPort("\\M", mux.inputs[12]); + cell->setPort("\\N", mux.inputs[13]); + cell->setPort("\\O", mux.inputs[14]); + cell->setPort("\\P", mux.inputs[15]); + cell->setPort("\\S", mux.selects[0]); + cell->setPort("\\T", mux.selects[1]); + cell->setPort("\\U", mux.selects[2]); + cell->setPort("\\V", mux.selects[3]); + cell->setPort("\\Y", bit); + return; + } + + log_abort(); + } + + void treecover(tree_t &tree) + { + int count_muxes_by_type[4] = {0, 0, 0, 0}; + find_best_cover(tree, tree.root); + implement_best_cover(tree, tree.root, count_muxes_by_type); + log(" Replaced tree at %s: %d MUX2, %d MUX4, %d MUX8, %d MUX16\n", log_signal(tree.root), + count_muxes_by_type[0], count_muxes_by_type[1], count_muxes_by_type[2], count_muxes_by_type[3]); + for (auto &it : tree.muxes) + module->remove(it.second); + } + + void run() + { + log("Covering MUX trees in module %s..\n", log_id(module)); + + treeify(); + + log(" Covering trees:\n"); + + // pre-fill cache of decoder muxes + if (!nodecode) + for (auto &tree : tree_list) { + find_best_cover(tree, tree.root); + tree.newmuxes.clear(); + } + + for (auto &tree : tree_list) + treecover(tree); + + if (!nodecode) + log(" Added a total of %d decoder MUXes.\n", decode_mux_counter); + } +}; + +struct MuxcoverPass : public Pass { + MuxcoverPass() : Pass("muxcover", "cover trees of MUX cells with wider MUXes") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" muxcover [options] [selection]\n"); + log("\n"); + log("Cover trees of $_MUX_ cells with $_MUX{4,8,16}_ cells\n"); + log("\n"); + log(" -mux4, -mux8, -mux16\n"); + log(" Use the specified types of MUXes. If none of those options are used,\n"); + log(" the effect is the same as if all of them where used.\n"); + log("\n"); + log(" -nodecode\n"); + log(" Do not insert decoder logic. This reduces the number of possible\n"); + log(" substitutions, but guarantees that the resulting circuit is not\n"); + log(" less efficient than the original circuit.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + log_header(design, "Executing MUXCOVER pass (mapping to wider MUXes).\n"); + + bool use_mux4 = false; + bool use_mux8 = false; + bool use_mux16 = false; + bool nodecode = false; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-mux4") { + use_mux4 = true; + continue; + } + if (args[argidx] == "-mux8") { + use_mux8 = true; + continue; + } + if (args[argidx] == "-mux16") { + use_mux16 = true; + continue; + } + if (args[argidx] == "-nodecode") { + nodecode = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + if (!use_mux4 && !use_mux8 && !use_mux16) { + use_mux4 = true; + use_mux8 = true; + use_mux16 = true; + } + + for (auto module : design->selected_modules()) + { + MuxcoverWorker worker(module); + worker.use_mux4 = use_mux4; + worker.use_mux8 = use_mux8; + worker.use_mux16 = use_mux16; + worker.nodecode = nodecode; + worker.run(); + } + } +} MuxcoverPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/nlutmap.cc b/passes/techmap/nlutmap.cc new file mode 100644 index 000000000..6fcdf82bd --- /dev/null +++ b/passes/techmap/nlutmap.cc @@ -0,0 +1,187 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include "kernel/sigtools.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct NlutmapConfig +{ + vector<int> luts; + bool assert_mode = false; +}; + +struct NlutmapWorker +{ + const NlutmapConfig &config; + pool<Cell*> mapped_cells; + Module *module; + + NlutmapWorker(const NlutmapConfig &config, Module *module) : + config(config), module(module) + { + } + + RTLIL::Selection get_selection() + { + RTLIL::Selection sel(false); + for (auto cell : module->cells()) + if (!mapped_cells.count(cell)) + sel.select(module, cell); + return sel; + } + + void run_abc(int lut_size) + { + Pass::call_on_selection(module->design, get_selection(), "lut2mux"); + + if (lut_size > 0) + Pass::call_on_selection(module->design, get_selection(), stringf("abc -lut 1:%d", lut_size)); + else + Pass::call_on_selection(module->design, get_selection(), "abc"); + + Pass::call_on_module(module->design, module, "opt_clean"); + } + + void run() + { + vector<int> available_luts = config.luts; + + while (GetSize(available_luts) > 1) + { + int n_luts = available_luts.back(); + int lut_size = GetSize(available_luts); + available_luts.pop_back(); + + if (n_luts == 0) + continue; + + run_abc(lut_size); + + SigMap sigmap(module); + dict<Cell*, int> candidate_ratings; + dict<SigBit, int> bit_lut_count; + + for (auto cell : module->cells()) + { + if (cell->type != "$lut" || mapped_cells.count(cell)) + continue; + + if (GetSize(cell->getPort("\\A")) == lut_size || lut_size == 2) + candidate_ratings[cell] = 0; + + for (auto &conn : cell->connections()) + for (auto bit : sigmap(conn.second)) + bit_lut_count[bit]++; + } + + for (auto &cand : candidate_ratings) + { + for (auto &conn : cand.first->connections()) + for (auto bit : sigmap(conn.second)) + cand.second -= bit_lut_count[bit]; + } + + vector<pair<int, IdString>> rated_candidates; + + for (auto &cand : candidate_ratings) + rated_candidates.push_back(pair<int, IdString>(cand.second, cand.first->name)); + + std::sort(rated_candidates.begin(), rated_candidates.end()); + + while (n_luts > 0 && !rated_candidates.empty()) { + mapped_cells.insert(module->cell(rated_candidates.back().second)); + rated_candidates.pop_back(); + n_luts--; + } + + if (!available_luts.empty()) + available_luts.back() += n_luts; + } + + if (config.assert_mode) { + for (auto cell : module->cells()) + if (cell->type == "$lut" && !mapped_cells.count(cell)) + log_error("Insufficient number of LUTs to map all logic cells!\n"); + } + + run_abc(0); + } +}; + +struct NlutmapPass : public Pass { + NlutmapPass() : Pass("nlutmap", "map to LUTs of different sizes") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" nlutmap [options] [selection]\n"); + log("\n"); + log("This pass uses successive calls to 'abc' to map to an architecture. That\n"); + log("provides a small number of differently sized LUTs.\n"); + log("\n"); + log(" -luts N_1,N_2,N_3,...\n"); + log(" The number of LUTs with 1, 2, 3, ... inputs that are\n"); + log(" available in the target architecture.\n"); + log("\n"); + log(" -assert\n"); + log(" Create an error if not all logic can be mapped\n"); + log("\n"); + log("Excess logic that does not fit into the specified LUTs is mapped back\n"); + log("to generic logic gates ($_AND_, etc.).\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + NlutmapConfig config; + + log_header(design, "Executing NLUTMAP pass (mapping to constant drivers).\n"); + log_push(); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-luts" && argidx+1 < args.size()) { + vector<string> tokens = split_tokens(args[++argidx], ","); + config.luts.clear(); + for (auto &token : tokens) + config.luts.push_back(atoi(token.c_str())); + continue; + } + if (args[argidx] == "-assert") { + config.assert_mode = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_whole_modules_warn()) + { + NlutmapWorker worker(config, module); + worker.run(); + } + + log_pop(); + } +} NlutmapPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/pmuxtree.cc b/passes/techmap/pmuxtree.cc new file mode 100644 index 000000000..c626dbcc5 --- /dev/null +++ b/passes/techmap/pmuxtree.cc @@ -0,0 +1,112 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include "kernel/sigtools.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +static SigSpec or_generator(Module *module, const SigSpec &sig) +{ + switch (GetSize(sig)) + { + case 0: + return State::S0; + case 1: + return sig; + case 2: + return module->Or(NEW_ID, sig[0], sig[1]); + default: + return module->ReduceOr(NEW_ID, sig); + } +} + +static SigSpec recursive_mux_generator(Module *module, const SigSpec &sig_data, const SigSpec &sig_sel, SigSpec &sig_or) +{ + if (GetSize(sig_sel) == 1) { + sig_or.append(sig_sel); + return sig_data; + } + + int left_size = GetSize(sig_sel) / 2; + int right_size = GetSize(sig_sel) - left_size; + int stride = GetSize(sig_data) / GetSize(sig_sel); + + SigSpec left_data = sig_data.extract(0, stride*left_size); + SigSpec right_data = sig_data.extract(stride*left_size, stride*right_size); + + SigSpec left_sel = sig_sel.extract(0, left_size); + SigSpec right_sel = sig_sel.extract(left_size, right_size); + + SigSpec left_or, left_result, right_result; + + left_result = recursive_mux_generator(module, left_data, left_sel, left_or); + right_result = recursive_mux_generator(module, right_data, right_sel, sig_or); + left_or = or_generator(module, left_or); + sig_or.append(left_or); + + return module->Mux(NEW_ID, right_result, left_result, left_or); +} + +struct PmuxtreePass : public Pass { + PmuxtreePass() : Pass("pmuxtree", "transform $pmux cells to trees of $mux cells") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" pmuxtree [options] [selection]\n"); + log("\n"); + log("This pass transforms $pmux cells to a trees of $mux cells.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + log_header(design, "Executing PMUXTREE 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 != "$pmux") + continue; + + SigSpec sig_data = cell->getPort("\\B"); + SigSpec sig_sel = cell->getPort("\\S"); + + if (!cell->getPort("\\A").is_fully_undef()) { + sig_data.append(cell->getPort("\\A")); + SigSpec sig_sel_or = module->ReduceOr(NEW_ID, sig_sel); + sig_sel.append(module->Not(NEW_ID, sig_sel_or)); + } + + SigSpec result, result_or; + result = recursive_mux_generator(module, sig_data, sig_sel, result_or); + module->connect(cell->getPort("\\Y"), result); + module->remove(cell); + } + } +} PmuxtreePass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/shregmap.cc b/passes/techmap/shregmap.cc new file mode 100644 index 000000000..6936b499e --- /dev/null +++ b/passes/techmap/shregmap.cc @@ -0,0 +1,584 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include "kernel/sigtools.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct ShregmapTech +{ + virtual ~ShregmapTech() { } + virtual bool analyze(vector<int> &taps) = 0; + virtual bool fixup(Cell *cell, dict<int, SigBit> &taps) = 0; +}; + +struct ShregmapOptions +{ + int minlen, maxlen; + int keep_before, keep_after; + bool zinit, init, params, ffe; + dict<IdString, pair<IdString, IdString>> ffcells; + ShregmapTech *tech; + + ShregmapOptions() + { + minlen = 2; + maxlen = 0; + keep_before = 0; + keep_after = 0; + zinit = false; + init = false; + params = false; + ffe = false; + tech = nullptr; + } +}; + +struct ShregmapTechGreenpak4 : ShregmapTech +{ + bool analyze(vector<int> &taps) + { + if (GetSize(taps) > 2 && taps[0] == 0 && taps[2] < 17) { + taps.clear(); + return true; + } + + if (GetSize(taps) > 2) + return false; + + if (taps.back() > 16) return false; + + return true; + } + + bool fixup(Cell *cell, dict<int, SigBit> &taps) + { + auto D = cell->getPort("\\D"); + auto C = cell->getPort("\\C"); + + auto newcell = cell->module->addCell(NEW_ID, "\\GP_SHREG"); + newcell->setPort("\\nRST", State::S1); + newcell->setPort("\\CLK", C); + newcell->setPort("\\IN", D); + + int i = 0; + for (auto tap : taps) { + newcell->setPort(i ? "\\OUTB" : "\\OUTA", tap.second); + newcell->setParam(i ? "\\OUTB_TAP" : "\\OUTA_TAP", tap.first + 1); + i++; + } + + cell->setParam("\\OUTA_INVERT", 0); + return false; + } +}; + +struct ShregmapWorker +{ + Module *module; + SigMap sigmap; + + const ShregmapOptions &opts; + int dff_count, shreg_count; + + pool<Cell*> remove_cells; + pool<SigBit> remove_init; + + dict<SigBit, bool> sigbit_init; + dict<SigBit, Cell*> sigbit_chain_next; + dict<SigBit, Cell*> sigbit_chain_prev; + pool<SigBit> sigbit_with_non_chain_users; + pool<Cell*> chain_start_cells; + + void make_sigbit_chain_next_prev() + { + for (auto wire : module->wires()) + { + if (wire->port_output || wire->get_bool_attribute("\\keep")) { + for (auto bit : sigmap(wire)) + sigbit_with_non_chain_users.insert(bit); + } + + if (wire->attributes.count("\\init")) { + SigSpec initsig = sigmap(wire); + Const initval = wire->attributes.at("\\init"); + for (int i = 0; i < GetSize(initsig) && i < GetSize(initval); i++) + if (initval[i] == State::S0 && !opts.zinit) + sigbit_init[initsig[i]] = false; + else if (initval[i] == State::S1) + sigbit_init[initsig[i]] = true; + } + } + + for (auto cell : module->cells()) + { + if (opts.ffcells.count(cell->type) && !cell->get_bool_attribute("\\keep")) + { + IdString d_port = opts.ffcells.at(cell->type).first; + IdString q_port = opts.ffcells.at(cell->type).second; + + SigBit d_bit = sigmap(cell->getPort(d_port).as_bit()); + SigBit q_bit = sigmap(cell->getPort(q_port).as_bit()); + + if (opts.init || sigbit_init.count(q_bit) == 0) + { + if (sigbit_chain_next.count(d_bit)) { + sigbit_with_non_chain_users.insert(d_bit); + } else + sigbit_chain_next[d_bit] = cell; + + sigbit_chain_prev[q_bit] = cell; + continue; + } + } + + for (auto conn : cell->connections()) + if (cell->input(conn.first)) + for (auto bit : sigmap(conn.second)) + sigbit_with_non_chain_users.insert(bit); + } + } + + void find_chain_start_cells() + { + for (auto it : sigbit_chain_next) + { + if (opts.tech == nullptr && sigbit_with_non_chain_users.count(it.first)) + goto start_cell; + + if (sigbit_chain_prev.count(it.first) != 0) + { + Cell *c1 = sigbit_chain_prev.at(it.first); + Cell *c2 = it.second; + + if (c1->type != c2->type) + goto start_cell; + + if (c1->parameters != c2->parameters) + goto start_cell; + + IdString d_port = opts.ffcells.at(c1->type).first; + IdString q_port = opts.ffcells.at(c1->type).second; + + auto c1_conn = c1->connections(); + auto c2_conn = c1->connections(); + + c1_conn.erase(d_port); + c1_conn.erase(q_port); + + c2_conn.erase(d_port); + c2_conn.erase(q_port); + + if (c1_conn != c2_conn) + goto start_cell; + + continue; + } + + start_cell: + chain_start_cells.insert(it.second); + } + } + + vector<Cell*> create_chain(Cell *start_cell) + { + vector<Cell*> chain; + + Cell *c = start_cell; + while (c != nullptr) + { + chain.push_back(c); + + IdString q_port = opts.ffcells.at(c->type).second; + SigBit q_bit = sigmap(c->getPort(q_port).as_bit()); + + if (sigbit_chain_next.count(q_bit) == 0) + break; + + c = sigbit_chain_next.at(q_bit); + if (chain_start_cells.count(c) != 0) + break; + } + + return chain; + } + + void process_chain(vector<Cell*> &chain) + { + if (GetSize(chain) < opts.keep_before + opts.minlen + opts.keep_after) + return; + + int cursor = opts.keep_before; + while (cursor < GetSize(chain) - opts.keep_after) + { + int depth = GetSize(chain) - opts.keep_after - cursor; + + if (opts.maxlen > 0) + depth = std::min(opts.maxlen, depth); + + Cell *first_cell = chain[cursor]; + IdString q_port = opts.ffcells.at(first_cell->type).second; + dict<int, SigBit> taps_dict; + + if (opts.tech) + { + vector<SigBit> qbits; + vector<int> taps; + + for (int i = 0; i < depth; i++) + { + Cell *cell = chain[cursor+i]; + auto qbit = sigmap(cell->getPort(q_port)); + qbits.push_back(qbit); + + if (sigbit_with_non_chain_users.count(qbit)) + taps.push_back(i); + } + + while (depth > 0) + { + if (taps.empty() || taps.back() < depth-1) + taps.push_back(depth-1); + + if (opts.tech->analyze(taps)) + break; + + taps.pop_back(); + depth--; + } + + depth = 0; + for (auto tap : taps) { + taps_dict[tap] = qbits.at(tap); + log_assert(depth < tap+1); + depth = tap+1; + } + } + + if (depth < 2) { + cursor++; + continue; + } + + Cell *last_cell = chain[cursor+depth-1]; + + log("Converting %s.%s ... %s.%s to a shift register with depth %d.\n", + log_id(module), log_id(first_cell), log_id(module), log_id(last_cell), depth); + + dff_count += depth; + shreg_count += 1; + + string shreg_cell_type_str = "$__SHREG"; + if (opts.params) { + shreg_cell_type_str += "_"; + } else { + if (first_cell->type[1] != '_') + shreg_cell_type_str += "_"; + shreg_cell_type_str += first_cell->type.substr(1); + } + + if (opts.init) { + vector<State> initval; + for (int i = depth-1; i >= 0; i--) { + SigBit bit = sigmap(chain[cursor+i]->getPort(q_port).as_bit()); + if (sigbit_init.count(bit) == 0) + initval.push_back(State::Sx); + else if (sigbit_init.at(bit)) + initval.push_back(State::S1); + else + initval.push_back(State::S0); + remove_init.insert(bit); + } + first_cell->setParam("\\INIT", initval); + } + + if (opts.zinit) + for (int i = depth-1; i >= 0; i--) { + SigBit bit = sigmap(chain[cursor+i]->getPort(q_port).as_bit()); + remove_init.insert(bit); + } + + if (opts.params) + { + int param_clkpol = -1; + int param_enpol = 2; + + if (first_cell->type == "$_DFF_N_") param_clkpol = 0; + if (first_cell->type == "$_DFF_P_") param_clkpol = 1; + + if (first_cell->type == "$_DFFE_NN_") param_clkpol = 0, param_enpol = 0; + if (first_cell->type == "$_DFFE_NP_") param_clkpol = 0, param_enpol = 1; + if (first_cell->type == "$_DFFE_PN_") param_clkpol = 1, param_enpol = 0; + if (first_cell->type == "$_DFFE_PP_") param_clkpol = 1, param_enpol = 1; + + log_assert(param_clkpol >= 0); + first_cell->setParam("\\CLKPOL", param_clkpol); + if (opts.ffe) first_cell->setParam("\\ENPOL", param_enpol); + } + + first_cell->type = shreg_cell_type_str; + first_cell->setPort(q_port, last_cell->getPort(q_port)); + first_cell->setParam("\\DEPTH", depth); + + if (opts.tech != nullptr && !opts.tech->fixup(first_cell, taps_dict)) + remove_cells.insert(first_cell); + + for (int i = 1; i < depth; i++) + remove_cells.insert(chain[cursor+i]); + cursor += depth; + } + } + + void cleanup() + { + for (auto cell : remove_cells) + module->remove(cell); + + for (auto wire : module->wires()) + { + if (wire->attributes.count("\\init") == 0) + continue; + + SigSpec initsig = sigmap(wire); + Const &initval = wire->attributes.at("\\init"); + + for (int i = 0; i < GetSize(initsig) && i < GetSize(initval); i++) + if (remove_init.count(initsig[i])) + initval[i] = State::Sx; + + if (SigSpec(initval).is_fully_undef()) + wire->attributes.erase("\\init"); + } + + remove_cells.clear(); + sigbit_chain_next.clear(); + sigbit_chain_prev.clear(); + chain_start_cells.clear(); + } + + ShregmapWorker(Module *module, const ShregmapOptions &opts) : + module(module), sigmap(module), opts(opts), dff_count(0), shreg_count(0) + { + make_sigbit_chain_next_prev(); + find_chain_start_cells(); + + for (auto c : chain_start_cells) { + vector<Cell*> chain = create_chain(c); + process_chain(chain); + } + + cleanup(); + } +}; + +struct ShregmapPass : public Pass { + ShregmapPass() : Pass("shregmap", "map shift registers") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" shregmap [options] [selection]\n"); + log("\n"); + log("This pass converts chains of $_DFF_[NP]_ gates to target specific shift register\n"); + log("primitives. The generated shift register will be of type $__SHREG_DFF_[NP]_ and\n"); + log("will use the same interface as the original $_DFF_*_ cells. The cell parameter\n"); + log("'DEPTH' will contain the depth of the shift register. Use a target-specific\n"); + log("'techmap' map file to convert those cells to the actual target cells.\n"); + log("\n"); + log(" -minlen N\n"); + log(" minimum length of shift register (default = 2)\n"); + log(" (this is the length after -keep_before and -keep_after)\n"); + log("\n"); + log(" -maxlen N\n"); + log(" maximum length of shift register (default = no limit)\n"); + log(" larger chains will be mapped to multiple shift register instances\n"); + log("\n"); + log(" -keep_before N\n"); + log(" number of DFFs to keep before the shift register (default = 0)\n"); + log("\n"); + log(" -keep_after N\n"); + log(" number of DFFs to keep after the shift register (default = 0)\n"); + log("\n"); + log(" -clkpol pos|neg|any\n"); + log(" limit match to only positive or negative edge clocks. (default = any)\n"); + log("\n"); + log(" -enpol pos|neg|none|any_or_none|any\n"); + log(" limit match to FFs with the specified enable polarity. (default = none)\n"); + log("\n"); + log(" -match <cell_type>[:<d_port_name>:<q_port_name>]\n"); + log(" match the specified cells instead of $_DFF_N_ and $_DFF_P_. If\n"); + log(" ':<d_port_name>:<q_port_name>' is omitted then 'D' and 'Q' is used\n"); + log(" by default. E.g. the option '-clkpol pos' is just an alias for\n"); + log(" '-match $_DFF_P_', which is an alias for '-match $_DFF_P_:D:Q'.\n"); + log("\n"); + log(" -params\n"); + log(" instead of encoding the clock and enable polarity in the cell name by\n"); + log(" deriving from the original cell name, simply name all generated cells\n"); + log(" $__SHREG_ and use CLKPOL and ENPOL parameters. An ENPOL value of 2 is\n"); + log(" used to denote cells without enable input. The ENPOL parameter is\n"); + log(" omitted when '-enpol none' (or no -enpol option) is passed.\n"); + log("\n"); + log(" -zinit\n"); + log(" assume the shift register is automatically zero-initialized, so it\n"); + log(" becomes legal to merge zero initialized FFs into the shift register.\n"); + log("\n"); + log(" -init\n"); + log(" map initialized registers to the shift reg, add an INIT parameter to\n"); + log(" generated cells with the initialization value. (first bit to shift out\n"); + log(" in LSB position)\n"); + log("\n"); + log(" -tech greenpak4\n"); + log(" map to greenpak4 shift registers.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + ShregmapOptions opts; + string clkpol, enpol; + + log_header(design, "Executing SHREGMAP pass (map shift registers).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-clkpol" && argidx+1 < args.size()) { + clkpol = args[++argidx]; + continue; + } + if (args[argidx] == "-enpol" && argidx+1 < args.size()) { + enpol = args[++argidx]; + continue; + } + if (args[argidx] == "-match" && argidx+1 < args.size()) { + vector<string> match_args = split_tokens(args[++argidx], ":"); + if (GetSize(match_args) < 2) + match_args.push_back("D"); + if (GetSize(match_args) < 3) + match_args.push_back("Q"); + IdString id_cell_type(RTLIL::escape_id(match_args[0])); + IdString id_d_port_name(RTLIL::escape_id(match_args[1])); + IdString id_q_port_name(RTLIL::escape_id(match_args[2])); + opts.ffcells[id_cell_type] = make_pair(id_d_port_name, id_q_port_name); + continue; + } + if (args[argidx] == "-minlen" && argidx+1 < args.size()) { + opts.minlen = atoi(args[++argidx].c_str()); + continue; + } + if (args[argidx] == "-maxlen" && argidx+1 < args.size()) { + opts.maxlen = atoi(args[++argidx].c_str()); + continue; + } + if (args[argidx] == "-keep_before" && argidx+1 < args.size()) { + opts.keep_before = atoi(args[++argidx].c_str()); + continue; + } + if (args[argidx] == "-keep_after" && argidx+1 < args.size()) { + opts.keep_after = atoi(args[++argidx].c_str()); + continue; + } + if (args[argidx] == "-tech" && argidx+1 < args.size() && opts.tech == nullptr) { + string tech = args[++argidx]; + if (tech == "greenpak4") { + clkpol = "pos"; + opts.zinit = true; + opts.tech = new ShregmapTechGreenpak4; + } else { + argidx--; + break; + } + continue; + } + if (args[argidx] == "-zinit") { + opts.zinit = true; + continue; + } + if (args[argidx] == "-init") { + opts.init = true; + continue; + } + if (args[argidx] == "-params") { + opts.params = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + if (opts.zinit && opts.init) + log_cmd_error("Options -zinit and -init are exclusive!\n"); + + if (opts.ffcells.empty()) + { + bool clk_pos = clkpol == "" || clkpol == "pos" || clkpol == "any"; + bool clk_neg = clkpol == "" || clkpol == "neg" || clkpol == "any"; + + bool en_none = enpol == "" || enpol == "none" || enpol == "any_or_none"; + bool en_pos = enpol == "pos" || enpol == "any" || enpol == "any_or_none"; + bool en_neg = enpol == "neg" || enpol == "any" || enpol == "any_or_none"; + + if (clk_pos && en_none) + opts.ffcells["$_DFF_P_"] = make_pair(IdString("\\D"), IdString("\\Q")); + if (clk_neg && en_none) + opts.ffcells["$_DFF_N_"] = make_pair(IdString("\\D"), IdString("\\Q")); + + if (clk_pos && en_pos) + opts.ffcells["$_DFFE_PP_"] = make_pair(IdString("\\D"), IdString("\\Q")); + if (clk_pos && en_neg) + opts.ffcells["$_DFFE_PN_"] = make_pair(IdString("\\D"), IdString("\\Q")); + + if (clk_neg && en_pos) + opts.ffcells["$_DFFE_NP_"] = make_pair(IdString("\\D"), IdString("\\Q")); + if (clk_neg && en_neg) + opts.ffcells["$_DFFE_NN_"] = make_pair(IdString("\\D"), IdString("\\Q")); + + if (en_pos || en_neg) + opts.ffe = true; + } + else + { + if (!clkpol.empty()) + log_cmd_error("Options -clkpol and -match are exclusive!\n"); + if (!enpol.empty()) + log_cmd_error("Options -enpol and -match are exclusive!\n"); + if (opts.params) + log_cmd_error("Options -params and -match are exclusive!\n"); + } + + int dff_count = 0; + int shreg_count = 0; + + for (auto module : design->selected_modules()) { + ShregmapWorker worker(module, opts); + dff_count += worker.dff_count; + shreg_count += worker.shreg_count; + } + + log("Converted %d dff cells into %d shift registers.\n", dff_count, shreg_count); + + if (opts.tech != nullptr) { + delete opts.tech; + opts.tech = nullptr; + } + } +} ShregmapPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/simplemap.cc b/passes/techmap/simplemap.cc index 9cea5f45d..0fb647344 100644 --- a/passes/techmap/simplemap.cc +++ b/passes/techmap/simplemap.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -35,6 +35,7 @@ void simplemap_not(RTLIL::Module *module, RTLIL::Cell *cell) for (int i = 0; i < GetSize(sig_y); i++) { RTLIL::Cell *gate = module->addCell(NEW_ID, "$_NOT_"); + gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src")); gate->setPort("\\A", sig_a[i]); gate->setPort("\\Y", sig_y[i]); } @@ -65,6 +66,7 @@ void simplemap_bitop(RTLIL::Module *module, RTLIL::Cell *cell) for (int i = 0; i < GetSize(sig_y); i++) { RTLIL::Cell *gate = module->addCell(NEW_ID, "$_NOT_"); + gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src")); gate->setPort("\\A", sig_t[i]); gate->setPort("\\Y", sig_y[i]); } @@ -81,6 +83,7 @@ void simplemap_bitop(RTLIL::Module *module, RTLIL::Cell *cell) for (int i = 0; i < GetSize(sig_y); i++) { RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type); + gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src")); gate->setPort("\\A", sig_a[i]); gate->setPort("\\B", sig_b[i]); gate->setPort("\\Y", sig_y[i]); @@ -94,7 +97,7 @@ void simplemap_reduce(RTLIL::Module *module, RTLIL::Cell *cell) if (sig_y.size() == 0) return; - + if (sig_a.size() == 0) { if (cell->type == "$reduce_and") module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(1, sig_y.size()))); if (cell->type == "$reduce_or") module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(0, sig_y.size()))); @@ -131,6 +134,7 @@ void simplemap_reduce(RTLIL::Module *module, RTLIL::Cell *cell) } RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type); + gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src")); gate->setPort("\\A", sig_a[i]); gate->setPort("\\B", sig_a[i+1]); gate->setPort("\\Y", sig_t[i/2]); @@ -143,6 +147,7 @@ void simplemap_reduce(RTLIL::Module *module, RTLIL::Cell *cell) if (cell->type == "$reduce_xnor") { RTLIL::SigSpec sig_t = module->addWire(NEW_ID); RTLIL::Cell *gate = module->addCell(NEW_ID, "$_NOT_"); + gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src")); gate->setPort("\\A", sig_a); gate->setPort("\\Y", sig_t); last_output_cell = gate; @@ -156,7 +161,7 @@ void simplemap_reduce(RTLIL::Module *module, RTLIL::Cell *cell) } } -static void logic_reduce(RTLIL::Module *module, RTLIL::SigSpec &sig) +static void logic_reduce(RTLIL::Module *module, RTLIL::SigSpec &sig, RTLIL::Cell *cell) { while (sig.size() > 1) { @@ -170,6 +175,7 @@ static void logic_reduce(RTLIL::Module *module, RTLIL::SigSpec &sig) } RTLIL::Cell *gate = module->addCell(NEW_ID, "$_OR_"); + gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src")); gate->setPort("\\A", sig[i]); gate->setPort("\\B", sig[i+1]); gate->setPort("\\Y", sig_t[i/2]); @@ -185,19 +191,20 @@ static void logic_reduce(RTLIL::Module *module, RTLIL::SigSpec &sig) void simplemap_lognot(RTLIL::Module *module, RTLIL::Cell *cell) { RTLIL::SigSpec sig_a = cell->getPort("\\A"); - logic_reduce(module, sig_a); + logic_reduce(module, sig_a, cell); RTLIL::SigSpec sig_y = cell->getPort("\\Y"); if (sig_y.size() == 0) return; - + if (sig_y.size() > 1) { module->connect(RTLIL::SigSig(sig_y.extract(1, sig_y.size()-1), RTLIL::SigSpec(0, sig_y.size()-1))); sig_y = sig_y.extract(0, 1); } RTLIL::Cell *gate = module->addCell(NEW_ID, "$_NOT_"); + gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src")); gate->setPort("\\A", sig_a); gate->setPort("\\Y", sig_y); } @@ -205,16 +212,16 @@ void simplemap_lognot(RTLIL::Module *module, RTLIL::Cell *cell) void simplemap_logbin(RTLIL::Module *module, RTLIL::Cell *cell) { RTLIL::SigSpec sig_a = cell->getPort("\\A"); - logic_reduce(module, sig_a); + logic_reduce(module, sig_a, cell); RTLIL::SigSpec sig_b = cell->getPort("\\B"); - logic_reduce(module, sig_b); + logic_reduce(module, sig_b, cell); RTLIL::SigSpec sig_y = cell->getPort("\\Y"); if (sig_y.size() == 0) return; - + if (sig_y.size() > 1) { module->connect(RTLIL::SigSig(sig_y.extract(1, sig_y.size()-1), RTLIL::SigSpec(0, sig_y.size()-1))); sig_y = sig_y.extract(0, 1); @@ -226,6 +233,7 @@ void simplemap_logbin(RTLIL::Module *module, RTLIL::Cell *cell) log_assert(!gate_type.empty()); RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type); + gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src")); gate->setPort("\\A", sig_a); gate->setPort("\\B", sig_b); gate->setPort("\\Y", sig_y); @@ -239,18 +247,21 @@ void simplemap_eqne(RTLIL::Module *module, RTLIL::Cell *cell) bool is_signed = cell->parameters.at("\\A_SIGNED").as_bool(); bool is_ne = cell->type == "$ne" || cell->type == "$nex"; - RTLIL::SigSpec xor_out = module->addWire(NEW_ID, std::max(GetSize(sig_a), GetSize(sig_b))); + RTLIL::SigSpec xor_out = module->addWire(NEW_ID, max(GetSize(sig_a), GetSize(sig_b))); RTLIL::Cell *xor_cell = module->addXor(NEW_ID, sig_a, sig_b, xor_out, is_signed); + xor_cell->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src")); simplemap_bitop(module, xor_cell); module->remove(xor_cell); RTLIL::SigSpec reduce_out = is_ne ? sig_y : module->addWire(NEW_ID); RTLIL::Cell *reduce_cell = module->addReduceOr(NEW_ID, xor_out, reduce_out); + reduce_cell->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src")); simplemap_reduce(module, reduce_cell); module->remove(reduce_cell); if (!is_ne) { RTLIL::Cell *not_cell = module->addLogicNot(NEW_ID, reduce_out, sig_y); + not_cell->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src")); simplemap_lognot(module, not_cell); module->remove(not_cell); } @@ -264,6 +275,7 @@ void simplemap_mux(RTLIL::Module *module, RTLIL::Cell *cell) for (int i = 0; i < GetSize(sig_y); i++) { RTLIL::Cell *gate = module->addCell(NEW_ID, "$_MUX_"); + gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src")); gate->setPort("\\A", sig_a[i]); gate->setPort("\\B", sig_b[i]); gate->setPort("\\S", cell->getPort("\\S")); @@ -271,6 +283,74 @@ void simplemap_mux(RTLIL::Module *module, RTLIL::Cell *cell) } } +void simplemap_tribuf(RTLIL::Module *module, RTLIL::Cell *cell) +{ + RTLIL::SigSpec sig_a = cell->getPort("\\A"); + RTLIL::SigSpec sig_e = cell->getPort("\\EN"); + RTLIL::SigSpec sig_y = cell->getPort("\\Y"); + + for (int i = 0; i < GetSize(sig_y); i++) { + RTLIL::Cell *gate = module->addCell(NEW_ID, "$_TBUF_"); + gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src")); + gate->setPort("\\A", sig_a[i]); + gate->setPort("\\E", sig_e); + gate->setPort("\\Y", sig_y[i]); + } +} + +void simplemap_lut(RTLIL::Module *module, RTLIL::Cell *cell) +{ + SigSpec lut_ctrl = cell->getPort("\\A"); + SigSpec lut_data = cell->getParam("\\LUT"); + lut_data.extend_u0(1 << cell->getParam("\\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, "$_MUX_"); + gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src")); + gate->setPort("\\A", lut_data[i]); + gate->setPort("\\B", lut_data[i+1]); + gate->setPort("\\S", lut_ctrl[idx]); + gate->setPort("\\Y", new_lut_data[i/2]); + } + lut_data = new_lut_data; + } + + module->connect(cell->getPort("\\Y"), lut_data); +} + +void simplemap_sop(RTLIL::Module *module, RTLIL::Cell *cell) +{ + SigSpec ctrl = cell->getPort("\\A"); + SigSpec table = cell->getParam("\\TABLE"); + + int width = cell->getParam("\\WIDTH").as_int(); + int depth = cell->getParam("\\DEPTH").as_int(); + table.extend_u0(2 * width * depth); + + SigSpec products; + + for (int i = 0; i < depth; i++) { + SigSpec in, pat; + for (int j = 0; j < width; j++) { + if (table[2*i*width + 2*j + 0] == State::S1) { + in.append(ctrl[j]); + pat.append(State::S0); + } + if (table[2*i*width + 2*j + 1] == State::S1) { + in.append(ctrl[j]); + pat.append(State::S1); + } + } + + products.append(GetSize(in) > 0 ? module->Eq(NEW_ID, in, pat) : State::S1); + } + + module->connect(cell->getPort("\\Y"), module->ReduceOr(NEW_ID, products)); +} + void simplemap_slice(RTLIL::Module *module, RTLIL::Cell *cell) { int offset = cell->parameters.at("\\OFFSET").as_int(); @@ -301,6 +381,7 @@ void simplemap_sr(RTLIL::Module *module, RTLIL::Cell *cell) for (int i = 0; i < width; i++) { RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type); + gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src")); gate->setPort("\\S", sig_s[i]); gate->setPort("\\R", sig_r[i]); gate->setPort("\\Q", sig_q[i]); @@ -320,6 +401,7 @@ void simplemap_dff(RTLIL::Module *module, RTLIL::Cell *cell) for (int i = 0; i < width; i++) { RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type); + gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src")); gate->setPort("\\C", sig_clk); gate->setPort("\\D", sig_d[i]); gate->setPort("\\Q", sig_q[i]); @@ -341,6 +423,7 @@ void simplemap_dffe(RTLIL::Module *module, RTLIL::Cell *cell) for (int i = 0; i < width; i++) { RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type); + gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src")); gate->setPort("\\C", sig_clk); gate->setPort("\\E", sig_en); gate->setPort("\\D", sig_d[i]); @@ -365,6 +448,7 @@ void simplemap_dffsr(RTLIL::Module *module, RTLIL::Cell *cell) for (int i = 0; i < width; i++) { RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type); + gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src")); gate->setPort("\\C", sig_clk); gate->setPort("\\S", sig_s[i]); gate->setPort("\\R", sig_r[i]); @@ -393,6 +477,7 @@ void simplemap_adff(RTLIL::Module *module, RTLIL::Cell *cell) 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("\\src", cell->get_strpool_attribute("\\src")); gate->setPort("\\C", sig_clk); gate->setPort("\\R", sig_rst); gate->setPort("\\D", sig_d[i]); @@ -413,6 +498,7 @@ void simplemap_dlatch(RTLIL::Module *module, RTLIL::Cell *cell) for (int i = 0; i < width; i++) { RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type); + gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src")); gate->setPort("\\E", sig_en); gate->setPort("\\D", sig_d[i]); gate->setPort("\\Q", sig_q[i]); @@ -440,6 +526,9 @@ void simplemap_get_mappers(std::map<RTLIL::IdString, void(*)(RTLIL::Module*, RTL mappers["$ne"] = simplemap_eqne; mappers["$nex"] = simplemap_eqne; mappers["$mux"] = simplemap_mux; + mappers["$tribuf"] = simplemap_tribuf; + mappers["$lut"] = simplemap_lut; + mappers["$sop"] = simplemap_sop; mappers["$slice"] = simplemap_slice; mappers["$concat"] = simplemap_concat; mappers["$sr"] = simplemap_sr; @@ -479,13 +568,13 @@ struct SimplemapPass : public Pass { log("\n"); 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\n"); + log(" $logic_not, $logic_and, $logic_or, $mux, $tribuf\n"); log(" $sr, $dff, $dffsr, $adff, $dlatch\n"); log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing SIMPLEMAP pass (map simple cells to gate primitives).\n"); + log_header(design, "Executing SIMPLEMAP pass (map simple cells to gate primitives).\n"); extra_args(args, 1, design); std::map<RTLIL::IdString, void(*)(RTLIL::Module*, RTLIL::Cell*)> mappers; @@ -507,5 +596,5 @@ struct SimplemapPass : public Pass { } } } SimplemapPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/techmap/simplemap.h b/passes/techmap/simplemap.h index dc2a395d3..c2d73ea79 100644 --- a/passes/techmap/simplemap.h +++ b/passes/techmap/simplemap.h @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -31,6 +31,7 @@ extern void simplemap_reduce(RTLIL::Module *module, RTLIL::Cell *cell); extern void simplemap_lognot(RTLIL::Module *module, RTLIL::Cell *cell); extern void simplemap_logbin(RTLIL::Module *module, RTLIL::Cell *cell); 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); diff --git a/passes/techmap/techmap.cc b/passes/techmap/techmap.cc index 521ac61a0..1ab6df1dc 100644 --- a/passes/techmap/techmap.cc +++ b/passes/techmap/techmap.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -49,7 +49,7 @@ void apply_prefix(std::string prefix, std::string &id) void apply_prefix(std::string prefix, RTLIL::SigSpec &sig, RTLIL::Module *module) { - std::vector<RTLIL::SigChunk> chunks = sig; + vector<SigChunk> chunks = sig; for (auto &chunk : chunks) if (chunk.wire != NULL) { std::string wire_name = chunk.wire->name.str(); @@ -66,6 +66,11 @@ struct TechmapWorker std::map<std::pair<RTLIL::IdString, std::map<RTLIL::IdString, RTLIL::Const>>, RTLIL::Module*> techmap_cache; std::map<RTLIL::Module*, bool> techmap_do_cache; std::set<RTLIL::Module*, RTLIL::IdString::compare_ptr_by_name<RTLIL::Module>> module_queue; + dict<Module*, SigMap> sigmaps; + + pool<IdString> flatten_do_list; + pool<IdString> flatten_done_list; + pool<Cell*> flatten_keep_list; struct TechmapWireData { RTLIL::Wire *wire; @@ -154,9 +159,10 @@ struct TechmapWorker void techmap_module_worker(RTLIL::Design *design, RTLIL::Module *module, RTLIL::Cell *cell, RTLIL::Module *tpl) { if (tpl->processes.size() != 0) { - log("Technology map yielded processes:\n"); + log("Technology map yielded processes:"); for (auto &it : tpl->processes) - log(" %s",RTLIL::id2cstr(it.first)); + log(" %s",RTLIL::id2cstr(it.first)); + log("\n"); if (autoproc_mode) { Pass::call_on_module(tpl->design, tpl, "proc"); log_assert(GetSize(tpl->processes) == 0); @@ -165,7 +171,10 @@ struct TechmapWorker } std::string orig_cell_name; + pool<string> extra_src_attrs; + if (!flatten_mode) + { for (auto &it : tpl->cells_) if (it.first == "\\_TECHMAP_REPLACE_") { orig_cell_name = cell->name.str(); @@ -173,6 +182,9 @@ struct TechmapWorker break; } + extra_src_attrs = cell->get_strpool_attribute("\\src"); + } + dict<IdString, IdString> memory_renames; for (auto &it : tpl->memories) { @@ -184,6 +196,8 @@ struct TechmapWorker m->start_offset = it.second->start_offset; m->size = it.second->size; m->attributes = it.second->attributes; + if (m->attributes.count("\\src")) + m->add_strpool_attribute("\\src", extra_src_attrs); module->memories[m->name] = m; memory_renames[it.first] = m->name; design->select(module, m); @@ -202,12 +216,28 @@ struct TechmapWorker w->port_id = 0; if (it.second->get_bool_attribute("\\_techmap_special_")) w->attributes.clear(); + if (w->attributes.count("\\src")) + w->add_strpool_attribute("\\src", extra_src_attrs); design->select(module, w); } + SigMap tpl_sigmap(tpl); + pool<SigBit> tpl_written_bits; + + for (auto &it1 : tpl->cells_) + for (auto &it2 : it1.second->connections_) + if (it1.second->output(it2.first)) + for (auto bit : tpl_sigmap(it2.second)) + tpl_written_bits.insert(bit); + for (auto &it1 : tpl->connections_) + for (auto bit : tpl_sigmap(it1.first)) + tpl_written_bits.insert(bit); + SigMap port_signal_map; + SigSig port_signal_assign; - for (auto &it : cell->connections()) { + for (auto &it : cell->connections()) + { RTLIL::IdString portname = it.first; if (positional_ports.count(portname) > 0) portname = positional_ports.at(portname); @@ -216,33 +246,76 @@ struct TechmapWorker log_error("Can't map port `%s' of cell `%s' to template `%s'!\n", portname.c_str(), cell->name.c_str(), tpl->name.c_str()); continue; } + RTLIL::Wire *w = tpl->wires_.at(portname); - RTLIL::SigSig c; - if (w->port_output) { + RTLIL::SigSig c, extra_connect; + + if (w->port_output && !w->port_input) { c.first = it.second; c.second = RTLIL::SigSpec(w); apply_prefix(cell->name.str(), c.second, module); - } else { + extra_connect.first = c.second; + extra_connect.second = c.first; + } else if (!w->port_output && w->port_input) { c.first = RTLIL::SigSpec(w); c.second = it.second; apply_prefix(cell->name.str(), c.first, module); + extra_connect.first = c.first; + extra_connect.second = c.second; + } else { + SigSpec sig_tpl = w, sig_tpl_pf = w, sig_mod = it.second; + apply_prefix(cell->name.str(), sig_tpl_pf, module); + for (int i = 0; i < GetSize(sig_tpl) && i < GetSize(sig_mod); i++) { + if (tpl_written_bits.count(tpl_sigmap(sig_tpl[i]))) { + c.first.append(sig_mod[i]); + c.second.append(sig_tpl_pf[i]); + } else { + c.first.append(sig_tpl_pf[i]); + c.second.append(sig_mod[i]); + } + } + extra_connect.first = sig_tpl_pf; + extra_connect.second = sig_mod; } + if (c.second.size() > c.first.size()) c.second.remove(c.first.size(), c.second.size() - c.first.size()); + if (c.second.size() < c.first.size()) c.second.append(RTLIL::SigSpec(RTLIL::State::S0, c.first.size() - c.second.size())); + log_assert(c.first.size() == c.second.size()); - if (flatten_mode) { + + if (flatten_mode) + { // more conservative approach: // connect internal and external wires + + if (sigmaps.count(module) == 0) + sigmaps[module].set(module); + + if (sigmaps.at(module)(c.first).has_const()) + log_error("Mismatch in directionality for cell port %s.%s.%s: %s <= %s\n", + log_id(module), log_id(cell), log_id(it.first), log_signal(c.first), log_signal(c.second)); + module->connect(c); - } else { + } + else + { // approach that yields nicer outputs: // replace internal wires that are connected to external wires + if (w->port_output) port_signal_map.add(c.second, c.first); else port_signal_map.add(c.first, c.second); + + for (auto &attr : w->attributes) { + if (attr.first == "\\src") + continue; + module->connect(extra_connect); + break; + } } } @@ -266,11 +339,14 @@ struct TechmapWorker port_signal_map.apply(it2.second); } - if (c->type == "$memrd" || c->type == "$memwr") { + if (c->type == "$memrd" || c->type == "$memwr" || c->type == "$meminit") { IdString memid = c->getParam("\\MEMID").decode_string(); - log_assert(memory_renames.count(memid)); + log_assert(memory_renames.count(memid) != 0); c->setParam("\\MEMID", Const(memory_renames[memid].str())); } + + if (c->attributes.count("\\src")) + c->add_strpool_attribute("\\src", extra_src_attrs); } for (auto &it : tpl->connections()) { @@ -317,6 +393,22 @@ struct TechmapWorker continue; } + if (flatten_mode) { + bool keepit = cell->get_bool_attribute("\\keep_hierarchy"); + for (auto &tpl_name : celltypeMap.at(cell_type)) + if (map->modules_[tpl_name]->get_bool_attribute("\\keep_hierarchy")) + keepit = true; + if (keepit) { + if (!flatten_keep_list[cell]) { + log("Keeping %s.%s (found keep_hierarchy property).\n", log_id(module), log_id(cell)); + flatten_keep_list.insert(cell); + } + if (!flatten_done_list[cell->type]) + flatten_do_list.insert(cell->type); + continue; + } + } + for (auto &conn : cell->connections()) { RTLIL::SigSpec sig = sigmap(conn.second); @@ -715,7 +807,7 @@ struct TechmapWorker if (recursive_mode) { if (log_continue) { - log_header("Continuing TECHMAP pass.\n"); + log_header(design, "Continuing TECHMAP pass.\n"); log_continue = false; } while (techmap_module(map, tpl, map, handled_cells, celltypeMap, true)) { } @@ -726,7 +818,7 @@ struct TechmapWorker continue; if (log_continue) { - log_header("Continuing TECHMAP pass.\n"); + log_header(design, "Continuing TECHMAP pass.\n"); log_continue = false; } @@ -769,7 +861,7 @@ struct TechmapWorker } if (log_continue) { - log_header("Continuing TECHMAP pass.\n"); + log_header(design, "Continuing TECHMAP pass.\n"); log_continue = false; } @@ -786,7 +878,7 @@ struct TechmapPass : public Pass { log(" techmap [-map filename] [selection]\n"); log("\n"); log("This pass implements a very simple technology mapper that replaces cells in\n"); - log("the design with implementations given in form of a verilog or ilang source\n"); + log("the design with implementations given in form of a Verilog or ilang source\n"); log("file.\n"); log("\n"); log(" -map filename\n"); @@ -798,11 +890,6 @@ struct TechmapPass : public Pass { log(" -map %%<design-name>\n"); log(" like -map above, but with an in-memory design instead of a file.\n"); log("\n"); - log(" -share_map filename\n"); - log(" like -map, but look for the file in the share directory (where the\n"); - log(" yosys data files are). this is mainly used internally when techmap\n"); - log(" is called from other commands.\n"); - log("\n"); log(" -extern\n"); log(" load the cell implementations as separate modules into the design\n"); log(" instead of inlining them.\n"); @@ -812,7 +899,7 @@ struct TechmapPass : public Pass { log("\n"); log(" -recursive\n"); log(" instead of the iterative breadth-first algorithm use a recursive\n"); - log(" depth-first algorithm. both methods should yield equivialent results,\n"); + log(" depth-first algorithm. both methods should yield equivalent results,\n"); log(" but may differ in performance.\n"); log("\n"); log(" -autoproc\n"); @@ -824,8 +911,8 @@ struct TechmapPass : public Pass { log(" as final cell types by this mode.\n"); log("\n"); log(" -D <define>, -I <incdir>\n"); - log(" this options are passed as-is to the verilog frontend for loading the\n"); - log(" map file. Note that the verilog frontend is also called with the\n"); + log(" this options are passed as-is to the Verilog frontend for loading the\n"); + log(" map file. Note that the Verilog frontend is also called with the\n"); log(" '-ignore_redef' option set.\n"); log("\n"); log("When a module in the map file has the 'techmap_celltype' attribute set, it will\n"); @@ -871,7 +958,7 @@ struct TechmapPass : public Pass { log(" of constant inputs and shorted inputs at this point and import the\n"); log(" constant and connected bits into the map module. All further commands\n"); log(" are executed in this copy. This is a very convenient way of creating\n"); - log(" optimizied specializations of techmap modules without using the special\n"); + log(" optimized specializations of techmap modules without using the special\n"); log(" parameters described below.\n"); log("\n"); log(" A _TECHMAP_DO_* command may start with the special token 'RECURSION; '.\n"); @@ -907,17 +994,17 @@ struct TechmapPass : public Pass { log("constant value.\n"); log("\n"); log("A cell with the name _TECHMAP_REPLACE_ in the map file will inherit the name\n"); - log("of the cell that is beeing replaced.\n"); + log("of the cell that is being replaced.\n"); log("\n"); log("See 'help extract' for a pass that does the opposite thing.\n"); log("\n"); log("See 'help flatten' for a pass that does flatten the design (which is\n"); - log("esentially techmap but using the design itself as map library).\n"); + log("essentially techmap but using the design itself as map library).\n"); log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing TECHMAP pass (map to technology primitives).\n"); + log_header(design, "Executing TECHMAP pass (map to technology primitives).\n"); log_push(); TechmapWorker worker; @@ -930,14 +1017,7 @@ struct TechmapPass : public Pass { size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { if (args[argidx] == "-map" && argidx+1 < args.size()) { - if (args[argidx+1].substr(0, 2) == "+/") - map_files.push_back(proc_share_dirname() + args[++argidx].substr(2)); - else - map_files.push_back(args[++argidx]); - continue; - } - if (args[argidx] == "-share_map" && argidx+1 < args.size()) { - map_files.push_back(proc_share_dirname() + args[++argidx]); + map_files.push_back(args[++argidx]); continue; } if (args[argidx] == "-max_iter" && argidx+1 < args.size()) { @@ -988,20 +1068,13 @@ struct TechmapPass : public Pass { map->add(mod->clone()); } else { std::ifstream f; + rewrite_filename(fn); f.open(fn.c_str()); if (f.fail()) log_cmd_error("Can't open map file `%s'\n", fn.c_str()); Frontend::frontend_call(map, &f, fn, (fn.size() > 3 && fn.substr(fn.size()-3) == ".il") ? "ilang" : verilog_frontend); } - dict<RTLIL::IdString, RTLIL::Module*> modules_new; - for (auto &it : map->modules_) { - if (it.first.substr(0, 2) == "\\$") - it.second->name = it.first.substr(1); - modules_new[it.second->name] = it.second; - } - map->modules_.swap(modules_new); - std::map<RTLIL::IdString, std::set<RTLIL::IdString, RTLIL::sort_by_id_str>> celltypeMap; for (auto &it : map->modules_) { if (it.second->attributes.count("\\techmap_celltype") && !it.second->attributes.at("\\techmap_celltype").bits.empty()) { @@ -1009,8 +1082,12 @@ struct TechmapPass : public Pass { for (char *q = strtok(p, " \t\r\n"); q; q = strtok(NULL, " \t\r\n")) celltypeMap[RTLIL::escape_id(q)].insert(it.first); free(p); - } else - celltypeMap[it.first].insert(it.first); + } else { + string module_name = it.first.str(); + if (module_name.substr(0, 2) == "\\$") + module_name = module_name.substr(1); + celltypeMap[module_name].insert(it.first); + } } for (auto module : design->modules()) @@ -1040,7 +1117,7 @@ struct TechmapPass : public Pass { log_pop(); } } TechmapPass; - + struct FlattenPass : public Pass { FlattenPass() : Pass("flatten", "flatten design") { } virtual void help() @@ -1050,13 +1127,16 @@ struct FlattenPass : public Pass { log(" flatten [selection]\n"); log("\n"); log("This pass flattens the design by replacing cells by their implementation. This\n"); - log("pass is very simmilar to the 'techmap' pass. The only difference is that this\n"); + log("pass is very similar to the 'techmap' pass. The only difference is that this\n"); log("pass is using the current design as mapping library.\n"); log("\n"); + log("Cells and/or modules with the 'keep_hierarchy' attribute set will not be\n"); + log("flattened by this command.\n"); + log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing FLATTEN pass (flatten design).\n"); + log_header(design, "Executing FLATTEN pass (flatten design).\n"); log_push(); extra_args(args, 1, design); @@ -1065,8 +1145,8 @@ struct FlattenPass : public Pass { worker.flatten_mode = true; std::map<RTLIL::IdString, std::set<RTLIL::IdString, RTLIL::sort_by_id_str>> celltypeMap; - for (auto &it : design->modules_) - celltypeMap[it.first].insert(it.first); + for (auto module : design->modules()) + celltypeMap[module->name].insert(module->name); RTLIL::Module *top_mod = NULL; if (design->full_selection()) @@ -1074,26 +1154,40 @@ struct FlattenPass : public Pass { if (mod->get_bool_attribute("\\top")) top_mod = mod; - bool did_something = true; std::set<RTLIL::Cell*> handled_cells; - while (did_something) { - did_something = false; - if (top_mod != NULL) { - if (worker.techmap_module(design, top_mod, design, handled_cells, celltypeMap, false)) - did_something = true; - } else { - for (auto mod : design->modules()) - if (worker.techmap_module(design, mod, design, handled_cells, celltypeMap, false)) - did_something = true; + if (top_mod != NULL) { + worker.flatten_do_list.insert(top_mod->name); + while (!worker.flatten_do_list.empty()) { + auto mod = design->module(*worker.flatten_do_list.begin()); + while (worker.techmap_module(design, mod, design, handled_cells, celltypeMap, false)) { } + worker.flatten_done_list.insert(mod->name); + worker.flatten_do_list.erase(mod->name); } + } else { + for (auto mod : vector<Module*>(design->modules())) + while (worker.techmap_module(design, mod, design, handled_cells, celltypeMap, false)) { } } log("No more expansions possible.\n"); - if (top_mod != NULL) { + if (top_mod != NULL) + { + pool<RTLIL::IdString> used_modules, new_used_modules; + new_used_modules.insert(top_mod->name); + while (!new_used_modules.empty()) { + pool<RTLIL::IdString> queue; + queue.swap(new_used_modules); + for (auto modname : queue) + used_modules.insert(modname); + for (auto modname : queue) + for (auto cell : design->module(modname)->cells()) + if (design->module(cell->type) && !used_modules[cell->type]) + new_used_modules.insert(cell->type); + } + dict<RTLIL::IdString, RTLIL::Module*> new_modules; - for (auto mod : design->modules()) - if (mod == top_mod || mod->get_bool_attribute("\\blackbox")) { + for (auto mod : vector<Module*>(design->modules())) + if (used_modules[mod->name] || mod->get_bool_attribute("\\blackbox")) { new_modules[mod->name] = mod; } else { log("Deleting now unused module %s.\n", log_id(mod)); diff --git a/passes/techmap/tribuf.cc b/passes/techmap/tribuf.cc new file mode 100644 index 000000000..03629082c --- /dev/null +++ b/passes/techmap/tribuf.cc @@ -0,0 +1,186 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include "kernel/sigtools.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct TribufConfig { + bool merge_mode; + bool logic_mode; + + TribufConfig() { + merge_mode = false; + logic_mode = false; + } +}; + +struct TribufWorker { + Module *module; + SigMap sigmap; + const TribufConfig &config; + + TribufWorker(Module *module, const TribufConfig &config) : module(module), sigmap(module), config(config) + { + } + + static bool is_all_z(SigSpec sig) + { + for (auto bit : sig) + if (bit != State::Sz) + return false; + return true; + } + + void run() + { + dict<SigSpec, vector<Cell*>> tribuf_cells; + pool<SigBit> output_bits; + + if (config.logic_mode) + for (auto wire : module->wires()) + if (wire->port_output) + for (auto bit : sigmap(wire)) + output_bits.insert(bit); + + for (auto cell : module->selected_cells()) + { + if (cell->type == "$tribuf") + tribuf_cells[sigmap(cell->getPort("\\Y"))].push_back(cell); + + if (cell->type == "$_TBUF_") + tribuf_cells[sigmap(cell->getPort("\\Y"))].push_back(cell); + + if (cell->type.in("$mux", "$_MUX_")) + { + IdString en_port = cell->type == "$mux" ? "\\EN" : "\\E"; + IdString tri_type = cell->type == "$mux" ? "$tribuf" : "$_TBUF_"; + + if (is_all_z(cell->getPort("\\A")) && is_all_z(cell->getPort("\\B"))) { + module->remove(cell); + continue; + } + + if (is_all_z(cell->getPort("\\A"))) { + cell->setPort("\\A", cell->getPort("\\B")); + cell->setPort(en_port, cell->getPort("\\S")); + cell->unsetPort("\\B"); + cell->unsetPort("\\S"); + cell->type = tri_type; + tribuf_cells[sigmap(cell->getPort("\\Y"))].push_back(cell); + continue; + } + + if (is_all_z(cell->getPort("\\B"))) { + cell->setPort(en_port, module->Not(NEW_ID, cell->getPort("\\S"))); + cell->unsetPort("\\B"); + cell->unsetPort("\\S"); + cell->type = tri_type; + tribuf_cells[sigmap(cell->getPort("\\Y"))].push_back(cell); + continue; + } + } + } + + if (config.merge_mode || config.logic_mode) + { + for (auto &it : tribuf_cells) + { + bool no_tribuf = false; + + if (config.logic_mode) { + no_tribuf = true; + for (auto bit : it.first) + if (output_bits.count(bit)) + no_tribuf = false; + } + + if (GetSize(it.second) <= 1 && !no_tribuf) + continue; + + SigSpec pmux_b, pmux_s; + for (auto cell : it.second) { + if (cell->type == "$tribuf") + pmux_s.append(cell->getPort("\\EN")); + else + pmux_s.append(cell->getPort("\\E")); + pmux_b.append(cell->getPort("\\A")); + module->remove(cell); + } + + SigSpec muxout = GetSize(pmux_s) > 1 ? module->Pmux(NEW_ID, SigSpec(State::Sx, GetSize(it.first)), pmux_b, pmux_s) : pmux_b; + + if (no_tribuf) + module->connect(it.first, muxout); + else + module->addTribuf(NEW_ID, muxout, module->ReduceOr(NEW_ID, pmux_s), it.first); + } + } + } +}; + +struct TribufPass : public Pass { + TribufPass() : Pass("tribuf", "infer tri-state buffers") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" tribuf [options] [selection]\n"); + log("\n"); + log("This pass transforms $mux cells with 'z' inputs to tristate buffers.\n"); + log("\n"); + log(" -merge\n"); + log(" merge multiple tri-state buffers driving the same net\n"); + log(" into a single buffer.\n"); + log("\n"); + log(" -logic\n"); + log(" convert tri-state buffers that do not drive output ports\n"); + log(" to non-tristate logic. this option implies -merge.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + TribufConfig config; + + log_header(design, "Executing TRIBUF pass.\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-merge") { + config.merge_mode = true; + continue; + } + if (args[argidx] == "-logic") { + config.logic_mode = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) { + TribufWorker worker(module, config); + worker.run(); + } + } +} TribufPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/tests/test_abcloop.cc b/passes/tests/test_abcloop.cc index 753fa7bf2..09cb41954 100644 --- a/passes/tests/test_abcloop.cc +++ b/passes/tests/test_abcloop.cc @@ -127,9 +127,9 @@ static void test_abcloop() module->fixup_ports(); Pass::call(design, "clean"); - ezDefaultSAT ez; + ezSatPtr ez; SigMap sigmap(module); - SatGen satgen(&ez, &sigmap); + SatGen satgen(ez.get(), &sigmap); for (auto c : module->cells()) { bool ok YS_ATTRIBUTE(unused) = satgen.importCell(c); @@ -137,7 +137,7 @@ static void test_abcloop() } std::vector<int> in_vec = satgen.importSigSpec(in_sig); - std::vector<int> inverse_in_vec = ez.vec_not(in_vec); + std::vector<int> inverse_in_vec = ez->vec_not(in_vec); std::vector<int> out_vec = satgen.importSigSpec(out_sig); @@ -148,7 +148,7 @@ static void test_abcloop() assumptions.push_back((i & (1 << j)) ? in_vec.at(j) : inverse_in_vec.at(j)); std::vector<bool> results; - if (!ez.solve(out_vec, results, assumptions)) { + if (!ez->solve(out_vec, results, assumptions)) { log("No stable solution for input %d found -> recreate module.\n", i); goto recreate_module; } @@ -156,10 +156,10 @@ static void test_abcloop() for (int j = 0; j < 4; j++) truthtab[i][j] = results[j]; - assumptions.push_back(ez.vec_ne(out_vec, ez.vec_const(results))); + assumptions.push_back(ez->vec_ne(out_vec, ez->vec_const(results))); std::vector<bool> results2; - if (ez.solve(out_vec, results2, assumptions)) { + if (ez->solve(out_vec, results2, assumptions)) { log("Two stable solutions for input %d found -> recreate module.\n", i); goto recreate_module; } @@ -177,9 +177,9 @@ static void test_abcloop() log("\n"); log("Pre- and post-abc truth table:\n"); - ezDefaultSAT ez; + ezSatPtr ez; SigMap sigmap(module); - SatGen satgen(&ez, &sigmap); + SatGen satgen(ez.get(), &sigmap); for (auto c : module->cells()) { bool ok YS_ATTRIBUTE(unused) = satgen.importCell(c); @@ -187,7 +187,7 @@ static void test_abcloop() } std::vector<int> in_vec = satgen.importSigSpec(in_sig); - std::vector<int> inverse_in_vec = ez.vec_not(in_vec); + std::vector<int> inverse_in_vec = ez->vec_not(in_vec); std::vector<int> out_vec = satgen.importSigSpec(out_sig); @@ -204,7 +204,7 @@ static void test_abcloop() truthtab2[i][j] = truthtab[i][j]; std::vector<bool> results; - if (!ez.solve(out_vec, results, assumptions)) { + if (!ez->solve(out_vec, results, assumptions)) { log("No stable solution for input %d found.\n", i); found_error = true; continue; @@ -213,10 +213,10 @@ static void test_abcloop() for (int j = 0; j < 4; j++) truthtab2[i][j] = results[j]; - assumptions.push_back(ez.vec_ne(out_vec, ez.vec_const(results))); + assumptions.push_back(ez->vec_ne(out_vec, ez->vec_const(results))); std::vector<bool> results2; - if (ez.solve(out_vec, results2, assumptions)) { + if (ez->solve(out_vec, results2, assumptions)) { log("Two stable solutions for input %d found -> recreate module.\n", i); found_error = true; } diff --git a/passes/tests/test_autotb.cc b/passes/tests/test_autotb.cc index 74ee0f5a9..59de111c2 100644 --- a/passes/tests/test_autotb.cc +++ b/passes/tests/test_autotb.cc @@ -2,11 +2,11 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -73,9 +73,14 @@ static std::string idy(std::string str1, std::string str2 = std::string(), std:: static void autotest(std::ostream &f, RTLIL::Design *design, int num_iter) { + f << stringf("`ifndef outfile\n"); + f << stringf("\t`define outfile \"/dev/stdout\"\n"); + f << stringf("`endif\n"); + f << stringf("module testbench;\n\n"); - f << stringf("integer i;\n\n"); + f << stringf("integer i;\n"); + f << stringf("integer file;\n\n"); f << stringf("reg [31:0] xorshift128_x = 123456789;\n"); f << stringf("reg [31:0] xorshift128_y = 362436069;\n"); @@ -195,7 +200,7 @@ static void autotest(std::ostream &f, RTLIL::Design *design, int num_iter) f << stringf(" } = {"); for (auto it = signal_clk.begin(); it != signal_clk.end(); it++) f << stringf("%s %s", it == signal_clk.begin() ? "" : ",", it->first.c_str()); - f << stringf(" } ^ (%d'b1 << (xorshift128_w %% %d));\n", total_clock_bits, total_clock_bits); + f << stringf(" } ^ (%d'b1 << (xorshift128_w %% %d));\n", total_clock_bits, total_clock_bits + 1); } f << stringf("end\n"); f << stringf("endtask\n\n"); @@ -206,7 +211,7 @@ static void autotest(std::ostream &f, RTLIL::Design *design, int num_iter) f << stringf("task %s;\n", idy(mod->name.str(), "print_status").c_str()); f << stringf("begin\n"); - f << stringf("\t$display(\"#OUT# %%b %%b %%b %%t %%d\", {"); + f << stringf("\t$fdisplay(file, \"#OUT# %%b %%b %%b %%t %%d\", {"); if (signal_in.size()) for (auto it = signal_in.begin(); it != signal_in.end(); it++) { f << stringf("%s %s", it == signal_in.begin() ? "" : ",", it->first.c_str()); @@ -218,7 +223,8 @@ static void autotest(std::ostream &f, RTLIL::Design *design, int num_iter) if (len > 0) header2 += shorthand, len--; header1.push_back(" " + it->first); - header1.back()[0] = shorthand++; + header1.back()[0] = shorthand; + shorthand = shorthand == 'Z' ? 'A' : shorthand+1; } else { f << stringf(" 1'bx"); @@ -237,7 +243,8 @@ static void autotest(std::ostream &f, RTLIL::Design *design, int num_iter) if (len > 0) header2 += shorthand, len--; header1.push_back(" " + it->first); - header1.back()[0] = shorthand++; + header1.back()[0] = shorthand; + shorthand = shorthand == 'Z' ? 'A' : shorthand+1; } } else { f << stringf(" 1'bx"); @@ -256,7 +263,8 @@ static void autotest(std::ostream &f, RTLIL::Design *design, int num_iter) if (len > 0) header2 += shorthand, len--; header1.push_back(" " + it->first); - header1.back()[0] = shorthand++; + header1.back()[0] = shorthand; + shorthand = shorthand == 'Z' ? 'A' : shorthand+1; } } else { f << stringf(" 1'bx"); @@ -268,17 +276,17 @@ static void autotest(std::ostream &f, RTLIL::Design *design, int num_iter) f << stringf("task %s;\n", idy(mod->name.str(), "print_header").c_str()); f << stringf("begin\n"); - f << stringf("\t$display(\"#OUT#\");\n"); + f << stringf("\t$fdisplay(file, \"#OUT#\");\n"); for (auto &hdr : header1) - f << stringf("\t$display(\"#OUT# %s\");\n", hdr.c_str()); - f << stringf("\t$display(\"#OUT#\");\n"); - f << stringf("\t$display(\"#OUT# %s\");\n", header2.c_str()); + f << stringf("\t$fdisplay(file, \"#OUT# %s\");\n", hdr.c_str()); + f << stringf("\t$fdisplay(file, \"#OUT#\");\n"); + f << stringf("\t$fdisplay(file, \"#OUT# %s\");\n", header2.c_str()); f << stringf("end\n"); f << stringf("endtask\n\n"); f << stringf("task %s;\n", idy(mod->name.str(), "test").c_str()); f << stringf("begin\n"); - f << stringf("\t$display(\"#OUT#\\n#OUT# ==== %s ====\");\n", idy(mod->name.str()).c_str()); + f << stringf("\t$fdisplay(file, \"#OUT#\\n#OUT# ==== %s ====\");\n", idy(mod->name.str()).c_str()); f << stringf("\t%s;\n", idy(mod->name.str(), "reset").c_str()); f << stringf("\tfor (i=0; i<%d; i=i+1) begin\n", num_iter); f << stringf("\t\tif (i %% 20 == 0) %s;\n", idy(mod->name.str(), "print_header").c_str()); @@ -293,9 +301,11 @@ static void autotest(std::ostream &f, RTLIL::Design *design, int num_iter) f << stringf("initial begin\n"); f << stringf("\t// $dumpfile(\"testbench.vcd\");\n"); f << stringf("\t// $dumpvars(0, testbench);\n"); + f << stringf("\tfile = $fopen(`outfile);\n"); for (auto it = design->modules_.begin(); it != design->modules_.end(); ++it) if (!it->second->get_bool_attribute("\\gentb_skip")) f << stringf("\t%s;\n", idy(it->first.str(), "test").c_str()); + f << stringf("\t$fclose(file);\n"); f << stringf("\t$finish;\n"); f << stringf("end\n\n"); @@ -310,7 +320,7 @@ struct TestAutotbBackend : public Backend { log("\n"); log(" test_autotb [options] [filename]\n"); log("\n"); - log("Automatically create primitive verilog test benches for all modules in the\n"); + log("Automatically create primitive Verilog test benches for all modules in the\n"); log("design. The generated testbenches toggle the input pins of the module in\n"); log("a semi-random manner and dumps the resulting output signals.\n"); log("\n"); @@ -326,14 +336,14 @@ struct TestAutotbBackend : public Backend { log("low in order to explore more inner states in a state machine.\n"); log("\n"); log(" -n <int>\n"); - log(" number of iterations the test bench shuld run (default = 1000)\n"); + log(" number of iterations the test bench should run (default = 1000)\n"); log("\n"); } virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) { int num_iter = 1000; - log_header("Executing TEST_AUTOTB backend (auto-generate pseudo-random test benches).\n"); + log_header(design, "Executing TEST_AUTOTB backend (auto-generate pseudo-random test benches).\n"); int argidx; for (argidx = 1; argidx < GetSize(args); argidx++) @@ -349,6 +359,6 @@ struct TestAutotbBackend : public Backend { autotest(*f, design, num_iter); } } TestAutotbBackend; - + PRIVATE_NAMESPACE_END diff --git a/passes/tests/test_cell.cc b/passes/tests/test_cell.cc index ea2ab1e65..8b800d414 100644 --- a/passes/tests/test_cell.cc +++ b/passes/tests/test_cell.cc @@ -164,6 +164,41 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type, cell->setParam("\\LUT", config.as_const()); } + if (cell_type == "$sop") + { + int width = 1 + xorshift32(8); + int depth = 1 + xorshift32(8); + + wire = module->addWire("\\A"); + wire->width = width; + wire->port_input = true; + cell->setPort("\\A", wire); + + wire = module->addWire("\\Y"); + wire->port_output = true; + cell->setPort("\\Y", wire); + + RTLIL::SigSpec config; + for (int i = 0; i < width*depth; i++) + switch (xorshift32(3)) { + case 0: + config.append(RTLIL::S1); + config.append(RTLIL::S0); + break; + case 1: + config.append(RTLIL::S0); + config.append(RTLIL::S1); + break; + case 2: + config.append(RTLIL::S0); + config.append(RTLIL::S0); + break; + } + + cell->setParam("\\DEPTH", depth); + cell->setParam("\\TABLE", config.as_const()); + } + if (cell_type_flags.find('A') != std::string::npos) { wire = module->addWire("\\A"); wire->width = 1 + xorshift32(8); @@ -256,7 +291,7 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type, case 2: n = xorshift32(GetSize(sig)); m = xorshift32(GetSize(sig)); - for (int i = std::min(n, m); i < std::max(n, m); i++) + for (int i = min(n, m); i < max(n, m); i++) sig[i] = xorshift32(2) == 1 ? RTLIL::S1 : RTLIL::S0; break; } @@ -278,10 +313,10 @@ static void run_eval_test(RTLIL::Design *design, bool verbose, bool nosat, std:: RTLIL::Module *gate_mod = design->module("\\gate"); ConstEval gold_ce(gold_mod), gate_ce(gate_mod); - ezDefaultSAT ez1, ez2; + ezSatPtr ez1, ez2; SigMap sigmap(gold_mod); - SatGen satgen1(&ez1, &sigmap); - SatGen satgen2(&ez2, &sigmap); + SatGen satgen1(ez1.get(), &sigmap); + SatGen satgen2(ez2.get(), &sigmap); satgen2.model_undef = true; if (!nosat) @@ -433,7 +468,7 @@ static void run_eval_test(RTLIL::Design *design, bool verbose, bool nosat, std:: std::vector<int> sat1_model = satgen1.importSigSpec(out_sig); std::vector<bool> sat1_model_value; - if (!ez1.solve(sat1_model, sat1_model_value, ez1.vec_eq(sat1_in_sig, sat1_in_val))) + if (!ez1->solve(sat1_model, sat1_model_value, ez1->vec_eq(sat1_in_sig, sat1_in_val))) log_error("Evaluating sat model 1 (no undef modeling) failed!\n"); if (verbose) { @@ -468,7 +503,7 @@ static void run_eval_test(RTLIL::Design *design, bool verbose, bool nosat, std:: std::vector<bool> sat2_model_value; - if (!ez2.solve(sat2_model, sat2_model_value, ez2.vec_eq(sat2_in_def_sig, sat2_in_def_val), ez2.vec_eq(sat2_in_undef_sig, sat2_in_undef_val))) + if (!ez2->solve(sat2_model, sat2_model_value, ez2->vec_eq(sat2_in_def_sig, sat2_in_def_val), ez2->vec_eq(sat2_in_undef_sig, sat2_in_undef_val))) log_error("Evaluating sat model 2 (undef modeling) failed!\n"); if (verbose) { @@ -534,7 +569,10 @@ struct TestCellPass : public Pass { log(" pass this option to techmap.\n"); log("\n"); log(" -simlib\n"); - log(" use \"techmap -map +/simlib.v -max_iter 2 -autoproc\"\n"); + log(" use \"techmap -D SIMLIB_NOCHECKS -map +/simlib.v -max_iter 2 -autoproc\"\n"); + log("\n"); + log(" -aigmap\n"); + log(" instead of calling \"techmap\", call \"aigmap\"\n"); log("\n"); log(" -muxdiv\n"); log(" when creating test benches with dividers, create an additional mux\n"); @@ -549,11 +587,14 @@ struct TestCellPass : public Pass { log(" -nosat\n"); log(" do not check SAT model or run SAT equivalence checking\n"); log("\n"); + log(" -noeval\n"); + log(" do not check const-eval models\n"); + log("\n"); log(" -v\n"); log(" print additional debug information to the console\n"); log("\n"); log(" -vlog {filename}\n"); - log(" create a verilog test bench to test simlib and write_verilog\n"); + log(" create a Verilog test bench to test simlib and write_verilog\n"); log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design*) @@ -567,6 +608,7 @@ struct TestCellPass : public Pass { bool verbose = false; bool constmode = false; bool nosat = false; + bool noeval = false; int argidx; for (argidx = 1; argidx < GetSize(args); argidx++) @@ -597,7 +639,11 @@ struct TestCellPass : public Pass { continue; } if (args[argidx] == "-simlib") { - techmap_cmd = "techmap -map +/simlib.v -max_iter 2 -autoproc"; + techmap_cmd = "techmap -D SIMLIB_NOCHECKS -map +/simlib.v -max_iter 2 -autoproc"; + continue; + } + if (args[argidx] == "-aigmap") { + techmap_cmd = "aigmap"; continue; } if (args[argidx] == "-muxdiv") { @@ -612,6 +658,10 @@ struct TestCellPass : public Pass { nosat = true; continue; } + if (args[argidx] == "-noeval") { + noeval = true; + continue; + } if (args[argidx] == "-v") { verbose = true; continue; @@ -682,6 +732,7 @@ struct TestCellPass : public Pass { // cell_types["$assert"] = "A"; cell_types["$lut"] = "*"; + cell_types["$sop"] = "*"; cell_types["$alu"] = "ABSY"; cell_types["$lcu"] = "*"; cell_types["$macc"] = "*"; @@ -765,7 +816,8 @@ struct TestCellPass : public Pass { Backend::backend_call(design, &vlog_file, "<test_cell -vlog>", "verilog -selected -noexpr"); uut_names.push_back(uut_name); } - run_eval_test(design, verbose, nosat, uut_name, vlog_file); + if (!noeval) + run_eval_test(design, verbose, nosat, uut_name, vlog_file); } delete design; } |