diff options
Diffstat (limited to 'passes')
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 ¶ : 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; } |