mirror of
https://github.com/YosysHQ/yosys
synced 2026-07-25 08:32:34 +00:00
293 lines
8.2 KiB
C++
293 lines
8.2 KiB
C++
/*
|
|
* yosys -- Yosys Open SYnthesis Suite
|
|
*
|
|
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
|
|
*
|
|
* 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 <functional>
|
|
#include <queue>
|
|
#include <ranges>
|
|
|
|
USING_YOSYS_NAMESPACE
|
|
PRIVATE_NAMESPACE_BEGIN
|
|
|
|
// An "object" is a cell or a wire.
|
|
// An object is "private" if its name starts with a '$'.
|
|
//
|
|
// The "autoname" pass renames private objects based on public object names,
|
|
// with suffixes added to show the relationship to the public objects.
|
|
// The pass chooses the "best" names based on minimizing their "cost".
|
|
|
|
// The cost of a new name for a private object is the cost along a path
|
|
// to a publicly named object.
|
|
struct cost {
|
|
// score is a property of one "edge", not of the entire path.
|
|
// 0 if the neighbour drives the object, and the wire's fanout otherwise
|
|
unsigned int score = UINT_MAX;
|
|
// New name length
|
|
size_t length = SIZE_MAX;
|
|
// Earlier discovered connection wins
|
|
int edge_pos = 0;
|
|
|
|
auto operator<=>(const cost &) const = default;
|
|
};
|
|
|
|
// Endpoints of an "edge" are always a cell index and a wire index. Goofy, I know.
|
|
// SigSig connections (module->connections()) don't count
|
|
// as neighbors. This means that the equivalent of `assign $w1 = \w2;` won't lead
|
|
// to $w1 being renamed.
|
|
struct Edge {
|
|
// Cell port name at which the wire connects to the cell
|
|
IdString port;
|
|
int cell;
|
|
int wire;
|
|
bool cell_is_output;
|
|
unsigned int score;
|
|
// Edge's position in the edge list
|
|
int pos_in_cell;
|
|
int pos_in_wire;
|
|
};
|
|
|
|
// decide() finds shortest names for every object reachable from a public name,
|
|
// cheapest-first. It doesn't apply the renames to the objects yet.
|
|
//
|
|
// commit() then renames in decide() order, which is topological, so a source
|
|
// neighbour always already carries its final uniquify()d name.
|
|
|
|
// A selected cell or wire
|
|
struct node {
|
|
// Exactly one of cell/wire is set
|
|
Cell *cell = nullptr;
|
|
Wire *wire = nullptr;
|
|
vector<Edge> edges;
|
|
// Ignores module->connections() just as the rest of the code
|
|
unsigned int fanout = 0;
|
|
bool is_public = false;
|
|
bool renameable = false;
|
|
bool selected = false;
|
|
|
|
size_t name_length = 0;
|
|
// Node index from which we want to construct the rename
|
|
int from_node = -1;
|
|
// Cost for the edge from that node
|
|
cost c;
|
|
// Suffix to append to the name of that node
|
|
string suffix;
|
|
// Is this name final?
|
|
bool decided = false;
|
|
|
|
const IdString& name() const { return cell ? cell->name : wire->name; }
|
|
};
|
|
|
|
// Decides the order of exploring neighbors
|
|
struct queue_item {
|
|
unsigned int score;
|
|
size_t length;
|
|
int index;
|
|
|
|
auto operator<=>(const queue_item &) const = default;
|
|
};
|
|
|
|
struct ModuleAutonamer
|
|
{
|
|
Module *module;
|
|
|
|
// Cells in module order, then wires in the order that they're seen from cells.
|
|
// The index doubles as the tie-break between equally good
|
|
// proposals for different nodes
|
|
vector<node> nodes;
|
|
vector<int> decided;
|
|
std::priority_queue<queue_item, std::vector<queue_item>, std::greater<>> queue;
|
|
int renamed = 0;
|
|
|
|
ModuleAutonamer(Module *module) : module(module) { build_adjacency(); }
|
|
|
|
void build_adjacency()
|
|
{
|
|
vector<Cell*> cells(module->cells().begin(), module->cells().end());
|
|
// Arbitrary. Kinda just for passing tests without modifying them
|
|
for (auto cell : cells | std::views::reverse)
|
|
nodes.emplace_back().cell = cell;
|
|
int ncells = GetSize(nodes);
|
|
|
|
idict<Wire*> wire_ids;
|
|
|
|
for (int ci = 0; ci < ncells; ci++) {
|
|
Cell *cell = nodes[ci].cell;
|
|
for (auto &conn : cell->connections()) {
|
|
bool cell_is_output = cell->output(conn.first);
|
|
pool<Wire*> seen_in_this_port;
|
|
for (auto bit : conn.second) {
|
|
if (bit.wire == nullptr)
|
|
continue;
|
|
int wi = ncells + wire_ids(bit.wire);
|
|
if (wi == GetSize(nodes))
|
|
nodes.emplace_back().wire = bit.wire;
|
|
nodes[wi].fanout++;
|
|
if (!seen_in_this_port.insert(bit.wire).second)
|
|
continue;
|
|
Edge edge{
|
|
.port = conn.first,
|
|
.cell = ci,
|
|
.wire = wi,
|
|
.cell_is_output = cell_is_output,
|
|
.score = 0, // no fanouts are final yet
|
|
.pos_in_cell = GetSize(nodes[ci].edges),
|
|
.pos_in_wire = GetSize(nodes[wi].edges),
|
|
};
|
|
nodes[ci].edges.push_back(edge);
|
|
nodes[wi].edges.push_back(edge);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Resolve selection before renaming
|
|
for (auto &nd : nodes) {
|
|
IdString name = nd.name();
|
|
nd.selected = nd.cell ? module->selected(nd.cell) : module->selected(nd.wire);
|
|
nd.is_public = (name[0] != '$');
|
|
nd.renameable = !nd.is_public && (nd.cell || nd.wire->port_id == 0);
|
|
if (nd.is_public)
|
|
nd.name_length = name.str().size();
|
|
}
|
|
|
|
// Only possible once every fanout is known
|
|
for (auto &nd : nodes)
|
|
for (auto &edge : nd.edges)
|
|
edge.score = edge.cell_is_output ? 0 : nodes[edge.wire].fanout;
|
|
}
|
|
|
|
void offer(int from, int to, const Edge &edge, int edge_pos)
|
|
{
|
|
node &nd = nodes[to];
|
|
if (!nd.renameable || nd.decided)
|
|
return;
|
|
string suffix = nd.cell
|
|
? stringf("_%s_%s", nd.cell->type.unescape(), edge.port.unescape())
|
|
: stringf("_%s", edge.port.unescape());
|
|
cost c{edge.score, nodes[from].name_length + suffix.length(), edge_pos};
|
|
if (c >= nd.c)
|
|
return;
|
|
nd.c = c;
|
|
nd.from_node = from;
|
|
nd.name_length = c.length;
|
|
nd.suffix = std::move(suffix);
|
|
queue.push(queue_item{c.score, c.length, to});
|
|
}
|
|
|
|
// Expand a public (or newly decided) node. The name it lends
|
|
// is already settled and the neighbour can keep what offer() built
|
|
void expand(int n)
|
|
{
|
|
const node &nd = nodes[n];
|
|
for (auto &edge : nd.edges)
|
|
if (nd.cell)
|
|
offer(n, edge.wire, edge, edge.pos_in_wire);
|
|
else
|
|
offer(n, edge.cell, edge, edge.pos_in_cell);
|
|
}
|
|
|
|
void decide()
|
|
{
|
|
for (int n = 0; n < GetSize(nodes); n++)
|
|
if (nodes[n].is_public)
|
|
expand(n);
|
|
|
|
while (!queue.empty()) {
|
|
int n = queue.top().index;
|
|
queue.pop();
|
|
if (nodes[n].decided)
|
|
continue;
|
|
nodes[n].decided = true;
|
|
decided.push_back(n);
|
|
expand(n);
|
|
}
|
|
}
|
|
|
|
void append_name(int n, string &out)
|
|
{
|
|
const node &nd = nodes[n];
|
|
if (nd.is_public || nd.selected)
|
|
return nd.name().append_to(&out);
|
|
append_name(nd.from_node, out);
|
|
out += nd.suffix;
|
|
}
|
|
|
|
void commit(int n)
|
|
{
|
|
node &nd = nodes[n];
|
|
if (!nd.selected)
|
|
return;
|
|
string full;
|
|
full.reserve(nd.name_length);
|
|
append_name(nd.from_node, full);
|
|
full += nd.suffix;
|
|
IdString name = module->uniquify(IdString(full));
|
|
if (nd.cell) {
|
|
log_debug("Rename cell %s in %s to %s.\n", nd.cell, module, name.unescape());
|
|
module->rename(nd.cell, name);
|
|
} else {
|
|
log_debug("Rename wire %s in %s to %s.\n", nd.wire, module, name.unescape());
|
|
module->rename(nd.wire, name);
|
|
}
|
|
renamed++;
|
|
}
|
|
|
|
void run()
|
|
{
|
|
decide();
|
|
for (int n : decided)
|
|
commit(n);
|
|
if (renamed > 0)
|
|
log("Renamed %d objects in module %s.\n", renamed, module);
|
|
}
|
|
};
|
|
|
|
struct AutonamePass : public Pass {
|
|
AutonamePass() : Pass("autoname", "automatically assign names to objects") { }
|
|
void help() override
|
|
{
|
|
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
|
log("\n");
|
|
log(" autoname [selection]\n");
|
|
log("\n");
|
|
log("Assign auto-generated public names to objects with private names (the ones\n");
|
|
log("with $-prefix).\n");
|
|
log("\n");
|
|
}
|
|
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
|
{
|
|
size_t argidx;
|
|
for (argidx = 1; argidx < args.size(); argidx++)
|
|
{
|
|
// if (args[argidx] == "-foo") {
|
|
// foo = true;
|
|
// continue;
|
|
// }
|
|
break;
|
|
}
|
|
extra_args(args, argidx, design);
|
|
|
|
log_header(design, "Executing AUTONAME pass.\n");
|
|
|
|
for (auto module : design->selected_modules())
|
|
ModuleAutonamer(module).run();
|
|
}
|
|
} AutonamePass;
|
|
|
|
PRIVATE_NAMESPACE_END
|