From f03c101fdf0f566ad5ae7c51ec0f2e1bbf72388b Mon Sep 17 00:00:00 2001 From: Alain Dargelas Date: Sun, 20 Oct 2024 21:36:04 -0700 Subject: [PATCH 1/4] Split netlist pass --- Makefile | 1 + passes/cmds/splitnetlist.cc | 252 ++++++++++++++++++++++++++++++++++++ 2 files changed, 253 insertions(+) create mode 100644 passes/cmds/splitnetlist.cc diff --git a/Makefile b/Makefile index 3770370d8..b07775252 100644 --- a/Makefile +++ b/Makefile @@ -750,6 +750,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..1e5639bc6 --- /dev/null +++ b/passes/cmds/splitnetlist.cc @@ -0,0 +1,252 @@ +#include "kernel/sigtools.h" +#include "kernel/yosys.h" +#include + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +int sigIsConstant(RTLIL::SigSpec sig) +{ + if (sig.is_chunk()) { + if ((sig.as_chunk()).wire == NULL) { + return 1; + } + } + return 0; +} + +void recordTransFanin(RTLIL::SigSpec &sig, dict *> &sig2CellsInFanin, + dict &lhsSig2RhsSig, std::set &visitedCells, std::set &visitedSigSpec) +{ + if (sigIsConstant(sig)) { + 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) +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 +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 (sigIsConstant(rhs)) { + 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 (sigIsConstant(sub_rhs)) { + 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 (sigIsConstant(sub_rhs)) { + 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") {} + void script() {} + + void execute(std::vector, RTLIL::Design *design) override + { + if (design == nullptr) { + log_error("No design object"); + return; + } + dict *> sig2CellsInFanin; + sigCellDrivers(design, sig2CellsInFanin); + dict lhsSig2RhsSig; + lhs2rhs(design, lhsSig2RhsSig); + typedef struct CellsAndSigs { + std::set visitedCells; + std::set visitedSigSpec; + } CellsAndSigs; + typedef std::map CellName_ObjectMap; + CellName_ObjectMap cellName_ObjectMap; + 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; + recordTransFanin(actual, sig2CellsInFanin, lhsSig2RhsSig, visitedCells, visitedSigSpec); + for (int i = 0; i < actual.size(); i++) { + SigSpec bit_sig = actual.extract(i, 1); + recordTransFanin(bit_sig, sig2CellsInFanin, lhsSig2RhsSig, visitedCells, visitedSigSpec); + } + + 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); + } + } + } + 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; + } + Pass::call(design, "submod -copy -noclean"); + // Rename all the modules + std::set modules; + for (Module *module : design->modules()) { + modules.insert(module); + } + std::string topmodule_name = id2String(design->top_module()->name); + for (Module *module : modules) { + std::string name = id2String(module->name); + name = replaceAll(name, topmodule_name + "_\\fast_", ""); + name = replaceAll(name, topmodule_name + "_\\slow_", ""); + name = "\\" + name; + design->rename(module, name); + } + } +} SplitNetlist; + +PRIVATE_NAMESPACE_END From b12ff1a63b77223d2451a6694520f4596bb81902 Mon Sep 17 00:00:00 2001 From: Alain Dargelas Date: Mon, 21 Oct 2024 09:41:04 -0700 Subject: [PATCH 2/4] rename fix --- passes/cmds/splitnetlist.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/passes/cmds/splitnetlist.cc b/passes/cmds/splitnetlist.cc index 1e5639bc6..95a361581 100644 --- a/passes/cmds/splitnetlist.cc +++ b/passes/cmds/splitnetlist.cc @@ -241,9 +241,8 @@ struct SplitNetlist : public ScriptPass { std::string topmodule_name = id2String(design->top_module()->name); for (Module *module : modules) { std::string name = id2String(module->name); - name = replaceAll(name, topmodule_name + "_\\fast_", ""); - name = replaceAll(name, topmodule_name + "_\\slow_", ""); - name = "\\" + name; + name = replaceAll(name, "\\" + topmodule_name + "_\\fast_", "\\"); + name = replaceAll(name, "\\" + topmodule_name + "_\\slow_", "\\"); design->rename(module, name); } } From c2aa611e5d523d13721f6873cbc151c5df7c2c7c Mon Sep 17 00:00:00 2001 From: Alain Dargelas Date: Mon, 21 Oct 2024 15:53:48 -0700 Subject: [PATCH 3/4] Fix comments and add freq annotation in sim pass --- passes/cmds/splitnetlist.cc | 68 ++++++++++++++++--------------------- passes/sat/sim.cc | 5 +-- 2 files changed, 33 insertions(+), 40 deletions(-) diff --git a/passes/cmds/splitnetlist.cc b/passes/cmds/splitnetlist.cc index 95a361581..bedb89088 100644 --- a/passes/cmds/splitnetlist.cc +++ b/passes/cmds/splitnetlist.cc @@ -5,20 +5,12 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -int sigIsConstant(RTLIL::SigSpec sig) -{ - if (sig.is_chunk()) { - if ((sig.as_chunk()).wire == NULL) { - return 1; - } - } - return 0; -} - +// 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 (sigIsConstant(sig)) { + if (sig.is_fully_const()) { return; } if (visitedSigSpec.count(sig)) { @@ -43,7 +35,6 @@ void recordTransFanin(RTLIL::SigSpec &sig, dict RTLIL::SigSpec sub_actual = *it; recordTransFanin(sub_actual, sig2CellsInFanin, lhsSig2RhsSig, visitedCells, visitedSigSpec); } - } else { recordTransFanin(actual, sig2CellsInFanin, lhsSig2RhsSig, visitedCells, visitedSigSpec); } @@ -57,7 +48,7 @@ void recordTransFanin(RTLIL::SigSpec &sig, dict } } -// Signal cell driver(s) +// 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()) { @@ -91,13 +82,13 @@ void sigCellDrivers(RTLIL::Design *design, dict } } -// Assign statements fanin +// 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 (sigIsConstant(rhs)) { + if (rhs.is_fully_const()) { continue; } if (!lhs.is_chunk()) { @@ -106,7 +97,7 @@ void lhs2rhs(RTLIL::Design *design, dict &lhsSig long unsigned rhsSize = 0; while (rit != rhs.chunks().rend()) { RTLIL::SigSpec sub_rhs = *rit; - if (sigIsConstant(sub_rhs)) { + if (sub_rhs.is_fully_const()) { rhsSize += (sub_rhs.as_chunk()).width; } else { rhsSize++; @@ -122,7 +113,7 @@ void lhs2rhs(RTLIL::Design *design, dict &lhsSig while (rit != rhs.chunks().rend()) { RTLIL::SigSpec sub_lhs = *lit; RTLIL::SigSpec sub_rhs = *rit; - if (sigIsConstant(sub_rhs)) { + if (sub_rhs.is_fully_const()) { int constSize = (sub_rhs.as_chunk()).width; while (constSize--) { lit++; @@ -167,7 +158,11 @@ std::string replaceAll(std::string_view str, std::string_view from, std::string_ } struct SplitNetlist : public ScriptPass { - SplitNetlist() : ScriptPass("splitnetlist", "Splits a netlist into multiple modules using transitive fanin grouping") {} + 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 @@ -176,16 +171,21 @@ struct SplitNetlist : public ScriptPass { 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; @@ -194,12 +194,14 @@ struct SplitNetlist : public ScriptPass { 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; @@ -211,7 +213,7 @@ struct SplitNetlist : public ScriptPass { } cellName_ObjectMap.emplace(std::string(po_prefix), components); } else { - CellsAndSigs &components = (*itr).second; + CellsAndSigs &components = itr->second; for (auto cell : visitedCells) { components.visitedCells.insert(cell); } @@ -220,31 +222,21 @@ struct SplitNetlist : public ScriptPass { } } } + // 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; + // 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); + 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; - //} + // 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 -noclean"); - // Rename all the modules - std::set modules; - for (Module *module : design->modules()) { - modules.insert(module); - } - std::string topmodule_name = id2String(design->top_module()->name); - for (Module *module : modules) { - std::string name = id2String(module->name); - name = replaceAll(name, "\\" + topmodule_name + "_\\fast_", "\\"); - name = replaceAll(name, "\\" + topmodule_name + "_\\slow_", "\\"); - design->rename(module, name); - } } } SplitNetlist; 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"; From 6d395134d070298f9e0a5f20512d86261569d91a Mon Sep 17 00:00:00 2001 From: Alain Dargelas Date: Mon, 21 Oct 2024 16:23:04 -0700 Subject: [PATCH 4/4] No -noclean --- passes/cmds/splitnetlist.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/passes/cmds/splitnetlist.cc b/passes/cmds/splitnetlist.cc index bedb89088..2ea94c78e 100644 --- a/passes/cmds/splitnetlist.cc +++ b/passes/cmds/splitnetlist.cc @@ -236,7 +236,7 @@ struct SplitNetlist : public ScriptPass { // std::cout << std::endl; } // Execute the submod command - Pass::call(design, "submod -copy -noclean"); + Pass::call(design, "submod -copy"); } } SplitNetlist;