2025-01-31 19:17:34 +01:00
|
|
|
#include "kernel/sigtools.h"
|
|
|
|
|
#include "kernel/yosys.h"
|
|
|
|
|
#include <set>
|
|
|
|
|
|
|
|
|
|
USING_YOSYS_NAMESPACE
|
|
|
|
|
PRIVATE_NAMESPACE_BEGIN
|
|
|
|
|
|
|
|
|
|
// Signal cell driver(s), precompute a cell output signal to a cell map
|
2025-02-27 01:17:09 +01:00
|
|
|
void sigCellDrivers(RTLIL::Module *module, SigMap &sigmap, dict<RTLIL::SigSpec, std::set<Cell *>> &sig2CellsInFanout,
|
2025-01-31 19:17:34 +01:00
|
|
|
dict<RTLIL::SigSpec, std::set<Cell *>> &sig2CellsInFanin)
|
|
|
|
|
{
|
2025-02-27 01:17:09 +01:00
|
|
|
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);
|
2025-01-31 19:17:34 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Assign statements fanin, fanout, traces the lhs2rhs and rhs2lhs sigspecs and precompute maps
|
2025-02-27 01:17:09 +01:00
|
|
|
void lhs2rhs_rhs2lhs(RTLIL::Module *module, SigMap &sigmap, dict<RTLIL::SigSpec, std::set<RTLIL::SigSpec>> &rhsSig2LhsSig,
|
2025-01-31 19:17:34 +01:00
|
|
|
dict<RTLIL::SigSpec, RTLIL::SigSpec> &lhsSig2rhsSig)
|
|
|
|
|
{
|
2025-02-27 01:17:09 +01:00
|
|
|
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()) {
|
2025-01-31 19:17:34 +01:00
|
|
|
std::vector<SigSpec> lhsBits;
|
|
|
|
|
for (int i = 0; i < lhs.size(); i++) {
|
|
|
|
|
SigSpec bit_sig = lhs.extract(i, 1);
|
|
|
|
|
lhsBits.push_back(bit_sig);
|
|
|
|
|
}
|
|
|
|
|
std::vector<SigSpec> rhsBits;
|
|
|
|
|
for (int i = 0; i < rhs.size(); i++) {
|
|
|
|
|
SigSpec bit_sig = rhs.extract(i, 1);
|
|
|
|
|
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]));
|
2025-02-27 01:17:09 +01:00
|
|
|
lhsSig2rhsSig[lhsBits[i]] = sigmap(rhsBits[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
rhsSig2LhsSig[sigmap(rhs)].insert(sigmap(lhs));
|
|
|
|
|
lhsSig2rhsSig[lhs] = sigmap(rhs);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-01 07:08:19 +01:00
|
|
|
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;
|
2025-02-28 21:07:58 +01:00
|
|
|
}
|
|
|
|
|
|
2025-03-03 19:08:27 +01:00
|
|
|
// 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.
|
2025-03-01 07:08:19 +01:00
|
|
|
void fixfanout(RTLIL::Design *design, RTLIL::Module *module, SigMap &sigmap, dict<RTLIL::SigSpec, std::set<Cell *>> &sig2CellsInFanout,
|
2025-03-03 18:35:49 +01:00
|
|
|
RTLIL::Cell *cell, int fanout, int limit, bool debug)
|
2025-02-27 01:17:09 +01:00
|
|
|
{
|
|
|
|
|
if (fanout <= limit) {
|
2025-03-03 18:35:49 +01:00
|
|
|
if (debug) {
|
2025-03-03 19:08:27 +01:00
|
|
|
std::cout << "Nothing to do for: " << cell->name.c_str() << std::endl;
|
|
|
|
|
std::cout << "Fanout: " << fanout << std::endl;
|
2025-03-03 18:35:49 +01:00
|
|
|
}
|
2025-02-27 01:17:09 +01:00
|
|
|
return; // No need to insert buffers
|
|
|
|
|
} else {
|
2025-03-03 18:35:49 +01:00
|
|
|
if (debug) {
|
2025-03-03 19:08:27 +01:00
|
|
|
std::cout << "Something to do for: " << cell->name.c_str() << std::endl;
|
|
|
|
|
std::cout << "Fanout: " << fanout << std::endl;
|
2025-03-03 18:35:49 +01:00
|
|
|
}
|
2025-02-27 01:17:09 +01:00
|
|
|
}
|
2025-03-03 19:08:27 +01:00
|
|
|
// 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.
|
2025-02-27 01:17:09 +01:00
|
|
|
int num_buffers = std::min((int)std::ceil(static_cast<double>(fanout) / limit), limit);
|
2025-03-03 19:08:27 +01:00
|
|
|
// The max output (fanout) per buffer: MaxOut = Ceil(Fanout / NbBuffers)
|
2025-02-27 01:17:09 +01:00
|
|
|
int max_output_per_buffer = std::ceil((float)fanout / (float)num_buffers);
|
2025-03-03 18:35:49 +01:00
|
|
|
if (debug) {
|
2025-03-03 19:08:27 +01:00
|
|
|
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;
|
2025-03-03 18:35:49 +01:00
|
|
|
}
|
2025-02-28 21:07:58 +01:00
|
|
|
|
|
|
|
|
// Get cell output
|
2025-02-27 01:17:09 +01:00
|
|
|
RTLIL::SigSpec cellOutSig;
|
|
|
|
|
for (auto &conn : cell->connections()) {
|
2025-02-27 01:51:39 +01:00
|
|
|
IdString portName = conn.first;
|
|
|
|
|
RTLIL::SigSpec actual = conn.second;
|
|
|
|
|
if (cell->output(portName)) {
|
|
|
|
|
cellOutSig = sigmap(actual);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2025-02-27 01:17:09 +01:00
|
|
|
}
|
2025-02-28 21:07:58 +01:00
|
|
|
|
2025-03-03 19:08:27 +01:00
|
|
|
// Keep track of the fanout count for each new buffer
|
|
|
|
|
std::map<Cell *, int> bufferActualFanout;
|
|
|
|
|
// Array of buffers (The buffer output signal and the buffer cell) per cell output chunks
|
2025-03-01 07:08:19 +01:00
|
|
|
std::map<RTLIL::SigSpec, std::vector<std::tuple<RTLIL::SigSpec, Cell *>>> buffer_outputs;
|
2025-03-03 19:08:27 +01:00
|
|
|
// Keep track of which buffer in the array is getting filled for a given chunk
|
2025-03-01 05:38:36 +01:00
|
|
|
std::map<SigSpec, int> bufferIndexes;
|
2025-03-03 19:08:27 +01:00
|
|
|
|
|
|
|
|
// Create buffers and new wires
|
2025-03-01 05:38:36 +01:00
|
|
|
for (SigChunk chunk : cellOutSig.chunks()) {
|
2025-03-01 07:08:19 +01:00
|
|
|
std::vector<std::tuple<RTLIL::SigSpec, Cell *>> buffer_chunk_outputs;
|
2025-03-01 05:38:36 +01:00
|
|
|
for (int i = 0; i < num_buffers; ++i) {
|
|
|
|
|
RTLIL::Cell *buffer = module->addCell(NEW_ID2_SUFFIX("fbuf"), ID($pos));
|
2025-03-01 07:08:19 +01:00
|
|
|
bufferActualFanout[buffer] = 0;
|
2025-02-28 21:07:58 +01:00
|
|
|
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();
|
2025-03-01 05:38:36 +01:00
|
|
|
buffer_chunk_outputs.push_back(std::make_tuple(buffer_output, buffer)); // Old - New
|
2025-03-01 07:08:19 +01:00
|
|
|
bufferIndexes[chunk] = 0;
|
2025-02-28 21:07:58 +01:00
|
|
|
}
|
2025-03-01 05:38:36 +01:00
|
|
|
buffer_outputs.emplace(sigmap(SigSpec(chunk)), buffer_chunk_outputs);
|
2025-02-27 01:17:09 +01:00
|
|
|
}
|
2025-02-28 21:07:58 +01:00
|
|
|
|
|
|
|
|
// Cumulate all cells in the fanout of this cell
|
2025-02-27 01:17:09 +01:00
|
|
|
std::set<Cell *> cells = sig2CellsInFanout[cellOutSig];
|
2025-03-01 07:08:19 +01:00
|
|
|
for (int i = 0; i < cellOutSig.size(); i++) {
|
2025-02-28 21:07:58 +01:00
|
|
|
SigSpec bit_sig = cellOutSig.extract(i, 1);
|
2025-03-01 07:08:19 +01:00
|
|
|
for (Cell *c : sig2CellsInFanout[sigmap(bit_sig)]) {
|
2025-02-28 21:07:58 +01:00
|
|
|
cells.insert(c);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-28 23:50:17 +01:00
|
|
|
// Fix input connections to cells in fanout of buffer to point to the inserted buffer
|
2025-02-27 01:17:09 +01:00
|
|
|
for (Cell *c : cells) {
|
2025-03-03 18:35:49 +01:00
|
|
|
if (debug)
|
2025-03-03 19:08:27 +01:00
|
|
|
std::cout << "\n CELL in fanout: " << c->name.c_str() << "\n" << std::flush;
|
2025-02-27 01:17:09 +01:00
|
|
|
for (auto &conn : c->connections()) {
|
|
|
|
|
IdString portName = conn.first;
|
2025-03-01 05:38:36 +01:00
|
|
|
RTLIL::SigSpec actual = sigmap(conn.second);
|
2025-02-27 01:17:09 +01:00
|
|
|
if (c->input(portName)) {
|
2025-02-27 01:51:39 +01:00
|
|
|
if (actual.is_chunk()) {
|
2025-03-03 19:08:27 +01:00
|
|
|
// Input of that cell is a chunk
|
2025-03-03 18:35:49 +01:00
|
|
|
if (debug)
|
2025-03-03 19:08:27 +01:00
|
|
|
std::cout << " IS A CHUNK" << std::endl;
|
2025-03-01 05:38:36 +01:00
|
|
|
if (buffer_outputs.find(actual) != buffer_outputs.end()) {
|
2025-03-03 19:08:27 +01:00
|
|
|
// 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
|
2025-03-01 07:08:19 +01:00
|
|
|
std::vector<std::tuple<RTLIL::SigSpec, Cell *>> &buf_info_vec = buffer_outputs[actual];
|
2025-03-03 19:08:27 +01:00
|
|
|
// Retrieve which buffer is getting filled
|
2025-03-01 05:38:36 +01:00
|
|
|
int bufferIndex = bufferIndexes[actual];
|
2025-03-01 07:08:19 +01:00
|
|
|
std::tuple<RTLIL::SigSpec, Cell *> &buf_info = buf_info_vec[bufferIndex];
|
2025-03-01 05:38:36 +01:00
|
|
|
SigSpec newSig = std::get<0>(buf_info);
|
2025-03-01 07:08:19 +01:00
|
|
|
Cell *newBuf = std::get<1>(buf_info);
|
2025-03-03 19:08:27 +01:00
|
|
|
// Override the fanout cell's input with the buffer output
|
2025-03-01 05:38:36 +01:00
|
|
|
c->setPort(portName, newSig);
|
2025-03-03 19:08:27 +01:00
|
|
|
// Keep track of fanout map information for recursive calls
|
2025-03-01 05:38:36 +01:00
|
|
|
sig2CellsInFanout[newSig].insert(c);
|
2025-03-03 19:08:27 +01:00
|
|
|
// Increment buffer capacity
|
2025-03-01 05:38:36 +01:00
|
|
|
bufferActualFanout[newBuf]++;
|
2025-03-03 19:08:27 +01:00
|
|
|
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
|
2025-03-01 05:38:36 +01:00
|
|
|
if (bufferActualFanout[newBuf] >= max_output_per_buffer) {
|
2025-03-03 19:08:27 +01:00
|
|
|
if (debug)
|
|
|
|
|
std::cout << " REACHED MAX" << std::endl;
|
|
|
|
|
if (int(buffer_outputs[actual].size() - 1) > bufferIndex) {
|
2025-03-01 05:38:36 +01:00
|
|
|
bufferIndexes[actual]++;
|
2025-03-03 19:08:27 +01:00
|
|
|
if (debug)
|
|
|
|
|
std::cout << " NEXT BUFFER" << std::endl;
|
2025-02-28 21:07:58 +01:00
|
|
|
}
|
2025-02-28 23:50:17 +01:00
|
|
|
}
|
2025-02-27 01:51:39 +01:00
|
|
|
}
|
|
|
|
|
} else {
|
2025-03-03 19:08:27 +01:00
|
|
|
// Input of that cell is a list of chunks
|
2025-03-03 18:35:49 +01:00
|
|
|
if (debug)
|
2025-03-03 19:08:27 +01:00
|
|
|
std::cout << " NOT A CHUNK" << std::endl;
|
2025-02-27 01:51:39 +01:00
|
|
|
bool match = false;
|
2025-03-03 19:08:27 +01:00
|
|
|
// Input chunk is one of the cell's outputs, its a match
|
2025-02-28 23:50:17 +01:00
|
|
|
for (SigChunk chunk_a : actual.chunks()) {
|
|
|
|
|
if (sigmap(SigSpec(chunk_a)) == sigmap(SigSpec(cellOutSig))) {
|
|
|
|
|
match = true;
|
|
|
|
|
} else {
|
|
|
|
|
for (SigChunk chunk_c : cellOutSig.chunks()) {
|
2025-03-01 07:08:19 +01:00
|
|
|
if (sigmap(SigSpec(chunk_a)) == sigmap(SigSpec(chunk_c))) {
|
2025-02-28 23:50:17 +01:00
|
|
|
match = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2025-02-28 21:07:58 +01:00
|
|
|
}
|
2025-02-27 01:51:39 +01:00
|
|
|
}
|
2025-02-28 21:07:58 +01:00
|
|
|
if (match)
|
|
|
|
|
break;
|
2025-02-27 01:51:39 +01:00
|
|
|
}
|
|
|
|
|
if (match) {
|
2025-03-03 19:08:27 +01:00
|
|
|
if (debug)
|
|
|
|
|
std::cout << " MATCH" << std::endl;
|
|
|
|
|
// Create a new chunk vector
|
2025-02-27 01:51:39 +01:00
|
|
|
std::vector<RTLIL::SigChunk> newChunks;
|
|
|
|
|
for (SigChunk chunk : actual.chunks()) {
|
2025-03-03 19:08:27 +01:00
|
|
|
bool replacedChunck = false;
|
2025-03-01 05:38:36 +01:00
|
|
|
if (buffer_outputs.find(chunk) != buffer_outputs.end()) {
|
2025-03-03 19:08:27 +01:00
|
|
|
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
|
2025-03-01 07:08:19 +01:00
|
|
|
std::vector<std::tuple<RTLIL::SigSpec, Cell *>> &buf_info_vec = buffer_outputs[chunk];
|
2025-03-03 19:08:27 +01:00
|
|
|
// Retrieve which buffer is getting filled
|
2025-03-01 07:08:19 +01:00
|
|
|
int bufferIndex = bufferIndexes[chunk];
|
|
|
|
|
std::tuple<RTLIL::SigSpec, Cell *> &buf_info = buf_info_vec[bufferIndex];
|
|
|
|
|
SigSpec newSig = std::get<0>(buf_info);
|
|
|
|
|
Cell *newBuf = std::get<1>(buf_info);
|
2025-03-03 19:08:27 +01:00
|
|
|
// Append the buffer's output in the chunck vector
|
2025-03-01 05:38:36 +01:00
|
|
|
newChunks.push_back(newSig.as_chunk());
|
2025-03-03 19:08:27 +01:00
|
|
|
// Keep track of fanout map information for recursive calls
|
2025-03-01 05:38:36 +01:00
|
|
|
sig2CellsInFanout[newSig].insert(c);
|
2025-03-03 19:08:27 +01:00
|
|
|
replacedChunck = true;
|
|
|
|
|
// Increment buffer capacity
|
2025-03-01 05:38:36 +01:00
|
|
|
bufferActualFanout[newBuf]++;
|
2025-03-03 19:08:27 +01:00
|
|
|
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
|
2025-03-01 05:38:36 +01:00
|
|
|
if (bufferActualFanout[newBuf] >= max_output_per_buffer) {
|
2025-03-03 19:08:27 +01:00
|
|
|
if (debug)
|
|
|
|
|
std::cout << " REACHED MAX" << std::endl;
|
|
|
|
|
if (int(buffer_outputs[chunk].size() - 1) > bufferIndex) {
|
2025-03-01 05:38:36 +01:00
|
|
|
bufferIndexes[chunk]++;
|
2025-03-03 19:08:27 +01:00
|
|
|
if (debug)
|
|
|
|
|
std::cout << " NEXT BUFFER" << std::endl;
|
2025-02-28 23:50:17 +01:00
|
|
|
}
|
2025-02-28 21:07:58 +01:00
|
|
|
}
|
|
|
|
|
}
|
2025-03-03 19:08:27 +01:00
|
|
|
if (!replacedChunck) {
|
|
|
|
|
// Append original chunck if no buffer used
|
2025-02-27 01:51:39 +01:00
|
|
|
newChunks.push_back(chunk);
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-03-03 19:08:27 +01:00
|
|
|
// Override the fanout cell's input with the newly created chunck vector
|
2025-02-28 23:50:17 +01:00
|
|
|
c->setPort(portName, newChunks);
|
2025-02-27 01:51:39 +01:00
|
|
|
break;
|
2025-02-27 01:17:09 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-02-27 01:51:39 +01:00
|
|
|
|
|
|
|
|
for (std::map<Cell *, int>::iterator itr = bufferActualFanout.begin(); itr != bufferActualFanout.end(); itr++) {
|
2025-02-27 01:17:09 +01:00
|
|
|
if (itr->second == 1) {
|
2025-03-03 18:35:49 +01:00
|
|
|
// Remove previously inserted buffers with fanout of 1 (Hard to predict the last buffer usage in above step)
|
2025-03-03 19:08:27 +01:00
|
|
|
if (debug)
|
|
|
|
|
std::cout << "Buffer with fanout 1: " << itr->first->name.c_str() << std::endl;
|
2025-02-28 21:07:58 +01:00
|
|
|
RTLIL::SigSpec bufferInSig = itr->first->getPort(ID::A);
|
|
|
|
|
RTLIL::SigSpec bufferOutSig = itr->first->getPort(ID::Y);
|
2025-02-27 01:17:09 +01:00
|
|
|
for (Cell *c : cells) {
|
2025-03-03 19:08:27 +01:00
|
|
|
if (debug)
|
|
|
|
|
std::cout << "Cell in its fanout: " << c->name.c_str() << std::endl;
|
2025-02-27 01:17:09 +01:00
|
|
|
for (auto &conn : c->connections()) {
|
|
|
|
|
IdString portName = conn.first;
|
|
|
|
|
RTLIL::SigSpec actual = conn.second;
|
|
|
|
|
if (c->input(portName)) {
|
2025-02-28 21:07:58 +01:00
|
|
|
if (actual.is_chunk()) {
|
|
|
|
|
if (bufferOutSig == sigmap(actual)) {
|
2025-03-03 19:08:27 +01:00
|
|
|
if (debug)
|
|
|
|
|
std::cout << "Replace: " << getParentWire(bufferOutSig)->name.c_str()
|
|
|
|
|
<< " by " << getParentWire(bufferInSig)->name.c_str() << std::endl;
|
2025-02-28 21:07:58 +01:00
|
|
|
c->setPort(portName, bufferInSig);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
std::vector<RTLIL::SigChunk> newChunks;
|
|
|
|
|
for (SigChunk chunk : actual.chunks()) {
|
|
|
|
|
if (sigmap(SigSpec(chunk)) == sigmap(bufferOutSig)) {
|
2025-03-03 19:08:27 +01:00
|
|
|
if (debug)
|
|
|
|
|
std::cout << "Replace: " << getParentWire(bufferOutSig)->name.c_str()
|
|
|
|
|
<< " by " << getParentWire(bufferInSig)->name.c_str()
|
|
|
|
|
<< std::endl;
|
2025-02-28 21:07:58 +01:00
|
|
|
newChunks.push_back(bufferInSig.as_chunk());
|
|
|
|
|
} else {
|
|
|
|
|
newChunks.push_back(chunk);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
c->setPort(portName, newChunks);
|
2025-02-27 01:17:09 +01:00
|
|
|
}
|
|
|
|
|
}
|
2025-01-31 19:17:34 +01:00
|
|
|
}
|
|
|
|
|
}
|
2025-03-01 05:38:36 +01:00
|
|
|
module->remove(itr->first);
|
|
|
|
|
module->remove({bufferOutSig.as_wire()});
|
2025-02-27 01:17:09 +01:00
|
|
|
} else {
|
2025-03-03 18:35:49 +01:00
|
|
|
// Recursively fix the fanout of the newly created buffers
|
|
|
|
|
fixfanout(design, module, sigmap, sig2CellsInFanout, itr->first, itr->second, limit, debug);
|
2025-01-31 19:17:34 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-27 01:51:39 +01:00
|
|
|
void calculateFanout(RTLIL::Module *module, SigMap &sigmap, dict<RTLIL::SigSpec, std::set<Cell *>> &sig2CellsInFanout, dict<Cell *, int> &cellFanout)
|
2025-02-27 01:17:09 +01:00
|
|
|
{
|
|
|
|
|
// Precompute cell output sigspec to cell map
|
|
|
|
|
dict<RTLIL::SigSpec, std::set<Cell *>> sig2CellsInFanin;
|
|
|
|
|
sigCellDrivers(module, sigmap, sig2CellsInFanout, sig2CellsInFanin);
|
|
|
|
|
// Precompute lhs2rhs and rhs2lhs sigspec map
|
|
|
|
|
dict<RTLIL::SigSpec, RTLIL::SigSpec> lhsSig2RhsSig;
|
|
|
|
|
dict<RTLIL::SigSpec, std::set<RTLIL::SigSpec>> rhsSig2LhsSig;
|
|
|
|
|
lhs2rhs_rhs2lhs(module, sigmap, rhsSig2LhsSig, lhsSig2RhsSig);
|
|
|
|
|
|
|
|
|
|
// Accumulate fanout from cell connections
|
|
|
|
|
dict<RTLIL::SigSpec, int> sigFanout;
|
|
|
|
|
for (auto itrSig : sig2CellsInFanout) {
|
|
|
|
|
SigSpec sigspec = itrSig.first;
|
|
|
|
|
std::set<Cell *> &cells = itrSig.second;
|
|
|
|
|
sigFanout[sigspec] = cells.size();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Accumulate fanout from assign stmts connections
|
|
|
|
|
for (auto itrSig : rhsSig2LhsSig) {
|
|
|
|
|
SigSpec sigspec = itrSig.first;
|
|
|
|
|
std::set<RTLIL::SigSpec> &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<Cell *> &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<Cell *> 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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-03 18:35:49 +01:00
|
|
|
std::string substringuntil(const std::string &str, char delimiter)
|
2025-03-01 07:08:19 +01:00
|
|
|
{
|
|
|
|
|
size_t pos = str.find(delimiter);
|
|
|
|
|
if (pos != std::string::npos) {
|
|
|
|
|
return str.substr(0, pos);
|
|
|
|
|
} else {
|
|
|
|
|
return str;
|
|
|
|
|
}
|
2025-03-01 06:54:28 +01:00
|
|
|
}
|
|
|
|
|
|
2025-01-31 19:17:34 +01:00
|
|
|
struct AnnotateCellFanout : public ScriptPass {
|
|
|
|
|
AnnotateCellFanout() : ScriptPass("annotate_cell_fanout", "Annotate the cell fanout on the cell") {}
|
|
|
|
|
void script() override {}
|
|
|
|
|
|
2025-02-27 01:17:09 +01:00
|
|
|
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
2025-01-31 19:17:34 +01:00
|
|
|
{
|
2025-02-27 01:17:09 +01:00
|
|
|
int limit = -1;
|
2025-03-01 07:08:19 +01:00
|
|
|
bool formalFriendly = false;
|
2025-03-03 18:35:49 +01:00
|
|
|
bool debug = false;
|
2025-01-31 19:17:34 +01:00
|
|
|
if (design == nullptr) {
|
2025-02-27 01:17:09 +01:00
|
|
|
log_error("No design object\n");
|
2025-01-31 19:17:34 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
log("Running annotate_cell_fanout pass\n");
|
|
|
|
|
log_flush();
|
|
|
|
|
|
2025-02-27 01:17:09 +01:00
|
|
|
size_t argidx;
|
|
|
|
|
for (argidx = 1; argidx < args.size(); argidx++) {
|
2025-03-03 18:35:49 +01:00
|
|
|
if (args[argidx] == "-debug") {
|
|
|
|
|
debug = true;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2025-02-27 01:17:09 +01:00
|
|
|
if (args[argidx] == "-limit") {
|
|
|
|
|
limit = std::atoi(args[++argidx].c_str());
|
|
|
|
|
continue;
|
2025-01-31 19:17:34 +01:00
|
|
|
}
|
2025-03-01 07:08:19 +01:00
|
|
|
if (args[argidx] == "-formal") {
|
|
|
|
|
formalFriendly = true;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2025-02-27 01:17:09 +01:00
|
|
|
break;
|
2025-01-31 19:17:34 +01:00
|
|
|
}
|
2025-02-27 01:17:09 +01:00
|
|
|
extra_args(args, argidx, design);
|
2025-02-27 01:51:39 +01:00
|
|
|
if ((limit != -1) && (limit < 2)) {
|
2025-02-27 01:17:09 +01:00
|
|
|
log_error("Fanout cannot be limited to less than 2\n");
|
|
|
|
|
return;
|
2025-01-31 19:17:34 +01:00
|
|
|
}
|
|
|
|
|
for (auto module : design->selected_modules()) {
|
2025-02-27 01:17:09 +01:00
|
|
|
bool fixedFanout = false;
|
|
|
|
|
{
|
2025-03-01 07:08:19 +01:00
|
|
|
// Split output nets of cells with high fanout
|
2025-02-28 21:07:58 +01:00
|
|
|
SigMap sigmap(module);
|
|
|
|
|
dict<Cell *, int> cellFanout;
|
|
|
|
|
dict<RTLIL::SigSpec, std::set<Cell *>> sig2CellsInFanout;
|
|
|
|
|
calculateFanout(module, sigmap, sig2CellsInFanout, cellFanout);
|
|
|
|
|
|
2025-03-01 07:08:19 +01:00
|
|
|
std::vector<Cell *> cellsToFixFanout;
|
2025-02-28 21:07:58 +01:00
|
|
|
for (auto itrCell : cellFanout) {
|
|
|
|
|
Cell *cell = itrCell.first;
|
|
|
|
|
int fanout = itrCell.second;
|
|
|
|
|
if (limit > 0 && (fanout > limit)) {
|
|
|
|
|
cellsToFixFanout.push_back(cell);
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-03-01 06:54:28 +01:00
|
|
|
std::set<std::string> netsToSplitS;
|
2025-02-28 21:07:58 +01:00
|
|
|
std::string netsToSplit;
|
2025-03-01 06:54:28 +01:00
|
|
|
std::string portsToSplit;
|
2025-03-01 07:08:19 +01:00
|
|
|
for (Cell *cell : cellsToFixFanout) {
|
2025-02-28 21:07:58 +01:00
|
|
|
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;
|
2025-03-01 07:08:19 +01:00
|
|
|
}
|
2025-02-28 21:07:58 +01:00
|
|
|
}
|
2025-03-01 06:54:28 +01:00
|
|
|
std::string parent = getParentWire(cellOutSig)->name.c_str();
|
2025-03-03 18:35:49 +01:00
|
|
|
parent = substringuntil(parent, '[');
|
2025-03-01 06:54:28 +01:00
|
|
|
if (netsToSplitS.find(parent) == netsToSplitS.end()) {
|
2025-03-01 07:08:19 +01:00
|
|
|
netsToSplit += std::string(" w:") + parent; // Wire
|
2025-03-03 18:35:49 +01:00
|
|
|
portsToSplit += std::string(" o:") + parent; // Output port
|
2025-03-01 06:54:28 +01:00
|
|
|
netsToSplitS.insert(parent);
|
2025-03-03 18:35:49 +01:00
|
|
|
// Splitnets has to be invoke with individual nets, sending a bunch of nets as selection
|
|
|
|
|
// selects more than required (bug in selection/splitnets).
|
2025-03-01 06:54:28 +01:00
|
|
|
std::string splitnets = std::string("splitnets ") + netsToSplit;
|
|
|
|
|
Pass::call(design, splitnets);
|
|
|
|
|
splitnets = std::string("splitnets -ports_only ") + portsToSplit;
|
2025-03-03 19:08:27 +01:00
|
|
|
if (!formalFriendly)
|
2025-03-01 07:08:19 +01:00
|
|
|
// Formal verification does not like ports to be split.
|
2025-03-03 19:08:27 +01:00
|
|
|
// 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);
|
2025-03-01 06:54:28 +01:00
|
|
|
netsToSplit = "";
|
|
|
|
|
portsToSplit = "";
|
|
|
|
|
}
|
2025-02-28 21:07:58 +01:00
|
|
|
}
|
|
|
|
|
}
|
2025-03-01 07:08:19 +01:00
|
|
|
|
2025-02-28 21:07:58 +01:00
|
|
|
{
|
|
|
|
|
// Fix high fanout
|
2025-02-27 01:17:09 +01:00
|
|
|
SigMap sigmap(module);
|
|
|
|
|
dict<Cell *, int> cellFanout;
|
|
|
|
|
dict<RTLIL::SigSpec, std::set<Cell *>> sig2CellsInFanout;
|
|
|
|
|
calculateFanout(module, sigmap, sig2CellsInFanout, cellFanout);
|
2025-02-28 21:07:58 +01:00
|
|
|
|
2025-02-27 01:17:09 +01:00
|
|
|
for (auto itrCell : cellFanout) {
|
|
|
|
|
Cell *cell = itrCell.first;
|
|
|
|
|
int fanout = itrCell.second;
|
|
|
|
|
if (limit > 0 && (fanout > limit)) {
|
2025-03-03 18:35:49 +01:00
|
|
|
fixfanout(design, module, sigmap, sig2CellsInFanout, cell, fanout, limit, debug);
|
2025-02-27 01:17:09 +01:00
|
|
|
fixedFanout = true;
|
|
|
|
|
} else {
|
2025-02-28 21:07:58 +01:00
|
|
|
// Add attribute with fanout info to every cell
|
2025-02-27 01:17:09 +01:00
|
|
|
cell->set_string_attribute("$FANOUT", std::to_string(fanout));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-02-28 23:50:17 +01:00
|
|
|
|
2025-02-27 01:17:09 +01:00
|
|
|
if (fixedFanout) {
|
2025-03-01 07:08:19 +01:00
|
|
|
// If Fanout got fixed, recalculate and annotate final fanout
|
2025-02-27 01:17:09 +01:00
|
|
|
SigMap sigmap(module);
|
|
|
|
|
dict<Cell *, int> cellFanout;
|
|
|
|
|
dict<RTLIL::SigSpec, std::set<Cell *>> sig2CellsInFanout;
|
|
|
|
|
calculateFanout(module, sigmap, sig2CellsInFanout, cellFanout);
|
|
|
|
|
for (auto itrCell : cellFanout) {
|
|
|
|
|
Cell *cell = itrCell.first;
|
|
|
|
|
int fanout = itrCell.second;
|
2025-02-28 21:07:58 +01:00
|
|
|
// Add attribute with fanout info to every cell
|
2025-02-27 01:17:09 +01:00
|
|
|
cell->set_string_attribute("$FANOUT", std::to_string(fanout));
|
2025-01-31 19:17:34 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log("End annotate_cell_fanout pass\n");
|
|
|
|
|
log_flush();
|
|
|
|
|
}
|
|
|
|
|
} SplitNetlist;
|
|
|
|
|
|
|
|
|
|
PRIVATE_NAMESPACE_END
|