From d5122ed2fa20e67d583b9ed02f7e93f068825954 Mon Sep 17 00:00:00 2001 From: Abhinav Tondapu Date: Fri, 27 Mar 2026 15:20:12 -0700 Subject: [PATCH 1/8] [ENG-1827] ignore placeholder/empty ports from verific --- frontends/verific/verific.cc | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc index 05b24fe7c..4cdda6165 100644 --- a/frontends/verific/verific.cc +++ b/frontends/verific/verific.cc @@ -1593,6 +1593,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma Instance *inst; PortRef *pr; Att *attr; + pool empty_port_nets; FOREACH_ATTRIBUTE(nl, mi, attr) { if (!strcmp(attr->Key(), "noblackbox")) @@ -1604,6 +1605,12 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma if (port->Bus()) continue; + if (port->GetAtt(" empty_port")) { + if (port->GetNet()) + empty_port_nets.insert(port->GetNet()); + continue; + } + if (verific_verbose) log(" importing port %s.\n", port->Name()); @@ -1687,6 +1694,9 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma FOREACH_NET_OF_NETLIST(nl, mi, net) { + if (empty_port_nets.count(net)) + continue; + if (net->IsRamNet()) { RTLIL::Memory *memory = new RTLIL::Memory; @@ -2291,6 +2301,8 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma } FOREACH_PORTREF_OF_INST(inst, mi2, pr) { + if (pr->GetPort()->GetAtt(" empty_port")) + continue; if (verific_verbose) log(" .%s(%s)\n", pr->GetPort()->Name(), pr->GetNet()->Name()); const char *port_name = pr->GetPort()->Name(); From 01f0fd751fbcd303e3c25989ec1b9fc0c4765dfd Mon Sep 17 00:00:00 2001 From: Stan Lee Date: Mon, 30 Mar 2026 12:13:50 -0700 Subject: [PATCH 2/8] fixes for arrays --- kernel/fstdata.cc | 31 +++++++++++-- passes/sat/sim.cc | 111 ++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 131 insertions(+), 11 deletions(-) diff --git a/kernel/fstdata.cc b/kernel/fstdata.cc index 2e3c429a5..03b808412 100644 --- a/kernel/fstdata.cc +++ b/kernel/fstdata.cc @@ -195,13 +195,38 @@ void FstData::extractVarNames() } if (clean_name[0]=='\\') clean_name = clean_name.substr(1); + + // Strip bit ranges like [4:0] from the end (only if no space) if (!has_space) { size_t pos = clean_name.find_last_of("["); - std::string index_or_range = clean_name.substr(pos+1); - if (index_or_range.find(":") != std::string::npos) { - clean_name = clean_name.substr(0,pos); + if (pos != std::string::npos) { + std::string index_or_range = clean_name.substr(pos+1); + if (index_or_range.find(":") != std::string::npos) { + clean_name = clean_name.substr(0,pos); + } + } + } else { + // Handle "signal [index][bitrange]" format + std::string full_name = h->u.var.name; + size_t space_pos = full_name.find(' '); + if (space_pos != std::string::npos) { + std::string suffix = full_name.substr(space_pos + 1); + // Parse first bracket pair for array index + if (!suffix.empty() && suffix[0] == '[') { + size_t close_bracket = suffix.find(']'); + if (close_bracket != std::string::npos) { + std::string index_str = suffix.substr(1, close_bracket - 1); + // Check it's an array index (no colon), not a bit range + if (index_str.find(':') == std::string::npos) { + int array_index = std::stoi(index_str); + memory_to_handle[var.scope+"."+clean_name][array_index] = var.id; + } + } + } } } + + // Handle memory addresses size_t pos = clean_name.find_last_of("<"); if (pos != std::string::npos && clean_name.back() == '>') { std::string mem_cell = clean_name.substr(0, pos); diff --git a/passes/sat/sim.cc b/passes/sat/sim.cc index d470d760f..d8c63390c 100644 --- a/passes/sat/sim.cc +++ b/passes/sat/sim.cc @@ -224,6 +224,54 @@ struct SimInstance dict fst_inputs; dict> fst_memories; + // For multi-dimensional arrays + dict> fst_array_handles; + dict> fst_array_inputs; + + // Helper function to detect and retrieve array element handles + // Returns non-empty dict if wire is a multi-dimensional array split in VCD + dict tryGetArrayHandles(FstData* fst, const std::string& scope, + Wire* wire, bool debug_mode) + { + std::string wire_name = scope + "." + RTLIL::unescape_id(wire->name); + fstHandle id = fst->getHandle(wire_name); + + if (id != 0) { + int fst_width = fst->getWidth(id); + if (fst_width != wire->width) { + // If there is a width mismatch, try to find array elements + if (debug_mode) { + log("Wire %s width mismatch (wire: %d, FST: %d), checking for array elements.\n", + wire_name.c_str(), wire->width, fst_width); + } + + // Array elements are stored in memory_to_handle + dict array_handles = fst->getMemoryHandles(wire_name); + if (!array_handles.empty()) { + + // Calculate total width of all array elements + int total_width = 0; + for (auto &pair : array_handles) { + total_width += fst->getWidth(pair.second); + } + + // If the total width of all array elements matches the wire, return the corresponding array handles + if (total_width == wire->width) { + if (debug_mode) { + log("Found %zu array elements for wire %s, total width: %d\n", + array_handles.size(), wire_name.c_str(), total_width); + } + return array_handles; + } else if (debug_mode) { + log_warning("Array elements total width %d doesn't match wire width %d\n", + total_width, wire->width); + } + } + } + } + return dict(); + } + SimInstance(SimShared *shared, std::string scope, Module *module, Cell *instance = nullptr, SimInstance *parent = nullptr) : shared(shared), scope(scope), module(module), instance(instance), parent(parent), sigmap(module) { @@ -255,18 +303,25 @@ struct SimInstance } } + // Populate fst_handles and fst_array_handles for signal lookups if ((shared->fst) && !(shared->hide_internal && wire->name[0] == '$')) { fstHandle id = shared->fst->getHandle(scope + "." + RTLIL::unescape_id(wire->name)); - if (id==0 && wire->name.isPublic()) { - if (shared->debug) { - log_warning("Unable to find wire %s in input file.\n", (scope + "." + RTLIL::unescape_id(wire->name))); - } - } else { + + // Try to get array element handles if this is a multi-dimensional array + dict array_handles = tryGetArrayHandles(shared->fst, scope, wire, shared->debug); + if (!array_handles.empty()) { + // Must be an array, store in fst_array_handles + fst_array_handles[wire] = array_handles; + } else if (id != 0) { + // Case of a regular wire/reg + fst_handles[wire] = id; if (shared->debug) { log("Found wire %s in input file.\n", (scope + "." + RTLIL::unescape_id(wire->name))); } + } else if (wire->name.isPublic() && shared->debug) { + // Not found + log_warning("Unable to find wire %s in input file.\n", (scope + "." + RTLIL::unescape_id(wire->name))); } - fst_handles[wire] = id; } if (wire->attributes.count(ID::init)) { @@ -1178,6 +1233,20 @@ struct SimInstance std::string v = shared->fst->valueOf(item.second); did_something |= set_state(item.first, Const::from_string(v)); } + // Handle multi-dimensional arrays by concatenating array elements + for(auto &item : fst_array_handles) { + Wire* wire = item.first; + dict& handles = item.second; + + // Concatenate values from all array elements in reverse order + std::string concatenated = ""; + for (int idx = handles.size() - 1; idx >= 0; idx--) { + if (handles.count(idx) == 0) continue; + concatenated += shared->fst->valueOf(handles[idx]); + } + + did_something |= set_state(wire, Const::from_string(concatenated)); + } for (auto cell : module->cells()) { if (cell->is_mem_cell()) { @@ -1227,6 +1296,20 @@ struct SimInstance std::string v = shared->fst->valueOf(item.second); did_something |= set_state(item.first, Const::from_string(v)); } + // Handle multi-dimensional array inputs by concatenating array elements + for(auto &item : fst_array_inputs) { + Wire* wire = item.first; + dict& handles = item.second; + + // Concatenate values from all array elements in reverse order + std::string concatenated = ""; + for (int idx = handles.size() - 1; idx >= 0; idx--) { + if (handles.count(idx) == 0) continue; + concatenated += shared->fst->valueOf(handles[idx]); + } + + did_something |= set_state(wire, Const::from_string(concatenated)); + } for (auto child : children) did_something |= child.second->setInputs(); @@ -1523,11 +1606,23 @@ struct SimWorker : SimShared SigMap sigmap(topmod); for (auto wire : topmod->wires()) { + + // Populate fst_inputs and fst_array_inputs for input ports if (wire->port_input) { fstHandle id = fst->getHandle(scope + "." + RTLIL::unescape_id(wire->name)); - if (id==0) + + // Try to get array element handles if this is a multi-dimensional array + dict array_handles = top->tryGetArrayHandles(fst, scope, wire, debug); + if (!array_handles.empty()) { + // Must be an array, store in fst_array_inputs + top->fst_array_inputs[wire] = array_handles; + } else if (id != 0) { + // Case of a regular wire/reg + top->fst_inputs[wire] = id; + } else { + // Not found log_error("Unable to find required '%s' signal in file\n",(scope + "." + RTLIL::unescape_id(wire->name))); - top->fst_inputs[wire] = id; + } } } From 857f356f1f14e8257816e562a0d6a69201a71043 Mon Sep 17 00:00:00 2001 From: Stan Lee Date: Mon, 30 Mar 2026 12:26:44 -0700 Subject: [PATCH 3/8] remove ambiguity in implementation --- kernel/fstdata.cc | 13 ++++++++++--- kernel/fstdata.h | 2 ++ passes/sat/sim.cc | 49 ++++++++++++++--------------------------------- 3 files changed, 26 insertions(+), 38 deletions(-) diff --git a/kernel/fstdata.cc b/kernel/fstdata.cc index 03b808412..7aee18837 100644 --- a/kernel/fstdata.cc +++ b/kernel/fstdata.cc @@ -96,10 +96,17 @@ fstHandle FstData::getHandle(std::string name) { return 0; }; -dict FstData::getMemoryHandles(std::string name) { +dict FstData::getMemoryHandles(std::string name) { if (memory_to_handle.find(name) != memory_to_handle.end()) return memory_to_handle[name]; - else + else + return dict(); +}; + +dict FstData::getArrayHandles(std::string name) { + if (array_to_handle.find(name) != array_to_handle.end()) + return array_to_handle[name]; + else return dict(); }; @@ -219,7 +226,7 @@ void FstData::extractVarNames() // Check it's an array index (no colon), not a bit range if (index_str.find(':') == std::string::npos) { int array_index = std::stoi(index_str); - memory_to_handle[var.scope+"."+clean_name][array_index] = var.id; + array_to_handle[var.scope+"."+clean_name][array_index] = var.id; } } } diff --git a/kernel/fstdata.h b/kernel/fstdata.h index f95806cca..87a7f3eb7 100644 --- a/kernel/fstdata.h +++ b/kernel/fstdata.h @@ -55,6 +55,7 @@ class FstData std::string valueOf(fstHandle signal); fstHandle getHandle(std::string name); dict getMemoryHandles(std::string name); + dict getArrayHandles(std::string name); double getTimescale() { return timescale; } const char *getTimescaleString() { return timescale_str.c_str(); } int getWidth(fstHandle signal); @@ -67,6 +68,7 @@ private: std::map handle_to_var; std::map name_to_handle; std::map> memory_to_handle; + std::map> array_to_handle; std::map last_data; uint64_t last_time; std::map past_data; diff --git a/passes/sat/sim.cc b/passes/sat/sim.cc index d8c63390c..571c5cd9b 100644 --- a/passes/sat/sim.cc +++ b/passes/sat/sim.cc @@ -230,43 +230,22 @@ struct SimInstance // Helper function to detect and retrieve array element handles // Returns non-empty dict if wire is a multi-dimensional array split in VCD - dict tryGetArrayHandles(FstData* fst, const std::string& scope, - Wire* wire, bool debug_mode) + dict tryGetArrayHandles(FstData* fst, const std::string& scope, Wire* wire) { std::string wire_name = scope + "." + RTLIL::unescape_id(wire->name); - fstHandle id = fst->getHandle(wire_name); + dict array_handles = fst->getArrayHandles(wire_name); - if (id != 0) { - int fst_width = fst->getWidth(id); - if (fst_width != wire->width) { - // If there is a width mismatch, try to find array elements - if (debug_mode) { - log("Wire %s width mismatch (wire: %d, FST: %d), checking for array elements.\n", - wire_name.c_str(), wire->width, fst_width); - } - - // Array elements are stored in memory_to_handle - dict array_handles = fst->getMemoryHandles(wire_name); - if (!array_handles.empty()) { - - // Calculate total width of all array elements - int total_width = 0; - for (auto &pair : array_handles) { - total_width += fst->getWidth(pair.second); - } - - // If the total width of all array elements matches the wire, return the corresponding array handles - if (total_width == wire->width) { - if (debug_mode) { - log("Found %zu array elements for wire %s, total width: %d\n", - array_handles.size(), wire_name.c_str(), total_width); - } - return array_handles; - } else if (debug_mode) { - log_warning("Array elements total width %d doesn't match wire width %d\n", - total_width, wire->width); - } + if (!array_handles.empty()) { + int total_width = 0; + for (auto &pair : array_handles) { + total_width += fst->getWidth(pair.second); + } + if (total_width == wire->width) { + if (shared->debug) { + log("Found %zu array elements for wire %s, total width: %d\n", + array_handles.size(), wire_name.c_str(), total_width); } + return array_handles; } } return dict(); @@ -308,7 +287,7 @@ struct SimInstance fstHandle id = shared->fst->getHandle(scope + "." + RTLIL::unescape_id(wire->name)); // Try to get array element handles if this is a multi-dimensional array - dict array_handles = tryGetArrayHandles(shared->fst, scope, wire, shared->debug); + dict array_handles = tryGetArrayHandles(shared->fst, scope, wire); if (!array_handles.empty()) { // Must be an array, store in fst_array_handles fst_array_handles[wire] = array_handles; @@ -1612,7 +1591,7 @@ struct SimWorker : SimShared fstHandle id = fst->getHandle(scope + "." + RTLIL::unescape_id(wire->name)); // Try to get array element handles if this is a multi-dimensional array - dict array_handles = top->tryGetArrayHandles(fst, scope, wire, debug); + dict array_handles = top->tryGetArrayHandles(fst, scope, wire); if (!array_handles.empty()) { // Must be an array, store in fst_array_inputs top->fst_array_inputs[wire] = array_handles; From e10f545c69a38c3fcca3e711dc190e4d69a8956f Mon Sep 17 00:00:00 2001 From: Stan Lee Date: Mon, 30 Mar 2026 13:13:14 -0700 Subject: [PATCH 4/8] fixes --- passes/sat/sim.cc | 43 ++++++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/passes/sat/sim.cc b/passes/sat/sim.cc index 571c5cd9b..2002f2089 100644 --- a/passes/sat/sim.cc +++ b/passes/sat/sim.cc @@ -251,6 +251,24 @@ struct SimInstance return dict(); } + // Helper function to set wire state from array element handles + // Concatenates values from array elements in descending index order + bool setStateFromArrayHandles(Wire* wire, dict& handles) + { + // Collect and sort indices in descending order (MSB = highest index) + std::vector indices; + for (auto &kv : handles) + indices.push_back(kv.first); + std::sort(indices.begin(), indices.end(), std::greater()); + + // Concatenate values in descending index order + std::string concatenated = ""; + for (int idx : indices) { + concatenated += shared->fst->valueOf(handles[idx]); + } + return set_state(wire, Const::from_string(concatenated)); + } + SimInstance(SimShared *shared, std::string scope, Module *module, Cell *instance = nullptr, SimInstance *parent = nullptr) : shared(shared), scope(scope), module(module), instance(instance), parent(parent), sigmap(module) { @@ -1214,17 +1232,7 @@ struct SimInstance } // Handle multi-dimensional arrays by concatenating array elements for(auto &item : fst_array_handles) { - Wire* wire = item.first; - dict& handles = item.second; - - // Concatenate values from all array elements in reverse order - std::string concatenated = ""; - for (int idx = handles.size() - 1; idx >= 0; idx--) { - if (handles.count(idx) == 0) continue; - concatenated += shared->fst->valueOf(handles[idx]); - } - - did_something |= set_state(wire, Const::from_string(concatenated)); + did_something |= setStateFromArrayHandles(item.first, item.second); } for (auto cell : module->cells()) { @@ -1277,19 +1285,8 @@ struct SimInstance } // Handle multi-dimensional array inputs by concatenating array elements for(auto &item : fst_array_inputs) { - Wire* wire = item.first; - dict& handles = item.second; - - // Concatenate values from all array elements in reverse order - std::string concatenated = ""; - for (int idx = handles.size() - 1; idx >= 0; idx--) { - if (handles.count(idx) == 0) continue; - concatenated += shared->fst->valueOf(handles[idx]); - } - - did_something |= set_state(wire, Const::from_string(concatenated)); + did_something |= setStateFromArrayHandles(item.first, item.second); } - for (auto child : children) did_something |= child.second->setInputs(); From c767d90f3dc8cfa904adf2b0e9bd00266005038a Mon Sep 17 00:00:00 2001 From: Stan Lee Date: Mon, 30 Mar 2026 14:13:43 -0700 Subject: [PATCH 5/8] add warning --- passes/sat/sim.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/passes/sat/sim.cc b/passes/sat/sim.cc index 2002f2089..65bddbe00 100644 --- a/passes/sat/sim.cc +++ b/passes/sat/sim.cc @@ -247,6 +247,8 @@ struct SimInstance } return array_handles; } + log_warning("Array wire '%s' found in VCD (total width %d) but does not match Yosys wire width %d; skipping.\n", + wire_name.c_str(), total_width, wire->width); } return dict(); } From df43a3097a9f20353712782e6bedae76aaa7dd6b Mon Sep 17 00:00:00 2001 From: Abhinav Tondapu Date: Mon, 30 Mar 2026 16:30:46 -0700 Subject: [PATCH 6/8] [ENG-1692] negopt runtime fix + small cleanup --- passes/silimate/negopt.cc | 22 +++++++++++- passes/silimate/peepopt_manual2sub.pmg | 6 ++-- passes/silimate/peepopt_muxneg.pmg | 2 +- passes/silimate/peepopt_negmux.pmg | 6 ++-- passes/silimate/peepopt_negneg.pmg | 2 +- passes/silimate/peepopt_negrebuild.pmg | 18 +++++++--- tests/silimate/negmux.ys | 50 ++++++++++++++++++++++++++ tests/silimate/negrebuild.ys | 29 +++++++++++++++ 8 files changed, 123 insertions(+), 12 deletions(-) diff --git a/passes/silimate/negopt.cc b/passes/silimate/negopt.cc index c658807b4..7d774375c 100644 --- a/passes/silimate/negopt.cc +++ b/passes/silimate/negopt.cc @@ -24,6 +24,26 @@ PRIVATE_NAMESPACE_BEGIN bool did_something; +// Normalize top-end sign/zero extension for PMG prefiltering +static SigSpec strip_ext_for_match(SigSpec sig) +{ + int n = GetSize(sig); + if (n <= 1) + return sig; + + while (n > 1) { + SigBit top = sig[n-1]; + SigBit prev = sig[n-2]; + if (top == prev || top == SigBit(State::S0)) { + n--; + continue; + } + break; + } + + return sig.extract(0, n); +} + #include "passes/silimate/peepopt_negopt.h" struct NegoptPass : public Pass { @@ -51,7 +71,7 @@ struct NegoptPass : public Pass { log(" - muxneg: s?(-a):(-b) => -(s?a:b)\n"); log(" - neg2sub: a + (-b) => a - b\n" ); log("\n"); - log("When called without options, both -pre and -post are executed.\n"); + log("Specify exactly one of -pre or -post.\n"); log("\n"); } diff --git a/passes/silimate/peepopt_manual2sub.pmg b/passes/silimate/peepopt_manual2sub.pmg index 257c93b86..898958b58 100644 --- a/passes/silimate/peepopt_manual2sub.pmg +++ b/passes/silimate/peepopt_manual2sub.pmg @@ -169,8 +169,8 @@ code root_add inner_add_B not_gate_B minuend subtrahend result_sig is_signed // Verify inner_add_B has the form (~b + 1): one port is constant 1, // the other is the NOT output - SigSpec pa = inner_add_B->getPort(ID::A); - SigSpec pb = inner_add_B->getPort(ID::B); + SigSpec pa = port(inner_add_B, \A); + SigSpec pb = port(inner_add_B, \B); SigSpec not_y = port(not_gate_B, \Y); auto is_one = [](SigSpec s) { @@ -191,7 +191,7 @@ code root_add inner_add_B not_gate_B minuend subtrahend result_sig is_signed // The minuend is whichever root_add port is NOT the inner_add_B output subtrahend = port(not_gate_B, \A); - if (inner_add_B->getPort(ID::Y) == root_add->getPort(ID::A)) + if (port(inner_add_B, \Y) == port(root_add, \A)) minuend = root_b; else minuend = root_a; diff --git a/passes/silimate/peepopt_muxneg.pmg b/passes/silimate/peepopt_muxneg.pmg index 5d03ad7e9..4b098ebd1 100644 --- a/passes/silimate/peepopt_muxneg.pmg +++ b/passes/silimate/peepopt_muxneg.pmg @@ -48,7 +48,7 @@ code mux_a mux_b mux_s mux_y neg_a_in neg_a_y neg_b_in neg_b_y neg_a_signed neg_ SigSpec neg_a_rs = neg_a_in; SigSpec neg_b_rs = neg_b_in; neg_a_rs.extend_u0(width, neg_a_signed); - neg_b_rs.extend_u0(width, neg_a_signed); + neg_b_rs.extend_u0(width, neg_b_signed); SigSpec mux_out = module->addWire(NEW_ID2_SUFFIX("y"), width); Cell *new_mux = module->addMux(NEW_ID2_SUFFIX("mux"), neg_a_rs, neg_b_rs, mux_s, mux_out); diff --git a/passes/silimate/peepopt_negmux.pmg b/passes/silimate/peepopt_negmux.pmg index fbedbf507..cfa1415f9 100644 --- a/passes/silimate/peepopt_negmux.pmg +++ b/passes/silimate/peepopt_negmux.pmg @@ -7,7 +7,7 @@ pattern negmux // -(s ? a : b) ===> s ? (-a) : (-b) // -state neg_a neg_y mux_a mux_b mux_s mux_y +state neg_a neg_y neg_a_norm mux_a mux_b mux_s mux_y state a_signed match neg @@ -15,11 +15,13 @@ match neg set neg_a port(neg, \A) set neg_y port(neg, \Y) set a_signed neg->getParam(\A_SIGNED).as_bool() + set neg_a_norm strip_ext_for_match(neg_a) endmatch match mux select mux->type == $mux - select nusers(port(mux, \Y)) == 2 + index strip_ext_for_match(port(mux, \Y)) === neg_a_norm + filter nusers(port(mux, \Y)) == 2 set mux_a port(mux, \A) set mux_b port(mux, \B) set mux_s port(mux, \S) diff --git a/passes/silimate/peepopt_negneg.pmg b/passes/silimate/peepopt_negneg.pmg index 09d7aa543..61d03bcc9 100644 --- a/passes/silimate/peepopt_negneg.pmg +++ b/passes/silimate/peepopt_negneg.pmg @@ -23,7 +23,7 @@ match neg2 endmatch code neg1_a neg1_y neg2_a - // Reject if inner negation truncates + // Reject if the inner negation widens its output if (GetSize(neg1_a) > GetSize(neg2_a)) reject; diff --git a/passes/silimate/peepopt_negrebuild.pmg b/passes/silimate/peepopt_negrebuild.pmg index 49683f09f..1094d6389 100644 --- a/passes/silimate/peepopt_negrebuild.pmg +++ b/passes/silimate/peepopt_negrebuild.pmg @@ -7,8 +7,8 @@ pattern negrebuild // (-a) + (-b) ===> -(a + b) // -state add_a add_b add_y neg1_a neg1_y neg2_a neg2_y -state add_signed add_b_signed neg1_signed neg2_signed +state add_a add_b add_y add_a_norm add_b_norm neg1_a neg1_y neg2_a neg2_y +state add_signed add_b_signed neg1_signed neg2_signed neg1_on_a match add select add->type == $add @@ -17,11 +17,20 @@ match add set add_y port(add, \Y) set add_signed add->getParam(\A_SIGNED).as_bool() set add_b_signed add->getParam(\B_SIGNED).as_bool() + set add_a_norm strip_ext_for_match(add_a) + set add_b_norm strip_ext_for_match(add_b) endmatch +code neg1_on_a + neg1_on_a = true; + branch; + neg1_on_a = false; +endcode + match neg1 select neg1->type == $neg - select nusers(port(neg1, \Y)) == 2 + index strip_ext_for_match(port(neg1, \Y)) === (neg1_on_a ? add_a_norm : add_b_norm) + filter nusers(port(neg1, \Y)) == 2 set neg1_a port(neg1, \A) set neg1_y port(neg1, \Y) set neg1_signed neg1->getParam(\A_SIGNED).as_bool() @@ -29,7 +38,8 @@ endmatch match neg2 select neg2->type == $neg - select nusers(port(neg2, \Y)) == 2 + index strip_ext_for_match(port(neg2, \Y)) === (neg1_on_a ? add_b_norm : add_a_norm) + filter nusers(port(neg2, \Y)) == 2 set neg2_a port(neg2, \A) set neg2_y port(neg2, \Y) set neg2_signed neg2->getParam(\A_SIGNED).as_bool() diff --git a/tests/silimate/negmux.ys b/tests/silimate/negmux.ys index 92eacd53b..1caafa7d0 100644 --- a/tests/silimate/negmux.ys +++ b/tests/silimate/negmux.ys @@ -42,6 +42,56 @@ select -assert-count 2 t:$neg design -reset log -pop +log -header "Positive case: neg input is sign extension of mux output" +log -push +design -reset +read_verilog < Date: Mon, 30 Mar 2026 16:34:20 -0700 Subject: [PATCH 7/8] add warning message --- passes/silimate/reg_rename.cc | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/passes/silimate/reg_rename.cc b/passes/silimate/reg_rename.cc index 5635b6869..81a06a425 100644 --- a/passes/silimate/reg_rename.cc +++ b/passes/silimate/reg_rename.cc @@ -113,12 +113,18 @@ struct RegRenameInstance { newWire = module->addWire(RTLIL::escape_id(baseName), origRegWidth); } + // Check if the bit index exceeds the actual wire width before creating SigSpec + if (index >= newWire->width) { + log_warning("Register bit index %d exceeds wire width %d for '%s' in scope '%s'. Skipping.\n", + index, newWire->width, baseName.c_str(), vcd_scope.c_str()); + continue; + } + // Log the connection of the new wire to the register log_debug("Connecting register wire %s[%d] to bit %d of %s in module %s\n", newWire->name.c_str(), index, index, log_id(newWire), log_id(module)); - // Replace old connection with a new one even at the input ports of subsequent cells from the register - // output + // Replace old connection with a new one even at the input ports of subsequent cells from the register output auto rewriter = [&](SigSpec &sig) { sig.replace(SigBit(oldWire), SigSpec(newWire, index, 1)); }; module->rewrite_sigspecs(rewriter); From db1e0701b0ddd28b66cf51a0d921f576ab9e9a04 Mon Sep 17 00:00:00 2001 From: Akash Levy Date: Mon, 30 Mar 2026 17:31:59 -0700 Subject: [PATCH 8/8] Apply suggestions from code review Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- passes/silimate/negopt.cc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/passes/silimate/negopt.cc b/passes/silimate/negopt.cc index 7d774375c..d4835c23b 100644 --- a/passes/silimate/negopt.cc +++ b/passes/silimate/negopt.cc @@ -24,7 +24,13 @@ PRIVATE_NAMESPACE_BEGIN bool did_something; -// Normalize top-end sign/zero extension for PMG prefiltering +// Normalize top-end sign/zero extension for PMG prefiltering. +// Strips any redundant high bits so that a sign- or zero-extended SigSpec +// and its narrower original compare equal under index lookups. +// - top == prev: sign extension (MSB replicated) +// - top == SigBit(State::S0): zero extension (constant-zero padding) +// Only the prefilter key is stripped; exact legality is re-checked in the +// code block, so false-positive index hits are safe. static SigSpec strip_ext_for_match(SigSpec sig) { int n = GetSize(sig); @@ -34,6 +40,7 @@ static SigSpec strip_ext_for_match(SigSpec sig) while (n > 1) { SigBit top = sig[n-1]; SigBit prev = sig[n-2]; + // Strip sign-extended (repeated MSB) or zero-extended (S0) bits if (top == prev || top == SigBit(State::S0)) { n--; continue;