2015-09-20 11:28:46 -05:00
|
|
|
/*
|
|
|
|
* 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;
|
2016-04-28 16:17:30 -05:00
|
|
|
bool verbose;
|
2015-09-20 11:28:46 -05:00
|
|
|
double grid;
|
|
|
|
|
2015-09-20 15:36:35 -05:00
|
|
|
std::ofstream dump_file;
|
|
|
|
|
2015-09-20 11:28:46 -05:00
|
|
|
QwpConfig() {
|
|
|
|
ltr = false;
|
|
|
|
alpha = false;
|
2016-04-28 16:17:30 -05:00
|
|
|
verbose = false;
|
2015-09-20 11:28:46 -05:00
|
|
|
grid = 1.0 / 16;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct QwpWorker
|
|
|
|
{
|
2015-09-20 15:36:35 -05:00
|
|
|
QwpConfig &config;
|
2015-09-20 11:28:46 -05:00
|
|
|
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;
|
|
|
|
|
2015-09-20 15:36:35 -05:00
|
|
|
// 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)
|
2015-09-20 11:28:46 -05:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-20 18:05:13 -05:00
|
|
|
void solve(bool alt_mode = false)
|
2015-09-20 11:28:46 -05:00
|
|
|
{
|
2015-09-26 03:42:27 -05:00
|
|
|
// A := constraint_matrix
|
|
|
|
// y := constraint_rhs_vector
|
2015-09-20 11:28:46 -05:00
|
|
|
//
|
|
|
|
// AA = A' * A
|
|
|
|
// Ay = A' * y
|
|
|
|
//
|
|
|
|
// M := [AA Ay]
|
|
|
|
|
2016-04-28 16:17:30 -05:00
|
|
|
if (config.verbose)
|
|
|
|
log("> System size: %d^2\n", GetSize(nodes));
|
|
|
|
|
2015-09-20 11:28:46 -05:00
|
|
|
// Row major order
|
2015-09-24 14:50:37 -05:00
|
|
|
int N = GetSize(nodes), N1 = N+1;
|
|
|
|
vector<double> M(N * N1);
|
|
|
|
|
2016-04-28 16:17:30 -05:00
|
|
|
if (config.verbose)
|
|
|
|
log("> Edge constraints: %d\n", GetSize(edges));
|
|
|
|
|
2015-09-26 03:42:27 -05:00
|
|
|
// 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.
|
2015-09-24 14:50:37 -05:00
|
|
|
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;
|
2015-09-20 11:28:46 -05:00
|
|
|
}
|
|
|
|
|
2016-04-28 16:17:30 -05:00
|
|
|
if (config.verbose)
|
|
|
|
log("> Node constraints: %d\n", GetSize(nodes));
|
|
|
|
|
2015-09-26 03:42:27 -05:00
|
|
|
// 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.
|
2015-09-24 14:50:37 -05:00
|
|
|
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;
|
2015-09-20 11:28:46 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#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
|
|
|
|
|
2016-04-28 16:17:30 -05:00
|
|
|
if (config.verbose)
|
|
|
|
log("> Solving\n");
|
|
|
|
|
2015-09-20 11:28:46 -05:00
|
|
|
// Solve "AA*x = Ay"
|
|
|
|
// (least squares fit for "A*x = y")
|
|
|
|
//
|
2015-09-21 03:27:18 -05:00
|
|
|
// Using gaussian elimination to get M := [Id x]
|
2015-09-20 11:28:46 -05:00
|
|
|
|
2015-09-24 15:16:37 -05:00
|
|
|
vector<int> pivot_cache;
|
|
|
|
vector<int> queue;
|
|
|
|
|
|
|
|
for (int i = 0; i < N; i++)
|
|
|
|
queue.push_back(i);
|
|
|
|
|
|
|
|
// gaussian elimination
|
2015-09-20 11:28:46 -05:00
|
|
|
for (int i = 0; i < N; i++)
|
|
|
|
{
|
2019-05-01 08:06:46 -05:00
|
|
|
if (config.verbose && N > 15 && ((i+1) % (N/15)) == 0)
|
2016-04-28 16:17:30 -05:00
|
|
|
log("> Solved %d%%: %d/%d\n", (100*(i+1))/N, i+1, N);
|
|
|
|
|
2015-09-24 15:16:37 -05:00
|
|
|
// 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();
|
|
|
|
|
2015-09-20 11:28:46 -05:00
|
|
|
// normalize row
|
2015-09-24 15:16:37 -05:00
|
|
|
for (int k = i+1; k < N1; k++)
|
|
|
|
M[k + row*N1] /= M[i + row*N1];
|
|
|
|
M[i + row*N1] = 1.0;
|
2015-09-20 11:28:46 -05:00
|
|
|
|
|
|
|
// elimination
|
2015-09-24 15:16:37 -05:00
|
|
|
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;
|
2015-09-20 11:28:46 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-28 16:17:30 -05:00
|
|
|
if (config.verbose)
|
|
|
|
log("> Solved\n");
|
|
|
|
|
2015-09-24 15:16:37 -05:00
|
|
|
log_assert(queue.empty());
|
|
|
|
log_assert(GetSize(pivot_cache) == N);
|
|
|
|
|
2015-09-20 11:28:46 -05:00
|
|
|
// back substitution
|
|
|
|
for (int i = N-1; i >= 0; i--)
|
|
|
|
for (int j = i+1; j < N; j++)
|
|
|
|
{
|
2015-09-24 15:16:37 -05:00
|
|
|
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;
|
2015-09-20 11:28:46 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#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
|
|
|
|
|
2016-04-28 16:17:30 -05:00
|
|
|
if (config.verbose)
|
|
|
|
log("> Update nodes\n");
|
|
|
|
|
2015-09-20 11:28:46 -05:00
|
|
|
// update node positions
|
|
|
|
for (int i = 0; i < N; i++)
|
2015-09-21 03:27:18 -05:00
|
|
|
{
|
|
|
|
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)) {
|
2015-10-25 13:30:49 -05:00
|
|
|
v = min(v, c+r);
|
|
|
|
v = max(v, c-r);
|
2015-09-21 03:27:18 -05:00
|
|
|
} else {
|
|
|
|
v = c;
|
|
|
|
}
|
|
|
|
|
2015-09-20 18:05:13 -05:00
|
|
|
if (alt_mode) {
|
|
|
|
if (!nodes[i].alt_tied)
|
2015-09-21 03:27:18 -05:00
|
|
|
nodes[i].alt_pos = v;
|
2015-09-20 18:05:13 -05:00
|
|
|
} else {
|
|
|
|
if (!nodes[i].tied)
|
2015-09-21 03:27:18 -05:00
|
|
|
nodes[i].pos = v;
|
2015-09-20 18:05:13 -05:00
|
|
|
}
|
2015-09-21 03:27:18 -05:00
|
|
|
}
|
2015-09-20 11:28:46 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
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");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-20 18:05:13 -05:00
|
|
|
void dump_svg(const pool<int> *green_nodes = nullptr, double median = -1)
|
2015-09-20 15:36:35 -05:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
|
2015-09-20 18:05:13 -05:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2015-09-20 15:36:35 -05:00
|
|
|
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;
|
|
|
|
|
2015-09-20 18:05:13 -05:00
|
|
|
x1 = 120 + 100 * (x1 - x_center) / x_radius;
|
|
|
|
y1 = 120 + 100 * (y1 - y_center) / y_radius;
|
2015-09-20 15:36:35 -05:00
|
|
|
|
2015-09-20 18:05:13 -05:00
|
|
|
x2 = 120 + 100 * (x2 - x_center) / x_radius;
|
|
|
|
y2 = 120 + 100 * (y2 - y_center) / y_radius;
|
2015-09-20 15:36:35 -05:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2015-09-20 18:05:13 -05:00
|
|
|
x = 120 + 100 * (x - x_center) / x_radius;
|
|
|
|
y = 120 + 100 * (y - y_center) / y_radius;
|
2015-09-20 15:36:35 -05:00
|
|
|
|
|
|
|
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");
|
|
|
|
}
|
|
|
|
|
2015-09-26 03:42:27 -05:00
|
|
|
void run_worker(int indent)
|
2015-09-20 11:28:46 -05:00
|
|
|
{
|
|
|
|
int count_cells = 0;
|
|
|
|
|
|
|
|
for (auto &node : nodes)
|
|
|
|
if (node.cell != nullptr)
|
|
|
|
count_cells++;
|
|
|
|
|
|
|
|
for (int i = 0; i < indent; i++)
|
|
|
|
log(" ");
|
|
|
|
|
2015-09-20 15:36:35 -05:00
|
|
|
string range_str;
|
|
|
|
|
2015-09-20 11:28:46 -05:00
|
|
|
if (direction == 'x')
|
2015-09-20 15:36:35 -05:00
|
|
|
range_str = stringf("X=%.2f:%.2f, Y=%.2f:%.2f",
|
|
|
|
midpos - radius, midpos + radius,
|
|
|
|
alt_midpos - alt_radius, alt_midpos + alt_radius);
|
2015-09-20 11:28:46 -05:00
|
|
|
else
|
2015-09-20 15:36:35 -05:00
|
|
|
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));
|
2015-09-20 11:28:46 -05:00
|
|
|
|
|
|
|
solve();
|
2015-09-20 18:05:13 -05:00
|
|
|
solve(true);
|
2015-09-20 11:28:46 -05:00
|
|
|
|
2015-09-20 15:36:35 -05:00
|
|
|
// detect median position and check for break condition
|
2015-09-20 11:28:46 -05:00
|
|
|
|
|
|
|
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));
|
|
|
|
|
2015-09-20 15:36:35 -05:00
|
|
|
std::sort(sorted_pos.begin(), sorted_pos.end());
|
2015-09-20 18:05:13 -05:00
|
|
|
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);
|
2015-09-20 15:36:35 -05:00
|
|
|
|
|
|
|
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;
|
2015-09-20 18:05:13 -05:00
|
|
|
for (int i = 0; i < median_sidx; i++)
|
2015-09-20 15:36:35 -05:00
|
|
|
green_nodes.insert(sorted_pos[i].second);
|
|
|
|
|
2015-09-20 18:05:13 -05:00
|
|
|
dump_svg(&green_nodes, median);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto &node : nodes)
|
|
|
|
{
|
|
|
|
double rel_pos = node.pos - median;
|
2015-09-21 03:27:18 -05:00
|
|
|
if (rel_pos < 0) {
|
2015-09-20 18:05:13 -05:00
|
|
|
node.pos = midpos + left_scale*rel_pos;
|
2015-09-21 03:27:18 -05:00
|
|
|
if (std::isfinite(node.pos)) {
|
2015-10-25 13:30:49 -05:00
|
|
|
node.pos = min(node.pos, midpos);
|
|
|
|
node.pos = max(node.pos, midpos - radius);
|
2015-09-21 03:27:18 -05:00
|
|
|
} else
|
|
|
|
node.pos = midpos - radius/2;
|
|
|
|
} else {
|
2015-09-20 18:05:13 -05:00
|
|
|
node.pos = midpos + right_scale*rel_pos;
|
2015-09-21 03:27:18 -05:00
|
|
|
if (std::isfinite(node.pos)) {
|
2015-10-25 13:30:49 -05:00
|
|
|
node.pos = max(node.pos, midpos);
|
|
|
|
node.pos = min(node.pos, midpos + radius);
|
2015-09-21 03:27:18 -05:00
|
|
|
} else
|
|
|
|
node.pos = midpos + radius/2;
|
|
|
|
}
|
2015-09-20 15:36:35 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if (GetSize(sorted_pos) < 2 || (2*radius <= config.grid && 2*alt_radius <= config.grid)) {
|
2015-09-20 11:28:46 -05:00
|
|
|
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;
|
|
|
|
|
2015-09-20 18:05:13 -05:00
|
|
|
if (k < median_sidx) {
|
2015-09-20 11:28:46 -05:00
|
|
|
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;
|
|
|
|
|
2015-09-20 18:05:13 -05:00
|
|
|
if (left_idx1 >= 0 && left_worker.nodes[left_idx1].cell && left_idx2 < 0) {
|
2015-09-20 11:28:46 -05:00
|
|
|
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
|
2015-09-20 18:05:13 -05:00
|
|
|
if (left_idx2 >= 0 && left_worker.nodes[left_idx2].cell && left_idx1 < 0) {
|
2015-09-20 11:28:46 -05:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2015-09-20 18:05:13 -05:00
|
|
|
if (right_idx1 >= 0 && right_worker.nodes[right_idx1].cell && right_idx2 < 0) {
|
2015-09-20 11:28:46 -05:00
|
|
|
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
|
2015-09-20 18:05:13 -05:00
|
|
|
if (right_idx2 >= 0 && right_worker.nodes[right_idx2].cell && right_idx1 < 0) {
|
2015-09-20 11:28:46 -05:00
|
|
|
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
|
|
|
|
|
2015-09-20 15:36:35 -05:00
|
|
|
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);
|
2015-09-20 11:28:46 -05:00
|
|
|
|
|
|
|
// 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;
|
|
|
|
}
|
2015-09-20 18:05:13 -05:00
|
|
|
|
|
|
|
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();
|
|
|
|
}
|
2015-09-20 11:28:46 -05:00
|
|
|
}
|
|
|
|
|
2015-09-24 14:22:24 -05:00
|
|
|
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) {
|
2015-10-25 13:30:49 -05:00
|
|
|
min_value = min(min_value, v);
|
|
|
|
max_value = max(max_value, v);
|
2015-09-24 14:22:24 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2015-10-25 13:30:49 -05:00
|
|
|
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));
|
2015-09-24 14:22:24 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2015-09-20 11:28:46 -05:00
|
|
|
void run()
|
|
|
|
{
|
2015-09-24 14:22:24 -05:00
|
|
|
log("\n");
|
2015-09-20 11:28:46 -05:00
|
|
|
log("Running qwp on module %s..\n", log_id(module));
|
|
|
|
|
2015-09-20 15:36:35 -05:00
|
|
|
if (config.dump_file.is_open())
|
|
|
|
config.dump_file << stringf("<h3>QWP protocol for module %s:</h3>\n", log_id(module));
|
|
|
|
|
2015-09-20 11:28:46 -05:00
|
|
|
load_module();
|
2015-09-20 15:36:35 -05:00
|
|
|
|
|
|
|
midpos = 0.5;
|
|
|
|
radius = 0.5;
|
|
|
|
alt_midpos = 0.5;
|
|
|
|
alt_radius = 0.5;
|
|
|
|
run_worker(1);
|
2015-09-20 11:28:46 -05:00
|
|
|
|
|
|
|
for (auto &node : nodes)
|
|
|
|
if (node.cell != nullptr)
|
|
|
|
node.cell->attributes["\\qwp_position"] = stringf("%f %f", node.pos, node.alt_pos);
|
2015-09-24 14:22:24 -05:00
|
|
|
|
|
|
|
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);
|
2015-09-20 11:28:46 -05:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct QwpPass : public Pass {
|
|
|
|
QwpPass() : Pass("qwp", "quadratic wirelength placer") { }
|
2018-07-21 01:41:18 -05:00
|
|
|
void help() YS_OVERRIDE
|
2015-09-20 11:28:46 -05:00
|
|
|
{
|
|
|
|
// |---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");
|
2015-09-20 15:36:35 -05:00
|
|
|
log(" -dump <html_file_name>\n");
|
|
|
|
log(" Dump a protocol of the placement algorithm to the html file.\n");
|
|
|
|
log("\n");
|
2016-04-28 16:17:30 -05:00
|
|
|
log(" -v\n");
|
|
|
|
log(" Verbose solver output for profiling or debugging\n");
|
|
|
|
log("\n");
|
2015-09-26 03:42:27 -05:00
|
|
|
log("Note: This implementation of a quadratic wirelength placer uses exact\n");
|
2015-09-20 11:28:46 -05:00
|
|
|
log("dense matrix operations. It is only a toy-placer for small circuits.\n");
|
|
|
|
log("\n");
|
|
|
|
}
|
2018-07-21 01:41:18 -05:00
|
|
|
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
2015-09-20 11:28:46 -05:00
|
|
|
{
|
|
|
|
QwpConfig config;
|
|
|
|
xorshift32_state = 123456789;
|
|
|
|
|
2016-04-21 16:28:37 -05:00
|
|
|
log_header(design, "Executing QWP pass (quadratic wirelength placer).\n");
|
2015-09-20 11:28:46 -05:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2016-04-28 16:17:30 -05:00
|
|
|
if (args[argidx] == "-v") {
|
|
|
|
config.verbose = true;
|
|
|
|
continue;
|
|
|
|
}
|
2015-09-20 11:28:46 -05:00
|
|
|
if (args[argidx] == "-grid" && argidx+1 < args.size()) {
|
|
|
|
config.grid = 1.0 / atoi(args[++argidx].c_str());
|
|
|
|
continue;
|
|
|
|
}
|
2015-09-20 15:36:35 -05:00
|
|
|
if (args[argidx] == "-dump" && argidx+1 < args.size()) {
|
|
|
|
config.dump_file.open(args[++argidx], std::ofstream::trunc);
|
2018-01-07 09:36:13 -06:00
|
|
|
yosys_output_files.insert(args[argidx]);
|
2015-09-20 15:36:35 -05:00
|
|
|
continue;
|
|
|
|
}
|
2015-09-20 11:28:46 -05:00
|
|
|
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
|