From 5bdc2d34514862ab5aee31f4dd0f2a918658bc0d Mon Sep 17 00:00:00 2001 From: Stan Lee Date: Fri, 27 Feb 2026 10:12:43 -0800 Subject: [PATCH 01/18] working implementation that i will improvee further --- kernel/fstdata.cc | 106 +++++++++++++++++++++++++++++++++++++++++----- kernel/fstdata.h | 3 ++ 2 files changed, 98 insertions(+), 11 deletions(-) diff --git a/kernel/fstdata.cc b/kernel/fstdata.cc index f0f00181c..3e3e8d127 100644 --- a/kernel/fstdata.cc +++ b/kernel/fstdata.cc @@ -114,16 +114,46 @@ void FstData::extractVarNames() struct fstHier *h; std::string fst_scope_name; + // Track current fork scope + std::string current_fork_scope; + fstHandle next_virtual_handle = (fstHandle)-1; // forks get negative handle values for better identification + while ((h = fstReaderIterateHier(ctx))) { switch (h->htyp) { - case FST_HT_SCOPE: { - fst_scope_name = fstReaderPushScope(ctx, h->u.scope.name, NULL); - break; + case FST_HT_SCOPE: { + fst_scope_name = fstReaderPushScope(ctx, h->u.scope.name, NULL); + + // Fork scopes are identified by FST_ST_VCD_FORK + if (h->u.scope.typ == FST_ST_VCD_FORK) { + current_fork_scope = fst_scope_name; + // Create new vector that contains struct members + fork_scopes[current_fork_scope] = std::vector(); } - case FST_HT_UPSCOPE: { - fst_scope_name = fstReaderPopScope(ctx); - break; + break; + } + case FST_HT_UPSCOPE: { + if (!current_fork_scope.empty() && current_fork_scope == fst_scope_name) { + + // Create a virtual handle + fstHandle virtual_handle = next_virtual_handle--; + + // Map the scope to the virtual handle + name_to_handle[current_fork_scope] = virtual_handle; + virtual_to_fork[virtual_handle] = current_fork_scope; + + // Calculate total width of the struct and map it to the virtual handle + int total_width = 0; + for (fstHandle member : fork_scopes[current_fork_scope]) { + if (handle_to_var.find(member) != handle_to_var.end()) { + total_width += handle_to_var[member].width; + } + } + virtual_handle_widths[virtual_handle] = total_width; + current_fork_scope.clear(); } + fst_scope_name = fstReaderPopScope(ctx); + break; + } case FST_HT_VAR: { FstVar var; var.id = h->u.var.handle; @@ -133,10 +163,13 @@ void FstData::extractVarNames() var.scope = fst_scope_name; normalize_brackets(var.scope); var.width = h->u.var.length; - vars.push_back(var); - if (!var.is_alias) - handle_to_var[h->u.var.handle] = var; - std::string clean_name; + vars.push_back(var); + if (!var.is_alias) + handle_to_var[h->u.var.handle] = var; + if (!current_fork_scope.empty()) { + fork_scopes[current_fork_scope].push_back(h->u.var.handle); + } + std::string clean_name; bool has_space = false; for(size_t i=0;iu.var.name);i++) { @@ -275,8 +308,59 @@ void FstData::reconstructAllAtTimes(std::vector &signal, uint64_t sta std::string FstData::valueOf(fstHandle signal) { + // Condition to check if the signal comes from a fork scope + if ((int)signal < 0 && virtual_to_fork.find(signal) != virtual_to_fork.end()) { + std::string fork_scope = virtual_to_fork[signal]; + if (fork_scopes.find(fork_scope) != fork_scopes.end()) { + + // Empty string to store the full struct values + std::string result; + + // Get the members of the fork scope + const std::vector& members = fork_scopes[fork_scope]; + for (auto it = members.rbegin(); it != members.rend(); ++it) { + fstHandle member = *it; + std::string member_val; + int expected_width = 0; + + // Get the width of the member + if (handle_to_var.find(member) != handle_to_var.end()) { + expected_width = handle_to_var[member].width; + } + + // Check if the member has a value + if (past_data.find(member) != past_data.end()) { + member_val = past_data[member]; + + // If the member value is shorter than the expected width, pad with zeros + if (expected_width > 0 && (int)member_val.length() < expected_width) { + member_val = std::string(expected_width - member_val.length(), '0') + member_val; + } + } else if (expected_width > 0) { + // If the member has no value, pad with x's + member_val = std::string(expected_width, 'x'); + } else { + member_val = "x"; + } + + // Add the member value to the result + result += member_val; + } + return result; + } + + // If the fork scope is not found, pad with x's based on the width + if (virtual_handle_widths.find(signal) != virtual_handle_widths.end()) { + return std::string(virtual_handle_widths[signal], 'x'); + } + return "x"; + } + if (past_data.find(signal) == past_data.end()) { - return std::string(handle_to_var[signal].width, 'x'); + if (handle_to_var.find(signal) != handle_to_var.end()) { + return std::string(handle_to_var[signal].width, 'x'); + } + return "x"; } return past_data[signal]; } diff --git a/kernel/fstdata.h b/kernel/fstdata.h index a8ae40301..b65681b00 100644 --- a/kernel/fstdata.h +++ b/kernel/fstdata.h @@ -69,6 +69,9 @@ private: uint64_t last_time; std::map past_data; uint64_t past_time; + std::map> fork_scopes; + std::map virtual_to_fork; + std::map virtual_handle_widths; double timescale; std::string timescale_str; uint64_t start_time; From 0aaca679cea2884feeab3e04d11824322368b7e6 Mon Sep 17 00:00:00 2001 From: Stan Lee Date: Fri, 27 Feb 2026 10:22:06 -0800 Subject: [PATCH 02/18] better but not ideal --- kernel/fstdata.cc | 103 +++++++++++++++++++--------------------------- kernel/fstdata.h | 4 +- 2 files changed, 43 insertions(+), 64 deletions(-) diff --git a/kernel/fstdata.cc b/kernel/fstdata.cc index 3e3e8d127..1ecf1f7e9 100644 --- a/kernel/fstdata.cc +++ b/kernel/fstdata.cc @@ -116,7 +116,8 @@ void FstData::extractVarNames() // Track current fork scope std::string current_fork_scope; - fstHandle next_virtual_handle = (fstHandle)-1; // forks get negative handle values for better identification + fstHandle next_fork_handle = 0x80000000; + std::map> fork_scopes; while ((h = fstReaderIterateHier(ctx))) { switch (h->htyp) { @@ -133,22 +134,13 @@ void FstData::extractVarNames() } case FST_HT_UPSCOPE: { if (!current_fork_scope.empty() && current_fork_scope == fst_scope_name) { - - // Create a virtual handle - fstHandle virtual_handle = next_virtual_handle--; - - // Map the scope to the virtual handle - name_to_handle[current_fork_scope] = virtual_handle; - virtual_to_fork[virtual_handle] = current_fork_scope; - - // Calculate total width of the struct and map it to the virtual handle - int total_width = 0; - for (fstHandle member : fork_scopes[current_fork_scope]) { - if (handle_to_var.find(member) != handle_to_var.end()) { - total_width += handle_to_var[member].width; - } - } - virtual_handle_widths[virtual_handle] = total_width; + fstHandle fork_handle = next_fork_handle++; + std::string normalized_fork_scope = current_fork_scope; + normalize_brackets(normalized_fork_scope); + + name_to_handle[normalized_fork_scope] = fork_handle; + fork_scope_members[fork_handle] = fork_scopes[current_fork_scope]; + current_fork_scope.clear(); } fst_scope_name = fstReaderPopScope(ctx); @@ -308,54 +300,43 @@ void FstData::reconstructAllAtTimes(std::vector &signal, uint64_t sta std::string FstData::valueOf(fstHandle signal) { - // Condition to check if the signal comes from a fork scope - if ((int)signal < 0 && virtual_to_fork.find(signal) != virtual_to_fork.end()) { - std::string fork_scope = virtual_to_fork[signal]; - if (fork_scopes.find(fork_scope) != fork_scopes.end()) { - - // Empty string to store the full struct values - std::string result; - - // Get the members of the fork scope - const std::vector& members = fork_scopes[fork_scope]; - for (auto it = members.rbegin(); it != members.rend(); ++it) { - fstHandle member = *it; - std::string member_val; - int expected_width = 0; - - // Get the width of the member - if (handle_to_var.find(member) != handle_to_var.end()) { - expected_width = handle_to_var[member].width; - } - - // Check if the member has a value - if (past_data.find(member) != past_data.end()) { - member_val = past_data[member]; - - // If the member value is shorter than the expected width, pad with zeros - if (expected_width > 0 && (int)member_val.length() < expected_width) { - member_val = std::string(expected_width - member_val.length(), '0') + member_val; - } - } else if (expected_width > 0) { - // If the member has no value, pad with x's - member_val = std::string(expected_width, 'x'); - } else { - member_val = "x"; - } - - // Add the member value to the result - result += member_val; + // Check if this is a fork scope (struct) + auto it = fork_scope_members.find(signal); + if (it != fork_scope_members.end()) { + std::string result; + const std::vector& members = it->second; + + // Iterate in REVERSE: first declared member is MSB in SystemVerilog packed structs + for (auto m = members.rbegin(); m != members.rend(); ++m) { + fstHandle member = *m; + std::string member_val; + int expected_width = 0; + + // Get the declared width of this member + if (handle_to_var.find(member) != handle_to_var.end()) { + expected_width = handle_to_var[member].width; } - return result; + + // Get the current value + if (past_data.find(member) != past_data.end()) { + member_val = past_data[member]; + // VCD drops leading zeros - must pad to full width + if (expected_width > 0 && (int)member_val.length() < expected_width) { + member_val = std::string(expected_width - member_val.length(), '0') + member_val; + } + } else if (expected_width > 0) { + // No value yet, use x's + member_val = std::string(expected_width, 'x'); + } else { + member_val = "x"; + } + + result += member_val; } - - // If the fork scope is not found, pad with x's based on the width - if (virtual_handle_widths.find(signal) != virtual_handle_widths.end()) { - return std::string(virtual_handle_widths[signal], 'x'); - } - return "x"; + return result; } + // Normal signal handling if (past_data.find(signal) == past_data.end()) { if (handle_to_var.find(signal) != handle_to_var.end()) { return std::string(handle_to_var[signal].width, 'x'); diff --git a/kernel/fstdata.h b/kernel/fstdata.h index b65681b00..ee421f9ac 100644 --- a/kernel/fstdata.h +++ b/kernel/fstdata.h @@ -69,9 +69,7 @@ private: uint64_t last_time; std::map past_data; uint64_t past_time; - std::map> fork_scopes; - std::map virtual_to_fork; - std::map virtual_handle_widths; + std::map> fork_scope_members; double timescale; std::string timescale_str; uint64_t start_time; From 48894488f1227d2aff1a08e6a0d9cd1531a7c5da Mon Sep 17 00:00:00 2001 From: Stan Lee Date: Fri, 27 Feb 2026 11:02:46 -0800 Subject: [PATCH 03/18] better method for assigning fsthandles --- kernel/fstdata.cc | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/kernel/fstdata.cc b/kernel/fstdata.cc index 1ecf1f7e9..ec61e80d4 100644 --- a/kernel/fstdata.cc +++ b/kernel/fstdata.cc @@ -116,7 +116,9 @@ void FstData::extractVarNames() // Track current fork scope std::string current_fork_scope; - fstHandle next_fork_handle = 0x80000000; + + // Start fork handles after the maximum real handle from FST file to avoid collisions + fstHandle next_fork_handle = fstReaderGetMaxHandle(ctx) + 1; std::map> fork_scopes; while ((h = fstReaderIterateHier(ctx))) { @@ -134,13 +136,16 @@ void FstData::extractVarNames() } case FST_HT_UPSCOPE: { if (!current_fork_scope.empty() && current_fork_scope == fst_scope_name) { + // Assign a unique handle to this fork scope fstHandle fork_handle = next_fork_handle++; + + // Map normalized scope name to the handle for future lookups via getHandle() std::string normalized_fork_scope = current_fork_scope; normalize_brackets(normalized_fork_scope); - name_to_handle[normalized_fork_scope] = fork_handle; fork_scope_members[fork_handle] = fork_scopes[current_fork_scope]; - + + // Clear current fork scope for the next scope current_fork_scope.clear(); } fst_scope_name = fstReaderPopScope(ctx); @@ -158,9 +163,12 @@ void FstData::extractVarNames() vars.push_back(var); if (!var.is_alias) handle_to_var[h->u.var.handle] = var; + + // Add variable to fork scope members if we are currently in a fork scope if (!current_fork_scope.empty()) { fork_scopes[current_fork_scope].push_back(h->u.var.handle); } + std::string clean_name; bool has_space = false; for(size_t i=0;iu.var.name);i++) From ae3b9b74e2a6d6c9deb5e77e84e6f44f7fd5932f Mon Sep 17 00:00:00 2001 From: Stan Lee Date: Fri, 27 Feb 2026 11:11:05 -0800 Subject: [PATCH 04/18] ready --- kernel/fstdata.cc | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/kernel/fstdata.cc b/kernel/fstdata.cc index ec61e80d4..a4c7d7e65 100644 --- a/kernel/fstdata.cc +++ b/kernel/fstdata.cc @@ -129,7 +129,7 @@ void FstData::extractVarNames() // Fork scopes are identified by FST_ST_VCD_FORK if (h->u.scope.typ == FST_ST_VCD_FORK) { current_fork_scope = fst_scope_name; - // Create new vector that contains struct members + // Create new vector that contains struct members copied during upscope fork_scopes[current_fork_scope] = std::vector(); } break; @@ -143,6 +143,9 @@ void FstData::extractVarNames() std::string normalized_fork_scope = current_fork_scope; normalize_brackets(normalized_fork_scope); name_to_handle[normalized_fork_scope] = fork_handle; + + // Copy the extracted members of the fork scope to the fork scope members map + // for value lookups in valueOf() fork_scope_members[fork_handle] = fork_scopes[current_fork_scope]; // Clear current fork scope for the next scope @@ -313,32 +316,32 @@ std::string FstData::valueOf(fstHandle signal) if (it != fork_scope_members.end()) { std::string result; const std::vector& members = it->second; - - // Iterate in REVERSE: first declared member is MSB in SystemVerilog packed structs + + // Iterate over members of the struct to get concatenated value. + // The first declared member is MSB in SystemVerilog packed structs for (auto m = members.rbegin(); m != members.rend(); ++m) { fstHandle member = *m; std::string member_val; int expected_width = 0; - + // Get the declared width of this member if (handle_to_var.find(member) != handle_to_var.end()) { expected_width = handle_to_var[member].width; } - - // Get the current value + // Get the current value of the member if (past_data.find(member) != past_data.end()) { member_val = past_data[member]; - // VCD drops leading zeros - must pad to full width + // Pad with zeros to the expected width of the member if (expected_width > 0 && (int)member_val.length() < expected_width) { member_val = std::string(expected_width - member_val.length(), '0') + member_val; } } else if (expected_width > 0) { - // No value yet, use x's + // No value yet, use X to pad member_val = std::string(expected_width, 'x'); - } else { + } else { // fallback to X member_val = "x"; } - + // Concatenate the member value to the overall struct value result += member_val; } return result; From fa1267e0cbd8be508345cfcbea8d62532249f51a Mon Sep 17 00:00:00 2001 From: Stan Lee Date: Fri, 27 Feb 2026 11:27:37 -0800 Subject: [PATCH 05/18] fix indents --- kernel/fstdata.cc | 71 +++++++++++++++++++++++------------------------ 1 file changed, 34 insertions(+), 37 deletions(-) diff --git a/kernel/fstdata.cc b/kernel/fstdata.cc index a4c7d7e65..6d45a8750 100644 --- a/kernel/fstdata.cc +++ b/kernel/fstdata.cc @@ -123,37 +123,37 @@ void FstData::extractVarNames() while ((h = fstReaderIterateHier(ctx))) { switch (h->htyp) { - case FST_HT_SCOPE: { - fst_scope_name = fstReaderPushScope(ctx, h->u.scope.name, NULL); + case FST_HT_SCOPE: { + fst_scope_name = fstReaderPushScope(ctx, h->u.scope.name, NULL); - // Fork scopes are identified by FST_ST_VCD_FORK - if (h->u.scope.typ == FST_ST_VCD_FORK) { - current_fork_scope = fst_scope_name; - // Create new vector that contains struct members copied during upscope - fork_scopes[current_fork_scope] = std::vector(); + // Fork scopes are identified by FST_ST_VCD_FORK + if (h->u.scope.typ == FST_ST_VCD_FORK) { + current_fork_scope = fst_scope_name; + // Create new vector that contains struct members copied during upscope + fork_scopes[current_fork_scope] = std::vector(); + } + break; } - break; - } - case FST_HT_UPSCOPE: { - if (!current_fork_scope.empty() && current_fork_scope == fst_scope_name) { - // Assign a unique handle to this fork scope - fstHandle fork_handle = next_fork_handle++; + case FST_HT_UPSCOPE: { + if (!current_fork_scope.empty() && current_fork_scope == fst_scope_name) { + // Assign a unique handle to this fork scope + fstHandle fork_handle = next_fork_handle++; - // Map normalized scope name to the handle for future lookups via getHandle() - std::string normalized_fork_scope = current_fork_scope; - normalize_brackets(normalized_fork_scope); - name_to_handle[normalized_fork_scope] = fork_handle; + // Map normalized scope name to the handle for future lookups via getHandle() + std::string normalized_fork_scope = current_fork_scope; + normalize_brackets(normalized_fork_scope); + name_to_handle[normalized_fork_scope] = fork_handle; - // Copy the extracted members of the fork scope to the fork scope members map - // for value lookups in valueOf() - fork_scope_members[fork_handle] = fork_scopes[current_fork_scope]; + // Copy the extracted members of the fork scope to the fork scope members map + // for value lookups in valueOf() + fork_scope_members[fork_handle] = fork_scopes[current_fork_scope]; - // Clear current fork scope for the next scope - current_fork_scope.clear(); + // Clear current fork scope for the next scope + current_fork_scope.clear(); + } + fst_scope_name = fstReaderPopScope(ctx); + break; } - fst_scope_name = fstReaderPopScope(ctx); - break; - } case FST_HT_VAR: { FstVar var; var.id = h->u.var.handle; @@ -163,14 +163,14 @@ void FstData::extractVarNames() var.scope = fst_scope_name; normalize_brackets(var.scope); var.width = h->u.var.length; - vars.push_back(var); - if (!var.is_alias) - handle_to_var[h->u.var.handle] = var; + vars.push_back(var); + if (!var.is_alias) + handle_to_var[h->u.var.handle] = var; - // Add variable to fork scope members if we are currently in a fork scope - if (!current_fork_scope.empty()) { - fork_scopes[current_fork_scope].push_back(h->u.var.handle); - } + // Add variable to fork scope members if we are currently in a fork scope + if (!current_fork_scope.empty()) { + fork_scopes[current_fork_scope].push_back(h->u.var.handle); + } std::string clean_name; bool has_space = false; @@ -348,11 +348,8 @@ std::string FstData::valueOf(fstHandle signal) } // Normal signal handling - if (past_data.find(signal) == past_data.end()) { - if (handle_to_var.find(signal) != handle_to_var.end()) { - return std::string(handle_to_var[signal].width, 'x'); - } - return "x"; + if (handle_to_var.find(signal) != handle_to_var.end()) { + return std::string(handle_to_var[signal].width, 'x'); } return past_data[signal]; } From 03ce300b49d2b516201aff748680fdb45a4b8490 Mon Sep 17 00:00:00 2001 From: Stan Lee Date: Fri, 27 Feb 2026 11:29:31 -0800 Subject: [PATCH 06/18] another indent --- kernel/fstdata.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/fstdata.cc b/kernel/fstdata.cc index 6d45a8750..395bab66f 100644 --- a/kernel/fstdata.cc +++ b/kernel/fstdata.cc @@ -172,7 +172,7 @@ void FstData::extractVarNames() fork_scopes[current_fork_scope].push_back(h->u.var.handle); } - std::string clean_name; + std::string clean_name; bool has_space = false; for(size_t i=0;iu.var.name);i++) { From d36e2f7d17a8353afd22dd8a32840cbcf9d6d4ae Mon Sep 17 00:00:00 2001 From: Stan Lee Date: Fri, 27 Feb 2026 11:40:13 -0800 Subject: [PATCH 07/18] resolve accidental change --- kernel/fstdata.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/fstdata.cc b/kernel/fstdata.cc index 395bab66f..fbcca6b82 100644 --- a/kernel/fstdata.cc +++ b/kernel/fstdata.cc @@ -348,7 +348,7 @@ std::string FstData::valueOf(fstHandle signal) } // Normal signal handling - if (handle_to_var.find(signal) != handle_to_var.end()) { + if (past_data.find(signal) == past_data.end()) { return std::string(handle_to_var[signal].width, 'x'); } return past_data[signal]; From c42d2c2d03dd814895fd1be18fe75f8d3b05b04b Mon Sep 17 00:00:00 2001 From: Stan Lee Date: Fri, 27 Feb 2026 11:54:43 -0800 Subject: [PATCH 08/18] support for nested structs --- kernel/fstdata.cc | 80 ++++++++++++++++++++++++++++------------------- 1 file changed, 47 insertions(+), 33 deletions(-) diff --git a/kernel/fstdata.cc b/kernel/fstdata.cc index fbcca6b82..0350be46b 100644 --- a/kernel/fstdata.cc +++ b/kernel/fstdata.cc @@ -114,8 +114,9 @@ void FstData::extractVarNames() struct fstHier *h; std::string fst_scope_name; - // Track current fork scope - std::string current_fork_scope; + // Track nested fork scopes using a stack to handle nested packed structs + // Begins with outmost scope and ends with innermost scope + std::vector fork_scope_stack; // Start fork handles after the maximum real handle from FST file to avoid collisions fstHandle next_fork_handle = fstReaderGetMaxHandle(ctx) + 1; @@ -126,30 +127,35 @@ void FstData::extractVarNames() case FST_HT_SCOPE: { fst_scope_name = fstReaderPushScope(ctx, h->u.scope.name, NULL); - // Fork scopes are identified by FST_ST_VCD_FORK + // Fork scopes are identified by FST_ST_VCD_FORK and are pushed onto the stack if (h->u.scope.typ == FST_ST_VCD_FORK) { - current_fork_scope = fst_scope_name; - // Create new vector that contains struct members copied during upscope - fork_scopes[current_fork_scope] = std::vector(); + fork_scope_stack.push_back(fst_scope_name); + // Create new vector that contains struct members + fork_scopes[fst_scope_name] = std::vector(); } break; } case FST_HT_UPSCOPE: { - if (!current_fork_scope.empty() && current_fork_scope == fst_scope_name) { - // Assign a unique handle to this fork scope + if (!fork_scope_stack.empty() && fork_scope_stack.back() == fst_scope_name) { + // Assign a unique handle to this fork scope and increment for future forks fstHandle fork_handle = next_fork_handle++; // Map normalized scope name to the handle for future lookups via getHandle() - std::string normalized_fork_scope = current_fork_scope; - normalize_brackets(normalized_fork_scope); - name_to_handle[normalized_fork_scope] = fork_handle; + normalize_brackets(fst_scope_name); + name_to_handle[fst_scope_name] = fork_handle; // Copy the extracted members of the fork scope to the fork scope members map // for value lookups in valueOf() - fork_scope_members[fork_handle] = fork_scopes[current_fork_scope]; + fork_scope_members[fork_handle] = fork_scopes[fst_scope_name]; - // Clear current fork scope for the next scope - current_fork_scope.clear(); + // If this is a nested fork scope, add its handle to the parent fork scope + if (fork_scope_stack.size() > 1) { + std::string parent_fork = fork_scope_stack[fork_scope_stack.size() - 2]; + fork_scopes[parent_fork].push_back(fork_handle); + } + + // Pop this fork scope from the stack + fork_scope_stack.pop_back(); } fst_scope_name = fstReaderPopScope(ctx); break; @@ -167,9 +173,9 @@ void FstData::extractVarNames() if (!var.is_alias) handle_to_var[h->u.var.handle] = var; - // Add variable to fork scope members if we are currently in a fork scope - if (!current_fork_scope.empty()) { - fork_scopes[current_fork_scope].push_back(h->u.var.handle); + // Add variable to the innermost fork scope in the fork scope stack + if (!fork_scope_stack.empty()) { + fork_scopes[fork_scope_stack.back()].push_back(h->u.var.handle); } std::string clean_name; @@ -322,24 +328,32 @@ std::string FstData::valueOf(fstHandle signal) for (auto m = members.rbegin(); m != members.rend(); ++m) { fstHandle member = *m; std::string member_val; - int expected_width = 0; + + // Check if this member is itself a nested fork scope (struct) + if (fork_scope_members.find(member) != fork_scope_members.end()) { + // Recursively get the value of the nested struct + member_val = valueOf(member); + } else { + // Regular variable - look up in past_data + int expected_width = 0; - // Get the declared width of this member - if (handle_to_var.find(member) != handle_to_var.end()) { - expected_width = handle_to_var[member].width; - } - // Get the current value of the member - if (past_data.find(member) != past_data.end()) { - member_val = past_data[member]; - // Pad with zeros to the expected width of the member - if (expected_width > 0 && (int)member_val.length() < expected_width) { - member_val = std::string(expected_width - member_val.length(), '0') + member_val; + // Get the declared width of this member + if (handle_to_var.find(member) != handle_to_var.end()) { + expected_width = handle_to_var[member].width; + } + // Get the current value of the member + if (past_data.find(member) != past_data.end()) { + member_val = past_data[member]; + // Pad with zeros to the expected width of the member + if (expected_width > 0 && (int)member_val.length() < expected_width) { + member_val = std::string(expected_width - member_val.length(), '0') + member_val; + } + } else if (expected_width > 0) { + // No value yet, use X to pad + member_val = std::string(expected_width, 'x'); + } else { // fallback to X + member_val = "x"; } - } else if (expected_width > 0) { - // No value yet, use X to pad - member_val = std::string(expected_width, 'x'); - } else { // fallback to X - member_val = "x"; } // Concatenate the member value to the overall struct value result += member_val; From 93af5a5232b8f64b3e2d8d54a046ae64e3ce48fd Mon Sep 17 00:00:00 2001 From: Stan Lee Date: Fri, 27 Feb 2026 12:17:43 -0800 Subject: [PATCH 09/18] in order --- kernel/fstdata.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/fstdata.cc b/kernel/fstdata.cc index 0350be46b..44f53e8bd 100644 --- a/kernel/fstdata.cc +++ b/kernel/fstdata.cc @@ -325,7 +325,7 @@ std::string FstData::valueOf(fstHandle signal) // Iterate over members of the struct to get concatenated value. // The first declared member is MSB in SystemVerilog packed structs - for (auto m = members.rbegin(); m != members.rend(); ++m) { + for (auto m = members.begin(); m != members.end(); m++) { fstHandle member = *m; std::string member_val; From 8ee71ddc7f98884c6de90d2432a002fa8bdb4a34 Mon Sep 17 00:00:00 2001 From: Stan Lee Date: Fri, 27 Feb 2026 12:19:14 -0800 Subject: [PATCH 10/18] bugfix --- kernel/fstdata.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/fstdata.cc b/kernel/fstdata.cc index 44f53e8bd..f417048f2 100644 --- a/kernel/fstdata.cc +++ b/kernel/fstdata.cc @@ -131,6 +131,7 @@ void FstData::extractVarNames() if (h->u.scope.typ == FST_ST_VCD_FORK) { fork_scope_stack.push_back(fst_scope_name); // Create new vector that contains struct members + normalize_brackets(fst_scope_name); fork_scopes[fst_scope_name] = std::vector(); } break; From c459a74c13ef4fd723bbaa45914dfaf642bb84dd Mon Sep 17 00:00:00 2001 From: Stan Lee Date: Sun, 1 Mar 2026 15:39:35 -0800 Subject: [PATCH 11/18] 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 12/18] 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 13/18] 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 14/18] 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 15/18] 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 16/18] 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) { From b438fd1fe99487bc7b26b6c574193f5e02fef637 Mon Sep 17 00:00:00 2001 From: tondapusili Date: Mon, 2 Mar 2026 19:33:25 -0800 Subject: [PATCH 17/18] negopt: fix quadratic blowup by adding index hints and deferring nusers to filter --- passes/silimate/negopt.cc | 21 ++++++-- passes/silimate/peepopt_manual2sub.pmg | 72 +++++++++++++++----------- passes/silimate/peepopt_muxneg.pmg | 4 +- passes/silimate/peepopt_neg2sub.pmg | 2 +- passes/silimate/peepopt_negexpand.pmg | 2 +- passes/silimate/peepopt_negmux.pmg | 6 +-- passes/silimate/peepopt_negneg.pmg | 2 +- passes/silimate/peepopt_negrebuild.pmg | 8 +-- 8 files changed, 72 insertions(+), 45 deletions(-) diff --git a/passes/silimate/negopt.cc b/passes/silimate/negopt.cc index e3066b9b7..28d047ed9 100644 --- a/passes/silimate/negopt.cc +++ b/passes/silimate/negopt.cc @@ -85,16 +85,29 @@ struct NegoptPass : public Pass { for (auto module : design->selected_modules()) { if (run_pre) { + // manual2sub and sub2neg only need to run once: no downstream + // pre-subpass creates the patterns they match + // separate pm instances so sub2neg sees the $sub cells manual2sub creates. + { + peepopt_pm pm(module); + pm.setup(module->selected_cells()); + pm.run_manual2sub(); + log_flush(); + } + { + peepopt_pm pm(module); + pm.setup(module->selected_cells()); + pm.run_sub2neg(); + log_flush(); + } + + // negexpand/negneg/negmux can feed each other. did_something = true; for (int iter = 0; iter < max_iterations && did_something; iter++) { did_something = false; peepopt_pm pm(module); pm.setup(module->selected_cells()); - pm.run_manual2sub(); // Reduce manual 2's complement to subtraction first - log_flush(); - pm.run_sub2neg(); - log_flush(); pm.run_negexpand(); log_flush(); pm.run_negneg(); diff --git a/passes/silimate/peepopt_manual2sub.pmg b/passes/silimate/peepopt_manual2sub.pmg index d82e5cf5f..b45a05308 100644 --- a/passes/silimate/peepopt_manual2sub.pmg +++ b/passes/silimate/peepopt_manual2sub.pmg @@ -12,9 +12,8 @@ pattern manual2sub state minuend subtrahend result_sig root_a root_b state is_signed -state inner_y +state inner_y inner_port_sig_A root_port_sig inner_port_sig_B -// 1. Match the "root" add (the one that produces the final result) match root_add select root_add->type == $add set root_a port(root_add, \A) @@ -23,8 +22,7 @@ match root_add set is_signed root_add->getParam(ID::A_SIGNED).as_bool() endmatch -// 2. Case A: (a + ~b) + 1 -// Check if root_add has a constant 1 +// Case A: (a + ~b) + 1 code root_add inner_y { SigSpec pa = root_a; @@ -51,20 +49,28 @@ code root_add inner_y } endcode -// Find the inner add -// inner_y is discovered in code so gating happens in the code block +// Find the inner add whose Y feeds the non-constant port of root_add match inner_add_A select inner_add_A->type == $add + index port(inner_add_A, \Y) === inner_y + filter nusers(port(inner_add_A, \Y)) == 2 endmatch -// Find the NOT gate on one of the ports of inner_add_A +// Branch over both ports of inner_add_A to find the NOT gate +code inner_port_sig_A + inner_port_sig_A = port(inner_add_A, \A); + branch; + inner_port_sig_A = port(inner_add_A, \B); +endcode + match not_gate_A select not_gate_A->type == $not + index port(not_gate_A, \Y) === inner_port_sig_A endmatch code root_add inner_add_A not_gate_A subtrahend minuend result_sig is_signed { - // Require consistent signedness on the root add. + // Require consistent signedness across the chain if (root_add->getParam(ID::B_SIGNED).as_bool() != is_signed) reject; @@ -73,11 +79,7 @@ code root_add inner_add_A not_gate_A subtrahend minuend result_sig is_signed if (inner_add_A->getParam(ID::B_SIGNED).as_bool() != is_signed) reject; - if (port(inner_add_A, \Y) != inner_y) - reject; - if (nusers(port(inner_add_A, \Y)) != 2) - reject; - + // Determine which port is the NOT output and which is the minuend SigSpec not_y = port(not_gate_A, \Y); SigSpec add_a = port(inner_add_A, \A); SigSpec add_b = port(inner_add_A, \B); @@ -92,7 +94,6 @@ code root_add inner_add_A not_gate_A subtrahend minuend result_sig is_signed subtrahend = port(not_gate_A, \A); - // Create the subtraction cell log("manual2sub in %s: Found (a + ~b) + 1 pattern, creating $sub for %s\n", log_id(module), log_signal(result_sig)); Cell *cell = root_add; int width = GetSize(result_sig); @@ -100,27 +101,25 @@ code root_add inner_add_A not_gate_A subtrahend minuend result_sig is_signed // Reject if the +1 wrap boundary is narrower than the final result if (inner_width < width) reject; + // Anchor both operands to the inner add width to preserve carry behavior SigSpec minuend_rs = minuend; SigSpec subtrahend_rs = subtrahend; - // Anchor both operands to the inner add width to preserve carry behavior minuend_rs.extend_u0(inner_width, is_signed); subtrahend_rs.extend_u0(inner_width, is_signed); SigSpec sub_y = result_sig; - // Extend the sub result back to the root width when needed if (inner_width != width) { sub_y = module->addWire(NEW_ID2_SUFFIX("sub_y"), inner_width); } Cell *sub = module->addSub(NEW_ID2_SUFFIX("sub"), minuend_rs, subtrahend_rs, sub_y, is_signed); + // Extend the sub result back to root width when inner is wider if (inner_width != width) { SigSpec sub_y_rs = sub_y; sub_y_rs.extend_u0(width, is_signed); module->connect(result_sig, sub_y_rs); } - // Let fixup_parameters handle width adjustments sub->fixup_parameters(); - // Remove old cells autoremove(root_add); autoremove(inner_add_A); autoremove(not_gate_A); @@ -130,23 +129,36 @@ code root_add inner_add_A not_gate_A subtrahend minuend result_sig is_signed } endcode -// 3. Case B: a + (~b + 1) +// Case B: a + (~b + 1) + +// Branch over both ports of root_add to find the inner (~b + 1) add +code root_port_sig + root_port_sig = port(root_add, \A); + branch; + root_port_sig = port(root_add, \B); +endcode -// Find the inner add on either port of root_add match inner_add_B select inner_add_B->type == $add - filter port(inner_add_B, \Y) == root_a || port(inner_add_B, \Y) == root_b - select nusers(port(inner_add_B, \Y)) == 2 + index port(inner_add_B, \Y) === root_port_sig + filter nusers(port(inner_add_B, \Y)) == 2 endmatch -// Check if inner_add_B has a constant 1 and a NOT gate +// Branch over both ports of inner_add_B to find the NOT gate +code inner_port_sig_B + inner_port_sig_B = port(inner_add_B, \A); + branch; + inner_port_sig_B = port(inner_add_B, \B); +endcode + match not_gate_B select not_gate_B->type == $not + index port(not_gate_B, \Y) === inner_port_sig_B endmatch code root_add inner_add_B not_gate_B minuend subtrahend result_sig is_signed { - // Require consistent signedness on the root add. + // Require consistent signedness across the chain if (root_add->getParam(ID::B_SIGNED).as_bool() != is_signed) reject; @@ -155,6 +167,8 @@ code root_add inner_add_B not_gate_B minuend subtrahend result_sig is_signed if (inner_add_B->getParam(ID::B_SIGNED).as_bool() != is_signed) reject; + // 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 not_y = port(not_gate_B, \Y); @@ -175,13 +189,13 @@ code root_add inner_add_B not_gate_B minuend subtrahend result_sig is_signed if (!valid) reject; + // 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)) minuend = root_b; else minuend = root_a; - // Create the subtraction cell log("manual2sub in %s: Found a + (~b + 1) pattern, creating $sub for %s\n", log_id(module), log_signal(result_sig)); Cell *cell = root_add; int width = GetSize(result_sig); @@ -190,32 +204,30 @@ code root_add inner_add_B not_gate_B minuend subtrahend result_sig is_signed // Reject if the +1 wrap boundary is narrower than the final result if (inner_width < width) reject; - + + // Anchor both operands to the inner add width to preserve carry behavior SigSpec minuend_rs = minuend; SigSpec subtrahend_rs = subtrahend; - // Anchor both operands to the inner add width to preserve carry behavior minuend_rs.extend_u0(inner_width, is_signed); subtrahend_rs.extend_u0(inner_width, is_signed); SigSpec sub_y = result_sig; - // Extend the sub result back to the root width when needed if (inner_width != width) { sub_y = module->addWire(NEW_ID2_SUFFIX("sub_y"), inner_width); } Cell *sub = module->addSub(NEW_ID2_SUFFIX("sub"), minuend_rs, subtrahend_rs, sub_y, is_signed); + // Extend the sub result back to root width when inner is wider if (inner_width != width) { SigSpec sub_y_rs = sub_y; sub_y_rs.extend_u0(width, is_signed); module->connect(result_sig, sub_y_rs); } - // Let fixup_parameters handle width adjustments sub->fixup_parameters(); - // Remove old cells autoremove(root_add); autoremove(inner_add_B); autoremove(not_gate_B); diff --git a/passes/silimate/peepopt_muxneg.pmg b/passes/silimate/peepopt_muxneg.pmg index 7a8c33eb5..e81fc7971 100644 --- a/passes/silimate/peepopt_muxneg.pmg +++ b/passes/silimate/peepopt_muxneg.pmg @@ -20,7 +20,7 @@ endmatch match neg_a select neg_a->type == $neg - select nusers(port(neg_a, \Y)) == 2 + filter nusers(port(neg_a, \Y)) == 2 index port(neg_a, \Y) === mux_a set neg_a_in port(neg_a, \A) set neg_a_y port(neg_a, \Y) @@ -29,7 +29,7 @@ endmatch match neg_b select neg_b->type == $neg - select nusers(port(neg_b, \Y)) == 2 + filter nusers(port(neg_b, \Y)) == 2 index port(neg_b, \Y) === mux_b set neg_b_in port(neg_b, \A) set neg_b_y port(neg_b, \Y) diff --git a/passes/silimate/peepopt_neg2sub.pmg b/passes/silimate/peepopt_neg2sub.pmg index 6a958e431..9594e3b47 100644 --- a/passes/silimate/peepopt_neg2sub.pmg +++ b/passes/silimate/peepopt_neg2sub.pmg @@ -28,7 +28,7 @@ endcode match neg select neg->type == $neg - select nusers(port(neg, \Y)) == 2 + filter nusers(port(neg, \Y)) == 2 index port(neg, \Y) === (neg_on_a ? add_a : add_b) set neg_a port(neg, \A) set neg_y port(neg, \Y) diff --git a/passes/silimate/peepopt_negexpand.pmg b/passes/silimate/peepopt_negexpand.pmg index a764bcb34..7ddc0b950 100644 --- a/passes/silimate/peepopt_negexpand.pmg +++ b/passes/silimate/peepopt_negexpand.pmg @@ -20,7 +20,7 @@ endmatch match add select add->type == $add index port(add, \Y) === neg_a - select nusers(port(add, \Y)) == 2 + filter nusers(port(add, \Y)) == 2 set add_a port(add, \A) set add_b port(add, \B) endmatch diff --git a/passes/silimate/peepopt_negmux.pmg b/passes/silimate/peepopt_negmux.pmg index 579732484..d1918e3bf 100644 --- a/passes/silimate/peepopt_negmux.pmg +++ b/passes/silimate/peepopt_negmux.pmg @@ -18,8 +18,8 @@ match neg endmatch match mux - select mux->type == $mux - select nusers(port(mux, \Y)) == 2 + select mux->type == $mux + select nusers(port(mux, \Y)) == 2 set mux_a port(mux, \A) set mux_b port(mux, \B) set mux_s port(mux, \S) @@ -43,7 +43,7 @@ code neg_a neg_y mux_a mux_b mux_s mux_y a_signed } { - // Anchor negations to the original negation width + // Anchor negations to the original neg output width int width = GetSize(neg_y); Cell *cell = neg; diff --git a/passes/silimate/peepopt_negneg.pmg b/passes/silimate/peepopt_negneg.pmg index eafebc0d2..166e641b0 100644 --- a/passes/silimate/peepopt_negneg.pmg +++ b/passes/silimate/peepopt_negneg.pmg @@ -18,7 +18,7 @@ endmatch match neg2 select neg2->type == $neg index port(neg2, \Y) === neg1_a - select nusers(port(neg2, \Y)) == 2 + filter nusers(port(neg2, \Y)) == 2 set neg2_a port(neg2, \A) endmatch diff --git a/passes/silimate/peepopt_negrebuild.pmg b/passes/silimate/peepopt_negrebuild.pmg index 57739b210..37c8fba5e 100644 --- a/passes/silimate/peepopt_negrebuild.pmg +++ b/passes/silimate/peepopt_negrebuild.pmg @@ -39,16 +39,16 @@ code add_a add_b add_y neg1_a neg1_y neg2_a neg2_y add_signed add_b_signed neg1_ if (add_signed != add_b_signed) reject; - // Require negations to share the add signedness + // Require both negations share the add's signedness if (neg1_signed != add_signed || neg2_signed != add_signed) reject; { - // Avoid matching the same neg cell twice + // Avoid matching the same neg cell for both inputs if (neg1 == neg2) reject; - // Require a single wrap boundary for both negations and the sum + // Require a single wrap boundary across both negations and the sum if (GetSize(neg1_y) != GetSize(neg2_y)) reject; if (GetSize(add_y) != GetSize(neg1_y)) @@ -89,11 +89,13 @@ code add_a add_b add_y neg1_a neg1_y neg2_a neg2_y add_signed add_b_signed neg1_ neg1_a_rs.extend_u0(width, add_signed); neg2_a_rs.extend_u0(width, add_signed); + // Build -(a + b) to replace (-a) + (-b) SigSpec sum = module->addWire(NEW_ID2_SUFFIX("sum"), width); Cell *new_add = module->addAdd(NEW_ID2_SUFFIX("add"), neg1_a_rs, neg2_a_rs, sum, add_signed); SigSpec neg_out = module->addWire(NEW_ID2_SUFFIX("neg_y"), width); Cell *new_neg = module->addNeg(NEW_ID2_SUFFIX("neg"), sum, neg_out, add_signed); + // Extend result back to original add output width SigSpec neg_out_rs = neg_out; neg_out_rs.extend_u0(GetSize(add_y), add_signed); module->connect(add_y, neg_out_rs); From 7e8331dd95e11765d33b26d67c043ee6a9249ae2 Mon Sep 17 00:00:00 2001 From: Stan Lee Date: Tue, 3 Mar 2026 15:15:26 -0800 Subject: [PATCH 18/18] greptile --- kernel/fstdata.cc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/kernel/fstdata.cc b/kernel/fstdata.cc index f417048f2..d9cc321da 100644 --- a/kernel/fstdata.cc +++ b/kernel/fstdata.cc @@ -116,10 +116,13 @@ void FstData::extractVarNames() // Track nested fork scopes using a stack to handle nested packed structs // Begins with outmost scope and ends with innermost scope + // Scopes are not normalized on the stack std::vector fork_scope_stack; // Start fork handles after the maximum real handle from FST file to avoid collisions fstHandle next_fork_handle = fstReaderGetMaxHandle(ctx) + 1; + + // Map of fork scopes to their members, which are all normalized std::map> fork_scopes; while ((h = fstReaderIterateHier(ctx))) { @@ -152,6 +155,7 @@ void FstData::extractVarNames() // If this is a nested fork scope, add its handle to the parent fork scope if (fork_scope_stack.size() > 1) { std::string parent_fork = fork_scope_stack[fork_scope_stack.size() - 2]; + normalize_brackets(parent_fork); fork_scopes[parent_fork].push_back(fork_handle); } @@ -176,7 +180,9 @@ void FstData::extractVarNames() // Add variable to the innermost fork scope in the fork scope stack if (!fork_scope_stack.empty()) { - fork_scopes[fork_scope_stack.back()].push_back(h->u.var.handle); + std::string current_fork = fork_scope_stack.back(); + normalize_brackets(current_fork); + fork_scopes[current_fork].push_back(h->u.var.handle); } std::string clean_name;