3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2026-07-15 03:35:40 +00:00

rtlil, patch: incremental signorm via connect_incremental, replacing batched sigNormalize in Patch::patch

This commit is contained in:
Emil J. Tywoniak 2026-05-29 12:36:48 +02:00
parent c3457e2e5c
commit c264649ae7
3 changed files with 73 additions and 61 deletions

View file

@ -2666,6 +2666,13 @@ public:
const pool<PortBit> &fanout(SigBit bit);
const dict<SigBit, pool<PortBit>> &signorm_fanout() const;
// Equivalent to `connect(lhs, rhs)` followed by `sigNormalize()` for the
// merge implied by this single connection: updates the sigmap, promotes
// the driven side as canonical, and re-normalizes any existing fanout
// consumers of the demoted side. Lets callers in signorm mode merge
// signals without the overhead of a full normalize.
void connect_incremental(const SigSpec &lhs, const SigSpec &rhs);
template<typename T> void rewrite_sigspecs(T &functor);
template<typename T> void rewrite_sigspecs2(T &functor);
void cloneInto(RTLIL::Module *new_mod) const;

View file

@ -225,54 +225,56 @@ struct RTLIL::SigNormIndex
}
}
void flush_connections() {
// Process one (lhs, rhs) connection: apply current sigmap, then for each
// bit pair either merge into sigmap (and enqueue the demoted side into
// newly_driven) or emit a $connect cell when both sides have drivers.
void flush_one_connection(SigSpec lhs, SigSpec rhs) {
std::vector<SigBit> connect_lhs;
std::vector<SigBit> connect_rhs;
sigmap.apply(lhs);
sigmap.apply(rhs);
auto rhs_bits = rhs.bits().begin();
for (auto l : lhs.bits()) {
auto r = *rhs_bits;
++rhs_bits;
if (l == r)
continue;
// TODO figure out what should happen with 'z
bool l_driven = !l.is_wire() || l.wire->known_driver();
bool r_driven = !r.is_wire() || r.wire->known_driver();
if (l_driven && r_driven) {
connect_lhs.push_back(l);
connect_rhs.push_back(r);
continue;
}
sigmap.add(l, r);
if (l_driven) {
sigmap.database.promote(l);
newly_driven.insert(r);
} else {
sigmap.database.promote(r);
newly_driven.insert(l);
}
}
if (!connect_lhs.empty()) {
Cell *cell = module->addCell(NEW_ID, ID($connect));
xlog("add connect (1) %s\n", cell->name);
cell->setParam(ID::WIDTH, GetSize(connect_lhs));
cell->setPort(ID::A, std::move(connect_lhs));
cell->setPort(ID::B, std::move(connect_rhs));
}
}
void flush_connections() {
auto begin = module->connections_.begin() + restored_connections;
auto end = module->connections_.end();
for (auto it = begin; it != end; ++it) {
auto &[lhs, rhs] = *it;
sigmap.apply(lhs);
sigmap.apply(rhs);
auto rhs_bits = rhs.bits().begin();
connect_lhs.clear();
connect_rhs.clear();
for (auto l : lhs.bits()) {
auto r = *rhs_bits;
++rhs_bits;
if (l == r)
continue;
// TODO figure out what should happen with 'z
bool l_driven = !l.is_wire() || l.wire->known_driver();
bool r_driven = !r.is_wire() || r.wire->known_driver();
if (l_driven && r_driven) {
connect_lhs.push_back(l);
connect_rhs.push_back(r);
continue;
}
sigmap.add(l, r);
if (l_driven) {
sigmap.database.promote(l);
newly_driven.insert(r);
} else {
sigmap.database.promote(r);
newly_driven.insert(l);
}
}
if (!connect_lhs.empty()) {
Cell *cell = module->addCell(NEW_ID, ID($connect));
xlog("add connect (1) %s\n", cell->name);
cell->setParam(ID::WIDTH, GetSize(connect_lhs));
cell->setPort(ID::A, std::move(connect_lhs));
cell->setPort(ID::B, std::move(connect_rhs));
}
}
for (auto it = begin; it != end; ++it)
flush_one_connection(it->first, it->second);
module->connections_.clear();
restored_connections = 0;
@ -572,6 +574,14 @@ const dict<RTLIL::SigBit, pool<RTLIL::PortBit>> &RTLIL::Module::signorm_fanout()
return sig_norm_index->fanout;
}
void RTLIL::Module::connect_incremental(const SigSpec &lhs, const SigSpec &rhs)
{
log_assert(sig_norm_index != nullptr);
log_assert(GetSize(lhs) == GetSize(rhs));
sig_norm_index->flush_one_connection(lhs, rhs);
sig_norm_index->flush_newly_driven();
}
void RTLIL::Module::remove(RTLIL::Cell *cell)
{
while (!cell->connections_.empty())

View file

@ -140,35 +140,29 @@ Cell* Patch::commit_cell(std::unique_ptr<Cell> cell) {
void Patch::patch(Cell* old_cell, IdString old_port, SigSpec new_sig) {
SigSpec old_sig = old_cell->getPort(old_port);
log_assert(old_sig.size() == new_sig.size());
log("patching %s %s which is %s with %s:\n", old_cell->name, old_port, log_signal(old_sig), log_signal(new_sig));
log_debug("patching %s %s which is %s with %s:\n", old_cell->name, old_port, log_signal(old_sig), log_signal(new_sig));
SrcCollector collector;
collector.collect_src(old_sig);
std::string src_str = AttrObject::strpool_attribute_to_str(collector.src);
old_cell->setPort(old_port, SigSpec());
mod->connect(old_sig, new_sig);
if (map)
map->add(old_sig, new_sig);
// Inefficient
// Record leaves (existing wires consumed as inputs by the new cells) so
// gc() stops at them. Detected via bit.wire->module being non-null:
// uncommitted wires belong to no module yet.
for (auto& cell : cells_) {
log_debug("cell %s\n", cell->name);
for (auto& [port_name, sig] : cell->connections()) {
log_debug("port %s\n", port_name);
auto dir = cell->port_dir(port_name);
if (dir == PD_INPUT || dir == PD_INOUT) {
for (auto bit : sig) {
log("bit %s\n", log_signal(bit));
if (bit.is_wire() && bit.wire->module) {
if (bit.is_wire() && bit.wire->module)
leaves.insert(bit.wire);
log_debug("leaf %s\n", bit.wire->name);
}
}
}
}
}
// Commit new cells/wires first so new_sig becomes a driven signal in the
// signorm index before we merge.
for (auto& cell: cells_) {
cell->set_src_attribute(src_str);
cell->fixup_parameters();
@ -178,12 +172,13 @@ void Patch::patch(Cell* old_cell, IdString old_port, SigSpec new_sig) {
for (auto& wire: wires_)
commit_wire(std::move(wire));
// Flush pending sigmap updates (from the mod->connect above) into the
// fanout index so gc() sees the updated fanout for cells whose outputs
// were the patched wires. Without this, downstream consumers like the
// $output_port / $public sentinels still appear in the OLD wire's fanout
// instead of the new representative.
mod->sigNormalize();
// Now drop old_cell's driver so old_sig is undriven, then merge it into
// new_sig. connect_incremental updates sigmap and re-normalizes fanout
// consumers in place — no full sigNormalize needed.
old_cell->setPort(old_port, SigSpec());
if (map)
map->add(old_sig, new_sig);
mod->connect_incremental(old_sig, new_sig);
gc(old_cell);
cells_.clear();