diff --git a/Makefile b/Makefile index 6639eb317..019db4559 100644 --- a/Makefile +++ b/Makefile @@ -744,6 +744,7 @@ OBJS += passes/cmds/splitfanout.o OBJS += passes/cmds/splitnets.o OBJS += passes/cmds/tee.o OBJS += passes/cmds/activity.o +OBJS += passes/cmds/splitnetlist.o OBJS += passes/sat/sim.o include $(YOSYS_SRC)/passes/hierarchy/Makefile.inc diff --git a/passes/cmds/splitnetlist.cc b/passes/cmds/splitnetlist.cc new file mode 100644 index 000000000..2ea94c78e --- /dev/null +++ b/passes/cmds/splitnetlist.cc @@ -0,0 +1,243 @@ +#include "kernel/sigtools.h" +#include "kernel/yosys.h" +#include + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +// Recursively traverses backward from a sig, record if a cell was traversed, and push onto the cell's inputs. +// Similarly with assign statements traverses lhs -> rhs +void recordTransFanin(RTLIL::SigSpec &sig, dict *> &sig2CellsInFanin, + dict &lhsSig2RhsSig, std::set &visitedCells, std::set &visitedSigSpec) +{ + if (sig.is_fully_const()) { + return; + } + if (visitedSigSpec.count(sig)) { + return; + } + visitedSigSpec.insert(sig); + if (sig2CellsInFanin.count(sig)) { + std::set *sigFanin = sig2CellsInFanin[sig]; + for (std::set::iterator it = sigFanin->begin(); it != sigFanin->end(); it++) { + Cell *cell = *it; + if (visitedCells.count(cell)) { + continue; + } + visitedCells.insert(cell); + for (auto &conn : cell->connections()) { + IdString portName = conn.first; + RTLIL::SigSpec actual = conn.second; + + if (cell->input(portName)) { + if (!actual.is_chunk()) { + for (auto it = actual.chunks().rbegin(); it != actual.chunks().rend(); ++it) { + RTLIL::SigSpec sub_actual = *it; + recordTransFanin(sub_actual, sig2CellsInFanin, lhsSig2RhsSig, visitedCells, visitedSigSpec); + } + } else { + recordTransFanin(actual, sig2CellsInFanin, lhsSig2RhsSig, visitedCells, visitedSigSpec); + } + } + } + } + } + if (lhsSig2RhsSig.count(sig)) { + RTLIL::SigSpec rhs = lhsSig2RhsSig[sig]; + recordTransFanin(rhs, sig2CellsInFanin, lhsSig2RhsSig, visitedCells, visitedSigSpec); + } +} + +// Signal cell driver(s), precompute a cell output signal to a cell map +void sigCellDrivers(RTLIL::Design *design, dict *> &sig2CellsInFanin) +{ + for (auto cell : design->top_module()->cells()) { + for (auto &conn : cell->connections()) { + IdString portName = conn.first; + RTLIL::SigSpec actual = conn.second; + std::set *newSet; + if (cell->output(portName)) { + if (!actual.is_chunk()) { + for (auto it = actual.chunks().rbegin(); it != actual.chunks().rend(); ++it) { + RTLIL::SigSpec sub_actual = *it; + if (sig2CellsInFanin.count(sub_actual)) { + newSet = sig2CellsInFanin[sub_actual]; + } else { + newSet = new std::set; + sig2CellsInFanin[sub_actual] = newSet; + } + newSet->insert(cell); + } + } else { + if (sig2CellsInFanin.count(actual)) { + newSet = sig2CellsInFanin[actual]; + } else { + newSet = new std::set; + sig2CellsInFanin[actual] = newSet; + } + newSet->insert(cell); + } + } + } + } +} + +// Assign statements fanin, traces the lhs to rhs sigspecs and precompute a map +void lhs2rhs(RTLIL::Design *design, dict &lhsSig2rhsSig) +{ + for (auto it = design->top_module()->connections().begin(); it != design->top_module()->connections().end(); ++it) { + RTLIL::SigSpec lhs = it->first; + RTLIL::SigSpec rhs = it->second; + if (rhs.is_fully_const()) { + continue; + } + if (!lhs.is_chunk()) { + if (lhs.chunks().size() != rhs.chunks().size()) { + auto rit = rhs.chunks().rbegin(); + long unsigned rhsSize = 0; + while (rit != rhs.chunks().rend()) { + RTLIL::SigSpec sub_rhs = *rit; + if (sub_rhs.is_fully_const()) { + rhsSize += (sub_rhs.as_chunk()).width; + } else { + rhsSize++; + } + rit++; + } + if (lhs.chunks().size() != rhsSize) { + continue; + } + } + auto lit = lhs.chunks().rbegin(); + auto rit = rhs.chunks().rbegin(); + while (rit != rhs.chunks().rend()) { + RTLIL::SigSpec sub_lhs = *lit; + RTLIL::SigSpec sub_rhs = *rit; + if (sub_rhs.is_fully_const()) { + int constSize = (sub_rhs.as_chunk()).width; + while (constSize--) { + lit++; + } + rit++; + continue; + } + lhsSig2rhsSig[sub_lhs] = sub_rhs; + lit++; + rit++; + } + } else { + lhsSig2rhsSig[lhs] = rhs; + } + } +} + +std::string_view rtrim_until(std::string_view str, char c) +{ + auto pos = str.rfind(c); + if (pos != std::string_view::npos) + str = str.substr(0, pos); + return str; +} + +std::string id2String(RTLIL::IdString internal_id) +{ + const char *str = internal_id.c_str(); + std::string result = str; + return result; +} + +std::string replaceAll(std::string_view str, std::string_view from, std::string_view to) +{ + size_t start_pos = 0; + std::string result(str); + while ((start_pos = result.find(from, start_pos)) != std::string::npos) { + result.replace(start_pos, from.length(), to); + start_pos += to.length(); // Handles case where 'to' is a substr of 'from' + } + return result; +} + +struct SplitNetlist : public ScriptPass { + SplitNetlist() + : ScriptPass("splitnetlist", "Splits a netlist into multiple modules using transitive fanin grouping. \ + The output names that belong in the same logical cluster have to have the same prefix: _") + { + } + void script() {} + + void execute(std::vector, RTLIL::Design *design) override + { + if (design == nullptr) { + log_error("No design object"); + return; + } + // Precompute cell output sigspec to cell map + dict *> sig2CellsInFanin; + sigCellDrivers(design, sig2CellsInFanin); + // Precompute lhs to rhs sigspec map + dict lhsSig2RhsSig; + lhs2rhs(design, lhsSig2RhsSig); + // Struct representing a cluster + typedef struct CellsAndSigs { + std::set visitedCells; + std::set visitedSigSpec; + } CellsAndSigs; + // Cluster mapped by prefix + typedef std::map CellName_ObjectMap; + CellName_ObjectMap cellName_ObjectMap; + // Record logic cone by output sharing the same prefix + for (auto wire : design->top_module()->wires()) { + if (!wire->port_output) + continue; + std::string output_port_name = wire->name.c_str(); + std::string_view po_prefix = rtrim_until(std::string_view(output_port_name), '_'); + std::set visitedCells; + std::set visitedSigSpec; + RTLIL::SigSpec actual = wire; + // Visit the output sigspec + recordTransFanin(actual, sig2CellsInFanin, lhsSig2RhsSig, visitedCells, visitedSigSpec); + // Visit the output sigspec bits + for (int i = 0; i < actual.size(); i++) { + SigSpec bit_sig = actual.extract(i, 1); + recordTransFanin(bit_sig, sig2CellsInFanin, lhsSig2RhsSig, visitedCells, visitedSigSpec); + } + // Record the visited objects in the corresponding cluster + CellName_ObjectMap::iterator itr = cellName_ObjectMap.find(std::string(po_prefix)); + if (itr == cellName_ObjectMap.end()) { + CellsAndSigs components; + for (auto cell : visitedCells) { + components.visitedCells.insert(cell); + } + for (auto sig : visitedSigSpec) { + components.visitedSigSpec.insert(sig); + } + cellName_ObjectMap.emplace(std::string(po_prefix), components); + } else { + CellsAndSigs &components = itr->second; + for (auto cell : visitedCells) { + components.visitedCells.insert(cell); + } + for (auto sig : visitedSigSpec) { + components.visitedSigSpec.insert(sig); + } + } + } + // Create submod attributes for the submod command + for (CellName_ObjectMap::iterator itr = cellName_ObjectMap.begin(); itr != cellName_ObjectMap.end(); itr++) { + // std::cout << "Cluster name: " << itr->first << std::endl; + CellsAndSigs &components = itr->second; + for (auto cell : components.visitedCells) { + cell->set_string_attribute(RTLIL::escape_id("submod"), itr->first); + // std::cout << " CELL: " << cell->name.c_str() << std::endl; + } + // for (auto sigspec : components.visitedSigSpec) { + // std::cout << " SIG: " << SigName(sigspec) << std::endl; + // } + // std::cout << std::endl; + } + // Execute the submod command + Pass::call(design, "submod -copy"); + } +} SplitNetlist; + +PRIVATE_NAMESPACE_END diff --git a/passes/sat/sim.cc b/passes/sat/sim.cc index 395c5abb5..c4bfe3ea1 100644 --- a/passes/sat/sim.cc +++ b/passes/sat/sim.cc @@ -2355,8 +2355,7 @@ struct VCDWriter : public OutputWriter } if (!worker->timescale.empty()) - vcdfile << stringf("$timescale %s $end\n", worker->timescale.c_str()); - + vcdfile << stringf("$timescale 1%s $end\n", worker->timescale.c_str()); worker->top->write_output_header( [this](IdString name) { vcdfile << stringf("$scope module %s $end\n", log_id(name)); }, [this]() { vcdfile << stringf("$upscope $end\n");}, @@ -2500,6 +2499,8 @@ struct AnnotateActivity : public OutputWriter { SignalActivityDataMap::iterator itr = dataMap.find(clk); std::vector &clktoggleCounts = itr->second.toggleCounts; double clk_period = real_timescale * (double)max_time / (clktoggleCounts[0] / 2); + double frequency = 1 / clk_period; + worker->top->module->set_string_attribute("$FREQUENCY", std::to_string(frequency)); if (debug) { std::cout << "Clock toggle count: " << clktoggleCounts[0] << "\n"; std::cout << "Max time: " << max_time << "\n";