From 6a26b2db55ee0aef6824c6a5f23a0ef7d9c23d8e Mon Sep 17 00:00:00 2001 From: Alain Dargelas Date: Wed, 26 Feb 2025 16:17:09 -0800 Subject: [PATCH 01/32] WIP --- passes/silimate/annotate_cell_fanout.cc | 300 +++++++++++++++++------- 1 file changed, 213 insertions(+), 87 deletions(-) diff --git a/passes/silimate/annotate_cell_fanout.cc b/passes/silimate/annotate_cell_fanout.cc index 71a0649a7..4a1ddc98f 100644 --- a/passes/silimate/annotate_cell_fanout.cc +++ b/passes/silimate/annotate_cell_fanout.cc @@ -6,28 +6,25 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN // Signal cell driver(s), precompute a cell output signal to a cell map -void sigCellDrivers(RTLIL::Design *design, dict> &sig2CellsInFanout, +void sigCellDrivers(RTLIL::Module *module, SigMap &sigmap, dict> &sig2CellsInFanout, dict> &sig2CellsInFanin) { - for (auto module : design->selected_modules()) { - SigMap sigmap(module); - for (auto cell : module->selected_cells()) { - for (auto &conn : cell->connections()) { - IdString portName = conn.first; - RTLIL::SigSpec actual = conn.second; - if (cell->output(portName)) { - sig2CellsInFanin[actual].insert(cell); - for (int i = 0; i < actual.size(); i++) { - SigSpec bit_sig = actual.extract(i, 1); - sig2CellsInFanin[sigmap(bit_sig)].insert(cell); - } - } else { - sig2CellsInFanout[sigmap(actual)].insert(cell); - for (int i = 0; i < actual.size(); i++) { - SigSpec bit_sig = actual.extract(i, 1); - if (!bit_sig.is_fully_const()) { - sig2CellsInFanout[sigmap(bit_sig)].insert(cell); - } + for (auto cell : module->selected_cells()) { + for (auto &conn : cell->connections()) { + IdString portName = conn.first; + RTLIL::SigSpec actual = conn.second; + if (cell->output(portName)) { + sig2CellsInFanin[sigmap(actual)].insert(cell); + for (int i = 0; i < actual.size(); i++) { + SigSpec bit_sig = actual.extract(i, 1); + sig2CellsInFanin[sigmap(bit_sig)].insert(cell); + } + } else { + sig2CellsInFanout[sigmap(actual)].insert(cell); + for (int i = 0; i < actual.size(); i++) { + SigSpec bit_sig = actual.extract(i, 1); + if (!bit_sig.is_fully_const()) { + sig2CellsInFanout[sigmap(bit_sig)].insert(cell); } } } @@ -36,14 +33,13 @@ void sigCellDrivers(RTLIL::Design *design, dict } // Assign statements fanin, fanout, traces the lhs2rhs and rhs2lhs sigspecs and precompute maps -void lhs2rhs_rhs2lhs(RTLIL::Design *design, dict> &rhsSig2LhsSig, +void lhs2rhs_rhs2lhs(RTLIL::Module *module, SigMap &sigmap, dict> &rhsSig2LhsSig, dict &lhsSig2rhsSig) { - for (auto module : design->selected_modules()) { - SigMap sigmap(module); - for (auto it = module->connections().begin(); it != module->connections().end(); ++it) { - RTLIL::SigSpec lhs = it->first; - RTLIL::SigSpec rhs = it->second; + for (auto it = module->connections().begin(); it != module->connections().end(); ++it) { + RTLIL::SigSpec lhs = it->first; + RTLIL::SigSpec rhs = it->second; + if (!lhs.is_chunk()) { std::vector lhsBits; for (int i = 0; i < lhs.size(); i++) { SigSpec bit_sig = lhs.extract(i, 1); @@ -52,96 +48,226 @@ void lhs2rhs_rhs2lhs(RTLIL::Design *design, dict rhsBits; for (int i = 0; i < rhs.size(); i++) { SigSpec bit_sig = rhs.extract(i, 1); - if (bit_sig.is_fully_const()) { - continue; - } rhsBits.push_back(bit_sig); } for (uint32_t i = 0; i < lhsBits.size(); i++) { if (i < rhsBits.size()) { rhsSig2LhsSig[sigmap(rhsBits[i])].insert(sigmap(lhsBits[i])); - lhsSig2rhsSig[sigmap(lhsBits[i])] = sigmap(rhsBits[i]); + lhsSig2rhsSig[lhsBits[i]] = sigmap(rhsBits[i]); + } + } + } else { + rhsSig2LhsSig[sigmap(rhs)].insert(sigmap(lhs)); + lhsSig2rhsSig[lhs] = sigmap(rhs); + } + } +} + +void fixfanout(RTLIL::Module *module, SigMap &sigmap, dict> &sig2CellsInFanout, RTLIL::Cell *cell, int fanout, + int limit) +{ + if (fanout <= limit) { + std::cout << "Nothing to do for: " << cell->name.c_str() << std::endl; + std::cout << "Fanout: " << fanout << std::endl; + return; // No need to insert buffers + } else { + std::cout << "Something to do for: " << cell->name.c_str() << std::endl; + std::cout << "Fanout: " << fanout << std::endl; + } + + int num_buffers = std::min((int)std::ceil(static_cast(fanout) / limit), limit); + int max_output_per_buffer = std::ceil((float)fanout / (float)num_buffers); + std::cout << "fanout: " << fanout << "\n"; + std::cout << "limit: " << limit << "\n"; + std::cout << "num_buffers: " << num_buffers << "\n"; + std::cout << "max_output_per_buffer: " << max_output_per_buffer << "\n"; + std::vector buffer_outputs; + std::vector buffers; + std::cout << "HERE1\n" << std::flush; + std::cout << "CELL: " << cell->name.c_str() << "\n" << std::flush; + RTLIL::SigSpec cellOutSig; + for (auto &conn : cell->connections()) { + IdString portName = conn.first; + RTLIL::SigSpec actual = conn.second; + if (cell->output(portName)) { + cellOutSig = sigmap(actual); + break; + } + } + std::cout << "HERE2\n" << std::flush; + for (int i = 0; i < num_buffers; ++i) { + RTLIL::Cell *buffer = module->addCell(NEW_ID2_SUFFIX("fbuf"), ID($buf)); // Assuming BUF is defined + RTLIL::SigSpec buffer_output = module->addWire(NEW_ID2_SUFFIX("fbuf")); + buffer->setPort(ID(A), cellOutSig); + buffer->setPort(ID(Y), sigmap(buffer_output)); + buffer_outputs.push_back(buffer_output); + buffers.push_back(buffer); + } + std::cout << "Lookup\n" << std::flush; + std::set cells = sig2CellsInFanout[cellOutSig]; + std::cout << "Lookup OK\n" << std::flush; + int indexCurrentBuffer = 0; + int indexFanout = 0; + std::map bufferActualFanout; + for (Cell *c : cells) { + for (auto &conn : c->connections()) { + IdString portName = conn.first; + RTLIL::SigSpec actual = conn.second; + if (c->input(portName)) { + if (sigmap(actual) == cellOutSig) { + c->setPort(portName, buffer_outputs[indexCurrentBuffer]); + sig2CellsInFanout[sigmap(buffer_outputs[indexCurrentBuffer])].insert(c); + indexFanout++; + bufferActualFanout[buffers[indexCurrentBuffer]] = indexFanout; + if (indexFanout >= max_output_per_buffer) { + indexFanout = 0; + indexCurrentBuffer++; + } } } } } + + // Recursively fix the fanout of the newly created buffers + for (std::map::iterator itr = bufferActualFanout.begin(); itr != bufferActualFanout.end(); itr++) { + if (itr->second == 1) { + std::cout << "Buffer of 1" << std::endl; + for (Cell *c : cells) { + for (auto &conn : c->connections()) { + IdString portName = conn.first; + RTLIL::SigSpec actual = conn.second; + if (c->input(portName)) { + if (sigmap(buffer_outputs[indexCurrentBuffer]) == sigmap(actual)) { + c->setPort(portName, cellOutSig); + std::cout << "Remove buffer of 1" << std::endl; + module->remove(buffers[indexCurrentBuffer]); + //module->remove({buffer_outputs[indexCurrentBuffer].as_wire()}); + break; + } + } + } + } + } else { + fixfanout(module, sigmap, sig2CellsInFanout, itr->first, itr->second, limit); + } + } +} + +void calculateFanout(RTLIL::Module *module, SigMap &sigmap, dict>& sig2CellsInFanout, dict &cellFanout) +{ + // Precompute cell output sigspec to cell map + dict> sig2CellsInFanin; + sigCellDrivers(module, sigmap, sig2CellsInFanout, sig2CellsInFanin); + // Precompute lhs2rhs and rhs2lhs sigspec map + dict lhsSig2RhsSig; + dict> rhsSig2LhsSig; + lhs2rhs_rhs2lhs(module, sigmap, rhsSig2LhsSig, lhsSig2RhsSig); + + // Accumulate fanout from cell connections + dict sigFanout; + for (auto itrSig : sig2CellsInFanout) { + SigSpec sigspec = itrSig.first; + std::set &cells = itrSig.second; + sigFanout[sigspec] = cells.size(); + } + + // Accumulate fanout from assign stmts connections + for (auto itrSig : rhsSig2LhsSig) { + SigSpec sigspec = itrSig.first; + std::set &fanout = itrSig.second; + if (sigFanout.count(sigspec)) { + sigFanout[sigspec] += fanout.size(); + } else { + sigFanout[sigspec] = fanout.size(); + } + } + + // Collect max fanout from all the output bits of a cell + for (auto itrSig : sigFanout) { + SigSpec sigspec = itrSig.first; + int fanout = itrSig.second; + std::set &cells = sig2CellsInFanin[sigspec]; + for (Cell *cell : cells) { + if (cellFanout.count(cell)) { + cellFanout[cell] = std::max(fanout, cellFanout[cell]); + } else { + cellFanout[cell] = fanout; + } + } + } + + // Find cells with no fanout info (connected to output ports, or not connected) + std::set noFanoutInfo; + for (auto cell : module->selected_cells()) { + if (!cellFanout.count(cell)) { + noFanoutInfo.insert(cell); + } + } + + // Set those cells to fanout 1 + for (auto cell : noFanoutInfo) { + cellFanout[cell] = 1; + } } struct AnnotateCellFanout : public ScriptPass { AnnotateCellFanout() : ScriptPass("annotate_cell_fanout", "Annotate the cell fanout on the cell") {} void script() override {} - void execute(std::vector, RTLIL::Design *design) override + void execute(std::vector args, RTLIL::Design *design) override { + int limit = -1; if (design == nullptr) { - log_error("No design object"); + log_error("No design object\n"); return; } log("Running annotate_cell_fanout pass\n"); log_flush(); - // Precompute cell output sigspec to cell map - dict> sig2CellsInFanin; - dict> sig2CellsInFanout; - sigCellDrivers(design, sig2CellsInFanout, sig2CellsInFanin); - // Precompute lhs2rhs and rhs2lhs sigspec map - dict lhsSig2RhsSig; - dict> rhsSig2LhsSig; - lhs2rhs_rhs2lhs(design, rhsSig2LhsSig, lhsSig2RhsSig); - // Accumulate fanout from cell connections - dict sigFanout; - for (auto itrSig : sig2CellsInFanout) { - SigSpec sigspec = itrSig.first; - std::set &cells = itrSig.second; - sigFanout[sigspec] = cells.size(); - } - - // Accumulate fanout from assign stmts connections - for (auto itrSig : rhsSig2LhsSig) { - SigSpec sigspec = itrSig.first; - std::set &fanout = itrSig.second; - if (sigFanout.count(sigspec)) { - sigFanout[sigspec] += fanout.size(); - } else { - sigFanout[sigspec] = fanout.size(); + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-limit") { + limit = std::atoi(args[++argidx].c_str()); + continue; } + break; } - - // Collect max fanout from all the output bits of a cell - dict cellFanout; - for (auto itrSig : sigFanout) { - SigSpec sigspec = itrSig.first; - int fanout = itrSig.second; - std::set &cells = sig2CellsInFanin[sigspec]; - for (Cell *cell : cells) { - if (cellFanout.count(cell)) { - cellFanout[cell] = std::max(fanout, cellFanout[cell]); - } else { - cellFanout[cell] = fanout; - } - } + extra_args(args, argidx, design); + if (limit < 2) { + log_error("Fanout cannot be limited to less than 2\n"); + return; } - - // Find cells with no fanout info (connected to output ports, or not connected) - std::set noFanoutInfo; for (auto module : design->selected_modules()) { - for (auto cell : module->selected_cells()) { - if (!cellFanout.count(cell)) { - noFanoutInfo.insert(cell); + bool fixedFanout = false; + { + SigMap sigmap(module); + dict cellFanout; + dict> sig2CellsInFanout; + calculateFanout(module, sigmap, sig2CellsInFanout, cellFanout); + // Add attribute with fanout info to every cell + for (auto itrCell : cellFanout) { + Cell *cell = itrCell.first; + int fanout = itrCell.second; + if (limit > 0 && (fanout > limit)) { + fixfanout(module, sigmap, sig2CellsInFanout, cell, fanout, limit); + fixedFanout = true; + } else { + cell->set_string_attribute("$FANOUT", std::to_string(fanout)); + } + } + } + if (fixedFanout) { + SigMap sigmap(module); + dict cellFanout; + dict> sig2CellsInFanout; + calculateFanout(module, sigmap, sig2CellsInFanout, cellFanout); + for (auto itrCell : cellFanout) { + Cell *cell = itrCell.first; + int fanout = itrCell.second; + cell->set_string_attribute("$FANOUT", std::to_string(fanout)); } } - } - // Set those cells to fanout 1 - for (auto cell : noFanoutInfo) { - cellFanout[cell] = 1; - } - - // Add attribute with fanout info to every cell - for (auto itrCell : cellFanout) { - Cell *cell = itrCell.first; - int fanout = itrCell.second; - cell->set_string_attribute("$FANOUT", std::to_string(fanout)); } log("End annotate_cell_fanout pass\n"); From ecd2ac43027889e3561d65288b64f6262b68347a Mon Sep 17 00:00:00 2001 From: Alain Dargelas Date: Wed, 26 Feb 2025 16:51:39 -0800 Subject: [PATCH 02/32] fixes --- passes/silimate/annotate_cell_fanout.cc | 87 ++++++++++++++++--------- 1 file changed, 58 insertions(+), 29 deletions(-) diff --git a/passes/silimate/annotate_cell_fanout.cc b/passes/silimate/annotate_cell_fanout.cc index 4a1ddc98f..dd10c123b 100644 --- a/passes/silimate/annotate_cell_fanout.cc +++ b/passes/silimate/annotate_cell_fanout.cc @@ -68,11 +68,11 @@ void fixfanout(RTLIL::Module *module, SigMap &sigmap, dictname.c_str() << std::endl; - std::cout << "Fanout: " << fanout << std::endl; + std::cout << "Nothing to do for: " << cell->name.c_str() << std::endl; + std::cout << "Fanout: " << fanout << std::endl; return; // No need to insert buffers } else { - std::cout << "Something to do for: " << cell->name.c_str() << std::endl; + std::cout << "Something to do for: " << cell->name.c_str() << std::endl; std::cout << "Fanout: " << fanout << std::endl; } @@ -83,19 +83,17 @@ void fixfanout(RTLIL::Module *module, SigMap &sigmap, dict buffer_outputs; - std::vector buffers; - std::cout << "HERE1\n" << std::flush; + std::vector buffers; std::cout << "CELL: " << cell->name.c_str() << "\n" << std::flush; RTLIL::SigSpec cellOutSig; for (auto &conn : cell->connections()) { - IdString portName = conn.first; - RTLIL::SigSpec actual = conn.second; - if (cell->output(portName)) { - cellOutSig = sigmap(actual); - break; - } + IdString portName = conn.first; + RTLIL::SigSpec actual = conn.second; + if (cell->output(portName)) { + cellOutSig = sigmap(actual); + break; + } } - std::cout << "HERE2\n" << std::flush; for (int i = 0; i < num_buffers; ++i) { RTLIL::Cell *buffer = module->addCell(NEW_ID2_SUFFIX("fbuf"), ID($buf)); // Assuming BUF is defined RTLIL::SigSpec buffer_output = module->addWire(NEW_ID2_SUFFIX("fbuf")); @@ -104,35 +102,66 @@ void fixfanout(RTLIL::Module *module, SigMap &sigmap, dict cells = sig2CellsInFanout[cellOutSig]; - std::cout << "Lookup OK\n" << std::flush; int indexCurrentBuffer = 0; int indexFanout = 0; - std::map bufferActualFanout; + std::map bufferActualFanout; for (Cell *c : cells) { for (auto &conn : c->connections()) { IdString portName = conn.first; RTLIL::SigSpec actual = conn.second; if (c->input(portName)) { - if (sigmap(actual) == cellOutSig) { - c->setPort(portName, buffer_outputs[indexCurrentBuffer]); - sig2CellsInFanout[sigmap(buffer_outputs[indexCurrentBuffer])].insert(c); - indexFanout++; - bufferActualFanout[buffers[indexCurrentBuffer]] = indexFanout; - if (indexFanout >= max_output_per_buffer) { - indexFanout = 0; - indexCurrentBuffer++; + if (actual.is_chunk()) { + if (sigmap(actual) == cellOutSig) { + std::cout << "vector size: " << buffer_outputs.size() << std::endl; + std::cout << "index : " << indexCurrentBuffer << std::endl; + c->setPort(portName, buffer_outputs[indexCurrentBuffer]); + sig2CellsInFanout[sigmap(buffer_outputs[indexCurrentBuffer])].insert(c); + indexFanout++; + bufferActualFanout[buffers[indexCurrentBuffer]] = indexFanout; + if (indexFanout >= max_output_per_buffer) { + indexFanout = 0; + indexCurrentBuffer++; + } + break; + } + } else { + bool match = false; + for (SigChunk chunk : actual.chunks()) { + if (sigmap(SigSpec(chunk)) == cellOutSig) { + match = true; + break; + } + } + if (match) { + std::vector newChunks; + for (SigChunk chunk : actual.chunks()) { + if (sigmap(SigSpec(chunk)) == cellOutSig) { + newChunks.push_back(buffer_outputs[indexCurrentBuffer].as_wire()); + } else { + newChunks.push_back(chunk); + } + } + c->setPort(portName, newChunks); + sig2CellsInFanout[sigmap(buffer_outputs[indexCurrentBuffer])].insert(c); + indexFanout++; + bufferActualFanout[buffers[indexCurrentBuffer]] = indexFanout; + if (indexFanout >= max_output_per_buffer) { + indexFanout = 0; + indexCurrentBuffer++; + } + break; } } } } } - + // Recursively fix the fanout of the newly created buffers - for (std::map::iterator itr = bufferActualFanout.begin(); itr != bufferActualFanout.end(); itr++) { + for (std::map::iterator itr = bufferActualFanout.begin(); itr != bufferActualFanout.end(); itr++) { if (itr->second == 1) { - std::cout << "Buffer of 1" << std::endl; + // Remove newly created buffers with a fanout of 1 + std::cout << "Buffer of 1" << std::endl; for (Cell *c : cells) { for (auto &conn : c->connections()) { IdString portName = conn.first; @@ -142,7 +171,7 @@ void fixfanout(RTLIL::Module *module, SigMap &sigmap, dictsetPort(portName, cellOutSig); std::cout << "Remove buffer of 1" << std::endl; module->remove(buffers[indexCurrentBuffer]); - //module->remove({buffer_outputs[indexCurrentBuffer].as_wire()}); + // module->remove({buffer_outputs[indexCurrentBuffer].as_wire()}); break; } } @@ -154,7 +183,7 @@ void fixfanout(RTLIL::Module *module, SigMap &sigmap, dict>& sig2CellsInFanout, dict &cellFanout) +void calculateFanout(RTLIL::Module *module, SigMap &sigmap, dict> &sig2CellsInFanout, dict &cellFanout) { // Precompute cell output sigspec to cell map dict> sig2CellsInFanin; @@ -234,7 +263,7 @@ struct AnnotateCellFanout : public ScriptPass { break; } extra_args(args, argidx, design); - if (limit < 2) { + if ((limit != -1) && (limit < 2)) { log_error("Fanout cannot be limited to less than 2\n"); return; } From 25553a8ca92466bfa14b06d00e66480295fac559 Mon Sep 17 00:00:00 2001 From: Alain Dargelas Date: Fri, 28 Feb 2025 12:07:58 -0800 Subject: [PATCH 03/32] splitnets --- passes/silimate/annotate_cell_fanout.cc | 187 +++++++++++++++++++----- 1 file changed, 147 insertions(+), 40 deletions(-) diff --git a/passes/silimate/annotate_cell_fanout.cc b/passes/silimate/annotate_cell_fanout.cc index dd10c123b..df2c35504 100644 --- a/passes/silimate/annotate_cell_fanout.cc +++ b/passes/silimate/annotate_cell_fanout.cc @@ -64,7 +64,19 @@ void lhs2rhs_rhs2lhs(RTLIL::Module *module, SigMap &sigmap, dict> &sig2CellsInFanout, RTLIL::Cell *cell, int fanout, +RTLIL::Wire* getParentWire(const RTLIL::SigSpec& sigspec) { + if (sigspec.empty()) { + return nullptr; // Empty SigSpec, no parent wire + } + + // Get the first SigBit + const RTLIL::SigBit& first_bit = sigspec[0]; + + // Return the parent wire + return first_bit.wire; +} + +void fixfanout(RTLIL::Design* design, RTLIL::Module *module, SigMap &sigmap, dict> &sig2CellsInFanout, RTLIL::Cell *cell, int fanout, int limit) { if (fanout <= limit) { @@ -82,9 +94,9 @@ void fixfanout(RTLIL::Module *module, SigMap &sigmap, dict buffer_outputs; - std::vector buffers; std::cout << "CELL: " << cell->name.c_str() << "\n" << std::flush; + + // Get cell output RTLIL::SigSpec cellOutSig; for (auto &conn : cell->connections()) { IdString portName = conn.first; @@ -94,61 +106,102 @@ void fixfanout(RTLIL::Module *module, SigMap &sigmap, dict>> buffer_outputs; + std::vector> buffers; for (int i = 0; i < num_buffers; ++i) { - RTLIL::Cell *buffer = module->addCell(NEW_ID2_SUFFIX("fbuf"), ID($buf)); // Assuming BUF is defined - RTLIL::SigSpec buffer_output = module->addWire(NEW_ID2_SUFFIX("fbuf")); - buffer->setPort(ID(A), cellOutSig); - buffer->setPort(ID(Y), sigmap(buffer_output)); - buffer_outputs.push_back(buffer_output); - buffers.push_back(buffer); + std::vector> buffer_chunk_outputs; + std::vector buffer_chunks; + for (SigChunk chunk : cellOutSig.chunks()) { + RTLIL::Cell *buffer = module->addCell(NEW_ID2_SUFFIX("fbuf"), ID($pos)); + RTLIL::SigSpec buffer_output = module->addWire(NEW_ID2_SUFFIX("fbuf"), chunk.size()); + buffer->setPort(ID(A), chunk); + buffer->setPort(ID(Y), sigmap(buffer_output)); + buffer->fixup_parameters(); + buffer_chunk_outputs.push_back(std::make_pair(chunk, buffer_output)); // Old - New + buffer_chunks.push_back(buffer); + } + buffer_outputs.push_back(buffer_chunk_outputs); + buffers.push_back(buffer_chunks); } + + // Cumulate all cells in the fanout of this cell std::set cells = sig2CellsInFanout[cellOutSig]; + for (int i = 0 ; i < cellOutSig.size(); i++) { + SigSpec bit_sig = cellOutSig.extract(i, 1); + for (Cell* c : sig2CellsInFanout[sigmap(bit_sig)]) { + cells.insert(c); + } + } + int indexCurrentBuffer = 0; int indexFanout = 0; std::map bufferActualFanout; for (Cell *c : cells) { + std::cout << " CELL in fanout: " << c->name.c_str() << "\n" << std::flush; for (auto &conn : c->connections()) { IdString portName = conn.first; RTLIL::SigSpec actual = conn.second; if (c->input(portName)) { if (actual.is_chunk()) { - if (sigmap(actual) == cellOutSig) { - std::cout << "vector size: " << buffer_outputs.size() << std::endl; - std::cout << "index : " << indexCurrentBuffer << std::endl; - c->setPort(portName, buffer_outputs[indexCurrentBuffer]); - sig2CellsInFanout[sigmap(buffer_outputs[indexCurrentBuffer])].insert(c); - indexFanout++; - bufferActualFanout[buffers[indexCurrentBuffer]] = indexFanout; - if (indexFanout >= max_output_per_buffer) { - indexFanout = 0; - indexCurrentBuffer++; + std::cout << " CHUNK, indexCurrentBuffer: " << indexCurrentBuffer << " buffer_outputs " << buffer_outputs.size() << std::endl; + for (std::pair& old_new : buffer_outputs[indexCurrentBuffer]) { + if (sigmap(old_new.first) == sigmap(actual)) { + std::cout << " MATCH" << std::endl; + c->setPort(portName, old_new.second); + sig2CellsInFanout[sigmap(old_new.second)].insert(c); + indexFanout++; + for (Cell* c : buffers[indexCurrentBuffer]) { + bufferActualFanout[c] = indexFanout; + } + break; } - break; + } + if (indexFanout >= max_output_per_buffer) { + indexFanout = 0; + if (buffer_outputs.size()-1 > indexCurrentBuffer) + indexCurrentBuffer++; } } else { + std::cout << "NOT CHUNK" << std::endl; bool match = false; - for (SigChunk chunk : actual.chunks()) { - if (sigmap(SigSpec(chunk)) == cellOutSig) { - match = true; - break; + for (SigChunk chunka : actual.chunks()) { + for (SigChunk chunks : cellOutSig.chunks()) { + if (sigmap(SigSpec(chunka)) == SigSpec(chunks)) { + match = true; + break; + } } + if (match) + break; } if (match) { + std::cout << "MATCH" << std::endl; std::vector newChunks; for (SigChunk chunk : actual.chunks()) { - if (sigmap(SigSpec(chunk)) == cellOutSig) { - newChunks.push_back(buffer_outputs[indexCurrentBuffer].as_wire()); - } else { + bool replaced = false; + for (std::pair& old_new : buffer_outputs[indexCurrentBuffer]) { + if (sigmap(old_new.first) == sigmap(SigSpec(chunk))) { + newChunks.push_back(old_new.second.as_chunk()); + sig2CellsInFanout[sigmap(old_new.second)].insert(c); + replaced = true; + break; + } + } + if (!replaced) { newChunks.push_back(chunk); } } c->setPort(portName, newChunks); - sig2CellsInFanout[sigmap(buffer_outputs[indexCurrentBuffer])].insert(c); indexFanout++; - bufferActualFanout[buffers[indexCurrentBuffer]] = indexFanout; + for (Cell *c : buffers[indexCurrentBuffer]) { + bufferActualFanout[c] = indexFanout; + } if (indexFanout >= max_output_per_buffer) { indexFanout = 0; - indexCurrentBuffer++; + if (buffer_outputs.size()-1 > indexCurrentBuffer) + indexCurrentBuffer++; } break; } @@ -160,25 +213,42 @@ void fixfanout(RTLIL::Module *module, SigMap &sigmap, dict::iterator itr = bufferActualFanout.begin(); itr != bufferActualFanout.end(); itr++) { if (itr->second == 1) { + std::cout << "Buffer with fanout 1: " << itr->first->name.c_str() << std::endl; + RTLIL::SigSpec bufferInSig = itr->first->getPort(ID::A); + RTLIL::SigSpec bufferOutSig = itr->first->getPort(ID::Y); + //std::cout << "bufferOutSig: " << bufferOutSig.as_wire()->name.c_str() + // << " bufferInSig: " << bufferInSig.as_wire()->name.c_str() << std::endl; // Remove newly created buffers with a fanout of 1 - std::cout << "Buffer of 1" << std::endl; for (Cell *c : cells) { + std::cout << "Cell in its fanout: " << c->name.c_str() << std::endl; for (auto &conn : c->connections()) { IdString portName = conn.first; RTLIL::SigSpec actual = conn.second; if (c->input(portName)) { - if (sigmap(buffer_outputs[indexCurrentBuffer]) == sigmap(actual)) { - c->setPort(portName, cellOutSig); - std::cout << "Remove buffer of 1" << std::endl; - module->remove(buffers[indexCurrentBuffer]); - // module->remove({buffer_outputs[indexCurrentBuffer].as_wire()}); - break; + if (actual.is_chunk()) { + if (bufferOutSig == sigmap(actual)) { + std::cout << "Replace1: " << getParentWire(bufferOutSig)->name.c_str() << " by " << getParentWire(bufferInSig)->name.c_str() << std::endl; + c->setPort(portName, bufferInSig); + } + } else { + std::vector newChunks; + for (SigChunk chunk : actual.chunks()) { + if (sigmap(SigSpec(chunk)) == sigmap(bufferOutSig)) { + std::cout << "Replace2: " << getParentWire(bufferOutSig)->name.c_str() << " by " << getParentWire(bufferInSig)->name.c_str() << std::endl; + newChunks.push_back(bufferInSig.as_chunk()); + } else { + newChunks.push_back(chunk); + } + } + c->setPort(portName, newChunks); } } } } + module->remove(itr->first); + module->remove({bufferOutSig.as_wire()}); } else { - fixfanout(module, sigmap, sig2CellsInFanout, itr->first, itr->second, limit); + fixfanout(design, module, sigmap, sig2CellsInFanout, itr->first, itr->second, limit); } } } @@ -270,23 +340,59 @@ struct AnnotateCellFanout : public ScriptPass { for (auto module : design->selected_modules()) { bool fixedFanout = false; { + // Split output nets of cells with high fanout SigMap sigmap(module); dict cellFanout; dict> sig2CellsInFanout; calculateFanout(module, sigmap, sig2CellsInFanout, cellFanout); - // Add attribute with fanout info to every cell + + std::vector cellsToFixFanout; for (auto itrCell : cellFanout) { Cell *cell = itrCell.first; int fanout = itrCell.second; if (limit > 0 && (fanout > limit)) { - fixfanout(module, sigmap, sig2CellsInFanout, cell, fanout, limit); + cellsToFixFanout.push_back(cell); + } + } + + std::string netsToSplit; + for (Cell* cell : cellsToFixFanout) { + RTLIL::SigSpec cellOutSig; + for (auto &conn : cell->connections()) { + IdString portName = conn.first; + RTLIL::SigSpec actual = conn.second; + if (cell->output(portName)) { + cellOutSig = sigmap(actual); + break; + } + } + netsToSplit += std::string(" w:") + getParentWire(cellOutSig)->name.c_str(); + } + std::string splitnets = std::string("splitnets ") + netsToSplit; + Pass::call(design, splitnets); + } + + { + // Fix high fanout + SigMap sigmap(module); + dict cellFanout; + dict> sig2CellsInFanout; + calculateFanout(module, sigmap, sig2CellsInFanout, cellFanout); + + for (auto itrCell : cellFanout) { + Cell *cell = itrCell.first; + int fanout = itrCell.second; + if (limit > 0 && (fanout > limit)) { + fixfanout(design, module, sigmap, sig2CellsInFanout, cell, fanout, limit); fixedFanout = true; } else { + // Add attribute with fanout info to every cell cell->set_string_attribute("$FANOUT", std::to_string(fanout)); } } } if (fixedFanout) { + // If Fanout got fixed, recalculate and annotate final fanout SigMap sigmap(module); dict cellFanout; dict> sig2CellsInFanout; @@ -294,6 +400,7 @@ struct AnnotateCellFanout : public ScriptPass { for (auto itrCell : cellFanout) { Cell *cell = itrCell.first; int fanout = itrCell.second; + // Add attribute with fanout info to every cell cell->set_string_attribute("$FANOUT", std::to_string(fanout)); } } From 274198915866e0b82a079af9ba2c675cc4da91af Mon Sep 17 00:00:00 2001 From: Alain Dargelas Date: Fri, 28 Feb 2025 14:50:17 -0800 Subject: [PATCH 04/32] honor limits --- passes/silimate/annotate_cell_fanout.cc | 65 +++++++++++++++++-------- 1 file changed, 44 insertions(+), 21 deletions(-) diff --git a/passes/silimate/annotate_cell_fanout.cc b/passes/silimate/annotate_cell_fanout.cc index df2c35504..91f000c53 100644 --- a/passes/silimate/annotate_cell_fanout.cc +++ b/passes/silimate/annotate_cell_fanout.cc @@ -87,7 +87,7 @@ void fixfanout(RTLIL::Design* design, RTLIL::Module *module, SigMap &sigmap, dic std::cout << "Something to do for: " << cell->name.c_str() << std::endl; std::cout << "Fanout: " << fanout << std::endl; } - + sigmap.set(module); int num_buffers = std::min((int)std::ceil(static_cast(fanout) / limit), limit); int max_output_per_buffer = std::ceil((float)fanout / (float)num_buffers); std::cout << "fanout: " << fanout << "\n"; @@ -135,6 +135,7 @@ void fixfanout(RTLIL::Design* design, RTLIL::Module *module, SigMap &sigmap, dic } } + // Fix input connections to cells in fanout of buffer to point to the inserted buffer int indexCurrentBuffer = 0; int indexFanout = 0; std::map bufferActualFanout; @@ -153,24 +154,33 @@ void fixfanout(RTLIL::Design* design, RTLIL::Module *module, SigMap &sigmap, dic sig2CellsInFanout[sigmap(old_new.second)].insert(c); indexFanout++; for (Cell* c : buffers[indexCurrentBuffer]) { - bufferActualFanout[c] = indexFanout; + if (bufferActualFanout.find(c) != bufferActualFanout.end()) { + bufferActualFanout[c] = std::max(indexFanout, bufferActualFanout[c]); + } else { + bufferActualFanout[c] = indexFanout; + } } break; } } if (indexFanout >= max_output_per_buffer) { - indexFanout = 0; - if (buffer_outputs.size()-1 > indexCurrentBuffer) + if (buffer_outputs.size()-1 > indexCurrentBuffer) { + indexFanout = 0; indexCurrentBuffer++; + } } } else { std::cout << "NOT CHUNK" << std::endl; bool match = false; - for (SigChunk chunka : actual.chunks()) { - for (SigChunk chunks : cellOutSig.chunks()) { - if (sigmap(SigSpec(chunka)) == SigSpec(chunks)) { - match = true; - break; + for (SigChunk chunk_a : actual.chunks()) { + if (sigmap(SigSpec(chunk_a)) == sigmap(SigSpec(cellOutSig))) { + match = true; + } else { + for (SigChunk chunk_c : cellOutSig.chunks()) { + if (sigmap(SigSpec(chunk_a)) == sigmap(SigSpec(chunk_c))) { + match = true; + break; + } } } if (match) @@ -179,13 +189,31 @@ void fixfanout(RTLIL::Design* design, RTLIL::Module *module, SigMap &sigmap, dic if (match) { std::cout << "MATCH" << std::endl; std::vector newChunks; + bool missed = true; for (SigChunk chunk : actual.chunks()) { bool replaced = false; for (std::pair& old_new : buffer_outputs[indexCurrentBuffer]) { if (sigmap(old_new.first) == sigmap(SigSpec(chunk))) { + missed = false; newChunks.push_back(old_new.second.as_chunk()); sig2CellsInFanout[sigmap(old_new.second)].insert(c); replaced = true; + indexFanout++; + + for (Cell *c : buffers[indexCurrentBuffer]) { + if (bufferActualFanout.find(c) != bufferActualFanout.end()) { + bufferActualFanout[c] = std::max(indexFanout, bufferActualFanout[c]); + } else { + bufferActualFanout[c] = indexFanout; + } + } + if (indexFanout >= max_output_per_buffer) { + if (buffer_outputs.size() - 1 > indexCurrentBuffer) { + indexFanout = 0; + indexCurrentBuffer++; + } + } + break; } } @@ -193,16 +221,10 @@ void fixfanout(RTLIL::Design* design, RTLIL::Module *module, SigMap &sigmap, dic newChunks.push_back(chunk); } } + if (missed) { + exit (1); + } c->setPort(portName, newChunks); - indexFanout++; - for (Cell *c : buffers[indexCurrentBuffer]) { - bufferActualFanout[c] = indexFanout; - } - if (indexFanout >= max_output_per_buffer) { - indexFanout = 0; - if (buffer_outputs.size()-1 > indexCurrentBuffer) - indexCurrentBuffer++; - } break; } } @@ -245,8 +267,8 @@ void fixfanout(RTLIL::Design* design, RTLIL::Module *module, SigMap &sigmap, dic } } } - module->remove(itr->first); - module->remove({bufferOutSig.as_wire()}); + //module->remove(itr->first); + //module->remove({bufferOutSig.as_wire()}); } else { fixfanout(design, module, sigmap, sig2CellsInFanout, itr->first, itr->second, limit); } @@ -368,7 +390,7 @@ struct AnnotateCellFanout : public ScriptPass { } netsToSplit += std::string(" w:") + getParentWire(cellOutSig)->name.c_str(); } - std::string splitnets = std::string("splitnets ") + netsToSplit; + std::string splitnets = std::string("splitnets -ports ") + netsToSplit; Pass::call(design, splitnets); } @@ -391,6 +413,7 @@ struct AnnotateCellFanout : public ScriptPass { } } } + if (fixedFanout) { // If Fanout got fixed, recalculate and annotate final fanout SigMap sigmap(module); From 0d854120a781758beb40f7d9a847429be1e3b37a Mon Sep 17 00:00:00 2001 From: Alain Dargelas Date: Fri, 28 Feb 2025 20:38:36 -0800 Subject: [PATCH 05/32] fanout per buffer --- passes/silimate/annotate_cell_fanout.cc | 123 ++++++++++++------------ 1 file changed, 60 insertions(+), 63 deletions(-) diff --git a/passes/silimate/annotate_cell_fanout.cc b/passes/silimate/annotate_cell_fanout.cc index 91f000c53..7f6e91e09 100644 --- a/passes/silimate/annotate_cell_fanout.cc +++ b/passes/silimate/annotate_cell_fanout.cc @@ -108,22 +108,22 @@ void fixfanout(RTLIL::Design* design, RTLIL::Module *module, SigMap &sigmap, dic } // Create buffers and new wires - std::vector>> buffer_outputs; - std::vector> buffers; - for (int i = 0; i < num_buffers; ++i) { - std::vector> buffer_chunk_outputs; - std::vector buffer_chunks; - for (SigChunk chunk : cellOutSig.chunks()) { - RTLIL::Cell *buffer = module->addCell(NEW_ID2_SUFFIX("fbuf"), ID($pos)); + std::map bufferActualFanout; + std::map>> buffer_outputs; + std::map bufferIndexes; + for (SigChunk chunk : cellOutSig.chunks()) { + std::vector> buffer_chunk_outputs; + for (int i = 0; i < num_buffers; ++i) { + RTLIL::Cell *buffer = module->addCell(NEW_ID2_SUFFIX("fbuf"), ID($pos)); + bufferActualFanout[buffer] = 0; RTLIL::SigSpec buffer_output = module->addWire(NEW_ID2_SUFFIX("fbuf"), chunk.size()); buffer->setPort(ID(A), chunk); buffer->setPort(ID(Y), sigmap(buffer_output)); buffer->fixup_parameters(); - buffer_chunk_outputs.push_back(std::make_pair(chunk, buffer_output)); // Old - New - buffer_chunks.push_back(buffer); + buffer_chunk_outputs.push_back(std::make_tuple(buffer_output, buffer)); // Old - New + bufferIndexes[chunk] = 0; } - buffer_outputs.push_back(buffer_chunk_outputs); - buffers.push_back(buffer_chunks); + buffer_outputs.emplace(sigmap(SigSpec(chunk)), buffer_chunk_outputs); } // Cumulate all cells in the fanout of this cell @@ -136,41 +136,37 @@ void fixfanout(RTLIL::Design* design, RTLIL::Module *module, SigMap &sigmap, dic } // Fix input connections to cells in fanout of buffer to point to the inserted buffer - int indexCurrentBuffer = 0; - int indexFanout = 0; - std::map bufferActualFanout; for (Cell *c : cells) { - std::cout << " CELL in fanout: " << c->name.c_str() << "\n" << std::flush; + std::cout << "\n CELL in fanout: " << c->name.c_str() << "\n" << std::flush; for (auto &conn : c->connections()) { IdString portName = conn.first; - RTLIL::SigSpec actual = conn.second; + RTLIL::SigSpec actual = sigmap(conn.second); if (c->input(portName)) { if (actual.is_chunk()) { - std::cout << " CHUNK, indexCurrentBuffer: " << indexCurrentBuffer << " buffer_outputs " << buffer_outputs.size() << std::endl; - for (std::pair& old_new : buffer_outputs[indexCurrentBuffer]) { - if (sigmap(old_new.first) == sigmap(actual)) { - std::cout << " MATCH" << std::endl; - c->setPort(portName, old_new.second); - sig2CellsInFanout[sigmap(old_new.second)].insert(c); - indexFanout++; - for (Cell* c : buffers[indexCurrentBuffer]) { - if (bufferActualFanout.find(c) != bufferActualFanout.end()) { - bufferActualFanout[c] = std::max(indexFanout, bufferActualFanout[c]); - } else { - bufferActualFanout[c] = indexFanout; - } + if (buffer_outputs.find(actual) != buffer_outputs.end()) { + std::cout << " CHUNK, indexCurrentBuffer: " << bufferIndexes[actual] << " buffer_outputs " << buffer_outputs[actual].size() << std::endl; + std::vector>& buf_info_vec = buffer_outputs[actual]; + int bufferIndex = bufferIndexes[actual]; + std::cout << " BUFFER INDEX: " << bufferIndex << std::endl; + std::cout << " VEC SIZE: " << buf_info_vec.size() << std::endl; + std::tuple& buf_info = buf_info_vec[bufferIndex]; + SigSpec newSig = std::get<0>(buf_info); + Cell* newBuf = std::get<1>(buf_info); + std::cout << " MATCH" << std::endl; + c->setPort(portName, newSig); + sig2CellsInFanout[newSig].insert(c); + bufferActualFanout[newBuf]++; + std::cout << " b: " << newBuf->name.c_str() << " fanout: " << bufferActualFanout[newBuf] << std::endl; + if (bufferActualFanout[newBuf] >= max_output_per_buffer) { + std::cout << " REACHED MAX" << std::endl; + if (buffer_outputs[actual].size() - 1 > bufferIndex) { + bufferIndexes[actual]++; + std::cout << " NEXT BUFFER" << std::endl; } - break; - } - } - if (indexFanout >= max_output_per_buffer) { - if (buffer_outputs.size()-1 > indexCurrentBuffer) { - indexFanout = 0; - indexCurrentBuffer++; } } } else { - std::cout << "NOT CHUNK" << std::endl; + std::cout << " NOT CHUNK" << std::endl; bool match = false; for (SigChunk chunk_a : actual.chunks()) { if (sigmap(SigSpec(chunk_a)) == sigmap(SigSpec(cellOutSig))) { @@ -187,34 +183,33 @@ void fixfanout(RTLIL::Design* design, RTLIL::Module *module, SigMap &sigmap, dic break; } if (match) { - std::cout << "MATCH" << std::endl; + + std::cout << " MATCH" << std::endl; std::vector newChunks; bool missed = true; for (SigChunk chunk : actual.chunks()) { bool replaced = false; - for (std::pair& old_new : buffer_outputs[indexCurrentBuffer]) { - if (sigmap(old_new.first) == sigmap(SigSpec(chunk))) { - missed = false; - newChunks.push_back(old_new.second.as_chunk()); - sig2CellsInFanout[sigmap(old_new.second)].insert(c); - replaced = true; - indexFanout++; - - for (Cell *c : buffers[indexCurrentBuffer]) { - if (bufferActualFanout.find(c) != bufferActualFanout.end()) { - bufferActualFanout[c] = std::max(indexFanout, bufferActualFanout[c]); - } else { - bufferActualFanout[c] = indexFanout; - } + if (buffer_outputs.find(chunk) != buffer_outputs.end()) { + std::cout << " CHUNK, indexCurrentBuffer: " << bufferIndexes[chunk] << " buffer_outputs " << buffer_outputs[chunk].size() << std::endl; + std::vector>& buf_info_vec = buffer_outputs[chunk]; + int bufferIndex = bufferIndexes[chunk]; + std::cout << " BUFFER INDEX: " << bufferIndex << std::endl; + std::cout << " VEC SIZE: " << buf_info_vec.size() << std::endl; + std::tuple& buf_info = buf_info_vec[bufferIndex]; + SigSpec newSig = std::get<0>(buf_info); + Cell* newBuf = std::get<1>(buf_info); + missed = false; + newChunks.push_back(newSig.as_chunk()); + sig2CellsInFanout[newSig].insert(c); + replaced = true; + bufferActualFanout[newBuf]++; + std::cout << " b: " << newBuf->name.c_str() << " fanout: " << bufferActualFanout[newBuf] << std::endl; + if (bufferActualFanout[newBuf] >= max_output_per_buffer) { + std::cout << " REACHED MAX" << std::endl; + if (buffer_outputs[chunk].size() - 1 > bufferIndex) { + bufferIndexes[chunk]++; + std::cout << " NEXT BUFFER" << std::endl; } - if (indexFanout >= max_output_per_buffer) { - if (buffer_outputs.size() - 1 > indexCurrentBuffer) { - indexFanout = 0; - indexCurrentBuffer++; - } - } - - break; } } if (!replaced) { @@ -226,6 +221,7 @@ void fixfanout(RTLIL::Design* design, RTLIL::Module *module, SigMap &sigmap, dic } c->setPort(portName, newChunks); break; + } } } @@ -267,8 +263,8 @@ void fixfanout(RTLIL::Design* design, RTLIL::Module *module, SigMap &sigmap, dic } } } - //module->remove(itr->first); - //module->remove({bufferOutSig.as_wire()}); + module->remove(itr->first); + module->remove({bufferOutSig.as_wire()}); } else { fixfanout(design, module, sigmap, sig2CellsInFanout, itr->first, itr->second, limit); } @@ -389,8 +385,9 @@ struct AnnotateCellFanout : public ScriptPass { } } netsToSplit += std::string(" w:") + getParentWire(cellOutSig)->name.c_str(); + netsToSplit += std::string(" x:") + getParentWire(cellOutSig)->name.c_str(); } - std::string splitnets = std::string("splitnets -ports ") + netsToSplit; + std::string splitnets = std::string("splitnets ") + netsToSplit; Pass::call(design, splitnets); } From aa4c5e9ee5061366db51da61dfcf330d4929f7f4 Mon Sep 17 00:00:00 2001 From: Alain Dargelas Date: Fri, 28 Feb 2025 21:54:28 -0800 Subject: [PATCH 06/32] working splinets --- passes/silimate/annotate_cell_fanout.cc | 29 ++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/passes/silimate/annotate_cell_fanout.cc b/passes/silimate/annotate_cell_fanout.cc index 7f6e91e09..c03a4d084 100644 --- a/passes/silimate/annotate_cell_fanout.cc +++ b/passes/silimate/annotate_cell_fanout.cc @@ -328,6 +328,15 @@ void calculateFanout(RTLIL::Module *module, SigMap &sigmap, dict netsToSplitS; std::string netsToSplit; + std::string portsToSplit; for (Cell* cell : cellsToFixFanout) { RTLIL::SigSpec cellOutSig; for (auto &conn : cell->connections()) { @@ -384,11 +394,20 @@ struct AnnotateCellFanout : public ScriptPass { break; } } - netsToSplit += std::string(" w:") + getParentWire(cellOutSig)->name.c_str(); - netsToSplit += std::string(" x:") + getParentWire(cellOutSig)->name.c_str(); + std::string parent = getParentWire(cellOutSig)->name.c_str(); + parent = substringUntil(parent, '['); + if (netsToSplitS.find(parent) == netsToSplitS.end()) { + netsToSplit += std::string(" w:") + parent; // Wire + portsToSplit += std::string(" o:") + parent; // Port + netsToSplitS.insert(parent); + std::string splitnets = std::string("splitnets ") + netsToSplit; + Pass::call(design, splitnets); + splitnets = std::string("splitnets -ports_only ") + portsToSplit; + Pass::call(design, splitnets); + netsToSplit = ""; + portsToSplit = ""; + } } - std::string splitnets = std::string("splitnets ") + netsToSplit; - Pass::call(design, splitnets); } { From 21f026cb054746a63449452c0919c7c73092f255 Mon Sep 17 00:00:00 2001 From: Alain Dargelas Date: Fri, 28 Feb 2025 22:08:19 -0800 Subject: [PATCH 07/32] formal --- passes/silimate/annotate_cell_fanout.cc | 122 ++++++++++++++---------- 1 file changed, 69 insertions(+), 53 deletions(-) diff --git a/passes/silimate/annotate_cell_fanout.cc b/passes/silimate/annotate_cell_fanout.cc index c03a4d084..6aed247cf 100644 --- a/passes/silimate/annotate_cell_fanout.cc +++ b/passes/silimate/annotate_cell_fanout.cc @@ -64,20 +64,21 @@ void lhs2rhs_rhs2lhs(RTLIL::Module *module, SigMap &sigmap, dict> &sig2CellsInFanout, RTLIL::Cell *cell, int fanout, - int limit) +void fixfanout(RTLIL::Design *design, RTLIL::Module *module, SigMap &sigmap, dict> &sig2CellsInFanout, + RTLIL::Cell *cell, int fanout, int limit) { if (fanout <= limit) { std::cout << "Nothing to do for: " << cell->name.c_str() << std::endl; @@ -109,28 +110,28 @@ void fixfanout(RTLIL::Design* design, RTLIL::Module *module, SigMap &sigmap, dic // Create buffers and new wires std::map bufferActualFanout; - std::map>> buffer_outputs; + std::map>> buffer_outputs; std::map bufferIndexes; for (SigChunk chunk : cellOutSig.chunks()) { - std::vector> buffer_chunk_outputs; + std::vector> buffer_chunk_outputs; for (int i = 0; i < num_buffers; ++i) { RTLIL::Cell *buffer = module->addCell(NEW_ID2_SUFFIX("fbuf"), ID($pos)); - bufferActualFanout[buffer] = 0; + bufferActualFanout[buffer] = 0; RTLIL::SigSpec buffer_output = module->addWire(NEW_ID2_SUFFIX("fbuf"), chunk.size()); buffer->setPort(ID(A), chunk); buffer->setPort(ID(Y), sigmap(buffer_output)); buffer->fixup_parameters(); buffer_chunk_outputs.push_back(std::make_tuple(buffer_output, buffer)); // Old - New - bufferIndexes[chunk] = 0; + bufferIndexes[chunk] = 0; } buffer_outputs.emplace(sigmap(SigSpec(chunk)), buffer_chunk_outputs); } // Cumulate all cells in the fanout of this cell std::set cells = sig2CellsInFanout[cellOutSig]; - for (int i = 0 ; i < cellOutSig.size(); i++) { + for (int i = 0; i < cellOutSig.size(); i++) { SigSpec bit_sig = cellOutSig.extract(i, 1); - for (Cell* c : sig2CellsInFanout[sigmap(bit_sig)]) { + for (Cell *c : sig2CellsInFanout[sigmap(bit_sig)]) { cells.insert(c); } } @@ -144,19 +145,21 @@ void fixfanout(RTLIL::Design* design, RTLIL::Module *module, SigMap &sigmap, dic if (c->input(portName)) { if (actual.is_chunk()) { if (buffer_outputs.find(actual) != buffer_outputs.end()) { - std::cout << " CHUNK, indexCurrentBuffer: " << bufferIndexes[actual] << " buffer_outputs " << buffer_outputs[actual].size() << std::endl; - std::vector>& buf_info_vec = buffer_outputs[actual]; + std::cout << " CHUNK, indexCurrentBuffer: " << bufferIndexes[actual] << " buffer_outputs " + << buffer_outputs[actual].size() << std::endl; + std::vector> &buf_info_vec = buffer_outputs[actual]; int bufferIndex = bufferIndexes[actual]; std::cout << " BUFFER INDEX: " << bufferIndex << std::endl; std::cout << " VEC SIZE: " << buf_info_vec.size() << std::endl; - std::tuple& buf_info = buf_info_vec[bufferIndex]; + std::tuple &buf_info = buf_info_vec[bufferIndex]; SigSpec newSig = std::get<0>(buf_info); - Cell* newBuf = std::get<1>(buf_info); + Cell *newBuf = std::get<1>(buf_info); std::cout << " MATCH" << std::endl; c->setPort(portName, newSig); sig2CellsInFanout[newSig].insert(c); bufferActualFanout[newBuf]++; - std::cout << " b: " << newBuf->name.c_str() << " fanout: " << bufferActualFanout[newBuf] << std::endl; + std::cout << " b: " << newBuf->name.c_str() << " fanout: " << bufferActualFanout[newBuf] + << std::endl; if (bufferActualFanout[newBuf] >= max_output_per_buffer) { std::cout << " REACHED MAX" << std::endl; if (buffer_outputs[actual].size() - 1 > bufferIndex) { @@ -173,7 +176,7 @@ void fixfanout(RTLIL::Design* design, RTLIL::Module *module, SigMap &sigmap, dic match = true; } else { for (SigChunk chunk_c : cellOutSig.chunks()) { - if (sigmap(SigSpec(chunk_a)) == sigmap(SigSpec(chunk_c))) { + if (sigmap(SigSpec(chunk_a)) == sigmap(SigSpec(chunk_c))) { match = true; break; } @@ -183,27 +186,29 @@ void fixfanout(RTLIL::Design* design, RTLIL::Module *module, SigMap &sigmap, dic break; } if (match) { - + std::cout << " MATCH" << std::endl; std::vector newChunks; bool missed = true; for (SigChunk chunk : actual.chunks()) { bool replaced = false; if (buffer_outputs.find(chunk) != buffer_outputs.end()) { - std::cout << " CHUNK, indexCurrentBuffer: " << bufferIndexes[chunk] << " buffer_outputs " << buffer_outputs[chunk].size() << std::endl; - std::vector>& buf_info_vec = buffer_outputs[chunk]; - int bufferIndex = bufferIndexes[chunk]; - std::cout << " BUFFER INDEX: " << bufferIndex << std::endl; - std::cout << " VEC SIZE: " << buf_info_vec.size() << std::endl; - std::tuple& buf_info = buf_info_vec[bufferIndex]; - SigSpec newSig = std::get<0>(buf_info); - Cell* newBuf = std::get<1>(buf_info); + std::cout << " CHUNK, indexCurrentBuffer: " << bufferIndexes[chunk] + << " buffer_outputs " << buffer_outputs[chunk].size() << std::endl; + std::vector> &buf_info_vec = buffer_outputs[chunk]; + int bufferIndex = bufferIndexes[chunk]; + std::cout << " BUFFER INDEX: " << bufferIndex << std::endl; + std::cout << " VEC SIZE: " << buf_info_vec.size() << std::endl; + std::tuple &buf_info = buf_info_vec[bufferIndex]; + SigSpec newSig = std::get<0>(buf_info); + Cell *newBuf = std::get<1>(buf_info); missed = false; newChunks.push_back(newSig.as_chunk()); sig2CellsInFanout[newSig].insert(c); replaced = true; bufferActualFanout[newBuf]++; - std::cout << " b: " << newBuf->name.c_str() << " fanout: " << bufferActualFanout[newBuf] << std::endl; + std::cout << " b: " << newBuf->name.c_str() + << " fanout: " << bufferActualFanout[newBuf] << std::endl; if (bufferActualFanout[newBuf] >= max_output_per_buffer) { std::cout << " REACHED MAX" << std::endl; if (buffer_outputs[chunk].size() - 1 > bufferIndex) { @@ -217,11 +222,10 @@ void fixfanout(RTLIL::Design* design, RTLIL::Module *module, SigMap &sigmap, dic } } if (missed) { - exit (1); + exit(1); } c->setPort(portName, newChunks); break; - } } } @@ -234,9 +238,9 @@ void fixfanout(RTLIL::Design* design, RTLIL::Module *module, SigMap &sigmap, dic std::cout << "Buffer with fanout 1: " << itr->first->name.c_str() << std::endl; RTLIL::SigSpec bufferInSig = itr->first->getPort(ID::A); RTLIL::SigSpec bufferOutSig = itr->first->getPort(ID::Y); - //std::cout << "bufferOutSig: " << bufferOutSig.as_wire()->name.c_str() + // std::cout << "bufferOutSig: " << bufferOutSig.as_wire()->name.c_str() // << " bufferInSig: " << bufferInSig.as_wire()->name.c_str() << std::endl; - // Remove newly created buffers with a fanout of 1 + // Remove newly created buffers with a fanout of 1 for (Cell *c : cells) { std::cout << "Cell in its fanout: " << c->name.c_str() << std::endl; for (auto &conn : c->connections()) { @@ -245,14 +249,16 @@ void fixfanout(RTLIL::Design* design, RTLIL::Module *module, SigMap &sigmap, dic if (c->input(portName)) { if (actual.is_chunk()) { if (bufferOutSig == sigmap(actual)) { - std::cout << "Replace1: " << getParentWire(bufferOutSig)->name.c_str() << " by " << getParentWire(bufferInSig)->name.c_str() << std::endl; + std::cout << "Replace1: " << getParentWire(bufferOutSig)->name.c_str() << " by " + << getParentWire(bufferInSig)->name.c_str() << std::endl; c->setPort(portName, bufferInSig); } } else { std::vector newChunks; for (SigChunk chunk : actual.chunks()) { if (sigmap(SigSpec(chunk)) == sigmap(bufferOutSig)) { - std::cout << "Replace2: " << getParentWire(bufferOutSig)->name.c_str() << " by " << getParentWire(bufferInSig)->name.c_str() << std::endl; + std::cout << "Replace2: " << getParentWire(bufferOutSig)->name.c_str() + << " by " << getParentWire(bufferInSig)->name.c_str() << std::endl; newChunks.push_back(bufferInSig.as_chunk()); } else { newChunks.push_back(chunk); @@ -328,13 +334,14 @@ void calculateFanout(RTLIL::Module *module, SigMap &sigmap, dict args, RTLIL::Design *design) override { int limit = -1; + bool formalFriendly = false; if (design == nullptr) { log_error("No design object\n"); return; @@ -357,6 +365,10 @@ struct AnnotateCellFanout : public ScriptPass { limit = std::atoi(args[++argidx].c_str()); continue; } + if (args[argidx] == "-formal") { + formalFriendly = true; + continue; + } break; } extra_args(args, argidx, design); @@ -367,13 +379,13 @@ struct AnnotateCellFanout : public ScriptPass { for (auto module : design->selected_modules()) { bool fixedFanout = false; { - // Split output nets of cells with high fanout + // Split output nets of cells with high fanout SigMap sigmap(module); dict cellFanout; dict> sig2CellsInFanout; calculateFanout(module, sigmap, sig2CellsInFanout, cellFanout); - std::vector cellsToFixFanout; + std::vector cellsToFixFanout; for (auto itrCell : cellFanout) { Cell *cell = itrCell.first; int fanout = itrCell.second; @@ -384,7 +396,7 @@ struct AnnotateCellFanout : public ScriptPass { std::set netsToSplitS; std::string netsToSplit; std::string portsToSplit; - for (Cell* cell : cellsToFixFanout) { + for (Cell *cell : cellsToFixFanout) { RTLIL::SigSpec cellOutSig; for (auto &conn : cell->connections()) { IdString portName = conn.first; @@ -392,24 +404,28 @@ struct AnnotateCellFanout : public ScriptPass { if (cell->output(portName)) { cellOutSig = sigmap(actual); break; - } + } } std::string parent = getParentWire(cellOutSig)->name.c_str(); parent = substringUntil(parent, '['); if (netsToSplitS.find(parent) == netsToSplitS.end()) { - netsToSplit += std::string(" w:") + parent; // Wire + netsToSplit += std::string(" w:") + parent; // Wire portsToSplit += std::string(" o:") + parent; // Port netsToSplitS.insert(parent); std::string splitnets = std::string("splitnets ") + netsToSplit; Pass::call(design, splitnets); splitnets = std::string("splitnets -ports_only ") + portsToSplit; - Pass::call(design, splitnets); + if (!formalFriendly) + // Formal verification does not like ports to be split. + // This will prevent some buffering to happen on output ports used also internally in high fanout, + // but it will make formal happy. + Pass::call(design, splitnets); netsToSplit = ""; portsToSplit = ""; } } } - + { // Fix high fanout SigMap sigmap(module); @@ -431,7 +447,7 @@ struct AnnotateCellFanout : public ScriptPass { } if (fixedFanout) { - // If Fanout got fixed, recalculate and annotate final fanout + // If Fanout got fixed, recalculate and annotate final fanout SigMap sigmap(module); dict cellFanout; dict> sig2CellsInFanout; From c8847e82864fc1f393e2ca6c43c01fb7786fee16 Mon Sep 17 00:00:00 2001 From: Alain Dargelas Date: Mon, 3 Mar 2025 09:35:49 -0800 Subject: [PATCH 08/32] debug flag --- passes/silimate/annotate_cell_fanout.cc | 101 ++++++++++++------------ 1 file changed, 51 insertions(+), 50 deletions(-) diff --git a/passes/silimate/annotate_cell_fanout.cc b/passes/silimate/annotate_cell_fanout.cc index 6aed247cf..a7c8d4b10 100644 --- a/passes/silimate/annotate_cell_fanout.cc +++ b/passes/silimate/annotate_cell_fanout.cc @@ -69,33 +69,36 @@ RTLIL::Wire *getParentWire(const RTLIL::SigSpec &sigspec) if (sigspec.empty()) { return nullptr; // Empty SigSpec, no parent wire } - // Get the first SigBit const RTLIL::SigBit &first_bit = sigspec[0]; - // Return the parent wire return first_bit.wire; } void fixfanout(RTLIL::Design *design, RTLIL::Module *module, SigMap &sigmap, dict> &sig2CellsInFanout, - RTLIL::Cell *cell, int fanout, int limit) + RTLIL::Cell *cell, int fanout, int limit, bool debug) { if (fanout <= limit) { - std::cout << "Nothing to do for: " << cell->name.c_str() << std::endl; - std::cout << "Fanout: " << fanout << std::endl; + if (debug) { + std::cout << "Nothing to do for: " << cell->name.c_str() << std::endl; + std::cout << "Fanout: " << fanout << std::endl; + } return; // No need to insert buffers } else { - std::cout << "Something to do for: " << cell->name.c_str() << std::endl; - std::cout << "Fanout: " << fanout << std::endl; + if (debug) { + std::cout << "Something to do for: " << cell->name.c_str() << std::endl; + std::cout << "Fanout: " << fanout << std::endl; + } } - sigmap.set(module); int num_buffers = std::min((int)std::ceil(static_cast(fanout) / limit), limit); int max_output_per_buffer = std::ceil((float)fanout / (float)num_buffers); - std::cout << "fanout: " << fanout << "\n"; - std::cout << "limit: " << limit << "\n"; - std::cout << "num_buffers: " << num_buffers << "\n"; - std::cout << "max_output_per_buffer: " << max_output_per_buffer << "\n"; - std::cout << "CELL: " << cell->name.c_str() << "\n" << std::flush; + if (debug) { + std::cout << "fanout: " << fanout << "\n"; + std::cout << "limit: " << limit << "\n"; + std::cout << "num_buffers: " << num_buffers << "\n"; + std::cout << "max_output_per_buffer: " << max_output_per_buffer << "\n"; + std::cout << "CELL: " << cell->name.c_str() << "\n" << std::flush; + } // Get cell output RTLIL::SigSpec cellOutSig; @@ -109,7 +112,7 @@ void fixfanout(RTLIL::Design *design, RTLIL::Module *module, SigMap &sigmap, dic } // Create buffers and new wires - std::map bufferActualFanout; + std::map bufferActualFanout; // Keep track of the fanout count for each new buffer std::map>> buffer_outputs; std::map bufferIndexes; for (SigChunk chunk : cellOutSig.chunks()) { @@ -138,38 +141,39 @@ void fixfanout(RTLIL::Design *design, RTLIL::Module *module, SigMap &sigmap, dic // Fix input connections to cells in fanout of buffer to point to the inserted buffer for (Cell *c : cells) { - std::cout << "\n CELL in fanout: " << c->name.c_str() << "\n" << std::flush; + if (debug) + std::cout << "\n CELL in fanout: " << c->name.c_str() << "\n" << std::flush; for (auto &conn : c->connections()) { IdString portName = conn.first; RTLIL::SigSpec actual = sigmap(conn.second); if (c->input(portName)) { if (actual.is_chunk()) { + if (debug) + std::cout << " IS A CHUNK" << std::endl; if (buffer_outputs.find(actual) != buffer_outputs.end()) { - std::cout << " CHUNK, indexCurrentBuffer: " << bufferIndexes[actual] << " buffer_outputs " + if (debug) std::cout << " CHUNK, indexCurrentBuffer: " << bufferIndexes[actual] << " buffer_outputs " << buffer_outputs[actual].size() << std::endl; std::vector> &buf_info_vec = buffer_outputs[actual]; int bufferIndex = bufferIndexes[actual]; - std::cout << " BUFFER INDEX: " << bufferIndex << std::endl; - std::cout << " VEC SIZE: " << buf_info_vec.size() << std::endl; std::tuple &buf_info = buf_info_vec[bufferIndex]; SigSpec newSig = std::get<0>(buf_info); Cell *newBuf = std::get<1>(buf_info); - std::cout << " MATCH" << std::endl; c->setPort(portName, newSig); sig2CellsInFanout[newSig].insert(c); bufferActualFanout[newBuf]++; - std::cout << " b: " << newBuf->name.c_str() << " fanout: " << bufferActualFanout[newBuf] - << std::endl; + if (debug)std::cout << " USE: " << newBuf->name.c_str() << " fanout: " << bufferActualFanout[newBuf] + << std::endl; if (bufferActualFanout[newBuf] >= max_output_per_buffer) { - std::cout << " REACHED MAX" << std::endl; + if (debug) std::cout << " REACHED MAX" << std::endl; if (buffer_outputs[actual].size() - 1 > bufferIndex) { bufferIndexes[actual]++; - std::cout << " NEXT BUFFER" << std::endl; + if (debug) std::cout << " NEXT BUFFER" << std::endl; } } } } else { - std::cout << " NOT CHUNK" << std::endl; + if (debug) + std::cout << " NOT A CHUNK" << std::endl; bool match = false; for (SigChunk chunk_a : actual.chunks()) { if (sigmap(SigSpec(chunk_a)) == sigmap(SigSpec(cellOutSig))) { @@ -186,34 +190,29 @@ void fixfanout(RTLIL::Design *design, RTLIL::Module *module, SigMap &sigmap, dic break; } if (match) { - - std::cout << " MATCH" << std::endl; + if (debug) std::cout << " MATCH" << std::endl; std::vector newChunks; - bool missed = true; for (SigChunk chunk : actual.chunks()) { bool replaced = false; if (buffer_outputs.find(chunk) != buffer_outputs.end()) { - std::cout << " CHUNK, indexCurrentBuffer: " << bufferIndexes[chunk] + if (debug) std::cout << " CHUNK, indexCurrentBuffer: " << bufferIndexes[chunk] << " buffer_outputs " << buffer_outputs[chunk].size() << std::endl; std::vector> &buf_info_vec = buffer_outputs[chunk]; int bufferIndex = bufferIndexes[chunk]; - std::cout << " BUFFER INDEX: " << bufferIndex << std::endl; - std::cout << " VEC SIZE: " << buf_info_vec.size() << std::endl; std::tuple &buf_info = buf_info_vec[bufferIndex]; SigSpec newSig = std::get<0>(buf_info); Cell *newBuf = std::get<1>(buf_info); - missed = false; newChunks.push_back(newSig.as_chunk()); sig2CellsInFanout[newSig].insert(c); replaced = true; bufferActualFanout[newBuf]++; - std::cout << " b: " << newBuf->name.c_str() + if (debug) std::cout << " USE: " << newBuf->name.c_str() << " fanout: " << bufferActualFanout[newBuf] << std::endl; if (bufferActualFanout[newBuf] >= max_output_per_buffer) { - std::cout << " REACHED MAX" << std::endl; + if (debug) std::cout << " REACHED MAX" << std::endl; if (buffer_outputs[chunk].size() - 1 > bufferIndex) { bufferIndexes[chunk]++; - std::cout << " NEXT BUFFER" << std::endl; + if (debug) std::cout << " NEXT BUFFER" << std::endl; } } } @@ -221,9 +220,6 @@ void fixfanout(RTLIL::Design *design, RTLIL::Module *module, SigMap &sigmap, dic newChunks.push_back(chunk); } } - if (missed) { - exit(1); - } c->setPort(portName, newChunks); break; } @@ -232,24 +228,21 @@ void fixfanout(RTLIL::Design *design, RTLIL::Module *module, SigMap &sigmap, dic } } - // Recursively fix the fanout of the newly created buffers for (std::map::iterator itr = bufferActualFanout.begin(); itr != bufferActualFanout.end(); itr++) { if (itr->second == 1) { - std::cout << "Buffer with fanout 1: " << itr->first->name.c_str() << std::endl; + // Remove previously inserted buffers with fanout of 1 (Hard to predict the last buffer usage in above step) + if (debug) std::cout << "Buffer with fanout 1: " << itr->first->name.c_str() << std::endl; RTLIL::SigSpec bufferInSig = itr->first->getPort(ID::A); RTLIL::SigSpec bufferOutSig = itr->first->getPort(ID::Y); - // std::cout << "bufferOutSig: " << bufferOutSig.as_wire()->name.c_str() - // << " bufferInSig: " << bufferInSig.as_wire()->name.c_str() << std::endl; - // Remove newly created buffers with a fanout of 1 for (Cell *c : cells) { - std::cout << "Cell in its fanout: " << c->name.c_str() << std::endl; + if (debug) std::cout << "Cell in its fanout: " << c->name.c_str() << std::endl; for (auto &conn : c->connections()) { IdString portName = conn.first; RTLIL::SigSpec actual = conn.second; if (c->input(portName)) { if (actual.is_chunk()) { if (bufferOutSig == sigmap(actual)) { - std::cout << "Replace1: " << getParentWire(bufferOutSig)->name.c_str() << " by " + if (debug) std::cout << "Replace: " << getParentWire(bufferOutSig)->name.c_str() << " by " << getParentWire(bufferInSig)->name.c_str() << std::endl; c->setPort(portName, bufferInSig); } @@ -257,7 +250,7 @@ void fixfanout(RTLIL::Design *design, RTLIL::Module *module, SigMap &sigmap, dic std::vector newChunks; for (SigChunk chunk : actual.chunks()) { if (sigmap(SigSpec(chunk)) == sigmap(bufferOutSig)) { - std::cout << "Replace2: " << getParentWire(bufferOutSig)->name.c_str() + if (debug) std::cout << "Replace: " << getParentWire(bufferOutSig)->name.c_str() << " by " << getParentWire(bufferInSig)->name.c_str() << std::endl; newChunks.push_back(bufferInSig.as_chunk()); } else { @@ -272,7 +265,8 @@ void fixfanout(RTLIL::Design *design, RTLIL::Module *module, SigMap &sigmap, dic module->remove(itr->first); module->remove({bufferOutSig.as_wire()}); } else { - fixfanout(design, module, sigmap, sig2CellsInFanout, itr->first, itr->second, limit); + // Recursively fix the fanout of the newly created buffers + fixfanout(design, module, sigmap, sig2CellsInFanout, itr->first, itr->second, limit, debug); } } } @@ -334,7 +328,7 @@ void calculateFanout(RTLIL::Module *module, SigMap &sigmap, dictname.c_str(); - parent = substringUntil(parent, '['); + parent = substringuntil(parent, '['); if (netsToSplitS.find(parent) == netsToSplitS.end()) { netsToSplit += std::string(" w:") + parent; // Wire - portsToSplit += std::string(" o:") + parent; // Port + portsToSplit += std::string(" o:") + parent; // Output port netsToSplitS.insert(parent); + // Splitnets has to be invoke with individual nets, sending a bunch of nets as selection + // selects more than required (bug in selection/splitnets). std::string splitnets = std::string("splitnets ") + netsToSplit; Pass::call(design, splitnets); splitnets = std::string("splitnets -ports_only ") + portsToSplit; @@ -437,7 +438,7 @@ struct AnnotateCellFanout : public ScriptPass { Cell *cell = itrCell.first; int fanout = itrCell.second; if (limit > 0 && (fanout > limit)) { - fixfanout(design, module, sigmap, sig2CellsInFanout, cell, fanout, limit); + fixfanout(design, module, sigmap, sig2CellsInFanout, cell, fanout, limit, debug); fixedFanout = true; } else { // Add attribute with fanout info to every cell From b95fc571360d72400274b5eeb1f88d92c99d8ec5 Mon Sep 17 00:00:00 2001 From: Alain Dargelas Date: Mon, 3 Mar 2025 10:08:27 -0800 Subject: [PATCH 09/32] comments --- passes/silimate/annotate_cell_fanout.cc | 132 ++++++++++++++++-------- 1 file changed, 90 insertions(+), 42 deletions(-) diff --git a/passes/silimate/annotate_cell_fanout.cc b/passes/silimate/annotate_cell_fanout.cc index a7c8d4b10..19552084b 100644 --- a/passes/silimate/annotate_cell_fanout.cc +++ b/passes/silimate/annotate_cell_fanout.cc @@ -75,29 +75,40 @@ RTLIL::Wire *getParentWire(const RTLIL::SigSpec &sigspec) return first_bit.wire; } +// For a given cell with fanout exceeding the limit, +// - create an array of buffers per cell output chunk (2 dimentions array of buffers) +// - connect cell chunk to corresponding buffers +// - reconnected cells in the fanout using the chunk to the newly created buffer +// - when a buffer reaches capacity, switch to the next buffer +// The capacity of the buffers might be larger than the limit in a given pass, +// Recursion is used until all buffers capacity is under or at the limit. void fixfanout(RTLIL::Design *design, RTLIL::Module *module, SigMap &sigmap, dict> &sig2CellsInFanout, RTLIL::Cell *cell, int fanout, int limit, bool debug) { if (fanout <= limit) { if (debug) { - std::cout << "Nothing to do for: " << cell->name.c_str() << std::endl; - std::cout << "Fanout: " << fanout << std::endl; + std::cout << "Nothing to do for: " << cell->name.c_str() << std::endl; + std::cout << "Fanout: " << fanout << std::endl; } return; // No need to insert buffers } else { if (debug) { - std::cout << "Something to do for: " << cell->name.c_str() << std::endl; - std::cout << "Fanout: " << fanout << std::endl; + std::cout << "Something to do for: " << cell->name.c_str() << std::endl; + std::cout << "Fanout: " << fanout << std::endl; } } + // The number of buffers inserted: NbBuffers = Min( Ceil( Fanout / Limit), Limit) + // By definition, we cannot insert more buffers than the limit (Use of the Min function), + // else the driving cell would violate the fanout limit. int num_buffers = std::min((int)std::ceil(static_cast(fanout) / limit), limit); + // The max output (fanout) per buffer: MaxOut = Ceil(Fanout / NbBuffers) int max_output_per_buffer = std::ceil((float)fanout / (float)num_buffers); if (debug) { - std::cout << "fanout: " << fanout << "\n"; - std::cout << "limit: " << limit << "\n"; - std::cout << "num_buffers: " << num_buffers << "\n"; - std::cout << "max_output_per_buffer: " << max_output_per_buffer << "\n"; - std::cout << "CELL: " << cell->name.c_str() << "\n" << std::flush; + std::cout << "Fanout: " << fanout << "\n"; + std::cout << "Limit: " << limit << "\n"; + std::cout << "Mum_buffers: " << num_buffers << "\n"; + std::cout << "Max_output_per_buffer: " << max_output_per_buffer << "\n"; + std::cout << "CELL: " << cell->name.c_str() << "\n" << std::flush; } // Get cell output @@ -111,10 +122,14 @@ void fixfanout(RTLIL::Design *design, RTLIL::Module *module, SigMap &sigmap, dic } } - // Create buffers and new wires - std::map bufferActualFanout; // Keep track of the fanout count for each new buffer + // Keep track of the fanout count for each new buffer + std::map bufferActualFanout; + // Array of buffers (The buffer output signal and the buffer cell) per cell output chunks std::map>> buffer_outputs; + // Keep track of which buffer in the array is getting filled for a given chunk std::map bufferIndexes; + + // Create buffers and new wires for (SigChunk chunk : cellOutSig.chunks()) { std::vector> buffer_chunk_outputs; for (int i = 0; i < num_buffers; ++i) { @@ -142,39 +157,53 @@ void fixfanout(RTLIL::Design *design, RTLIL::Module *module, SigMap &sigmap, dic // Fix input connections to cells in fanout of buffer to point to the inserted buffer for (Cell *c : cells) { if (debug) - std::cout << "\n CELL in fanout: " << c->name.c_str() << "\n" << std::flush; + std::cout << "\n CELL in fanout: " << c->name.c_str() << "\n" << std::flush; for (auto &conn : c->connections()) { IdString portName = conn.first; RTLIL::SigSpec actual = sigmap(conn.second); if (c->input(portName)) { if (actual.is_chunk()) { + // Input of that cell is a chunk if (debug) - std::cout << " IS A CHUNK" << std::endl; + std::cout << " IS A CHUNK" << std::endl; if (buffer_outputs.find(actual) != buffer_outputs.end()) { - if (debug) std::cout << " CHUNK, indexCurrentBuffer: " << bufferIndexes[actual] << " buffer_outputs " - << buffer_outputs[actual].size() << std::endl; + // Input is one of the cell's outputs, its a match + if (debug) + std::cout << " CHUNK, indexCurrentBuffer: " << bufferIndexes[actual] << " buffer_outputs " + << buffer_outputs[actual].size() << std::endl; + // Retrieve the buffer information for that cell's chunk std::vector> &buf_info_vec = buffer_outputs[actual]; + // Retrieve which buffer is getting filled int bufferIndex = bufferIndexes[actual]; std::tuple &buf_info = buf_info_vec[bufferIndex]; SigSpec newSig = std::get<0>(buf_info); Cell *newBuf = std::get<1>(buf_info); + // Override the fanout cell's input with the buffer output c->setPort(portName, newSig); + // Keep track of fanout map information for recursive calls sig2CellsInFanout[newSig].insert(c); + // Increment buffer capacity bufferActualFanout[newBuf]++; - if (debug)std::cout << " USE: " << newBuf->name.c_str() << " fanout: " << bufferActualFanout[newBuf] - << std::endl; + if (debug) + std::cout << " USE: " << newBuf->name.c_str() << " fanout: " << bufferActualFanout[newBuf] + << std::endl; + // If we reached capacity for a given buffer, move to the next buffer if (bufferActualFanout[newBuf] >= max_output_per_buffer) { - if (debug) std::cout << " REACHED MAX" << std::endl; - if (buffer_outputs[actual].size() - 1 > bufferIndex) { + if (debug) + std::cout << " REACHED MAX" << std::endl; + if (int(buffer_outputs[actual].size() - 1) > bufferIndex) { bufferIndexes[actual]++; - if (debug) std::cout << " NEXT BUFFER" << std::endl; + if (debug) + std::cout << " NEXT BUFFER" << std::endl; } } } } else { + // Input of that cell is a list of chunks if (debug) - std::cout << " NOT A CHUNK" << std::endl; + std::cout << " NOT A CHUNK" << std::endl; bool match = false; + // Input chunk is one of the cell's outputs, its a match for (SigChunk chunk_a : actual.chunks()) { if (sigmap(SigSpec(chunk_a)) == sigmap(SigSpec(cellOutSig))) { match = true; @@ -190,36 +219,50 @@ void fixfanout(RTLIL::Design *design, RTLIL::Module *module, SigMap &sigmap, dic break; } if (match) { - if (debug) std::cout << " MATCH" << std::endl; + if (debug) + std::cout << " MATCH" << std::endl; + // Create a new chunk vector std::vector newChunks; for (SigChunk chunk : actual.chunks()) { - bool replaced = false; + bool replacedChunck = false; if (buffer_outputs.find(chunk) != buffer_outputs.end()) { - if (debug) std::cout << " CHUNK, indexCurrentBuffer: " << bufferIndexes[chunk] - << " buffer_outputs " << buffer_outputs[chunk].size() << std::endl; + if (debug) + std::cout << " CHUNK, indexCurrentBuffer: " << bufferIndexes[chunk] + << " buffer_outputs " << buffer_outputs[chunk].size() << std::endl; + // Retrieve the buffer information for that cell's chunk std::vector> &buf_info_vec = buffer_outputs[chunk]; + // Retrieve which buffer is getting filled int bufferIndex = bufferIndexes[chunk]; std::tuple &buf_info = buf_info_vec[bufferIndex]; SigSpec newSig = std::get<0>(buf_info); Cell *newBuf = std::get<1>(buf_info); + // Append the buffer's output in the chunck vector newChunks.push_back(newSig.as_chunk()); + // Keep track of fanout map information for recursive calls sig2CellsInFanout[newSig].insert(c); - replaced = true; + replacedChunck = true; + // Increment buffer capacity bufferActualFanout[newBuf]++; - if (debug) std::cout << " USE: " << newBuf->name.c_str() - << " fanout: " << bufferActualFanout[newBuf] << std::endl; + if (debug) + std::cout << " USE: " << newBuf->name.c_str() + << " fanout: " << bufferActualFanout[newBuf] << std::endl; + // If we reached capacity for a given buffer, move to the next buffer if (bufferActualFanout[newBuf] >= max_output_per_buffer) { - if (debug) std::cout << " REACHED MAX" << std::endl; - if (buffer_outputs[chunk].size() - 1 > bufferIndex) { + if (debug) + std::cout << " REACHED MAX" << std::endl; + if (int(buffer_outputs[chunk].size() - 1) > bufferIndex) { bufferIndexes[chunk]++; - if (debug) std::cout << " NEXT BUFFER" << std::endl; + if (debug) + std::cout << " NEXT BUFFER" << std::endl; } } } - if (!replaced) { + if (!replacedChunck) { + // Append original chunck if no buffer used newChunks.push_back(chunk); } } + // Override the fanout cell's input with the newly created chunck vector c->setPort(portName, newChunks); break; } @@ -231,27 +274,32 @@ void fixfanout(RTLIL::Design *design, RTLIL::Module *module, SigMap &sigmap, dic for (std::map::iterator itr = bufferActualFanout.begin(); itr != bufferActualFanout.end(); itr++) { if (itr->second == 1) { // Remove previously inserted buffers with fanout of 1 (Hard to predict the last buffer usage in above step) - if (debug) std::cout << "Buffer with fanout 1: " << itr->first->name.c_str() << std::endl; + if (debug) + std::cout << "Buffer with fanout 1: " << itr->first->name.c_str() << std::endl; RTLIL::SigSpec bufferInSig = itr->first->getPort(ID::A); RTLIL::SigSpec bufferOutSig = itr->first->getPort(ID::Y); for (Cell *c : cells) { - if (debug) std::cout << "Cell in its fanout: " << c->name.c_str() << std::endl; + if (debug) + std::cout << "Cell in its fanout: " << c->name.c_str() << std::endl; for (auto &conn : c->connections()) { IdString portName = conn.first; RTLIL::SigSpec actual = conn.second; if (c->input(portName)) { if (actual.is_chunk()) { if (bufferOutSig == sigmap(actual)) { - if (debug) std::cout << "Replace: " << getParentWire(bufferOutSig)->name.c_str() << " by " - << getParentWire(bufferInSig)->name.c_str() << std::endl; + if (debug) + std::cout << "Replace: " << getParentWire(bufferOutSig)->name.c_str() + << " by " << getParentWire(bufferInSig)->name.c_str() << std::endl; c->setPort(portName, bufferInSig); } } else { std::vector newChunks; for (SigChunk chunk : actual.chunks()) { if (sigmap(SigSpec(chunk)) == sigmap(bufferOutSig)) { - if (debug) std::cout << "Replace: " << getParentWire(bufferOutSig)->name.c_str() - << " by " << getParentWire(bufferInSig)->name.c_str() << std::endl; + if (debug) + std::cout << "Replace: " << getParentWire(bufferOutSig)->name.c_str() + << " by " << getParentWire(bufferInSig)->name.c_str() + << std::endl; newChunks.push_back(bufferInSig.as_chunk()); } else { newChunks.push_back(chunk); @@ -416,11 +464,11 @@ struct AnnotateCellFanout : public ScriptPass { std::string splitnets = std::string("splitnets ") + netsToSplit; Pass::call(design, splitnets); splitnets = std::string("splitnets -ports_only ") + portsToSplit; - if (!formalFriendly) + if (!formalFriendly) // Formal verification does not like ports to be split. - // This will prevent some buffering to happen on output ports used also internally in high fanout, - // but it will make formal happy. - Pass::call(design, splitnets); + // This will prevent some buffering to happen on output ports used also internally in high + // fanout, but it will make formal happy. + Pass::call(design, splitnets); netsToSplit = ""; portsToSplit = ""; } From c7623e4850b84f36ad69cd99656c895ab5f56162 Mon Sep 17 00:00:00 2001 From: Alain Dargelas Date: Mon, 3 Mar 2025 10:39:47 -0800 Subject: [PATCH 10/32] cleanup --- passes/silimate/annotate_cell_fanout.cc | 95 +++++++++++++++---------- 1 file changed, 59 insertions(+), 36 deletions(-) diff --git a/passes/silimate/annotate_cell_fanout.cc b/passes/silimate/annotate_cell_fanout.cc index 19552084b..3c8d99917 100644 --- a/passes/silimate/annotate_cell_fanout.cc +++ b/passes/silimate/annotate_cell_fanout.cc @@ -75,6 +75,28 @@ RTLIL::Wire *getParentWire(const RTLIL::SigSpec &sigspec) return first_bit.wire; } +// Find if a signal is used in another (One level) +bool isSigSpecUsedIn(SigSpec &haystack, SigMap &sigmap, SigSpec &needle) +{ + bool match = false; + // Input chunk is one of the cell's outputs, its a match + for (SigChunk chunk_a : haystack.chunks()) { + if (sigmap(SigSpec(chunk_a)) == sigmap(needle)) { + match = true; + } else { + for (SigChunk chunk_c : needle.chunks()) { + if (sigmap(SigSpec(chunk_a)) == sigmap(SigSpec(chunk_c))) { + match = true; + break; + } + } + } + if (match) + break; + } + return match; +} + // For a given cell with fanout exceeding the limit, // - create an array of buffers per cell output chunk (2 dimentions array of buffers) // - connect cell chunk to corresponding buffers @@ -202,29 +224,13 @@ void fixfanout(RTLIL::Design *design, RTLIL::Module *module, SigMap &sigmap, dic // Input of that cell is a list of chunks if (debug) std::cout << " NOT A CHUNK" << std::endl; - bool match = false; - // Input chunk is one of the cell's outputs, its a match - for (SigChunk chunk_a : actual.chunks()) { - if (sigmap(SigSpec(chunk_a)) == sigmap(SigSpec(cellOutSig))) { - match = true; - } else { - for (SigChunk chunk_c : cellOutSig.chunks()) { - if (sigmap(SigSpec(chunk_a)) == sigmap(SigSpec(chunk_c))) { - match = true; - break; - } - } - } - if (match) - break; - } - if (match) { + if (isSigSpecUsedIn(actual, sigmap, cellOutSig)) { if (debug) std::cout << " MATCH" << std::endl; // Create a new chunk vector std::vector newChunks; for (SigChunk chunk : actual.chunks()) { - bool replacedChunck = false; + bool replacedChunk = false; if (buffer_outputs.find(chunk) != buffer_outputs.end()) { if (debug) std::cout << " CHUNK, indexCurrentBuffer: " << bufferIndexes[chunk] @@ -236,11 +242,11 @@ void fixfanout(RTLIL::Design *design, RTLIL::Module *module, SigMap &sigmap, dic std::tuple &buf_info = buf_info_vec[bufferIndex]; SigSpec newSig = std::get<0>(buf_info); Cell *newBuf = std::get<1>(buf_info); - // Append the buffer's output in the chunck vector + // Append the buffer's output in the chunk vector newChunks.push_back(newSig.as_chunk()); // Keep track of fanout map information for recursive calls sig2CellsInFanout[newSig].insert(c); - replacedChunck = true; + replacedChunk = true; // Increment buffer capacity bufferActualFanout[newBuf]++; if (debug) @@ -257,12 +263,12 @@ void fixfanout(RTLIL::Design *design, RTLIL::Module *module, SigMap &sigmap, dic } } } - if (!replacedChunck) { - // Append original chunck if no buffer used + if (!replacedChunk) { + // Append original chunk if no buffer used newChunks.push_back(chunk); } } - // Override the fanout cell's input with the newly created chunck vector + // Override the fanout cell's input with the newly created chunk vector c->setPort(portName, newChunks); break; } @@ -271,6 +277,7 @@ void fixfanout(RTLIL::Design *design, RTLIL::Module *module, SigMap &sigmap, dic } } + // Post-processing for all newly added buffers for (std::map::iterator itr = bufferActualFanout.begin(); itr != bufferActualFanout.end(); itr++) { if (itr->second == 1) { // Remove previously inserted buffers with fanout of 1 (Hard to predict the last buffer usage in above step) @@ -278,38 +285,54 @@ void fixfanout(RTLIL::Design *design, RTLIL::Module *module, SigMap &sigmap, dic std::cout << "Buffer with fanout 1: " << itr->first->name.c_str() << std::endl; RTLIL::SigSpec bufferInSig = itr->first->getPort(ID::A); RTLIL::SigSpec bufferOutSig = itr->first->getPort(ID::Y); + // Find which cell use that buffer's output and reconnect its input to the former cell (buffer's input) for (Cell *c : cells) { + bool reconnected = false; if (debug) - std::cout << "Cell in its fanout: " << c->name.c_str() << std::endl; + std::cout << " Cell in its fanout: " << c->name.c_str() << std::endl; for (auto &conn : c->connections()) { IdString portName = conn.first; RTLIL::SigSpec actual = conn.second; if (c->input(portName)) { if (actual.is_chunk()) { + // Input is a chunk if (bufferOutSig == sigmap(actual)) { + // Input is one of the cell's outputs, its a match if (debug) - std::cout << "Replace: " << getParentWire(bufferOutSig)->name.c_str() + std::cout << " Replace: " << getParentWire(bufferOutSig)->name.c_str() << " by " << getParentWire(bufferInSig)->name.c_str() << std::endl; + // Override cell's input with original buffer's input c->setPort(portName, bufferInSig); + reconnected = true; + break; } } else { - std::vector newChunks; - for (SigChunk chunk : actual.chunks()) { - if (sigmap(SigSpec(chunk)) == sigmap(bufferOutSig)) { - if (debug) - std::cout << "Replace: " << getParentWire(bufferOutSig)->name.c_str() - << " by " << getParentWire(bufferInSig)->name.c_str() - << std::endl; - newChunks.push_back(bufferInSig.as_chunk()); - } else { - newChunks.push_back(chunk); + // Input is a vector of chunks + if (isSigSpecUsedIn(actual, sigmap, bufferOutSig)) { + std::vector newChunks; + for (SigChunk chunk : actual.chunks()) { + if (sigmap(SigSpec(chunk)) == sigmap(bufferOutSig)) { + if (debug) + std::cout + << " Replace: " + << getParentWire(bufferOutSig)->name.c_str() << " by " + << getParentWire(bufferInSig)->name.c_str() << std::endl; + newChunks.push_back(bufferInSig.as_chunk()); + } else { + newChunks.push_back(chunk); + } } + c->setPort(portName, newChunks); + reconnected = true; + break; } - c->setPort(portName, newChunks); } } } + if (reconnected) + break; } + // Delete the now unsused buffer and it's output signal module->remove(itr->first); module->remove({bufferOutSig.as_wire()}); } else { From be95a08c08ed8e0015918488948879b86b2ec3d8 Mon Sep 17 00:00:00 2001 From: Alain Dargelas Date: Mon, 3 Mar 2025 11:23:45 -0800 Subject: [PATCH 11/32] cleanup --- passes/silimate/annotate_cell_fanout.cc | 254 ++++++++++++------------ 1 file changed, 122 insertions(+), 132 deletions(-) diff --git a/passes/silimate/annotate_cell_fanout.cc b/passes/silimate/annotate_cell_fanout.cc index 3c8d99917..b205c9043 100644 --- a/passes/silimate/annotate_cell_fanout.cc +++ b/passes/silimate/annotate_cell_fanout.cc @@ -97,6 +97,112 @@ bool isSigSpecUsedIn(SigSpec &haystack, SigMap &sigmap, SigSpec &needle) return match; } +// Remove a buffer and fix the fanout connections to use the buffer's input +void removeBuffer(Module *module, SigMap &sigmap, std::set& fanoutcells, Cell *buffer, bool debug) +{ + if (debug) + std::cout << "Buffer with fanout 1: " << buffer->name.c_str() << std::endl; + RTLIL::SigSpec bufferInSig = buffer->getPort(ID::A); + RTLIL::SigSpec bufferOutSig = buffer->getPort(ID::Y); + // Find which cell use that buffer's output and reconnect its input to the former cell (buffer's input) + for (Cell *c : fanoutcells) { + bool reconnected = false; + if (debug) + std::cout << " Cell in its fanout: " << c->name.c_str() << std::endl; + for (auto &conn : c->connections()) { + IdString portName = conn.first; + RTLIL::SigSpec actual = conn.second; + if (c->input(portName)) { + if (actual.is_chunk()) { + // Input is a chunk + if (bufferOutSig == sigmap(actual)) { + // Input is one of the cell's outputs, its a match + if (debug) + std::cout << " Replace: " << getParentWire(bufferOutSig)->name.c_str() << " by " + << getParentWire(bufferInSig)->name.c_str() << std::endl; + // Override cell's input with original buffer's input + c->setPort(portName, bufferInSig); + reconnected = true; + break; + } + } else { + // Input is a vector of chunks + if (isSigSpecUsedIn(actual, sigmap, bufferOutSig)) { + std::vector newChunks; + for (SigChunk chunk : actual.chunks()) { + if (sigmap(SigSpec(chunk)) == sigmap(bufferOutSig)) { + if (debug) + std::cout << " Replace: " << getParentWire(bufferOutSig)->name.c_str() + << " by " << getParentWire(bufferInSig)->name.c_str() << std::endl; + newChunks.push_back(bufferInSig.as_chunk()); + } else { + newChunks.push_back(chunk); + } + } + c->setPort(portName, newChunks); + reconnected = true; + break; + } + } + } + } + if (reconnected) + break; + } + // Delete the now unsused buffer and it's output signal + module->remove(buffer); + module->remove({bufferOutSig.as_wire()}); +} + +RTLIL::SigSpec getCellOutputSigSpec(Cell* cell, SigMap& sigmap) { + RTLIL::SigSpec cellOutSig; + for (auto &conn : cell->connections()) { + IdString portName = conn.first; + RTLIL::SigSpec actual = conn.second; + if (cell->output(portName)) { + cellOutSig = sigmap(actual); + break; + } + } + return cellOutSig; +} + +// Get new output signal for a given signal, used all datastructures with change to buffer +SigSpec updateToBuffer(std::map& bufferIndexes, std::map>>& buffer_outputs, + dict> &sig2CellsInFanout, + std::map& bufferActualFanout, int max_output_per_buffer, Cell* cell, SigSpec sigToReplace, bool debug) +{ + if (debug) + std::cout << " CHUNK, indexCurrentBuffer: " << bufferIndexes[sigToReplace] << " buffer_outputs " << buffer_outputs[sigToReplace].size() + << std::endl; + // Retrieve the buffer information for that cell's chunk + std::vector> &buf_info_vec = buffer_outputs[sigToReplace]; + // Retrieve which buffer is getting filled + int bufferIndex = bufferIndexes[sigToReplace]; + std::tuple &buf_info = buf_info_vec[bufferIndex]; + SigSpec newSig = std::get<0>(buf_info); + Cell *newBuf = std::get<1>(buf_info); + // Keep track of fanout map information for recursive calls + sig2CellsInFanout[newSig].insert(cell); + // Increment buffer capacity + bufferActualFanout[newBuf]++; + if (debug) + std::cout << " USE: " << newBuf->name.c_str() << " fanout: " << bufferActualFanout[newBuf] << std::endl; + // If we reached capacity for a given buffer, move to the next buffer + if (bufferActualFanout[newBuf] >= max_output_per_buffer) { + if (debug) + std::cout << " REACHED MAX" << std::endl; + if (int(buffer_outputs[sigToReplace].size() - 1) > bufferIndex) { + bufferIndexes[sigToReplace]++; + if (debug) + std::cout << " NEXT BUFFER" << std::endl; + } + } + // Return buffer's output + return newSig; +} + // For a given cell with fanout exceeding the limit, // - create an array of buffers per cell output chunk (2 dimentions array of buffers) // - connect cell chunk to corresponding buffers @@ -134,15 +240,7 @@ void fixfanout(RTLIL::Design *design, RTLIL::Module *module, SigMap &sigmap, dic } // Get cell output - RTLIL::SigSpec cellOutSig; - for (auto &conn : cell->connections()) { - IdString portName = conn.first; - RTLIL::SigSpec actual = conn.second; - if (cell->output(portName)) { - cellOutSig = sigmap(actual); - break; - } - } + RTLIL::SigSpec cellOutSig = getCellOutputSigSpec(cell, sigmap); // Keep track of the fanout count for each new buffer std::map bufferActualFanout; @@ -151,7 +249,7 @@ void fixfanout(RTLIL::Design *design, RTLIL::Module *module, SigMap &sigmap, dic // Keep track of which buffer in the array is getting filled for a given chunk std::map bufferIndexes; - // Create buffers and new wires + // Create new buffers and new wires for (SigChunk chunk : cellOutSig.chunks()) { std::vector> buffer_chunk_outputs; for (int i = 0; i < num_buffers; ++i) { @@ -168,108 +266,53 @@ void fixfanout(RTLIL::Design *design, RTLIL::Module *module, SigMap &sigmap, dic } // Cumulate all cells in the fanout of this cell - std::set cells = sig2CellsInFanout[cellOutSig]; + std::set fanoutcells = sig2CellsInFanout[cellOutSig]; for (int i = 0; i < cellOutSig.size(); i++) { SigSpec bit_sig = cellOutSig.extract(i, 1); for (Cell *c : sig2CellsInFanout[sigmap(bit_sig)]) { - cells.insert(c); + fanoutcells.insert(c); } } // Fix input connections to cells in fanout of buffer to point to the inserted buffer - for (Cell *c : cells) { + for (Cell *fanoutcell : fanoutcells) { if (debug) - std::cout << "\n CELL in fanout: " << c->name.c_str() << "\n" << std::flush; - for (auto &conn : c->connections()) { + std::cout << "\n CELL in fanout: " << fanoutcell->name.c_str() << "\n" << std::flush; + for (auto &conn : fanoutcell->connections()) { IdString portName = conn.first; RTLIL::SigSpec actual = sigmap(conn.second); - if (c->input(portName)) { + if (fanoutcell->input(portName)) { if (actual.is_chunk()) { // Input of that cell is a chunk if (debug) std::cout << " IS A CHUNK" << std::endl; if (buffer_outputs.find(actual) != buffer_outputs.end()) { // Input is one of the cell's outputs, its a match - if (debug) - std::cout << " CHUNK, indexCurrentBuffer: " << bufferIndexes[actual] << " buffer_outputs " - << buffer_outputs[actual].size() << std::endl; - // Retrieve the buffer information for that cell's chunk - std::vector> &buf_info_vec = buffer_outputs[actual]; - // Retrieve which buffer is getting filled - int bufferIndex = bufferIndexes[actual]; - std::tuple &buf_info = buf_info_vec[bufferIndex]; - SigSpec newSig = std::get<0>(buf_info); - Cell *newBuf = std::get<1>(buf_info); + SigSpec newSig = updateToBuffer(bufferIndexes, buffer_outputs, sig2CellsInFanout, + bufferActualFanout, max_output_per_buffer, fanoutcell, actual, debug); // Override the fanout cell's input with the buffer output - c->setPort(portName, newSig); - // Keep track of fanout map information for recursive calls - sig2CellsInFanout[newSig].insert(c); - // Increment buffer capacity - bufferActualFanout[newBuf]++; - if (debug) - std::cout << " USE: " << newBuf->name.c_str() << " fanout: " << bufferActualFanout[newBuf] - << std::endl; - // If we reached capacity for a given buffer, move to the next buffer - if (bufferActualFanout[newBuf] >= max_output_per_buffer) { - if (debug) - std::cout << " REACHED MAX" << std::endl; - if (int(buffer_outputs[actual].size() - 1) > bufferIndex) { - bufferIndexes[actual]++; - if (debug) - std::cout << " NEXT BUFFER" << std::endl; - } - } + fanoutcell->setPort(portName, newSig); } } else { // Input of that cell is a list of chunks if (debug) std::cout << " NOT A CHUNK" << std::endl; if (isSigSpecUsedIn(actual, sigmap, cellOutSig)) { - if (debug) - std::cout << " MATCH" << std::endl; // Create a new chunk vector std::vector newChunks; for (SigChunk chunk : actual.chunks()) { - bool replacedChunk = false; if (buffer_outputs.find(chunk) != buffer_outputs.end()) { - if (debug) - std::cout << " CHUNK, indexCurrentBuffer: " << bufferIndexes[chunk] - << " buffer_outputs " << buffer_outputs[chunk].size() << std::endl; - // Retrieve the buffer information for that cell's chunk - std::vector> &buf_info_vec = buffer_outputs[chunk]; - // Retrieve which buffer is getting filled - int bufferIndex = bufferIndexes[chunk]; - std::tuple &buf_info = buf_info_vec[bufferIndex]; - SigSpec newSig = std::get<0>(buf_info); - Cell *newBuf = std::get<1>(buf_info); + SigSpec newSig = updateToBuffer(bufferIndexes, buffer_outputs, sig2CellsInFanout, + bufferActualFanout, max_output_per_buffer, fanoutcell, chunk, debug); // Append the buffer's output in the chunk vector newChunks.push_back(newSig.as_chunk()); - // Keep track of fanout map information for recursive calls - sig2CellsInFanout[newSig].insert(c); - replacedChunk = true; - // Increment buffer capacity - bufferActualFanout[newBuf]++; - if (debug) - std::cout << " USE: " << newBuf->name.c_str() - << " fanout: " << bufferActualFanout[newBuf] << std::endl; - // If we reached capacity for a given buffer, move to the next buffer - if (bufferActualFanout[newBuf] >= max_output_per_buffer) { - if (debug) - std::cout << " REACHED MAX" << std::endl; - if (int(buffer_outputs[chunk].size() - 1) > bufferIndex) { - bufferIndexes[chunk]++; - if (debug) - std::cout << " NEXT BUFFER" << std::endl; - } - } - } - if (!replacedChunk) { + } else { // Append original chunk if no buffer used newChunks.push_back(chunk); } } // Override the fanout cell's input with the newly created chunk vector - c->setPort(portName, newChunks); + fanoutcell->setPort(portName, newChunks); break; } } @@ -281,60 +324,7 @@ void fixfanout(RTLIL::Design *design, RTLIL::Module *module, SigMap &sigmap, dic for (std::map::iterator itr = bufferActualFanout.begin(); itr != bufferActualFanout.end(); itr++) { if (itr->second == 1) { // Remove previously inserted buffers with fanout of 1 (Hard to predict the last buffer usage in above step) - if (debug) - std::cout << "Buffer with fanout 1: " << itr->first->name.c_str() << std::endl; - RTLIL::SigSpec bufferInSig = itr->first->getPort(ID::A); - RTLIL::SigSpec bufferOutSig = itr->first->getPort(ID::Y); - // Find which cell use that buffer's output and reconnect its input to the former cell (buffer's input) - for (Cell *c : cells) { - bool reconnected = false; - if (debug) - std::cout << " Cell in its fanout: " << c->name.c_str() << std::endl; - for (auto &conn : c->connections()) { - IdString portName = conn.first; - RTLIL::SigSpec actual = conn.second; - if (c->input(portName)) { - if (actual.is_chunk()) { - // Input is a chunk - if (bufferOutSig == sigmap(actual)) { - // Input is one of the cell's outputs, its a match - if (debug) - std::cout << " Replace: " << getParentWire(bufferOutSig)->name.c_str() - << " by " << getParentWire(bufferInSig)->name.c_str() << std::endl; - // Override cell's input with original buffer's input - c->setPort(portName, bufferInSig); - reconnected = true; - break; - } - } else { - // Input is a vector of chunks - if (isSigSpecUsedIn(actual, sigmap, bufferOutSig)) { - std::vector newChunks; - for (SigChunk chunk : actual.chunks()) { - if (sigmap(SigSpec(chunk)) == sigmap(bufferOutSig)) { - if (debug) - std::cout - << " Replace: " - << getParentWire(bufferOutSig)->name.c_str() << " by " - << getParentWire(bufferInSig)->name.c_str() << std::endl; - newChunks.push_back(bufferInSig.as_chunk()); - } else { - newChunks.push_back(chunk); - } - } - c->setPort(portName, newChunks); - reconnected = true; - break; - } - } - } - } - if (reconnected) - break; - } - // Delete the now unsused buffer and it's output signal - module->remove(itr->first); - module->remove({bufferOutSig.as_wire()}); + removeBuffer(module, sigmap, fanoutcells, itr->first, debug); } else { // Recursively fix the fanout of the newly created buffers fixfanout(design, module, sigmap, sig2CellsInFanout, itr->first, itr->second, limit, debug); From a1ad385702a9e30928132d1d7f9ed3c67a8bfac9 Mon Sep 17 00:00:00 2001 From: Alain Dargelas Date: Mon, 3 Mar 2025 11:31:27 -0800 Subject: [PATCH 12/32] help --- passes/silimate/annotate_cell_fanout.cc | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/passes/silimate/annotate_cell_fanout.cc b/passes/silimate/annotate_cell_fanout.cc index b205c9043..90bdcc4ca 100644 --- a/passes/silimate/annotate_cell_fanout.cc +++ b/passes/silimate/annotate_cell_fanout.cc @@ -389,6 +389,7 @@ void calculateFanout(RTLIL::Module *module, SigMap &sigmap, dict\n"); + log(" Limits the fanout by inserting balanced buffer trees.\n"); + log(" -formal\n"); + log(" For formal verification to pass, will prevent splitnets passes on ports, even if they have large fanout.\n"); + log(" -debug\n"); + log(" Debug trace.\n"); + log("\n"); + } void execute(std::vector args, RTLIL::Design *design) override { int limit = -1; From d5d58349fdce73d0f2f0d6a85f73a584d8bbf868 Mon Sep 17 00:00:00 2001 From: Alain Dargelas Date: Mon, 3 Mar 2025 15:06:02 -0800 Subject: [PATCH 13/32] split input nets with high fanout --- passes/silimate/annotate_cell_fanout.cc | 125 ++++++++++++++---------- 1 file changed, 74 insertions(+), 51 deletions(-) diff --git a/passes/silimate/annotate_cell_fanout.cc b/passes/silimate/annotate_cell_fanout.cc index 90bdcc4ca..5aaef030b 100644 --- a/passes/silimate/annotate_cell_fanout.cc +++ b/passes/silimate/annotate_cell_fanout.cc @@ -98,7 +98,7 @@ bool isSigSpecUsedIn(SigSpec &haystack, SigMap &sigmap, SigSpec &needle) } // Remove a buffer and fix the fanout connections to use the buffer's input -void removeBuffer(Module *module, SigMap &sigmap, std::set& fanoutcells, Cell *buffer, bool debug) +void removeBuffer(Module *module, SigMap &sigmap, std::set &fanoutcells, Cell *buffer, bool debug) { if (debug) std::cout << "Buffer with fanout 1: " << buffer->name.c_str() << std::endl; @@ -154,7 +154,8 @@ void removeBuffer(Module *module, SigMap &sigmap, std::set& fanoutcells, module->remove({bufferOutSig.as_wire()}); } -RTLIL::SigSpec getCellOutputSigSpec(Cell* cell, SigMap& sigmap) { +RTLIL::SigSpec getCellOutputSigSpec(Cell *cell, SigMap &sigmap) +{ RTLIL::SigSpec cellOutSig; for (auto &conn : cell->connections()) { IdString portName = conn.first; @@ -168,14 +169,14 @@ RTLIL::SigSpec getCellOutputSigSpec(Cell* cell, SigMap& sigmap) { } // Get new output signal for a given signal, used all datastructures with change to buffer -SigSpec updateToBuffer(std::map& bufferIndexes, std::map>>& buffer_outputs, - dict> &sig2CellsInFanout, - std::map& bufferActualFanout, int max_output_per_buffer, Cell* cell, SigSpec sigToReplace, bool debug) +SigSpec updateToBuffer(std::map &bufferIndexes, + std::map>> &buffer_outputs, + dict> &sig2CellsInFanout, std::map &bufferActualFanout, + int max_output_per_buffer, Cell *cell, SigSpec sigToReplace, bool debug) { if (debug) - std::cout << " CHUNK, indexCurrentBuffer: " << bufferIndexes[sigToReplace] << " buffer_outputs " << buffer_outputs[sigToReplace].size() - << std::endl; + std::cout << " CHUNK, indexCurrentBuffer: " << bufferIndexes[sigToReplace] << " buffer_outputs " + << buffer_outputs[sigToReplace].size() << std::endl; // Retrieve the buffer information for that cell's chunk std::vector> &buf_info_vec = buffer_outputs[sigToReplace]; // Retrieve which buffer is getting filled @@ -288,8 +289,8 @@ void fixfanout(RTLIL::Design *design, RTLIL::Module *module, SigMap &sigmap, dic std::cout << " IS A CHUNK" << std::endl; if (buffer_outputs.find(actual) != buffer_outputs.end()) { // Input is one of the cell's outputs, its a match - SigSpec newSig = updateToBuffer(bufferIndexes, buffer_outputs, sig2CellsInFanout, - bufferActualFanout, max_output_per_buffer, fanoutcell, actual, debug); + SigSpec newSig = updateToBuffer(bufferIndexes, buffer_outputs, sig2CellsInFanout, bufferActualFanout, + max_output_per_buffer, fanoutcell, actual, debug); // Override the fanout cell's input with the buffer output fanoutcell->setPort(portName, newSig); } @@ -302,8 +303,9 @@ void fixfanout(RTLIL::Design *design, RTLIL::Module *module, SigMap &sigmap, dic std::vector newChunks; for (SigChunk chunk : actual.chunks()) { if (buffer_outputs.find(chunk) != buffer_outputs.end()) { - SigSpec newSig = updateToBuffer(bufferIndexes, buffer_outputs, sig2CellsInFanout, - bufferActualFanout, max_output_per_buffer, fanoutcell, chunk, debug); + SigSpec newSig = + updateToBuffer(bufferIndexes, buffer_outputs, sig2CellsInFanout, bufferActualFanout, + max_output_per_buffer, fanoutcell, chunk, debug); // Append the buffer's output in the chunk vector newChunks.push_back(newSig.as_chunk()); } else { @@ -332,7 +334,8 @@ void fixfanout(RTLIL::Design *design, RTLIL::Module *module, SigMap &sigmap, dic } } -void calculateFanout(RTLIL::Module *module, SigMap &sigmap, dict> &sig2CellsInFanout, dict &cellFanout) +void calculateFanout(RTLIL::Module *module, SigMap &sigmap, dict> &sig2CellsInFanout, dict &cellFanout, + dict &sigFanout) { // Precompute cell output sigspec to cell map dict> sig2CellsInFanin; @@ -343,7 +346,6 @@ void calculateFanout(RTLIL::Module *module, SigMap &sigmap, dict sigFanout; for (auto itrSig : sig2CellsInFanout) { SigSpec sigspec = itrSig.first; std::set &cells = itrSig.second; @@ -400,10 +402,41 @@ std::string substringuntil(const std::string &str, char delimiter) } } +void splitNet(Design *design, std::set &netsToSplitS, RTLIL::SigSpec &sigToSplit, bool formalFriendly, bool inputsToo = false) +{ + Wire *parentWire = getParentWire(sigToSplit); + if (!parentWire) + return; + std::string parent = parentWire->name.c_str(); + if (parent == "") { + return; + } + parent = substringuntil(parent, '['); + if (netsToSplitS.find(parent) == netsToSplitS.end()) { + netsToSplitS.insert(parent); + // Splitnets has to be invoke with individual nets. Sending a bunch of nets as selection, + // selects more than required (bug in selection/splitnets). + std::cout << "splitnets -ports_only w:" << parent << std::endl; + Pass::call(design, "splitnets w:" + parent); // Wire + if (!formalFriendly) { + // Formal verification does not like ports to be split. + // This option will prevent some buffering to happen on high fanout input/output ports, + // but it will make formal happy. + if (inputsToo) { + std::cout << "splitnets -ports_only x:" << parent << std::endl; + Pass::call(design, "splitnets -ports_only x:" + parent); // Input/Output port + } else { + std::cout << "splitnets -ports_only o:" << parent << std::endl; + Pass::call(design, "splitnets -ports_only o:" + parent); // Output port + } + } + } +} + struct AnnotateCellFanout : public ScriptPass { AnnotateCellFanout() : ScriptPass("annotate_cell_fanout", "Annotate the cell fanout on the cell") {} void script() override {} - void help() override + void help() override { log("\n"); log(" annotate_cell_fanout [options] [selection]\n"); @@ -453,53 +486,41 @@ struct AnnotateCellFanout : public ScriptPass { } for (auto module : design->selected_modules()) { bool fixedFanout = false; + { - // Split output nets of cells with high fanout + // Calculate fanout SigMap sigmap(module); dict cellFanout; + dict sigFanout; dict> sig2CellsInFanout; - calculateFanout(module, sigmap, sig2CellsInFanout, cellFanout); + calculateFanout(module, sigmap, sig2CellsInFanout, cellFanout, sigFanout); - std::vector cellsToFixFanout; + std::set netsToSplitS; + // Split output nets of cells with high fanout for (auto itrCell : cellFanout) { Cell *cell = itrCell.first; int fanout = itrCell.second; if (limit > 0 && (fanout > limit)) { - cellsToFixFanout.push_back(cell); + RTLIL::SigSpec cellOutSig = getCellOutputSigSpec(cell, sigmap); + splitNet(design, netsToSplitS, cellOutSig, formalFriendly); } } - std::set netsToSplitS; - std::string netsToSplit; - std::string portsToSplit; - for (Cell *cell : cellsToFixFanout) { - RTLIL::SigSpec cellOutSig; - for (auto &conn : cell->connections()) { - IdString portName = conn.first; - RTLIL::SigSpec actual = conn.second; - if (cell->output(portName)) { - cellOutSig = sigmap(actual); - break; + + // Split module input nets with high fanout + std::set wiresToSplit; + for (Wire *wire : module->wires()) { + if (wire->port_input) { + SigSpec inp = sigmap(wire); + int fanout = sigFanout[inp]; + std::cout << "Input" << wire->name.c_str() << " fanout: " << fanout << std::endl; + if (limit > 0 && (fanout > limit)) { + wiresToSplit.insert(wire); } } - std::string parent = getParentWire(cellOutSig)->name.c_str(); - parent = substringuntil(parent, '['); - if (netsToSplitS.find(parent) == netsToSplitS.end()) { - netsToSplit += std::string(" w:") + parent; // Wire - portsToSplit += std::string(" o:") + parent; // Output port - netsToSplitS.insert(parent); - // Splitnets has to be invoke with individual nets, sending a bunch of nets as selection - // selects more than required (bug in selection/splitnets). - std::string splitnets = std::string("splitnets ") + netsToSplit; - Pass::call(design, splitnets); - splitnets = std::string("splitnets -ports_only ") + portsToSplit; - if (!formalFriendly) - // Formal verification does not like ports to be split. - // This will prevent some buffering to happen on output ports used also internally in high - // fanout, but it will make formal happy. - Pass::call(design, splitnets); - netsToSplit = ""; - portsToSplit = ""; - } + } + for (Wire *wire : wiresToSplit) { + SigSpec inp = sigmap(wire); + splitNet(design, netsToSplitS, inp, formalFriendly, true); } } @@ -507,8 +528,9 @@ struct AnnotateCellFanout : public ScriptPass { // Fix high fanout SigMap sigmap(module); dict cellFanout; + dict sigFanout; dict> sig2CellsInFanout; - calculateFanout(module, sigmap, sig2CellsInFanout, cellFanout); + calculateFanout(module, sigmap, sig2CellsInFanout, cellFanout, sigFanout); for (auto itrCell : cellFanout) { Cell *cell = itrCell.first; @@ -527,8 +549,9 @@ struct AnnotateCellFanout : public ScriptPass { // If Fanout got fixed, recalculate and annotate final fanout SigMap sigmap(module); dict cellFanout; + dict sigFanout; dict> sig2CellsInFanout; - calculateFanout(module, sigmap, sig2CellsInFanout, cellFanout); + calculateFanout(module, sigmap, sig2CellsInFanout, cellFanout, sigFanout); for (auto itrCell : cellFanout) { Cell *cell = itrCell.first; int fanout = itrCell.second; From 6cff6d9c49ceea0338c35734fd5129ffdea1c238 Mon Sep 17 00:00:00 2001 From: Alain Dargelas Date: Mon, 3 Mar 2025 16:51:10 -0800 Subject: [PATCH 14/32] input fanout --- passes/silimate/annotate_cell_fanout.cc | 125 +++++++++++++++++++----- 1 file changed, 100 insertions(+), 25 deletions(-) diff --git a/passes/silimate/annotate_cell_fanout.cc b/passes/silimate/annotate_cell_fanout.cc index 5aaef030b..91d0fe000 100644 --- a/passes/silimate/annotate_cell_fanout.cc +++ b/passes/silimate/annotate_cell_fanout.cc @@ -75,6 +75,13 @@ RTLIL::Wire *getParentWire(const RTLIL::SigSpec &sigspec) return first_bit.wire; } +std::string getSigSpecName(const RTLIL::SigSpec &sigspec) { + Wire* wire = getParentWire(sigspec); + if (wire) + return wire->name.c_str(); + return ""; +} + // Find if a signal is used in another (One level) bool isSigSpecUsedIn(SigSpec &haystack, SigMap &sigmap, SigSpec &needle) { @@ -204,6 +211,53 @@ SigSpec updateToBuffer(std::map &bufferIndexes, return newSig; } +RTLIL::IdString generateSigSpecName(RTLIL::Module *module, const RTLIL::SigSpec &sigspec) +{ + if (sigspec.empty()) { + return RTLIL::IdString(); // Empty SigSpec, return empty IdString + } + + std::stringstream ss; + + if (sigspec.is_wire()) { + // Handle wires + ss << sigspec.as_wire()->name.str(); + } else if (sigspec.size() == 1 && sigspec[0].wire) { + // Handle single-bit SigSpecs + ss << sigspec[0].wire->name.str(); + if (sigspec[0].wire->width != 1) { + ss << "[" << sigspec[0].offset << "]"; + } + } else if (sigspec.is_chunk()) { + // Handle slices + RTLIL::Wire *parent_wire = sigspec[0].wire; + SigChunk chunk = sigspec.as_chunk(); + if (parent_wire) { + ss << parent_wire->name.str() << "[" << chunk.offset + chunk.width - 1 << ":" << chunk.offset << "]"; + } + } else if (!sigspec.is_chunk()) { + // Handle vector of chunks + int max = 0; + int min = INT_MAX; + RTLIL::Wire *parent_wire = sigspec[0].wire; + for (SigChunk chunk : sigspec.chunks()) { + max = std::max(max, chunk.offset); + min = std::min(min, chunk.offset); + } + if (parent_wire) { + ss << parent_wire->name.str() << "[" << max << ":" << min << "]"; + } else { + ss << "\\sigspec_[" << max << ":" << min << "]"; + } + } else { + // Handle other cases (e.g., constants) + ss << "\\sigspec_"; + } + + RTLIL::IdString base_name = RTLIL::IdString(ss.str()); + return RTLIL::IdString(ss.str()); +} + // For a given cell with fanout exceeding the limit, // - create an array of buffers per cell output chunk (2 dimentions array of buffers) // - connect cell chunk to corresponding buffers @@ -212,17 +266,21 @@ SigSpec updateToBuffer(std::map &bufferIndexes, // The capacity of the buffers might be larger than the limit in a given pass, // Recursion is used until all buffers capacity is under or at the limit. void fixfanout(RTLIL::Design *design, RTLIL::Module *module, SigMap &sigmap, dict> &sig2CellsInFanout, - RTLIL::Cell *cell, int fanout, int limit, bool debug) + SigSpec sigToBuffer, int fanout, int limit, bool debug) { + if (sigToBuffer.is_fully_const()) { + return; + } + std::string signame = generateSigSpecName(module, sigToBuffer).c_str(); if (fanout <= limit) { if (debug) { - std::cout << "Nothing to do for: " << cell->name.c_str() << std::endl; + std::cout << "Nothing to do for: " << signame << std::endl; std::cout << "Fanout: " << fanout << std::endl; } return; // No need to insert buffers } else { if (debug) { - std::cout << "Something to do for: " << cell->name.c_str() << std::endl; + std::cout << "Something to do for: " << signame << std::endl; std::cout << "Fanout: " << fanout << std::endl; } } @@ -235,14 +293,11 @@ void fixfanout(RTLIL::Design *design, RTLIL::Module *module, SigMap &sigmap, dic if (debug) { std::cout << "Fanout: " << fanout << "\n"; std::cout << "Limit: " << limit << "\n"; - std::cout << "Mum_buffers: " << num_buffers << "\n"; + std::cout << "Num_buffers: " << num_buffers << "\n"; std::cout << "Max_output_per_buffer: " << max_output_per_buffer << "\n"; - std::cout << "CELL: " << cell->name.c_str() << "\n" << std::flush; + std::cout << "Signal: " << signame << "\n" << std::flush; } - // Get cell output - RTLIL::SigSpec cellOutSig = getCellOutputSigSpec(cell, sigmap); - // Keep track of the fanout count for each new buffer std::map bufferActualFanout; // Array of buffers (The buffer output signal and the buffer cell) per cell output chunks @@ -251,25 +306,28 @@ void fixfanout(RTLIL::Design *design, RTLIL::Module *module, SigMap &sigmap, dic std::map bufferIndexes; // Create new buffers and new wires - for (SigChunk chunk : cellOutSig.chunks()) { + int index_buffer = 0; + for (SigChunk chunk : sigToBuffer.chunks()) { std::vector> buffer_chunk_outputs; for (int i = 0; i < num_buffers; ++i) { - RTLIL::Cell *buffer = module->addCell(NEW_ID2_SUFFIX("fbuf"), ID($pos)); + std::cout << "Name: " << signame + "_fbuf" + std::to_string(index_buffer) << std::endl; + RTLIL::Cell *buffer = module->addCell(signame + "_fbuf" + std::to_string(index_buffer), ID($pos)); bufferActualFanout[buffer] = 0; - RTLIL::SigSpec buffer_output = module->addWire(NEW_ID2_SUFFIX("fbuf"), chunk.size()); + RTLIL::SigSpec buffer_output = module->addWire(signame + "_wbuf" + std::to_string(index_buffer) , chunk.size()); buffer->setPort(ID(A), chunk); buffer->setPort(ID(Y), sigmap(buffer_output)); buffer->fixup_parameters(); buffer_chunk_outputs.push_back(std::make_tuple(buffer_output, buffer)); // Old - New bufferIndexes[chunk] = 0; + index_buffer++; } buffer_outputs.emplace(sigmap(SigSpec(chunk)), buffer_chunk_outputs); } // Cumulate all cells in the fanout of this cell - std::set fanoutcells = sig2CellsInFanout[cellOutSig]; - for (int i = 0; i < cellOutSig.size(); i++) { - SigSpec bit_sig = cellOutSig.extract(i, 1); + std::set fanoutcells = sig2CellsInFanout[sigToBuffer]; + for (int i = 0; i < sigToBuffer.size(); i++) { + SigSpec bit_sig = sigToBuffer.extract(i, 1); for (Cell *c : sig2CellsInFanout[sigmap(bit_sig)]) { fanoutcells.insert(c); } @@ -288,6 +346,7 @@ void fixfanout(RTLIL::Design *design, RTLIL::Module *module, SigMap &sigmap, dic if (debug) std::cout << " IS A CHUNK" << std::endl; if (buffer_outputs.find(actual) != buffer_outputs.end()) { + if (debug) std::cout << " MATCH" << std::endl; // Input is one of the cell's outputs, its a match SigSpec newSig = updateToBuffer(bufferIndexes, buffer_outputs, sig2CellsInFanout, bufferActualFanout, max_output_per_buffer, fanoutcell, actual, debug); @@ -298,11 +357,12 @@ void fixfanout(RTLIL::Design *design, RTLIL::Module *module, SigMap &sigmap, dic // Input of that cell is a list of chunks if (debug) std::cout << " NOT A CHUNK" << std::endl; - if (isSigSpecUsedIn(actual, sigmap, cellOutSig)) { + if (isSigSpecUsedIn(actual, sigmap, sigToBuffer)) { // Create a new chunk vector std::vector newChunks; for (SigChunk chunk : actual.chunks()) { if (buffer_outputs.find(chunk) != buffer_outputs.end()) { + if (debug) std::cout << " MATCH" << std::endl; SigSpec newSig = updateToBuffer(bufferIndexes, buffer_outputs, sig2CellsInFanout, bufferActualFanout, max_output_per_buffer, fanoutcell, chunk, debug); @@ -329,7 +389,8 @@ void fixfanout(RTLIL::Design *design, RTLIL::Module *module, SigMap &sigmap, dic removeBuffer(module, sigmap, fanoutcells, itr->first, debug); } else { // Recursively fix the fanout of the newly created buffers - fixfanout(design, module, sigmap, sig2CellsInFanout, itr->first, itr->second, limit, debug); + RTLIL::SigSpec sig = getCellOutputSigSpec(itr->first, sigmap); + fixfanout(design, module, sigmap, sig2CellsInFanout, sig, itr->second, limit, debug); } } } @@ -402,7 +463,7 @@ std::string substringuntil(const std::string &str, char delimiter) } } -void splitNet(Design *design, std::set &netsToSplitS, RTLIL::SigSpec &sigToSplit, bool formalFriendly, bool inputsToo = false) +void splitNet(Design *design, std::set &netsToSplitS, RTLIL::SigSpec &sigToSplit, bool formalFriendly, bool inputPort = false) { Wire *parentWire = getParentWire(sigToSplit); if (!parentWire) @@ -416,17 +477,14 @@ void splitNet(Design *design, std::set &netsToSplitS, RTLIL::SigSpe netsToSplitS.insert(parent); // Splitnets has to be invoke with individual nets. Sending a bunch of nets as selection, // selects more than required (bug in selection/splitnets). - std::cout << "splitnets -ports_only w:" << parent << std::endl; Pass::call(design, "splitnets w:" + parent); // Wire if (!formalFriendly) { // Formal verification does not like ports to be split. // This option will prevent some buffering to happen on high fanout input/output ports, // but it will make formal happy. - if (inputsToo) { - std::cout << "splitnets -ports_only x:" << parent << std::endl; - Pass::call(design, "splitnets -ports_only x:" + parent); // Input/Output port + if (inputPort) { + Pass::call(design, "splitnets -ports_only i:" + parent); // Input port } else { - std::cout << "splitnets -ports_only o:" << parent << std::endl; Pass::call(design, "splitnets -ports_only o:" + parent); // Output port } } @@ -496,7 +554,7 @@ struct AnnotateCellFanout : public ScriptPass { calculateFanout(module, sigmap, sig2CellsInFanout, cellFanout, sigFanout); std::set netsToSplitS; - // Split output nets of cells with high fanout + // Split cells' output nets with high fanout for (auto itrCell : cellFanout) { Cell *cell = itrCell.first; int fanout = itrCell.second; @@ -512,7 +570,6 @@ struct AnnotateCellFanout : public ScriptPass { if (wire->port_input) { SigSpec inp = sigmap(wire); int fanout = sigFanout[inp]; - std::cout << "Input" << wire->name.c_str() << " fanout: " << fanout << std::endl; if (limit > 0 && (fanout > limit)) { wiresToSplit.insert(wire); } @@ -532,17 +589,35 @@ struct AnnotateCellFanout : public ScriptPass { dict> sig2CellsInFanout; calculateFanout(module, sigmap, sig2CellsInFanout, cellFanout, sigFanout); + // Fix cells outputs with high fanout for (auto itrCell : cellFanout) { Cell *cell = itrCell.first; int fanout = itrCell.second; if (limit > 0 && (fanout > limit)) { - fixfanout(design, module, sigmap, sig2CellsInFanout, cell, fanout, limit, debug); + RTLIL::SigSpec cellOutSig = getCellOutputSigSpec(cell, sigmap); + fixfanout(design, module, sigmap, sig2CellsInFanout, cellOutSig, fanout, limit, debug); fixedFanout = true; } else { // Add attribute with fanout info to every cell cell->set_string_attribute("$FANOUT", std::to_string(fanout)); } } + + // Fix module input nets with high fanout + std::map sigsToFix; + for (Wire *wire : module->wires()) { + if (wire->port_input) { + SigSpec inp = sigmap(wire); + int fanout = sigFanout[inp]; + if (limit > 0 && (fanout > limit)) { + sigsToFix.emplace(inp, fanout); + } + } + } + for (auto sig : sigsToFix) { + fixfanout(design, module, sigmap, sig2CellsInFanout, sig.first, sig.second, limit, debug); + fixedFanout = true; + } } if (fixedFanout) { From 425e1a03ae09966e29c73b3fd258c8f4c613645d Mon Sep 17 00:00:00 2001 From: Alain Dargelas Date: Mon, 3 Mar 2025 21:45:53 -0800 Subject: [PATCH 15/32] reuse net on same cell --- passes/silimate/annotate_cell_fanout.cc | 164 +++++++++++++----------- 1 file changed, 92 insertions(+), 72 deletions(-) diff --git a/passes/silimate/annotate_cell_fanout.cc b/passes/silimate/annotate_cell_fanout.cc index 91d0fe000..345cd1cc0 100644 --- a/passes/silimate/annotate_cell_fanout.cc +++ b/passes/silimate/annotate_cell_fanout.cc @@ -5,6 +5,53 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN +RTLIL::IdString generateSigSpecName(const RTLIL::SigSpec &sigspec) +{ + if (sigspec.empty()) { + return RTLIL::IdString(); // Empty SigSpec, return empty IdString + } + + std::stringstream ss; + + if (sigspec.is_wire()) { + // Handle wires + ss << sigspec.as_wire()->name.str(); + } else if (sigspec.size() == 1 && sigspec[0].wire) { + // Handle single-bit SigSpecs + ss << sigspec[0].wire->name.str(); + if (sigspec[0].wire->width != 1) { + ss << "[" << sigspec[0].offset << "]"; + } + } else if (sigspec.is_chunk()) { + // Handle slices + RTLIL::Wire *parent_wire = sigspec[0].wire; + SigChunk chunk = sigspec.as_chunk(); + if (parent_wire) { + ss << parent_wire->name.str() << "[" << chunk.offset + chunk.width - 1 << ":" << chunk.offset << "]"; + } + } else if (!sigspec.is_chunk()) { + // Handle vector of chunks + int max = 0; + int min = INT_MAX; + RTLIL::Wire *parent_wire = sigspec[0].wire; + for (SigChunk chunk : sigspec.chunks()) { + max = std::max(max, chunk.offset); + min = std::min(min, chunk.offset); + } + if (parent_wire) { + ss << parent_wire->name.str() << "[" << max << ":" << min << "]"; + } else { + ss << "\\sigspec_[" << max << ":" << min << "]"; + } + } else { + // Handle other cases (e.g., constants) + ss << "\\sigspec_"; + } + + RTLIL::IdString base_name = RTLIL::IdString(ss.str()); + return RTLIL::IdString(ss.str()); +} + // Signal cell driver(s), precompute a cell output signal to a cell map void sigCellDrivers(RTLIL::Module *module, SigMap &sigmap, dict> &sig2CellsInFanout, dict> &sig2CellsInFanin) @@ -75,13 +122,6 @@ RTLIL::Wire *getParentWire(const RTLIL::SigSpec &sigspec) return first_bit.wire; } -std::string getSigSpecName(const RTLIL::SigSpec &sigspec) { - Wire* wire = getParentWire(sigspec); - if (wire) - return wire->name.c_str(); - return ""; -} - // Find if a signal is used in another (One level) bool isSigSpecUsedIn(SigSpec &haystack, SigMap &sigmap, SigSpec &needle) { @@ -179,11 +219,23 @@ RTLIL::SigSpec getCellOutputSigSpec(Cell *cell, SigMap &sigmap) SigSpec updateToBuffer(std::map &bufferIndexes, std::map>> &buffer_outputs, dict> &sig2CellsInFanout, std::map &bufferActualFanout, - int max_output_per_buffer, Cell *cell, SigSpec sigToReplace, bool debug) + std::map> &usedBuffers, int max_output_per_buffer, Cell *fanoutcell, SigSpec sigToReplace, + bool debug) { if (debug) std::cout << " CHUNK, indexCurrentBuffer: " << bufferIndexes[sigToReplace] << " buffer_outputs " << buffer_outputs[sigToReplace].size() << std::endl; + // Reuse cached result for a given cell; + std::map>::iterator itrCell = usedBuffers.find(fanoutcell); + if (itrCell != usedBuffers.end()) { + std::map::iterator itrBuffer = itrCell->second.find(sigToReplace); + if (itrBuffer != itrCell->second.end()) { + if (debug) + std::cout << "REUSE CACHE:" << fanoutcell->name.c_str() << " SIG: " << generateSigSpecName(sigToReplace).c_str() + << std::endl; + return itrBuffer->second; + } + } // Retrieve the buffer information for that cell's chunk std::vector> &buf_info_vec = buffer_outputs[sigToReplace]; // Retrieve which buffer is getting filled @@ -192,7 +244,7 @@ SigSpec updateToBuffer(std::map &bufferIndexes, SigSpec newSig = std::get<0>(buf_info); Cell *newBuf = std::get<1>(buf_info); // Keep track of fanout map information for recursive calls - sig2CellsInFanout[newSig].insert(cell); + sig2CellsInFanout[newSig].insert(fanoutcell); // Increment buffer capacity bufferActualFanout[newBuf]++; if (debug) @@ -207,57 +259,21 @@ SigSpec updateToBuffer(std::map &bufferIndexes, std::cout << " NEXT BUFFER" << std::endl; } } + // Cache result + if (debug) + std::cout << "CACHE:" << fanoutcell->name.c_str() << " SIG: " << generateSigSpecName(sigToReplace).c_str() << " BY " + << generateSigSpecName(newSig).c_str() << std::endl; + if (itrCell == usedBuffers.end()) { + std::map tmpPair; + tmpPair.emplace(sigToReplace, newSig); + usedBuffers.emplace(fanoutcell, tmpPair); + } else { + itrCell->second.emplace(sigToReplace, newSig); + } // Return buffer's output return newSig; } -RTLIL::IdString generateSigSpecName(RTLIL::Module *module, const RTLIL::SigSpec &sigspec) -{ - if (sigspec.empty()) { - return RTLIL::IdString(); // Empty SigSpec, return empty IdString - } - - std::stringstream ss; - - if (sigspec.is_wire()) { - // Handle wires - ss << sigspec.as_wire()->name.str(); - } else if (sigspec.size() == 1 && sigspec[0].wire) { - // Handle single-bit SigSpecs - ss << sigspec[0].wire->name.str(); - if (sigspec[0].wire->width != 1) { - ss << "[" << sigspec[0].offset << "]"; - } - } else if (sigspec.is_chunk()) { - // Handle slices - RTLIL::Wire *parent_wire = sigspec[0].wire; - SigChunk chunk = sigspec.as_chunk(); - if (parent_wire) { - ss << parent_wire->name.str() << "[" << chunk.offset + chunk.width - 1 << ":" << chunk.offset << "]"; - } - } else if (!sigspec.is_chunk()) { - // Handle vector of chunks - int max = 0; - int min = INT_MAX; - RTLIL::Wire *parent_wire = sigspec[0].wire; - for (SigChunk chunk : sigspec.chunks()) { - max = std::max(max, chunk.offset); - min = std::min(min, chunk.offset); - } - if (parent_wire) { - ss << parent_wire->name.str() << "[" << max << ":" << min << "]"; - } else { - ss << "\\sigspec_[" << max << ":" << min << "]"; - } - } else { - // Handle other cases (e.g., constants) - ss << "\\sigspec_"; - } - - RTLIL::IdString base_name = RTLIL::IdString(ss.str()); - return RTLIL::IdString(ss.str()); -} - // For a given cell with fanout exceeding the limit, // - create an array of buffers per cell output chunk (2 dimentions array of buffers) // - connect cell chunk to corresponding buffers @@ -265,13 +281,13 @@ RTLIL::IdString generateSigSpecName(RTLIL::Module *module, const RTLIL::SigSpec // - when a buffer reaches capacity, switch to the next buffer // The capacity of the buffers might be larger than the limit in a given pass, // Recursion is used until all buffers capacity is under or at the limit. -void fixfanout(RTLIL::Design *design, RTLIL::Module *module, SigMap &sigmap, dict> &sig2CellsInFanout, - SigSpec sigToBuffer, int fanout, int limit, bool debug) +void fixfanout(RTLIL::Module *module, SigMap &sigmap, dict> &sig2CellsInFanout, + std::map> &usedBuffers, SigSpec sigToBuffer, int fanout, int limit, bool debug) { if (sigToBuffer.is_fully_const()) { return; } - std::string signame = generateSigSpecName(module, sigToBuffer).c_str(); + std::string signame = generateSigSpecName(sigToBuffer).c_str(); if (fanout <= limit) { if (debug) { std::cout << "Nothing to do for: " << signame << std::endl; @@ -310,10 +326,9 @@ void fixfanout(RTLIL::Design *design, RTLIL::Module *module, SigMap &sigmap, dic for (SigChunk chunk : sigToBuffer.chunks()) { std::vector> buffer_chunk_outputs; for (int i = 0; i < num_buffers; ++i) { - std::cout << "Name: " << signame + "_fbuf" + std::to_string(index_buffer) << std::endl; - RTLIL::Cell *buffer = module->addCell(signame + "_fbuf" + std::to_string(index_buffer), ID($pos)); + RTLIL::Cell *buffer = module->addCell(signame + "_fbuf" + std::to_string(index_buffer), ID($pos)); bufferActualFanout[buffer] = 0; - RTLIL::SigSpec buffer_output = module->addWire(signame + "_wbuf" + std::to_string(index_buffer) , chunk.size()); + RTLIL::SigSpec buffer_output = module->addWire(signame + "_wbuf" + std::to_string(index_buffer), chunk.size()); buffer->setPort(ID(A), chunk); buffer->setPort(ID(Y), sigmap(buffer_output)); buffer->fixup_parameters(); @@ -337,19 +352,22 @@ void fixfanout(RTLIL::Design *design, RTLIL::Module *module, SigMap &sigmap, dic for (Cell *fanoutcell : fanoutcells) { if (debug) std::cout << "\n CELL in fanout: " << fanoutcell->name.c_str() << "\n" << std::flush; + // For a given cell, if a buffer drives multiple inputs, use the same buffer for all connections to that cell for (auto &conn : fanoutcell->connections()) { IdString portName = conn.first; - RTLIL::SigSpec actual = sigmap(conn.second); + // RTLIL::SigSpec actual = sigmap(conn.second); + RTLIL::SigSpec actual = conn.second; if (fanoutcell->input(portName)) { if (actual.is_chunk()) { // Input of that cell is a chunk if (debug) std::cout << " IS A CHUNK" << std::endl; if (buffer_outputs.find(actual) != buffer_outputs.end()) { - if (debug) std::cout << " MATCH" << std::endl; + if (debug) + std::cout << " MATCH" << std::endl; // Input is one of the cell's outputs, its a match SigSpec newSig = updateToBuffer(bufferIndexes, buffer_outputs, sig2CellsInFanout, bufferActualFanout, - max_output_per_buffer, fanoutcell, actual, debug); + usedBuffers, max_output_per_buffer, fanoutcell, actual, debug); // Override the fanout cell's input with the buffer output fanoutcell->setPort(portName, newSig); } @@ -362,10 +380,11 @@ void fixfanout(RTLIL::Design *design, RTLIL::Module *module, SigMap &sigmap, dic std::vector newChunks; for (SigChunk chunk : actual.chunks()) { if (buffer_outputs.find(chunk) != buffer_outputs.end()) { - if (debug) std::cout << " MATCH" << std::endl; + if (debug) + std::cout << " MATCH" << std::endl; SigSpec newSig = updateToBuffer(bufferIndexes, buffer_outputs, sig2CellsInFanout, bufferActualFanout, - max_output_per_buffer, fanoutcell, chunk, debug); + usedBuffers, max_output_per_buffer, fanoutcell, chunk, debug); // Append the buffer's output in the chunk vector newChunks.push_back(newSig.as_chunk()); } else { @@ -375,7 +394,6 @@ void fixfanout(RTLIL::Design *design, RTLIL::Module *module, SigMap &sigmap, dic } // Override the fanout cell's input with the newly created chunk vector fanoutcell->setPort(portName, newChunks); - break; } } } @@ -390,7 +408,7 @@ void fixfanout(RTLIL::Design *design, RTLIL::Module *module, SigMap &sigmap, dic } else { // Recursively fix the fanout of the newly created buffers RTLIL::SigSpec sig = getCellOutputSigSpec(itr->first, sigmap); - fixfanout(design, module, sigmap, sig2CellsInFanout, sig, itr->second, limit, debug); + fixfanout(module, sigmap, sig2CellsInFanout, usedBuffers, sig, itr->second, limit, debug); } } } @@ -583,19 +601,21 @@ struct AnnotateCellFanout : public ScriptPass { { // Fix high fanout + std::map> usedBuffers; + SigMap sigmap(module); dict cellFanout; dict sigFanout; dict> sig2CellsInFanout; calculateFanout(module, sigmap, sig2CellsInFanout, cellFanout, sigFanout); - // Fix cells outputs with high fanout + // Fix cells outputs with high fanout for (auto itrCell : cellFanout) { Cell *cell = itrCell.first; int fanout = itrCell.second; if (limit > 0 && (fanout > limit)) { RTLIL::SigSpec cellOutSig = getCellOutputSigSpec(cell, sigmap); - fixfanout(design, module, sigmap, sig2CellsInFanout, cellOutSig, fanout, limit, debug); + fixfanout(module, sigmap, sig2CellsInFanout, usedBuffers, cellOutSig, fanout, limit, debug); fixedFanout = true; } else { // Add attribute with fanout info to every cell @@ -615,7 +635,7 @@ struct AnnotateCellFanout : public ScriptPass { } } for (auto sig : sigsToFix) { - fixfanout(design, module, sigmap, sig2CellsInFanout, sig.first, sig.second, limit, debug); + fixfanout(module, sigmap, sig2CellsInFanout, usedBuffers, sig.first, sig.second, limit, debug); fixedFanout = true; } } From a8fa936080d5e293ee4c702e3e683094c26a04d5 Mon Sep 17 00:00:00 2001 From: Alain Dargelas Date: Tue, 4 Mar 2025 09:20:31 -0800 Subject: [PATCH 16/32] simplify --- passes/silimate/annotate_cell_fanout.cc | 40 ++++++++++--------------- 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/passes/silimate/annotate_cell_fanout.cc b/passes/silimate/annotate_cell_fanout.cc index 345cd1cc0..1f7d9720c 100644 --- a/passes/silimate/annotate_cell_fanout.cc +++ b/passes/silimate/annotate_cell_fanout.cc @@ -219,23 +219,21 @@ RTLIL::SigSpec getCellOutputSigSpec(Cell *cell, SigMap &sigmap) SigSpec updateToBuffer(std::map &bufferIndexes, std::map>> &buffer_outputs, dict> &sig2CellsInFanout, std::map &bufferActualFanout, - std::map> &usedBuffers, int max_output_per_buffer, Cell *fanoutcell, SigSpec sigToReplace, + std::map &usedBuffers, int max_output_per_buffer, Cell *fanoutcell, SigSpec sigToReplace, bool debug) { if (debug) std::cout << " CHUNK, indexCurrentBuffer: " << bufferIndexes[sigToReplace] << " buffer_outputs " << buffer_outputs[sigToReplace].size() << std::endl; // Reuse cached result for a given cell; - std::map>::iterator itrCell = usedBuffers.find(fanoutcell); - if (itrCell != usedBuffers.end()) { - std::map::iterator itrBuffer = itrCell->second.find(sigToReplace); - if (itrBuffer != itrCell->second.end()) { - if (debug) - std::cout << "REUSE CACHE:" << fanoutcell->name.c_str() << " SIG: " << generateSigSpecName(sigToReplace).c_str() - << std::endl; - return itrBuffer->second; - } + std::map::iterator itrBuffer = usedBuffers.find(sigToReplace); + if (itrBuffer != usedBuffers.end()) { + if (debug) + std::cout << "REUSE CACHE:" << fanoutcell->name.c_str() << " SIG: " << generateSigSpecName(sigToReplace).c_str() + << std::endl; + return itrBuffer->second; } + // Retrieve the buffer information for that cell's chunk std::vector> &buf_info_vec = buffer_outputs[sigToReplace]; // Retrieve which buffer is getting filled @@ -262,14 +260,9 @@ SigSpec updateToBuffer(std::map &bufferIndexes, // Cache result if (debug) std::cout << "CACHE:" << fanoutcell->name.c_str() << " SIG: " << generateSigSpecName(sigToReplace).c_str() << " BY " - << generateSigSpecName(newSig).c_str() << std::endl; - if (itrCell == usedBuffers.end()) { - std::map tmpPair; - tmpPair.emplace(sigToReplace, newSig); - usedBuffers.emplace(fanoutcell, tmpPair); - } else { - itrCell->second.emplace(sigToReplace, newSig); - } + << generateSigSpecName(newSig).c_str() << std::endl; + usedBuffers.emplace(sigToReplace, newSig); + // Return buffer's output return newSig; } @@ -281,8 +274,7 @@ SigSpec updateToBuffer(std::map &bufferIndexes, // - when a buffer reaches capacity, switch to the next buffer // The capacity of the buffers might be larger than the limit in a given pass, // Recursion is used until all buffers capacity is under or at the limit. -void fixfanout(RTLIL::Module *module, SigMap &sigmap, dict> &sig2CellsInFanout, - std::map> &usedBuffers, SigSpec sigToBuffer, int fanout, int limit, bool debug) +void fixfanout(RTLIL::Module *module, SigMap &sigmap, dict> &sig2CellsInFanout, SigSpec sigToBuffer, int fanout, int limit, bool debug) { if (sigToBuffer.is_fully_const()) { return; @@ -353,6 +345,7 @@ void fixfanout(RTLIL::Module *module, SigMap &sigmap, dictname.c_str() << "\n" << std::flush; // For a given cell, if a buffer drives multiple inputs, use the same buffer for all connections to that cell + std::map usedBuffers; for (auto &conn : fanoutcell->connections()) { IdString portName = conn.first; // RTLIL::SigSpec actual = sigmap(conn.second); @@ -408,7 +401,7 @@ void fixfanout(RTLIL::Module *module, SigMap &sigmap, dictfirst, sigmap); - fixfanout(module, sigmap, sig2CellsInFanout, usedBuffers, sig, itr->second, limit, debug); + fixfanout(module, sigmap, sig2CellsInFanout, sig, itr->second, limit, debug); } } } @@ -601,7 +594,6 @@ struct AnnotateCellFanout : public ScriptPass { { // Fix high fanout - std::map> usedBuffers; SigMap sigmap(module); dict cellFanout; @@ -615,7 +607,7 @@ struct AnnotateCellFanout : public ScriptPass { int fanout = itrCell.second; if (limit > 0 && (fanout > limit)) { RTLIL::SigSpec cellOutSig = getCellOutputSigSpec(cell, sigmap); - fixfanout(module, sigmap, sig2CellsInFanout, usedBuffers, cellOutSig, fanout, limit, debug); + fixfanout(module, sigmap, sig2CellsInFanout, cellOutSig, fanout, limit, debug); fixedFanout = true; } else { // Add attribute with fanout info to every cell @@ -635,7 +627,7 @@ struct AnnotateCellFanout : public ScriptPass { } } for (auto sig : sigsToFix) { - fixfanout(module, sigmap, sig2CellsInFanout, usedBuffers, sig.first, sig.second, limit, debug); + fixfanout(module, sigmap, sig2CellsInFanout, sig.first, sig.second, limit, debug); fixedFanout = true; } } From fd2ac3ad8dee11630885e095cc07b0827e12f832 Mon Sep 17 00:00:00 2001 From: Alain Dargelas Date: Tue, 4 Mar 2025 09:22:55 -0800 Subject: [PATCH 17/32] simplify --- passes/silimate/annotate_cell_fanout.cc | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/passes/silimate/annotate_cell_fanout.cc b/passes/silimate/annotate_cell_fanout.cc index 1f7d9720c..71f3ccfca 100644 --- a/passes/silimate/annotate_cell_fanout.cc +++ b/passes/silimate/annotate_cell_fanout.cc @@ -217,7 +217,7 @@ RTLIL::SigSpec getCellOutputSigSpec(Cell *cell, SigMap &sigmap) // Get new output signal for a given signal, used all datastructures with change to buffer SigSpec updateToBuffer(std::map &bufferIndexes, - std::map>> &buffer_outputs, + std::map>> &buffer_outputs, dict> &sig2CellsInFanout, std::map &bufferActualFanout, std::map &usedBuffers, int max_output_per_buffer, Cell *fanoutcell, SigSpec sigToReplace, bool debug) @@ -235,12 +235,12 @@ SigSpec updateToBuffer(std::map &bufferIndexes, } // Retrieve the buffer information for that cell's chunk - std::vector> &buf_info_vec = buffer_outputs[sigToReplace]; + std::vector> &buf_info_vec = buffer_outputs[sigToReplace]; // Retrieve which buffer is getting filled int bufferIndex = bufferIndexes[sigToReplace]; - std::tuple &buf_info = buf_info_vec[bufferIndex]; - SigSpec newSig = std::get<0>(buf_info); - Cell *newBuf = std::get<1>(buf_info); + std::pair &buf_info = buf_info_vec[bufferIndex]; + SigSpec newSig = buf_info.first; + Cell *newBuf = buf_info.second; // Keep track of fanout map information for recursive calls sig2CellsInFanout[newSig].insert(fanoutcell); // Increment buffer capacity @@ -309,14 +309,14 @@ void fixfanout(RTLIL::Module *module, SigMap &sigmap, dict bufferActualFanout; // Array of buffers (The buffer output signal and the buffer cell) per cell output chunks - std::map>> buffer_outputs; + std::map>> buffer_outputs; // Keep track of which buffer in the array is getting filled for a given chunk std::map bufferIndexes; // Create new buffers and new wires int index_buffer = 0; for (SigChunk chunk : sigToBuffer.chunks()) { - std::vector> buffer_chunk_outputs; + std::vector> buffer_chunk_outputs; for (int i = 0; i < num_buffers; ++i) { RTLIL::Cell *buffer = module->addCell(signame + "_fbuf" + std::to_string(index_buffer), ID($pos)); bufferActualFanout[buffer] = 0; @@ -324,7 +324,7 @@ void fixfanout(RTLIL::Module *module, SigMap &sigmap, dictsetPort(ID(A), chunk); buffer->setPort(ID(Y), sigmap(buffer_output)); buffer->fixup_parameters(); - buffer_chunk_outputs.push_back(std::make_tuple(buffer_output, buffer)); // Old - New + buffer_chunk_outputs.push_back(std::make_pair(buffer_output, buffer)); // Old - New bufferIndexes[chunk] = 0; index_buffer++; } From 104d39057ec356fb39d21fd1db19a105e352da69 Mon Sep 17 00:00:00 2001 From: Alain Dargelas Date: Tue, 4 Mar 2025 10:31:20 -0800 Subject: [PATCH 18/32] unique names --- passes/silimate/annotate_cell_fanout.cc | 88 +++++++++++++++---------- 1 file changed, 52 insertions(+), 36 deletions(-) diff --git a/passes/silimate/annotate_cell_fanout.cc b/passes/silimate/annotate_cell_fanout.cc index 71f3ccfca..c93eb2292 100644 --- a/passes/silimate/annotate_cell_fanout.cc +++ b/passes/silimate/annotate_cell_fanout.cc @@ -5,14 +5,24 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -RTLIL::IdString generateSigSpecName(const RTLIL::SigSpec &sigspec) +// Return substring up to a delimiter or full string if not found +std::string substringuntil(const std::string &str, char delimiter) +{ + size_t pos = str.find(delimiter); + if (pos != std::string::npos) { + return str.substr(0, pos); + } else { + return str; + } +} + +// Generate a human readable name for a sigspec, uniquify if necessary +RTLIL::IdString generateSigSpecName(Module* module, const RTLIL::SigSpec &sigspec, bool makeUnique = false, std::string postfix = "", bool cellName = false) { if (sigspec.empty()) { return RTLIL::IdString(); // Empty SigSpec, return empty IdString } - std::stringstream ss; - if (sigspec.is_wire()) { // Handle wires ss << sigspec.as_wire()->name.str(); @@ -22,33 +32,47 @@ RTLIL::IdString generateSigSpecName(const RTLIL::SigSpec &sigspec) if (sigspec[0].wire->width != 1) { ss << "[" << sigspec[0].offset << "]"; } - } else if (sigspec.is_chunk()) { - // Handle slices - RTLIL::Wire *parent_wire = sigspec[0].wire; - SigChunk chunk = sigspec.as_chunk(); - if (parent_wire) { - ss << parent_wire->name.str() << "[" << chunk.offset + chunk.width - 1 << ":" << chunk.offset << "]"; - } } else if (!sigspec.is_chunk()) { // Handle vector of chunks int max = 0; int min = INT_MAX; RTLIL::Wire *parent_wire = sigspec[0].wire; + int width = 0; for (SigChunk chunk : sigspec.chunks()) { + width += chunk.width; max = std::max(max, chunk.offset); min = std::min(min, chunk.offset); } + if (max == 0) { + max = width - 1; + } if (parent_wire) { - ss << parent_wire->name.str() << "[" << max << ":" << min << "]"; + if (max != min) { + ss << substringuntil(parent_wire->name.str(), '[') << "[" << max << ":" << min << "]"; + } else { + ss << parent_wire->name.str(); + } } else { ss << "\\sigspec_[" << max << ":" << min << "]"; } - } else { - // Handle other cases (e.g., constants) - ss << "\\sigspec_"; + } + ss << postfix; + if (makeUnique) { + RTLIL::IdString base_name = RTLIL::IdString(ss.str()); + // Ensure uniqueness + int counter = 0; + if (cellName) { + while (module->cells_.count(RTLIL::IdString(ss.str()))) { + ss.str(""); + ss << base_name.str() << "_" << counter++; + } + } else { + while (module->wires_.count(RTLIL::IdString(ss.str()))) { + ss.str(""); + ss << base_name.str() << "_" << counter++; + } + } } - - RTLIL::IdString base_name = RTLIL::IdString(ss.str()); return RTLIL::IdString(ss.str()); } @@ -111,6 +135,7 @@ void lhs2rhs_rhs2lhs(RTLIL::Module *module, SigMap &sigmap, dict &bufferIndexes, +SigSpec updateToBuffer(Module* module, std::map &bufferIndexes, std::map>> &buffer_outputs, dict> &sig2CellsInFanout, std::map &bufferActualFanout, std::map &usedBuffers, int max_output_per_buffer, Cell *fanoutcell, SigSpec sigToReplace, @@ -229,7 +254,7 @@ SigSpec updateToBuffer(std::map &bufferIndexes, std::map::iterator itrBuffer = usedBuffers.find(sigToReplace); if (itrBuffer != usedBuffers.end()) { if (debug) - std::cout << "REUSE CACHE:" << fanoutcell->name.c_str() << " SIG: " << generateSigSpecName(sigToReplace).c_str() + std::cout << "REUSE CACHE:" << fanoutcell->name.c_str() << " SIG: " << generateSigSpecName(module, sigToReplace).c_str() << std::endl; return itrBuffer->second; } @@ -259,8 +284,8 @@ SigSpec updateToBuffer(std::map &bufferIndexes, } // Cache result if (debug) - std::cout << "CACHE:" << fanoutcell->name.c_str() << " SIG: " << generateSigSpecName(sigToReplace).c_str() << " BY " - << generateSigSpecName(newSig).c_str() << std::endl; + std::cout << "CACHE:" << fanoutcell->name.c_str() << " SIG: " << generateSigSpecName(module, sigToReplace).c_str() << " BY " + << generateSigSpecName(module, newSig).c_str() << std::endl; usedBuffers.emplace(sigToReplace, newSig); // Return buffer's output @@ -279,7 +304,7 @@ void fixfanout(RTLIL::Module *module, SigMap &sigmap, dict> buffer_chunk_outputs; for (int i = 0; i < num_buffers; ++i) { - RTLIL::Cell *buffer = module->addCell(signame + "_fbuf" + std::to_string(index_buffer), ID($pos)); + std::string wireName = generateSigSpecName(module, sigToBuffer, true, "_wbuf" + std::to_string(index_buffer)).c_str(); + std::string cellName = generateSigSpecName(module, sigToBuffer, true, "_fbuf" + std::to_string(index_buffer), true).c_str(); + RTLIL::Cell *buffer = module->addCell(cellName, ID($pos)); bufferActualFanout[buffer] = 0; - RTLIL::SigSpec buffer_output = module->addWire(signame + "_wbuf" + std::to_string(index_buffer), chunk.size()); + RTLIL::SigSpec buffer_output = module->addWire(wireName, chunk.size()); buffer->setPort(ID(A), chunk); buffer->setPort(ID(Y), sigmap(buffer_output)); buffer->fixup_parameters(); @@ -359,7 +386,7 @@ void fixfanout(RTLIL::Module *module, SigMap &sigmap, dictsetPort(portName, newSig); @@ -376,7 +403,7 @@ void fixfanout(RTLIL::Module *module, SigMap &sigmap, dict &netsToSplitS, RTLIL::SigSpec &sigToSplit, bool formalFriendly, bool inputPort = false) { Wire *parentWire = getParentWire(sigToSplit); From 3e25ff70eeb1aabab5abdcec68d1d1daa7c95929 Mon Sep 17 00:00:00 2001 From: Alain Dargelas Date: Tue, 4 Mar 2025 10:52:46 -0800 Subject: [PATCH 19/32] input fanout --- passes/silimate/annotate_cell_fanout.cc | 28 +++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/passes/silimate/annotate_cell_fanout.cc b/passes/silimate/annotate_cell_fanout.cc index c93eb2292..549a84b4d 100644 --- a/passes/silimate/annotate_cell_fanout.cc +++ b/passes/silimate/annotate_cell_fanout.cc @@ -488,6 +488,24 @@ void calculateFanout(RTLIL::Module *module, SigMap &sigmap, dictwires()) { + if (wire->port_input) { + SigSpec inp = sigmap(wire); + int fanout = sigFanout[inp]; + if (fanout == 0) { + int max = 0; + for (int i = 0; i < inp.size(); i++) { + SigSpec bit_sig = inp.extract(i, 1); + int fa = sigFanout[bit_sig]; + max = std::max(max, fa); + } + sigFanout[inp] = max; + } + } + } + + } void splitNet(Design *design, std::set &netsToSplitS, RTLIL::SigSpec &sigToSplit, bool formalFriendly, bool inputPort = false) @@ -639,6 +657,8 @@ struct AnnotateCellFanout : public ScriptPass { int fanout = sigFanout[inp]; if (limit > 0 && (fanout > limit)) { sigsToFix.emplace(inp, fanout); + } else { + wire->set_string_attribute("$FANOUT", std::to_string(fanout)); } } } @@ -661,6 +681,14 @@ struct AnnotateCellFanout : public ScriptPass { // Add attribute with fanout info to every cell cell->set_string_attribute("$FANOUT", std::to_string(fanout)); } + for (Wire *wire : module->wires()) { + if (wire->port_input) { + SigSpec inp = sigmap(wire); + int fanout = sigFanout[inp]; + // Add attribute with fanout info to every input port + wire->set_string_attribute("$FANOUT", std::to_string(fanout)); + } + } } } From 864850ab51109336852418df3a8d97614a34ea91 Mon Sep 17 00:00:00 2001 From: Alain Dargelas Date: Tue, 4 Mar 2025 11:09:09 -0800 Subject: [PATCH 20/32] speedup --- passes/silimate/annotate_cell_fanout.cc | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/passes/silimate/annotate_cell_fanout.cc b/passes/silimate/annotate_cell_fanout.cc index 549a84b4d..3b9875d39 100644 --- a/passes/silimate/annotate_cell_fanout.cc +++ b/passes/silimate/annotate_cell_fanout.cc @@ -517,6 +517,8 @@ void splitNet(Design *design, std::set &netsToSplitS, RTLIL::SigSpe if (parent == "") { return; } + if (parentWire->width == 1) + return; parent = substringuntil(parent, '['); if (netsToSplitS.find(parent) == netsToSplitS.end()) { netsToSplitS.insert(parent); @@ -604,8 +606,14 @@ struct AnnotateCellFanout : public ScriptPass { Cell *cell = itrCell.first; int fanout = itrCell.second; if (limit > 0 && (fanout > limit)) { - RTLIL::SigSpec cellOutSig = getCellOutputSigSpec(cell, sigmap); - splitNet(design, netsToSplitS, cellOutSig, formalFriendly); + for (auto &conn : cell->connections()) { + IdString portName = conn.first; + RTLIL::SigSpec actual = conn.second; + if (cell->output(portName)) { + RTLIL::SigSpec cellOutSig = sigmap(actual); + splitNet(design, netsToSplitS, cellOutSig, formalFriendly); + } + } } } @@ -640,8 +648,14 @@ struct AnnotateCellFanout : public ScriptPass { Cell *cell = itrCell.first; int fanout = itrCell.second; if (limit > 0 && (fanout > limit)) { - RTLIL::SigSpec cellOutSig = getCellOutputSigSpec(cell, sigmap); - fixfanout(module, sigmap, sig2CellsInFanout, cellOutSig, fanout, limit, debug); + for (auto &conn : cell->connections()) { + IdString portName = conn.first; + RTLIL::SigSpec actual = conn.second; + if (cell->output(portName)) { + RTLIL::SigSpec cellOutSig = sigmap(actual); + fixfanout(module, sigmap, sig2CellsInFanout, cellOutSig, fanout, limit, debug); + } + } fixedFanout = true; } else { // Add attribute with fanout info to every cell From 1027a968605290d4d10726f86abef4263c7ee4d1 Mon Sep 17 00:00:00 2001 From: Alain Dargelas Date: Tue, 4 Mar 2025 11:43:12 -0800 Subject: [PATCH 21/32] inputs option --- passes/silimate/annotate_cell_fanout.cc | 77 +++++++++++++++---------- 1 file changed, 45 insertions(+), 32 deletions(-) diff --git a/passes/silimate/annotate_cell_fanout.cc b/passes/silimate/annotate_cell_fanout.cc index 3b9875d39..c73bf28f6 100644 --- a/passes/silimate/annotate_cell_fanout.cc +++ b/passes/silimate/annotate_cell_fanout.cc @@ -17,7 +17,8 @@ std::string substringuntil(const std::string &str, char delimiter) } // Generate a human readable name for a sigspec, uniquify if necessary -RTLIL::IdString generateSigSpecName(Module* module, const RTLIL::SigSpec &sigspec, bool makeUnique = false, std::string postfix = "", bool cellName = false) +RTLIL::IdString generateSigSpecName(Module *module, const RTLIL::SigSpec &sigspec, bool makeUnique = false, std::string postfix = "", + bool cellName = false) { if (sigspec.empty()) { return RTLIL::IdString(); // Empty SigSpec, return empty IdString @@ -55,7 +56,7 @@ RTLIL::IdString generateSigSpecName(Module* module, const RTLIL::SigSpec &sigspe } else { ss << "\\sigspec_[" << max << ":" << min << "]"; } - } + } ss << postfix; if (makeUnique) { RTLIL::IdString base_name = RTLIL::IdString(ss.str()); @@ -241,11 +242,10 @@ RTLIL::SigSpec getCellOutputSigSpec(Cell *cell, SigMap &sigmap) } // Get new output signal for a given signal, used all datastructures with change to buffer -SigSpec updateToBuffer(Module* module, std::map &bufferIndexes, +SigSpec updateToBuffer(Module *module, std::map &bufferIndexes, std::map>> &buffer_outputs, dict> &sig2CellsInFanout, std::map &bufferActualFanout, - std::map &usedBuffers, int max_output_per_buffer, Cell *fanoutcell, SigSpec sigToReplace, - bool debug) + std::map &usedBuffers, int max_output_per_buffer, Cell *fanoutcell, SigSpec sigToReplace, bool debug) { if (debug) std::cout << " CHUNK, indexCurrentBuffer: " << bufferIndexes[sigToReplace] << " buffer_outputs " @@ -255,10 +255,10 @@ SigSpec updateToBuffer(Module* module, std::map &bufferIndexes, if (itrBuffer != usedBuffers.end()) { if (debug) std::cout << "REUSE CACHE:" << fanoutcell->name.c_str() << " SIG: " << generateSigSpecName(module, sigToReplace).c_str() - << std::endl; + << std::endl; return itrBuffer->second; } - + // Retrieve the buffer information for that cell's chunk std::vector> &buf_info_vec = buffer_outputs[sigToReplace]; // Retrieve which buffer is getting filled @@ -285,9 +285,9 @@ SigSpec updateToBuffer(Module* module, std::map &bufferIndexes, // Cache result if (debug) std::cout << "CACHE:" << fanoutcell->name.c_str() << " SIG: " << generateSigSpecName(module, sigToReplace).c_str() << " BY " - << generateSigSpecName(module, newSig).c_str() << std::endl; + << generateSigSpecName(module, newSig).c_str() << std::endl; usedBuffers.emplace(sigToReplace, newSig); - + // Return buffer's output return newSig; } @@ -299,7 +299,8 @@ SigSpec updateToBuffer(Module* module, std::map &bufferIndexes, // - when a buffer reaches capacity, switch to the next buffer // The capacity of the buffers might be larger than the limit in a given pass, // Recursion is used until all buffers capacity is under or at the limit. -void fixfanout(RTLIL::Module *module, SigMap &sigmap, dict> &sig2CellsInFanout, SigSpec sigToBuffer, int fanout, int limit, bool debug) +void fixfanout(RTLIL::Module *module, SigMap &sigmap, dict> &sig2CellsInFanout, SigSpec sigToBuffer, int fanout, + int limit, bool debug) { if (sigToBuffer.is_fully_const()) { return; @@ -386,8 +387,9 @@ void fixfanout(RTLIL::Module *module, SigMap &sigmap, dictsetPort(portName, newSig); } @@ -402,9 +404,9 @@ void fixfanout(RTLIL::Module *module, SigMap &sigmap, dictwires()) { if (wire->port_input) { SigSpec inp = sigmap(wire); @@ -504,8 +507,6 @@ void calculateFanout(RTLIL::Module *module, SigMap &sigmap, dict &netsToSplitS, RTLIL::SigSpec &sigToSplit, bool formalFriendly, bool inputPort = false) @@ -552,6 +553,8 @@ struct AnnotateCellFanout : public ScriptPass { log(" Limits the fanout by inserting balanced buffer trees.\n"); log(" -formal\n"); log(" For formal verification to pass, will prevent splitnets passes on ports, even if they have large fanout.\n"); + log(" -inputs\n"); + log(" Fix module inputs fanout.\n"); log(" -debug\n"); log(" Debug trace.\n"); log("\n"); @@ -560,6 +563,7 @@ struct AnnotateCellFanout : public ScriptPass { { int limit = -1; bool formalFriendly = false; + bool inputs = false; bool debug = false; if (design == nullptr) { log_error("No design object\n"); @@ -582,6 +586,10 @@ struct AnnotateCellFanout : public ScriptPass { formalFriendly = true; continue; } + if (args[argidx] == "-inputs") { + inputs = true; + continue; + } break; } extra_args(args, argidx, design); @@ -611,32 +619,33 @@ struct AnnotateCellFanout : public ScriptPass { RTLIL::SigSpec actual = conn.second; if (cell->output(portName)) { RTLIL::SigSpec cellOutSig = sigmap(actual); - splitNet(design, netsToSplitS, cellOutSig, formalFriendly); + splitNet(design, netsToSplitS, cellOutSig, formalFriendly); } } } } - // Split module input nets with high fanout - std::set wiresToSplit; - for (Wire *wire : module->wires()) { - if (wire->port_input) { - SigSpec inp = sigmap(wire); - int fanout = sigFanout[inp]; - if (limit > 0 && (fanout > limit)) { - wiresToSplit.insert(wire); + if (inputs) { + // Split module input nets with high fanout + std::set wiresToSplit; + for (Wire *wire : module->wires()) { + if (wire->port_input) { + SigSpec inp = sigmap(wire); + int fanout = sigFanout[inp]; + if (limit > 0 && (fanout > limit)) { + wiresToSplit.insert(wire); + } } } - } - for (Wire *wire : wiresToSplit) { - SigSpec inp = sigmap(wire); - splitNet(design, netsToSplitS, inp, formalFriendly, true); + for (Wire *wire : wiresToSplit) { + SigSpec inp = sigmap(wire); + splitNet(design, netsToSplitS, inp, formalFriendly, true); + } } } { // Fix high fanout - SigMap sigmap(module); dict cellFanout; dict sigFanout; @@ -670,7 +679,11 @@ struct AnnotateCellFanout : public ScriptPass { SigSpec inp = sigmap(wire); int fanout = sigFanout[inp]; if (limit > 0 && (fanout > limit)) { - sigsToFix.emplace(inp, fanout); + if (inputs) { + sigsToFix.emplace(inp, fanout); + } else { + wire->set_string_attribute("$FANOUT", std::to_string(fanout)); + } } else { wire->set_string_attribute("$FANOUT", std::to_string(fanout)); } From 7df2a8eb8e9a412914efae4c57b74f2dd4a8e67c Mon Sep 17 00:00:00 2001 From: Alain Dargelas Date: Tue, 4 Mar 2025 14:32:44 -0800 Subject: [PATCH 22/32] splinets fencing --- passes/silimate/annotate_cell_fanout.cc | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/passes/silimate/annotate_cell_fanout.cc b/passes/silimate/annotate_cell_fanout.cc index c73bf28f6..06cec687c 100644 --- a/passes/silimate/annotate_cell_fanout.cc +++ b/passes/silimate/annotate_cell_fanout.cc @@ -509,7 +509,7 @@ void calculateFanout(RTLIL::Module *module, SigMap &sigmap, dict &netsToSplitS, RTLIL::SigSpec &sigToSplit, bool formalFriendly, bool inputPort = false) +void splitNet(Design *design, std::set &netsToSplitS, RTLIL::SigSpec &sigToSplit, bool formalFriendly, bool debug, bool inputPort = false) { Wire *parentWire = getParentWire(sigToSplit); if (!parentWire) @@ -521,19 +521,25 @@ void splitNet(Design *design, std::set &netsToSplitS, RTLIL::SigSpe if (parentWire->width == 1) return; parent = substringuntil(parent, '['); + if (debug) { + std::cout << "splitnets: " << parent << std::endl; + } if (netsToSplitS.find(parent) == netsToSplitS.end()) { netsToSplitS.insert(parent); // Splitnets has to be invoke with individual nets. Sending a bunch of nets as selection, // selects more than required (bug in selection/splitnets). - Pass::call(design, "splitnets w:" + parent); // Wire + if ((!parentWire->port_input) && (!parentWire->port_output)) + Pass::call(design, "splitnets w:" + parent); // Wire if (!formalFriendly) { // Formal verification does not like ports to be split. // This option will prevent some buffering to happen on high fanout input/output ports, // but it will make formal happy. if (inputPort) { - Pass::call(design, "splitnets -ports_only i:" + parent); // Input port + if (parentWire->port_input) + Pass::call(design, "splitnets -ports_only i:" + parent); // Input port } else { - Pass::call(design, "splitnets -ports_only o:" + parent); // Output port + if (parentWire->port_output) + Pass::call(design, "splitnets -ports_only o:" + parent); // Output port } } } @@ -609,7 +615,7 @@ struct AnnotateCellFanout : public ScriptPass { calculateFanout(module, sigmap, sig2CellsInFanout, cellFanout, sigFanout); std::set netsToSplitS; - // Split cells' output nets with high fanout + // Split cells output nets with high fanout for (auto itrCell : cellFanout) { Cell *cell = itrCell.first; int fanout = itrCell.second; @@ -619,7 +625,7 @@ struct AnnotateCellFanout : public ScriptPass { RTLIL::SigSpec actual = conn.second; if (cell->output(portName)) { RTLIL::SigSpec cellOutSig = sigmap(actual); - splitNet(design, netsToSplitS, cellOutSig, formalFriendly); + splitNet(design, netsToSplitS, cellOutSig, formalFriendly, debug); } } } @@ -639,7 +645,7 @@ struct AnnotateCellFanout : public ScriptPass { } for (Wire *wire : wiresToSplit) { SigSpec inp = sigmap(wire); - splitNet(design, netsToSplitS, inp, formalFriendly, true); + splitNet(design, netsToSplitS, inp, formalFriendly, debug, true); } } } From 4035a24e4c51906a745f424689acc39ebd46c1a0 Mon Sep 17 00:00:00 2001 From: Alain Dargelas Date: Tue, 4 Mar 2025 15:06:58 -0800 Subject: [PATCH 23/32] bulk splitnets --- passes/silimate/annotate_cell_fanout.cc | 83 ++++++++++++++----------- 1 file changed, 46 insertions(+), 37 deletions(-) diff --git a/passes/silimate/annotate_cell_fanout.cc b/passes/silimate/annotate_cell_fanout.cc index 06cec687c..18864d973 100644 --- a/passes/silimate/annotate_cell_fanout.cc +++ b/passes/silimate/annotate_cell_fanout.cc @@ -509,40 +509,50 @@ void calculateFanout(RTLIL::Module *module, SigMap &sigmap, dict &netsToSplitS, RTLIL::SigSpec &sigToSplit, bool formalFriendly, bool debug, bool inputPort = false) +void splitNets(Design *design, std::set &netsToSplitS, std::vector &sigsToSplit, bool formalFriendly, bool debug, + bool inputPort = false) { - Wire *parentWire = getParentWire(sigToSplit); - if (!parentWire) - return; - std::string parent = parentWire->name.c_str(); - if (parent == "") { - return; - } - if (parentWire->width == 1) - return; - parent = substringuntil(parent, '['); - if (debug) { - std::cout << "splitnets: " << parent << std::endl; - } - if (netsToSplitS.find(parent) == netsToSplitS.end()) { - netsToSplitS.insert(parent); - // Splitnets has to be invoke with individual nets. Sending a bunch of nets as selection, - // selects more than required (bug in selection/splitnets). - if ((!parentWire->port_input) && (!parentWire->port_output)) - Pass::call(design, "splitnets w:" + parent); // Wire - if (!formalFriendly) { - // Formal verification does not like ports to be split. - // This option will prevent some buffering to happen on high fanout input/output ports, - // but it will make formal happy. - if (inputPort) { - if (parentWire->port_input) - Pass::call(design, "splitnets -ports_only i:" + parent); // Input port - } else { - if (parentWire->port_output) - Pass::call(design, "splitnets -ports_only o:" + parent); // Output port + std::string wires; + std::string inputs; + std::string outputs; + for (RTLIL::SigSpec sigToSplit : sigsToSplit) { + Wire *parentWire = getParentWire(sigToSplit); + if (!parentWire) + continue; + std::string parent = parentWire->name.c_str(); + if (parent == "") { + continue; + } + if (parentWire->width == 1) + continue; + parent = substringuntil(parent, '['); + if (netsToSplitS.find(parent) == netsToSplitS.end()) { + netsToSplitS.insert(parent); + if (debug) { + std::cout << "splitnets: " << parent << std::endl; + } + if ((!parentWire->port_input) && (!parentWire->port_output)) + wires += " w:" + parent; + if (!formalFriendly) { + // Formal verification does not like ports to be split. + // This option will prevent some buffering to happen on high fanout input/output ports, + // but it will make formal happy. + if (inputPort) { + if (parentWire->port_input) + inputs += " i:" + parent; + } else { + if (parentWire->port_output) + outputs += " o:" + parent; + } } } } + if (!wires.empty()) { + Pass::call(design, "splitnets" + wires); // Wire + } + if (!inputs.empty() || !outputs.empty()) { + Pass::call(design, "splitnets -ports_only" + inputs + outputs); // Input port + } } struct AnnotateCellFanout : public ScriptPass { @@ -616,6 +626,7 @@ struct AnnotateCellFanout : public ScriptPass { std::set netsToSplitS; // Split cells output nets with high fanout + std::vector cellOutputsToSplit; for (auto itrCell : cellFanout) { Cell *cell = itrCell.first; int fanout = itrCell.second; @@ -625,28 +636,26 @@ struct AnnotateCellFanout : public ScriptPass { RTLIL::SigSpec actual = conn.second; if (cell->output(portName)) { RTLIL::SigSpec cellOutSig = sigmap(actual); - splitNet(design, netsToSplitS, cellOutSig, formalFriendly, debug); + cellOutputsToSplit.push_back(cellOutSig); } } } } + splitNets(design, netsToSplitS, cellOutputsToSplit, formalFriendly, debug); if (inputs) { // Split module input nets with high fanout - std::set wiresToSplit; + std::vector wiresToSplit; for (Wire *wire : module->wires()) { if (wire->port_input) { SigSpec inp = sigmap(wire); int fanout = sigFanout[inp]; if (limit > 0 && (fanout > limit)) { - wiresToSplit.insert(wire); + wiresToSplit.push_back(inp); } } } - for (Wire *wire : wiresToSplit) { - SigSpec inp = sigmap(wire); - splitNet(design, netsToSplitS, inp, formalFriendly, debug, true); - } + splitNets(design, netsToSplitS, wiresToSplit, formalFriendly, debug, true); } } From 9f36d9dea9845ac8bafb7ad6e2f9759675b3f504 Mon Sep 17 00:00:00 2001 From: Alain Dargelas Date: Tue, 4 Mar 2025 15:52:10 -0800 Subject: [PATCH 24/32] comments --- passes/silimate/annotate_cell_fanout.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/passes/silimate/annotate_cell_fanout.cc b/passes/silimate/annotate_cell_fanout.cc index 18864d973..180d041ec 100644 --- a/passes/silimate/annotate_cell_fanout.cc +++ b/passes/silimate/annotate_cell_fanout.cc @@ -16,7 +16,7 @@ std::string substringuntil(const std::string &str, char delimiter) } } -// Generate a human readable name for a sigspec, uniquify if necessary +// Generate a meaningful name for a sigspec, uniquify if necessary RTLIL::IdString generateSigSpecName(Module *module, const RTLIL::SigSpec &sigspec, bool makeUnique = false, std::string postfix = "", bool cellName = false) { @@ -227,6 +227,7 @@ void removeBuffer(Module *module, SigMap &sigmap, std::set &fanoutcells, module->remove({bufferOutSig.as_wire()}); } +// Returns the first output (sigmaped sigspec) of a cell RTLIL::SigSpec getCellOutputSigSpec(Cell *cell, SigMap &sigmap) { RTLIL::SigSpec cellOutSig; @@ -435,6 +436,7 @@ void fixfanout(RTLIL::Module *module, SigMap &sigmap, dict> &sig2CellsInFanout, dict &cellFanout, dict &sigFanout) { @@ -509,6 +511,7 @@ void calculateFanout(RTLIL::Module *module, SigMap &sigmap, dict &netsToSplitS, std::vector &sigsToSplit, bool formalFriendly, bool debug, bool inputPort = false) { From bc05f4feadbd04952486908bc831e6ebfe3b1dc6 Mon Sep 17 00:00:00 2001 From: Alain Dargelas Date: Wed, 5 Mar 2025 09:40:26 -0800 Subject: [PATCH 25/32] Use buf instead of pos --- passes/silimate/annotate_cell_fanout.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/passes/silimate/annotate_cell_fanout.cc b/passes/silimate/annotate_cell_fanout.cc index 180d041ec..2f6034fa9 100644 --- a/passes/silimate/annotate_cell_fanout.cc +++ b/passes/silimate/annotate_cell_fanout.cc @@ -347,7 +347,7 @@ void fixfanout(RTLIL::Module *module, SigMap &sigmap, dictaddCell(cellName, ID($pos)); + RTLIL::Cell *buffer = module->addCell(cellName, ID($buf)); bufferActualFanout[buffer] = 0; RTLIL::SigSpec buffer_output = module->addWire(wireName, chunk.size()); buffer->setPort(ID(A), chunk); From 7d46ab08d57e3d66e8f6b3522285024c58bce513 Mon Sep 17 00:00:00 2001 From: Alain Dargelas Date: Wed, 5 Mar 2025 10:28:49 -0800 Subject: [PATCH 26/32] clocks, resets filters --- passes/silimate/annotate_cell_fanout.cc | 50 ++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/passes/silimate/annotate_cell_fanout.cc b/passes/silimate/annotate_cell_fanout.cc index 2f6034fa9..886be5f04 100644 --- a/passes/silimate/annotate_cell_fanout.cc +++ b/passes/silimate/annotate_cell_fanout.cc @@ -436,7 +436,7 @@ void fixfanout(RTLIL::Module *module, SigMap &sigmap, dict> &sig2CellsInFanout, dict &cellFanout, dict &sigFanout) { @@ -574,6 +574,10 @@ struct AnnotateCellFanout : public ScriptPass { log(" For formal verification to pass, will prevent splitnets passes on ports, even if they have large fanout.\n"); log(" -inputs\n"); log(" Fix module inputs fanout.\n"); + log(" -clocks\n"); + log(" Fix module clocks fanout.\n"); + log(" -resets\n"); + log(" Fix module resets fanout.\n"); log(" -debug\n"); log(" Debug trace.\n"); log("\n"); @@ -582,7 +586,9 @@ struct AnnotateCellFanout : public ScriptPass { { int limit = -1; bool formalFriendly = false; - bool inputs = false; + bool buffer_inputs = false; + bool buffer_clocks = false; + bool buffer_resets = false; bool debug = false; if (design == nullptr) { log_error("No design object\n"); @@ -606,7 +612,15 @@ struct AnnotateCellFanout : public ScriptPass { continue; } if (args[argidx] == "-inputs") { - inputs = true; + buffer_inputs = true; + continue; + } + if (args[argidx] == "-clocks") { + buffer_clocks = true; + continue; + } + if (args[argidx] == "-resets") { + buffer_resets = true; continue; } break; @@ -646,7 +660,7 @@ struct AnnotateCellFanout : public ScriptPass { } splitNets(design, netsToSplitS, cellOutputsToSplit, formalFriendly, debug); - if (inputs) { + if (buffer_inputs) { // Split module input nets with high fanout std::vector wiresToSplit; for (Wire *wire : module->wires()) { @@ -690,14 +704,40 @@ struct AnnotateCellFanout : public ScriptPass { } } + // Mark clocks, resets + std::set clock_sigs; + std::set reset_sigs; + for (auto cell : module->selected_cells()) { + if (cell->type.in(ID($dff), ID($dffe), ID($dffsr), ID($dffsre), ID($adff), ID($adffe), ID($aldff), + ID($aldffe), ID($sdff), ID($sdffe), ID($sdffce), ID($fsm), ID($memrd), ID($memrd_v2), + ID($memwr), ID($memwr_v2))) { + // Check for clock input connection + if (cell->hasPort(ID(CLK))) { + RTLIL::SigSpec clk_sig = sigmap(cell->getPort(ID(CLK))); + clock_sigs.insert(clk_sig); + } + if (cell->hasPort(ID(RST))) { + RTLIL::SigSpec clk_sig = sigmap(cell->getPort(ID(RST))); + reset_sigs.insert(clk_sig); + } + if (cell->hasPort(ID(ARST))) { + RTLIL::SigSpec clk_sig = sigmap(cell->getPort(ID(ARST))); + reset_sigs.insert(clk_sig); + } + } + } + // Fix module input nets with high fanout std::map sigsToFix; for (Wire *wire : module->wires()) { if (wire->port_input) { SigSpec inp = sigmap(wire); + bool is_clock = clock_sigs.find(inp) != clock_sigs.end(); + bool is_reset = reset_sigs.find(inp) != reset_sigs.end(); + bool filter_sig = (is_clock && !buffer_clocks) || (is_reset && !buffer_resets); int fanout = sigFanout[inp]; if (limit > 0 && (fanout > limit)) { - if (inputs) { + if (buffer_inputs && !(filter_sig)) { sigsToFix.emplace(inp, fanout); } else { wire->set_string_attribute("$FANOUT", std::to_string(fanout)); From f65d82293520e51f488f8a04a265153674276935 Mon Sep 17 00:00:00 2001 From: Alain Dargelas Date: Wed, 5 Mar 2025 11:49:12 -0800 Subject: [PATCH 27/32] cleanup dangling buffers --- passes/silimate/annotate_cell_fanout.cc | 33 +++++++++++++++++++------ 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/passes/silimate/annotate_cell_fanout.cc b/passes/silimate/annotate_cell_fanout.cc index 886be5f04..18cc285d0 100644 --- a/passes/silimate/annotate_cell_fanout.cc +++ b/passes/silimate/annotate_cell_fanout.cc @@ -171,7 +171,7 @@ bool isSigSpecUsedIn(SigSpec &haystack, SigMap &sigmap, SigSpec &needle) } // Remove a buffer and fix the fanout connections to use the buffer's input -void removeBuffer(Module *module, SigMap &sigmap, std::set &fanoutcells, Cell *buffer, bool debug) +void removeBuffer(Module *module, SigMap &sigmap, std::set &fanoutcells, std::map &insertedBuffers, Cell *buffer, bool debug) { if (debug) std::cout << "Buffer with fanout 1: " << buffer->name.c_str() << std::endl; @@ -223,6 +223,8 @@ void removeBuffer(Module *module, SigMap &sigmap, std::set &fanoutcells, break; } // Delete the now unsused buffer and it's output signal + auto itr = insertedBuffers.find(buffer); + insertedBuffers.erase(itr); module->remove(buffer); module->remove({bufferOutSig.as_wire()}); } @@ -300,8 +302,8 @@ SigSpec updateToBuffer(Module *module, std::map &bufferIndexes, // - when a buffer reaches capacity, switch to the next buffer // The capacity of the buffers might be larger than the limit in a given pass, // Recursion is used until all buffers capacity is under or at the limit. -void fixfanout(RTLIL::Module *module, SigMap &sigmap, dict> &sig2CellsInFanout, SigSpec sigToBuffer, int fanout, - int limit, bool debug) +void fixfanout(RTLIL::Module *module, SigMap &sigmap, dict> &sig2CellsInFanout, + std::map &insertedBuffers, SigSpec sigToBuffer, int fanout, int limit, bool debug) { if (sigToBuffer.is_fully_const()) { return; @@ -350,6 +352,7 @@ void fixfanout(RTLIL::Module *module, SigMap &sigmap, dictaddCell(cellName, ID($buf)); bufferActualFanout[buffer] = 0; RTLIL::SigSpec buffer_output = module->addWire(wireName, chunk.size()); + insertedBuffers.emplace(buffer, buffer_output.as_wire()); buffer->setPort(ID(A), chunk); buffer->setPort(ID(Y), sigmap(buffer_output)); buffer->fixup_parameters(); @@ -427,11 +430,11 @@ void fixfanout(RTLIL::Module *module, SigMap &sigmap, dict::iterator itr = bufferActualFanout.begin(); itr != bufferActualFanout.end(); itr++) { if (itr->second == 1) { // Remove previously inserted buffers with fanout of 1 (Hard to predict the last buffer usage in above step) - removeBuffer(module, sigmap, fanoutcells, itr->first, debug); + removeBuffer(module, sigmap, fanoutcells, insertedBuffers, itr->first, debug); } else { // Recursively fix the fanout of the newly created buffers RTLIL::SigSpec sig = getCellOutputSigSpec(itr->first, sigmap); - fixfanout(module, sigmap, sig2CellsInFanout, sig, itr->second, limit, debug); + fixfanout(module, sigmap, sig2CellsInFanout, insertedBuffers, sig, itr->second, limit, debug); } } } @@ -632,7 +635,7 @@ struct AnnotateCellFanout : public ScriptPass { } for (auto module : design->selected_modules()) { bool fixedFanout = false; - + std::map insertedBuffers; { // Calculate fanout SigMap sigmap(module); @@ -694,7 +697,7 @@ struct AnnotateCellFanout : public ScriptPass { RTLIL::SigSpec actual = conn.second; if (cell->output(portName)) { RTLIL::SigSpec cellOutSig = sigmap(actual); - fixfanout(module, sigmap, sig2CellsInFanout, cellOutSig, fanout, limit, debug); + fixfanout(module, sigmap, sig2CellsInFanout, insertedBuffers, cellOutSig, fanout, limit, debug); } } fixedFanout = true; @@ -748,7 +751,7 @@ struct AnnotateCellFanout : public ScriptPass { } } for (auto sig : sigsToFix) { - fixfanout(module, sigmap, sig2CellsInFanout, sig.first, sig.second, limit, debug); + fixfanout(module, sigmap, sig2CellsInFanout, insertedBuffers, sig.first, sig.second, limit, debug); fixedFanout = true; } } @@ -763,6 +766,19 @@ struct AnnotateCellFanout : public ScriptPass { for (auto itrCell : cellFanout) { Cell *cell = itrCell.first; int fanout = itrCell.second; + // Final cleanup, remove buffers of 1 + if ((fanout == 1) && insertedBuffers.find(cell) != insertedBuffers.end()) { + SigSpec bufferOut = insertedBuffers.find(cell)->second; + std::set fanoutcells = sig2CellsInFanout[bufferOut]; + for (int i = 0; i < bufferOut.size(); i++) { + SigSpec bit_sig = bufferOut.extract(i, 1); + for (Cell *c : sig2CellsInFanout[sigmap(bit_sig)]) { + fanoutcells.insert(c); + } + } + removeBuffer(module, sigmap, fanoutcells, insertedBuffers, cell, debug); + continue; + } // Add attribute with fanout info to every cell cell->set_string_attribute("$FANOUT", std::to_string(fanout)); } @@ -774,6 +790,7 @@ struct AnnotateCellFanout : public ScriptPass { wire->set_string_attribute("$FANOUT", std::to_string(fanout)); } } + log("Added %ld buffers in module %s\n", insertedBuffers.size(), module->name.c_str()); } } From 4b598e317b8aa7870dc1f0d2b464bed3530a746f Mon Sep 17 00:00:00 2001 From: Alain Dargelas Date: Wed, 5 Mar 2025 13:10:53 -0800 Subject: [PATCH 28/32] getfanout factor --- passes/silimate/annotate_cell_fanout.cc | 35 ++++++++++++++----------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/passes/silimate/annotate_cell_fanout.cc b/passes/silimate/annotate_cell_fanout.cc index 18cc285d0..490750bf0 100644 --- a/passes/silimate/annotate_cell_fanout.cc +++ b/passes/silimate/annotate_cell_fanout.cc @@ -77,6 +77,18 @@ RTLIL::IdString generateSigSpecName(Module *module, const RTLIL::SigSpec &sigspe return RTLIL::IdString(ss.str()); } +// Collect fanout cells of a given sig, collects all bits connections +void getFanout(dict> &sig2CellsInFanout, SigMap &sigmap, SigSpec sig, std::set &fanoutcells) +{ + fanoutcells = sig2CellsInFanout[sig]; + for (int i = 0; i < sig.size(); i++) { + SigSpec bit_sig = sig.extract(i, 1); + for (Cell *c : sig2CellsInFanout[sigmap(bit_sig)]) { + fanoutcells.insert(c); + } + } +} + // Signal cell driver(s), precompute a cell output signal to a cell map void sigCellDrivers(RTLIL::Module *module, SigMap &sigmap, dict> &sig2CellsInFanout, dict> &sig2CellsInFanin) @@ -364,13 +376,8 @@ void fixfanout(RTLIL::Module *module, SigMap &sigmap, dict fanoutcells = sig2CellsInFanout[sigToBuffer]; - for (int i = 0; i < sigToBuffer.size(); i++) { - SigSpec bit_sig = sigToBuffer.extract(i, 1); - for (Cell *c : sig2CellsInFanout[sigmap(bit_sig)]) { - fanoutcells.insert(c); - } - } + std::set fanoutcells; + getFanout(sig2CellsInFanout, sigmap, sigToBuffer, fanoutcells); // Fix input connections to cells in fanout of buffer to point to the inserted buffer for (Cell *fanoutcell : fanoutcells) { @@ -635,7 +642,7 @@ struct AnnotateCellFanout : public ScriptPass { } for (auto module : design->selected_modules()) { bool fixedFanout = false; - std::map insertedBuffers; + std::map insertedBuffers; { // Calculate fanout SigMap sigmap(module); @@ -697,7 +704,8 @@ struct AnnotateCellFanout : public ScriptPass { RTLIL::SigSpec actual = conn.second; if (cell->output(portName)) { RTLIL::SigSpec cellOutSig = sigmap(actual); - fixfanout(module, sigmap, sig2CellsInFanout, insertedBuffers, cellOutSig, fanout, limit, debug); + fixfanout(module, sigmap, sig2CellsInFanout, insertedBuffers, cellOutSig, fanout, + limit, debug); } } fixedFanout = true; @@ -769,13 +777,8 @@ struct AnnotateCellFanout : public ScriptPass { // Final cleanup, remove buffers of 1 if ((fanout == 1) && insertedBuffers.find(cell) != insertedBuffers.end()) { SigSpec bufferOut = insertedBuffers.find(cell)->second; - std::set fanoutcells = sig2CellsInFanout[bufferOut]; - for (int i = 0; i < bufferOut.size(); i++) { - SigSpec bit_sig = bufferOut.extract(i, 1); - for (Cell *c : sig2CellsInFanout[sigmap(bit_sig)]) { - fanoutcells.insert(c); - } - } + std::set fanoutcells; + getFanout(sig2CellsInFanout, sigmap, bufferOut, fanoutcells); removeBuffer(module, sigmap, fanoutcells, insertedBuffers, cell, debug); continue; } From d4e4ece9fe3b0513aba9608c0e808b549a431134 Mon Sep 17 00:00:00 2001 From: Alain Dargelas Date: Thu, 6 Mar 2025 10:47:27 -0800 Subject: [PATCH 29/32] bit split initial splitfanout call --- passes/silimate/annotate_cell_fanout.cc | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/passes/silimate/annotate_cell_fanout.cc b/passes/silimate/annotate_cell_fanout.cc index 490750bf0..e268e31ff 100644 --- a/passes/silimate/annotate_cell_fanout.cc +++ b/passes/silimate/annotate_cell_fanout.cc @@ -704,8 +704,12 @@ struct AnnotateCellFanout : public ScriptPass { RTLIL::SigSpec actual = conn.second; if (cell->output(portName)) { RTLIL::SigSpec cellOutSig = sigmap(actual); - fixfanout(module, sigmap, sig2CellsInFanout, insertedBuffers, cellOutSig, fanout, - limit, debug); + for (int i = 0; i < cellOutSig.size(); i++) { + SigSpec bit_sig = cellOutSig.extract(i, 1); + int bitfanout = sig2CellsInFanout[bit_sig].size(); + fixfanout(module, sigmap, sig2CellsInFanout, insertedBuffers, bit_sig, + bitfanout, limit, debug); + } } } fixedFanout = true; @@ -759,7 +763,11 @@ struct AnnotateCellFanout : public ScriptPass { } } for (auto sig : sigsToFix) { - fixfanout(module, sigmap, sig2CellsInFanout, insertedBuffers, sig.first, sig.second, limit, debug); + for (int i = 0; i < sig.first.size(); i++) { + SigSpec bit_sig = sig.first.extract(i, 1); + int bitfanout = sig2CellsInFanout[bit_sig].size(); + fixfanout(module, sigmap, sig2CellsInFanout, insertedBuffers, bit_sig, bitfanout, limit, debug); + } fixedFanout = true; } } From 81ab3b5fed321b9787ffdbd1427c4ace891ed4ec Mon Sep 17 00:00:00 2001 From: Alain Dargelas Date: Thu, 6 Mar 2025 16:21:11 -0800 Subject: [PATCH 30/32] call splitnets for the whole design --- passes/silimate/annotate_cell_fanout.cc | 140 +++++++++++++----------- 1 file changed, 77 insertions(+), 63 deletions(-) diff --git a/passes/silimate/annotate_cell_fanout.cc b/passes/silimate/annotate_cell_fanout.cc index e268e31ff..64da08516 100644 --- a/passes/silimate/annotate_cell_fanout.cc +++ b/passes/silimate/annotate_cell_fanout.cc @@ -522,50 +522,48 @@ void calculateFanout(RTLIL::Module *module, SigMap &sigmap, dict &netsToSplitS, std::vector &sigsToSplit, bool formalFriendly, bool debug, - bool inputPort = false) +void splitNets(Design *design, std::map> &sigsToSplit, bool formalFriendly, bool inputPort = false) { std::string wires; std::string inputs; std::string outputs; - for (RTLIL::SigSpec sigToSplit : sigsToSplit) { - Wire *parentWire = getParentWire(sigToSplit); - if (!parentWire) - continue; - std::string parent = parentWire->name.c_str(); - if (parent == "") { - continue; - } - if (parentWire->width == 1) - continue; - parent = substringuntil(parent, '['); - if (netsToSplitS.find(parent) == netsToSplitS.end()) { - netsToSplitS.insert(parent); - if (debug) { - std::cout << "splitnets: " << parent << std::endl; - } + // Memorize selection + Pass::call(design, "select -set presplitnets %"); + // Clear selection + Pass::call(design, "select -none"); + std::set selected; + for (std::map>::iterator itr = sigsToSplit.begin(); itr != sigsToSplit.end(); itr++) { + Module *module = itr->first; + for (RTLIL::SigSpec sigToSplit : itr->second) { + Wire *parentWire = getParentWire(sigToSplit); + if (!parentWire) + continue; + if (selected.find(parentWire) != selected.end()) + continue; + selected.insert(parentWire); if ((!parentWire->port_input) && (!parentWire->port_output)) - wires += " w:" + parent; + design->select(module, parentWire); if (!formalFriendly) { // Formal verification does not like ports to be split. // This option will prevent some buffering to happen on high fanout input/output ports, // but it will make formal happy. if (inputPort) { if (parentWire->port_input) - inputs += " i:" + parent; + design->select(module, parentWire); } else { if (parentWire->port_output) - outputs += " o:" + parent; + design->select(module, parentWire); } } } } - if (!wires.empty()) { - Pass::call(design, "splitnets" + wires); // Wire - } - if (!inputs.empty() || !outputs.empty()) { - Pass::call(design, "splitnets -ports_only" + inputs + outputs); // Input port + if (formalFriendly) { + Pass::call(design, "splitnets"); + } else { + Pass::call(design, "splitnets -ports"); } + // Restore selection + Pass::call(design, "select @presplitnets"); } struct AnnotateCellFanout : public ScriptPass { @@ -640,52 +638,68 @@ struct AnnotateCellFanout : public ScriptPass { log_error("Fanout cannot be limited to less than 2\n"); return; } + + // Collect all the high fanout signals to split + std::map> signalsToSplit; + std::map> portsToSplit; + + // Split all the high fanout nets for the whole design for (auto module : design->selected_modules()) { - bool fixedFanout = false; - std::map insertedBuffers; - { - // Calculate fanout - SigMap sigmap(module); - dict cellFanout; - dict sigFanout; - dict> sig2CellsInFanout; - calculateFanout(module, sigmap, sig2CellsInFanout, cellFanout, sigFanout); + // Calculate fanout + SigMap sigmap(module); + dict cellFanout; + dict sigFanout; + dict> sig2CellsInFanout; + calculateFanout(module, sigmap, sig2CellsInFanout, cellFanout, sigFanout); - std::set netsToSplitS; - // Split cells output nets with high fanout - std::vector cellOutputsToSplit; - for (auto itrCell : cellFanout) { - Cell *cell = itrCell.first; - int fanout = itrCell.second; - if (limit > 0 && (fanout > limit)) { - for (auto &conn : cell->connections()) { - IdString portName = conn.first; - RTLIL::SigSpec actual = conn.second; - if (cell->output(portName)) { - RTLIL::SigSpec cellOutSig = sigmap(actual); - cellOutputsToSplit.push_back(cellOutSig); - } + // Split cells output nets with high fanout + std::vector cellOutputsToSplit; + for (auto itrCell : cellFanout) { + Cell *cell = itrCell.first; + int fanout = itrCell.second; + if (limit > 0 && (fanout > limit)) { + int nbOutputs = 0; + for (auto &conn : cell->connections()) { + IdString portName = conn.first; + if (cell->output(portName)) { + nbOutputs++; } } - } - splitNets(design, netsToSplitS, cellOutputsToSplit, formalFriendly, debug); - - if (buffer_inputs) { - // Split module input nets with high fanout - std::vector wiresToSplit; - for (Wire *wire : module->wires()) { - if (wire->port_input) { - SigSpec inp = sigmap(wire); - int fanout = sigFanout[inp]; - if (limit > 0 && (fanout > limit)) { - wiresToSplit.push_back(inp); - } + for (auto &conn : cell->connections()) { + IdString portName = conn.first; + RTLIL::SigSpec actual = conn.second; + if (cell->output(portName)) { + RTLIL::SigSpec cellOutSig = sigmap(actual); + cellOutputsToSplit.push_back(cellOutSig); } } - splitNets(design, netsToSplitS, wiresToSplit, formalFriendly, debug, true); } } + signalsToSplit.emplace(module, cellOutputsToSplit); + if (buffer_inputs) { + // Split module input nets with high fanout + std::vector wiresToSplit; + for (Wire *wire : module->wires()) { + if (wire->port_input) { + SigSpec inp = sigmap(wire); + int fanout = sigFanout[inp]; + if (limit > 0 && (fanout > limit)) { + wiresToSplit.push_back(inp); + } + } + } + portsToSplit.emplace(module, wiresToSplit); + } + } + splitNets(design, signalsToSplit, formalFriendly); + splitNets(design, portsToSplit, formalFriendly, true); + + // Fix the high fanout nets + for (auto module : design->selected_modules()) { + bool fixedFanout = false; + // All the buffers inserted in the module + std::map insertedBuffers; { // Fix high fanout SigMap sigmap(module); From c54c3f66b7342eee4420acc99957d62a4f3ec942 Mon Sep 17 00:00:00 2001 From: Alain Dargelas Date: Thu, 6 Mar 2025 16:46:22 -0800 Subject: [PATCH 31/32] Single call to splitnets --- passes/silimate/annotate_cell_fanout.cc | 44 +++++++++---------------- 1 file changed, 16 insertions(+), 28 deletions(-) diff --git a/passes/silimate/annotate_cell_fanout.cc b/passes/silimate/annotate_cell_fanout.cc index 64da08516..b16bb0445 100644 --- a/passes/silimate/annotate_cell_fanout.cc +++ b/passes/silimate/annotate_cell_fanout.cc @@ -522,7 +522,7 @@ void calculateFanout(RTLIL::Module *module, SigMap &sigmap, dict> &sigsToSplit, bool formalFriendly, bool inputPort = false) +void splitNets(Design *design, std::map> &sigsToSplit, bool formalFriendly) { std::string wires; std::string inputs; @@ -541,27 +541,18 @@ void splitNets(Design *design, std::map> & if (selected.find(parentWire) != selected.end()) continue; selected.insert(parentWire); - if ((!parentWire->port_input) && (!parentWire->port_output)) - design->select(module, parentWire); - if (!formalFriendly) { + if (formalFriendly) { // Formal verification does not like ports to be split. // This option will prevent some buffering to happen on high fanout input/output ports, // but it will make formal happy. - if (inputPort) { - if (parentWire->port_input) - design->select(module, parentWire); - } else { - if (parentWire->port_output) - design->select(module, parentWire); - } + if ((!parentWire->port_input) && (!parentWire->port_output)) + design->select(module, parentWire); + } else { + design->select(module, parentWire); } } } - if (formalFriendly) { - Pass::call(design, "splitnets"); - } else { - Pass::call(design, "splitnets -ports"); - } + Pass::call(design, std::string("splitnets") + (formalFriendly ? "" : " -ports")); // Restore selection Pass::call(design, "select @presplitnets"); } @@ -641,9 +632,6 @@ struct AnnotateCellFanout : public ScriptPass { // Collect all the high fanout signals to split std::map> signalsToSplit; - std::map> portsToSplit; - - // Split all the high fanout nets for the whole design for (auto module : design->selected_modules()) { // Calculate fanout SigMap sigmap(module); @@ -652,8 +640,8 @@ struct AnnotateCellFanout : public ScriptPass { dict> sig2CellsInFanout; calculateFanout(module, sigmap, sig2CellsInFanout, cellFanout, sigFanout); - // Split cells output nets with high fanout - std::vector cellOutputsToSplit; + // Cells output nets with high fanout + std::vector netsToSplit; for (auto itrCell : cellFanout) { Cell *cell = itrCell.first; int fanout = itrCell.second; @@ -670,30 +658,28 @@ struct AnnotateCellFanout : public ScriptPass { RTLIL::SigSpec actual = conn.second; if (cell->output(portName)) { RTLIL::SigSpec cellOutSig = sigmap(actual); - cellOutputsToSplit.push_back(cellOutSig); + netsToSplit.push_back(cellOutSig); } } } } - signalsToSplit.emplace(module, cellOutputsToSplit); - if (buffer_inputs) { - // Split module input nets with high fanout + // Module input nets with high fanout std::vector wiresToSplit; for (Wire *wire : module->wires()) { if (wire->port_input) { SigSpec inp = sigmap(wire); int fanout = sigFanout[inp]; if (limit > 0 && (fanout > limit)) { - wiresToSplit.push_back(inp); + netsToSplit.push_back(inp); } } } - portsToSplit.emplace(module, wiresToSplit); } + signalsToSplit.emplace(module, netsToSplit); } + // Split all the high fanout nets in one pass for the whole design splitNets(design, signalsToSplit, formalFriendly); - splitNets(design, portsToSplit, formalFriendly, true); // Fix the high fanout nets for (auto module : design->selected_modules()) { @@ -793,6 +779,8 @@ struct AnnotateCellFanout : public ScriptPass { dict sigFanout; dict> sig2CellsInFanout; calculateFanout(module, sigmap, sig2CellsInFanout, cellFanout, sigFanout); + + // Cleanup and annotation for (auto itrCell : cellFanout) { Cell *cell = itrCell.first; int fanout = itrCell.second; From 52b1cbf456de42c606f7ece241cda2db075f59a5 Mon Sep 17 00:00:00 2001 From: Alain Dargelas Date: Fri, 7 Mar 2025 14:11:25 -0800 Subject: [PATCH 32/32] Separate into 2 passes --- passes/silimate/annotate_cell_fanout.cc | 132 +++++++++++++++--------- 1 file changed, 84 insertions(+), 48 deletions(-) diff --git a/passes/silimate/annotate_cell_fanout.cc b/passes/silimate/annotate_cell_fanout.cc index b16bb0445..0dfce01fd 100644 --- a/passes/silimate/annotate_cell_fanout.cc +++ b/passes/silimate/annotate_cell_fanout.cc @@ -522,15 +522,16 @@ void calculateFanout(RTLIL::Module *module, SigMap &sigmap, dict> &sigsToSplit, bool formalFriendly) +void splitNets(Design *design, std::map> &sigsToSplit) { std::string wires; std::string inputs; std::string outputs; - // Memorize selection + // Memorize previous selection Pass::call(design, "select -set presplitnets %"); // Clear selection Pass::call(design, "select -none"); + // Select all the wires to split std::set selected; for (std::map>::iterator itr = sigsToSplit.begin(); itr != sigsToSplit.end(); itr++) { Module *module = itr->first; @@ -541,87 +542,53 @@ void splitNets(Design *design, std::map> & if (selected.find(parentWire) != selected.end()) continue; selected.insert(parentWire); - if (formalFriendly) { - // Formal verification does not like ports to be split. - // This option will prevent some buffering to happen on high fanout input/output ports, - // but it will make formal happy. - if ((!parentWire->port_input) && (!parentWire->port_output)) - design->select(module, parentWire); - } else { - design->select(module, parentWire); - } + design->select(module, parentWire); } } - Pass::call(design, std::string("splitnets") + (formalFriendly ? "" : " -ports")); - // Restore selection + // Split the wires + Pass::call(design, std::string("splitnets -ports")); + // Restore previous selection Pass::call(design, "select @presplitnets"); } -struct AnnotateCellFanout : public ScriptPass { - AnnotateCellFanout() : ScriptPass("annotate_cell_fanout", "Annotate the cell fanout on the cell") {} +// Split high fanout nets +struct SplitHighFanoutNets : public ScriptPass { + SplitHighFanoutNets() : ScriptPass("split_high_fanout_nets", "Split high fanout nets") {} void script() override {} void help() override { log("\n"); - log(" annotate_cell_fanout [options] [selection]\n"); + log(" split_high_fanout_nets [options] [selection]\n"); log("\n"); - log("This pass annotates cell fanout and optionally inserts balanced buffer trees to limit fanout.\n"); + log("Split high fanout nets.\n"); log("\n"); log(" -limit \n"); log(" Limits the fanout by inserting balanced buffer trees.\n"); - log(" -formal\n"); - log(" For formal verification to pass, will prevent splitnets passes on ports, even if they have large fanout.\n"); log(" -inputs\n"); log(" Fix module inputs fanout.\n"); - log(" -clocks\n"); - log(" Fix module clocks fanout.\n"); - log(" -resets\n"); - log(" Fix module resets fanout.\n"); - log(" -debug\n"); - log(" Debug trace.\n"); log("\n"); } void execute(std::vector args, RTLIL::Design *design) override { int limit = -1; - bool formalFriendly = false; bool buffer_inputs = false; - bool buffer_clocks = false; - bool buffer_resets = false; - bool debug = false; if (design == nullptr) { log_error("No design object\n"); return; } - log("Running annotate_cell_fanout pass\n"); + log("Running split_high_fanout_nets pass\n"); log_flush(); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { - if (args[argidx] == "-debug") { - debug = true; - continue; - } if (args[argidx] == "-limit") { limit = std::atoi(args[++argidx].c_str()); continue; } - if (args[argidx] == "-formal") { - formalFriendly = true; - continue; - } if (args[argidx] == "-inputs") { buffer_inputs = true; continue; } - if (args[argidx] == "-clocks") { - buffer_clocks = true; - continue; - } - if (args[argidx] == "-resets") { - buffer_resets = true; - continue; - } break; } extra_args(args, argidx, design); @@ -679,7 +646,76 @@ struct AnnotateCellFanout : public ScriptPass { signalsToSplit.emplace(module, netsToSplit); } // Split all the high fanout nets in one pass for the whole design - splitNets(design, signalsToSplit, formalFriendly); + splitNets(design, signalsToSplit); + } +} SplitHighFanoutNets; + +// Annotate cell fanout and optionally insert balanced buffer trees to limit fanout +struct AnnotateCellFanout : public ScriptPass { + AnnotateCellFanout() : ScriptPass("annotate_cell_fanout", "Annotate the cell fanout on the cell") {} + void script() override {} + void help() override + { + log("\n"); + log(" annotate_cell_fanout [options] [selection]\n"); + log("\n"); + log("This pass annotates cell fanout and optionally inserts balanced buffer trees to limit fanout.\n"); + log("\n"); + log(" -limit \n"); + log(" Limits the fanout by inserting balanced buffer trees.\n"); + log(" -inputs\n"); + log(" Fix module inputs fanout.\n"); + log(" -clocks\n"); + log(" Fix module clocks fanout.\n"); + log(" -resets\n"); + log(" Fix module resets fanout.\n"); + log(" -debug\n"); + log(" Debug trace.\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *design) override + { + int limit = -1; + bool buffer_inputs = false; + bool buffer_clocks = false; + bool buffer_resets = false; + bool debug = false; + if (design == nullptr) { + log_error("No design object\n"); + return; + } + log("Running annotate_cell_fanout pass\n"); + log_flush(); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-debug") { + debug = true; + continue; + } + if (args[argidx] == "-limit") { + limit = std::atoi(args[++argidx].c_str()); + continue; + } + if (args[argidx] == "-inputs") { + buffer_inputs = true; + continue; + } + if (args[argidx] == "-clocks") { + buffer_clocks = true; + continue; + } + if (args[argidx] == "-resets") { + buffer_resets = true; + continue; + } + break; + } + extra_args(args, argidx, design); + if ((limit != -1) && (limit < 2)) { + log_error("Fanout cannot be limited to less than 2\n"); + return; + } // Fix the high fanout nets for (auto module : design->selected_modules()) { @@ -810,6 +846,6 @@ struct AnnotateCellFanout : public ScriptPass { log("End annotate_cell_fanout pass\n"); log_flush(); } -} SplitNetlist; +} AnnotateCellFanout; PRIVATE_NAMESPACE_END