mirror of
https://github.com/YosysHQ/yosys
synced 2025-04-12 20:18:20 +00:00
write_cxxrtl: elide wires for results of comb cells used once.
This results in massive gains in performance, equally massive reduction in compile time, and improved readability.
This commit is contained in:
parent
d20e971725
commit
d6d7273421
|
@ -26,7 +26,175 @@
|
||||||
USING_YOSYS_NAMESPACE
|
USING_YOSYS_NAMESPACE
|
||||||
PRIVATE_NAMESPACE_BEGIN
|
PRIVATE_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
static bool is_unary_cell(RTLIL::IdString type)
|
||||||
|
{
|
||||||
|
return type.in(
|
||||||
|
ID($not), ID($logic_not), ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_xnor), ID($reduce_bool),
|
||||||
|
ID($pos), ID($neg));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool is_binary_cell(RTLIL::IdString type)
|
||||||
|
{
|
||||||
|
return type.in(
|
||||||
|
ID($and), ID($or), ID($xor), ID($xnor), ID($logic_and), ID($logic_or),
|
||||||
|
ID($shl), ID($sshl), ID($shr), ID($sshr), ID($shift), ID($shiftx),
|
||||||
|
ID($eq), ID($ne), ID($eqx), ID($nex), ID($gt), ID($ge), ID($lt), ID($le),
|
||||||
|
ID($add), ID($sub), ID($mul), ID($div), ID($mod));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool is_elidable_cell(RTLIL::IdString type)
|
||||||
|
{
|
||||||
|
return is_unary_cell(type) || is_binary_cell(type) || type == ID($mux);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool is_ff_cell(RTLIL::IdString type)
|
||||||
|
{
|
||||||
|
return type.in(
|
||||||
|
ID($dff), ID($dffe), ID($adff), ID($dffsr));
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FlowGraph {
|
||||||
|
struct Node {
|
||||||
|
enum class Type {
|
||||||
|
CONNECT,
|
||||||
|
CELL,
|
||||||
|
PROCESS
|
||||||
|
};
|
||||||
|
|
||||||
|
Type type;
|
||||||
|
RTLIL::SigSig connect = {};
|
||||||
|
const RTLIL::Cell *cell = NULL;
|
||||||
|
const RTLIL::Process *process = NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<Node*> nodes;
|
||||||
|
dict<const RTLIL::Wire*, pool<Node*, hash_ptr_ops>> wire_defs, wire_uses;
|
||||||
|
dict<const RTLIL::Wire*, bool> wire_def_elidable, wire_use_elidable;
|
||||||
|
|
||||||
|
~FlowGraph()
|
||||||
|
{
|
||||||
|
for (auto node : nodes)
|
||||||
|
delete node;
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_defs(Node *node, const RTLIL::SigSpec &sig, bool elidable)
|
||||||
|
{
|
||||||
|
for (auto chunk : sig.chunks())
|
||||||
|
if (chunk.wire)
|
||||||
|
wire_defs[chunk.wire].insert(node);
|
||||||
|
// Only defs of an entire wire in the right order can be elided.
|
||||||
|
if (sig.is_wire())
|
||||||
|
wire_def_elidable[sig.as_wire()] = elidable;
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_uses(Node *node, const RTLIL::SigSpec &sig)
|
||||||
|
{
|
||||||
|
for (auto chunk : sig.chunks())
|
||||||
|
if (chunk.wire) {
|
||||||
|
wire_uses[chunk.wire].insert(node);
|
||||||
|
// Only a single use of an entire wire in the right order can be elided.
|
||||||
|
// (But the use can include other chunks.)
|
||||||
|
if (!wire_use_elidable.count(chunk.wire))
|
||||||
|
wire_use_elidable[chunk.wire] = true;
|
||||||
|
else
|
||||||
|
wire_use_elidable[chunk.wire] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_elidable(const RTLIL::Wire *wire) const
|
||||||
|
{
|
||||||
|
if (wire_def_elidable.count(wire) && wire_use_elidable.count(wire))
|
||||||
|
return wire_def_elidable.at(wire) && wire_use_elidable.at(wire);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connections
|
||||||
|
void add_connect_defs_uses(Node *node, const RTLIL::SigSig &conn)
|
||||||
|
{
|
||||||
|
add_defs(node, conn.first, /*elidable=*/true);
|
||||||
|
add_uses(node, conn.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_node(const RTLIL::SigSig &conn)
|
||||||
|
{
|
||||||
|
Node *node = new Node;
|
||||||
|
node->type = Node::Type::CONNECT;
|
||||||
|
node->connect = conn;
|
||||||
|
nodes.push_back(node);
|
||||||
|
add_connect_defs_uses(node, conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cells
|
||||||
|
void add_cell_defs_uses(Node *node, const RTLIL::Cell *cell)
|
||||||
|
{
|
||||||
|
log_assert(cell->known());
|
||||||
|
for (auto conn : cell->connections()) {
|
||||||
|
if (cell->output(conn.first)) {
|
||||||
|
if (is_ff_cell(cell->type))
|
||||||
|
/* non-combinatorial outputs do not introduce defs */;
|
||||||
|
else if (is_elidable_cell(cell->type))
|
||||||
|
add_defs(node, conn.second, /*elidable=*/true);
|
||||||
|
else
|
||||||
|
add_defs(node, conn.second, /*elidable=*/false);
|
||||||
|
}
|
||||||
|
if (cell->input(conn.first))
|
||||||
|
add_uses(node, conn.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_node(const RTLIL::Cell *cell)
|
||||||
|
{
|
||||||
|
Node *node = new Node;
|
||||||
|
node->type = Node::Type::CELL;
|
||||||
|
node->cell = cell;
|
||||||
|
nodes.push_back(node);
|
||||||
|
add_cell_defs_uses(node, cell);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Processes
|
||||||
|
void add_case_defs_uses(Node *node, const RTLIL::CaseRule *case_)
|
||||||
|
{
|
||||||
|
for (auto &action : case_->actions) {
|
||||||
|
add_defs(node, action.first, /*elidable=*/false);
|
||||||
|
add_uses(node, action.second);
|
||||||
|
}
|
||||||
|
for (auto sub_switch : case_->switches) {
|
||||||
|
add_uses(node, sub_switch->signal);
|
||||||
|
for (auto sub_case : sub_switch->cases) {
|
||||||
|
for (auto &compare : sub_case->compare)
|
||||||
|
add_uses(node, compare);
|
||||||
|
add_case_defs_uses(node, sub_case);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_process_defs_uses(Node *node, const RTLIL::Process *process)
|
||||||
|
{
|
||||||
|
add_case_defs_uses(node, &process->root_case);
|
||||||
|
for (auto sync : process->syncs)
|
||||||
|
for (auto action : sync->actions) {
|
||||||
|
if (sync->type == RTLIL::STp || sync->type == RTLIL::STn || sync->type == RTLIL::STe)
|
||||||
|
/* sync actions do not introduce feedback */;
|
||||||
|
else
|
||||||
|
add_defs(node, action.first, /*elidable=*/false);
|
||||||
|
add_uses(node, action.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_node(const RTLIL::Process *process)
|
||||||
|
{
|
||||||
|
Node *node = new Node;
|
||||||
|
node->type = Node::Type::PROCESS;
|
||||||
|
node->process = process;
|
||||||
|
nodes.push_back(node);
|
||||||
|
add_process_defs_uses(node, process);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
struct CxxrtlWorker {
|
struct CxxrtlWorker {
|
||||||
|
bool elide_internal = false;
|
||||||
|
bool elide_public = false;
|
||||||
|
|
||||||
std::ostream &f;
|
std::ostream &f;
|
||||||
std::string indent;
|
std::string indent;
|
||||||
int temporary = 0;
|
int temporary = 0;
|
||||||
|
@ -35,6 +203,7 @@ struct CxxrtlWorker {
|
||||||
pool<const RTLIL::Wire*> sync_wires;
|
pool<const RTLIL::Wire*> sync_wires;
|
||||||
dict<RTLIL::SigBit, RTLIL::SyncType> sync_types;
|
dict<RTLIL::SigBit, RTLIL::SyncType> sync_types;
|
||||||
pool<const RTLIL::Memory*> writable_memories;
|
pool<const RTLIL::Memory*> writable_memories;
|
||||||
|
dict<const RTLIL::Wire*, FlowGraph::Node> elided_wires;
|
||||||
|
|
||||||
CxxrtlWorker(std::ostream &f) : f(f) {}
|
CxxrtlWorker(std::ostream &f) : f(f) {}
|
||||||
|
|
||||||
|
@ -183,7 +352,21 @@ struct CxxrtlWorker {
|
||||||
dump_const(chunk.data, chunk.width, chunk.offset);
|
dump_const(chunk.data, chunk.width, chunk.offset);
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
f << mangle(chunk.wire) << (is_lhs ? ".next" : ".curr");
|
if (!is_lhs && elided_wires.count(chunk.wire)) {
|
||||||
|
const FlowGraph::Node &node = elided_wires[chunk.wire];
|
||||||
|
switch (node.type) {
|
||||||
|
case FlowGraph::Node::Type::CONNECT:
|
||||||
|
dump_connect_elided(node.connect);
|
||||||
|
break;
|
||||||
|
case FlowGraph::Node::Type::CELL:
|
||||||
|
dump_cell_elided(node.cell);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
log_assert(false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
f << mangle(chunk.wire) << (is_lhs ? ".next" : ".curr");
|
||||||
|
}
|
||||||
if (chunk.width == chunk.wire->width && chunk.offset == 0)
|
if (chunk.width == chunk.wire->width && chunk.offset == 0)
|
||||||
return false;
|
return false;
|
||||||
else if (chunk.width == 1)
|
else if (chunk.width == 1)
|
||||||
|
@ -228,56 +411,134 @@ struct CxxrtlWorker {
|
||||||
f << ".val()";
|
f << ".val()";
|
||||||
}
|
}
|
||||||
|
|
||||||
void dump_assign(const RTLIL::SigSig &sigsig)
|
void collect_sigspec_rhs(const RTLIL::SigSpec &sig, std::vector<RTLIL::IdString> &cells)
|
||||||
{
|
{
|
||||||
|
for (auto chunk : sig.chunks()) {
|
||||||
|
if (!chunk.wire || !elided_wires.count(chunk.wire))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const FlowGraph::Node &node = elided_wires[chunk.wire];
|
||||||
|
switch (node.type) {
|
||||||
|
case FlowGraph::Node::Type::CONNECT:
|
||||||
|
collect_connect(node.connect, cells);
|
||||||
|
break;
|
||||||
|
case FlowGraph::Node::Type::CELL:
|
||||||
|
collect_cell(node.cell, cells);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
log_assert(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void dump_connect_elided(const RTLIL::SigSig &conn)
|
||||||
|
{
|
||||||
|
dump_sigspec_rhs(conn.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_connect_elided(const RTLIL::SigSig &conn)
|
||||||
|
{
|
||||||
|
return conn.first.is_wire() && elided_wires.count(conn.first.as_wire());
|
||||||
|
}
|
||||||
|
|
||||||
|
void collect_connect(const RTLIL::SigSig &conn, std::vector<RTLIL::IdString> &cells)
|
||||||
|
{
|
||||||
|
if (!is_connect_elided(conn))
|
||||||
|
return;
|
||||||
|
|
||||||
|
collect_sigspec_rhs(conn.second, cells);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dump_connect(const RTLIL::SigSig &conn)
|
||||||
|
{
|
||||||
|
if (is_connect_elided(conn))
|
||||||
|
return;
|
||||||
|
|
||||||
f << indent;
|
f << indent;
|
||||||
dump_sigspec_lhs(sigsig.first);
|
dump_sigspec_lhs(conn.first);
|
||||||
f << " = ";
|
f << " = ";
|
||||||
dump_sigspec_rhs(sigsig.second);
|
dump_connect_elided(conn);
|
||||||
f << ";\n";
|
f << ";\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
void dump_cell(const RTLIL::Cell *cell)
|
void dump_cell_elided(const RTLIL::Cell *cell)
|
||||||
{
|
{
|
||||||
dump_attrs(cell);
|
|
||||||
f << indent << "// cell " << cell->name.str() << "\n";
|
|
||||||
// Unary cells
|
// Unary cells
|
||||||
if (cell->type.in(
|
if (is_unary_cell(cell->type)) {
|
||||||
ID($not), ID($logic_not), ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_xnor), ID($reduce_bool),
|
f << cell->type.substr(1) << '_' <<
|
||||||
ID($pos), ID($neg))) {
|
|
||||||
f << indent;
|
|
||||||
dump_sigspec_lhs(cell->getPort(ID(Y)));
|
|
||||||
f << " = " << cell->type.substr(1) << '_' <<
|
|
||||||
(cell->getParam(ID(A_SIGNED)).as_bool() ? 's' : 'u') <<
|
(cell->getParam(ID(A_SIGNED)).as_bool() ? 's' : 'u') <<
|
||||||
"<" << cell->getParam(ID(Y_WIDTH)).as_int() << ">(";
|
"<" << cell->getParam(ID(Y_WIDTH)).as_int() << ">(";
|
||||||
dump_sigspec_rhs(cell->getPort(ID(A)));
|
dump_sigspec_rhs(cell->getPort(ID(A)));
|
||||||
f << ");\n";
|
f << ")";
|
||||||
// Binary cells
|
// Binary cells
|
||||||
} else if (cell->type.in(
|
} else if (is_binary_cell(cell->type)) {
|
||||||
ID($and), ID($or), ID($xor), ID($xnor), ID($logic_and), ID($logic_or),
|
f << cell->type.substr(1) << '_' <<
|
||||||
ID($shl), ID($sshl), ID($shr), ID($sshr), ID($shift), ID($shiftx),
|
|
||||||
ID($eq), ID($ne), ID($eqx), ID($nex), ID($gt), ID($ge), ID($lt), ID($le),
|
|
||||||
ID($add), ID($sub), ID($mul), ID($div), ID($mod))) {
|
|
||||||
f << indent;
|
|
||||||
dump_sigspec_lhs(cell->getPort(ID(Y)));
|
|
||||||
f << " = " << cell->type.substr(1) << '_' <<
|
|
||||||
(cell->getParam(ID(A_SIGNED)).as_bool() ? 's' : 'u') <<
|
(cell->getParam(ID(A_SIGNED)).as_bool() ? 's' : 'u') <<
|
||||||
(cell->getParam(ID(B_SIGNED)).as_bool() ? 's' : 'u') <<
|
(cell->getParam(ID(B_SIGNED)).as_bool() ? 's' : 'u') <<
|
||||||
"<" << cell->getParam(ID(Y_WIDTH)).as_int() << ">(";
|
"<" << cell->getParam(ID(Y_WIDTH)).as_int() << ">(";
|
||||||
dump_sigspec_rhs(cell->getPort(ID(A)));
|
dump_sigspec_rhs(cell->getPort(ID(A)));
|
||||||
f << ", ";
|
f << ", ";
|
||||||
dump_sigspec_rhs(cell->getPort(ID(B)));
|
dump_sigspec_rhs(cell->getPort(ID(B)));
|
||||||
f << ");\n";
|
f << ")";
|
||||||
// Muxes
|
// Muxes
|
||||||
} else if (cell->type == ID($mux)) {
|
} else if (cell->type == ID($mux)) {
|
||||||
f << indent;
|
f << "(";
|
||||||
dump_sigspec_lhs(cell->getPort(ID(Y)));
|
|
||||||
f << " = ";
|
|
||||||
dump_sigspec_rhs(cell->getPort(ID(S)));
|
dump_sigspec_rhs(cell->getPort(ID(S)));
|
||||||
f << " ? ";
|
f << " ? ";
|
||||||
dump_sigspec_rhs(cell->getPort(ID(B)));
|
dump_sigspec_rhs(cell->getPort(ID(B)));
|
||||||
f << " : ";
|
f << " : ";
|
||||||
dump_sigspec_rhs(cell->getPort(ID(A)));
|
dump_sigspec_rhs(cell->getPort(ID(A)));
|
||||||
|
f << ")";
|
||||||
|
} else {
|
||||||
|
log_assert(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_cell_elided(const RTLIL::Cell *cell)
|
||||||
|
{
|
||||||
|
return cell->hasPort(ID(Y)) && cell->getPort(ID(Y)).is_wire() && elided_wires.count(cell->getPort(ID(Y)).as_wire());
|
||||||
|
}
|
||||||
|
|
||||||
|
void collect_cell(const RTLIL::Cell *cell, std::vector<RTLIL::IdString> &cells)
|
||||||
|
{
|
||||||
|
if (!is_cell_elided(cell))
|
||||||
|
return;
|
||||||
|
|
||||||
|
cells.push_back(cell->name);
|
||||||
|
for (auto port : cell->connections())
|
||||||
|
if (port.first != ID(Y))
|
||||||
|
collect_sigspec_rhs(port.second, cells);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dump_cell(const RTLIL::Cell *cell)
|
||||||
|
{
|
||||||
|
if (is_cell_elided(cell))
|
||||||
|
return;
|
||||||
|
if (cell->type == ID($meminit))
|
||||||
|
return; // Handled elsewhere.
|
||||||
|
|
||||||
|
std::vector<RTLIL::IdString> elided_cells;
|
||||||
|
if (is_elidable_cell(cell->type)) {
|
||||||
|
for (auto port : cell->connections())
|
||||||
|
if (port.first != ID(Y))
|
||||||
|
collect_sigspec_rhs(port.second, elided_cells);
|
||||||
|
}
|
||||||
|
if (elided_cells.empty()) {
|
||||||
|
dump_attrs(cell);
|
||||||
|
f << indent << "// cell " << cell->name.str() << "\n";
|
||||||
|
} else {
|
||||||
|
f << indent << "// cells";
|
||||||
|
for (auto elided_cell : elided_cells)
|
||||||
|
f << " " << elided_cell.str();
|
||||||
|
f << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Elidable cells
|
||||||
|
if (is_elidable_cell(cell->type)) {
|
||||||
|
f << indent;
|
||||||
|
dump_sigspec_lhs(cell->getPort(ID(Y)));
|
||||||
|
f << " = ";
|
||||||
|
dump_cell_elided(cell);
|
||||||
f << ";\n";
|
f << ";\n";
|
||||||
// Parallel (one-hot) muxes
|
// Parallel (one-hot) muxes
|
||||||
} else if (cell->type == ID($pmux)) {
|
} else if (cell->type == ID($pmux)) {
|
||||||
|
@ -309,7 +570,7 @@ struct CxxrtlWorker {
|
||||||
dec_indent();
|
dec_indent();
|
||||||
f << indent << "}\n";
|
f << indent << "}\n";
|
||||||
// Flip-flops
|
// Flip-flops
|
||||||
} else if (cell->type.in(ID($dff), ID($dffe), ID($adff), ID($dffsr))) {
|
} else if (is_ff_cell(cell->type)) {
|
||||||
if (cell->getPort(ID(CLK)).is_wire()) {
|
if (cell->getPort(ID(CLK)).is_wire()) {
|
||||||
// Edge-sensitive logic
|
// Edge-sensitive logic
|
||||||
RTLIL::SigBit clk_bit = cell->getPort(ID(CLK))[0];
|
RTLIL::SigBit clk_bit = cell->getPort(ID(CLK))[0];
|
||||||
|
@ -437,8 +698,6 @@ struct CxxrtlWorker {
|
||||||
f << indent << "}\n";
|
f << indent << "}\n";
|
||||||
}
|
}
|
||||||
// Memory initializers
|
// Memory initializers
|
||||||
} else if (cell->type == ID($meminit)) {
|
|
||||||
// Handled elsewhere.
|
|
||||||
} else if (cell->type[0] == '$') {
|
} else if (cell->type[0] == '$') {
|
||||||
log_cmd_error("Unsupported internal cell `%s'.\n", cell->type.c_str());
|
log_cmd_error("Unsupported internal cell `%s'.\n", cell->type.c_str());
|
||||||
} else {
|
} else {
|
||||||
|
@ -446,6 +705,15 @@ struct CxxrtlWorker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void dump_assign(const RTLIL::SigSig &sigsig)
|
||||||
|
{
|
||||||
|
f << indent;
|
||||||
|
dump_sigspec_lhs(sigsig.first);
|
||||||
|
f << " = ";
|
||||||
|
dump_sigspec_rhs(sigsig.second);
|
||||||
|
f << ";\n";
|
||||||
|
}
|
||||||
|
|
||||||
void dump_case_rule(const RTLIL::CaseRule *rule)
|
void dump_case_rule(const RTLIL::CaseRule *rule)
|
||||||
{
|
{
|
||||||
for (auto action : rule->actions)
|
for (auto action : rule->actions)
|
||||||
|
@ -571,6 +839,9 @@ struct CxxrtlWorker {
|
||||||
|
|
||||||
void dump_wire(const RTLIL::Wire *wire)
|
void dump_wire(const RTLIL::Wire *wire)
|
||||||
{
|
{
|
||||||
|
if (elided_wires.count(wire))
|
||||||
|
return;
|
||||||
|
|
||||||
dump_attrs(wire);
|
dump_attrs(wire);
|
||||||
f << indent << "wire<" << wire->width << "> " << mangle(wire);
|
f << indent << "wire<" << wire->width << "> " << mangle(wire);
|
||||||
if (wire->attributes.count(ID(init))) {
|
if (wire->attributes.count(ID(init))) {
|
||||||
|
@ -661,7 +932,7 @@ struct CxxrtlWorker {
|
||||||
dump_cell(cell);
|
dump_cell(cell);
|
||||||
f << indent << "// connections\n";
|
f << indent << "// connections\n";
|
||||||
for (auto conn : module->connections())
|
for (auto conn : module->connections())
|
||||||
dump_assign(conn);
|
dump_connect(conn);
|
||||||
for (auto proc : module->processes)
|
for (auto proc : module->processes)
|
||||||
dump_process(proc.second);
|
dump_process(proc.second);
|
||||||
for (auto sync_type : sync_types) {
|
for (auto sync_type : sync_types) {
|
||||||
|
@ -680,6 +951,8 @@ struct CxxrtlWorker {
|
||||||
inc_indent();
|
inc_indent();
|
||||||
f << indent << "bool changed = false;\n";
|
f << indent << "bool changed = false;\n";
|
||||||
for (auto wire : module->wires()) {
|
for (auto wire : module->wires()) {
|
||||||
|
if (elided_wires.count(wire))
|
||||||
|
continue;
|
||||||
if (sync_wires[wire]) {
|
if (sync_wires[wire]) {
|
||||||
std::string wire_prev = mangle(wire) + "_prev";
|
std::string wire_prev = mangle(wire) + "_prev";
|
||||||
std::string wire_curr = mangle(wire) + ".curr";
|
std::string wire_curr = mangle(wire) + ".curr";
|
||||||
|
@ -773,10 +1046,16 @@ struct CxxrtlWorker {
|
||||||
void analyze_design(RTLIL::Design *design)
|
void analyze_design(RTLIL::Design *design)
|
||||||
{
|
{
|
||||||
for (auto module : design->modules()) {
|
for (auto module : design->modules()) {
|
||||||
|
FlowGraph flow;
|
||||||
SigMap &sigmap = sigmaps[module];
|
SigMap &sigmap = sigmaps[module];
|
||||||
sigmap.set(module);
|
sigmap.set(module);
|
||||||
|
|
||||||
|
for (auto conn : module->connections())
|
||||||
|
flow.add_node(conn);
|
||||||
|
|
||||||
for (auto cell : module->cells()) {
|
for (auto cell : module->cells()) {
|
||||||
|
flow.add_node(cell);
|
||||||
|
|
||||||
// Various DFF cells are treated like posedge/negedge processes, see above for details.
|
// Various DFF cells are treated like posedge/negedge processes, see above for details.
|
||||||
if (cell->type.in(ID($dff), ID($dffe), ID($adff), ID($dffsr))) {
|
if (cell->type.in(ID($dff), ID($dffe), ID($adff), ID($dffsr))) {
|
||||||
if (cell->getPort(ID(CLK)).is_wire())
|
if (cell->getPort(ID(CLK)).is_wire())
|
||||||
|
@ -802,7 +1081,9 @@ struct CxxrtlWorker {
|
||||||
log_assert(false);
|
log_assert(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto proc : module->processes)
|
for (auto proc : module->processes) {
|
||||||
|
flow.add_node(proc.second);
|
||||||
|
|
||||||
for (auto sync : proc.second->syncs)
|
for (auto sync : proc.second->syncs)
|
||||||
switch (sync->type) {
|
switch (sync->type) {
|
||||||
// Edge-type sync rules require pre-registration.
|
// Edge-type sync rules require pre-registration.
|
||||||
|
@ -826,6 +1107,18 @@ struct CxxrtlWorker {
|
||||||
case RTLIL::STg:
|
case RTLIL::STg:
|
||||||
log_cmd_error("Global clock is not supported.\n");
|
log_cmd_error("Global clock is not supported.\n");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto wire : module->wires()) {
|
||||||
|
if (!flow.is_elidable(wire)) continue;
|
||||||
|
if (wire->port_id != 0) continue;
|
||||||
|
if (wire->get_bool_attribute(ID(keep))) continue;
|
||||||
|
if (wire->name.begins_with("$") && !elide_internal) continue;
|
||||||
|
if (wire->name.begins_with("\\") && !elide_public) continue;
|
||||||
|
if (sync_wires[wire]) continue;
|
||||||
|
log_assert(flow.wire_defs[wire].size() == 1);
|
||||||
|
elided_wires[wire] = **flow.wire_defs[wire].begin();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -879,23 +1172,54 @@ struct CxxrtlBackend : public Backend {
|
||||||
log("\n");
|
log("\n");
|
||||||
log("Write C++ code for simulating the design.\n");
|
log("Write C++ code for simulating the design.\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
|
// -O2 (and not -O1) is the default because wire elision results in dramatic (>10x) decrease in compile- and run-time,
|
||||||
|
// which is well worth the need to manually drop to -O1 or to mark interesting wires with (*keep*).
|
||||||
|
log(" -O <level>\n");
|
||||||
|
log(" set the optimization level. the default is -O2.\n");
|
||||||
|
log("\n");
|
||||||
|
log(" -O0\n");
|
||||||
|
log(" no optimization.\n");
|
||||||
|
log("\n");
|
||||||
|
log(" -O1\n");
|
||||||
|
log(" elide internal wires if possible.\n");
|
||||||
|
log("\n");
|
||||||
|
log(" -O2\n");
|
||||||
|
log(" like -O1, and elide public wires not marked (*keep*) if possible.\n");
|
||||||
|
log("\n");
|
||||||
}
|
}
|
||||||
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||||
{
|
{
|
||||||
|
int opt_level = 2;
|
||||||
|
|
||||||
log_header(design, "Executing CXXRTL backend.\n");
|
log_header(design, "Executing CXXRTL backend.\n");
|
||||||
|
|
||||||
size_t argidx;
|
size_t argidx;
|
||||||
for (argidx = 1; argidx < args.size(); argidx++)
|
for (argidx = 1; argidx < args.size(); argidx++)
|
||||||
{
|
{
|
||||||
// if (args[argidx] == "-top" && argidx+1 < args.size()) {
|
if (args[argidx] == "-O" && argidx+1 < args.size()) {
|
||||||
// top_module_name = args[++argidx];
|
opt_level = std::stoi(args[++argidx]);
|
||||||
// continue;
|
continue;
|
||||||
// }
|
}
|
||||||
|
if (args[argidx].substr(0, 2) == "-O" && args[argidx].size() == 3 && isdigit(args[argidx][2])) {
|
||||||
|
opt_level = std::stoi(args[argidx].substr(2));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
extra_args(f, filename, args, argidx);
|
extra_args(f, filename, args, argidx);
|
||||||
|
|
||||||
CxxrtlWorker worker(*f);
|
CxxrtlWorker worker(*f);
|
||||||
|
switch (opt_level) {
|
||||||
|
case 2:
|
||||||
|
worker.elide_public = true;
|
||||||
|
case 1:
|
||||||
|
worker.elide_internal = true;
|
||||||
|
case 0:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
log_cmd_error("Invalid optimization level %d.\n", opt_level);
|
||||||
|
}
|
||||||
|
|
||||||
worker.prepare_design(design);
|
worker.prepare_design(design);
|
||||||
worker.dump_design(design);
|
worker.dump_design(design);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue