From c459a74c13ef4fd723bbaa45914dfaf642bb84dd Mon Sep 17 00:00:00 2001 From: Stan Lee Date: Sun, 1 Mar 2026 15:39:35 -0800 Subject: [PATCH 1/6] autoscoping --- kernel/fstdata.cc | 74 +++++++++++++++++++++++++++++++++++ kernel/fstdata.h | 1 + passes/sat/sim.cc | 18 ++++++++- passes/silimate/reg_rename.cc | 29 +++++++++++--- 4 files changed, 114 insertions(+), 8 deletions(-) diff --git a/kernel/fstdata.cc b/kernel/fstdata.cc index f0f00181c..52f690774 100644 --- a/kernel/fstdata.cc +++ b/kernel/fstdata.cc @@ -280,3 +280,77 @@ std::string FstData::valueOf(fstHandle signal) } return past_data[signal]; } + +// Auto-discover scope from FST by finding the top module +std::string FstData::autoScope(Module *topmod) { + + log("Auto-discovering scope from file...\n"); + std::string top = RTLIL::unescape_id(topmod->name); + + // Option 1 - Instance based scope matching + // Will fail if the DUT instance name != the top module name + log("Trying instance-based scope matching...\n"); + for (const auto& var : vars) { + // Check if this scope ends with our top module + log_debug("Checking scope: %s\n", var.scope.c_str()); + if (var.scope == top || + var.scope.find("." + top) != std::string::npos) { + // Extract the full path up to (and including) the top module + size_t pos = var.scope.find(top); + if (pos != std::string::npos) { + return var.scope.substr(0, pos + top.length()); + } + } + } + + // Option 2 - Post based scope matching + // Matches based on exact port name matching of the top module + log("Trying port-based scope matching...\n"); + + // Map port name to their bit widths (RTL reference point) + dict ports; + for (auto wire : topmod->wires()) { + if (wire->port_input || wire->port_output) { + ports[RTLIL::unescape_id(wire->name)] = wire->width; + } + } + + // For each scope, track which ports were found with matching width + // (VCD reference point) + dict> scope_found_ports; + for (const auto& var : vars) { + + // Strip array '[]' notation from variable name + std::string var_name = var.name; + size_t bracket = var_name.find('['); + if (bracket != std::string::npos) { + var_name = var_name.substr(0, bracket); + } + + // Check if this variable name matches one of our port names + if (ports.count(var_name)) { + // Also check if width matches + if (ports[var_name] == var.width) { + scope_found_ports[var.scope].insert(var_name); + } + } + } + + // Compare RTl and VCD references and find an exact match + for (const auto& entry : scope_found_ports) { + const std::string& scope = entry.first; + const std::set& found = entry.second; + + // Check if all port names exist in this scope + if (found.size() == ports.size()) { + log("Auto-discovered scope: %s (matched all %d ports by name)\n", + scope.c_str(), (int)ports.size()); + return scope; + } + } + + // No match found + log_warning("Could not auto-discover scope for module '%s'...\n", + RTLIL::unescape_id(topmod->name).c_str()); + return ""; +} diff --git a/kernel/fstdata.h b/kernel/fstdata.h index a8ae40301..ba262dcf2 100644 --- a/kernel/fstdata.h +++ b/kernel/fstdata.h @@ -57,6 +57,7 @@ class FstData dict getMemoryHandles(std::string name); double getTimescale() { return timescale; } const char *getTimescaleString() { return timescale_str.c_str(); } + std::string autoScope(Module *topmod); private: void extractVarNames(); diff --git a/passes/sat/sim.cc b/passes/sat/sim.cc index 3530bc7af..6f980ed56 100644 --- a/passes/sat/sim.cc +++ b/passes/sat/sim.cc @@ -1474,8 +1474,22 @@ struct SimWorker : SimShared log_assert(top == nullptr); fst = new FstData(sim_filename); timescale = fst->getTimescaleString(); - if (scope.empty()) - log_error("Scope must be defined for co-simulation.\n"); + if (scope.empty()) { + scope = fst->autoScope(topmod); + if (scope.empty()) { + std::set unique_scopes; + for (const auto& var : fst->getVars()) { + unique_scopes.insert(var.scope); + } + log_warning("Available scopes:\n"); + for (const auto& scope : unique_scopes) { + log_warning(" %s\n", scope.c_str()); + } + log_error("No scope found for module '%s'. Please specify -scope explicitly with above options.\n", + RTLIL::unescape_id(topmod->name).c_str()); + } + } + log("Using scope: \"%s\"\n", scope.c_str()); top = new SimInstance(this, scope, topmod); register_signals(); diff --git a/passes/silimate/reg_rename.cc b/passes/silimate/reg_rename.cc index b4a4871f2..066633780 100644 --- a/passes/silimate/reg_rename.cc +++ b/passes/silimate/reg_rename.cc @@ -188,15 +188,35 @@ struct RegRenamePass : public Pass { } extra_args(args, argidx, design); + // Extract top module + Module *topmod = design->top_module(); + if (!topmod) + log_error("No top module found!\n"); + // Extract pre-optimization register widths from VCD file dict, int> vcd_reg_widths; if (!vcd_filename.empty()) { - if (scope.empty()) { - log_error("No scope provided. Use -scope option.\n"); - } log("Reading VCD file: %s\n", vcd_filename.c_str()); try { FstData fst(vcd_filename); + if (scope.empty()) { + scope = fst.autoScope(topmod); + if (scope.empty()) { + log_warning("No scope found for module '%s'. Please specify -scope explicitly.\n", + RTLIL::unescape_id(topmod->name).c_str()); + std::set unique_scopes; + for (const auto& var : fst.getVars()) { + unique_scopes.insert(var.scope); + } + log_warning("Available scopes:\n"); + for (const auto& scope : unique_scopes) { + log_warning(" %s\n", scope.c_str()); + } + log_error("No scope found for module '%s'. Please specify -scope explicitly with above options.\n", + RTLIL::unescape_id(topmod->name).c_str()); + } + } + log("Using scope: \"%s\"\n", scope.c_str()); for (auto &var : fst.getVars()) { if (var.is_reg) { std::string reg_vcd_scope = var.scope; @@ -223,9 +243,6 @@ struct RegRenamePass : public Pass { } // STEP 2: Build hierarchy and process - Module *topmod = design->top_module(); - if (!topmod) - log_error("No top module found!\n"); log("Building hierarchy from scope: %s\n", scope.c_str()); // Build hierarchy and process register renamings From 6594ff508f5b1aef983242eeb1ce13e86fddd847 Mon Sep 17 00:00:00 2001 From: Stan Lee Date: Mon, 2 Mar 2026 00:42:34 -0800 Subject: [PATCH 2/6] improvement --- kernel/fstdata.cc | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/kernel/fstdata.cc b/kernel/fstdata.cc index 52f690774..377ad1a74 100644 --- a/kernel/fstdata.cc +++ b/kernel/fstdata.cc @@ -308,10 +308,10 @@ std::string FstData::autoScope(Module *topmod) { log("Trying port-based scope matching...\n"); // Map port name to their bit widths (RTL reference point) - dict ports; + dict ports2widths; for (auto wire : topmod->wires()) { if (wire->port_input || wire->port_output) { - ports[RTLIL::unescape_id(wire->name)] = wire->width; + ports2widths[RTLIL::unescape_id(wire->name)] = wire->width; } } @@ -322,15 +322,16 @@ std::string FstData::autoScope(Module *topmod) { // Strip array '[]' notation from variable name std::string var_name = var.name; + log("Checking variable: %s with scope: %s\n", var_name.c_str(), var.scope.c_str()); size_t bracket = var_name.find('['); if (bracket != std::string::npos) { var_name = var_name.substr(0, bracket); } // Check if this variable name matches one of our port names - if (ports.count(var_name)) { + if (ports2widths.count(var_name)) { // Also check if width matches - if (ports[var_name] == var.width) { + if (ports2widths[var_name] == var.width) { scope_found_ports[var.scope].insert(var_name); } } @@ -342,9 +343,9 @@ std::string FstData::autoScope(Module *topmod) { const std::set& found = entry.second; // Check if all port names exist in this scope - if (found.size() == ports.size()) { + if (found.size() == ports2widths.size()) { log("Auto-discovered scope: %s (matched all %d ports by name)\n", - scope.c_str(), (int)ports.size()); + scope.c_str(), (int)ports2widths.size()); return scope; } } From da25b800bc2818aa4a870e1577d1b321b7e604f5 Mon Sep 17 00:00:00 2001 From: Stan Lee Date: Mon, 2 Mar 2026 11:05:44 -0800 Subject: [PATCH 3/6] finalized --- kernel/fstdata.cc | 54 ++++++++++++++++++----------------- passes/sat/sim.cc | 10 +------ passes/silimate/reg_rename.cc | 12 +------- 3 files changed, 30 insertions(+), 46 deletions(-) diff --git a/kernel/fstdata.cc b/kernel/fstdata.cc index 377ad1a74..35e9c139e 100644 --- a/kernel/fstdata.cc +++ b/kernel/fstdata.cc @@ -298,7 +298,9 @@ std::string FstData::autoScope(Module *topmod) { // Extract the full path up to (and including) the top module size_t pos = var.scope.find(top); if (pos != std::string::npos) { - return var.scope.substr(0, pos + top.length()); + std::string scope = var.scope.substr(0, pos + top.length()); + log("Found scope: %s\n", scope.c_str()); + return scope; } } } @@ -307,47 +309,47 @@ std::string FstData::autoScope(Module *topmod) { // Matches based on exact port name matching of the top module log("Trying port-based scope matching...\n"); - // Map port name to their bit widths (RTL reference point) - dict ports2widths; + // Map top moduleport name to their bit widths (RTL reference point) + dict top2widths; for (auto wire : topmod->wires()) { if (wire->port_input || wire->port_output) { - ports2widths[RTLIL::unescape_id(wire->name)] = wire->width; + log("Extracted %d ports from top module\n", GetSize(top2widths)); + top2widths[RTLIL::unescape_id(wire->name)] = wire->width; } } - // For each scope, track which ports were found with matching width - // (VCD reference point) - dict> scope_found_ports; + // For each scope, track the number of matching ports + dict scopes2matches; for (const auto& var : vars) { // Strip array '[]' notation from variable name std::string var_name = var.name; - log("Checking variable: %s with scope: %s\n", var_name.c_str(), var.scope.c_str()); size_t bracket = var_name.find('['); if (bracket != std::string::npos) { var_name = var_name.substr(0, bracket); } - - // Check if this variable name matches one of our port names - if (ports2widths.count(var_name)) { - // Also check if width matches - if (ports2widths[var_name] == var.width) { - scope_found_ports[var.scope].insert(var_name); + + // Check if this variable name matches one of our top module port names and width + if (top2widths.count(var_name) && top2widths[var_name] == var.width) { + scopes2matches[var.scope] += 1; + } + } + + // Find scopes with exact matches + // If there is a tie, return the longest scope + std::string result = ""; + for (const auto& entry : scopes2matches) { + int num_matches = entry.second; + if (num_matches == GetSize(top2widths)) { + std::string scope = entry.first; + if (result.empty() || scope.length() > result.length()) { + result = scope; } } } - - // Compare RTl and VCD references and find an exact match - for (const auto& entry : scope_found_ports) { - const std::string& scope = entry.first; - const std::set& found = entry.second; - - // Check if all port names exist in this scope - if (found.size() == ports2widths.size()) { - log("Auto-discovered scope: %s (matched all %d ports by name)\n", - scope.c_str(), (int)ports2widths.size()); - return scope; - } + if (!result.empty()) { + log("Found scope: %s\n", result.c_str()); + return result; } // No match found diff --git a/passes/sat/sim.cc b/passes/sat/sim.cc index 6f980ed56..ed7ed71ac 100644 --- a/passes/sat/sim.cc +++ b/passes/sat/sim.cc @@ -1477,15 +1477,7 @@ struct SimWorker : SimShared if (scope.empty()) { scope = fst->autoScope(topmod); if (scope.empty()) { - std::set unique_scopes; - for (const auto& var : fst->getVars()) { - unique_scopes.insert(var.scope); - } - log_warning("Available scopes:\n"); - for (const auto& scope : unique_scopes) { - log_warning(" %s\n", scope.c_str()); - } - log_error("No scope found for module '%s'. Please specify -scope explicitly with above options.\n", + log_error("No scope found for module '%s'. Please specify -scope explicitly.\n", RTLIL::unescape_id(topmod->name).c_str()); } } diff --git a/passes/silimate/reg_rename.cc b/passes/silimate/reg_rename.cc index 066633780..5635b6869 100644 --- a/passes/silimate/reg_rename.cc +++ b/passes/silimate/reg_rename.cc @@ -202,17 +202,7 @@ struct RegRenamePass : public Pass { if (scope.empty()) { scope = fst.autoScope(topmod); if (scope.empty()) { - log_warning("No scope found for module '%s'. Please specify -scope explicitly.\n", - RTLIL::unescape_id(topmod->name).c_str()); - std::set unique_scopes; - for (const auto& var : fst.getVars()) { - unique_scopes.insert(var.scope); - } - log_warning("Available scopes:\n"); - for (const auto& scope : unique_scopes) { - log_warning(" %s\n", scope.c_str()); - } - log_error("No scope found for module '%s'. Please specify -scope explicitly with above options.\n", + log_error("No scope found for module '%s'. Please specify -scope explicitly.\n", RTLIL::unescape_id(topmod->name).c_str()); } } From 83e05a6509c98e55c112726afcbe7204fb2f9259 Mon Sep 17 00:00:00 2001 From: Stan Lee Date: Mon, 2 Mar 2026 12:07:59 -0800 Subject: [PATCH 4/6] fixes --- kernel/fstdata.cc | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/kernel/fstdata.cc b/kernel/fstdata.cc index 35e9c139e..d34138e28 100644 --- a/kernel/fstdata.cc +++ b/kernel/fstdata.cc @@ -299,7 +299,6 @@ std::string FstData::autoScope(Module *topmod) { size_t pos = var.scope.find(top); if (pos != std::string::npos) { std::string scope = var.scope.substr(0, pos + top.length()); - log("Found scope: %s\n", scope.c_str()); return scope; } } @@ -313,10 +312,10 @@ std::string FstData::autoScope(Module *topmod) { dict top2widths; for (auto wire : topmod->wires()) { if (wire->port_input || wire->port_output) { - log("Extracted %d ports from top module\n", GetSize(top2widths)); top2widths[RTLIL::unescape_id(wire->name)] = wire->width; } } + log("Extracted %d ports from top module\n", GetSize(top2widths)); // For each scope, track the number of matching ports dict scopes2matches; @@ -348,12 +347,16 @@ std::string FstData::autoScope(Module *topmod) { } } if (!result.empty()) { - log("Found scope: %s\n", result.c_str()); return result; } // No match found log_warning("Could not auto-discover scope for module '%s'...\n", RTLIL::unescape_id(topmod->name).c_str()); + log("Available scopes:\n"); + for (const auto& entry : scopes2matches) { + std::string scope = entry.first; + log(" %s\n", scope.c_str()); + } return ""; } From a449e6ab3846a55127d8d39b1a544925c23fbe7d Mon Sep 17 00:00:00 2001 From: Stan Lee Date: Mon, 2 Mar 2026 12:19:09 -0800 Subject: [PATCH 5/6] always dump available scopes --- kernel/fstdata.cc | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/kernel/fstdata.cc b/kernel/fstdata.cc index d34138e28..6d098dd0f 100644 --- a/kernel/fstdata.cc +++ b/kernel/fstdata.cc @@ -287,6 +287,15 @@ std::string FstData::autoScope(Module *topmod) { log("Auto-discovering scope from file...\n"); std::string top = RTLIL::unescape_id(topmod->name); + log("Available scopes:\n"); + std::set unique_scopes; + for (const auto& var : vars) { + unique_scopes.insert(var.scope); + } + for (const auto& scope : unique_scopes) { + log(" %s\n", scope.c_str()); + } + // Option 1 - Instance based scope matching // Will fail if the DUT instance name != the top module name log("Trying instance-based scope matching...\n"); @@ -353,10 +362,5 @@ std::string FstData::autoScope(Module *topmod) { // No match found log_warning("Could not auto-discover scope for module '%s'...\n", RTLIL::unescape_id(topmod->name).c_str()); - log("Available scopes:\n"); - for (const auto& entry : scopes2matches) { - std::string scope = entry.first; - log(" %s\n", scope.c_str()); - } return ""; } From a1470e14fe2835d6ee849e0b2bd4b5b4d0d80160 Mon Sep 17 00:00:00 2001 From: Stan Lee Date: Mon, 2 Mar 2026 12:57:37 -0800 Subject: [PATCH 6/6] typos --- kernel/fstdata.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/fstdata.cc b/kernel/fstdata.cc index 6d098dd0f..b868890dc 100644 --- a/kernel/fstdata.cc +++ b/kernel/fstdata.cc @@ -313,11 +313,11 @@ std::string FstData::autoScope(Module *topmod) { } } - // Option 2 - Post based scope matching + // Option 2 - Port based scope matching // Matches based on exact port name matching of the top module log("Trying port-based scope matching...\n"); - // Map top moduleport name to their bit widths (RTL reference point) + // Map top module port name to their bit widths (RTL reference point) dict top2widths; for (auto wire : topmod->wires()) { if (wire->port_input || wire->port_output) {