aboutsummaryrefslogtreecommitdiffstats
path: root/passes
diff options
context:
space:
mode:
Diffstat (limited to 'passes')
-rw-r--r--passes/cmds/Makefile.inc3
-rw-r--r--passes/cmds/edgetypes.cc106
-rw-r--r--passes/cmds/plugin.cc2
-rw-r--r--passes/cmds/qwp.cc840
-rw-r--r--passes/cmds/scc.cc4
-rw-r--r--passes/cmds/show.cc2
-rw-r--r--passes/cmds/splice.cc2
-rw-r--r--passes/cmds/splitnets.cc25
-rw-r--r--passes/cmds/stat.cc4
-rw-r--r--passes/cmds/torder.cc123
-rw-r--r--passes/equiv/Makefile.inc3
-rw-r--r--passes/equiv/equiv_add.cc125
-rw-r--r--passes/equiv/equiv_induct.cc8
-rw-r--r--passes/equiv/equiv_make.cc2
-rw-r--r--passes/equiv/equiv_mark.cc239
-rw-r--r--passes/equiv/equiv_miter.cc2
-rw-r--r--passes/equiv/equiv_purge.cc210
-rw-r--r--passes/equiv/equiv_simple.cc6
-rw-r--r--passes/equiv/equiv_struct.cc336
-rw-r--r--passes/fsm/fsm_detect.cc42
-rw-r--r--passes/fsm/fsm_opt.cc2
-rw-r--r--passes/fsm/fsmdata.h2
-rw-r--r--passes/hierarchy/Makefile.inc1
-rw-r--r--passes/hierarchy/hierarchy.cc10
-rw-r--r--passes/hierarchy/singleton.cc101
-rw-r--r--passes/memory/memory_bram.cc39
-rw-r--r--passes/memory/memory_collect.cc39
-rw-r--r--passes/memory/memory_dff.cc65
-rw-r--r--passes/memory/memory_map.cc33
-rw-r--r--passes/memory/memory_share.cc2
-rw-r--r--passes/memory/memory_unpack.cc1
-rw-r--r--passes/opt/opt_const.cc44
-rw-r--r--passes/opt/opt_muxtree.cc2
-rw-r--r--passes/opt/opt_rmdff.cc20
-rw-r--r--passes/opt/share.cc62
-rw-r--r--passes/opt/wreduce.cc65
-rw-r--r--passes/proc/proc_mux.cc189
-rw-r--r--passes/sat/eval.cc2
-rw-r--r--passes/sat/expose.cc20
-rw-r--r--passes/sat/freduce.cc2
-rw-r--r--passes/sat/sat.cc51
-rw-r--r--passes/techmap/Makefile.inc2
-rw-r--r--passes/techmap/abc.cc67
-rw-r--r--passes/techmap/alumacc.cc16
-rw-r--r--passes/techmap/dffinit.cc26
-rw-r--r--passes/techmap/dfflibmap.cc1
-rw-r--r--passes/techmap/extract.cc4
-rw-r--r--passes/techmap/iopadmap.cc6
-rw-r--r--passes/techmap/lut2mux.cc93
-rw-r--r--passes/techmap/maccmap.cc2
-rw-r--r--passes/techmap/muxcover.cc6
-rw-r--r--passes/techmap/nlutmap.cc173
-rw-r--r--passes/techmap/simplemap.cc4
-rw-r--r--passes/techmap/techmap.cc4
-rw-r--r--passes/tests/test_cell.cc13
55 files changed, 2978 insertions, 275 deletions
diff --git a/passes/cmds/Makefile.inc b/passes/cmds/Makefile.inc
index e4b40c41c..01ada7739 100644
--- a/passes/cmds/Makefile.inc
+++ b/passes/cmds/Makefile.inc
@@ -14,6 +14,7 @@ OBJS += passes/cmds/setattr.o
OBJS += passes/cmds/copy.o
OBJS += passes/cmds/splice.o
OBJS += passes/cmds/scc.o
+OBJS += passes/cmds/torder.o
OBJS += passes/cmds/logcmd.o
OBJS += passes/cmds/tee.o
OBJS += passes/cmds/write_file.o
@@ -22,4 +23,6 @@ OBJS += passes/cmds/cover.o
OBJS += passes/cmds/trace.o
OBJS += passes/cmds/plugin.o
OBJS += passes/cmds/check.o
+OBJS += passes/cmds/qwp.o
+OBJS += passes/cmds/edgetypes.o
diff --git a/passes/cmds/edgetypes.cc b/passes/cmds/edgetypes.cc
new file mode 100644
index 000000000..7b75a009f
--- /dev/null
+++ b/passes/cmds/edgetypes.cc
@@ -0,0 +1,106 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct EdgetypePass : public Pass {
+ EdgetypePass() : Pass("edgetypes", "list all types of edges in selection") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" edgetypes [options] [selection]\n");
+ log("\n");
+ log("This command lists all unique types of 'edges' found in the selection. An 'edge'\n");
+ log("is a 4-tuple of source and sink cell type and port name.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ // if (args[argidx] == "-ltr") {
+ // config.ltr = true;
+ // continue;
+ // }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ pool<string> edge_cache;
+
+ for (auto module : design->selected_modules())
+ {
+ SigMap sigmap(module);
+ dict<SigBit, pool<tuple<IdString, IdString, int>>> bit_sources, bit_sinks;
+ pool<std::pair<IdString, IdString>> multibit_ports;
+
+ for (auto cell : module->selected_cells())
+ for (auto conn : cell->connections())
+ {
+ IdString cell_type = cell->type;
+ IdString port_name = conn.first;
+ SigSpec sig = sigmap(conn.second);
+
+ if (GetSize(sig) > 1)
+ multibit_ports.insert(std::pair<IdString, IdString>(cell_type, port_name));
+
+ for (int i = 0; i < GetSize(sig); i++) {
+ if (cell->output(port_name))
+ bit_sources[sig[i]].insert(tuple<IdString, IdString, int>(cell_type, port_name, i));
+ if (cell->input(port_name))
+ bit_sinks[sig[i]].insert(tuple<IdString, IdString, int>(cell_type, port_name, i));
+ }
+ }
+
+ for (auto &it : bit_sources)
+ for (auto &source : it.second)
+ for (auto &sink : bit_sinks[it.first])
+ {
+ auto source_cell_type = std::get<0>(source);
+ auto source_port_name = std::get<1>(source);
+ auto source_bit_index = std::get<2>(source);
+
+ auto sink_cell_type = std::get<0>(sink);
+ auto sink_port_name = std::get<1>(sink);
+ auto sink_bit_index = std::get<2>(sink);
+
+ string source_str = multibit_ports.count(std::pair<IdString, IdString>(source_cell_type, source_port_name)) ?
+ stringf("%s.%s[%d]", log_id(source_cell_type), log_id(source_port_name), source_bit_index) :
+ stringf("%s.%s", log_id(source_cell_type), log_id(source_port_name));
+
+ string sink_str = multibit_ports.count(std::pair<IdString, IdString>(sink_cell_type, sink_port_name)) ?
+ stringf("%s.%s[%d]", log_id(sink_cell_type), log_id(sink_port_name), sink_bit_index) :
+ stringf("%s.%s", log_id(sink_cell_type), log_id(sink_port_name));
+
+ edge_cache.insert(source_str + " " + sink_str);
+ }
+ }
+
+ edge_cache.sort();
+ for (auto &str : edge_cache)
+ log("%s\n", str.c_str());
+ }
+} EdgetypePass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/plugin.cc b/passes/cmds/plugin.cc
index f7c65bbde..e2d80d9bf 100644
--- a/passes/cmds/plugin.cc
+++ b/passes/cmds/plugin.cc
@@ -115,7 +115,7 @@ struct PluginPass : public Pass {
log("\n");
int max_alias_len = 1;
for (auto &it : loaded_plugin_aliases)
- max_alias_len = std::max(max_alias_len, GetSize(it.first));
+ max_alias_len = max(max_alias_len, GetSize(it.first));
for (auto &it : loaded_plugin_aliases)
log("Alias: %-*s %s\n", max_alias_len, it.first.c_str(), it.second.c_str());
}
diff --git a/passes/cmds/qwp.cc b/passes/cmds/qwp.cc
new file mode 100644
index 000000000..8ec815a72
--- /dev/null
+++ b/passes/cmds/qwp.cc
@@ -0,0 +1,840 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
+
+#undef LOG_MATRICES
+#undef PYPLOT_EDGES
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+static uint32_t xorshift32_state;
+
+static double xorshift32()
+{
+ xorshift32_state ^= xorshift32_state << 13;
+ xorshift32_state ^= xorshift32_state >> 17;
+ xorshift32_state ^= xorshift32_state << 5;
+ return (xorshift32_state % 1000000) / 1e6;
+}
+
+struct QwpConfig
+{
+ bool ltr;
+ bool alpha;
+ double grid;
+
+ std::ofstream dump_file;
+
+ QwpConfig() {
+ ltr = false;
+ alpha = false;
+ grid = 1.0 / 16;
+ }
+};
+
+struct QwpWorker
+{
+ QwpConfig &config;
+ Module *module;
+ char direction;
+
+ struct Node {
+ Cell *cell;
+ bool tied, alt_tied;
+
+ // pos = position in current direction
+ // alt_pos = position in the other direction
+ double pos, alt_pos;
+
+ Node() {
+ cell = nullptr;
+ tied = false;
+ pos = xorshift32();
+ alt_tied = false;
+ alt_pos = xorshift32();
+ }
+
+ void tie(double v) {
+ tied = true;
+ pos = v;
+ }
+
+ void alt_tie(double v) {
+ alt_tied = true;
+ alt_pos = v;
+ }
+
+ void swap_alt() {
+ std::swap(tied, alt_tied);
+ std::swap(pos, alt_pos);
+ }
+
+ void proj_left(double midpos) {
+ cell = nullptr;
+ tie(pos > midpos ? midpos : pos);
+ }
+
+ void proj_right(double midpos) {
+ cell = nullptr;
+ tie(pos < midpos ? midpos : pos);
+ }
+ };
+
+ vector<Node> nodes;
+ dict<pair<int, int>, double> edges;
+ dict<Cell*, int> cell_to_node;
+
+ // worker state variables
+ double midpos;
+ double radius;
+ double alt_midpos;
+ double alt_radius;
+
+ QwpWorker(QwpConfig &config, Module *module, char direction = 'x') : config(config), module(module), direction(direction)
+ {
+ log_assert(direction == 'x' || direction == 'y');
+ }
+
+ void load_module()
+ {
+ log_assert(direction == 'x');
+
+ SigMap sigmap(module);
+ dict<SigBit, pool<int>> bits_to_nodes;
+
+ if (config.ltr || config.alpha)
+ {
+ dict<Wire*, double> alpha_inputs, alpha_outputs;
+
+ if (config.alpha)
+ {
+ dict<string, Wire*> alpha_order;
+
+ for (auto wire : module->wires()) {
+ if (wire->port_input || wire->port_output)
+ alpha_order[wire->name.str()] = wire;
+ }
+
+ alpha_order.sort();
+
+ for (auto &it : alpha_order) {
+ if (it.second->port_input) {
+ int idx = GetSize(alpha_inputs);
+ alpha_inputs[it.second] = idx + 0.5;
+ }
+ if (it.second->port_output) {
+ int idx = GetSize(alpha_outputs);
+ alpha_outputs[it.second] = idx + 0.5;
+ }
+ }
+ }
+
+ for (auto wire : module->wires())
+ {
+ if (!wire->port_input && !wire->port_output)
+ continue;
+
+ int idx = GetSize(nodes);
+ nodes.push_back(Node());
+
+ if (config.ltr) {
+ if (wire->port_input)
+ nodes[idx].tie(0.0);
+ else
+ nodes[idx].tie(1.0);
+ }
+
+ if (config.alpha) {
+ if (wire->port_input)
+ nodes[idx].alt_tie(alpha_inputs.at(wire) / GetSize(alpha_inputs));
+ else
+ nodes[idx].alt_tie(alpha_outputs.at(wire) / GetSize(alpha_outputs));
+ }
+
+ for (auto bit : sigmap(wire))
+ bits_to_nodes[bit].insert(idx);
+ }
+ }
+
+ for (auto cell : module->selected_cells())
+ {
+ log_assert(cell_to_node.count(cell) == 0);
+ int idx = GetSize(nodes);
+ nodes.push_back(Node());
+
+ cell_to_node[cell] = GetSize(nodes);
+ nodes[idx].cell = cell;
+
+ for (auto &conn : cell->connections())
+ for (auto bit : sigmap(conn.second))
+ bits_to_nodes[bit].insert(idx);
+ }
+
+ for (auto &it : bits_to_nodes)
+ {
+ if (GetSize(it.second) > 100)
+ continue;
+
+ for (int idx1 : it.second)
+ for (int idx2 : it.second)
+ if (idx1 < idx2)
+ edges[pair<int, int>(idx1, idx2)] += 1.0 / GetSize(it.second);
+ }
+ }
+
+ void solve(bool alt_mode = false)
+ {
+ // A := constraint_matrix
+ // y := constraint_rhs_vector
+ //
+ // AA = A' * A
+ // Ay = A' * y
+ //
+ // M := [AA Ay]
+
+ // Row major order
+ int N = GetSize(nodes), N1 = N+1;
+ vector<double> M(N * N1);
+
+ // Edge constraints:
+ // A[i,:] := [ 0 0 .... 0 weight 0 ... 0 -weight 0 ... 0 0], y[i] := 0
+ //
+ // i.e. nonzero columns in A[i,:] at the two node indices.
+ for (auto &edge : edges)
+ {
+ int idx1 = edge.first.first;
+ int idx2 = edge.first.second;
+ double weight = edge.second * (1.0 + xorshift32() * 1e-3);
+
+ M[idx1 + idx1*N1] += weight * weight;
+ M[idx2 + idx2*N1] += weight * weight;
+
+ M[idx1 + idx2*N1] += -weight * weight;
+ M[idx2 + idx1*N1] += -weight * weight;
+ }
+
+ // Node constraints:
+ // A[i,:] := [ 0 0 .... 0 weight 0 ... 0 0], y[i] := weight * pos
+ //
+ // i.e. nonzero column in A[i,:] at the node index
+ //
+ // "tied" nodes have a large weight, pinning them in position. Untied
+ // nodes have a small weight, giving then a tiny preference to stay at
+ // the current position, making sure that AA never is singular.
+ for (int idx = 0; idx < GetSize(nodes); idx++)
+ {
+ auto &node = nodes[idx];
+ double rhs = (alt_mode ? node.alt_pos : node.pos);
+
+ double weight = 1e-3;
+ if (alt_mode ? node.alt_tied : node.tied)
+ weight = 1e3;
+ weight *= (1.0 + xorshift32() * 1e-3);
+
+ M[idx + idx*N1] += weight * weight;
+ M[N + idx*N1] += rhs * weight * weight;
+ }
+
+#ifdef LOG_MATRICES
+ log("\n");
+ for (int i = 0; i < N; i++) {
+ for (int j = 0; j < N+1; j++)
+ log(" %10.2e", M[(N+1)*i + j]);
+ log("\n");
+ }
+#endif
+
+ // Solve "AA*x = Ay"
+ // (least squares fit for "A*x = y")
+ //
+ // Using gaussian elimination to get M := [Id x]
+
+ vector<int> pivot_cache;
+ vector<int> queue;
+
+ for (int i = 0; i < N; i++)
+ queue.push_back(i);
+
+ // gaussian elimination
+ for (int i = 0; i < N; i++)
+ {
+ // find best row
+ int best_row = queue.front();
+ int best_row_queue_idx = 0;
+ double best_row_absval = 0;
+
+ for (int k = 0; k < GetSize(queue); k++) {
+ int row = queue[k];
+ double absval = fabs(M[i + row*N1]);
+ if (absval > best_row_absval) {
+ best_row = row;
+ best_row_queue_idx = k;
+ best_row_absval = absval;
+ }
+ }
+
+ int row = best_row;
+ pivot_cache.push_back(row);
+
+ queue[best_row_queue_idx] = queue.back();
+ queue.pop_back();
+
+ // normalize row
+ for (int k = i+1; k < N1; k++)
+ M[k + row*N1] /= M[i + row*N1];
+ M[i + row*N1] = 1.0;
+
+ // elimination
+ for (int other_row : queue) {
+ double d = M[i + other_row*N1];
+ for (int k = i+1; k < N1; k++)
+ M[k + other_row*N1] -= d*M[k + row*N1];
+ M[i + other_row*N1] = 0.0;
+ }
+ }
+
+ log_assert(queue.empty());
+ log_assert(GetSize(pivot_cache) == N);
+
+ // back substitution
+ for (int i = N-1; i >= 0; i--)
+ for (int j = i+1; j < N; j++)
+ {
+ int row = pivot_cache[i];
+ int other_row = pivot_cache[j];
+ M[N + row*N1] -= M[j + row*N1] * M[N + other_row*N1];
+ M[j + row*N1] = 0.0;
+ }
+
+#ifdef LOG_MATRICES
+ log("\n");
+ for (int i = 0; i < N; i++) {
+ for (int j = 0; j < N+1; j++)
+ log(" %10.2e", M[(N+1)*i + j]);
+ log("\n");
+ }
+#endif
+
+ // update node positions
+ for (int i = 0; i < N; i++)
+ {
+ double v = M[(N+1)*i + N];
+ double c = alt_mode ? alt_midpos : midpos;
+ double r = alt_mode ? alt_radius : radius;
+
+ if (std::isfinite(v)) {
+ v = min(v, c+r);
+ v = max(v, c-r);
+ } else {
+ v = c;
+ }
+
+ if (alt_mode) {
+ if (!nodes[i].alt_tied)
+ nodes[i].alt_pos = v;
+ } else {
+ if (!nodes[i].tied)
+ nodes[i].pos = v;
+ }
+ }
+ }
+
+ void log_cell_coordinates(int indent, bool log_all_nodes = false)
+ {
+ for (auto &node : nodes)
+ {
+ if (node.cell == nullptr && !log_all_nodes)
+ continue;
+
+ for (int i = 0; i < indent; i++)
+ log(" ");
+
+ if (direction == 'x')
+ log("X=%.2f, Y=%.2f", node.pos, node.alt_pos);
+ else
+ log("X=%.2f, Y=%.2f", node.alt_pos, node.pos);
+
+ if (node.tied)
+ log(" [%c-tied]", direction);
+
+ if (node.alt_tied)
+ log(" [%c-tied]", direction == 'x' ? 'y' : 'x');
+
+ if (node.cell != nullptr)
+ log(" %s (%s)", log_id(node.cell), log_id(node.cell->type));
+ else
+ log(" (none)");
+
+ log("\n");
+ }
+ }
+
+ void dump_svg(const pool<int> *green_nodes = nullptr, double median = -1)
+ {
+ double x_center = direction == 'x' ? midpos : alt_midpos;
+ double y_center = direction == 'y' ? midpos : alt_midpos;
+
+ double x_radius = direction == 'x' ? radius : alt_radius;
+ double y_radius = direction == 'y' ? radius : alt_radius;
+
+ config.dump_file << stringf("<svg height=\"240\" width=\"470\">\n");
+ config.dump_file << stringf("<rect x=\"0\" y=\"0\" width=\"470\" height=\"240\" style=\"fill:rgb(250,250,200);\" />\n");
+ config.dump_file << stringf("<rect x=\"20\" y=\"20\" width=\"200\" height=\"200\" style=\"fill:rgb(200,200,200);\" />\n");
+ config.dump_file << stringf("<rect x=\"250\" y=\"20\" width=\"200\" height=\"200\" style=\"fill:rgb(200,200,200);\" />\n");
+
+ double win_x = 250 + 200 * (direction == 'x' ? midpos - radius : alt_midpos - alt_radius);
+ double win_y = 20 + 200 * (direction == 'y' ? midpos - radius : alt_midpos - alt_radius);
+
+ double win_w = 200 * (direction == 'x' ? 2*radius : 2*alt_radius);
+ double win_h = 200 * (direction == 'y' ? 2*radius : 2*alt_radius);
+
+ config.dump_file << stringf("<rect x=\"%.2f\" y=\"%.2f\" width=\"%.2f\" height=\"%.2f\" "
+ "style=\"stroke:rgb(0,0,0);stroke-width:1;fill:none\" />\n", win_x, win_y, win_w, win_h);
+
+ if (median >= 0)
+ {
+ double x1 = 20.0, x2 = 220.0, y1 = 20.0, y2 = 220.0;
+
+ if (direction == 'x')
+ x1 = x2 = 120 + 100 * (median - x_center) / x_radius;
+ else
+ y1 = y2 = 120 + 100 * (median - y_center) / y_radius;
+
+ config.dump_file << stringf("<line x1=\"%.2f\" y1=\"%.2f\" x2=\"%.2f\" y2=\"%.2f\" "
+ "style=\"stroke:rgb(150,0,150);stroke-width:1\" />\n", x1, y1, x2, y2);
+ }
+
+ for (auto &edge : edges)
+ {
+ auto &node1 = nodes[edge.first.first];
+ auto &node2 = nodes[edge.first.second];
+
+ double x1 = direction == 'x' ? node1.pos : node1.alt_pos;
+ double y1 = direction == 'y' ? node1.pos : node1.alt_pos;
+
+ double x2 = direction == 'x' ? node2.pos : node2.alt_pos;
+ double y2 = direction == 'y' ? node2.pos : node2.alt_pos;
+
+ x1 = 120 + 100 * (x1 - x_center) / x_radius;
+ y1 = 120 + 100 * (y1 - y_center) / y_radius;
+
+ x2 = 120 + 100 * (x2 - x_center) / x_radius;
+ y2 = 120 + 100 * (y2 - y_center) / y_radius;
+
+ config.dump_file << stringf("<line x1=\"%.2f\" y1=\"%.2f\" x2=\"%.2f\" y2=\"%.2f\" "
+ "style=\"stroke:rgb(0,0,0);stroke-width:1\" />\n", x1, y1, x2, y2);
+ }
+
+ for (int i = 0; i < GetSize(nodes); i++)
+ {
+ auto &node = nodes[i];
+
+ double x = direction == 'x' ? node.pos : node.alt_pos;
+ double y = direction == 'y' ? node.pos : node.alt_pos;
+
+ x = 120 + 100 * (x - x_center) / x_radius;
+ y = 120 + 100 * (y - y_center) / y_radius;
+
+ const char *color = node.cell == nullptr ? "blue" : "red";
+
+ if (green_nodes != nullptr && green_nodes->count(i))
+ color = "green";
+
+ config.dump_file << stringf("<circle cx=\"%.2f\" cy=\"%.2f\" r=\"3\" fill=\"%s\"/>\n", x, y, color);
+ }
+
+ config.dump_file << stringf("</svg>\n");
+ }
+
+ void run_worker(int indent)
+ {
+ int count_cells = 0;
+
+ for (auto &node : nodes)
+ if (node.cell != nullptr)
+ count_cells++;
+
+ for (int i = 0; i < indent; i++)
+ log(" ");
+
+ string range_str;
+
+ if (direction == 'x')
+ range_str = stringf("X=%.2f:%.2f, Y=%.2f:%.2f",
+ midpos - radius, midpos + radius,
+ alt_midpos - alt_radius, alt_midpos + alt_radius);
+ else
+ range_str = stringf("X=%.2f:%.2f, Y=%.2f:%.2f",
+ alt_midpos - alt_radius, alt_midpos + alt_radius,
+ midpos - radius, midpos + radius);
+
+ log("%c-qwp on %s with %d cells, %d nodes, and %d edges.\n", direction,
+ range_str.c_str(), count_cells, GetSize(nodes), GetSize(edges));
+
+ solve();
+ solve(true);
+
+ // detect median position and check for break condition
+
+ vector<pair<double, int>> sorted_pos;
+ for (int i = 0; i < GetSize(nodes); i++)
+ if (nodes[i].cell != nullptr)
+ sorted_pos.push_back(pair<double, int>(nodes[i].pos, i));
+
+ std::sort(sorted_pos.begin(), sorted_pos.end());
+ int median_sidx = GetSize(sorted_pos)/2;
+ double median = sorted_pos[median_sidx].first;
+
+ double left_scale = radius / (median - (midpos - radius));
+ double right_scale = radius / ((midpos + radius) - median);
+
+ if (config.dump_file.is_open())
+ {
+ config.dump_file << stringf("<h4>LSQ %c-Solution for %s:</h4>\n", direction, range_str.c_str());
+
+ pool<int> green_nodes;
+ for (int i = 0; i < median_sidx; i++)
+ green_nodes.insert(sorted_pos[i].second);
+
+ dump_svg(&green_nodes, median);
+ }
+
+ for (auto &node : nodes)
+ {
+ double rel_pos = node.pos - median;
+ if (rel_pos < 0) {
+ node.pos = midpos + left_scale*rel_pos;
+ if (std::isfinite(node.pos)) {
+ node.pos = min(node.pos, midpos);
+ node.pos = max(node.pos, midpos - radius);
+ } else
+ node.pos = midpos - radius/2;
+ } else {
+ node.pos = midpos + right_scale*rel_pos;
+ if (std::isfinite(node.pos)) {
+ node.pos = max(node.pos, midpos);
+ node.pos = min(node.pos, midpos + radius);
+ } else
+ node.pos = midpos + radius/2;
+ }
+ }
+
+ if (GetSize(sorted_pos) < 2 || (2*radius <= config.grid && 2*alt_radius <= config.grid)) {
+ log_cell_coordinates(indent + 1);
+ return;
+ }
+
+ // create child workers
+
+ char child_direction = direction == 'x' ? 'y' : 'x';
+
+ QwpWorker left_worker(config, module, child_direction);
+ QwpWorker right_worker(config, module, child_direction);
+
+ // duplicate nodes into child workers
+
+ dict<int, int> left_nodes, right_nodes;
+
+ for (int k = 0; k < GetSize(sorted_pos); k++)
+ {
+ int i = sorted_pos[k].second;
+
+ if (k < median_sidx) {
+ left_nodes[i] = GetSize(left_worker.nodes);
+ left_worker.nodes.push_back(nodes[i]);
+ if (left_worker.nodes.back().pos > midpos)
+ left_worker.nodes.back().pos = midpos;
+ left_worker.nodes.back().swap_alt();
+ } else {
+ right_nodes[i] = GetSize(right_worker.nodes);
+ right_worker.nodes.push_back(nodes[i]);
+ if (right_worker.nodes.back().pos < midpos)
+ right_worker.nodes.back().pos = midpos;
+ right_worker.nodes.back().swap_alt();
+ }
+ }
+
+ // duplicate edges into child workers, project nodes as needed
+
+ for (auto &edge : edges)
+ {
+ int idx1 = edge.first.first;
+ int idx2 = edge.first.second;
+ double weight = edge.second;
+
+ if (nodes[idx1].cell == nullptr && nodes[idx2].cell == nullptr)
+ continue;
+
+ int left_idx1 = left_nodes.count(idx1) ? left_nodes.at(idx1) : -1;
+ int left_idx2 = left_nodes.count(idx2) ? left_nodes.at(idx2) : -1;
+
+ int right_idx1 = right_nodes.count(idx1) ? right_nodes.at(idx1) : -1;
+ int right_idx2 = right_nodes.count(idx2) ? right_nodes.at(idx2) : -1;
+
+ if (left_idx1 >= 0 && left_worker.nodes[left_idx1].cell && left_idx2 < 0) {
+ left_idx2 = left_nodes[idx2] = GetSize(left_worker.nodes);
+ left_worker.nodes.push_back(nodes[idx2]);
+ left_worker.nodes.back().proj_left(midpos);
+ left_worker.nodes.back().swap_alt();
+ } else
+ if (left_idx2 >= 0 && left_worker.nodes[left_idx2].cell && left_idx1 < 0) {
+ left_idx1 = left_nodes[idx1] = GetSize(left_worker.nodes);
+ left_worker.nodes.push_back(nodes[idx1]);
+ left_worker.nodes.back().proj_left(midpos);
+ left_worker.nodes.back().swap_alt();
+ }
+
+ if (right_idx1 >= 0 && right_worker.nodes[right_idx1].cell && right_idx2 < 0) {
+ right_idx2 = right_nodes[idx2] = GetSize(right_worker.nodes);
+ right_worker.nodes.push_back(nodes[idx2]);
+ right_worker.nodes.back().proj_right(midpos);
+ right_worker.nodes.back().swap_alt();
+ } else
+ if (right_idx2 >= 0 && right_worker.nodes[right_idx2].cell && right_idx1 < 0) {
+ right_idx1 = right_nodes[idx1] = GetSize(right_worker.nodes);
+ right_worker.nodes.push_back(nodes[idx1]);
+ right_worker.nodes.back().proj_right(midpos);
+ right_worker.nodes.back().swap_alt();
+ }
+
+ if (left_idx1 >= 0 && left_idx2 >= 0)
+ left_worker.edges[pair<int, int>(left_idx1, left_idx2)] += weight;
+
+ if (right_idx1 >= 0 && right_idx2 >= 0)
+ right_worker.edges[pair<int, int>(right_idx1, right_idx2)] += weight;
+ }
+
+ // run child workers
+
+ left_worker.midpos = right_worker.midpos = alt_midpos;
+ left_worker.radius = right_worker.radius = alt_radius;
+
+ left_worker.alt_midpos = midpos - radius/2;
+ right_worker.alt_midpos = midpos + radius/2;
+ left_worker.alt_radius = right_worker.alt_radius = radius/2;
+
+ left_worker.run_worker(indent+1);
+ right_worker.run_worker(indent+1);
+
+ // re-integrate results
+
+ for (auto &it : left_nodes)
+ if (left_worker.nodes[it.second].cell != nullptr) {
+ nodes[it.first].pos = left_worker.nodes[it.second].alt_pos;
+ nodes[it.first].alt_pos = left_worker.nodes[it.second].pos;
+ }
+
+ for (auto &it : right_nodes)
+ if (right_worker.nodes[it.second].cell != nullptr) {
+ nodes[it.first].pos = right_worker.nodes[it.second].alt_pos;
+ nodes[it.first].alt_pos = right_worker.nodes[it.second].pos;
+ }
+
+ if (config.dump_file.is_open()) {
+ config.dump_file << stringf("<h4>Final %c-Solution for %s:</h4>\n", direction, range_str.c_str());
+ dump_svg();
+ }
+ }
+
+ void histogram(const vector<double> &values)
+ {
+ if (values.empty()) {
+ log("no data\n");
+ return;
+ }
+
+ double min_value = values.front();
+ double max_value = values.front();
+
+ for (auto &v : values) {
+ min_value = min(min_value, v);
+ max_value = max(max_value, v);
+ }
+
+ if (fabs(max_value - min_value) < 0.001) {
+ log("all values in range %f .. %f\n", min_value, max_value);
+ return;
+ }
+
+ vector<int> buckets(60);
+ int max_bucket_val = 0;
+
+ for (auto &v : values) {
+ int idx = min(int(GetSize(buckets) * (v - min_value) / (max_value - min_value)), GetSize(buckets)-1);
+ max_bucket_val = max(max_bucket_val, ++buckets.at(idx));
+ }
+
+ for (int i = 4; i >= 0; i--) {
+ for (int k = 0; k < GetSize(buckets); k++) {
+ int v = 10 * buckets[k] / max_bucket_val;
+ if (v >= 2*i+1)
+ log(v == 2*i+1 ? "." : ":");
+ else
+ log(i == 0 ? (buckets[k] > 0 ? "," : "_") : " ");
+ }
+ log("\n");
+ }
+ log("%-30f%30f\n", min_value, max_value);
+ }
+
+ void run()
+ {
+ log("\n");
+ log("Running qwp on module %s..\n", log_id(module));
+
+ if (config.dump_file.is_open())
+ config.dump_file << stringf("<h3>QWP protocol for module %s:</h3>\n", log_id(module));
+
+ load_module();
+
+ midpos = 0.5;
+ radius = 0.5;
+ alt_midpos = 0.5;
+ alt_radius = 0.5;
+ run_worker(1);
+
+ for (auto &node : nodes)
+ if (node.cell != nullptr)
+ node.cell->attributes["\\qwp_position"] = stringf("%f %f", node.pos, node.alt_pos);
+
+ vector<double> edge_lengths;
+ vector<double> weighted_edge_lengths;
+
+ double total_edge_length = 0;
+ double total_weighted_edge_length = 0;
+
+ for (auto &edge : edges)
+ {
+ auto &node1 = nodes[edge.first.first];
+ auto &node2 = nodes[edge.first.second];
+
+ double distance = sqrt(pow(node1.pos - node2.pos, 2) + pow(node1.alt_pos - node2.alt_pos, 2));
+ double weighted_distance = distance * edge.second;
+
+ edge_lengths.push_back(distance);
+ weighted_edge_lengths.push_back(weighted_distance);
+
+ total_edge_length += distance;
+ total_weighted_edge_length += weighted_distance;
+ }
+
+ log("\n");
+ log("Summary for module %s:\n", log_id(module));
+ log("Number of edges: %d\n", GetSize(edges));
+ log("Total edge length: %f\n", total_edge_length);
+ log("Total weighted edge length: %f\n", total_weighted_edge_length);
+
+ log("\n");
+ log("Histogram over edge lengths:\n");
+ histogram(edge_lengths);
+
+ log("\n");
+ log("Histogram over weighted edge lengths:\n");
+ histogram(weighted_edge_lengths);
+ }
+};
+
+struct QwpPass : public Pass {
+ QwpPass() : Pass("qwp", "quadratic wirelength placer") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" qwp [options] [selection]\n");
+ log("\n");
+ log("This command runs quadratic wirelength placement on the selected modules and\n");
+ log("annotates the cells in the design with 'qwp_position' attributes.\n");
+ log("\n");
+ log(" -ltr\n");
+ log(" Add left-to-right constraints: constrain all inputs on the left border\n");
+ log(" outputs to the right border.\n");
+ log("\n");
+ log(" -alpha\n");
+ log(" Add constraints for inputs/outputs to be placed in alphanumerical\n");
+ log(" order along the y-axis (top-to-bottom).\n");
+ log("\n");
+ log(" -grid N\n");
+ log(" Number of grid divisions in x- and y-direction. (default=16)\n");
+ log("\n");
+ log(" -dump <html_file_name>\n");
+ log(" Dump a protocol of the placement algorithm to the html file.\n");
+ log("\n");
+ log("Note: This implementation of a quadratic wirelength placer uses exact\n");
+ log("dense matrix operations. It is only a toy-placer for small circuits.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ QwpConfig config;
+ xorshift32_state = 123456789;
+
+ log_header("Executing QWP pass (quadratic wirelength placer).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-ltr") {
+ config.ltr = true;
+ continue;
+ }
+ if (args[argidx] == "-alpha") {
+ config.alpha = true;
+ continue;
+ }
+ if (args[argidx] == "-grid" && argidx+1 < args.size()) {
+ config.grid = 1.0 / atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-dump" && argidx+1 < args.size()) {
+ config.dump_file.open(args[++argidx], std::ofstream::trunc);
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ {
+ QwpWorker worker(config, module);
+ worker.run();
+
+#ifdef PYPLOT_EDGES
+ log("\n");
+ log("plt.figure(figsize=(10, 10));\n");
+
+ for (auto &edge : worker.edges) {
+ log("plt.plot([%.2f, %.2f], [%.2f, %.2f], \"r-\");\n",
+ worker.nodes[edge.first.first].pos,
+ worker.nodes[edge.first.second].pos,
+ worker.nodes[edge.first.first].alt_pos,
+ worker.nodes[edge.first.second].alt_pos);
+ }
+
+ for (auto &node : worker.nodes) {
+ const char *style = node.cell != nullptr ? "ko" : "ks";
+ log("plt.plot([%.2f], [%.2f], \"%s\");\n", node.pos, node.alt_pos, style);
+ }
+#endif
+ }
+ }
+} QwpPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/scc.cc b/passes/cmds/scc.cc
index 43a43b4fc..532026f26 100644
--- a/passes/cmds/scc.cc
+++ b/passes/cmds/scc.cc
@@ -69,10 +69,10 @@ struct SccWorker
for (auto nextCell : cellToNextCell[cell])
if (cellLabels.count(nextCell) == 0) {
run(nextCell, depth+1, maxDepth);
- cellLabels[cell].second = std::min(cellLabels[cell].second, cellLabels[nextCell].second);
+ cellLabels[cell].second = min(cellLabels[cell].second, cellLabels[nextCell].second);
} else
if (cellsOnStack.count(nextCell) > 0 && (maxDepth < 0 || cellDepth[nextCell] + maxDepth > depth)) {
- cellLabels[cell].second = std::min(cellLabels[cell].second, cellLabels[nextCell].second);
+ cellLabels[cell].second = min(cellLabels[cell].second, cellLabels[nextCell].second);
}
if (cellLabels[cell].first == cellLabels[cell].second)
diff --git a/passes/cmds/show.cc b/passes/cmds/show.cc
index 3035e7301..3b03d6802 100644
--- a/passes/cmds/show.cc
+++ b/passes/cmds/show.cc
@@ -552,7 +552,7 @@ struct ShowWorker
continue;
if (design->selected_whole_module(module->name)) {
if (module->get_bool_attribute("\\blackbox")) {
- log("Skipping blackbox module %s.\n", id2cstr(module->name));
+ // log("Skipping blackbox module %s.\n", id2cstr(module->name));
continue;
} else
if (module->cells_.empty() && module->connections().empty() && module->processes.empty()) {
diff --git a/passes/cmds/splice.cc b/passes/cmds/splice.cc
index 4ce2ec11c..2556fb740 100644
--- a/passes/cmds/splice.cc
+++ b/passes/cmds/splice.cc
@@ -64,7 +64,7 @@ struct SpliceWorker
return sliced_signals_cache.at(sig);
int offset = 0;
- int p = driven_bits_map.at(sig.extract(0, 1).to_single_sigbit()) - 1;
+ int p = driven_bits_map.at(sig.extract(0, 1).as_bit()) - 1;
while (driven_bits.at(p) != RTLIL::State::Sm)
p--, offset++;
diff --git a/passes/cmds/splitnets.cc b/passes/cmds/splitnets.cc
index 3cd857f41..a64c48791 100644
--- a/passes/cmds/splitnets.cc
+++ b/passes/cmds/splitnets.cc
@@ -50,7 +50,7 @@ struct SplitnetsWorker
new_wire_name += format.substr(1, 1);
RTLIL::Wire *new_wire = module->addWire(module->uniquify(new_wire_name), width);
- new_wire->port_id = wire->port_id;
+ new_wire->port_id = wire->port_id ? wire->port_id + offset : 0;
new_wire->port_input = wire->port_input;
new_wire->port_output = wire->port_output;
@@ -130,14 +130,24 @@ struct SplitnetsPass : public Pass {
}
extra_args(args, argidx, design);
- for (auto &mod_it : design->modules_)
+ for (auto module : design->selected_modules())
{
- RTLIL::Module *module = mod_it.second;
- if (!design->selected(module))
- continue;
-
SplitnetsWorker worker;
+ if (flag_ports)
+ {
+ int normalized_port_factor = 0;
+
+ for (auto wire : module->wires())
+ if (wire->port_id != 0) {
+ normalized_port_factor = max(normalized_port_factor, wire->port_id+1);
+ normalized_port_factor = max(normalized_port_factor, GetSize(wire)+1);
+ }
+
+ for (auto wire : module->wires())
+ wire->port_id *= normalized_port_factor;
+ }
+
if (flag_driver)
{
CellTypes ct(design);
@@ -194,7 +204,8 @@ struct SplitnetsPass : public Pass {
delete_wires.insert(it.first);
module->remove(delete_wires);
- module->fixup_ports();
+ if (flag_ports)
+ module->fixup_ports();
}
}
} SplitnetsPass;
diff --git a/passes/cmds/stat.cc b/passes/cmds/stat.cc
index 0aa76467f..af56a9e20 100644
--- a/passes/cmds/stat.cc
+++ b/passes/cmds/stat.cc
@@ -110,7 +110,7 @@ struct statdata_t
int width_a = it.second->hasPort("\\A") ? GetSize(it.second->getPort("\\A")) : 0;
int width_b = it.second->hasPort("\\B") ? GetSize(it.second->getPort("\\B")) : 0;
int width_y = it.second->hasPort("\\Y") ? GetSize(it.second->getPort("\\Y")) : 0;
- cell_type = stringf("%s_%d", cell_type.c_str(), std::max<int>({width_a, width_b, width_y}));
+ cell_type = stringf("%s_%d", cell_type.c_str(), max<int>({width_a, width_b, width_y}));
}
else if (cell_type.in("$mux", "$pmux"))
cell_type = stringf("%s_%d", cell_type.c_str(), GetSize(it.second->getPort("\\Y")));
@@ -223,7 +223,7 @@ struct StatPass : public Pass {
data.log_data();
}
- if (top_mod != NULL)
+ if (top_mod != NULL && GetSize(mod_stat) > 1)
{
log("\n");
log("=== design hierarchy ===\n");
diff --git a/passes/cmds/torder.cc b/passes/cmds/torder.cc
new file mode 100644
index 000000000..50317c023
--- /dev/null
+++ b/passes/cmds/torder.cc
@@ -0,0 +1,123 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/yosys.h"
+#include "kernel/celltypes.h"
+#include "kernel/sigtools.h"
+#include "kernel/utils.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct TorderPass : public Pass {
+ TorderPass() : Pass("torder", "print cells in topological order") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" torder [options] [selection]\n");
+ log("\n");
+ log("This command prints the selected cells in topological order.\n");
+ log("\n");
+ log(" -stop <cell_type> <cell_port>\n");
+ log(" do not use the specified cell port in topological sorting\n");
+ log("\n");
+ log(" -noautostop\n");
+ log(" by default Q outputs of internal FF cells and memory read port outputs\n");
+ log(" are not used in topological sorting. this option deactivates that.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ bool noautostop = false;
+ dict<IdString, pool<IdString>> stop_db;
+
+ log_header("Executing TORDER pass (print cells in topological order).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-stop" && argidx+2 < args.size()) {
+ IdString cell_type = RTLIL::escape_id(args[++argidx]);
+ IdString cell_port = RTLIL::escape_id(args[++argidx]);
+ stop_db[cell_type].insert(cell_port);
+ continue;
+ }
+ if (args[argidx] == "-noautostop") {
+ noautostop = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ {
+ log("module %s\n", log_id(module));
+
+ SigMap sigmap(module);
+ dict<SigBit, pool<IdString>> bit_drivers, bit_users;
+ TopoSort<IdString, RTLIL::sort_by_id_str> toposort;
+
+ for (auto cell : module->selected_cells())
+ for (auto conn : cell->connections())
+ {
+ if (stop_db.count(cell->type) && stop_db.at(cell->type).count(conn.first))
+ continue;
+
+ if (!noautostop && yosys_celltypes.cell_known(cell->type)) {
+ if (conn.first.in("\\Q", "\\CTRL_OUT", "\\RD_DATA"))
+ continue;
+ if (cell->type == "$memrd" && conn.first == "\\DATA")
+ continue;
+ }
+
+ if (cell->input(conn.first))
+ for (auto bit : sigmap(conn.second))
+ bit_users[bit].insert(cell->name);
+
+ if (cell->output(conn.first))
+ for (auto bit : sigmap(conn.second))
+ bit_drivers[bit].insert(cell->name);
+
+ toposort.node(cell->name);
+ }
+
+ for (auto &it : bit_users)
+ if (bit_drivers.count(it.first))
+ for (auto driver_cell : bit_drivers.at(it.first))
+ for (auto user_cell : it.second)
+ toposort.edge(driver_cell, user_cell);
+
+ toposort.analyze_loops = true;
+ toposort.sort();
+
+ for (auto &it : toposort.loops) {
+ log(" loop");
+ for (auto cell : it)
+ log(" %s", log_id(cell));
+ log("\n");
+ }
+
+ for (auto cell : toposort.sorted)
+ log(" cell %s\n", log_id(cell));
+ }
+ }
+} TorderPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/equiv/Makefile.inc b/passes/equiv/Makefile.inc
index 548eaca3b..dd7b3be02 100644
--- a/passes/equiv/Makefile.inc
+++ b/passes/equiv/Makefile.inc
@@ -6,4 +6,7 @@ OBJS += passes/equiv/equiv_status.o
OBJS += passes/equiv/equiv_add.o
OBJS += passes/equiv/equiv_remove.o
OBJS += passes/equiv/equiv_induct.o
+OBJS += passes/equiv/equiv_struct.o
+OBJS += passes/equiv/equiv_purge.o
+OBJS += passes/equiv/equiv_mark.o
diff --git a/passes/equiv/equiv_add.cc b/passes/equiv/equiv_add.cc
index 4ce750b1d..9ae490fe7 100644
--- a/passes/equiv/equiv_add.cc
+++ b/passes/equiv/equiv_add.cc
@@ -33,56 +33,117 @@ struct EquivAddPass : public Pass {
log("\n");
log("This command adds an $equiv cell for the specified signals.\n");
log("\n");
+ log("\n");
+ log(" equiv_add -cell gold_cell gate_cell\n");
+ log("\n");
+ log("This command adds $equiv cells for the ports of the specified cells.\n");
+ log("\n");
}
virtual void execute(std::vector<std::string> args, Design *design)
{
- if (GetSize(args) != 3)
- cmd_error(args, GetSize(args)-1, "Invalid number of arguments.");
-
if (design->selected_active_module.empty())
log_cmd_error("This command must be executed in module context!\n");
Module *module = design->module(design->selected_active_module);
log_assert(module != nullptr);
- SigSpec gold_signal, gate_signal;
+ if (GetSize(args) == 4 && args[1] == "-cell")
+ {
+ Cell *gold_cell = module->cell(RTLIL::escape_id(args[2]));
+ Cell *gate_cell = module->cell(RTLIL::escape_id(args[3]));
- if (!SigSpec::parse(gate_signal, module, args[2]))
- log_cmd_error("Error in gate signal: %s\n", args[2].c_str());
+ if (gold_cell == nullptr)
+ log_cmd_error("Can't find gold cell '%s'.\n", args[2].c_str());
- if (!SigSpec::parse_rhs(gate_signal, gold_signal, module, args[1]))
- log_cmd_error("Error in gold signal: %s\n", args[1].c_str());
+ if (gate_cell == nullptr)
+ log_cmd_error("Can't find gate cell '%s'.\n", args[3].c_str());
- log_assert(GetSize(gold_signal) == GetSize(gate_signal));
- SigSpec equiv_signal = module->addWire(NEW_ID, GetSize(gold_signal));
+ for (auto conn : gold_cell->connections())
+ {
+ auto port = conn.first;
+ SigSpec gold_sig = gold_cell->getPort(port);
+ SigSpec gate_sig = gate_cell->getPort(port);
+ int width = min(GetSize(gold_sig), GetSize(gate_sig));
- SigMap sigmap(module);
- sigmap.apply(gold_signal);
- sigmap.apply(gate_signal);
+ if (gold_cell->input(port) && gate_cell->input(port))
+ {
+ SigSpec combined_sig = module->addWire(NEW_ID, width);
- dict<SigBit, SigBit> to_equiv_bits;
- pool<Cell*> added_equiv_cells;
+ for (int i = 0; i < width; i++) {
+ module->addEquiv(NEW_ID, gold_sig[i], gate_sig[i], combined_sig[i]);
+ gold_sig[i] = gate_sig[i] = combined_sig[i];
+ }
- for (int i = 0; i < GetSize(gold_signal); i++) {
- Cell *equiv_cell = module->addEquiv(NEW_ID, gold_signal[i], gate_signal[i], equiv_signal[i]);
- equiv_cell->set_bool_attribute("\\keep");
- to_equiv_bits[gold_signal[i]] = equiv_signal[i];
- to_equiv_bits[gate_signal[i]] = equiv_signal[i];
- added_equiv_cells.insert(equiv_cell);
+ gold_cell->setPort(port, gold_sig);
+ gate_cell->setPort(port, gate_sig);
+ continue;
+ }
+
+ if (gold_cell->output(port) && gate_cell->output(port))
+ {
+ SigSpec new_gold_wire = module->addWire(NEW_ID, width);
+ SigSpec new_gate_wire = module->addWire(NEW_ID, width);
+ SigSig gg_conn;
+
+ for (int i = 0; i < width; i++) {
+ module->addEquiv(NEW_ID, new_gold_wire[i], new_gold_wire[i], gold_sig[i]);
+ gg_conn.first.append(gate_sig[i]);
+ gg_conn.second.append(gold_sig[i]);
+ gold_sig[i] = new_gold_wire[i];
+ gate_sig[i] = new_gate_wire[i];
+ }
+
+ module->connect(gg_conn);
+ gold_cell->setPort(port, gold_sig);
+ gate_cell->setPort(port, gate_sig);
+ continue;
+ }
+ }
}
+ else
+ {
+ if (GetSize(args) != 3)
+ cmd_error(args, GetSize(args)-1, "Invalid number of arguments.");
- for (auto cell : module->cells())
- for (auto conn : cell->connections())
- if (!added_equiv_cells.count(cell) && cell->input(conn.first)) {
- SigSpec new_sig;
- for (auto bit : conn.second)
- if (to_equiv_bits.count(sigmap(bit)))
- new_sig.append(to_equiv_bits.at(sigmap(bit)));
- else
- new_sig.append(bit);
- if (conn.second != new_sig)
- cell->setPort(conn.first, new_sig);
+ SigSpec gold_signal, gate_signal;
+
+ if (!SigSpec::parse(gate_signal, module, args[2]))
+ log_cmd_error("Error in gate signal: %s\n", args[2].c_str());
+
+ if (!SigSpec::parse_rhs(gate_signal, gold_signal, module, args[1]))
+ log_cmd_error("Error in gold signal: %s\n", args[1].c_str());
+
+ log_assert(GetSize(gold_signal) == GetSize(gate_signal));
+ SigSpec equiv_signal = module->addWire(NEW_ID, GetSize(gold_signal));
+
+ SigMap sigmap(module);
+ sigmap.apply(gold_signal);
+ sigmap.apply(gate_signal);
+
+ dict<SigBit, SigBit> to_equiv_bits;
+ pool<Cell*> added_equiv_cells;
+
+ for (int i = 0; i < GetSize(gold_signal); i++) {
+ Cell *equiv_cell = module->addEquiv(NEW_ID, gold_signal[i], gate_signal[i], equiv_signal[i]);
+ equiv_cell->set_bool_attribute("\\keep");
+ to_equiv_bits[gold_signal[i]] = equiv_signal[i];
+ to_equiv_bits[gate_signal[i]] = equiv_signal[i];
+ added_equiv_cells.insert(equiv_cell);
}
+
+ for (auto cell : module->cells())
+ for (auto conn : cell->connections())
+ if (!added_equiv_cells.count(cell) && cell->input(conn.first)) {
+ SigSpec new_sig;
+ for (auto bit : conn.second)
+ if (to_equiv_bits.count(sigmap(bit)))
+ new_sig.append(to_equiv_bits.at(sigmap(bit)));
+ else
+ new_sig.append(bit);
+ if (conn.second != new_sig)
+ cell->setPort(conn.first, new_sig);
+ }
+ }
}
} EquivAddPass;
diff --git a/passes/equiv/equiv_induct.cc b/passes/equiv/equiv_induct.cc
index a536fe308..cdb951ec9 100644
--- a/passes/equiv/equiv_induct.cc
+++ b/passes/equiv/equiv_induct.cc
@@ -59,8 +59,8 @@ struct EquivInductWorker
cell_warn_cache.insert(cell);
}
if (cell->type == "$equiv") {
- SigBit bit_a = sigmap(cell->getPort("\\A")).to_single_sigbit();
- SigBit bit_b = sigmap(cell->getPort("\\B")).to_single_sigbit();
+ SigBit bit_a = sigmap(cell->getPort("\\A")).as_bit();
+ SigBit bit_b = sigmap(cell->getPort("\\B")).as_bit();
if (bit_a != bit_b) {
int ez_a = satgen.importSigBit(bit_a, step);
int ez_b = satgen.importSigBit(bit_b, step);
@@ -137,8 +137,8 @@ struct EquivInductWorker
for (auto cell : workset)
{
- SigBit bit_a = sigmap(cell->getPort("\\A")).to_single_sigbit();
- SigBit bit_b = sigmap(cell->getPort("\\B")).to_single_sigbit();
+ SigBit bit_a = sigmap(cell->getPort("\\A")).as_bit();
+ SigBit bit_b = sigmap(cell->getPort("\\B")).as_bit();
log(" Trying to prove $equiv for %s:", log_signal(sigmap(cell->getPort("\\Y"))));
diff --git a/passes/equiv/equiv_make.cc b/passes/equiv/equiv_make.cc
index c001fdbfe..8b063c542 100644
--- a/passes/equiv/equiv_make.cc
+++ b/passes/equiv/equiv_make.cc
@@ -280,7 +280,7 @@ struct EquivMakeWorker
for (auto c : cells_list)
for (auto &conn : c->connections())
- if (ct.cell_input(c->type, conn.first)) {
+ if (!ct.cell_output(c->type, conn.first)) {
SigSpec old_sig = assign_map(conn.second);
SigSpec new_sig = rd_signal_map(old_sig);
if (old_sig != new_sig) {
diff --git a/passes/equiv/equiv_mark.cc b/passes/equiv/equiv_mark.cc
new file mode 100644
index 000000000..3e9819d1a
--- /dev/null
+++ b/passes/equiv/equiv_mark.cc
@@ -0,0 +1,239 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct EquivMarkWorker
+{
+ Module *module;
+ SigMap sigmap;
+
+ // cache for traversing signal flow graph
+ dict<SigBit, pool<IdString>> up_bit2cells;
+ dict<IdString, pool<SigBit>> up_cell2bits;
+ pool<IdString> edge_cells, equiv_cells;
+
+ // graph traversal state
+ pool<SigBit> queue, visited;
+
+ // assigned regions
+ dict<IdString, int> cell_regions;
+ dict<SigBit, int> bit_regions;
+ int next_region;
+
+ // merge-find
+ mfp<int> region_mf;
+
+ EquivMarkWorker(Module *module) : module(module), sigmap(module)
+ {
+ for (auto cell : module->cells())
+ {
+ if (cell->type == "$equiv")
+ equiv_cells.insert(cell->name);
+
+ for (auto &port : cell->connections())
+ {
+ if (cell->input(port.first))
+ for (auto bit : sigmap(port.second))
+ up_cell2bits[cell->name].insert(bit);
+
+ if (cell->output(port.first))
+ for (auto bit : sigmap(port.second))
+ up_bit2cells[bit].insert(cell->name);
+ }
+ }
+
+ next_region = 0;
+ }
+
+ void mark()
+ {
+ while (!queue.empty())
+ {
+ pool<IdString> cells;
+
+ for (auto &bit : queue)
+ {
+ // log_assert(bit_regions.count(bit) == 0);
+ bit_regions[bit] = next_region;
+ visited.insert(bit);
+
+ for (auto cell : up_bit2cells[bit])
+ if (edge_cells.count(cell) == 0)
+ cells.insert(cell);
+ }
+
+ queue.clear();
+
+ for (auto cell : cells)
+ {
+ if (next_region == 0 && equiv_cells.count(cell))
+ continue;
+
+ if (cell_regions.count(cell)) {
+ if (cell_regions.at(cell) != 0)
+ region_mf.merge(cell_regions.at(cell), next_region);
+ continue;
+ }
+
+ cell_regions[cell] = next_region;
+
+ for (auto bit : up_cell2bits[cell])
+ if (visited.count(bit) == 0)
+ queue.insert(bit);
+ }
+ }
+
+ next_region++;
+ }
+
+ void run()
+ {
+ log("Running equiv_mark on module %s:\n", log_id(module));
+
+ // marking region 0
+
+ for (auto wire : module->wires())
+ if (wire->port_id > 0)
+ for (auto bit : sigmap(wire))
+ queue.insert(bit);
+
+ for (auto cell_name : equiv_cells)
+ {
+ auto cell = module->cell(cell_name);
+
+ SigSpec sig_a = sigmap(cell->getPort("\\A"));
+ SigSpec sig_b = sigmap(cell->getPort("\\B"));
+
+ if (sig_a == sig_b) {
+ for (auto bit : sig_a)
+ queue.insert(bit);
+ edge_cells.insert(cell_name);
+ cell_regions[cell_name] = 0;
+ }
+ }
+
+ mark();
+
+ // marking unsolved regions
+
+ for (auto cell : module->cells())
+ {
+ if (cell_regions.count(cell->name) || cell->type != "$equiv")
+ continue;
+
+ SigSpec sig_a = sigmap(cell->getPort("\\A"));
+ SigSpec sig_b = sigmap(cell->getPort("\\B"));
+
+ log_assert(sig_a != sig_b);
+
+ for (auto bit : sig_a)
+ queue.insert(bit);
+
+ for (auto bit : sig_b)
+ queue.insert(bit);
+
+ cell_regions[cell->name] = next_region;
+ mark();
+ }
+
+ // setting attributes
+
+ dict<int, int> final_region_map;
+ int next_final_region = 0;
+
+ dict<int, int> region_cell_count;
+ dict<int, int> region_wire_count;
+
+ for (int i = 0; i < next_region; i++) {
+ int r = region_mf.find(i);
+ if (final_region_map.count(r) == 0)
+ final_region_map[r] = next_final_region++;
+ final_region_map[i] = final_region_map[r];
+ }
+
+ for (auto cell : module->cells())
+ {
+ if (cell_regions.count(cell->name)) {
+ int r = final_region_map.at(cell_regions.at(cell->name));
+ cell->attributes["\\equiv_region"] = Const(r);
+ region_cell_count[r]++;
+ } else
+ cell->attributes.erase("\\equiv_region");
+ }
+
+ for (auto wire : module->wires())
+ {
+ pool<int> regions;
+ for (auto bit : sigmap(wire))
+ if (bit_regions.count(bit))
+ regions.insert(region_mf.find(bit_regions.at(bit)));
+
+ if (GetSize(regions) == 1) {
+ int r = final_region_map.at(*regions.begin());
+ wire->attributes["\\equiv_region"] = Const(r);
+ region_wire_count[r]++;
+ } else
+ wire->attributes.erase("\\equiv_region");
+ }
+
+ for (int i = 0; i < next_final_region; i++)
+ log(" region %d: %d cells, %d wires\n", i, region_wire_count[i], region_cell_count[i]);
+ }
+};
+
+struct EquivMarkPass : public Pass {
+ EquivMarkPass() : Pass("equiv_mark", "mark equivalence checking regions") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" equiv_mark [options] [selection]\n");
+ log("\n");
+ log("This command marks the regions in an equivalence checking module. Region 0 is\n");
+ log("the proven part of the circuit. Regions with higher numbers are connected\n");
+ log("unproven subcricuits. The integer attribute 'equiv_region' is set on all\n");
+ log("wires and cells.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, Design *design)
+ {
+ log_header("Executing EQUIV_MARK pass.\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ // if (args[argidx] == "-foobar") {
+ // continue;
+ // }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_whole_modules_warn()) {
+ EquivMarkWorker worker(module);
+ worker.run();
+ }
+ }
+} EquivMarkPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/equiv/equiv_miter.cc b/passes/equiv/equiv_miter.cc
index 34318dec2..982176c44 100644
--- a/passes/equiv/equiv_miter.cc
+++ b/passes/equiv/equiv_miter.cc
@@ -156,7 +156,7 @@ struct EquivMiterWorker
struct RewriteSigSpecWorker {
RTLIL::Module * mod;
void operator()(SigSpec &sig) {
- vector<RTLIL::SigChunk> chunks = sig.chunks();
+ vector<SigChunk> chunks = sig.chunks();
for (auto &c : chunks)
if (c.wire != NULL)
c.wire = mod->wires_.at(c.wire->name);
diff --git a/passes/equiv/equiv_purge.cc b/passes/equiv/equiv_purge.cc
new file mode 100644
index 000000000..f4141ad4d
--- /dev/null
+++ b/passes/equiv/equiv_purge.cc
@@ -0,0 +1,210 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct EquivPurgeWorker
+{
+ Module *module;
+ SigMap sigmap;
+ int name_cnt;
+
+ EquivPurgeWorker(Module *module) : module(module), sigmap(module), name_cnt(0) { }
+
+ SigSpec make_output(SigSpec sig, IdString cellname)
+ {
+ if (sig.is_wire()) {
+ Wire *wire = sig.as_wire();
+ if (wire->name[0] == '\\') {
+ if (!wire->port_output) {
+ log(" Module output: %s (%s)\n", log_signal(wire), log_id(cellname));
+ wire->port_output = true;
+ }
+ return wire;
+ }
+ }
+
+ while (1)
+ {
+ IdString name = stringf("\\equiv_%d", name_cnt++);
+ if (module->count_id(name))
+ continue;
+
+ Wire *wire = module->addWire(name, GetSize(sig));
+ wire->port_output = true;
+ module->connect(wire, sig);
+ log(" Module output: %s (%s)\n", log_signal(wire), log_id(cellname));
+ return wire;
+ }
+ }
+
+ SigSpec make_input(SigSpec sig)
+ {
+ if (sig.is_wire()) {
+ Wire *wire = sig.as_wire();
+ if (wire->name[0] == '\\') {
+ if (!wire->port_output) {
+ log(" Module input: %s\n", log_signal(wire));
+ wire->port_input = true;
+ }
+ return module->addWire(NEW_ID, GetSize(sig));
+ }
+ }
+
+ while (1)
+ {
+ IdString name = stringf("\\equiv_%d", name_cnt++);
+ if (module->count_id(name))
+ continue;
+
+ Wire *wire = module->addWire(name, GetSize(sig));
+ wire->port_input = true;
+ module->connect(sig, wire);
+ log(" Module input: %s\n", log_signal(wire));
+ return module->addWire(NEW_ID, GetSize(sig));
+ }
+ }
+
+ void run()
+ {
+ log("Running equiv_purge on module %s:\n", log_id(module));
+
+ for (auto wire : module->wires()) {
+ wire->port_input = false;
+ wire->port_output = false;
+ }
+
+ pool<SigBit> queue, visited;
+
+ // cache for traversing signal flow graph
+ dict<SigBit, pool<IdString>> up_bit2cells;
+ dict<IdString, pool<SigBit>> up_cell2bits;
+
+ for (auto cell : module->cells())
+ {
+ if (cell->type != "$equiv") {
+ for (auto &port : cell->connections()) {
+ if (cell->input(port.first))
+ for (auto bit : sigmap(port.second))
+ up_cell2bits[cell->name].insert(bit);
+ if (cell->output(port.first))
+ for (auto bit : sigmap(port.second))
+ up_bit2cells[bit].insert(cell->name);
+ }
+ continue;
+ }
+
+ SigSpec sig_a = sigmap(cell->getPort("\\A"));
+ SigSpec sig_b = sigmap(cell->getPort("\\B"));
+ SigSpec sig_y = sigmap(cell->getPort("\\Y"));
+
+ if (sig_a == sig_b)
+ continue;
+
+ for (auto bit : sig_a)
+ queue.insert(bit);
+
+ for (auto bit : sig_b)
+ queue.insert(bit);
+
+ for (auto bit : sig_y)
+ visited.insert(bit);
+
+ cell->setPort("\\Y", make_output(sig_y, cell->name));
+ }
+
+ SigSpec srcsig;
+ SigMap rewrite_sigmap(module);
+
+ while (!queue.empty())
+ {
+ pool<SigBit> next_queue;
+
+ for (auto bit : queue)
+ visited.insert(bit);
+
+ for (auto bit : queue)
+ {
+ auto &cells = up_bit2cells[bit];
+
+ if (cells.empty()) {
+ srcsig.append(bit);
+ } else {
+ for (auto cell : cells)
+ for (auto bit : up_cell2bits[cell])
+ if (visited.count(bit) == 0)
+ next_queue.insert(bit);
+ }
+ }
+
+ next_queue.swap(queue);
+ }
+
+ srcsig.sort_and_unify();
+
+ for (SigChunk chunk : srcsig.chunks())
+ if (chunk.wire != nullptr)
+ rewrite_sigmap.add(chunk, make_input(chunk));
+
+ for (auto cell : module->cells())
+ if (cell->type == "$equiv")
+ cell->setPort("\\Y", rewrite_sigmap(sigmap(cell->getPort("\\Y"))));
+
+ module->fixup_ports();
+ }
+};
+
+struct EquivPurgePass : public Pass {
+ EquivPurgePass() : Pass("equiv_purge", "purge equivalence checking module") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" equiv_purge [options] [selection]\n");
+ log("\n");
+ log("This command removes the proven part of an equivalence checking module, leaving\n");
+ log("only the unproven segments in the design. This will also remove and add module\n");
+ log("ports as needed.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, Design *design)
+ {
+ log_header("Executing EQUIV_PURGE pass.\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ // if (args[argidx] == "-foobar") {
+ // continue;
+ // }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_whole_modules_warn()) {
+ EquivPurgeWorker worker(module);
+ worker.run();
+ }
+ }
+} EquivPurgePass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/equiv/equiv_simple.cc b/passes/equiv/equiv_simple.cc
index 1f52a6321..fa22dc621 100644
--- a/passes/equiv/equiv_simple.cc
+++ b/passes/equiv/equiv_simple.cc
@@ -89,8 +89,8 @@ struct EquivSimpleWorker
bool run_cell()
{
- SigBit bit_a = sigmap(equiv_cell->getPort("\\A")).to_single_sigbit();
- SigBit bit_b = sigmap(equiv_cell->getPort("\\B")).to_single_sigbit();
+ SigBit bit_a = sigmap(equiv_cell->getPort("\\A")).as_bit();
+ SigBit bit_b = sigmap(equiv_cell->getPort("\\B")).as_bit();
int ez_context = ez->frozen_literal();
if (satgen.model_undef)
@@ -314,7 +314,7 @@ struct EquivSimplePass : public Pass {
for (auto cell : module->selected_cells())
if (cell->type == "$equiv" && cell->getPort("\\A") != cell->getPort("\\B")) {
- auto bit = sigmap(cell->getPort("\\Y").to_single_sigbit());
+ auto bit = sigmap(cell->getPort("\\Y").as_bit());
auto bit_group = bit;
if (!nogroup && bit_group.wire)
bit_group.offset = 0;
diff --git a/passes/equiv/equiv_struct.cc b/passes/equiv/equiv_struct.cc
new file mode 100644
index 000000000..9f962d7ec
--- /dev/null
+++ b/passes/equiv/equiv_struct.cc
@@ -0,0 +1,336 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct EquivStructWorker
+{
+ Module *module;
+ SigMap sigmap;
+ SigMap equiv_bits;
+ bool mode_fwd;
+ bool mode_icells;
+ int merge_count;
+
+ struct merge_key_t
+ {
+ IdString type;
+ vector<pair<IdString, Const>> parameters;
+ vector<pair<IdString, int>> port_sizes;
+ vector<tuple<IdString, int, SigBit>> connections;
+
+ bool operator==(const merge_key_t &other) const {
+ return type == other.type && connections == other.connections &&
+ parameters == other.parameters && port_sizes == other.port_sizes;
+ }
+
+ unsigned int hash() const {
+ unsigned int h = mkhash_init;
+ h = mkhash(h, mkhash(type));
+ h = mkhash(h, mkhash(parameters));
+ h = mkhash(h, mkhash(connections));
+ return h;
+ }
+ };
+
+ dict<merge_key_t, pool<IdString>> merge_cache;
+ pool<merge_key_t> fwd_merge_cache, bwd_merge_cache;
+
+ void merge_cell_pair(Cell *cell_a, Cell *cell_b)
+ {
+ SigMap merged_map;
+ merge_count++;
+
+ SigSpec inputs_a, inputs_b;
+ vector<string> input_names;
+
+ for (auto &port_a : cell_a->connections())
+ {
+ SigSpec bits_a = sigmap(port_a.second);
+ SigSpec bits_b = sigmap(cell_b->getPort(port_a.first));
+
+ log_assert(GetSize(bits_a) == GetSize(bits_b));
+
+ if (!cell_a->output(port_a.first))
+ for (int i = 0; i < GetSize(bits_a); i++)
+ if (bits_a[i] != bits_b[i]) {
+ inputs_a.append(bits_a[i]);
+ inputs_b.append(bits_b[i]);
+ input_names.push_back(GetSize(bits_a) == 1 ? port_a.first.str() :
+ stringf("%s[%d]", log_id(port_a.first), i));
+ }
+ }
+
+ for (int i = 0; i < GetSize(inputs_a); i++) {
+ SigBit bit_a = inputs_a[i], bit_b = inputs_b[i];
+ SigBit bit_y = module->addWire(NEW_ID);
+ log(" New $equiv for input %s: A: %s, B: %s, Y: %s\n",
+ input_names[i].c_str(), log_signal(bit_a), log_signal(bit_b), log_signal(bit_y));
+ module->addEquiv(NEW_ID, bit_a, bit_b, bit_y);
+ merged_map.add(bit_a, bit_y);
+ merged_map.add(bit_b, bit_y);
+ }
+
+ std::vector<IdString> outport_names, inport_names;
+
+ for (auto &port_a : cell_a->connections())
+ if (cell_a->output(port_a.first))
+ outport_names.push_back(port_a.first);
+ else
+ inport_names.push_back(port_a.first);
+
+ for (auto &pn : inport_names)
+ cell_a->setPort(pn, merged_map(sigmap(cell_a->getPort(pn))));
+
+ for (auto &pn : outport_names) {
+ SigSpec sig_a = cell_a->getPort(pn);
+ SigSpec sig_b = cell_b->getPort(pn);
+ module->connect(sig_b, sig_a);
+ }
+
+ auto merged_attr = cell_b->get_strpool_attribute("\\equiv_merged");
+ merged_attr.insert(log_id(cell_b));
+ cell_a->add_strpool_attribute("\\equiv_merged", merged_attr);
+ module->remove(cell_b);
+ }
+
+ EquivStructWorker(Module *module, bool mode_fwd, bool mode_icells) :
+ module(module), sigmap(module), equiv_bits(module),
+ mode_fwd(mode_fwd), mode_icells(mode_icells), merge_count(0)
+ {
+ log(" Starting new iteration.\n");
+
+ pool<SigBit> equiv_inputs;
+ pool<IdString> cells;
+
+ for (auto cell : module->selected_cells())
+ if (cell->type == "$equiv") {
+ SigBit sig_a = sigmap(cell->getPort("\\A").as_bit());
+ SigBit sig_b = sigmap(cell->getPort("\\B").as_bit());
+ equiv_bits.add(sig_b, sig_a);
+ equiv_inputs.insert(sig_a);
+ equiv_inputs.insert(sig_b);
+ cells.insert(cell->name);
+ } else {
+ if (mode_icells || module->design->module(cell->type))
+ cells.insert(cell->name);
+ }
+
+ for (auto cell : module->selected_cells())
+ if (cell->type == "$equiv") {
+ SigBit sig_a = sigmap(cell->getPort("\\A").as_bit());
+ SigBit sig_b = sigmap(cell->getPort("\\B").as_bit());
+ SigBit sig_y = sigmap(cell->getPort("\\Y").as_bit());
+ if (sig_a == sig_b && equiv_inputs.count(sig_y)) {
+ log(" Purging redundant $equiv cell %s.\n", log_id(cell));
+ module->remove(cell);
+ merge_count++;
+ }
+ }
+
+ if (merge_count > 0)
+ return;
+
+ for (auto cell_name : cells)
+ {
+ merge_key_t key;
+ vector<tuple<IdString, int, SigBit>> fwd_connections;
+
+ Cell *cell = module->cell(cell_name);
+ key.type = cell->type;
+
+ for (auto &it : cell->parameters)
+ key.parameters.push_back(it);
+ std::sort(key.parameters.begin(), key.parameters.end());
+
+ for (auto &it : cell->connections())
+ key.port_sizes.push_back(make_pair(it.first, GetSize(it.second)));
+ std::sort(key.port_sizes.begin(), key.port_sizes.end());
+
+ for (auto &conn : cell->connections())
+ {
+ if (cell->input(conn.first)) {
+ SigSpec sig = sigmap(conn.second);
+ for (int i = 0; i < GetSize(sig); i++)
+ fwd_connections.push_back(make_tuple(conn.first, i, sig[i]));
+ }
+
+ if (cell->output(conn.first)) {
+ SigSpec sig = equiv_bits(conn.second);
+ for (int i = 0; i < GetSize(sig); i++) {
+ key.connections.clear();
+ key.connections.push_back(make_tuple(conn.first, i, sig[i]));
+
+ if (merge_cache.count(key))
+ bwd_merge_cache.insert(key);
+ merge_cache[key].insert(cell_name);
+ }
+ }
+ }
+
+ std::sort(fwd_connections.begin(), fwd_connections.end());
+ key.connections.swap(fwd_connections);
+
+ if (merge_cache.count(key))
+ fwd_merge_cache.insert(key);
+ merge_cache[key].insert(cell_name);
+ }
+
+ for (int phase = 0; phase < 2; phase++)
+ {
+ auto &queue = phase ? bwd_merge_cache : fwd_merge_cache;
+
+ for (auto &key : queue)
+ {
+ const char *strategy = nullptr;
+ vector<Cell*> gold_cells, gate_cells, other_cells;
+ vector<pair<Cell*, Cell*>> cell_pairs;
+
+ for (auto cell_name : merge_cache[key]) {
+ Cell *c = module->cell(cell_name);
+ if (c != nullptr) {
+ string n = cell_name.str();
+ if (GetSize(n) > 5 && n.substr(GetSize(n)-5) == "_gold")
+ gold_cells.push_back(c);
+ else if (GetSize(n) > 5 && n.substr(GetSize(n)-5) == "_gate")
+ gate_cells.push_back(c);
+ else
+ other_cells.push_back(c);
+ }
+ }
+
+ if (GetSize(gold_cells) > 1 || GetSize(gate_cells) > 1 || GetSize(other_cells) > 1)
+ {
+ strategy = "deduplicate";
+ for (int i = 0; i+1 < GetSize(gold_cells); i += 2)
+ cell_pairs.push_back(make_pair(gold_cells[i], gold_cells[i+1]));
+ for (int i = 0; i+1 < GetSize(gate_cells); i += 2)
+ cell_pairs.push_back(make_pair(gate_cells[i], gate_cells[i+1]));
+ for (int i = 0; i+1 < GetSize(other_cells); i += 2)
+ cell_pairs.push_back(make_pair(other_cells[i], other_cells[i+1]));
+ goto run_strategy;
+ }
+
+ if (GetSize(gold_cells) == 1 && GetSize(gate_cells) == 1)
+ {
+ strategy = "gold-gate-pairs";
+ cell_pairs.push_back(make_pair(gold_cells[0], gate_cells[0]));
+ goto run_strategy;
+ }
+
+ if (GetSize(gold_cells) == 1 && GetSize(other_cells) == 1)
+ {
+ strategy = "gold-guess";
+ cell_pairs.push_back(make_pair(gold_cells[0], other_cells[0]));
+ goto run_strategy;
+ }
+
+ if (GetSize(other_cells) == 1 && GetSize(gate_cells) == 1)
+ {
+ strategy = "gate-guess";
+ cell_pairs.push_back(make_pair(other_cells[0], gate_cells[0]));
+ goto run_strategy;
+ }
+
+ log_assert(GetSize(gold_cells) + GetSize(gate_cells) + GetSize(other_cells) < 2);
+ continue;
+
+ run_strategy:
+ log(" %s merging %d cells (from group of %d) using strategy %s:\n", phase ? "Bwd" : "Fwd",
+ 2*GetSize(cell_pairs), GetSize(gold_cells) + GetSize(gate_cells) + GetSize(other_cells), strategy);
+ for (auto it : cell_pairs) {
+ log(" Merging cells %s and %s.\n", log_id(it.first), log_id(it.second));
+ merge_cell_pair(it.first, it.second);
+ }
+ }
+
+ if (merge_count > 0)
+ return;
+ }
+
+ log(" Nothing to merge.\n");
+ }
+};
+
+struct EquivStructPass : public Pass {
+ EquivStructPass() : Pass("equiv_struct", "structural equivalence checking") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" equiv_struct [options] [selection]\n");
+ log("\n");
+ log("This command adds additional $equiv cells based on the assumption that the\n");
+ log("gold and gate circuit are structurally equivalent. Note that this can introduce\n");
+ log("bad $equiv cells in cases where the netlists are not structurally equivalent,\n");
+ log("for example when analyzing circuits with cells with commutative inputs. This\n");
+ log("command will also de-duplicate gates.\n");
+ log("\n");
+ log(" -fwd\n");
+ log(" by default this command performans forward sweeps until nothing can\n");
+ log(" be merged by forwards sweeps, the backward sweeps until forward\n");
+ log(" sweeps are effective again. with this option set only forward sweeps\n");
+ log(" are performed.\n");
+ log("\n");
+ log(" -icells\n");
+ log(" by default, the internal RTL and gate cell types are ignored. add\n");
+ log(" this option to also process those cell types with this command.\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, Design *design)
+ {
+ bool mode_icells = false;
+ bool mode_fwd = false;
+
+ log_header("Executing EQUIV_STRUCT pass.\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-fwd") {
+ mode_fwd = true;
+ continue;
+ }
+ if (args[argidx] == "-icells") {
+ mode_icells = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules()) {
+ int module_merge_count = 0;
+ log("Running equiv_struct on module %s:\n", log_id(module));
+ while (1) {
+ EquivStructWorker worker(module, mode_fwd, mode_icells);
+ if (worker.merge_count == 0)
+ break;
+ module_merge_count += worker.merge_count;
+ }
+ if (module_merge_count)
+ log(" Performed a total of %d merges in module %s.\n", module_merge_count, log_id(module));
+ }
+ }
+} EquivStructPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/fsm/fsm_detect.cc b/passes/fsm/fsm_detect.cc
index 7a621b567..740113e35 100644
--- a/passes/fsm/fsm_detect.cc
+++ b/passes/fsm/fsm_detect.cc
@@ -34,7 +34,7 @@ static SigSet<sig2driver_entry_t> sig2driver, sig2user;
static std::set<RTLIL::Cell*> muxtree_cells;
static SigPool sig_at_port;
-static bool check_state_mux_tree(RTLIL::SigSpec old_sig, RTLIL::SigSpec sig, SigPool &recursion_monitor)
+static bool check_state_mux_tree(RTLIL::SigSpec old_sig, RTLIL::SigSpec sig, pool<Cell*> &recursion_monitor)
{
if (sig_at_port.check_any(assign_map(sig)))
return false;
@@ -42,31 +42,39 @@ static bool check_state_mux_tree(RTLIL::SigSpec old_sig, RTLIL::SigSpec sig, Sig
if (sig.is_fully_const() || old_sig == sig)
return true;
- if (recursion_monitor.check_any(sig)) {
- log_warning("logic loop in mux tree at signal %s in module %s.\n",
- log_signal(sig), RTLIL::id2cstr(module->name));
- return false;
- }
-
- recursion_monitor.add(sig);
-
std::set<sig2driver_entry_t> cellport_list;
sig2driver.find(sig, cellport_list);
- for (auto &cellport : cellport_list) {
+ for (auto &cellport : cellport_list)
+ {
if ((cellport.first->type != "$mux" && cellport.first->type != "$pmux") || cellport.second != "\\Y")
return false;
+
+ if (recursion_monitor.count(cellport.first)) {
+ log_warning("logic loop in mux tree at signal %s in module %s.\n",
+ log_signal(sig), RTLIL::id2cstr(module->name));
+ return false;
+ }
+
+ recursion_monitor.insert(cellport.first);
+
RTLIL::SigSpec sig_a = assign_map(cellport.first->getPort("\\A"));
RTLIL::SigSpec sig_b = assign_map(cellport.first->getPort("\\B"));
- if (!check_state_mux_tree(old_sig, sig_a, recursion_monitor))
+
+ if (!check_state_mux_tree(old_sig, sig_a, recursion_monitor)) {
+ recursion_monitor.erase(cellport.first);
return false;
+ }
+
for (int i = 0; i < sig_b.size(); i += sig_a.size())
- if (!check_state_mux_tree(old_sig, sig_b.extract(i, sig_a.size()), recursion_monitor))
+ if (!check_state_mux_tree(old_sig, sig_b.extract(i, sig_a.size()), recursion_monitor)) {
+ recursion_monitor.erase(cellport.first);
return false;
+ }
+
+ recursion_monitor.erase(cellport.first);
muxtree_cells.insert(cellport.first);
}
- recursion_monitor.del(sig);
-
return true;
}
@@ -81,6 +89,8 @@ static bool check_state_users(RTLIL::SigSpec sig)
RTLIL::Cell *cell = cellport.first;
if (muxtree_cells.count(cell) > 0)
continue;
+ if (cell->type == "$logic_not" && assign_map(cell->getPort("\\A")) == sig)
+ continue;
if (cellport.second != "\\A" && cellport.second != "\\B")
return false;
if (!cell->hasPort("\\A") || !cell->hasPort("\\B") || !cell->hasPort("\\Y"))
@@ -100,6 +110,8 @@ static bool check_state_users(RTLIL::SigSpec sig)
static void detect_fsm(RTLIL::Wire *wire)
{
+ if (wire->attributes.count("\\init") > 0)
+ return;
if (wire->attributes.count("\\fsm_encoding") > 0 || wire->width <= 1)
return;
if (sig_at_port.check_any(assign_map(RTLIL::SigSpec(wire))))
@@ -111,7 +123,7 @@ static void detect_fsm(RTLIL::Wire *wire)
if ((cellport.first->type != "$dff" && cellport.first->type != "$adff") || cellport.second != "\\Q")
continue;
muxtree_cells.clear();
- SigPool recursion_monitor;
+ pool<Cell*> recursion_monitor;
RTLIL::SigSpec sig_q = assign_map(cellport.first->getPort("\\Q"));
RTLIL::SigSpec sig_d = assign_map(cellport.first->getPort("\\D"));
if (sig_q == RTLIL::SigSpec(wire) && check_state_mux_tree(sig_q, sig_d, recursion_monitor) && check_state_users(sig_q)) {
diff --git a/passes/fsm/fsm_opt.cc b/passes/fsm/fsm_opt.cc
index 7322368cd..a7cc95ffa 100644
--- a/passes/fsm/fsm_opt.cc
+++ b/passes/fsm/fsm_opt.cc
@@ -78,7 +78,7 @@ struct FsmOpt
bool signal_is_unused(RTLIL::SigSpec sig)
{
- RTLIL::SigBit bit = sig.to_single_sigbit();
+ RTLIL::SigBit bit = sig.as_bit();
if (bit.wire == NULL || bit.wire->attributes.count("\\unused_bits") == 0)
return false;
diff --git a/passes/fsm/fsmdata.h b/passes/fsm/fsmdata.h
index 1b98ccbac..68222769a 100644
--- a/passes/fsm/fsmdata.h
+++ b/passes/fsm/fsmdata.h
@@ -39,7 +39,7 @@ struct FsmData
int state_num_log2 = 0;
for (int i = state_table.size(); i > 0; i = i >> 1)
state_num_log2++;
- state_num_log2 = std::max(state_num_log2, 1);
+ state_num_log2 = max(state_num_log2, 1);
cell->parameters["\\STATE_BITS"] = RTLIL::Const(state_bits);
cell->parameters["\\STATE_NUM"] = RTLIL::Const(state_table.size());
diff --git a/passes/hierarchy/Makefile.inc b/passes/hierarchy/Makefile.inc
index 99aa1e116..1fb669c11 100644
--- a/passes/hierarchy/Makefile.inc
+++ b/passes/hierarchy/Makefile.inc
@@ -1,4 +1,5 @@
OBJS += passes/hierarchy/hierarchy.o
+OBJS += passes/hierarchy/singleton.o
OBJS += passes/hierarchy/submod.o
diff --git a/passes/hierarchy/hierarchy.cc b/passes/hierarchy/hierarchy.cc
index 598fe9396..fcc30d175 100644
--- a/passes/hierarchy/hierarchy.cc
+++ b/passes/hierarchy/hierarchy.cc
@@ -66,7 +66,7 @@ void generate(RTLIL::Design *design, const std::vector<std::string> &celltypes,
for (auto &conn : i2.second->connections()) {
if (conn.first[0] != '$')
portnames.insert(conn.first);
- portwidths[conn.first] = std::max(portwidths[conn.first], conn.second.size());
+ portwidths[conn.first] = max(portwidths[conn.first], conn.second.size());
}
for (auto &para : i2.second->parameters)
parameters.insert(para.first);
@@ -84,8 +84,8 @@ void generate(RTLIL::Design *design, const std::vector<std::string> &celltypes,
for (auto &decl : portdecls)
if (decl.index > 0) {
- portwidths[decl.portname] = std::max(portwidths[decl.portname], 1);
- portwidths[decl.portname] = std::max(portwidths[decl.portname], portwidths[stringf("$%d", decl.index)]);
+ portwidths[decl.portname] = max(portwidths[decl.portname], 1);
+ portwidths[decl.portname] = max(portwidths[decl.portname], portwidths[stringf("$%d", decl.index)]);
log(" port %d: %s [%d:0] %s\n", decl.index, decl.input ? decl.output ? "inout" : "input" : "output", portwidths[decl.portname]-1, RTLIL::id2cstr(decl.portname));
if (indices.count(decl.index) > ports.size())
log_error("Port index (%d) exceeds number of found ports (%d).\n", decl.index, int(ports.size()));
@@ -106,7 +106,7 @@ void generate(RTLIL::Design *design, const std::vector<std::string> &celltypes,
log_assert(!indices.empty());
indices.erase(d.index);
ports[d.index-1] = d;
- portwidths[d.portname] = std::max(portwidths[d.portname], 1);
+ portwidths[d.portname] = max(portwidths[d.portname], 1);
log(" port %d: %s [%d:0] %s\n", d.index, d.input ? d.output ? "inout" : "input" : "output", portwidths[d.portname]-1, RTLIL::id2cstr(d.portname));
goto found_matching_decl;
}
@@ -327,7 +327,7 @@ int find_top_mod_score(Design *design, Module *module, dict<Module*, int> &db)
db[module] = 0;
for (auto cell : module->cells())
if (design->module(cell->type))
- db[module] = std::max(db[module], find_top_mod_score(design, design->module(cell->type), db) + 1);
+ db[module] = max(db[module], find_top_mod_score(design, design->module(cell->type), db) + 1);
}
return db.at(module);
}
diff --git a/passes/hierarchy/singleton.cc b/passes/hierarchy/singleton.cc
new file mode 100644
index 000000000..5715c0eb1
--- /dev/null
+++ b/passes/hierarchy/singleton.cc
@@ -0,0 +1,101 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/yosys.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct SingletonPass : public Pass {
+ SingletonPass() : Pass("singleton", "create singleton modules") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" singleton [selection]\n");
+ log("\n");
+ log("By default, a module that is instantiated by several other modules is only\n");
+ log("kept once in the design. This preserves the original modularity of the design\n");
+ log("and reduces the overall size of the design in memory. But it prevents certain\n");
+ log("optimizations and other operations on the design. This pass creates singleton\n");
+ log("modules for all selected cells. The created modules are marked with the\n");
+ log("'singleton' attribute.\n");
+ log("\n");
+ log("This commands only operates on modules that by themself have the 'singleton'\n");
+ log("attribute set (the 'top' module is a singleton implicitly).\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header("Executing SINGLETON pass (creating singleton modules).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ // if (args[argidx] == "-check") {
+ // flag_check = true;
+ // continue;
+ // }
+ }
+ extra_args(args, argidx, design);
+
+ bool did_something = true;
+ int singleton_cnt = 0;
+
+ while (did_something)
+ {
+ did_something = false;
+
+ for (auto module : design->selected_modules())
+ {
+ if (!module->get_bool_attribute("\\singleton") && !module->get_bool_attribute("\\top"))
+ continue;
+
+ for (auto cell : module->selected_cells())
+ {
+ auto tmod = design->module(cell->type);
+
+ if (tmod == nullptr)
+ continue;
+
+ if (tmod->get_bool_attribute("\\blackbox"))
+ continue;
+
+ if (tmod->get_bool_attribute("\\singleton"))
+ continue;
+
+ cell->type = module->name.str() + "." + log_id(cell->name);
+ log("Creating singleton '%s'.\n", log_id(cell->type));
+
+ auto smod = tmod->clone();
+ smod->name = cell->type;
+ smod->set_bool_attribute("\\singleton");
+ design->add(smod);
+
+ did_something = true;
+ singleton_cnt++;
+ }
+ }
+ }
+
+ log("Created %d singleton modules.\n", singleton_cnt);
+ }
+} SingletonPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/memory/memory_bram.cc b/passes/memory/memory_bram.cc
index 824d6a6e8..f2d9b5847 100644
--- a/passes/memory/memory_bram.cc
+++ b/passes/memory/memory_bram.cc
@@ -112,15 +112,15 @@ struct rules_t
if (ports[i] != other.ports[i])
log_error("Bram %s variants %d and %d have different number of %c-ports.\n", log_id(name), variant, other.variant, 'A'+i);
if (wrmode[i] != other.wrmode[i])
- variant_params[stringf("\\CFG_WRMODE_%c", 'A' + i)] = wrmode[1];
+ variant_params[stringf("\\CFG_WRMODE_%c", 'A' + i)] = wrmode[i];
if (enable[i] != other.enable[i])
- variant_params[stringf("\\CFG_ENABLE_%c", 'A' + i)] = enable[1];
+ variant_params[stringf("\\CFG_ENABLE_%c", 'A' + i)] = enable[i];
if (transp[i] != other.transp[i])
- variant_params[stringf("\\CFG_TRANSP_%c", 'A' + i)] = transp[1];
+ variant_params[stringf("\\CFG_TRANSP_%c", 'A' + i)] = transp[i];
if (clocks[i] != other.clocks[i])
- variant_params[stringf("\\CFG_CLOCKS_%c", 'A' + i)] = clocks[1];
+ variant_params[stringf("\\CFG_CLOCKS_%c", 'A' + i)] = clocks[i];
if (clkpol[i] != other.clkpol[i])
- variant_params[stringf("\\CFG_CLKPOL_%c", 'A' + i)] = clkpol[1];
+ variant_params[stringf("\\CFG_CLKPOL_%c", 'A' + i)] = clkpol[i];
}
}
};
@@ -387,9 +387,9 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
if (pi.clkpol > 1)
clkpol_wr_ports.insert(pi.clkpol);
}
- clocks_max = std::max(clocks_max, pi.clocks);
- clkpol_max = std::max(clkpol_max, pi.clkpol);
- transp_max = std::max(transp_max, pi.transp);
+ clocks_max = max(clocks_max, pi.clocks);
+ clkpol_max = max(clkpol_max, pi.clkpol);
+ transp_max = max(transp_max, pi.transp);
}
log(" Mapping to bram type %s (variant %d):\n", log_id(bram.name), bram.variant);
@@ -429,11 +429,12 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
rd_clkpol.extend_u0(rd_ports);
rd_transp.extend_u0(rd_ports);
+ SigSpec rd_en = cell->getPort("\\RD_EN");
SigSpec rd_clk = cell->getPort("\\RD_CLK");
SigSpec rd_data = cell->getPort("\\RD_DATA");
SigSpec rd_addr = cell->getPort("\\RD_ADDR");
- if (match.shuffle_enable && bram.dbits >= portinfos.at(match.shuffle_enable - 'A').enable*2 && portinfos.at(match.shuffle_enable - 'A').enable > 0)
+ if (match.shuffle_enable && bram.dbits >= portinfos.at(match.shuffle_enable - 'A').enable*2 && portinfos.at(match.shuffle_enable - 'A').enable > 0 && wr_ports > 0)
{
int bucket_size = bram.dbits / portinfos.at(match.shuffle_enable - 'A').enable;
log(" Shuffle bit order to accommodate enable buckets of size %d..\n", bucket_size);
@@ -688,6 +689,10 @@ grow_read_ports:;
log(" Bram port %c%d.%d has incompatible clock polarity.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
goto skip_bram_rport;
}
+ if (rd_en[cell_port_i] != State::S1 && pi.enable == 0) {
+ log(" Bram port %c%d.%d has no read enable input.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
+ goto skip_bram_rport;
+ }
skip_bram_rport_clkcheck:
if (read_transp.count(pi.transp) && read_transp.at(pi.transp) != transp) {
if (match.make_transp && wr_ports <= 1) {
@@ -713,6 +718,7 @@ grow_read_ports:;
clock_polarities[pi.clkpol] = clkdom.second;
read_transp[pi.transp] = transp;
pi.sig_clock = clkdom.first;
+ pi.sig_en = rd_en[cell_port_i];
pi.effective_clkpol = clkdom.second;
}
@@ -853,8 +859,11 @@ grow_read_ports:;
if (pi.enable)
{
SigSpec sig_en = pi.sig_en;
- sig_en.extend_u0((grid_d+1) * pi.enable);
- sig_en = sig_en.extract(grid_d * pi.enable, pi.enable);
+
+ if (pi.wrmode == 1) {
+ sig_en.extend_u0((grid_d+1) * pi.enable);
+ sig_en = sig_en.extract(grid_d * pi.enable, pi.enable);
+ }
if (!addr_ok.empty())
sig_en = module->Mux(NEW_ID, SigSpec(0, GetSize(sig_en)), sig_en, addr_ok);
@@ -886,6 +895,8 @@ grow_read_ports:;
if (pi.make_outreg) {
SigSpec bram_dout_q = module->addWire(NEW_ID, bram.dbits);
+ if (!pi.sig_en.empty())
+ bram_dout = module->Mux(NEW_ID, bram_dout_q, bram_dout, pi.sig_en);
module->addDff(NEW_ID, pi.sig_clock, bram_dout, bram_dout_q, pi.effective_clkpol);
bram_dout = bram_dout_q;
}
@@ -966,7 +977,7 @@ void handle_cell(Cell *cell, const rules_t &rules)
log("\n");
pool<pair<IdString, int>> failed_brams;
- dict<pair<int, int>, std::tuple<int, int, int>> best_rule_cache;
+ dict<pair<int, int>, tuple<int, int, int>> best_rule_cache;
for (int i = 0; i < GetSize(rules.matches); i++)
{
@@ -1067,7 +1078,7 @@ void handle_cell(Cell *cell, const rules_t &rules)
}
log(" Storing for later selection.\n");
- best_rule_cache[pair<int, int>(i, vi)] = std::tuple<int, int, int>(match_properties["efficiency"], -match_properties["cells"], -match_properties["acells"]);
+ best_rule_cache[pair<int, int>(i, vi)] = tuple<int, int, int>(match_properties["efficiency"], -match_properties["cells"], -match_properties["acells"]);
next_match_rule:
if (or_next_if_better || best_rule_cache.empty())
@@ -1126,7 +1137,7 @@ struct MemoryBramPass : public Pass {
log(" groups 2 # number of port groups\n");
log(" ports 1 1 # number of ports in each group\n");
log(" wrmode 1 0 # set to '1' if this groups is write ports\n");
- log(" enable 4 0 # number of enable bits (for write ports)\n");
+ log(" enable 4 1 # number of enable bits\n");
log(" transp 0 2 # transparent (for read ports)\n");
log(" clocks 1 2 # clock configuration\n");
log(" clkpol 2 2 # clock polarity configuration\n");
diff --git a/passes/memory/memory_collect.cc b/passes/memory/memory_collect.cc
index 91b5759eb..5c0acb3e5 100644
--- a/passes/memory/memory_collect.cc
+++ b/passes/memory/memory_collect.cc
@@ -57,13 +57,14 @@ Cell *handle_memory(Module *module, RTLIL::Memory *memory)
SigSpec sig_rd_transparent;
SigSpec sig_rd_addr;
SigSpec sig_rd_data;
+ SigSpec sig_rd_en;
std::vector<Cell*> memcells;
for (auto &cell_it : module->cells_) {
Cell *cell = cell_it.second;
if (cell->type.in("$memrd", "$memwr", "$meminit") && memory->name == cell->parameters["\\MEMID"].decode_string()) {
- addr_bits = std::max(addr_bits, cell->getParam("\\ABITS").as_int());
+ addr_bits = max(addr_bits, cell->getParam("\\ABITS").as_int());
memcells.push_back(cell);
}
}
@@ -139,22 +140,27 @@ Cell *handle_memory(Module *module, RTLIL::Memory *memory)
SigSpec transparent = SigSpec(cell->parameters["\\TRANSPARENT"]);
SigSpec addr = sigmap(cell->getPort("\\ADDR"));
SigSpec data = sigmap(cell->getPort("\\DATA"));
+ SigSpec en = sigmap(cell->getPort("\\EN"));
- clk.extend_u0(1, false);
- clk_enable.extend_u0(1, false);
- clk_polarity.extend_u0(1, false);
- transparent.extend_u0(1, false);
- addr.extend_u0(addr_bits, false);
- data.extend_u0(memory->width, false);
-
- sig_rd_clk.append(clk);
- sig_rd_clk_enable.append(clk_enable);
- sig_rd_clk_polarity.append(clk_polarity);
- sig_rd_transparent.append(transparent);
- sig_rd_addr.append(addr);
- sig_rd_data.append(data);
-
- rd_ports++;
+ if (!en.is_fully_zero())
+ {
+ clk.extend_u0(1, false);
+ clk_enable.extend_u0(1, false);
+ clk_polarity.extend_u0(1, false);
+ transparent.extend_u0(1, false);
+ addr.extend_u0(addr_bits, false);
+ data.extend_u0(memory->width, false);
+
+ sig_rd_clk.append(clk);
+ sig_rd_clk_enable.append(clk_enable);
+ sig_rd_clk_polarity.append(clk_polarity);
+ sig_rd_transparent.append(transparent);
+ sig_rd_addr.append(addr);
+ sig_rd_data.append(data);
+ sig_rd_en.append(en);
+
+ rd_ports++;
+ }
continue;
}
}
@@ -203,6 +209,7 @@ Cell *handle_memory(Module *module, RTLIL::Memory *memory)
mem->setPort("\\RD_CLK", sig_rd_clk);
mem->setPort("\\RD_ADDR", sig_rd_addr);
mem->setPort("\\RD_DATA", sig_rd_data);
+ mem->setPort("\\RD_EN", sig_rd_en);
for (auto c : memcells)
module->remove(c);
diff --git a/passes/memory/memory_dff.cc b/passes/memory/memory_dff.cc
index 5584f27c3..2eec0207b 100644
--- a/passes/memory/memory_dff.cc
+++ b/passes/memory/memory_dff.cc
@@ -31,6 +31,8 @@ struct MemoryDffWorker
vector<Cell*> dff_cells;
dict<SigBit, SigBit> invbits;
dict<SigBit, int> sigbit_users_count;
+ dict<SigSpec, Cell*> mux_cells_a, mux_cells_b;
+ pool<Cell*> forward_merged_dffs, candidate_dffs;
MemoryDffWorker(Module *module) : module(module), sigmap(module) { }
@@ -45,6 +47,9 @@ struct MemoryDffWorker
for (auto cell : dff_cells)
{
+ if (after && forward_merged_dffs.count(cell))
+ continue;
+
SigSpec this_clk = cell->getPort("\\CLK");
bool this_clk_polarity = cell->parameters["\\CLK_POLARITY"].as_bool();
@@ -70,6 +75,7 @@ struct MemoryDffWorker
bit = d;
clk = this_clk;
clk_polarity = this_clk_polarity;
+ candidate_dffs.insert(cell);
goto replaced_this_bit;
}
@@ -86,6 +92,7 @@ struct MemoryDffWorker
RTLIL::SigSpec clk = RTLIL::SigSpec(RTLIL::State::Sx);
bool clk_polarity = 0;
+ candidate_dffs.clear();
RTLIL::SigSpec sig_addr = cell->getPort("\\ADDR");
if (!find_sig_before_dff(sig_addr, clk, clk_polarity)) {
@@ -105,13 +112,18 @@ struct MemoryDffWorker
return;
}
- if (clk != RTLIL::SigSpec(RTLIL::State::Sx)) {
+ if (clk != RTLIL::SigSpec(RTLIL::State::Sx))
+ {
+ for (auto cell : candidate_dffs)
+ forward_merged_dffs.insert(cell);
+
cell->setPort("\\CLK", clk);
cell->setPort("\\ADDR", sig_addr);
cell->setPort("\\DATA", sig_data);
cell->setPort("\\EN", sig_en);
cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1);
cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity);
+
log("merged $dff to cell.\n");
return;
}
@@ -150,16 +162,44 @@ struct MemoryDffWorker
if (sigbit_users_count[bit] > 1)
goto skip_ff_after_read_merging;
- if (find_sig_before_dff(sig_data, clk_data, clk_polarity, true) && clk_data != RTLIL::SigSpec(RTLIL::State::Sx))
+ if (mux_cells_a.count(sig_data) || mux_cells_b.count(sig_data))
{
- disconnect_dff(sig_data);
- cell->setPort("\\CLK", clk_data);
- cell->setPort("\\DATA", sig_data);
- cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1);
- cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity);
- cell->parameters["\\TRANSPARENT"] = RTLIL::Const(0);
- log("merged data $dff to cell.\n");
- return;
+ bool enable_invert = mux_cells_a.count(sig_data);
+ Cell *mux = enable_invert ? mux_cells_a.at(sig_data) : mux_cells_b.at(sig_data);
+ SigSpec check_q = sigmap(mux->getPort(enable_invert ? "\\B" : "\\A"));
+
+ sig_data = sigmap(mux->getPort("\\Y"));
+ for (auto bit : sig_data)
+ if (sigbit_users_count[bit] > 1)
+ goto skip_ff_after_read_merging;
+
+ if (find_sig_before_dff(sig_data, clk_data, clk_polarity, true) && clk_data != RTLIL::SigSpec(RTLIL::State::Sx) && sig_data == check_q)
+ {
+ disconnect_dff(sig_data);
+ cell->setPort("\\CLK", clk_data);
+ cell->setPort("\\EN", enable_invert ? module->LogicNot(NEW_ID, mux->getPort("\\S")) : mux->getPort("\\S"));
+ cell->setPort("\\DATA", sig_data);
+ cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1);
+ cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity);
+ cell->parameters["\\TRANSPARENT"] = RTLIL::Const(0);
+ log("merged data $dff with rd enable to cell.\n");
+ return;
+ }
+ }
+ else
+ {
+ if (find_sig_before_dff(sig_data, clk_data, clk_polarity, true) && clk_data != RTLIL::SigSpec(RTLIL::State::Sx))
+ {
+ disconnect_dff(sig_data);
+ cell->setPort("\\CLK", clk_data);
+ cell->setPort("\\EN", State::S1);
+ cell->setPort("\\DATA", sig_data);
+ cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1);
+ cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity);
+ cell->parameters["\\TRANSPARENT"] = RTLIL::Const(0);
+ log("merged data $dff to cell.\n");
+ return;
+ }
}
skip_ff_after_read_merging:;
@@ -169,6 +209,7 @@ struct MemoryDffWorker
clk_addr != RTLIL::SigSpec(RTLIL::State::Sx))
{
cell->setPort("\\CLK", clk_addr);
+ cell->setPort("\\EN", State::S1);
cell->setPort("\\ADDR", sig_addr);
cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1);
cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity);
@@ -191,6 +232,10 @@ struct MemoryDffWorker
for (auto cell : module->cells()) {
if (cell->type == "$dff")
dff_cells.push_back(cell);
+ if (cell->type == "$mux") {
+ mux_cells_a[sigmap(cell->getPort("\\A"))] = cell;
+ mux_cells_b[sigmap(cell->getPort("\\B"))] = cell;
+ }
if (cell->type == "$not" || cell->type == "$_NOT_" || (cell->type == "$logic_not" && GetSize(cell->getPort("\\A")) == 1)) {
SigSpec sig_a = cell->getPort("\\A");
SigSpec sig_y = cell->getPort("\\Y");
diff --git a/passes/memory/memory_map.cc b/passes/memory/memory_map.cc
index 524fa8d2b..0b8ccb363 100644
--- a/passes/memory/memory_map.cc
+++ b/passes/memory/memory_map.cc
@@ -200,34 +200,43 @@ struct MemoryMapWorker
if (cell->parameters["\\RD_CLK_ENABLE"].bits[i] == RTLIL::State::S1)
{
+ RTLIL::Cell *dff_cell = nullptr;
+
if (cell->parameters["\\RD_TRANSPARENT"].bits[i] == RTLIL::State::S1)
{
- RTLIL::Cell *c = module->addCell(genid(cell->name, "$rdreg", i), "$dff");
- c->parameters["\\WIDTH"] = RTLIL::Const(mem_abits);
- c->parameters["\\CLK_POLARITY"] = RTLIL::Const(cell->parameters["\\RD_CLK_POLARITY"].bits[i]);
- c->setPort("\\CLK", cell->getPort("\\RD_CLK").extract(i, 1));
- c->setPort("\\D", rd_addr);
+ dff_cell = module->addCell(genid(cell->name, "$rdreg", i), "$dff");
+ dff_cell->parameters["\\WIDTH"] = RTLIL::Const(mem_abits);
+ dff_cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(cell->parameters["\\RD_CLK_POLARITY"].bits[i]);
+ dff_cell->setPort("\\CLK", cell->getPort("\\RD_CLK").extract(i, 1));
+ dff_cell->setPort("\\D", rd_addr);
count_dff++;
RTLIL::Wire *w = module->addWire(genid(cell->name, "$rdreg", i, "$q"), mem_abits);
- c->setPort("\\Q", RTLIL::SigSpec(w));
+ dff_cell->setPort("\\Q", RTLIL::SigSpec(w));
rd_addr = RTLIL::SigSpec(w);
}
else
{
- RTLIL::Cell *c = module->addCell(genid(cell->name, "$rdreg", i), "$dff");
- c->parameters["\\WIDTH"] = cell->parameters["\\WIDTH"];
- c->parameters["\\CLK_POLARITY"] = RTLIL::Const(cell->parameters["\\RD_CLK_POLARITY"].bits[i]);
- c->setPort("\\CLK", cell->getPort("\\RD_CLK").extract(i, 1));
- c->setPort("\\Q", rd_signals.back());
+ dff_cell = module->addCell(genid(cell->name, "$rdreg", i), "$dff");
+ dff_cell->parameters["\\WIDTH"] = cell->parameters["\\WIDTH"];
+ dff_cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(cell->parameters["\\RD_CLK_POLARITY"].bits[i]);
+ dff_cell->setPort("\\CLK", cell->getPort("\\RD_CLK").extract(i, 1));
+ dff_cell->setPort("\\Q", rd_signals.back());
count_dff++;
RTLIL::Wire *w = module->addWire(genid(cell->name, "$rdreg", i, "$d"), mem_width);
rd_signals.clear();
rd_signals.push_back(RTLIL::SigSpec(w));
- c->setPort("\\D", rd_signals.back());
+ dff_cell->setPort("\\D", rd_signals.back());
+ }
+
+ SigBit en_bit = cell->getPort("\\RD_EN").extract(i);
+ if (en_bit != State::S1) {
+ SigSpec new_d = module->Mux(genid(cell->name, "$rdenmux", i),
+ dff_cell->getPort("\\Q"), dff_cell->getPort("\\D"), en_bit);
+ dff_cell->setPort("\\D", new_d);
}
}
diff --git a/passes/memory/memory_share.cc b/passes/memory/memory_share.cc
index b8f27025a..3a6fd0b44 100644
--- a/passes/memory/memory_share.cc
+++ b/passes/memory/memory_share.cc
@@ -210,7 +210,7 @@ struct MemoryShareWorker
if (non_feedback_nets.count(sig_data[i]))
goto not_pure_feedback_port;
- async_rd_bits[sig_addr].resize(std::max(async_rd_bits.size(), sig_data.size()));
+ async_rd_bits[sig_addr].resize(max(async_rd_bits.size(), sig_data.size()));
for (int i = 0; i < int(sig_data.size()); i++)
async_rd_bits[sig_addr][i].insert(sig_data[i]);
diff --git a/passes/memory/memory_unpack.cc b/passes/memory/memory_unpack.cc
index a497362bf..60724da77 100644
--- a/passes/memory/memory_unpack.cc
+++ b/passes/memory/memory_unpack.cc
@@ -57,6 +57,7 @@ void handle_memory(RTLIL::Module *module, RTLIL::Cell *memory)
cell->parameters["\\CLK_POLARITY"] = RTLIL::SigSpec(memory->parameters.at("\\RD_CLK_POLARITY")).extract(i, 1).as_const();
cell->parameters["\\TRANSPARENT"] = RTLIL::SigSpec(memory->parameters.at("\\RD_TRANSPARENT")).extract(i, 1).as_const();
cell->setPort("\\CLK", memory->getPort("\\RD_CLK").extract(i, 1));
+ cell->setPort("\\EN", memory->getPort("\\RD_EN").extract(i, 1));
cell->setPort("\\ADDR", memory->getPort("\\RD_ADDR").extract(i*abits, abits));
cell->setPort("\\DATA", memory->getPort("\\RD_DATA").extract(i*mem->width, mem->width));
}
diff --git a/passes/opt/opt_const.cc b/passes/opt/opt_const.cc
index 32a804260..0fcacf0a8 100644
--- a/passes/opt/opt_const.cc
+++ b/passes/opt/opt_const.cc
@@ -228,6 +228,37 @@ void handle_clkpol_celltype_swap(Cell *cell, string type1, string type2, IdStrin
}
}
+bool is_one_or_minus_one(const Const &value, bool is_signed, bool &is_negative)
+{
+ bool all_bits_one = true;
+ bool last_bit_one = true;
+
+ if (GetSize(value.bits) < 1)
+ return false;
+
+ if (GetSize(value.bits) == 1) {
+ if (value.bits[0] != State::S1)
+ return false;
+ if (is_signed)
+ is_negative = true;
+ return true;
+ }
+
+ for (int i = 0; i < GetSize(value.bits); i++) {
+ if (value.bits[i] != State::S1)
+ all_bits_one = false;
+ if (value.bits[i] != (i ? State::S0 : State::S1))
+ last_bit_one = false;
+ }
+
+ if (all_bits_one && is_signed) {
+ is_negative = true;
+ return true;
+ }
+
+ return last_bit_one;
+}
+
void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool consume_x, bool mux_undef, bool mux_bool, bool do_fine, bool keepdc, bool clkinv)
{
if (!design->selected(module))
@@ -249,6 +280,8 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if ((cell->type == "$_NOT_" || cell->type == "$not" || cell->type == "$logic_not") &&
cell->getPort("\\A").size() == 1 && cell->getPort("\\Y").size() == 1)
invert_map[assign_map(cell->getPort("\\Y"))] = assign_map(cell->getPort("\\A"));
+ if ((cell->type == "$mux" || cell->type == "$_MUX_") && cell->getPort("\\A") == SigSpec(State::S1) && cell->getPort("\\B") == SigSpec(State::S0))
+ invert_map[assign_map(cell->getPort("\\Y"))] = assign_map(cell->getPort("\\S"));
if (ct_combinational.cell_known(cell->type))
for (auto &conn : cell->connections()) {
RTLIL::SigSpec sig = assign_map(conn.second);
@@ -556,7 +589,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
RTLIL::SigSpec b = cell->getPort("\\B");
if (cell->parameters["\\A_WIDTH"].as_int() != cell->parameters["\\B_WIDTH"].as_int()) {
- int width = std::max(cell->parameters["\\A_WIDTH"].as_int(), cell->parameters["\\B_WIDTH"].as_int());
+ int width = max(cell->parameters["\\A_WIDTH"].as_int(), cell->parameters["\\B_WIDTH"].as_int());
a.extend_u0(width, cell->parameters["\\A_SIGNED"].as_bool() && cell->parameters["\\B_SIGNED"].as_bool());
b.extend_u0(width, cell->parameters["\\A_SIGNED"].as_bool() && cell->parameters["\\B_SIGNED"].as_bool());
}
@@ -682,6 +715,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
{
bool identity_wrt_a = false;
bool identity_wrt_b = false;
+ bool arith_inverse = false;
if (cell->type == "$add" || cell->type == "$sub" || cell->type == "$or" || cell->type == "$xor")
{
@@ -708,10 +742,10 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
RTLIL::SigSpec a = assign_map(cell->getPort("\\A"));
RTLIL::SigSpec b = assign_map(cell->getPort("\\B"));
- if (a.is_fully_const() && a.size() <= 32 && a.as_int() == 1)
+ if (a.is_fully_const() && is_one_or_minus_one(a.as_const(), cell->getParam("\\A_SIGNED").as_bool(), arith_inverse))
identity_wrt_b = true;
-
- if (b.is_fully_const() && b.size() <= 32 && b.as_int() == 1)
+ else
+ if (b.is_fully_const() && is_one_or_minus_one(b.as_const(), cell->getParam("\\B_SIGNED").as_bool(), arith_inverse))
identity_wrt_a = true;
}
@@ -739,7 +773,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
cell->parameters.at("\\A_SIGNED") = cell->parameters.at("\\B_SIGNED");
}
- cell->type = "$pos";
+ cell->type = arith_inverse ? "$neg" : "$pos";
cell->unsetPort("\\B");
cell->parameters.erase("\\B_WIDTH");
cell->parameters.erase("\\B_SIGNED");
diff --git a/passes/opt/opt_muxtree.cc b/passes/opt/opt_muxtree.cc
index 15d59202e..905a01627 100644
--- a/passes/opt/opt_muxtree.cc
+++ b/passes/opt/opt_muxtree.cc
@@ -136,7 +136,7 @@ struct OptMuxtreeWorker
}
}
for (auto wire : module->wires()) {
- if (wire->port_output)
+ if (wire->port_output || wire->get_bool_attribute("\\keep"))
for (int idx : sig2bits(RTLIL::SigSpec(wire)))
bit2info[idx].seen_non_mux = true;
}
diff --git a/passes/opt/opt_rmdff.cc b/passes/opt/opt_rmdff.cc
index 2ecbb31a8..e1b184af3 100644
--- a/passes/opt/opt_rmdff.cc
+++ b/passes/opt/opt_rmdff.cc
@@ -28,6 +28,15 @@ PRIVATE_NAMESPACE_BEGIN
SigMap assign_map, dff_init_map;
SigSet<RTLIL::Cell*> mux_drivers;
+dict<SigBit, pool<SigBit>> init_attributes;
+
+void remove_init_attr(SigSpec sig)
+{
+ for (auto bit : assign_map(sig))
+ if (init_attributes.count(bit))
+ for (auto wbit : init_attributes.at(bit))
+ wbit.wire->attributes.at("\\init")[wbit.offset] = State::Sx;
+}
bool handle_dlatch(RTLIL::Module *mod, RTLIL::Cell *dlatch)
{
@@ -52,6 +61,7 @@ bool handle_dlatch(RTLIL::Module *mod, RTLIL::Cell *dlatch)
delete_dlatch:
log("Removing %s (%s) from module %s.\n", dlatch->name.c_str(), dlatch->type.c_str(), mod->name.c_str());
+ remove_init_attr(dlatch->getPort("\\Q"));
mod->remove(dlatch);
return true;
}
@@ -145,7 +155,6 @@ bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff)
}
if (sig_d.is_fully_const() && (!sig_r.size() || val_rv == sig_d.as_const()) && (!has_init || val_init == sig_d.as_const())) {
- log_dump(sig_q, sig_d);
mod->connect(sig_q, sig_d);
goto delete_dff;
}
@@ -162,6 +171,7 @@ bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff)
delete_dff:
log("Removing %s (%s) from module %s.\n", dff->name.c_str(), dff->type.c_str(), mod->name.c_str());
+ remove_init_attr(dff->getPort("\\Q"));
mod->remove(dff);
return true;
}
@@ -193,8 +203,14 @@ struct OptRmdffPass : public Pass {
assign_map.set(mod_it.second);
dff_init_map.set(mod_it.second);
for (auto &it : mod_it.second->wires_)
- if (it.second->attributes.count("\\init") != 0)
+ if (it.second->attributes.count("\\init") != 0) {
dff_init_map.add(it.second, it.second->attributes.at("\\init"));
+ for (int i = 0; i < GetSize(it.second); i++) {
+ SigBit wire_bit(it.second, i), mapped_bit = assign_map(wire_bit);
+ if (mapped_bit.wire)
+ init_attributes[mapped_bit].insert(wire_bit);
+ }
+ }
mux_drivers.clear();
std::vector<RTLIL::IdString> dff_list;
diff --git a/passes/opt/share.cc b/passes/opt/share.cc
index 2c39708bb..1d9b1006e 100644
--- a/passes/opt/share.cc
+++ b/passes/opt/share.cc
@@ -113,8 +113,8 @@ struct ShareWorker
static int bits_macc_port(const Macc::port_t &p, int width)
{
if (GetSize(p.in_a) == 0 || GetSize(p.in_b) == 0)
- return std::min(std::max(GetSize(p.in_a), GetSize(p.in_b)), width);
- return std::min(GetSize(p.in_a), width) * std::min(GetSize(p.in_b), width) / 2;
+ return min(max(GetSize(p.in_a), GetSize(p.in_b)), width);
+ return min(GetSize(p.in_a), width) * min(GetSize(p.in_b), width) / 2;
}
static int bits_macc(const Macc &m, int width)
@@ -224,13 +224,13 @@ struct ShareWorker
supermacc->ports.push_back(p);
}
- int score = 1000 + abs(GetSize(p1.in_a) - GetSize(p2.in_a)) * std::max(abs(GetSize(p1.in_b) - GetSize(p2.in_b)), 1);
+ int score = 1000 + abs(GetSize(p1.in_a) - GetSize(p2.in_a)) * max(abs(GetSize(p1.in_b) - GetSize(p2.in_b)), 1);
- for (int i = 0; i < std::min(GetSize(p1.in_a), GetSize(p2.in_a)); i++)
+ for (int i = 0; i < min(GetSize(p1.in_a), GetSize(p2.in_a)); i++)
if (p1.in_a[i] == p2.in_a[i] && score > 0)
score--;
- for (int i = 0; i < std::min(GetSize(p1.in_b), GetSize(p2.in_b)); i++)
+ for (int i = 0; i < min(GetSize(p1.in_b), GetSize(p2.in_b)); i++)
if (p1.in_b[i] == p2.in_b[i] && score > 0)
score--;
@@ -243,7 +243,7 @@ struct ShareWorker
Macc m1(c1), m2(c2), supermacc;
int w1 = GetSize(c1->getPort("\\Y")), w2 = GetSize(c2->getPort("\\Y"));
- int width = std::max(w1, w2);
+ int width = max(w1, w2);
m1.optimize(w1);
m2.optimize(w2);
@@ -419,8 +419,8 @@ struct ShareWorker
int a2_width = c2->parameters.at("\\A_WIDTH").as_int();
int y2_width = c2->parameters.at("\\Y_WIDTH").as_int();
- if (std::max(a1_width, a2_width) > 2 * std::min(a1_width, a2_width)) return false;
- if (std::max(y1_width, y2_width) > 2 * std::min(y1_width, y2_width)) return false;
+ if (max(a1_width, a2_width) > 2 * min(a1_width, a2_width)) return false;
+ if (max(y1_width, y2_width) > 2 * min(y1_width, y2_width)) return false;
}
return true;
@@ -438,9 +438,9 @@ struct ShareWorker
int b2_width = c2->parameters.at("\\B_WIDTH").as_int();
int y2_width = c2->parameters.at("\\Y_WIDTH").as_int();
- if (std::max(a1_width, a2_width) > 2 * std::min(a1_width, a2_width)) return false;
- if (std::max(b1_width, b2_width) > 2 * std::min(b1_width, b2_width)) return false;
- if (std::max(y1_width, y2_width) > 2 * std::min(y1_width, y2_width)) return false;
+ if (max(a1_width, a2_width) > 2 * min(a1_width, a2_width)) return false;
+ if (max(b1_width, b2_width) > 2 * min(b1_width, b2_width)) return false;
+ if (max(y1_width, y2_width) > 2 * min(y1_width, y2_width)) return false;
}
return true;
@@ -458,15 +458,15 @@ struct ShareWorker
int b2_width = c2->parameters.at("\\B_WIDTH").as_int();
int y2_width = c2->parameters.at("\\Y_WIDTH").as_int();
- int min1_width = std::min(a1_width, b1_width);
- int max1_width = std::max(a1_width, b1_width);
+ int min1_width = min(a1_width, b1_width);
+ int max1_width = max(a1_width, b1_width);
- int min2_width = std::min(a2_width, b2_width);
- int max2_width = std::max(a2_width, b2_width);
+ int min2_width = min(a2_width, b2_width);
+ int max2_width = max(a2_width, b2_width);
- if (std::max(min1_width, min2_width) > 2 * std::min(min1_width, min2_width)) return false;
- if (std::max(max1_width, max2_width) > 2 * std::min(max1_width, max2_width)) return false;
- if (std::max(y1_width, y2_width) > 2 * std::min(y1_width, y2_width)) return false;
+ if (max(min1_width, min2_width) > 2 * min(min1_width, min2_width)) return false;
+ if (max(max1_width, max2_width) > 2 * min(max1_width, max2_width)) return false;
+ if (max(y1_width, y2_width) > 2 * min(y1_width, y2_width)) return false;
}
return true;
@@ -475,7 +475,7 @@ struct ShareWorker
if (c1->type == "$macc")
{
if (!config.opt_aggressive)
- if (share_macc(c1, c2) > 2 * std::min(bits_macc(c1), bits_macc(c2))) return false;
+ if (share_macc(c1, c2) > 2 * min(bits_macc(c1), bits_macc(c2))) return false;
return true;
}
@@ -532,8 +532,8 @@ struct ShareWorker
RTLIL::SigSpec a2 = c2->getPort("\\A");
RTLIL::SigSpec y2 = c2->getPort("\\Y");
- int a_width = std::max(a1.size(), a2.size());
- int y_width = std::max(y1.size(), y2.size());
+ int a_width = max(a1.size(), a2.size());
+ int y_width = max(y1.size(), y2.size());
a1.extend_u0(a_width, a_signed);
a2.extend_u0(a_width, a_signed);
@@ -563,11 +563,11 @@ struct ShareWorker
if (config.generic_cbin_ops.count(c1->type))
{
- int score_unflipped = std::max(c1->parameters.at("\\A_WIDTH").as_int(), c2->parameters.at("\\A_WIDTH").as_int()) +
- std::max(c1->parameters.at("\\B_WIDTH").as_int(), c2->parameters.at("\\B_WIDTH").as_int());
+ int score_unflipped = max(c1->parameters.at("\\A_WIDTH").as_int(), c2->parameters.at("\\A_WIDTH").as_int()) +
+ max(c1->parameters.at("\\B_WIDTH").as_int(), c2->parameters.at("\\B_WIDTH").as_int());
- int score_flipped = std::max(c1->parameters.at("\\A_WIDTH").as_int(), c2->parameters.at("\\B_WIDTH").as_int()) +
- std::max(c1->parameters.at("\\B_WIDTH").as_int(), c2->parameters.at("\\A_WIDTH").as_int());
+ int score_flipped = max(c1->parameters.at("\\A_WIDTH").as_int(), c2->parameters.at("\\B_WIDTH").as_int()) +
+ max(c1->parameters.at("\\B_WIDTH").as_int(), c2->parameters.at("\\A_WIDTH").as_int());
if (score_flipped < score_unflipped)
{
@@ -630,13 +630,13 @@ struct ShareWorker
RTLIL::SigSpec b2 = c2->getPort("\\B");
RTLIL::SigSpec y2 = c2->getPort("\\Y");
- int a_width = std::max(a1.size(), a2.size());
- int b_width = std::max(b1.size(), b2.size());
- int y_width = std::max(y1.size(), y2.size());
+ int a_width = max(a1.size(), a2.size());
+ int b_width = max(b1.size(), b2.size());
+ int y_width = max(y1.size(), y2.size());
if (c1->type == "$shr" && a_signed)
{
- a_width = std::max(y_width, a_width);
+ a_width = max(y_width, a_width);
if (a1.size() < y1.size()) a1.extend_u0(y1.size(), true);
if (a2.size() < y2.size()) a2.extend_u0(y2.size(), true);
@@ -708,6 +708,10 @@ struct ShareWorker
if (c1->type == "$memrd")
{
RTLIL::Cell *supercell = module->addCell(NEW_ID, c1);
+ RTLIL::SigSpec addr1 = c1->getPort("\\ADDR");
+ RTLIL::SigSpec addr2 = c2->getPort("\\ADDR");
+ if (addr1 != addr2)
+ supercell->setPort("\\ADDR", module->Mux(NEW_ID, addr2, addr1, act));
supercell_aux.insert(module->addPos(NEW_ID, supercell->getPort("\\DATA"), c2->getPort("\\DATA")));
supercell_aux.insert(supercell);
return supercell;
diff --git a/passes/opt/wreduce.cc b/passes/opt/wreduce.cc
index 70a40e960..4f08da675 100644
--- a/passes/opt/wreduce.cc
+++ b/passes/opt/wreduce.cc
@@ -37,7 +37,7 @@ struct WreduceConfig
"$and", "$or", "$xor", "$xnor",
"$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx",
"$lt", "$le", "$eq", "$ne", "$eqx", "$nex", "$ge", "$gt",
- "$add", "$sub", // "$mul", "$div", "$mod", "$pow",
+ "$add", "$sub", "$mul", // "$div", "$mod", "$pow",
"$mux", "$pmux"
});
}
@@ -51,6 +51,7 @@ struct WreduceWorker
std::set<Cell*, IdString::compare_ptr_by_name<Cell>> work_queue_cells;
std::set<SigBit> work_queue_bits;
+ pool<SigBit> keep_bits;
WreduceWorker(WreduceConfig *config, Module *module) :
config(config), module(module), mi(module) { }
@@ -65,10 +66,13 @@ struct WreduceWorker
SigSpec sig_y = mi.sigmap(cell->getPort("\\Y"));
std::vector<SigBit> bits_removed;
+ if (sig_y.has_const())
+ return;
+
for (int i = GetSize(sig_y)-1; i >= 0; i--)
{
auto info = mi.query(sig_y[i]);
- if (!info->is_output && GetSize(info->ports) <= 1) {
+ if (!info->is_output && GetSize(info->ports) <= 1 && !keep_bits.count(mi.sigmap(sig_y[i]))) {
bits_removed.push_back(Sx);
continue;
}
@@ -172,6 +176,11 @@ struct WreduceWorker
if (cell->type.in("$mux", "$pmux"))
return run_cell_mux(cell);
+ SigSpec sig = mi.sigmap(cell->getPort("\\Y"));
+
+ if (sig.has_const())
+ return;
+
// Reduce size of ports A and B based on constant input bits and size of output port
@@ -179,8 +188,8 @@ struct WreduceWorker
int max_port_b_size = cell->hasPort("\\B") ? GetSize(cell->getPort("\\B")) : -1;
if (cell->type.in("$not", "$pos", "$neg", "$and", "$or", "$xor", "$add", "$sub")) {
- max_port_a_size = std::min(max_port_a_size, GetSize(cell->getPort("\\Y")));
- max_port_b_size = std::min(max_port_b_size, GetSize(cell->getPort("\\Y")));
+ max_port_a_size = min(max_port_a_size, GetSize(sig));
+ max_port_b_size = min(max_port_b_size, GetSize(sig));
}
bool port_a_signed = false;
@@ -192,10 +201,33 @@ struct WreduceWorker
if (max_port_b_size >= 0)
run_reduce_inport(cell, 'B', max_port_b_size, port_b_signed, did_something);
+ if (cell->hasPort("\\A") && cell->hasPort("\\B") && port_a_signed && port_b_signed) {
+ SigSpec sig_a = mi.sigmap(cell->getPort("\\A")), sig_b = mi.sigmap(cell->getPort("\\B"));
+ if (GetSize(sig_a) > 0 && sig_a[GetSize(sig_a)-1] == State::S0 &&
+ GetSize(sig_b) > 0 && sig_b[GetSize(sig_b)-1] == State::S0) {
+ log("Converting cell %s.%s (%s) from signed to unsigned.\n",
+ log_id(module), log_id(cell), log_id(cell->type));
+ cell->setParam("\\A_SIGNED", 0);
+ cell->setParam("\\B_SIGNED", 0);
+ port_a_signed = false;
+ port_b_signed = false;
+ did_something = true;
+ }
+ }
+
+ if (cell->hasPort("\\A") && !cell->hasPort("\\B") && port_a_signed) {
+ SigSpec sig_a = mi.sigmap(cell->getPort("\\A"));
+ if (GetSize(sig_a) > 0 && sig_a[GetSize(sig_a)-1] == State::S0) {
+ log("Converting cell %s.%s (%s) from signed to unsigned.\n",
+ log_id(module), log_id(cell), log_id(cell->type));
+ cell->setParam("\\A_SIGNED", 0);
+ port_a_signed = false;
+ did_something = true;
+ }
+ }
- // Reduce size of port Y based on sizes for A and B and unused bits in Y
- SigSpec sig = mi.sigmap(cell->getPort("\\Y"));
+ // Reduce size of port Y based on sizes for A and B and unused bits in Y
int bits_removed = 0;
if (port_a_signed && cell->type == "$shr") {
@@ -221,7 +253,7 @@ struct WreduceWorker
if (cell->hasPort("\\A")) a_size = GetSize(cell->getPort("\\A"));
if (cell->hasPort("\\B")) b_size = GetSize(cell->getPort("\\B"));
- int max_y_size = std::max(a_size, b_size);
+ int max_y_size = max(a_size, b_size);
if (cell->type == "$add")
max_y_size++;
@@ -265,6 +297,11 @@ struct WreduceWorker
void run()
{
+ for (auto w : module->wires())
+ if (w->get_bool_attribute("\\keep"))
+ for (auto bit : mi.sigmap(w))
+ keep_bits.insert(bit);
+
for (auto c : module->selected_cells())
work_queue_cells.insert(c);
@@ -348,14 +385,16 @@ struct WreducePass : public Pass {
continue;
for (auto c : module->selected_cells())
- if (c->type.in({"$reduce_and", "$reduce_or", "$reduce_xor", "$reduce_xnor", "$reduce_bool",
+ if (c->type.in("$reduce_and", "$reduce_or", "$reduce_xor", "$reduce_xnor", "$reduce_bool",
"$lt", "$le", "$eq", "$ne", "$eqx", "$nex", "$ge", "$gt",
- "$logic_not", "$logic_and", "$logic_or"}) && GetSize(c->getPort("\\Y")) > 1) {
+ "$logic_not", "$logic_and", "$logic_or") && GetSize(c->getPort("\\Y")) > 1) {
SigSpec sig = c->getPort("\\Y");
- c->setPort("\\Y", sig[0]);
- c->setParam("\\Y_WIDTH", 1);
- sig.remove(0);
- module->connect(sig, Const(0, GetSize(sig)));
+ if (!sig.has_const()) {
+ c->setPort("\\Y", sig[0]);
+ c->setParam("\\Y_WIDTH", 1);
+ sig.remove(0);
+ module->connect(sig, Const(0, GetSize(sig)));
+ }
}
WreduceWorker worker(&config, module);
diff --git a/passes/proc/proc_mux.cc b/passes/proc/proc_mux.cc
index 904d92114..943e8c562 100644
--- a/passes/proc/proc_mux.cc
+++ b/passes/proc/proc_mux.cc
@@ -27,35 +27,121 @@
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
-RTLIL::SigSpec find_any_lvalue(const RTLIL::CaseRule *cs)
+struct SigSnippets
{
- for (auto &action : cs->actions) {
- if (action.first.size())
- return action.first;
- }
+ idict<SigSpec> sigidx;
+ dict<SigBit, int> bit2snippet;
+ pool<int> snippets;
- for (auto sw : cs->switches)
- for (auto cs2 : sw->cases) {
- RTLIL::SigSpec sig = find_any_lvalue(cs2);
- if (sig.size())
- return sig;
+ void insert(SigSpec sig)
+ {
+ if (sig.empty())
+ return;
+
+ int key = sigidx(sig);
+ if (snippets.count(key))
+ return;
+
+ SigSpec new_sig;
+
+ for (int i = 0; i < GetSize(sig); i++)
+ {
+ int other_key = bit2snippet.at(sig[i], -1);
+
+ if (other_key < 0) {
+ new_sig.append(sig[i]);
+ continue;
+ }
+
+ if (!new_sig.empty()) {
+ int new_key = sigidx(new_sig);
+ snippets.insert(new_key);
+ for (auto bit : new_sig)
+ bit2snippet[bit] = new_key;
+ new_sig = SigSpec();
+ }
+
+ SigSpec other_sig = sigidx[other_key];
+ int k = 0, n = 1;
+
+ while (other_sig[k] != sig[i]) {
+ k++;
+ log_assert(k < GetSize(other_sig));
+ }
+
+ while (i+n < GetSize(sig) && k+n < GetSize(other_sig) && sig[i+n] == other_sig[k+n])
+ n++;
+
+ SigSpec sig1 = other_sig.extract(0, k);
+ SigSpec sig2 = other_sig.extract(k, n);
+ SigSpec sig3 = other_sig.extract(k+n, GetSize(other_sig)-k-n);
+
+ for (auto bit : other_sig)
+ bit2snippet.erase(bit);
+ snippets.erase(other_key);
+
+ insert(sig1);
+ insert(sig2);
+ insert(sig3);
+
+ i += n-1;
+ }
+
+ if (!new_sig.empty()) {
+ int new_key = sigidx(new_sig);
+ snippets.insert(new_key);
+ for (auto bit : new_sig)
+ bit2snippet[bit] = new_key;
+ }
}
- return RTLIL::SigSpec();
-}
+ void insert(const RTLIL::CaseRule *cs)
+ {
+ for (auto &action : cs->actions)
+ insert(action.first);
-void extract_core_signal(const RTLIL::CaseRule *cs, RTLIL::SigSpec &sig)
+ for (auto sw : cs->switches)
+ for (auto cs2 : sw->cases)
+ insert(cs2);
+ }
+};
+
+struct SnippetSwCache
{
- for (auto &action : cs->actions) {
- RTLIL::SigSpec lvalue = action.first.extract(sig);
- if (lvalue.size())
- sig = lvalue;
+ dict<RTLIL::SwitchRule*, pool<int>, hash_ptr_ops> cache;
+ const SigSnippets *snippets;
+ int current_snippet;
+
+ bool check(RTLIL::SwitchRule *sw)
+ {
+ return cache[sw].count(current_snippet) != 0;
}
- for (auto sw : cs->switches)
- for (auto cs2 : sw->cases)
- extract_core_signal(cs2, sig);
-}
+ void insert(const RTLIL::CaseRule *cs, vector<RTLIL::SwitchRule*> &sw_stack)
+ {
+ for (auto &action : cs->actions)
+ for (auto bit : action.first) {
+ int sn = snippets->bit2snippet.at(bit, -1);
+ if (sn < 0)
+ continue;
+ for (auto sw : sw_stack)
+ cache[sw].insert(sn);
+ }
+
+ for (auto sw : cs->switches) {
+ sw_stack.push_back(sw);
+ for (auto cs2 : sw->cases)
+ insert(cs2, sw_stack);
+ sw_stack.pop_back();
+ }
+ }
+
+ void insert(const RTLIL::CaseRule *cs)
+ {
+ vector<RTLIL::SwitchRule*> sw_stack;
+ insert(cs, sw_stack);
+ }
+};
RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SwitchRule *sw)
{
@@ -179,7 +265,8 @@ void append_pmux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::ve
last_mux_cell->parameters["\\S_WIDTH"] = last_mux_cell->getPort("\\S").size();
}
-RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, RTLIL::CaseRule *cs, const RTLIL::SigSpec &sig, const RTLIL::SigSpec &defval)
+RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, SnippetSwCache &swcache, dict<RTLIL::SwitchRule*, bool, hash_ptr_ops> &swpara,
+ RTLIL::CaseRule *cs, const RTLIL::SigSpec &sig, const RTLIL::SigSpec &defval)
{
RTLIL::SigSpec result = defval;
@@ -190,9 +277,37 @@ RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, RTLIL::CaseRule *cs, const
for (auto sw : cs->switches)
{
+ if (!swcache.check(sw))
+ continue;
+
// detect groups of parallel cases
std::vector<int> pgroups(sw->cases.size());
+ bool is_simple_parallel_case = true;
+
if (!sw->get_bool_attribute("\\parallel_case")) {
+ if (!swpara.count(sw)) {
+ pool<Const> case_values;
+ for (size_t i = 0; i < sw->cases.size(); i++) {
+ RTLIL::CaseRule *cs2 = sw->cases[i];
+ for (auto pat : cs2->compare) {
+ if (!pat.is_fully_def())
+ goto not_simple_parallel_case;
+ Const cpat = pat.as_const();
+ if (case_values.count(cpat))
+ goto not_simple_parallel_case;
+ case_values.insert(cpat);
+ }
+ }
+ if (0)
+ not_simple_parallel_case:
+ is_simple_parallel_case = false;
+ swpara[sw] = is_simple_parallel_case;
+ } else {
+ is_simple_parallel_case = swpara.at(sw);
+ }
+ }
+
+ if (!is_simple_parallel_case) {
BitPatternPool pool(sw->signal.size());
bool extra_group_for_next_case = false;
for (size_t i = 0; i < sw->cases.size(); i++) {
@@ -225,7 +340,7 @@ RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, RTLIL::CaseRule *cs, const
for (size_t i = 0; i < sw->cases.size(); i++) {
int case_idx = sw->cases.size() - i - 1;
RTLIL::CaseRule *cs2 = sw->cases[case_idx];
- RTLIL::SigSpec value = signal_to_mux_tree(mod, cs2, sig, initial_val);
+ RTLIL::SigSpec value = signal_to_mux_tree(mod, swcache, swpara, cs2, sig, initial_val);
if (last_mux_cell && pgroups[case_idx] == pgroups[case_idx+1])
append_pmux(mod, sw->signal, cs2->compare, value, last_mux_cell, sw);
else
@@ -238,24 +353,26 @@ RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, RTLIL::CaseRule *cs, const
void proc_mux(RTLIL::Module *mod, RTLIL::Process *proc)
{
- bool first = true;
- while (1)
- {
- RTLIL::SigSpec sig = find_any_lvalue(&proc->root_case);
+ log("Creating decoders for process `%s.%s'.\n", mod->name.c_str(), proc->name.c_str());
- if (sig.size() == 0)
- break;
+ SigSnippets sigsnip;
+ sigsnip.insert(&proc->root_case);
- if (first) {
- log("Creating decoders for process `%s.%s'.\n", mod->name.c_str(), proc->name.c_str());
- first = false;
- }
+ SnippetSwCache swcache;
+ swcache.snippets = &sigsnip;
+ swcache.insert(&proc->root_case);
+
+ dict<RTLIL::SwitchRule*, bool, hash_ptr_ops> swpara;
- extract_core_signal(&proc->root_case, sig);
+ int cnt = 0;
+ for (int idx : sigsnip.snippets)
+ {
+ swcache.current_snippet = idx;
+ RTLIL::SigSpec sig = sigsnip.sigidx[idx];
- log(" creating decoder for signal `%s'.\n", log_signal(sig));
+ log("%6d/%d: %s\n", ++cnt, GetSize(sigsnip.snippets), log_signal(sig));
- RTLIL::SigSpec value = signal_to_mux_tree(mod, &proc->root_case, sig, RTLIL::SigSpec(RTLIL::State::Sx, sig.size()));
+ RTLIL::SigSpec value = signal_to_mux_tree(mod, swcache, swpara, &proc->root_case, sig, RTLIL::SigSpec(RTLIL::State::Sx, sig.size()));
mod->connect(RTLIL::SigSig(sig, value));
}
}
diff --git a/passes/sat/eval.cc b/passes/sat/eval.cc
index 522664032..614a1bd31 100644
--- a/passes/sat/eval.cc
+++ b/passes/sat/eval.cc
@@ -568,7 +568,7 @@ struct EvalPass : public Pass {
if (tab_column_width.size() < row.size())
tab_column_width.resize(row.size());
for (size_t i = 0; i < row.size(); i++)
- tab_column_width[i] = std::max(tab_column_width[i], int(row[i].size()));
+ tab_column_width[i] = max(tab_column_width[i], int(row[i].size()));
}
log("\n");
diff --git a/passes/sat/expose.cc b/passes/sat/expose.cc
index ca784890b..ebdf2ed5d 100644
--- a/passes/sat/expose.cc
+++ b/passes/sat/expose.cc
@@ -116,7 +116,7 @@ void create_dff_dq_map(std::map<RTLIL::IdString, dff_map_info_t> &map, RTLIL::De
info.cell = it.second;
if (info.cell->type == "$dff") {
- info.bit_clk = sigmap(info.cell->getPort("\\CLK")).to_single_sigbit();
+ info.bit_clk = sigmap(info.cell->getPort("\\CLK")).as_bit();
info.clk_polarity = info.cell->parameters.at("\\CLK_POLARITY").as_bool();
std::vector<RTLIL::SigBit> sig_d = sigmap(info.cell->getPort("\\D")).to_sigbit_vector();
std::vector<RTLIL::SigBit> sig_q = sigmap(info.cell->getPort("\\Q")).to_sigbit_vector();
@@ -128,8 +128,8 @@ void create_dff_dq_map(std::map<RTLIL::IdString, dff_map_info_t> &map, RTLIL::De
}
if (info.cell->type == "$adff") {
- info.bit_clk = sigmap(info.cell->getPort("\\CLK")).to_single_sigbit();
- info.bit_arst = sigmap(info.cell->getPort("\\ARST")).to_single_sigbit();
+ info.bit_clk = sigmap(info.cell->getPort("\\CLK")).as_bit();
+ info.bit_arst = sigmap(info.cell->getPort("\\ARST")).as_bit();
info.clk_polarity = info.cell->parameters.at("\\CLK_POLARITY").as_bool();
info.arst_polarity = info.cell->parameters.at("\\ARST_POLARITY").as_bool();
std::vector<RTLIL::SigBit> sig_d = sigmap(info.cell->getPort("\\D")).to_sigbit_vector();
@@ -144,21 +144,21 @@ void create_dff_dq_map(std::map<RTLIL::IdString, dff_map_info_t> &map, RTLIL::De
}
if (info.cell->type == "$_DFF_N_" || info.cell->type == "$_DFF_P_") {
- info.bit_clk = sigmap(info.cell->getPort("\\C")).to_single_sigbit();
+ info.bit_clk = sigmap(info.cell->getPort("\\C")).as_bit();
info.clk_polarity = info.cell->type == "$_DFF_P_";
- info.bit_d = sigmap(info.cell->getPort("\\D")).to_single_sigbit();
- bit_info[sigmap(info.cell->getPort("\\Q")).to_single_sigbit()] = info;
+ info.bit_d = sigmap(info.cell->getPort("\\D")).as_bit();
+ bit_info[sigmap(info.cell->getPort("\\Q")).as_bit()] = info;
continue;
}
if (info.cell->type.size() == 10 && info.cell->type.substr(0, 6) == "$_DFF_") {
- info.bit_clk = sigmap(info.cell->getPort("\\C")).to_single_sigbit();
- info.bit_arst = sigmap(info.cell->getPort("\\R")).to_single_sigbit();
+ info.bit_clk = sigmap(info.cell->getPort("\\C")).as_bit();
+ info.bit_arst = sigmap(info.cell->getPort("\\R")).as_bit();
info.clk_polarity = info.cell->type[6] == 'P';
info.arst_polarity = info.cell->type[7] == 'P';
info.arst_value = info.cell->type[0] == '1' ? RTLIL::State::S1 : RTLIL::State::S0;
- info.bit_d = sigmap(info.cell->getPort("\\D")).to_single_sigbit();
- bit_info[sigmap(info.cell->getPort("\\Q")).to_single_sigbit()] = info;
+ info.bit_d = sigmap(info.cell->getPort("\\D")).as_bit();
+ bit_info[sigmap(info.cell->getPort("\\Q")).as_bit()] = info;
continue;
}
}
diff --git a/passes/sat/freduce.cc b/passes/sat/freduce.cc
index e0d11243b..373b80488 100644
--- a/passes/sat/freduce.cc
+++ b/passes/sat/freduce.cc
@@ -265,7 +265,7 @@ struct PerformReduction
}
int max_child_depth = 0;
for (auto &bit : drv.second)
- max_child_depth = std::max(register_cone_worker(celldone, sigdepth, bit), max_child_depth);
+ max_child_depth = max(register_cone_worker(celldone, sigdepth, bit), max_child_depth);
sigdepth[out] = max_child_depth + 1;
} else {
pi_bits.push_back(out);
diff --git a/passes/sat/sat.cc b/passes/sat/sat.cc
index c83286924..2e9c6d2f9 100644
--- a/passes/sat/sat.cc
+++ b/passes/sat/sat.cc
@@ -606,8 +606,8 @@ struct SatHelper
int maxModelWidth = 10;
for (auto &info : modelInfo) {
- maxModelName = std::max(maxModelName, int(info.description.size()));
- maxModelWidth = std::max(maxModelWidth, info.width);
+ maxModelName = max(maxModelName, int(info.description.size()));
+ maxModelWidth = max(maxModelWidth, info.width);
}
log("\n");
@@ -781,9 +781,9 @@ struct SatHelper
wavedata[info.description].first = info.width;
wavedata[info.description].second[info.timestep] = value;
- mintime = std::min(mintime, info.timestep);
- maxtime = std::max(maxtime, info.timestep);
- maxwidth = std::max(maxwidth, info.width);
+ mintime = min(mintime, info.timestep);
+ maxtime = max(maxtime, info.timestep);
+ maxwidth = max(maxwidth, info.width);
}
fprintf(f, "{ \"signal\": [");
@@ -936,6 +936,9 @@ struct SatPass : public Pass {
log(" -show-inputs, -show-outputs, -show-ports\n");
log(" add all module (input/output) ports to the list of shown signals\n");
log("\n");
+ log(" -show-regs, -show-public, -show-all\n");
+ log(" show all registers, show signals with 'public' names, show all signals\n");
+ log("\n");
log(" -ignore_div_by_zero\n");
log(" ignore all solutions that involve a division by zero\n");
log("\n");
@@ -1064,6 +1067,7 @@ struct SatPass : public Pass {
bool verify = false, fail_on_timeout = false, enable_undef = false, set_def_inputs = false;
bool ignore_div_by_zero = false, set_init_undef = false, set_init_zero = false, max_undef = false;
bool tempinduct = false, prove_asserts = false, show_inputs = false, show_outputs = false;
+ bool show_regs = false, show_public = false, show_all = false;
bool ignore_unknown_cells = false, falsify = false, tempinduct_def = false, set_init_def = false;
bool tempinduct_baseonly = false, tempinduct_inductonly = false, set_assumes = false;
int tempinduct_skip = 0, stepsize = 1;
@@ -1112,7 +1116,7 @@ struct SatPass : public Pass {
continue;
}
if (args[argidx] == "-stepsize" && argidx+1 < args.size()) {
- stepsize = std::max(1, atoi(args[++argidx].c_str()));
+ stepsize = max(1, atoi(args[++argidx].c_str()));
continue;
}
if (args[argidx] == "-ignore_div_by_zero") {
@@ -1272,6 +1276,18 @@ struct SatPass : public Pass {
show_outputs = true;
continue;
}
+ if (args[argidx] == "-show-regs") {
+ show_regs = true;
+ continue;
+ }
+ if (args[argidx] == "-show-public") {
+ show_public = true;
+ continue;
+ }
+ if (args[argidx] == "-show-all") {
+ show_all = true;
+ continue;
+ }
if (args[argidx] == "-ignore_unknown_cells") {
ignore_unknown_cells = true;
continue;
@@ -1331,6 +1347,29 @@ struct SatPass : public Pass {
shows.push_back(it.second->name.str());
}
+ if (show_regs) {
+ pool<Wire*> reg_wires;
+ for (auto cell : module->cells()) {
+ if (cell->type == "$dff" || cell->type.substr(0, 6) == "$_DFF_")
+ for (auto bit : cell->getPort("\\Q"))
+ if (bit.wire)
+ reg_wires.insert(bit.wire);
+ }
+ for (auto wire : reg_wires)
+ shows.push_back(wire->name.str());
+ }
+
+ if (show_public) {
+ for (auto wire : module->wires())
+ if (wire->name[0] == '\\')
+ shows.push_back(wire->name.str());
+ }
+
+ if (show_all) {
+ for (auto wire : module->wires())
+ shows.push_back(wire->name.str());
+ }
+
if (tempinduct)
{
if (loopcount > 0 || max_undef)
diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc
index af4816a72..674cb77cb 100644
--- a/passes/techmap/Makefile.inc
+++ b/passes/techmap/Makefile.inc
@@ -20,6 +20,8 @@ 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
endif
GENFILES += passes/techmap/techmap.inc
diff --git a/passes/techmap/abc.cc b/passes/techmap/abc.cc
index d1e629b5a..5c5148590 100644
--- a/passes/techmap/abc.cc
+++ b/passes/techmap/abc.cc
@@ -100,6 +100,7 @@ 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;
@@ -837,17 +838,28 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
fprintf(f, "GATE ONE 1 Y=CONST1;\n");
fprintf(f, "GATE BUF %d Y=A; PIN * NONINV 1 999 1 0 1 0\n", get_cell_cost("$_BUF_"));
fprintf(f, "GATE NOT %d Y=!A; PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_NOT_"));
- fprintf(f, "GATE AND %d Y=A*B; PIN * NONINV 1 999 1 0 1 0\n", get_cell_cost("$_AND_"));
- fprintf(f, "GATE NAND %d Y=!(A*B); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_NAND_"));
- fprintf(f, "GATE OR %d Y=A+B; PIN * NONINV 1 999 1 0 1 0\n", get_cell_cost("$_OR_"));
- fprintf(f, "GATE NOR %d Y=!(A+B); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_NOR_"));
- fprintf(f, "GATE XOR %d Y=(A*!B)+(!A*B); PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_XOR_"));
- fprintf(f, "GATE XNOR %d Y=(A*B)+(!A*!B); PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_XNOR_"));
- fprintf(f, "GATE AOI3 %d Y=!((A*B)+C); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_AOI3_"));
- fprintf(f, "GATE OAI3 %d Y=!((A+B)*C); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_OAI3_"));
- fprintf(f, "GATE AOI4 %d Y=!((A*B)+(C*D)); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_AOI4_"));
- fprintf(f, "GATE OAI4 %d Y=!((A+B)*(C+D)); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_OAI4_"));
- fprintf(f, "GATE MUX %d Y=(A*B)+(S*B)+(!S*A); PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_MUX_"));
+ if (enabled_gates.empty() || enabled_gates.count("AND"))
+ fprintf(f, "GATE AND %d Y=A*B; PIN * NONINV 1 999 1 0 1 0\n", get_cell_cost("$_AND_"));
+ if (enabled_gates.empty() || enabled_gates.count("NAND"))
+ fprintf(f, "GATE NAND %d Y=!(A*B); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_NAND_"));
+ if (enabled_gates.empty() || enabled_gates.count("OR"))
+ fprintf(f, "GATE OR %d Y=A+B; PIN * NONINV 1 999 1 0 1 0\n", get_cell_cost("$_OR_"));
+ if (enabled_gates.empty() || enabled_gates.count("NOR"))
+ fprintf(f, "GATE NOR %d Y=!(A+B); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_NOR_"));
+ if (enabled_gates.empty() || enabled_gates.count("XOR"))
+ fprintf(f, "GATE XOR %d Y=(A*!B)+(!A*B); PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_XOR_"));
+ if (enabled_gates.empty() || enabled_gates.count("XNOR"))
+ fprintf(f, "GATE XNOR %d Y=(A*B)+(!A*!B); PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_XNOR_"));
+ if (enabled_gates.empty() || enabled_gates.count("AOI3"))
+ fprintf(f, "GATE AOI3 %d Y=!((A*B)+C); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_AOI3_"));
+ if (enabled_gates.empty() || enabled_gates.count("OAI3"))
+ fprintf(f, "GATE OAI3 %d Y=!((A+B)*C); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_OAI3_"));
+ if (enabled_gates.empty() || enabled_gates.count("AOI4"))
+ fprintf(f, "GATE AOI4 %d Y=!((A*B)+(C*D)); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_AOI4_"));
+ if (enabled_gates.empty() || enabled_gates.count("OAI4"))
+ fprintf(f, "GATE OAI4 %d Y=!((A+B)*(C+D)); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_OAI4_"));
+ if (enabled_gates.empty() || enabled_gates.count("MUX"))
+ fprintf(f, "GATE MUX %d Y=(A*B)+(S*B)+(!S*A); PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_MUX_"));
if (map_mux4)
fprintf(f, "GATE MUX4 %d Y=(!S*!T*A)+(S*!T*B)+(!S*T*C)+(S*T*D); PIN * UNKNOWN 1 999 1 0 1 0\n", 2*get_cell_cost("$_MUX_"));
if (map_mux8)
@@ -1075,6 +1087,12 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
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;
@@ -1224,6 +1242,11 @@ struct AbcPass : public Pass {
// 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");
@@ -1274,6 +1297,7 @@ struct AbcPass : public Pass {
map_mux4 = false;
map_mux8 = false;
map_mux16 = false;
+ enabled_gates.clear();
#ifdef _WIN32
if (!check_file_exists(exe_file + ".exe") && check_file_exists(proc_self_dirname() + "..\\yosys-abc.exe"))
@@ -1338,6 +1362,25 @@ struct AbcPass : public Pass {
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;
@@ -1393,7 +1436,7 @@ struct AbcPass : public Pass {
std::set<RTLIL::Cell*> expand_queue_up, next_expand_queue_up;
std::set<RTLIL::Cell*> expand_queue_down, next_expand_queue_down;
- typedef std::tuple<bool, RTLIL::SigSpec, bool, RTLIL::SigSpec> clkdomain_t;
+ typedef tuple<bool, RTLIL::SigSpec, bool, RTLIL::SigSpec> clkdomain_t;
std::map<clkdomain_t, std::vector<RTLIL::Cell*>> assigned_cells;
std::map<RTLIL::Cell*, clkdomain_t> assigned_cells_reverse;
diff --git a/passes/techmap/alumacc.cc b/passes/techmap/alumacc.cc
index 54c9bc1aa..3c7ff4b92 100644
--- a/passes/techmap/alumacc.cc
+++ b/passes/techmap/alumacc.cc
@@ -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);
diff --git a/passes/techmap/dffinit.cc b/passes/techmap/dffinit.cc
index 2215c18e5..e0273f439 100644
--- a/passes/techmap/dffinit.cc
+++ b/passes/techmap/dffinit.cc
@@ -63,16 +63,26 @@ struct DffinitPass : public Pass {
SigMap sigmap(module);
dict<SigBit, State> init_bits;
pool<SigBit> cleanup_bits;
+ pool<SigBit> used_bits;
- for (auto wire : module->selected_wires())
+ for (auto wire : module->selected_wires()) {
if (wire->attributes.count("\\init")) {
Const value = wire->attributes.at("\\init");
- for (int i = 0; i < std::min(GetSize(value), GetSize(wire)); i++)
+ 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;
@@ -90,7 +100,7 @@ struct DffinitPass : public Pass {
for (int i = 0; i < GetSize(sig); i++) {
if (init_bits.count(sig[i]) == 0)
continue;
- while (GetSize(value.bits) < i)
+ while (GetSize(value.bits) <= i)
value.bits.push_back(State::S0);
value.bits[i] = init_bits.at(sig[i]);
cleanup_bits.insert(sig[i]);
@@ -104,11 +114,15 @@ struct DffinitPass : public Pass {
for (auto wire : module->selected_wires())
if (wire->attributes.count("\\init")) {
- Const value = wire->attributes.at("\\init");
+ Const &value = wire->attributes.at("\\init");
bool do_cleanup = true;
- for (int i = 0; i < std::min(GetSize(value), GetSize(wire)); i++)
- if (cleanup_bits.count(sigmap(SigBit(wire, i))) == 0)
+ 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");
diff --git a/passes/techmap/dfflibmap.cc b/passes/techmap/dfflibmap.cc
index 64131c1a8..693651bfd 100644
--- a/passes/techmap/dfflibmap.cc
+++ b/passes/techmap/dfflibmap.cc
@@ -491,6 +491,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") {
diff --git a/passes/techmap/extract.cc b/passes/techmap/extract.cc
index 68a7fc1f6..fc73177ce 100644
--- a/passes/techmap/extract.cc
+++ b/passes/techmap/extract.cc
@@ -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))
@@ -737,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);
diff --git a/passes/techmap/iopadmap.cc b/passes/techmap/iopadmap.cc
index 0e0a2adc9..9dab40ca8 100644
--- a/passes/techmap/iopadmap.cc
+++ b/passes/techmap/iopadmap.cc
@@ -45,10 +45,10 @@ 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 input.\n");
log("\n");
log(" -outpad <celltype> <portname>[:<portname>]\n");
log(" -inoutpad <celltype> <portname>[:<portname>]\n");
diff --git a/passes/techmap/lut2mux.cc b/passes/techmap/lut2mux.cc
new file mode 100644
index 000000000..fe55e499a
--- /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("Executing LUT2MUX pass (mapping to constant drivers).\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 dad1c06ac..d5b8fe804 100644
--- a/passes/techmap/maccmap.cc
+++ b/passes/techmap/maccmap.cc
@@ -134,7 +134,7 @@ struct MaccmapWorker
}
return retval;
#else
- return std::max(n - 1, 0);
+ return max(n - 1, 0);
#endif
}
diff --git a/passes/techmap/muxcover.cc b/passes/techmap/muxcover.cc
index b250c5680..514c3365f 100644
--- a/passes/techmap/muxcover.cc
+++ b/passes/techmap/muxcover.cc
@@ -49,8 +49,8 @@ struct MuxcoverWorker
vector<tree_t> tree_list;
- dict<std::tuple<SigBit, SigBit, SigBit>, std::tuple<SigBit, pool<SigBit>, bool>> decode_mux_cache;
- dict<SigBit, std::tuple<SigBit, SigBit, SigBit>> decode_mux_reverse_cache;
+ 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;
@@ -142,7 +142,7 @@ struct MuxcoverWorker
if (A == B)
return 0;
- std::tuple<SigBit, SigBit, SigBit> key(A, B, sel);
+ 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);
diff --git a/passes/techmap/nlutmap.cc b/passes/techmap/nlutmap.cc
new file mode 100644
index 000000000..7ece40059
--- /dev/null
+++ b/passes/techmap/nlutmap.cc
@@ -0,0 +1,173 @@
+/*
+ * 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;
+};
+
+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 (!available_luts.empty())
+ {
+ 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)
+ 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;
+ }
+
+ 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("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("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;
+ }
+ 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/simplemap.cc b/passes/techmap/simplemap.cc
index 0fb5b3741..f6ac3964b 100644
--- a/passes/techmap/simplemap.cc
+++ b/passes/techmap/simplemap.cc
@@ -247,7 +247,7 @@ 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);
@@ -293,7 +293,7 @@ void simplemap_tribuf(RTLIL::Module *module, RTLIL::Cell *cell)
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[i]);
+ gate->setPort("\\E", sig_e);
gate->setPort("\\Y", sig_y[i]);
}
}
diff --git a/passes/techmap/techmap.cc b/passes/techmap/techmap.cc
index 30a6d784f..19b2bda9c 100644
--- a/passes/techmap/techmap.cc
+++ b/passes/techmap/techmap.cc
@@ -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();
@@ -257,7 +257,7 @@ struct TechmapWorker
} 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++) {
+ 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]);
diff --git a/passes/tests/test_cell.cc b/passes/tests/test_cell.cc
index abac62231..a8fcac9bc 100644
--- a/passes/tests/test_cell.cc
+++ b/passes/tests/test_cell.cc
@@ -256,7 +256,7 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type,
case 2:
n = xorshift32(GetSize(sig));
m = xorshift32(GetSize(sig));
- for (int i = std::min(n, m); i < std::max(n, m); i++)
+ for (int i = min(n, m); i < max(n, m); i++)
sig[i] = xorshift32(2) == 1 ? RTLIL::S1 : RTLIL::S0;
break;
}
@@ -552,6 +552,9 @@ struct TestCellPass : public Pass {
log(" -nosat\n");
log(" do not check SAT model or run SAT equivalence checking\n");
log("\n");
+ log(" -noeval\n");
+ log(" do not check const-eval models\n");
+ log("\n");
log(" -v\n");
log(" print additional debug information to the console\n");
log("\n");
@@ -570,6 +573,7 @@ struct TestCellPass : public Pass {
bool verbose = false;
bool constmode = false;
bool nosat = false;
+ bool noeval = false;
int argidx;
for (argidx = 1; argidx < GetSize(args); argidx++)
@@ -619,6 +623,10 @@ struct TestCellPass : public Pass {
nosat = true;
continue;
}
+ if (args[argidx] == "-noeval") {
+ noeval = true;
+ continue;
+ }
if (args[argidx] == "-v") {
verbose = true;
continue;
@@ -772,7 +780,8 @@ struct TestCellPass : public Pass {
Backend::backend_call(design, &vlog_file, "<test_cell -vlog>", "verilog -selected -noexpr");
uut_names.push_back(uut_name);
}
- run_eval_test(design, verbose, nosat, uut_name, vlog_file);
+ if (!noeval)
+ run_eval_test(design, verbose, nosat, uut_name, vlog_file);
}
delete design;
}