diff options
Diffstat (limited to 'passes/techmap')
-rw-r--r-- | passes/techmap/Makefile.inc | 23 | ||||
-rw-r--r-- | passes/techmap/abc.cc | 1666 | ||||
-rw-r--r-- | passes/techmap/aigmap.cc | 149 | ||||
-rw-r--r-- | passes/techmap/alumacc.cc | 24 | ||||
-rw-r--r-- | passes/techmap/deminout.cc | 116 | ||||
-rw-r--r-- | passes/techmap/dff2dffe.cc | 76 | ||||
-rw-r--r-- | passes/techmap/dffinit.cc | 135 | ||||
-rw-r--r-- | passes/techmap/dfflibmap.cc | 117 | ||||
-rw-r--r-- | passes/techmap/dffsr2dff.cc | 213 | ||||
-rw-r--r-- | passes/techmap/extract.cc | 32 | ||||
-rw-r--r-- | passes/techmap/hilomap.cc | 8 | ||||
-rw-r--r-- | passes/techmap/iopadmap.cc | 187 | ||||
-rw-r--r-- | passes/techmap/libparse.cc | 12 | ||||
-rw-r--r-- | passes/techmap/libparse.h | 4 | ||||
-rw-r--r-- | passes/techmap/lut2mux.cc | 93 | ||||
-rw-r--r-- | passes/techmap/maccmap.cc | 14 | ||||
-rw-r--r-- | passes/techmap/muxcover.cc | 632 | ||||
-rw-r--r-- | passes/techmap/nlutmap.cc | 187 | ||||
-rw-r--r-- | passes/techmap/pmuxtree.cc | 112 | ||||
-rw-r--r-- | passes/techmap/shregmap.cc | 584 | ||||
-rw-r--r-- | passes/techmap/simplemap.cc | 115 | ||||
-rw-r--r-- | passes/techmap/simplemap.h | 5 | ||||
-rw-r--r-- | passes/techmap/techmap.cc | 224 | ||||
-rw-r--r-- | passes/techmap/tribuf.cc | 186 |
24 files changed, 4728 insertions, 186 deletions
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/techmap/abc.cc b/passes/techmap/abc.cc new file mode 100644 index 000000000..cc79296c5 --- /dev/null +++ b/passes/techmap/abc.cc @@ -0,0 +1,1666 @@ +/* + * 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. + * + */ + +// [[CITE]] ABC +// Berkeley Logic Synthesis and Verification Group, ABC: A System for Sequential Synthesis and Verification +// http://www.eecs.berkeley.edu/~alanmi/abc/ + +// [[CITE]] Berkeley Logic Interchange Format (BLIF) +// University of California. Berkeley. July 28, 1992 +// 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 +// http://en.wikipedia.org/wiki/Topological_sorting + +#define ABC_COMMAND_LIB "strash; dc2; scorr; ifraig; retime -o {D}; strash; dch -f; map {D}" +#define ABC_COMMAND_CTR "strash; dc2; scorr; ifraig; retime -o {D}; strash; dch -f; map {D}; buffer; upsize {D}; dnsize {D}; stime -p" +#define ABC_COMMAND_LUT "strash; dc2; scorr; ifraig; retime -o; strash; dch -f; if; mfs" +#define ABC_COMMAND_SOP "strash; dc2; scorr; ifraig; retime -o; strash; dch -f; cover {I} {P}" +#define ABC_COMMAND_DFL "strash; dc2; scorr; ifraig; retime -o; strash; dch -f; map" + +#define ABC_FAST_COMMAND_LIB "retime -o {D}; map {D}" +#define ABC_FAST_COMMAND_CTR "retime -o {D}; map {D}; buffer; upsize {D}; dnsize {D}; stime -p" +#define ABC_FAST_COMMAND_LUT "retime -o; if" +#define ABC_FAST_COMMAND_SOP "retime -o; cover -I {I} -P {P}" +#define ABC_FAST_COMMAND_DFL "retime -o; map" + +#include "kernel/register.h" +#include "kernel/sigtools.h" +#include "kernel/celltypes.h" +#include "kernel/cost.h" +#include "kernel/log.h" +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <cerrno> +#include <sstream> +#include <climits> + +#ifndef _WIN32 +# include <unistd.h> +# include <dirent.h> +#endif + +#include "frontends/blif/blifparse.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +enum class gate_type_t { + G_NONE, + G_FF, + G_BUF, + G_NOT, + G_AND, + G_NAND, + G_OR, + G_NOR, + G_XOR, + G_XNOR, + G_MUX, + G_AOI3, + G_OAI3, + G_AOI4, + G_OAI4 +}; + +#define G(_name) gate_type_t::G_ ## _name + +struct gate_t +{ + int id; + gate_type_t type; + int in1, in2, in3, in4; + bool is_port; + 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; + +int map_signal(RTLIL::SigBit bit, gate_type_t gate_type = G(NONE), int in1 = -1, int in2 = -1, int in3 = -1, int in4 = -1) +{ + assign_map.apply(bit); + + if (signal_map.count(bit) == 0) { + gate_t gate; + gate.id = signal_list.size(); + gate.type = G(NONE); + gate.in1 = -1; + gate.in2 = -1; + gate.in3 = -1; + gate.in4 = -1; + gate.is_port = false; + gate.bit = bit; + signal_list.push_back(gate); + signal_map[bit] = gate.id; + } + + gate_t &gate = signal_list[signal_map[bit]]; + + if (gate_type != G(NONE)) + gate.type = gate_type; + if (in1 >= 0) + gate.in1 = in1; + if (in2 >= 0) + gate.in2 = in2; + if (in3 >= 0) + gate.in3 = in3; + if (in4 >= 0) + gate.in4 = in4; + + return gate.id; +} + +void mark_port(RTLIL::SigSpec sig) +{ + for (auto &bit : assign_map(sig)) + if (bit.wire != NULL && signal_map.count(bit) > 0) + signal_list[signal_map[bit]].is_port = true; +} + +void extract_cell(RTLIL::Cell *cell, bool keepff) +{ + if (cell->type == "$_DFF_N_" || cell->type == "$_DFF_P_") + { + if (clk_polarity != (cell->type == "$_DFF_P_")) + return; + if (clk_sig != assign_map(cell->getPort("\\C"))) + return; + if (GetSize(en_sig) != 0) + return; + goto matching_dff; + } + + if (cell->type == "$_DFFE_NN_" || cell->type == "$_DFFE_NP_" || cell->type == "$_DFFE_PN_" || cell->type == "$_DFFE_PP_") + { + if (clk_polarity != (cell->type == "$_DFFE_PN_" || cell->type == "$_DFFE_PP_")) + return; + if (en_polarity != (cell->type == "$_DFFE_NP_" || cell->type == "$_DFFE_PP_")) + return; + if (clk_sig != assign_map(cell->getPort("\\C"))) + return; + if (en_sig != assign_map(cell->getPort("\\E"))) + return; + goto matching_dff; + } + + if (0) { + matching_dff: + RTLIL::SigSpec sig_d = cell->getPort("\\D"); + RTLIL::SigSpec sig_q = cell->getPort("\\Q"); + + if (keepff) + for (auto &c : sig_q.chunks()) + if (c.wire != NULL) + c.wire->attributes["\\keep"] = 1; + + assign_map.apply(sig_d); + assign_map.apply(sig_q); + + map_signal(sig_q, G(FF), map_signal(sig_d)); + + module->remove(cell); + return; + } + + if (cell->type.in("$_BUF_", "$_NOT_")) + { + RTLIL::SigSpec sig_a = cell->getPort("\\A"); + RTLIL::SigSpec sig_y = cell->getPort("\\Y"); + + assign_map.apply(sig_a); + assign_map.apply(sig_y); + + map_signal(sig_y, cell->type == "$_BUF_" ? G(BUF) : G(NOT), map_signal(sig_a)); + + module->remove(cell); + return; + } + + if (cell->type.in("$_AND_", "$_NAND_", "$_OR_", "$_NOR_", "$_XOR_", "$_XNOR_")) + { + RTLIL::SigSpec sig_a = cell->getPort("\\A"); + RTLIL::SigSpec sig_b = cell->getPort("\\B"); + RTLIL::SigSpec sig_y = cell->getPort("\\Y"); + + assign_map.apply(sig_a); + assign_map.apply(sig_b); + assign_map.apply(sig_y); + + int mapped_a = map_signal(sig_a); + int mapped_b = map_signal(sig_b); + + if (cell->type == "$_AND_") + map_signal(sig_y, G(AND), mapped_a, mapped_b); + else if (cell->type == "$_NAND_") + map_signal(sig_y, G(NAND), mapped_a, mapped_b); + else if (cell->type == "$_OR_") + map_signal(sig_y, G(OR), mapped_a, mapped_b); + else if (cell->type == "$_NOR_") + map_signal(sig_y, G(NOR), mapped_a, mapped_b); + else if (cell->type == "$_XOR_") + map_signal(sig_y, G(XOR), mapped_a, mapped_b); + else if (cell->type == "$_XNOR_") + map_signal(sig_y, G(XNOR), mapped_a, mapped_b); + else + log_abort(); + + module->remove(cell); + return; + } + + if (cell->type == "$_MUX_") + { + RTLIL::SigSpec sig_a = cell->getPort("\\A"); + RTLIL::SigSpec sig_b = cell->getPort("\\B"); + RTLIL::SigSpec sig_s = cell->getPort("\\S"); + RTLIL::SigSpec sig_y = cell->getPort("\\Y"); + + assign_map.apply(sig_a); + assign_map.apply(sig_b); + assign_map.apply(sig_s); + assign_map.apply(sig_y); + + int mapped_a = map_signal(sig_a); + int mapped_b = map_signal(sig_b); + int mapped_s = map_signal(sig_s); + + map_signal(sig_y, G(MUX), mapped_a, mapped_b, mapped_s); + + module->remove(cell); + return; + } + + if (cell->type.in("$_AOI3_", "$_OAI3_")) + { + RTLIL::SigSpec sig_a = cell->getPort("\\A"); + RTLIL::SigSpec sig_b = cell->getPort("\\B"); + RTLIL::SigSpec sig_c = cell->getPort("\\C"); + RTLIL::SigSpec sig_y = cell->getPort("\\Y"); + + assign_map.apply(sig_a); + assign_map.apply(sig_b); + assign_map.apply(sig_c); + assign_map.apply(sig_y); + + int mapped_a = map_signal(sig_a); + int mapped_b = map_signal(sig_b); + int mapped_c = map_signal(sig_c); + + map_signal(sig_y, cell->type == "$_AOI3_" ? G(AOI3) : G(OAI3), mapped_a, mapped_b, mapped_c); + + module->remove(cell); + return; + } + + if (cell->type.in("$_AOI4_", "$_OAI4_")) + { + RTLIL::SigSpec sig_a = cell->getPort("\\A"); + RTLIL::SigSpec sig_b = cell->getPort("\\B"); + RTLIL::SigSpec sig_c = cell->getPort("\\C"); + RTLIL::SigSpec sig_d = cell->getPort("\\D"); + RTLIL::SigSpec sig_y = cell->getPort("\\Y"); + + assign_map.apply(sig_a); + assign_map.apply(sig_b); + assign_map.apply(sig_c); + assign_map.apply(sig_d); + assign_map.apply(sig_y); + + int mapped_a = map_signal(sig_a); + int mapped_b = map_signal(sig_b); + int mapped_c = map_signal(sig_c); + int mapped_d = map_signal(sig_d); + + map_signal(sig_y, cell->type == "$_AOI4_" ? G(AOI4) : G(OAI4), mapped_a, mapped_b, mapped_c, mapped_d); + + module->remove(cell); + return; + } +} + +std::string remap_name(RTLIL::IdString abc_name) +{ + std::stringstream sstr; + sstr << "$abc$" << map_autoidx << "$" << abc_name.substr(1); + return sstr.str(); +} + +void dump_loop_graph(FILE *f, int &nr, std::map<int, std::set<int>> &edges, std::set<int> &workpool, std::vector<int> &in_counts) +{ + if (f == NULL) + return; + + log("Dumping loop state graph to slide %d.\n", ++nr); + + fprintf(f, "digraph \"slide%d\" {\n", nr); + fprintf(f, " label=\"slide%d\";\n", nr); + fprintf(f, " rankdir=\"TD\";\n"); + + std::set<int> nodes; + for (auto &e : edges) { + nodes.insert(e.first); + for (auto n : e.second) + nodes.insert(n); + } + + for (auto n : nodes) + fprintf(f, " n%d [label=\"%s\\nid=%d, count=%d\"%s];\n", n, log_signal(signal_list[n].bit), + n, in_counts[n], workpool.count(n) ? ", shape=box" : ""); + + for (auto &e : edges) + for (auto n : e.second) + fprintf(f, " n%d -> n%d;\n", e.first, n); + + fprintf(f, "}\n"); +} + +void handle_loops() +{ + // http://en.wikipedia.org/wiki/Topological_sorting + // (Kahn, Arthur B. (1962), "Topological sorting of large networks") + + std::map<int, std::set<int>> edges; + std::vector<int> in_edges_count(signal_list.size()); + std::set<int> workpool; + + FILE *dot_f = NULL; + int dot_nr = 0; + + // uncomment for troubleshooting the loop detection code + // dot_f = fopen("test.dot", "w"); + + for (auto &g : signal_list) { + if (g.type == G(NONE) || g.type == G(FF)) { + workpool.insert(g.id); + } else { + if (g.in1 >= 0) { + edges[g.in1].insert(g.id); + in_edges_count[g.id]++; + } + if (g.in2 >= 0 && g.in2 != g.in1) { + edges[g.in2].insert(g.id); + in_edges_count[g.id]++; + } + if (g.in3 >= 0 && g.in3 != g.in2 && g.in3 != g.in1) { + edges[g.in3].insert(g.id); + in_edges_count[g.id]++; + } + if (g.in4 >= 0 && g.in4 != g.in3 && g.in4 != g.in2 && g.in4 != g.in1) { + edges[g.in4].insert(g.id); + in_edges_count[g.id]++; + } + } + } + + dump_loop_graph(dot_f, dot_nr, edges, workpool, in_edges_count); + + while (workpool.size() > 0) + { + int id = *workpool.begin(); + workpool.erase(id); + + // log("Removing non-loop node %d from graph: %s\n", id, log_signal(signal_list[id].bit)); + + for (int id2 : edges[id]) { + log_assert(in_edges_count[id2] > 0); + if (--in_edges_count[id2] == 0) + workpool.insert(id2); + } + edges.erase(id); + + dump_loop_graph(dot_f, dot_nr, edges, workpool, in_edges_count); + + while (workpool.size() == 0) + { + if (edges.size() == 0) + break; + + int id1 = edges.begin()->first; + + for (auto &edge_it : edges) { + int id2 = edge_it.first; + RTLIL::Wire *w1 = signal_list[id1].bit.wire; + RTLIL::Wire *w2 = signal_list[id2].bit.wire; + if (w1 == NULL) + id1 = id2; + else if (w2 == NULL) + continue; + else if (w1->name[0] == '$' && w2->name[0] == '\\') + id1 = id2; + else if (w1->name[0] == '\\' && w2->name[0] == '$') + continue; + else if (edges[id1].size() < edges[id2].size()) + id1 = id2; + else if (edges[id1].size() > edges[id2].size()) + continue; + else if (w2->name.str() < w1->name.str()) + id1 = id2; + } + + if (edges[id1].size() == 0) { + edges.erase(id1); + continue; + } + + log_assert(signal_list[id1].bit.wire != NULL); + + std::stringstream sstr; + sstr << "$abcloop$" << (autoidx++); + RTLIL::Wire *wire = module->addWire(sstr.str()); + + bool first_line = true; + for (int id2 : edges[id1]) { + if (first_line) + log("Breaking loop using new signal %s: %s -> %s\n", log_signal(RTLIL::SigSpec(wire)), + log_signal(signal_list[id1].bit), log_signal(signal_list[id2].bit)); + else + log(" %*s %s -> %s\n", int(strlen(log_signal(RTLIL::SigSpec(wire)))), "", + log_signal(signal_list[id1].bit), log_signal(signal_list[id2].bit)); + first_line = false; + } + + int id3 = map_signal(RTLIL::SigSpec(wire)); + signal_list[id1].is_port = true; + signal_list[id3].is_port = true; + log_assert(id3 == int(in_edges_count.size())); + in_edges_count.push_back(0); + workpool.insert(id3); + + for (int id2 : edges[id1]) { + if (signal_list[id2].in1 == id1) + signal_list[id2].in1 = id3; + if (signal_list[id2].in2 == id1) + signal_list[id2].in2 = id3; + if (signal_list[id2].in3 == id1) + signal_list[id2].in3 = id3; + if (signal_list[id2].in4 == id1) + signal_list[id2].in4 = id3; + } + edges[id1].swap(edges[id3]); + + module->connect(RTLIL::SigSig(signal_list[id3].bit, signal_list[id1].bit)); + dump_loop_graph(dot_f, dot_nr, edges, workpool, in_edges_count); + } + } + + if (dot_f != NULL) + fclose(dot_f); +} + +std::string add_echos_to_abc_cmd(std::string str) +{ + std::string new_str, token; + for (size_t i = 0; i < str.size(); i++) { + token += str[i]; + if (str[i] == ';') { + while (i+1 < str.size() && str[i+1] == ' ') + i++; + new_str += "echo + " + token + " " + token + " "; + token.clear(); + } + } + + if (!token.empty()) { + if (!new_str.empty()) + new_str += "echo + " + token + "; "; + new_str += token; + } + + return new_str; +} + +std::string fold_abc_cmd(std::string str) +{ + std::string token, new_str = " "; + int char_counter = 10; + + for (size_t i = 0; i <= str.size(); i++) { + if (i < str.size()) + token += str[i]; + if (i == str.size() || str[i] == ';') { + if (char_counter + token.size() > 75) + new_str += "\n ", char_counter = 14; + new_str += token, char_counter += token.size(); + token.clear(); + } + } + + return new_str; +} + +std::string replace_tempdir(std::string text, std::string tempdir_name, bool show_tempdir) +{ + if (show_tempdir) + return text; + + while (1) { + size_t pos = text.find(tempdir_name); + if (pos == std::string::npos) + break; + text = text.substr(0, pos) + "<abc-temp-dir>" + text.substr(pos + GetSize(tempdir_name)); + } + + std::string selfdir_name = proc_self_dirname(); + while (1) { + size_t pos = text.find(selfdir_name); + if (pos == std::string::npos) + break; + text = text.substr(0, pos) + "<yosys-exe-dir>/" + text.substr(pos + GetSize(selfdir_name)); + } + + return text; +} + +struct abc_output_filter +{ + bool got_cr; + int escape_seq_state; + std::string linebuf; + std::string tempdir_name; + bool show_tempdir; + + abc_output_filter(std::string tempdir_name, bool show_tempdir) : tempdir_name(tempdir_name), show_tempdir(show_tempdir) + { + got_cr = false; + escape_seq_state = 0; + } + + void next_char(char ch) + { + if (escape_seq_state == 0 && ch == '\033') { + escape_seq_state = 1; + return; + } + if (escape_seq_state == 1) { + escape_seq_state = ch == '[' ? 2 : 0; + return; + } + if (escape_seq_state == 2) { + if ((ch < '0' || '9' < ch) && ch != ';') + escape_seq_state = 0; + return; + } + escape_seq_state = 0; + if (ch == '\r') { + got_cr = true; + return; + } + if (ch == '\n') { + log("ABC: %s\n", replace_tempdir(linebuf, tempdir_name, show_tempdir).c_str()); + got_cr = false, linebuf.clear(); + return; + } + if (got_cr) + got_cr = false, linebuf.clear(); + linebuf += ch; + } + + void next_line(const std::string &line) + { + for (char ch : line) + next_char(ch); + } +}; + +void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::string script_file, std::string exe_file, + std::string liberty_file, std::string constr_file, bool cleanup, vector<int> lut_costs, bool dff_mode, std::string clk_str, + bool keepff, std::string delay_target, 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++; + + signal_map.clear(); + signal_list.clear(); + + if (clk_str != "$") + { + assign_map.set(module); + + clk_polarity = true; + clk_sig = RTLIL::SigSpec(); + + en_polarity = true; + en_sig = RTLIL::SigSpec(); + } + + std::string tempdir_name = "/tmp/yosys-abc-XXXXXX"; + if (!cleanup) + tempdir_name[0] = tempdir_name[4] = '_'; + tempdir_name = make_temp_dir(tempdir_name); + 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()); + + if (!liberty_file.empty()) { + abc_script += stringf("read_lib -w %s; ", liberty_file.c_str()); + if (!constr_file.empty()) + abc_script += stringf("read_constr -v %s; ", constr_file.c_str()); + } else + 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()); + + if (!script_file.empty()) { + if (script_file[0] == '+') { + for (size_t i = 1; i < script_file.size(); i++) + if (script_file[i] == '\'') + abc_script += "'\\''"; + else if (script_file[i] == ',') + abc_script += " "; + else + abc_script += script_file[i]; + } else + abc_script += stringf("source %s", script_file.c_str()); + } 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; + 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); + + for (size_t i = 0; i+1 < abc_script.size(); i++) + if (abc_script[i] == ';' && abc_script[i+1] == ' ') + abc_script[i+1] = '\n'; + + FILE *f = fopen(stringf("%s/abc.script", tempdir_name.c_str()).c_str(), "wt"); + fprintf(f, "%s\n", abc_script.c_str()); + fclose(f); + + if (!clk_str.empty() && clk_str != "$") + { + if (clk_str.find(',') != std::string::npos) { + int pos = clk_str.find(','); + std::string en_str = clk_str.substr(pos+1); + clk_str = clk_str.substr(0, pos); + if (en_str[0] == '!') { + en_polarity = false; + en_str = en_str.substr(1); + } + if (module->wires_.count(RTLIL::escape_id(en_str)) != 0) + en_sig = assign_map(RTLIL::SigSpec(module->wires_.at(RTLIL::escape_id(en_str)), 0)); + } + if (clk_str[0] == '!') { + clk_polarity = false; + clk_str = clk_str.substr(1); + } + if (module->wires_.count(RTLIL::escape_id(clk_str)) != 0) + clk_sig = assign_map(RTLIL::SigSpec(module->wires_.at(RTLIL::escape_id(clk_str)), 0)); + } + + if (dff_mode && clk_sig.empty()) + log_error("Clock domain %s not found.\n", clk_str.c_str()); + + if (dff_mode || !clk_str.empty()) + { + if (clk_sig.size() == 0) + log("No%s clock domain found. Not extracting any FF cells.\n", clk_str.empty() ? "" : " matching"); + else { + log("Found%s %s clock domain: %s", clk_str.empty() ? "" : " matching", clk_polarity ? "posedge" : "negedge", log_signal(clk_sig)); + if (en_sig.size() != 0) + log(", enabled by %s%s", en_polarity ? "" : "!", log_signal(en_sig)); + log("\n"); + } + } + + for (auto c : cells) + extract_cell(c, keepff); + + for (auto &wire_it : module->wires_) { + if (wire_it.second->port_id > 0 || wire_it.second->get_bool_attribute("\\keep")) + mark_port(RTLIL::SigSpec(wire_it.second)); + } + + for (auto &cell_it : module->cells_) + for (auto &port_it : cell_it.second->connections()) + mark_port(port_it.second); + + if (clk_sig.size() != 0) + mark_port(clk_sig); + + if (en_sig.size() != 0) + mark_port(en_sig); + + handle_loops(); + + std::string buffer = stringf("%s/input.blif", 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)); + + fprintf(f, ".model netlist\n"); + + int count_input = 0; + fprintf(f, ".inputs"); + for (auto &si : signal_list) { + if (!si.is_port || si.type != G(NONE)) + continue; + fprintf(f, " n%d", si.id); + count_input++; + } + if (count_input == 0) + fprintf(f, " dummy_input\n"); + fprintf(f, "\n"); + + int count_output = 0; + fprintf(f, ".outputs"); + for (auto &si : signal_list) { + if (!si.is_port || si.type == G(NONE)) + continue; + fprintf(f, " n%d", si.id); + count_output++; + } + fprintf(f, "\n"); + + for (auto &si : signal_list) + fprintf(f, "# n%-5d %s\n", si.id, log_signal(si.bit)); + + for (auto &si : signal_list) { + if (si.bit.wire == NULL) { + fprintf(f, ".names n%d\n", si.id); + if (si.bit == RTLIL::State::S1) + fprintf(f, "1\n"); + } + } + + int count_gates = 0; + for (auto &si : signal_list) { + if (si.type == G(BUF)) { + fprintf(f, ".names n%d n%d\n", si.in1, si.id); + fprintf(f, "1 1\n"); + } else if (si.type == G(NOT)) { + fprintf(f, ".names n%d n%d\n", si.in1, si.id); + fprintf(f, "0 1\n"); + } else if (si.type == G(AND)) { + fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id); + fprintf(f, "11 1\n"); + } else if (si.type == G(NAND)) { + fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id); + fprintf(f, "0- 1\n"); + fprintf(f, "-0 1\n"); + } else if (si.type == G(OR)) { + fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id); + fprintf(f, "-1 1\n"); + fprintf(f, "1- 1\n"); + } else if (si.type == G(NOR)) { + fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id); + fprintf(f, "00 1\n"); + } else if (si.type == G(XOR)) { + fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id); + fprintf(f, "01 1\n"); + fprintf(f, "10 1\n"); + } else if (si.type == G(XNOR)) { + fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id); + fprintf(f, "00 1\n"); + fprintf(f, "11 1\n"); + } else if (si.type == G(MUX)) { + fprintf(f, ".names n%d n%d n%d n%d\n", si.in1, si.in2, si.in3, si.id); + fprintf(f, "1-0 1\n"); + fprintf(f, "-11 1\n"); + } else if (si.type == G(AOI3)) { + fprintf(f, ".names n%d n%d n%d n%d\n", si.in1, si.in2, si.in3, si.id); + fprintf(f, "-00 1\n"); + fprintf(f, "0-0 1\n"); + } else if (si.type == G(OAI3)) { + fprintf(f, ".names n%d n%d n%d n%d\n", si.in1, si.in2, si.in3, si.id); + fprintf(f, "00- 1\n"); + fprintf(f, "--0 1\n"); + } else if (si.type == G(AOI4)) { + fprintf(f, ".names n%d n%d n%d n%d n%d\n", si.in1, si.in2, si.in3, si.in4, si.id); + fprintf(f, "-0-0 1\n"); + fprintf(f, "-00- 1\n"); + fprintf(f, "0--0 1\n"); + fprintf(f, "0-0- 1\n"); + } else if (si.type == G(OAI4)) { + fprintf(f, ".names n%d n%d n%d n%d n%d\n", si.in1, si.in2, si.in3, si.in4, si.id); + fprintf(f, "00-- 1\n"); + fprintf(f, "--00 1\n"); + } else if (si.type == G(FF)) { + fprintf(f, ".latch n%d n%d\n", si.in1, si.id); + } else if (si.type != G(NONE)) + log_abort(); + if (si.type != G(NONE)) + count_gates++; + } + + fprintf(f, ".end\n"); + fclose(f); + + log("Extracted %d gates and %d wires to a netlist network with %d inputs and %d outputs.\n", + count_gates, GetSize(signal_list), count_input, count_output); + log_push(); + + if (count_output > 0) + { + log_header(design, "Executing ABC.\n"); + + buffer = stringf("%s/stdcells.genlib", 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)); + fprintf(f, "GATE ZERO 1 Y=CONST0;\n"); + 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_")); + 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_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 < GetSize(lut_costs); i++) + fprintf(f, "%d %d.00 1.00\n", i+1, lut_costs.at(i)); + fclose(f); + } + + buffer = stringf("%s -s -f %s/abc.script 2>&1", exe_file.c_str(), tempdir_name.c_str()); + log("Running ABC command: %s\n", replace_tempdir(buffer, tempdir_name, show_tempdir).c_str()); + + abc_output_filter filt(tempdir_name, show_tempdir); + int ret = run_command(buffer, std::bind(&abc_output_filter::next_line, filt, std::placeholders::_1)); + if (ret != 0) + 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"); + 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(); + RTLIL::Design *mapped_design = new RTLIL::Design; + parse_blif(mapped_design, ifs, builtin_lib ? "\\DFF" : "\\_dff_", false, sop_mode); + + ifs.close(); + + 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"); + for (auto &it : mapped_mod->wires_) { + RTLIL::Wire *w = it.second; + RTLIL::Wire *wire = module->addWire(remap_name(w->name)); + if (markgroups) wire->attributes["\\abcgroup"] = map_autoidx; + design->select(module, wire); + } + + std::map<std::string, int> cell_stats; + for (auto c : mapped_mod->cells()) + { + if (builtin_lib) + { + cell_stats[RTLIL::unescape_id(c->type)]++; + if (c->type == "\\ZERO" || c->type == "\\ONE") { + RTLIL::SigSig conn; + conn.first = RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)]); + conn.second = RTLIL::SigSpec(c->type == "\\ZERO" ? 0 : 1, 1); + module->connect(conn); + continue; + } + if (c->type == "\\BUF") { + RTLIL::SigSig conn; + conn.first = RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)]); + conn.second = RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\A").as_wire()->name)]); + module->connect(conn); + continue; + } + if (c->type == "\\NOT") { + RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_NOT_"); + if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx; + cell->setPort("\\A", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\A").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 == "\\AND" || c->type == "\\OR" || c->type == "\\XOR" || c->type == "\\NAND" || c->type == "\\NOR" || c->type == "\\XNOR") { + 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("\\Y", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)])); + design->select(module, cell); + continue; + } + if (c->type == "\\MUX") { + RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_MUX_"); + 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("\\S", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\S").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 == "\\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 == "\\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 == "\\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("\\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; + } + 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") { + 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; + } + } + + 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()) { + if (!conn.first.is_fully_const()) + conn.first = RTLIL::SigSpec(module->wires_[remap_name(conn.first.as_wire()->name)]); + if (!conn.second.is_fully_const()) + conn.second = RTLIL::SigSpec(module->wires_[remap_name(conn.second.as_wire()->name)]); + module->connect(conn); + } + + for (auto &it : cell_stats) + log("ABC RESULTS: %15s cells: %8d\n", it.first.c_str(), it.second); + int in_wires = 0, out_wires = 0; + for (auto &si : signal_list) + if (si.is_port) { + char buffer[100]; + snprintf(buffer, 100, "\\n%d", si.id); + RTLIL::SigSig conn; + if (si.type != G(NONE)) { + conn.first = si.bit; + conn.second = RTLIL::SigSpec(module->wires_[remap_name(buffer)]); + out_wires++; + } else { + conn.first = RTLIL::SigSpec(module->wires_[remap_name(buffer)]); + conn.second = si.bit; + in_wires++; + } + module->connect(conn); + } + log("ABC RESULTS: internal signals: %8d\n", int(signal_list.size()) - in_wires - out_wires); + log("ABC RESULTS: input signals: %8d\n", in_wires); + log("ABC RESULTS: output signals: %8d\n", out_wires); + + delete mapped_design; + } + else + { + log("Don't call ABC as there is nothing to map.\n"); + } + + if (cleanup) + { + log("Removing temp directory.\n"); + remove_directory(tempdir_name); + } + + log_pop(); +} + +struct AbcPass : public Pass { + AbcPass() : Pass("abc", "use ABC for technology mapping") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" abc [options] [selection]\n"); + log("\n"); + log("This pass uses the ABC tool [1] for technology mapping of yosys's internal gate\n"); + log("library to a target architecture.\n"); + log("\n"); + log(" -exe <command>\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 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"); + log(" if no -script parameter is given, the following scripts are used:\n"); + log("\n"); + log(" for -liberty without -constr:\n"); + log("%s\n", fold_abc_cmd(ABC_COMMAND_LIB).c_str()); + log("\n"); + log(" for -liberty with -constr:\n"); + log("%s\n", fold_abc_cmd(ABC_COMMAND_CTR).c_str()); + log("\n"); + log(" for -lut/-luts (only one LUT size):\n"); + 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"); + log(" -fast\n"); + log(" use different default scripts that are slightly faster (at the cost\n"); + log(" of output quality):\n"); + log("\n"); + log(" for -liberty without -constr:\n"); + log("%s\n", fold_abc_cmd(ABC_FAST_COMMAND_LIB).c_str()); + log("\n"); + log(" for -liberty with -constr:\n"); + log("%s\n", fold_abc_cmd(ABC_FAST_COMMAND_CTR).c_str()); + log("\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"); + log(" -liberty <file>\n"); + log(" generate netlists for the specified cell library (using the liberty\n"); + log(" file format).\n"); + log("\n"); + log(" -constr <file>\n"); + log(" pass this file with timing constraints to ABC. use with -liberty.\n"); + log("\n"); + log(" a constr file contains two lines:\n"); + log(" set_driving_cell <cell_name>\n"); + log(" set_load <floating_point_number>\n"); + log("\n"); + log(" the set_driving_cell statement defines which cell type is assumed to\n"); + log(" drive the primary inputs and the set_load statement sets the load in\n"); + log(" femtofarads for each primary output.\n"); + log("\n"); + log(" -D <picoseconds>\n"); + 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"); + log(" -lut <w1>:<w2>\n"); + log(" generate netlist using luts of (max) the specified width <w2>. All\n"); + log(" luts with width <= <w1> have constant cost. for luts larger than <w1>\n"); + 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"); + log(" domain is passed through ABC independently.\n"); + log("\n"); + log(" -clk [!]<clock-signal-name>[,[!]<enable-signal-name>]\n"); + log(" use only the specified clock domain. this is like -dff, but only FF\n"); + log(" cells that belong to the specified clock domain are used.\n"); + log("\n"); + log(" -keepff\n"); + log(" set the \"keep\" attribute on flip-flop output wires. (and thus preserve\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"); + log(" are not removed. this is useful for debugging.\n"); + log("\n"); + log(" -showtmp\n"); + log(" print the temp dir name in log. usually this is suppressed so that the\n"); + log(" command output is identical across runs.\n"); + log("\n"); + log(" -markgroups\n"); + log(" set a 'abcgroup' attribute on all objects created by ABC. The value of\n"); + log(" this attribute is a unique integer for each ABC process started. This\n"); + log(" is useful for debugging the partitioning of clock domains.\n"); + log("\n"); + log("When neither -liberty nor -lut is used, the Yosys standard cell library is\n"); + log("loaded into ABC before the ABC script is executed.\n"); + log("\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("[1] http://www.eecs.berkeley.edu/~alanmi/abc/\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + 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"; +#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, 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]; + if (!getcwd(pwd, sizeof(pwd))) { + log_cmd_error("getcwd failed: %s\n", strerror(errno)); + log_abort(); + } + for (argidx = 1; argidx < args.size(); argidx++) { + std::string arg = args[argidx]; + if (arg == "-exe" && argidx+1 < args.size()) { + exe_file = args[++argidx]; + continue; + } + if (arg == "-script" && argidx+1 < args.size()) { + script_file = args[++argidx]; + 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() && !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() && !is_absolute_path(constr_file)) + constr_file = std::string(pwd) + "/" + constr_file; + continue; + } + if (arg == "-D" && argidx+1 < args.size()) { + 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()); + } else { + 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") { + fast_mode = true; + continue; + } + if (arg == "-dff") { + dff_mode = true; + continue; + } + if (arg == "-clk" && argidx+1 < args.size()) { + clk_str = args[++argidx]; + dff_mode = true; + continue; + } + if (arg == "-keepff") { + keepff = true; + continue; + } + if (arg == "-nocleanup") { + cleanup = false; + continue; + } + if (arg == "-showtmp") { + show_tempdir = true; + continue; + } + if (arg == "-markgroups") { + markgroups = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + 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"); + + for (auto mod : design->selected_modules()) + if (mod->processes.size() > 0) + log("Skipping module %s as it contains processes.\n", log_id(mod)); + else if (!dff_mode || !clk_str.empty()) + abc_module(design, mod, script_file, exe_file, liberty_file, constr_file, cleanup, lut_costs, dff_mode, clk_str, keepff, + delay_target, sop_inputs, sop_products, fast_mode, mod->selected_cells(), show_tempdir, sop_mode); + else + { + assign_map.set(mod); + CellTypes ct(design); + + std::vector<RTLIL::Cell*> all_cells = mod->selected_cells(); + std::set<RTLIL::Cell*> unassigned_cells(all_cells.begin(), all_cells.end()); + + std::set<RTLIL::Cell*> expand_queue, next_expand_queue; + std::set<RTLIL::Cell*> expand_queue_up, next_expand_queue_up; + std::set<RTLIL::Cell*> expand_queue_down, next_expand_queue_down; + + 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; + + std::map<RTLIL::Cell*, std::set<RTLIL::SigBit>> cell_to_bit, cell_to_bit_up, cell_to_bit_down; + std::map<RTLIL::SigBit, std::set<RTLIL::Cell*>> bit_to_cell, bit_to_cell_up, bit_to_cell_down; + + for (auto cell : all_cells) + { + clkdomain_t key; + + for (auto &conn : cell->connections()) + for (auto bit : conn.second) { + bit = assign_map(bit); + if (bit.wire != nullptr) { + cell_to_bit[cell].insert(bit); + bit_to_cell[bit].insert(cell); + if (ct.cell_input(cell->type, conn.first)) { + cell_to_bit_up[cell].insert(bit); + bit_to_cell_down[bit].insert(cell); + } + if (ct.cell_output(cell->type, conn.first)) { + cell_to_bit_down[cell].insert(bit); + bit_to_cell_up[bit].insert(cell); + } + } + } + + if (cell->type == "$_DFF_N_" || cell->type == "$_DFF_P_") + { + key = clkdomain_t(cell->type == "$_DFF_P_", assign_map(cell->getPort("\\C")), true, RTLIL::SigSpec()); + } + else + if (cell->type == "$_DFFE_NN_" || cell->type == "$_DFFE_NP_" || cell->type == "$_DFFE_PN_" || cell->type == "$_DFFE_PP_") + { + bool this_clk_pol = cell->type == "$_DFFE_PN_" || cell->type == "$_DFFE_PP_"; + bool this_en_pol = cell->type == "$_DFFE_NP_" || cell->type == "$_DFFE_PP_"; + key = clkdomain_t(this_clk_pol, assign_map(cell->getPort("\\C")), this_en_pol, assign_map(cell->getPort("\\E"))); + } + else + continue; + + unassigned_cells.erase(cell); + expand_queue.insert(cell); + expand_queue_up.insert(cell); + expand_queue_down.insert(cell); + + assigned_cells[key].push_back(cell); + assigned_cells_reverse[cell] = key; + } + + while (!expand_queue_up.empty() || !expand_queue_down.empty()) + { + if (!expand_queue_up.empty()) + { + RTLIL::Cell *cell = *expand_queue_up.begin(); + clkdomain_t key = assigned_cells_reverse.at(cell); + expand_queue_up.erase(cell); + + for (auto bit : cell_to_bit_up[cell]) + for (auto c : bit_to_cell_up[bit]) + if (unassigned_cells.count(c)) { + unassigned_cells.erase(c); + next_expand_queue_up.insert(c); + assigned_cells[key].push_back(c); + assigned_cells_reverse[c] = key; + expand_queue.insert(c); + } + } + + if (!expand_queue_down.empty()) + { + RTLIL::Cell *cell = *expand_queue_down.begin(); + clkdomain_t key = assigned_cells_reverse.at(cell); + expand_queue_down.erase(cell); + + for (auto bit : cell_to_bit_down[cell]) + for (auto c : bit_to_cell_down[bit]) + if (unassigned_cells.count(c)) { + unassigned_cells.erase(c); + next_expand_queue_up.insert(c); + assigned_cells[key].push_back(c); + assigned_cells_reverse[c] = key; + expand_queue.insert(c); + } + } + + if (expand_queue_up.empty() && expand_queue_down.empty()) { + expand_queue_up.swap(next_expand_queue_up); + expand_queue_down.swap(next_expand_queue_down); + } + } + + while (!expand_queue.empty()) + { + RTLIL::Cell *cell = *expand_queue.begin(); + clkdomain_t key = assigned_cells_reverse.at(cell); + expand_queue.erase(cell); + + for (auto bit : cell_to_bit.at(cell)) { + for (auto c : bit_to_cell[bit]) + if (unassigned_cells.count(c)) { + unassigned_cells.erase(c); + next_expand_queue.insert(c); + assigned_cells[key].push_back(c); + assigned_cells_reverse[c] = key; + } + bit_to_cell[bit].clear(); + } + + if (expand_queue.empty()) + expand_queue.swap(next_expand_queue); + } + + clkdomain_t key(true, RTLIL::SigSpec(), true, RTLIL::SigSpec()); + for (auto cell : unassigned_cells) { + assigned_cells[key].push_back(cell); + assigned_cells_reverse[cell] = key; + } + + 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)), + std::get<2>(it.first) ? "" : "!", log_signal(std::get<3>(it.first))); + + for (auto &it : assigned_cells) { + clk_polarity = std::get<0>(it.first); + clk_sig = assign_map(std::get<1>(it.first)); + en_polarity = std::get<2>(it.first); + en_sig = assign_map(std::get<3>(it.first)); + abc_module(design, mod, script_file, exe_file, liberty_file, constr_file, cleanup, lut_costs, !clk_sig.empty(), "$", + keepff, delay_target, sop_inputs, sop_products, fast_mode, it.second, show_tempdir, sop_mode); + assign_map.set(mod); + } + } + + assign_map.clear(); + signal_list.clear(); + signal_map.clear(); + + 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 |