mirror of
https://github.com/YosysHQ/yosys
synced 2025-09-30 13:19:05 +00:00
Merge pull request #5353 from jix/new_bufnorm
Extended buffer normalization
This commit is contained in:
commit
d5053033e4
16 changed files with 1037 additions and 218 deletions
1
Makefile
1
Makefile
|
@ -644,6 +644,7 @@ $(eval $(call add_include_file,frontends/blif/blifparse.h))
|
||||||
$(eval $(call add_include_file,backends/rtlil/rtlil_backend.h))
|
$(eval $(call add_include_file,backends/rtlil/rtlil_backend.h))
|
||||||
|
|
||||||
OBJS += kernel/driver.o kernel/register.o kernel/rtlil.o kernel/log.o kernel/calc.o kernel/yosys.o kernel/io.o kernel/gzip.o
|
OBJS += kernel/driver.o kernel/register.o kernel/rtlil.o kernel/log.o kernel/calc.o kernel/yosys.o kernel/io.o kernel/gzip.o
|
||||||
|
OBJS += kernel/rtlil_bufnorm.o
|
||||||
OBJS += kernel/log_help.o
|
OBJS += kernel/log_help.o
|
||||||
ifeq ($(ENABLE_VERIFIC_YOSYSHQ_EXTENSIONS),1)
|
ifeq ($(ENABLE_VERIFIC_YOSYSHQ_EXTENSIONS),1)
|
||||||
OBJS += kernel/log_compat.o
|
OBJS += kernel/log_compat.o
|
||||||
|
|
|
@ -91,7 +91,7 @@ struct Index {
|
||||||
int pos = index_wires(info, m);
|
int pos = index_wires(info, m);
|
||||||
|
|
||||||
for (auto cell : m->cells()) {
|
for (auto cell : m->cells()) {
|
||||||
if (cell->type.in(KNOWN_OPS) || cell->type.in(ID($scopeinfo), ID($specify2), ID($specify3)))
|
if (cell->type.in(KNOWN_OPS) || cell->type.in(ID($scopeinfo), ID($specify2), ID($specify3), ID($input_port)))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
Module *submodule = m->design->module(cell->type);
|
Module *submodule = m->design->module(cell->type);
|
||||||
|
@ -566,7 +566,7 @@ struct Index {
|
||||||
}
|
}
|
||||||
|
|
||||||
Lit ret;
|
Lit ret;
|
||||||
if (!bit.wire->port_input) {
|
if (!bit.wire->port_input || bit.wire->port_output) {
|
||||||
// an output of a cell
|
// an output of a cell
|
||||||
Cell *driver = bit.wire->driverCell();
|
Cell *driver = bit.wire->driverCell();
|
||||||
|
|
||||||
|
@ -618,7 +618,7 @@ struct Index {
|
||||||
|
|
||||||
if (!cursor) {
|
if (!cursor) {
|
||||||
log_assert(bit.wire->module == top);
|
log_assert(bit.wire->module == top);
|
||||||
log_assert(bit.wire->port_input);
|
log_assert(bit.wire->port_input && !bit.wire->port_output);
|
||||||
return lits[top_minfo->windices[bit.wire] + bit.offset];
|
return lits[top_minfo->windices[bit.wire] + bit.offset];
|
||||||
} else {
|
} else {
|
||||||
log_assert(bit.wire->module == cursor->leaf_module(*this));
|
log_assert(bit.wire->module == cursor->leaf_module(*this));
|
||||||
|
@ -723,7 +723,7 @@ struct AigerWriter : Index<AigerWriter, unsigned int, 0, 1> {
|
||||||
for (auto id : top->ports) {
|
for (auto id : top->ports) {
|
||||||
Wire *w = top->wire(id);
|
Wire *w = top->wire(id);
|
||||||
log_assert(w);
|
log_assert(w);
|
||||||
if (w->port_input)
|
if (w->port_input && !w->port_output)
|
||||||
for (int i = 0; i < w->width; i++) {
|
for (int i = 0; i < w->width; i++) {
|
||||||
pi_literal(SigBit(w, i)) = lit_counter;
|
pi_literal(SigBit(w, i)) = lit_counter;
|
||||||
inputs.push_back(SigBit(w, i));
|
inputs.push_back(SigBit(w, i));
|
||||||
|
@ -828,7 +828,7 @@ struct XAigerAnalysis : Index<XAigerAnalysis, int, 0, 0> {
|
||||||
{
|
{
|
||||||
log_assert(cursor.is_top()); // TOOD: fix analyzer to work with hierarchy
|
log_assert(cursor.is_top()); // TOOD: fix analyzer to work with hierarchy
|
||||||
|
|
||||||
if (bit.wire->port_input)
|
if (bit.wire->port_input && !bit.wire->port_output)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Cell *driver = bit.wire->driverCell();
|
Cell *driver = bit.wire->driverCell();
|
||||||
|
@ -838,7 +838,7 @@ struct XAigerAnalysis : Index<XAigerAnalysis, int, 0, 0> {
|
||||||
|
|
||||||
int max = 1;
|
int max = 1;
|
||||||
for (auto wire : mod->wires())
|
for (auto wire : mod->wires())
|
||||||
if (wire->port_input)
|
if (wire->port_input && !wire->port_output)
|
||||||
for (int i = 0; i < wire->width; i++) {
|
for (int i = 0; i < wire->width; i++) {
|
||||||
int ilevel = visit(cursor, driver->getPort(wire->name)[i]);
|
int ilevel = visit(cursor, driver->getPort(wire->name)[i]);
|
||||||
max = std::max(max, ilevel + 1);
|
max = std::max(max, ilevel + 1);
|
||||||
|
@ -858,7 +858,7 @@ struct XAigerAnalysis : Index<XAigerAnalysis, int, 0, 0> {
|
||||||
for (auto id : top->ports) {
|
for (auto id : top->ports) {
|
||||||
Wire *w = top->wire(id);
|
Wire *w = top->wire(id);
|
||||||
log_assert(w);
|
log_assert(w);
|
||||||
if (w->port_input)
|
if (w->port_input && !w->port_output)
|
||||||
for (int i = 0; i < w->width; i++)
|
for (int i = 0; i < w->width; i++)
|
||||||
pi_literal(SigBit(w, i)) = 0;
|
pi_literal(SigBit(w, i)) = 0;
|
||||||
}
|
}
|
||||||
|
@ -868,7 +868,7 @@ struct XAigerAnalysis : Index<XAigerAnalysis, int, 0, 0> {
|
||||||
Module *def = design->module(box->type);
|
Module *def = design->module(box->type);
|
||||||
if (!(def && def->has_attribute(ID::abc9_box_id)))
|
if (!(def && def->has_attribute(ID::abc9_box_id)))
|
||||||
for (auto &conn : box->connections_)
|
for (auto &conn : box->connections_)
|
||||||
if (box->output(conn.first))
|
if (box->port_dir(conn.first) != RTLIL::PD_INPUT)
|
||||||
for (auto bit : conn.second)
|
for (auto bit : conn.second)
|
||||||
pi_literal(bit, &cursor) = 0;
|
pi_literal(bit, &cursor) = 0;
|
||||||
}
|
}
|
||||||
|
@ -883,7 +883,7 @@ struct XAigerAnalysis : Index<XAigerAnalysis, int, 0, 0> {
|
||||||
Module *def = design->module(box->type);
|
Module *def = design->module(box->type);
|
||||||
if (!(def && def->has_attribute(ID::abc9_box_id)))
|
if (!(def && def->has_attribute(ID::abc9_box_id)))
|
||||||
for (auto &conn : box->connections_)
|
for (auto &conn : box->connections_)
|
||||||
if (box->input(conn.first))
|
if (box->port_dir(conn.first) == RTLIL::PD_INPUT)
|
||||||
for (auto bit : conn.second)
|
for (auto bit : conn.second)
|
||||||
(void) eval_po(bit);
|
(void) eval_po(bit);
|
||||||
}
|
}
|
||||||
|
@ -950,12 +950,7 @@ struct XAigerWriter : AigerWriter {
|
||||||
void append_opaque_box_ports(Cell *box, HierCursor &cursor, bool inputs)
|
void append_opaque_box_ports(Cell *box, HierCursor &cursor, bool inputs)
|
||||||
{
|
{
|
||||||
for (auto &conn : box->connections_) {
|
for (auto &conn : box->connections_) {
|
||||||
bool is_input = box->input(conn.first);
|
bool is_input = box->port_dir(conn.first) == RTLIL::PD_INPUT;
|
||||||
bool is_output = box->output(conn.first);
|
|
||||||
|
|
||||||
if (!(is_input || is_output) || (is_input && is_output))
|
|
||||||
log_error("Ambiguous port direction on %s/%s\n",
|
|
||||||
log_id(box->type), log_id(conn.first));
|
|
||||||
|
|
||||||
if (is_input && inputs) {
|
if (is_input && inputs) {
|
||||||
int bitp = 0;
|
int bitp = 0;
|
||||||
|
@ -980,10 +975,10 @@ struct XAigerWriter : AigerWriter {
|
||||||
|
|
||||||
bitp++;
|
bitp++;
|
||||||
}
|
}
|
||||||
} else if (is_output && !inputs) {
|
} else if (!is_input && !inputs) {
|
||||||
for (auto &bit : conn.second) {
|
for (auto &bit : conn.second) {
|
||||||
if (!bit.wire || bit.wire->port_input)
|
if (!bit.wire || (bit.wire->port_input && !bit.wire->port_output))
|
||||||
log_error("Bad connection");
|
log_error("Bad connection %s/%s ~ %s\n", log_id(box), log_id(conn.first), log_signal(conn.second));
|
||||||
|
|
||||||
|
|
||||||
ensure_pi(bit, cursor);
|
ensure_pi(bit, cursor);
|
||||||
|
@ -1119,7 +1114,7 @@ struct XAigerWriter : AigerWriter {
|
||||||
holes_pi_idx++;
|
holes_pi_idx++;
|
||||||
}
|
}
|
||||||
holes_wb->setPort(port_id, in_conn);
|
holes_wb->setPort(port_id, in_conn);
|
||||||
} else if (port->port_output && !port->port_input) {
|
} else if (port->port_output) {
|
||||||
// primary
|
// primary
|
||||||
for (int i = 0; i < port->width; i++) {
|
for (int i = 0; i < port->width; i++) {
|
||||||
SigBit bit;
|
SigBit bit;
|
||||||
|
@ -1172,7 +1167,7 @@ struct XAigerWriter : AigerWriter {
|
||||||
log_assert(port);
|
log_assert(port);
|
||||||
if (port->port_input && !port->port_output) {
|
if (port->port_input && !port->port_output) {
|
||||||
box_co_num += port->width;
|
box_co_num += port->width;
|
||||||
} else if (port->port_output && !port->port_input) {
|
} else if (port->port_output) {
|
||||||
box_ci_num += port->width;
|
box_ci_num += port->width;
|
||||||
} else {
|
} else {
|
||||||
log_abort();
|
log_abort();
|
||||||
|
@ -1195,7 +1190,7 @@ struct XAigerWriter : AigerWriter {
|
||||||
reset_counters();
|
reset_counters();
|
||||||
|
|
||||||
for (auto w : top->wires())
|
for (auto w : top->wires())
|
||||||
if (w->port_input)
|
if (w->port_input && !w->port_output)
|
||||||
for (int i = 0; i < w->width; i++)
|
for (int i = 0; i < w->width; i++)
|
||||||
ensure_pi(SigBit(w, i));
|
ensure_pi(SigBit(w, i));
|
||||||
|
|
||||||
|
|
|
@ -1099,6 +1099,33 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cell->type.in(ID($_BUF_), ID($buf))) {
|
if (cell->type.in(ID($_BUF_), ID($buf))) {
|
||||||
|
if (cell->type == ID($buf) && cell->getPort(ID::A).has_const(State::Sz)) {
|
||||||
|
RTLIL::SigSpec a = cell->getPort(ID::A);
|
||||||
|
RTLIL::SigSpec y = cell->getPort(ID::Y);
|
||||||
|
a.extend_u0(GetSize(y));
|
||||||
|
|
||||||
|
if (a.has_const(State::Sz)) {
|
||||||
|
SigSpec new_a;
|
||||||
|
SigSpec new_y;
|
||||||
|
for (int i = 0; i < GetSize(a); ++i) {
|
||||||
|
SigBit b = a[i];
|
||||||
|
if (b == State::Sz)
|
||||||
|
continue;
|
||||||
|
new_a.append(b);
|
||||||
|
new_y.append(y[i]);
|
||||||
|
}
|
||||||
|
a = std::move(new_a);
|
||||||
|
y = std::move(new_y);
|
||||||
|
}
|
||||||
|
if (!y.empty()) {
|
||||||
|
f << stringf("%s" "assign ", indent);
|
||||||
|
dump_sigspec(f, y);
|
||||||
|
f << stringf(" = ");
|
||||||
|
dump_sigspec(f, a);
|
||||||
|
f << stringf(";\n");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
f << stringf("%s" "assign ", indent);
|
f << stringf("%s" "assign ", indent);
|
||||||
dump_sigspec(f, cell->getPort(ID::Y));
|
dump_sigspec(f, cell->getPort(ID::Y));
|
||||||
f << stringf(" = ");
|
f << stringf(" = ");
|
||||||
|
@ -1498,6 +1525,29 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cell->type == ID($input_port))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (cell->type == ID($connect))
|
||||||
|
{
|
||||||
|
int width = cell->getParam(ID::WIDTH).as_int() ;
|
||||||
|
if (width == 1) {
|
||||||
|
f << stringf("%s" "tran(", indent);
|
||||||
|
dump_sigspec(f, cell->getPort(ID::A));
|
||||||
|
f << stringf(", ");
|
||||||
|
dump_sigspec(f, cell->getPort(ID::B));
|
||||||
|
f << stringf(");\n");
|
||||||
|
} else {
|
||||||
|
auto tran_id = next_auto_id();
|
||||||
|
f << stringf("%s" "tran %s[%d:0](", indent, tran_id, width - 1);
|
||||||
|
dump_sigspec(f, cell->getPort(ID::A));
|
||||||
|
f << stringf(", ");
|
||||||
|
dump_sigspec(f, cell->getPort(ID::B));
|
||||||
|
f << stringf(");\n");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (cell->is_builtin_ff())
|
if (cell->is_builtin_ff())
|
||||||
{
|
{
|
||||||
FfData ff(nullptr, cell);
|
FfData ff(nullptr, cell);
|
||||||
|
|
|
@ -2756,20 +2756,25 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin
|
||||||
newNode = std::make_unique<AstNode>(location, AST_GENBLOCK);
|
newNode = std::make_unique<AstNode>(location, AST_GENBLOCK);
|
||||||
int num = max(children.at(0)->range_left, children.at(0)->range_right) - min(children.at(0)->range_left, children.at(0)->range_right) + 1;
|
int num = max(children.at(0)->range_left, children.at(0)->range_right) - min(children.at(0)->range_left, children.at(0)->range_right) + 1;
|
||||||
|
|
||||||
|
if (this->children.at(1)->type == AST_PRIMITIVE) {
|
||||||
|
// Move the range to the AST_PRIMITIVE node and replace this with the AST_PRIMITIVE node handled below
|
||||||
|
newNode = std::move(this->children.at(1));
|
||||||
|
newNode->range_left = this->children.at(0)->range_left;
|
||||||
|
newNode->range_right = this->children.at(0)->range_right;
|
||||||
|
newNode->range_valid = true;
|
||||||
|
goto apply_newNode;
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < num; i++) {
|
for (int i = 0; i < num; i++) {
|
||||||
int idx = children.at(0)->range_left > children.at(0)->range_right ? children.at(0)->range_right + i : children.at(0)->range_right - i;
|
int idx = children.at(0)->range_left > children.at(0)->range_right ? children.at(0)->range_right + i : children.at(0)->range_right - i;
|
||||||
auto new_cell_owned = children.at(1)->clone();
|
auto new_cell_owned = children.at(1)->clone();
|
||||||
auto* new_cell = new_cell_owned.get();
|
auto* new_cell = new_cell_owned.get();
|
||||||
newNode->children.push_back(std::move(new_cell_owned));
|
newNode->children.push_back(std::move(new_cell_owned));
|
||||||
new_cell->str += stringf("[%d]", idx);
|
new_cell->str += stringf("[%d]", idx);
|
||||||
if (new_cell->type == AST_PRIMITIVE) {
|
|
||||||
input_error("Cell arrays of primitives are currently not supported.\n");
|
|
||||||
} else {
|
|
||||||
this->dumpAst(NULL, " ");
|
|
||||||
log_assert(new_cell->children.at(0)->type == AST_CELLTYPE);
|
log_assert(new_cell->children.at(0)->type == AST_CELLTYPE);
|
||||||
new_cell->children.at(0)->str = stringf("$array:%d:%d:%s", i, num, new_cell->children.at(0)->str);
|
new_cell->children.at(0)->str = stringf("$array:%d:%d:%s", i, num, new_cell->children.at(0)->str);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
goto apply_newNode;
|
goto apply_newNode;
|
||||||
}
|
}
|
||||||
|
@ -2789,6 +2794,11 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin
|
||||||
}
|
}
|
||||||
children.clear();
|
children.clear();
|
||||||
|
|
||||||
|
// TODO handle bit-widths of primitives and support cell arrays for more primitives
|
||||||
|
|
||||||
|
if (range_valid && str != "tran")
|
||||||
|
input_error("Cell arrays of primitives are currently not supported.\n");
|
||||||
|
|
||||||
if (str == "bufif0" || str == "bufif1" || str == "notif0" || str == "notif1")
|
if (str == "bufif0" || str == "bufif1" || str == "notif0" || str == "notif1")
|
||||||
{
|
{
|
||||||
if (children_list.size() != 3)
|
if (children_list.size() != 3)
|
||||||
|
@ -2817,7 +2827,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin
|
||||||
fixup_hierarchy_flags();
|
fixup_hierarchy_flags();
|
||||||
did_something = true;
|
did_something = true;
|
||||||
}
|
}
|
||||||
else if (str == "buf" || str == "not")
|
else if (str == "buf" || str == "not" || str == "tran")
|
||||||
{
|
{
|
||||||
auto& input = children_list.back();
|
auto& input = children_list.back();
|
||||||
if (str == "not")
|
if (str == "not")
|
||||||
|
|
|
@ -493,7 +493,7 @@ TIME_SCALE_SUFFIX [munpf]?s
|
||||||
|
|
||||||
\"{3}(\"{0,2}([^\\"]|\\.|\\\n))*\"{3} { return process_str(yytext + 3, yyleng - 6, true, out_loc); }
|
\"{3}(\"{0,2}([^\\"]|\\.|\\\n))*\"{3} { return process_str(yytext + 3, yyleng - 6, true, out_loc); }
|
||||||
|
|
||||||
and|nand|or|nor|xor|xnor|not|buf|bufif0|bufif1|notif0|notif1 {
|
and|nand|or|nor|xor|xnor|not|buf|bufif0|bufif1|notif0|notif1|tran {
|
||||||
auto val = std::make_unique<std::string>(YYText());
|
auto val = std::make_unique<std::string>(YYText());
|
||||||
return parser::make_TOK_PRIMITIVE(std::move(val), out_loc);
|
return parser::make_TOK_PRIMITIVE(std::move(val), out_loc);
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,6 +111,8 @@ struct CellTypes
|
||||||
setup_type(ID($original_tag), {ID::A}, {ID::Y});
|
setup_type(ID($original_tag), {ID::A}, {ID::Y});
|
||||||
setup_type(ID($future_ff), {ID::A}, {ID::Y});
|
setup_type(ID($future_ff), {ID::A}, {ID::Y});
|
||||||
setup_type(ID($scopeinfo), {}, {});
|
setup_type(ID($scopeinfo), {}, {});
|
||||||
|
setup_type(ID($input_port), {}, {ID::Y});
|
||||||
|
setup_type(ID($connect), {ID::A, ID::B}, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
void setup_internals_eval()
|
void setup_internals_eval()
|
||||||
|
@ -320,6 +322,16 @@ struct CellTypes
|
||||||
return it != cell_types.end() && it->second.inputs.count(port) != 0;
|
return it != cell_types.end() && it->second.inputs.count(port) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RTLIL::PortDir cell_port_dir(RTLIL::IdString type, RTLIL::IdString port) const
|
||||||
|
{
|
||||||
|
auto it = cell_types.find(type);
|
||||||
|
if (it == cell_types.end())
|
||||||
|
return RTLIL::PD_UNKNOWN;
|
||||||
|
bool is_input = it->second.inputs.count(port);
|
||||||
|
bool is_output = it->second.outputs.count(port);
|
||||||
|
return RTLIL::PortDir(is_input + is_output * 2);
|
||||||
|
}
|
||||||
|
|
||||||
bool cell_evaluable(const RTLIL::IdString &type) const
|
bool cell_evaluable(const RTLIL::IdString &type) const
|
||||||
{
|
{
|
||||||
auto it = cell_types.find(type);
|
auto it = cell_types.find(type);
|
||||||
|
|
247
kernel/rtlil.cc
247
kernel/rtlil.cc
|
@ -2451,6 +2451,19 @@ namespace {
|
||||||
check_expected();
|
check_expected();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (cell->type.in(ID($input_port))) {
|
||||||
|
param(ID::WIDTH);
|
||||||
|
port(ID::Y, param(ID::WIDTH));
|
||||||
|
check_expected();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (cell->type.in(ID($connect))) {
|
||||||
|
param(ID::WIDTH);
|
||||||
|
port(ID::A, param(ID::WIDTH));
|
||||||
|
port(ID::B, param(ID::WIDTH));
|
||||||
|
check_expected();
|
||||||
|
return;
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
* Checklist for adding internal cell types
|
* Checklist for adding internal cell types
|
||||||
* ========================================
|
* ========================================
|
||||||
|
@ -2831,8 +2844,14 @@ void RTLIL::Module::remove(RTLIL::Cell *cell)
|
||||||
log_assert(cells_.count(cell->name) != 0);
|
log_assert(cells_.count(cell->name) != 0);
|
||||||
log_assert(refcount_cells_ == 0);
|
log_assert(refcount_cells_ == 0);
|
||||||
cells_.erase(cell->name);
|
cells_.erase(cell->name);
|
||||||
|
if (design && design->flagBufferedNormalized && buf_norm_cell_queue.count(cell)) {
|
||||||
|
cell->type.clear();
|
||||||
|
cell->name.clear();
|
||||||
|
pending_deleted_cells.insert(cell);
|
||||||
|
} else {
|
||||||
delete cell;
|
delete cell;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void RTLIL::Module::remove(RTLIL::Process *process)
|
void RTLIL::Module::remove(RTLIL::Process *process)
|
||||||
{
|
{
|
||||||
|
@ -3006,6 +3025,14 @@ void RTLIL::Module::fixup_ports()
|
||||||
|
|
||||||
std::sort(all_ports.begin(), all_ports.end(), fixup_ports_compare);
|
std::sort(all_ports.begin(), all_ports.end(), fixup_ports_compare);
|
||||||
|
|
||||||
|
if (design && design->flagBufferedNormalized) {
|
||||||
|
for (auto &w : wires_)
|
||||||
|
if (w.second->driverCell_ && w.second->driverCell_->type == ID($input_port))
|
||||||
|
buf_norm_wire_queue.insert(w.second);
|
||||||
|
|
||||||
|
buf_norm_wire_queue.insert(all_ports.begin(), all_ports.end());
|
||||||
|
}
|
||||||
|
|
||||||
ports.clear();
|
ports.clear();
|
||||||
for (size_t i = 0; i < all_ports.size(); i++) {
|
for (size_t i = 0; i < all_ports.size(); i++) {
|
||||||
ports.push_back(all_ports[i]->name);
|
ports.push_back(all_ports[i]->name);
|
||||||
|
@ -4150,188 +4177,7 @@ bool RTLIL::Cell::hasPort(const RTLIL::IdString& portname) const
|
||||||
return connections_.count(portname) != 0;
|
return connections_.count(portname) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RTLIL::Cell::unsetPort(const RTLIL::IdString& portname)
|
// bufnorm
|
||||||
{
|
|
||||||
RTLIL::SigSpec signal;
|
|
||||||
auto conn_it = connections_.find(portname);
|
|
||||||
|
|
||||||
if (conn_it != connections_.end())
|
|
||||||
{
|
|
||||||
for (auto mon : module->monitors)
|
|
||||||
mon->notify_connect(this, conn_it->first, conn_it->second, signal);
|
|
||||||
|
|
||||||
if (module->design)
|
|
||||||
for (auto mon : module->design->monitors)
|
|
||||||
mon->notify_connect(this, conn_it->first, conn_it->second, signal);
|
|
||||||
|
|
||||||
if (yosys_xtrace) {
|
|
||||||
log("#X# Unconnect %s.%s.%s\n", log_id(this->module), log_id(this), log_id(portname));
|
|
||||||
log_backtrace("-X- ", yosys_xtrace-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
connections_.erase(conn_it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RTLIL::Design::bufNormalize(bool enable)
|
|
||||||
{
|
|
||||||
if (!enable)
|
|
||||||
{
|
|
||||||
if (!flagBufferedNormalized)
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (auto module : modules()) {
|
|
||||||
module->bufNormQueue.clear();
|
|
||||||
for (auto wire : module->wires()) {
|
|
||||||
wire->driverCell_ = nullptr;
|
|
||||||
wire->driverPort_ = IdString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
flagBufferedNormalized = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!flagBufferedNormalized)
|
|
||||||
{
|
|
||||||
for (auto module : modules())
|
|
||||||
{
|
|
||||||
for (auto cell : module->cells())
|
|
||||||
for (auto &conn : cell->connections()) {
|
|
||||||
if (!cell->output(conn.first) || GetSize(conn.second) == 0)
|
|
||||||
continue;
|
|
||||||
if (conn.second.is_wire()) {
|
|
||||||
Wire *wire = conn.second.as_wire();
|
|
||||||
log_assert(wire->driverCell_ == nullptr);
|
|
||||||
wire->driverCell_ = cell;
|
|
||||||
wire->driverPort_ = conn.first;
|
|
||||||
} else {
|
|
||||||
pair<RTLIL::Cell*, RTLIL::IdString> key(cell, conn.first);
|
|
||||||
module->bufNormQueue.insert(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
flagBufferedNormalized = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto module : modules())
|
|
||||||
module->bufNormalize();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RTLIL::Module::bufNormalize()
|
|
||||||
{
|
|
||||||
if (!design->flagBufferedNormalized)
|
|
||||||
return;
|
|
||||||
|
|
||||||
while (GetSize(bufNormQueue) || !connections_.empty())
|
|
||||||
{
|
|
||||||
pool<pair<RTLIL::Cell*, RTLIL::IdString>> queue;
|
|
||||||
bufNormQueue.swap(queue);
|
|
||||||
|
|
||||||
pool<Wire*> outWires;
|
|
||||||
for (auto &conn : connections())
|
|
||||||
for (auto &chunk : conn.first.chunks())
|
|
||||||
if (chunk.wire) outWires.insert(chunk.wire);
|
|
||||||
|
|
||||||
SigMap sigmap(this);
|
|
||||||
new_connections({});
|
|
||||||
|
|
||||||
for (auto &key : queue)
|
|
||||||
{
|
|
||||||
Cell *cell = key.first;
|
|
||||||
const IdString &portname = key.second;
|
|
||||||
const SigSpec &sig = cell->getPort(portname);
|
|
||||||
if (GetSize(sig) == 0) continue;
|
|
||||||
|
|
||||||
if (sig.is_wire()) {
|
|
||||||
Wire *wire = sig.as_wire();
|
|
||||||
if (wire->driverCell_) {
|
|
||||||
log_error("Conflict between %s %s in module %s\n",
|
|
||||||
log_id(cell), log_id(wire->driverCell_), log_id(this));
|
|
||||||
}
|
|
||||||
log_assert(wire->driverCell_ == nullptr);
|
|
||||||
wire->driverCell_ = cell;
|
|
||||||
wire->driverPort_ = portname;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto &chunk : sig.chunks())
|
|
||||||
if (chunk.wire) outWires.insert(chunk.wire);
|
|
||||||
|
|
||||||
Wire *wire = addWire(NEW_ID, GetSize(sig));
|
|
||||||
sigmap.add(sig, wire);
|
|
||||||
cell->setPort(portname, wire);
|
|
||||||
|
|
||||||
// FIXME: Move init attributes from old 'sig' to new 'wire'
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto wire : outWires)
|
|
||||||
{
|
|
||||||
SigSpec outsig = wire, insig = sigmap(wire);
|
|
||||||
for (int i = 0; i < GetSize(wire); i++)
|
|
||||||
if (insig[i] == outsig[i])
|
|
||||||
insig[i] = State::Sx;
|
|
||||||
addBuf(NEW_ID, insig, outsig);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RTLIL::Cell::setPort(const RTLIL::IdString& portname, RTLIL::SigSpec signal)
|
|
||||||
{
|
|
||||||
auto r = connections_.insert(portname);
|
|
||||||
auto conn_it = r.first;
|
|
||||||
if (!r.second && conn_it->second == signal)
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (auto mon : module->monitors)
|
|
||||||
mon->notify_connect(this, conn_it->first, conn_it->second, signal);
|
|
||||||
|
|
||||||
if (module->design)
|
|
||||||
for (auto mon : module->design->monitors)
|
|
||||||
mon->notify_connect(this, conn_it->first, conn_it->second, signal);
|
|
||||||
|
|
||||||
if (yosys_xtrace) {
|
|
||||||
log("#X# Connect %s.%s.%s = %s (%d)\n", log_id(this->module), log_id(this), log_id(portname), log_signal(signal), GetSize(signal));
|
|
||||||
log_backtrace("-X- ", yosys_xtrace-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (module->design && module->design->flagBufferedNormalized && output(portname))
|
|
||||||
{
|
|
||||||
pair<RTLIL::Cell*, RTLIL::IdString> key(this, portname);
|
|
||||||
|
|
||||||
if (conn_it->second.is_wire()) {
|
|
||||||
Wire *w = conn_it->second.as_wire();
|
|
||||||
if (w->driverCell_ == this && w->driverPort_ == portname) {
|
|
||||||
w->driverCell_ = nullptr;
|
|
||||||
w->driverPort_ = IdString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GetSize(signal) == 0) {
|
|
||||||
module->bufNormQueue.erase(key);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!signal.is_wire()) {
|
|
||||||
module->bufNormQueue.insert(key);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
Wire *w = signal.as_wire();
|
|
||||||
if (w->driverCell_ != nullptr) {
|
|
||||||
pair<RTLIL::Cell*, RTLIL::IdString> other_key(w->driverCell_, w->driverPort_);
|
|
||||||
module->bufNormQueue.insert(other_key);
|
|
||||||
}
|
|
||||||
w->driverCell_ = this;
|
|
||||||
w->driverPort_ = portname;
|
|
||||||
|
|
||||||
module->bufNormQueue.erase(key);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
conn_it->second = std::move(signal);
|
|
||||||
}
|
|
||||||
|
|
||||||
const RTLIL::SigSpec &RTLIL::Cell::getPort(const RTLIL::IdString& portname) const
|
const RTLIL::SigSpec &RTLIL::Cell::getPort(const RTLIL::IdString& portname) const
|
||||||
{
|
{
|
||||||
|
@ -4376,6 +4222,22 @@ bool RTLIL::Cell::output(const RTLIL::IdString& portname) const
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RTLIL::PortDir RTLIL::Cell::port_dir(const RTLIL::IdString& portname) const
|
||||||
|
{
|
||||||
|
if (yosys_celltypes.cell_known(type))
|
||||||
|
return yosys_celltypes.cell_port_dir(type, portname);
|
||||||
|
if (module && module->design) {
|
||||||
|
RTLIL::Module *m = module->design->module(type);
|
||||||
|
if (m == nullptr)
|
||||||
|
return PortDir::PD_UNKNOWN;
|
||||||
|
RTLIL::Wire *w = m->wire(portname);
|
||||||
|
if (w == nullptr)
|
||||||
|
return PortDir::PD_UNKNOWN;
|
||||||
|
return PortDir(w->port_input + w->port_output * 2);
|
||||||
|
}
|
||||||
|
return PortDir::PD_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
bool RTLIL::Cell::hasParam(const RTLIL::IdString& paramname) const
|
bool RTLIL::Cell::hasParam(const RTLIL::IdString& paramname) const
|
||||||
{
|
{
|
||||||
return parameters.count(paramname) != 0;
|
return parameters.count(paramname) != 0;
|
||||||
|
@ -5518,6 +5380,15 @@ bool RTLIL::SigSpec::is_chunk() const
|
||||||
return GetSize(chunks_) == 1;
|
return GetSize(chunks_) == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool RTLIL::SigSpec::known_driver() const
|
||||||
|
{
|
||||||
|
pack();
|
||||||
|
for (auto &chunk : chunks_)
|
||||||
|
if (chunk.is_wire() && !chunk.wire->known_driver())
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool RTLIL::SigSpec::is_fully_const() const
|
bool RTLIL::SigSpec::is_fully_const() const
|
||||||
{
|
{
|
||||||
cover("kernel.rtlil.sigspec.is_fully_const");
|
cover("kernel.rtlil.sigspec.is_fully_const");
|
||||||
|
@ -5600,6 +5471,18 @@ bool RTLIL::SigSpec::has_const() const
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool RTLIL::SigSpec::has_const(State state) const
|
||||||
|
{
|
||||||
|
cover("kernel.rtlil.sigspec.has_const");
|
||||||
|
|
||||||
|
pack();
|
||||||
|
for (auto it = chunks_.begin(); it != chunks_.end(); it++)
|
||||||
|
if (it->width > 0 && it->wire == NULL && std::find(it->data.begin(), it->data.end(), state) != it->data.end())
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool RTLIL::SigSpec::has_marked_bits() const
|
bool RTLIL::SigSpec::has_marked_bits() const
|
||||||
{
|
{
|
||||||
cover("kernel.rtlil.sigspec.has_marked_bits");
|
cover("kernel.rtlil.sigspec.has_marked_bits");
|
||||||
|
|
|
@ -91,6 +91,13 @@ namespace RTLIL
|
||||||
STATIC_ID_END,
|
STATIC_ID_END,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum PortDir : unsigned char {
|
||||||
|
PD_UNKNOWN = 0,
|
||||||
|
PD_INPUT = 1,
|
||||||
|
PD_OUTPUT = 2,
|
||||||
|
PD_INOUT = 3
|
||||||
|
};
|
||||||
|
|
||||||
struct Const;
|
struct Const;
|
||||||
struct AttrObject;
|
struct AttrObject;
|
||||||
struct NamedObject;
|
struct NamedObject;
|
||||||
|
@ -1322,12 +1329,15 @@ public:
|
||||||
bool is_chunk() const;
|
bool is_chunk() const;
|
||||||
inline bool is_bit() const { return width_ == 1; }
|
inline bool is_bit() const { return width_ == 1; }
|
||||||
|
|
||||||
|
bool known_driver() const;
|
||||||
|
|
||||||
bool is_fully_const() const;
|
bool is_fully_const() const;
|
||||||
bool is_fully_zero() const;
|
bool is_fully_zero() const;
|
||||||
bool is_fully_ones() const;
|
bool is_fully_ones() const;
|
||||||
bool is_fully_def() const;
|
bool is_fully_def() const;
|
||||||
bool is_fully_undef() const;
|
bool is_fully_undef() const;
|
||||||
bool has_const() const;
|
bool has_const() const;
|
||||||
|
bool has_const(State state) const;
|
||||||
bool has_marked_bits() const;
|
bool has_marked_bits() const;
|
||||||
bool is_onehot(int *pos = nullptr) const;
|
bool is_onehot(int *pos = nullptr) const;
|
||||||
|
|
||||||
|
@ -1719,7 +1729,11 @@ public:
|
||||||
std::vector<RTLIL::IdString> ports;
|
std::vector<RTLIL::IdString> ports;
|
||||||
void fixup_ports();
|
void fixup_ports();
|
||||||
|
|
||||||
pool<pair<RTLIL::Cell*, RTLIL::IdString>> bufNormQueue;
|
pool<RTLIL::Cell *> buf_norm_cell_queue;
|
||||||
|
pool<pair<RTLIL::Cell *, RTLIL::IdString>> buf_norm_cell_port_queue;
|
||||||
|
pool<RTLIL::Wire *> buf_norm_wire_queue;
|
||||||
|
pool<RTLIL::Cell *> pending_deleted_cells;
|
||||||
|
dict<RTLIL::Wire *, pool<RTLIL::Cell *>> buf_norm_connect_index;
|
||||||
void bufNormalize();
|
void bufNormalize();
|
||||||
|
|
||||||
template<typename T> void rewrite_sigspecs(T &functor);
|
template<typename T> void rewrite_sigspecs(T &functor);
|
||||||
|
@ -2052,6 +2066,8 @@ public:
|
||||||
int width, start_offset, port_id;
|
int width, start_offset, port_id;
|
||||||
bool port_input, port_output, upto, is_signed;
|
bool port_input, port_output, upto, is_signed;
|
||||||
|
|
||||||
|
bool known_driver() const { return driverCell_ != nullptr; }
|
||||||
|
|
||||||
RTLIL::Cell *driverCell() const { log_assert(driverCell_); return driverCell_; };
|
RTLIL::Cell *driverCell() const { log_assert(driverCell_); return driverCell_; };
|
||||||
RTLIL::IdString driverPort() const { log_assert(driverCell_); return driverPort_; };
|
RTLIL::IdString driverPort() const { log_assert(driverCell_); return driverPort_; };
|
||||||
|
|
||||||
|
@ -2123,6 +2139,7 @@ public:
|
||||||
bool known() const;
|
bool known() const;
|
||||||
bool input(const RTLIL::IdString &portname) const;
|
bool input(const RTLIL::IdString &portname) const;
|
||||||
bool output(const RTLIL::IdString &portname) const;
|
bool output(const RTLIL::IdString &portname) const;
|
||||||
|
PortDir port_dir(const RTLIL::IdString &portname) const;
|
||||||
|
|
||||||
// access cell parameters
|
// access cell parameters
|
||||||
bool hasParam(const RTLIL::IdString ¶mname) const;
|
bool hasParam(const RTLIL::IdString ¶mname) const;
|
||||||
|
|
679
kernel/rtlil_bufnorm.cc
Normal file
679
kernel/rtlil_bufnorm.cc
Normal file
|
@ -0,0 +1,679 @@
|
||||||
|
/*
|
||||||
|
* 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 "kernel/sigtools.h"
|
||||||
|
#include "kernel/modtools.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
YOSYS_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
|
||||||
|
void RTLIL::Design::bufNormalize(bool enable)
|
||||||
|
{
|
||||||
|
if (!enable)
|
||||||
|
{
|
||||||
|
if (!flagBufferedNormalized)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (auto module : modules()) {
|
||||||
|
module->buf_norm_cell_queue.clear();
|
||||||
|
module->buf_norm_wire_queue.clear();
|
||||||
|
module->buf_norm_cell_port_queue.clear();
|
||||||
|
for (auto wire : module->wires()) {
|
||||||
|
wire->driverCell_ = nullptr;
|
||||||
|
wire->driverPort_ = IdString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
flagBufferedNormalized = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!flagBufferedNormalized)
|
||||||
|
{
|
||||||
|
for (auto module : modules())
|
||||||
|
{
|
||||||
|
// When entering buf normalized mode, we need the first module-level bufNormalize
|
||||||
|
// call to know about all drivers, about all module ports (whether represented by
|
||||||
|
// a cell or not) and about all used but undriven wires (whether represented by a
|
||||||
|
// cell or not). We ensure this by enqueing all cell output ports and all wires.
|
||||||
|
|
||||||
|
for (auto cell : module->cells())
|
||||||
|
for (auto &conn : cell->connections()) {
|
||||||
|
if (GetSize(conn.second) == 0 || (cell->port_dir(conn.first) != RTLIL::PD_OUTPUT && cell->port_dir(conn.first) != RTLIL::PD_INOUT))
|
||||||
|
continue;
|
||||||
|
module->buf_norm_cell_queue.insert(cell);
|
||||||
|
module->buf_norm_cell_port_queue.emplace(cell, conn.first);
|
||||||
|
}
|
||||||
|
for (auto wire : module->wires())
|
||||||
|
module->buf_norm_wire_queue.insert(wire);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
flagBufferedNormalized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto module : modules())
|
||||||
|
module->bufNormalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct bit_drive_data_t {
|
||||||
|
int drivers = 0;
|
||||||
|
int inout = 0;
|
||||||
|
int users = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef ModWalker::PortBit PortBit;
|
||||||
|
|
||||||
|
void RTLIL::Module::bufNormalize()
|
||||||
|
{
|
||||||
|
// Since this is kernel code, we only log with yosys_xtrace set to not get
|
||||||
|
// in the way when using `debug` to debug specific passes.q
|
||||||
|
#define xlog(...) do { if (yosys_xtrace) log("#X [bufnorm] " __VA_ARGS__); } while (0)
|
||||||
|
|
||||||
|
if (!design->flagBufferedNormalized)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!buf_norm_cell_queue.empty() || !buf_norm_wire_queue.empty() || !connections_.empty())
|
||||||
|
{
|
||||||
|
// Ensure that every enqueued input port is represented by a cell
|
||||||
|
for (auto wire : buf_norm_wire_queue) {
|
||||||
|
if (wire->port_input && !wire->port_output) {
|
||||||
|
if (wire->driverCell_ != nullptr && wire->driverCell_->type != ID($input_port)) {
|
||||||
|
wire->driverCell_ = nullptr;
|
||||||
|
wire->driverPort_.clear();
|
||||||
|
}
|
||||||
|
if (wire->driverCell_ == nullptr) {
|
||||||
|
Cell *input_port_cell = addCell(NEW_ID, ID($input_port));
|
||||||
|
input_port_cell->setParam(ID::WIDTH, GetSize(wire));
|
||||||
|
input_port_cell->setPort(ID::Y, wire); // this hits the fast path that doesn't mutate the queues
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next we will temporarily undo buf normalization locally for
|
||||||
|
// everything enqueued. This means we will turn $buf and $connect back
|
||||||
|
// into connections. When doing this we also need to enqueue the other
|
||||||
|
// end of $buf and $connect cells, so we use a queue and do this until
|
||||||
|
// reaching a fixed point.
|
||||||
|
|
||||||
|
// While doing this, we will also discover all drivers fully connected
|
||||||
|
// to enqueued wires. We keep track of which wires are driven by a
|
||||||
|
// unique and full cell ports (in which case the wire can stay
|
||||||
|
// connected to the port) and which cell ports will need to be
|
||||||
|
// reconnected to a fresh intermediate wire to re-normalize the module.
|
||||||
|
|
||||||
|
idict<Wire *> wire_queue_entries; // Ordered queue of wires to process
|
||||||
|
int wire_queue_pos = 0; // Index up to which we processed the wires
|
||||||
|
|
||||||
|
// Wires with their unique driving cell port. If we know a wire is
|
||||||
|
// driven by multiple (potential) drivers, this is indicated by a
|
||||||
|
// nullptr as cell.
|
||||||
|
dict<Wire *, std::pair<Cell *, IdString>> direct_driven_wires;
|
||||||
|
|
||||||
|
// Set of non-unique or driving cell ports for each processed wire.
|
||||||
|
dict<Wire *, pool<std::pair<Cell *, IdString>>> direct_driven_wires_conflicts;
|
||||||
|
|
||||||
|
// Set of cell ports that need a fresh intermediate wire.
|
||||||
|
pool<std::pair<Cell *, IdString>> pending_ports;
|
||||||
|
|
||||||
|
// This helper will be called for every output/inout cell port that is
|
||||||
|
// already enqueued or becomes reachable when denormalizing $buf or
|
||||||
|
// $connect cells.
|
||||||
|
auto enqueue_cell_port = [&](Cell *cell, IdString port) {
|
||||||
|
xlog("processing cell port %s.%s\n", log_id(cell), log_id(port));
|
||||||
|
|
||||||
|
// An empty cell type means the cell got removed
|
||||||
|
if (cell->type.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
|
||||||
|
SigSpec const &sig = cell->getPort(port);
|
||||||
|
if (cell->type == ID($input_port)) {
|
||||||
|
// If an `$input_port` cell isn't fully connected to a full
|
||||||
|
// input port wire, we remove it since the wires are still the
|
||||||
|
// canonical source of module ports and the `$input_port` cells
|
||||||
|
// are just helpers to simplfiy the bufnorm invariant.
|
||||||
|
log_assert(port == ID::Y);
|
||||||
|
if (!sig.is_wire()) {
|
||||||
|
buf_norm_cell_queue.insert(cell);
|
||||||
|
remove(cell);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Wire *w = sig.as_wire();
|
||||||
|
if (!w->port_input || w->port_output) {
|
||||||
|
buf_norm_cell_queue.insert(cell);
|
||||||
|
remove(cell);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
w->driverCell_ = cell;
|
||||||
|
w->driverPort_ = ID::Y;
|
||||||
|
} else if (cell->type == ID($buf) && cell->attributes.empty() && !cell->name.isPublic()) {
|
||||||
|
// For a plain `$buf` cell, we enqueue all wires on its input
|
||||||
|
// side, bypass it using module level connections (skipping 'z
|
||||||
|
// bits) and then remove the cell. Eventually the module level
|
||||||
|
// connections will turn back into `$buf` and `$connect` cells,
|
||||||
|
// but since we also need to handle externally added module
|
||||||
|
// level connections, turning everything into connections first
|
||||||
|
// simplifies the logic for doing so.
|
||||||
|
|
||||||
|
// TODO: We could defer removing the $buf cells here, and
|
||||||
|
// re-use them in case we would create a new identical cell
|
||||||
|
// later.
|
||||||
|
log_assert(port == ID::Y);
|
||||||
|
SigSpec sig_a = cell->getPort(ID::A);
|
||||||
|
SigSpec sig_y = sig;
|
||||||
|
|
||||||
|
for (auto const &s : {sig_a, sig})
|
||||||
|
for (auto const &chunk : s.chunks())
|
||||||
|
if (chunk.wire)
|
||||||
|
wire_queue_entries(chunk.wire);
|
||||||
|
|
||||||
|
if (sig_a.has_const(State::Sz)) {
|
||||||
|
SigSpec new_a;
|
||||||
|
SigSpec new_y;
|
||||||
|
for (int i = 0; i < GetSize(sig_a); ++i) {
|
||||||
|
SigBit b = sig_a[i];
|
||||||
|
if (b == State::Sz)
|
||||||
|
continue;
|
||||||
|
new_a.append(b);
|
||||||
|
new_y.append(sig_y[i]);
|
||||||
|
}
|
||||||
|
sig_a = std::move(new_a);
|
||||||
|
sig_y = std::move(new_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sig_y.empty())
|
||||||
|
connect(sig_y, sig_a);
|
||||||
|
buf_norm_cell_queue.insert(cell);
|
||||||
|
remove(cell);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure all wires of the cell port are enqueued, ensuring we
|
||||||
|
// detect other connected drivers (output and inout).
|
||||||
|
for (auto const &chunk : sig.chunks())
|
||||||
|
if (chunk.wire)
|
||||||
|
wire_queue_entries(chunk.wire);
|
||||||
|
|
||||||
|
if (sig.is_wire()) {
|
||||||
|
// If the full cell port is connected to a full wire, we might be
|
||||||
|
// able to keep that connection if this is a unique output port driving that wire
|
||||||
|
Wire *w = sig.as_wire();
|
||||||
|
|
||||||
|
// We try to store the current port as unique driver, if this
|
||||||
|
// succeeds we're done with the port.
|
||||||
|
auto [found, inserted] = direct_driven_wires.emplace(w, {cell, port});
|
||||||
|
if (inserted || (found->second.first == cell && found->second.second == port))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// When this failed, we store this port as a conflict. If we
|
||||||
|
// had already stored a candidate for a unique driver, we also
|
||||||
|
// move it to the conflicts, leaving a nullptr marker.
|
||||||
|
|
||||||
|
auto &conflicts = direct_driven_wires_conflicts[w];
|
||||||
|
if (Cell *other_cell = found->second.first) {
|
||||||
|
if (other_cell->type == ID($input_port)) {
|
||||||
|
// Multiple input port cells
|
||||||
|
log_assert(cell->type != ID($input_port));
|
||||||
|
} else {
|
||||||
|
pending_ports.insert(found->second);
|
||||||
|
conflicts.emplace(found->second);
|
||||||
|
found->second = {nullptr, {}};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cell->type == ID($input_port)) {
|
||||||
|
found->second = {cell, port};
|
||||||
|
} else {
|
||||||
|
conflicts.emplace(cell, port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds this port to the ports that need a fresh intermediate wire.
|
||||||
|
// For full wires uniquely driven by a full output port, this isn't
|
||||||
|
// reached due to the `return` above.
|
||||||
|
pending_ports.emplace(cell, port);
|
||||||
|
};
|
||||||
|
|
||||||
|
// We process all explicitly enqueued cell ports (clearing the module level queue).
|
||||||
|
for (auto const &[cell, port_name] : buf_norm_cell_port_queue)
|
||||||
|
enqueue_cell_port(cell, port_name);
|
||||||
|
buf_norm_cell_port_queue.clear();
|
||||||
|
|
||||||
|
// And enqueue all wires for `$buf`/`$connect` processing (clearing the module level queue).
|
||||||
|
for (auto wire : buf_norm_wire_queue)
|
||||||
|
wire_queue_entries(wire);
|
||||||
|
buf_norm_wire_queue.clear();
|
||||||
|
|
||||||
|
// We also enqueue all wires that saw newly added module level connections.
|
||||||
|
for (auto &[a, b] : connections_)
|
||||||
|
for (auto &sig : {a, b})
|
||||||
|
for (auto const &chunk : sig.chunks())
|
||||||
|
if (chunk.wire)
|
||||||
|
wire_queue_entries(chunk.wire);
|
||||||
|
|
||||||
|
// We then process all wires by processing known driving cell ports
|
||||||
|
// (previously buf normalized) and following all `$connect` cells (that
|
||||||
|
// have a dedicated module level index while the design is in buf
|
||||||
|
// normalized mode).
|
||||||
|
while (wire_queue_pos < GetSize(wire_queue_entries)) {
|
||||||
|
auto wire = wire_queue_entries[wire_queue_pos++];
|
||||||
|
xlog("processing wire %s\n", log_id(wire));
|
||||||
|
|
||||||
|
if (wire->driverCell_) {
|
||||||
|
Cell *cell = wire->driverCell_;
|
||||||
|
IdString port = wire->driverPort_;
|
||||||
|
enqueue_cell_port(cell, port);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
auto found = buf_norm_connect_index.find(wire);
|
||||||
|
if (found == buf_norm_connect_index.end())
|
||||||
|
break;
|
||||||
|
while (!found->second.empty()) {
|
||||||
|
Cell *connect_cell = *found->second.begin();
|
||||||
|
log_assert(connect_cell->type == ID($connect));
|
||||||
|
SigSpec const &sig_a = connect_cell->getPort(ID::A);
|
||||||
|
SigSpec const &sig_b = connect_cell->getPort(ID::B);
|
||||||
|
xlog("found $connect cell %s: %s <-> %s\n", log_id(connect_cell), log_signal(sig_a), log_signal(sig_b));
|
||||||
|
for (auto &side : {sig_a, sig_b})
|
||||||
|
for (auto chunk : side.chunks())
|
||||||
|
if (chunk.wire)
|
||||||
|
wire_queue_entries(chunk.wire);
|
||||||
|
connect(sig_a, sig_b);
|
||||||
|
buf_norm_cell_queue.insert(connect_cell);
|
||||||
|
remove(connect_cell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this point we know all cell ports and wires that need to be
|
||||||
|
// re-normalized and know their connectivity is represented by module
|
||||||
|
// level connections.
|
||||||
|
|
||||||
|
// As a first step for re-normalization we add all require intermediate
|
||||||
|
// wires for cell output and inout ports.
|
||||||
|
for (auto &[cell, port] : pending_ports) {
|
||||||
|
SigSpec const &sig = cell->getPort(port);
|
||||||
|
Wire *w = addWire(NEW_ID, GetSize(sig));
|
||||||
|
|
||||||
|
// We update the module level connections, `direct_driven_wires`
|
||||||
|
// and `direct_driven_wires_conflicts` in such a way that they
|
||||||
|
// correspond to what you would get if the intermediate wires had
|
||||||
|
// been in place from the beginning.
|
||||||
|
connect(sig, w);
|
||||||
|
auto port_dir = cell->port_dir(port);
|
||||||
|
if (port_dir == RTLIL::PD_INOUT || port_dir == RTLIL::PD_UNKNOWN) {
|
||||||
|
direct_driven_wires.emplace(w, {nullptr, {}});
|
||||||
|
direct_driven_wires_conflicts[w].emplace(cell, port);
|
||||||
|
} else {
|
||||||
|
direct_driven_wires.emplace(w, {cell, port});
|
||||||
|
}
|
||||||
|
|
||||||
|
cell->setPort(port, w);
|
||||||
|
wire_queue_entries(w);
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this point we're done with creating wires and know which ones are
|
||||||
|
// fully driven by full output ports of existing cells.
|
||||||
|
|
||||||
|
// First we clear the bufnorm data for all processed wires, all of
|
||||||
|
// these will be reassigned later, but we use `driverCell_ == nullptr`
|
||||||
|
// to keep track of the wires that we still have to update.
|
||||||
|
for (auto wire : wire_queue_entries) {
|
||||||
|
wire->driverCell_ = nullptr;
|
||||||
|
wire->driverPort_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// For the unique output cell ports fully connected to a full wire, we
|
||||||
|
// can update the bufnorm data right away. For all other wires we will
|
||||||
|
// have to create new `$buf` cells.
|
||||||
|
for (auto const &[wire, cellport] : direct_driven_wires) {
|
||||||
|
wire->driverCell_ = cellport.first;
|
||||||
|
wire->driverPort_ = cellport.second;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// To create fresh `$buf` cells for all remaining wires, we need to
|
||||||
|
// process the module level connectivity to figure out what the input
|
||||||
|
// of those `$buf` cells should be and to figure out whether we need
|
||||||
|
// any `$connect` cells to represent bidirectional inout connections
|
||||||
|
// (or driver conflicts).
|
||||||
|
|
||||||
|
if (yosys_xtrace)
|
||||||
|
for (auto const &[lhs, rhs] : connections_)
|
||||||
|
xlog("connection %s <-> %s\n", log_signal(lhs), log_signal(rhs));
|
||||||
|
|
||||||
|
|
||||||
|
// We transfer the connectivity into a sigmap and then clear the module
|
||||||
|
// level connections. This forgets about the structure of module level
|
||||||
|
// connections, but bufnorm only guarantees that the connectivity as
|
||||||
|
// maintained by a `SigMap` is preserved.
|
||||||
|
SigMap sigmap(this);
|
||||||
|
new_connections({});
|
||||||
|
|
||||||
|
pool<SigBit> conflicted;
|
||||||
|
pool<SigBit> driven;
|
||||||
|
|
||||||
|
// We iterate over all direct driven wires and try to make that wire's
|
||||||
|
// sigbits the representative sigbit for the net. We do a second pass
|
||||||
|
// to detect conflicts to then remove the conflicts from `driven`.
|
||||||
|
for (bool check : {false, true}) {
|
||||||
|
for (auto const &[wire, cellport] : direct_driven_wires) {
|
||||||
|
if (cellport.first == nullptr)
|
||||||
|
continue;
|
||||||
|
auto const &[cell, port] = cellport;
|
||||||
|
|
||||||
|
SigSpec z_mask;
|
||||||
|
if (cell->type == ID($buf))
|
||||||
|
z_mask = cell->getPort(ID::A);
|
||||||
|
|
||||||
|
for (int i = 0; i != GetSize(wire); ++i) {
|
||||||
|
SigBit driver = SigBit(wire, i);
|
||||||
|
if (!z_mask.empty() && z_mask[i] == State::Sz)
|
||||||
|
continue;
|
||||||
|
if (check) {
|
||||||
|
SigBit repr = sigmap(driver);
|
||||||
|
if (repr != driver)
|
||||||
|
conflicted.insert(repr);
|
||||||
|
else
|
||||||
|
driven.insert(repr);
|
||||||
|
} else {
|
||||||
|
sigmap.database.promote(driver);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that module level inout ports are directly driven or
|
||||||
|
// connected using `$connect` cells and never `$buf`fered.
|
||||||
|
for (auto wire : wire_queue_entries) {
|
||||||
|
if (!wire->port_input || !wire->port_output)
|
||||||
|
continue;
|
||||||
|
for (int i = 0; i != GetSize(wire); ++i) {
|
||||||
|
SigBit driver = SigBit(wire, i);
|
||||||
|
SigBit repr = sigmap(driver);
|
||||||
|
if (driver != repr)
|
||||||
|
driven.erase(repr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &bit : conflicted)
|
||||||
|
driven.erase(bit);
|
||||||
|
|
||||||
|
// Module level bitwise connections not representable by `$buf` cells
|
||||||
|
pool<pair<SigBit, SigBit>> undirected_connections;
|
||||||
|
|
||||||
|
// Starts out empty but is updated with the connectivity realized by freshly added `$buf` cells
|
||||||
|
SigMap buf_connected;
|
||||||
|
|
||||||
|
// For every enqueued wire, we compute a SigSpec of representative
|
||||||
|
// drivers. If there are any bits without a unique driver we represent
|
||||||
|
// that with `Sz`. If there are multiple drivers for a net, they become
|
||||||
|
// connected via `$connect` cells but every wire of the net has the
|
||||||
|
// corresponding bit still driven by a buffered `Sz`.
|
||||||
|
for (auto wire : wire_queue_entries) {
|
||||||
|
SigSpec wire_drivers;
|
||||||
|
for (int i = 0; i < GetSize(wire); ++i) {
|
||||||
|
SigBit bit(wire, i);
|
||||||
|
SigBit mapped = sigmap(bit);
|
||||||
|
xlog("bit %s -> mapped %s\n", log_signal(bit), log_signal(mapped));
|
||||||
|
|
||||||
|
|
||||||
|
buf_connected.apply(bit);
|
||||||
|
buf_connected.add(bit, mapped);
|
||||||
|
buf_connected.database.promote(mapped);
|
||||||
|
|
||||||
|
if (wire->driverCell_ == nullptr) {
|
||||||
|
if (!mapped.is_wire() || driven.count(mapped)) {
|
||||||
|
wire_drivers.append(mapped);
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
wire_drivers.append(State::Sz);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bit < mapped)
|
||||||
|
undirected_connections.emplace(bit, mapped);
|
||||||
|
else if (mapped < bit)
|
||||||
|
undirected_connections.emplace(mapped, bit);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wire->driverCell_ == nullptr) {
|
||||||
|
xlog("wire %s drivers %s\n", log_id(wire), log_signal(wire_drivers));
|
||||||
|
addBuf(NEW_ID, wire_drivers, wire);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally we group the bitwise connections to emit word-level $connect cells
|
||||||
|
|
||||||
|
static auto sort_key = [](std::pair<SigBit, SigBit> const &p) {
|
||||||
|
int first_offset = p.first.is_wire() ? p.first.offset : 0;
|
||||||
|
int second_offset = p.second.is_wire() ? p.second.offset : 0;
|
||||||
|
return std::make_tuple(p.first.wire, p.second.wire, first_offset - second_offset, p);
|
||||||
|
};
|
||||||
|
|
||||||
|
undirected_connections.sort([](std::pair<SigBit, SigBit> const &p, std::pair<SigBit, SigBit> const &q) {
|
||||||
|
return sort_key(p) < sort_key(q);
|
||||||
|
});
|
||||||
|
|
||||||
|
SigSpec tmp_a, tmp_b;
|
||||||
|
|
||||||
|
for (auto &[bit_a, bit_b] : undirected_connections) {
|
||||||
|
tmp_a.append(bit_a);
|
||||||
|
tmp_b.append(bit_b);
|
||||||
|
}
|
||||||
|
|
||||||
|
xlog("LHS: %s\n", log_signal(tmp_a));
|
||||||
|
xlog("RHS: %s\n", log_signal(tmp_b));
|
||||||
|
|
||||||
|
|
||||||
|
SigSpec sig_a, sig_b;
|
||||||
|
SigBit next_a, next_b;
|
||||||
|
|
||||||
|
auto emit_connect_cell = [&]() {
|
||||||
|
if (sig_a.empty())
|
||||||
|
return;
|
||||||
|
xlog("connect %s <-> %s\n", log_signal(sig_a), log_signal(sig_b));
|
||||||
|
Cell *connect_cell = addCell(NEW_ID, ID($connect));
|
||||||
|
connect_cell->setParam(ID::WIDTH, GetSize(sig_a));
|
||||||
|
connect_cell->setPort(ID::A, sig_a);
|
||||||
|
connect_cell->setPort(ID::B, sig_b);
|
||||||
|
sig_a = SigSpec();
|
||||||
|
sig_b = SigSpec();
|
||||||
|
};
|
||||||
|
|
||||||
|
for (auto &[bit_a, bit_b] : undirected_connections) {
|
||||||
|
if (bit_a == bit_b)
|
||||||
|
continue;
|
||||||
|
if (bit_a != next_a || bit_b != next_b)
|
||||||
|
emit_connect_cell();
|
||||||
|
|
||||||
|
sig_a.append(bit_a);
|
||||||
|
sig_b.append(bit_b);
|
||||||
|
next_a = bit_a;
|
||||||
|
next_b = bit_b;
|
||||||
|
if (next_a.is_wire())
|
||||||
|
next_a.offset++;
|
||||||
|
if (next_b.is_wire())
|
||||||
|
next_b.offset++;
|
||||||
|
|
||||||
|
}
|
||||||
|
emit_connect_cell();
|
||||||
|
|
||||||
|
buf_norm_cell_queue.clear();
|
||||||
|
|
||||||
|
log_assert(buf_norm_cell_port_queue.empty());
|
||||||
|
log_assert(buf_norm_wire_queue.empty());
|
||||||
|
log_assert(connections_.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto cell : pending_deleted_cells) {
|
||||||
|
delete cell;
|
||||||
|
}
|
||||||
|
pending_deleted_cells.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RTLIL::Cell::unsetPort(const RTLIL::IdString& portname)
|
||||||
|
{
|
||||||
|
RTLIL::SigSpec signal;
|
||||||
|
auto conn_it = connections_.find(portname);
|
||||||
|
|
||||||
|
if (conn_it != connections_.end())
|
||||||
|
{
|
||||||
|
for (auto mon : module->monitors)
|
||||||
|
mon->notify_connect(this, conn_it->first, conn_it->second, signal);
|
||||||
|
|
||||||
|
if (module->design)
|
||||||
|
for (auto mon : module->design->monitors)
|
||||||
|
mon->notify_connect(this, conn_it->first, conn_it->second, signal);
|
||||||
|
|
||||||
|
if (yosys_xtrace) {
|
||||||
|
log("#X# Unconnect %s.%s.%s\n", log_id(this->module), log_id(this), log_id(portname));
|
||||||
|
log_backtrace("-X- ", yosys_xtrace-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (module->design && module->design->flagBufferedNormalized) {
|
||||||
|
if (conn_it->second.is_wire()) {
|
||||||
|
Wire *w = conn_it->second.as_wire();
|
||||||
|
if (w->driverCell_ == this && w->driverPort_ == portname) {
|
||||||
|
w->driverCell_ = nullptr;
|
||||||
|
w->driverPort_ = IdString();
|
||||||
|
module->buf_norm_wire_queue.insert(w);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == ID($connect)) {
|
||||||
|
for (auto &[port, sig] : connections_) {
|
||||||
|
for (auto &chunk : sig.chunks()) {
|
||||||
|
if (!chunk.wire)
|
||||||
|
continue;
|
||||||
|
auto it = module->buf_norm_connect_index.find(chunk.wire);
|
||||||
|
if (it == module->buf_norm_connect_index.end())
|
||||||
|
continue;
|
||||||
|
it->second.erase(this);
|
||||||
|
if (it->second.empty())
|
||||||
|
module->buf_norm_connect_index.erase(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
connections_.erase(conn_it);
|
||||||
|
for (auto &[port, sig] : connections_) {
|
||||||
|
for (auto &chunk : sig.chunks()) {
|
||||||
|
if (!chunk.wire)
|
||||||
|
continue;
|
||||||
|
module->buf_norm_connect_index[chunk.wire].insert(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
connections_.erase(conn_it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RTLIL::Cell::setPort(const RTLIL::IdString& portname, RTLIL::SigSpec signal)
|
||||||
|
{
|
||||||
|
auto r = connections_.insert(portname);
|
||||||
|
auto conn_it = r.first;
|
||||||
|
if (!r.second && conn_it->second == signal)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (auto mon : module->monitors)
|
||||||
|
mon->notify_connect(this, conn_it->first, conn_it->second, signal);
|
||||||
|
|
||||||
|
if (module->design)
|
||||||
|
for (auto mon : module->design->monitors)
|
||||||
|
mon->notify_connect(this, conn_it->first, conn_it->second, signal);
|
||||||
|
|
||||||
|
if (yosys_xtrace) {
|
||||||
|
log("#X# Connect %s.%s.%s = %s (%d)\n", log_id(this->module), log_id(this), log_id(portname), log_signal(signal), GetSize(signal));
|
||||||
|
log_backtrace("-X- ", yosys_xtrace-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (module->design && module->design->flagBufferedNormalized)
|
||||||
|
{
|
||||||
|
// We eagerly clear a driver that got disconnected by changing this port connection
|
||||||
|
if (conn_it->second.is_wire()) {
|
||||||
|
Wire *w = conn_it->second.as_wire();
|
||||||
|
if (w->driverCell_ == this && w->driverPort_ == portname) {
|
||||||
|
w->driverCell_ = nullptr;
|
||||||
|
w->driverPort_ = IdString();
|
||||||
|
module->buf_norm_wire_queue.insert(w);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto dir = port_dir(portname);
|
||||||
|
// This is a fast path that handles connecting a full driverless wire to an output port,
|
||||||
|
// everything else is goes through the bufnorm queues and is handled during the next
|
||||||
|
// bufNormalize call
|
||||||
|
if ((dir == RTLIL::PD_OUTPUT || dir == RTLIL::PD_INOUT) && signal.is_wire()) {
|
||||||
|
Wire *w = signal.as_wire();
|
||||||
|
if (w->driverCell_ == nullptr) {
|
||||||
|
w->driverCell_ = this;
|
||||||
|
w->driverPort_ = portname;
|
||||||
|
|
||||||
|
conn_it->second = std::move(signal);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dir == RTLIL::PD_OUTPUT || dir == RTLIL::PD_INOUT) {
|
||||||
|
module->buf_norm_cell_queue.insert(this);
|
||||||
|
module->buf_norm_cell_port_queue.emplace(this, portname);
|
||||||
|
} else {
|
||||||
|
for (auto &chunk : signal.chunks())
|
||||||
|
if (chunk.wire != nullptr && chunk.wire->driverCell_ == nullptr)
|
||||||
|
module->buf_norm_wire_queue.insert(chunk.wire);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == ID($connect)) {
|
||||||
|
for (auto &[port, sig] : connections_) {
|
||||||
|
for (auto &chunk : sig.chunks()) {
|
||||||
|
if (!chunk.wire)
|
||||||
|
continue;
|
||||||
|
auto it = module->buf_norm_connect_index.find(chunk.wire);
|
||||||
|
if (it == module->buf_norm_connect_index.end())
|
||||||
|
continue;
|
||||||
|
it->second.erase(this);
|
||||||
|
if (it->second.empty())
|
||||||
|
module->buf_norm_connect_index.erase(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
conn_it->second = std::move(signal);
|
||||||
|
for (auto &[port, sig] : connections_) {
|
||||||
|
for (auto &chunk : sig.chunks()) {
|
||||||
|
if (!chunk.wire)
|
||||||
|
continue;
|
||||||
|
module->buf_norm_connect_index[chunk.wire].insert(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
conn_it->second = std::move(signal);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
YOSYS_NAMESPACE_END
|
|
@ -600,14 +600,39 @@ void rmunused_module(RTLIL::Module *module, bool purge_mode, bool verbose, bool
|
||||||
log("Finding unused cells or wires in module %s..\n", module->name);
|
log("Finding unused cells or wires in module %s..\n", module->name);
|
||||||
|
|
||||||
std::vector<RTLIL::Cell*> delcells;
|
std::vector<RTLIL::Cell*> delcells;
|
||||||
for (auto cell : module->cells())
|
for (auto cell : module->cells()) {
|
||||||
if (cell->type.in(ID($pos), ID($_BUF_), ID($buf)) && !cell->has_keep_attr()) {
|
if (cell->type.in(ID($pos), ID($_BUF_), ID($buf)) && !cell->has_keep_attr()) {
|
||||||
bool is_signed = cell->type == ID($pos) && cell->getParam(ID::A_SIGNED).as_bool();
|
bool is_signed = cell->type == ID($pos) && cell->getParam(ID::A_SIGNED).as_bool();
|
||||||
RTLIL::SigSpec a = cell->getPort(ID::A);
|
RTLIL::SigSpec a = cell->getPort(ID::A);
|
||||||
RTLIL::SigSpec y = cell->getPort(ID::Y);
|
RTLIL::SigSpec y = cell->getPort(ID::Y);
|
||||||
a.extend_u0(GetSize(y), is_signed);
|
a.extend_u0(GetSize(y), is_signed);
|
||||||
|
|
||||||
|
if (a.has_const(State::Sz)) {
|
||||||
|
SigSpec new_a;
|
||||||
|
SigSpec new_y;
|
||||||
|
for (int i = 0; i < GetSize(a); ++i) {
|
||||||
|
SigBit b = a[i];
|
||||||
|
if (b == State::Sz)
|
||||||
|
continue;
|
||||||
|
new_a.append(b);
|
||||||
|
new_y.append(y[i]);
|
||||||
|
}
|
||||||
|
a = std::move(new_a);
|
||||||
|
y = std::move(new_y);
|
||||||
|
}
|
||||||
|
if (!y.empty())
|
||||||
module->connect(y, a);
|
module->connect(y, a);
|
||||||
delcells.push_back(cell);
|
delcells.push_back(cell);
|
||||||
|
} else if (cell->type.in(ID($connect)) && !cell->has_keep_attr()) {
|
||||||
|
RTLIL::SigSpec a = cell->getPort(ID::A);
|
||||||
|
RTLIL::SigSpec b = cell->getPort(ID::B);
|
||||||
|
if (a.has_const() && !b.has_const())
|
||||||
|
std::swap(a, b);
|
||||||
|
module->connect(a, b);
|
||||||
|
delcells.push_back(cell);
|
||||||
|
} else if (cell->type.in(ID($input_port)) && !cell->has_keep_attr()) {
|
||||||
|
delcells.push_back(cell);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for (auto cell : delcells) {
|
for (auto cell : delcells) {
|
||||||
if (verbose)
|
if (verbose)
|
||||||
|
|
|
@ -1566,6 +1566,70 @@ clone_lut:
|
||||||
design->remove(mapped_mod);
|
design->remove(mapped_mod);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void replace_zbufs(Design *design)
|
||||||
|
{
|
||||||
|
design->bufNormalize(true);
|
||||||
|
std::vector<Cell *> zbufs;
|
||||||
|
|
||||||
|
for (auto mod : design->modules()) {
|
||||||
|
zbufs.clear();
|
||||||
|
for (auto cell : mod->cells()) {
|
||||||
|
if (cell->type != ID($buf))
|
||||||
|
continue;
|
||||||
|
auto &sig = cell->getPort(ID::A);
|
||||||
|
for (int i = 0; i < GetSize(sig); ++i) {
|
||||||
|
if (sig[i] == State::Sz) {
|
||||||
|
zbufs.push_back(cell);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto cell : zbufs) {
|
||||||
|
auto sig = cell->getPort(ID::A);
|
||||||
|
for (int i = 0; i < GetSize(sig); ++i) {
|
||||||
|
if (sig[i] == State::Sz) {
|
||||||
|
Wire *w = mod->addWire(NEW_ID);
|
||||||
|
Cell *ud = mod->addCell(NEW_ID, ID($tribuf));
|
||||||
|
ud->set_bool_attribute(ID(aiger2_zbuf));
|
||||||
|
ud->setParam(ID::WIDTH, 1);
|
||||||
|
ud->setPort(ID::Y, w);
|
||||||
|
ud->setPort(ID::EN, State::S0);
|
||||||
|
ud->setPort(ID::A, State::S0);
|
||||||
|
sig[i] = w;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log("XXX %s -> %s\n", log_signal(cell->getPort(ID::A)), log_signal(sig));
|
||||||
|
cell->setPort(ID::A, sig);
|
||||||
|
}
|
||||||
|
|
||||||
|
mod->bufNormalize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void restore_zbufs(Design *design)
|
||||||
|
{
|
||||||
|
std::vector<Cell *> to_remove;
|
||||||
|
|
||||||
|
for (auto mod : design->modules()) {
|
||||||
|
to_remove.clear();
|
||||||
|
for (auto cell : mod->cells())
|
||||||
|
if (cell->type == ID($tribuf) && cell->has_attribute(ID(aiger2_zbuf)))
|
||||||
|
to_remove.push_back(cell);
|
||||||
|
|
||||||
|
for (auto cell : to_remove) {
|
||||||
|
SigSpec sig_y = cell->getPort(ID::Y);
|
||||||
|
mod->addBuf(NEW_ID, Const(State::Sz, GetSize(sig_y)), sig_y);
|
||||||
|
mod->remove(cell);
|
||||||
|
}
|
||||||
|
mod->bufNormalize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
struct Abc9OpsPass : public Pass {
|
struct Abc9OpsPass : public Pass {
|
||||||
Abc9OpsPass() : Pass("abc9_ops", "helper functions for ABC9") { }
|
Abc9OpsPass() : Pass("abc9_ops", "helper functions for ABC9") { }
|
||||||
void help() override
|
void help() override
|
||||||
|
@ -1668,6 +1732,8 @@ struct Abc9OpsPass : public Pass {
|
||||||
bool prep_lut_mode = false;
|
bool prep_lut_mode = false;
|
||||||
bool prep_box_mode = false;
|
bool prep_box_mode = false;
|
||||||
bool reintegrate_mode = false;
|
bool reintegrate_mode = false;
|
||||||
|
bool replace_zbufs_mode = false;
|
||||||
|
bool restore_zbufs_mode = false;
|
||||||
bool dff_mode = false;
|
bool dff_mode = false;
|
||||||
std::string write_lut_dst;
|
std::string write_lut_dst;
|
||||||
int maxlut = 0;
|
int maxlut = 0;
|
||||||
|
@ -1754,16 +1820,30 @@ struct Abc9OpsPass : public Pass {
|
||||||
dff_mode = true;
|
dff_mode = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (arg == "-replace_zbufs") {
|
||||||
|
replace_zbufs_mode = true;
|
||||||
|
valid = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (arg == "-restore_zbufs") {
|
||||||
|
restore_zbufs_mode = true;
|
||||||
|
valid = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
extra_args(args, argidx, design);
|
extra_args(args, argidx, design);
|
||||||
|
|
||||||
if (!valid)
|
if (!valid)
|
||||||
log_cmd_error("At least one of -check, -break_scc, -prep_{delays,xaiger,dff[123],lut,box}, -write_{lut,box}, -reintegrate must be specified.\n");
|
log_cmd_error("At least one of -check, -break_scc, -prep_{delays,xaiger,dff[123],lut,box}, -write_{lut,box}, -reintegrate, -{replace,restore}_zbufs must be specified.\n");
|
||||||
|
|
||||||
if (dff_mode && !check_mode && !prep_hier_mode && !prep_delays_mode && !prep_xaiger_mode && !reintegrate_mode)
|
if (dff_mode && !check_mode && !prep_hier_mode && !prep_delays_mode && !prep_xaiger_mode && !reintegrate_mode)
|
||||||
log_cmd_error("'-dff' option is only relevant for -prep_{hier,delay,xaiger} or -reintegrate.\n");
|
log_cmd_error("'-dff' option is only relevant for -prep_{hier,delay,xaiger} or -reintegrate.\n");
|
||||||
|
|
||||||
|
if (replace_zbufs_mode)
|
||||||
|
replace_zbufs(design);
|
||||||
|
if (restore_zbufs_mode)
|
||||||
|
restore_zbufs(design);
|
||||||
if (check_mode)
|
if (check_mode)
|
||||||
check(design, dff_mode);
|
check(design, dff_mode);
|
||||||
if (prep_hier_mode)
|
if (prep_hier_mode)
|
||||||
|
|
|
@ -169,10 +169,12 @@ struct AbcNewPass : public ScriptPass {
|
||||||
}
|
}
|
||||||
|
|
||||||
run(stringf(" abc9_ops -write_box %s/input.box", tmpdir));
|
run(stringf(" abc9_ops -write_box %s/input.box", tmpdir));
|
||||||
|
run(" abc9_ops -replace_zbufs");
|
||||||
run(stringf(" write_xaiger2 -mapping_prep -map2 %s/input.map2 %s/input.xaig", tmpdir, tmpdir));
|
run(stringf(" write_xaiger2 -mapping_prep -map2 %s/input.map2 %s/input.xaig", tmpdir, tmpdir));
|
||||||
run(stringf(" abc9_exe %s -cwd %s -box %s/input.box", exe_options, tmpdir, tmpdir));
|
run(stringf(" abc9_exe %s -cwd %s -box %s/input.box", exe_options, tmpdir, tmpdir));
|
||||||
run(stringf(" read_xaiger2 -sc_mapping -module_name %s -map2 %s/input.map2 %s/output.aig",
|
run(stringf(" read_xaiger2 -sc_mapping -module_name %s -map2 %s/input.map2 %s/output.aig",
|
||||||
modname.c_str(), tmpdir.c_str(), tmpdir.c_str()));
|
modname.c_str(), tmpdir.c_str(), tmpdir.c_str()));
|
||||||
|
run(" abc9_ops -restore_zbufs");
|
||||||
|
|
||||||
if (!help_mode && mod->has_attribute(ID(abc9_script))) {
|
if (!help_mode && mod->has_attribute(ID(abc9_script))) {
|
||||||
if (script_save.empty())
|
if (script_save.empty())
|
||||||
|
|
|
@ -47,6 +47,21 @@ void simplemap_buf(RTLIL::Module *module, RTLIL::Cell *cell)
|
||||||
RTLIL::SigSpec sig_a = cell->getPort(ID::A);
|
RTLIL::SigSpec sig_a = cell->getPort(ID::A);
|
||||||
RTLIL::SigSpec sig_y = cell->getPort(ID::Y);
|
RTLIL::SigSpec sig_y = cell->getPort(ID::Y);
|
||||||
|
|
||||||
|
if (sig_a.has_const(State::Sz)) {
|
||||||
|
SigSpec new_a;
|
||||||
|
SigSpec new_y;
|
||||||
|
for (int i = 0; i < GetSize(sig_a); ++i) {
|
||||||
|
SigBit b = sig_a[i];
|
||||||
|
if (b == State::Sz)
|
||||||
|
continue;
|
||||||
|
new_a.append(b);
|
||||||
|
new_y.append(sig_y[i]);
|
||||||
|
}
|
||||||
|
sig_a = std::move(new_a);
|
||||||
|
sig_y = std::move(new_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sig_y.empty())
|
||||||
module->connect(RTLIL::SigSig(sig_y, sig_a));
|
module->connect(RTLIL::SigSig(sig_y, sig_a));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3216,3 +3216,26 @@ module \$scopeinfo ();
|
||||||
parameter TYPE = "";
|
parameter TYPE = "";
|
||||||
|
|
||||||
endmodule
|
endmodule
|
||||||
|
|
||||||
|
// --------------------------------------------------------
|
||||||
|
//* group wire
|
||||||
|
module \$connect (A, B);
|
||||||
|
|
||||||
|
parameter WIDTH = 0;
|
||||||
|
|
||||||
|
inout [WIDTH-1:0] A;
|
||||||
|
inout [WIDTH-1:0] B;
|
||||||
|
|
||||||
|
tran connect[WIDTH-1:0] (A, B);
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
// --------------------------------------------------------
|
||||||
|
//* group wire
|
||||||
|
module \$input_port (Y);
|
||||||
|
|
||||||
|
parameter WIDTH = 0;
|
||||||
|
|
||||||
|
inout [WIDTH-1:0] Y;
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
|
@ -647,3 +647,28 @@ module _90_lut;
|
||||||
endmodule
|
endmodule
|
||||||
`endif
|
`endif
|
||||||
|
|
||||||
|
|
||||||
|
// --------------------------------------------------------
|
||||||
|
// Bufnorm helpers
|
||||||
|
// --------------------------------------------------------
|
||||||
|
|
||||||
|
(* techmap_celltype = "$connect" *)
|
||||||
|
module \$connect (A, B);
|
||||||
|
|
||||||
|
parameter WIDTH = 0;
|
||||||
|
|
||||||
|
inout [WIDTH-1:0] A;
|
||||||
|
inout [WIDTH-1:0] B;
|
||||||
|
|
||||||
|
assign A = B; // RTLIL assignments are not inherently directed
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
(* techmap_celltype = "$input_port" *)
|
||||||
|
module \$input_port (Y);
|
||||||
|
|
||||||
|
parameter WIDTH = 0;
|
||||||
|
|
||||||
|
inout [WIDTH-1:0] Y; // This cell is just a maker, so we leave Y undriven
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
|
@ -157,6 +157,7 @@ splitnets -ports
|
||||||
copy test gold
|
copy test gold
|
||||||
flatten gold
|
flatten gold
|
||||||
techmap submodule1
|
techmap submodule1
|
||||||
|
opt_clean
|
||||||
select test
|
select test
|
||||||
write_aiger2 -flatten aiger2_ops.aig
|
write_aiger2 -flatten aiger2_ops.aig
|
||||||
select -clear
|
select -clear
|
||||||
|
@ -216,6 +217,7 @@ prep -top top
|
||||||
techmap t:$add
|
techmap t:$add
|
||||||
|
|
||||||
splitnets -ports top
|
splitnets -ports top
|
||||||
|
opt_clean
|
||||||
write_aiger2 -flatten aiger2_flatten.aig
|
write_aiger2 -flatten aiger2_flatten.aig
|
||||||
flatten
|
flatten
|
||||||
rename top gold
|
rename top gold
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue