pull main

This commit is contained in:
Stan Lee 2026-04-20 16:57:15 -07:00
commit f377bb04e4
4 changed files with 113 additions and 275 deletions

View File

@ -103,13 +103,6 @@ dict<int,fstHandle> FstData::getMemoryHandles(std::string name) {
return dict<int,fstHandle>();
};
dict<std::vector<int>,fstHandle> FstData::getArrayHandles(std::string name) {
if (array_to_handle.find(name) != array_to_handle.end())
return array_to_handle[name];
else
return dict<std::vector<int>,fstHandle>();
};
static std::string remove_spaces(std::string str)
{
str.erase(std::remove(str.begin(), str.end(), ' '), str.end());
@ -121,54 +114,13 @@ void FstData::extractVarNames()
struct fstHier *h;
std::string fst_scope_name;
// 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<std::string> 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<std::string, std::vector<fstHandle>> fork_scopes;
while ((h = fstReaderIterateHier(ctx))) {
switch (h->htyp) {
case FST_HT_SCOPE: {
fst_scope_name = fstReaderPushScope(ctx, h->u.scope.name, NULL);
// Fork scopes are identified by FST_ST_VCD_FORK and are pushed onto the stack
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<fstHandle>();
}
break;
}
case FST_HT_UPSCOPE: {
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()
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[fst_scope_name];
// 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);
}
// Pop this fork scope from the stack
fork_scope_stack.pop_back();
}
fst_scope_name = fstReaderPopScope(ctx);
break;
}
@ -185,70 +137,17 @@ void FstData::extractVarNames()
if (!var.is_alias)
handle_to_var[h->u.var.handle] = var;
// Add variable to the innermost fork scope in the fork scope stack
if (!fork_scope_stack.empty()) {
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 = var.name;
if (!clean_name.empty() && clean_name[0] == '\\')
clean_name = clean_name.substr(1);
std::string clean_name;
bool has_space = false;
for(size_t i=0;i<strlen(h->u.var.name);i++)
{
char c = h->u.var.name[i];
if(c==' ') { has_space = true; break; }
clean_name += c;
}
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("[");
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 [i0][i1]...[iN][bitrange]" format
std::string full_name = h->u.var.name;
size_t space_pos = full_name.find(' ');
if (space_pos != std::string::npos) {
// Extract "[i0][i1]...[iN][bitrange]" suffix
std::string suffix = full_name.substr(space_pos + 1);
// Parse arbitrary number of array indices
size_t open = 0;
std::vector<int> array_indices;
while (open < suffix.length() && suffix[open] == '[') {
size_t close = suffix.find(']', open);
if (close != std::string::npos) {
std::string index_str = suffix.substr(open + 1, close - open - 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);
array_indices.push_back(array_index);
}
} else {
log_warning("Error parsing array index in : %s\n", full_name.c_str());
break;
}
// Move to next opening bracket
open = close + 1;
}
// Add array indices to array_to_handle map if there are any
if (!array_indices.empty()) {
array_to_handle[var.scope+"."+clean_name][array_indices] = var.id;
}
// Strip trailing bit range [N:M] if present
if (!clean_name.empty() && clean_name.back() == ']') {
size_t open = clean_name.rfind('[');
if (open != std::string::npos) {
std::string inner = clean_name.substr(open + 1, clean_name.size() - open - 2);
if (inner.find(':') != std::string::npos)
clean_name.erase(open);
}
}
@ -375,51 +274,6 @@ void FstData::reconstructAllAtTimes(std::vector<fstHandle> &signal, uint64_t sta
std::string FstData::valueOf(fstHandle signal)
{
// 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<fstHandle>& members = it->second;
// Iterate over members of the struct to get concatenated value.
// The first declared member is MSB in SystemVerilog packed structs
for (auto m = members.begin(); m != members.end(); m++) {
fstHandle member = *m;
std::string member_val;
// 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;
}
} 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;
}
return result;
}
// Normal signal handling
if (past_data.find(signal) == past_data.end()) {
return std::string(handle_to_var[signal].width, 'x');
}
@ -428,29 +282,16 @@ std::string FstData::valueOf(fstHandle signal)
int FstData::getWidth(fstHandle signal)
{
// Check if signal is a fork scope (struct)
if (fork_scope_members.count(signal)) {
// Sum the widths of all members of the fork scope, which may be forks themselves
int width = 0;
for (fstHandle member : fork_scope_members[signal]) {
width += getWidth(member);
}
return width;
}
if (handle_to_var.count(signal)) {
return handle_to_var[signal].width;
}
// Signal not found
log_warning("Signal %d was not extracted from file...\n", signal);
return 0;
}
// Auto-discover scope from FST by finding the top module
std::string FstData::autoScope(Module *topmod) {
log("Auto-discovering scopes from %d candidates...\n", GetSize(name_to_handle));
std::string top = RTLIL::unescape_id(topmod->name);
std::string scope = "";
@ -463,41 +304,44 @@ std::string FstData::autoScope(Module *topmod) {
}
log("Extracted %d ports from module '%s'\n", GetSize(top2widths), top.c_str());
// For each scope, track the number of matching ports
dict<std::string, int> scopes2matches;
// Use name_to_handle to get all signals from the FST file
// Extract list of candidate scopes from name_to_handle
pool<std::string> candidate_scopes;
for (auto entry : name_to_handle) {
std::string name = entry.first;
fstHandle handle = entry.second;
// Extract signal name and scope using '.'
// Signal names of form '{scope}.signal_name' with scope potentially
// having zero to multiple '.'
size_t last_dot = name.find_last_of('.');
if (last_dot != std::string::npos) { // no '.' means no scope/signal extraction is possible
if (last_dot != std::string::npos) {
std::string scope = name.substr(0, last_dot);
std::string signal_name = name.substr(last_dot + 1);
candidate_scopes.insert(scope);
}
}
log("Auto-discovering scopes from %d candidates...\n", GetSize(candidate_scopes));
// Check that signal is in the top module and width matches
if (top2widths.count(signal_name)) {
int signal_width = getWidth(handle);
if (signal_width == top2widths[signal_name]) {
scopes2matches[scope]++;
}
// Track number of exact matches for each scope, adding to results if all match
std::vector<std::string> results;
for (const auto &scope_candidate : candidate_scopes) {
int matches = 0;
// Loop through all top-level ports
for (auto &port : top2widths) {
const std::string &port_name = port.first;
int port_width = port.second;
std::string key = scope_candidate + "." + port_name;
auto it = name_to_handle.find(key);
// Check the signal exists and has correct width to determine a match
if (it != name_to_handle.end() && getWidth(it->second) == port_width) {
matches++;
}
}
}
// Find scopes with exact matches and add to array
std::vector<std::string> results;
for (const auto& entry : scopes2matches) {
int num_matches = entry.second;
if (num_matches == GetSize(top2widths)) {
std::string scope = entry.first;
results.push_back(scope);
// If all ports match, add to results
if (matches == GetSize(top2widths)) {
results.push_back(scope_candidate);
}
}
// Logging results
if (results.empty()) {
log_warning("Could not auto-discover scope for module '%s'...\n",
top.c_str());

View File

@ -55,7 +55,6 @@ class FstData
std::string valueOf(fstHandle signal);
fstHandle getHandle(std::string name);
dict<int,fstHandle> getMemoryHandles(std::string name);
dict<std::vector<int>,fstHandle> getArrayHandles(std::string name);
double getTimescale() { return timescale; }
const char *getTimescaleString() { return timescale_str.c_str(); }
int getWidth(fstHandle signal);
@ -68,12 +67,10 @@ private:
std::map<fstHandle, FstVar> handle_to_var;
std::map<std::string, fstHandle> name_to_handle;
std::map<std::string, dict<int, fstHandle>> memory_to_handle;
std::map<std::string, dict<std::vector<int>, fstHandle>> array_to_handle;
std::map<fstHandle, std::string> last_data;
uint64_t last_time;
std::map<fstHandle, std::string> past_data;
uint64_t past_time;
std::map<fstHandle, std::vector<fstHandle>> fork_scope_members;
double timescale;
std::string timescale_str;
uint64_t start_time;

View File

@ -228,35 +228,6 @@ struct SimInstance
dict<Wire*, fstHandle> fst_inputs;
dict<IdString, dict<int,fstHandle>> fst_memories;
// For multi-dimensional arrays
dict<Wire*, dict<std::vector<int>,fstHandle>> fst_array_handles;
dict<Wire*, dict<std::vector<int>,fstHandle>> 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<std::vector<int>, fstHandle> tryGetArrayHandles(FstData* fst, const std::string& scope, Wire* wire)
{
std::string wire_name = scope + "." + RTLIL::unescape_id(wire->name);
dict<std::vector<int>, fstHandle> array_handles = fst->getArrayHandles(wire_name);
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;
}
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<std::vector<int>, fstHandle>();
}
// Helper function to set wire state from array element handles
// Concatenates values from array elements in descending index order
bool setStateFromArrayHandles(Wire* wire, dict<std::vector<int>, fstHandle>& handles)
@ -307,16 +278,11 @@ struct SimInstance
}
}
// Populate fst_handles and fst_array_handles for signal lookups
// Populate fst_handles for signal lookups
if ((shared->fst) && !(shared->hide_internal && wire->name[0] == '$')) {
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<std::vector<int>, fstHandle> 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;
} else if (id != 0) {
if (id != 0) {
// Case of a regular wire/reg
fst_handles[wire] = id;
if (shared->debug) {
@ -1258,10 +1224,6 @@ 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) {
did_something |= setStateFromArrayHandles(item.first, item.second);
}
for (auto cell : module->cells())
{
if (cell->is_mem_cell()) {
@ -1301,11 +1263,6 @@ struct SimInstance
// Overwrite simulation register state with the ground truth
did_something |= set_state(wire, vcd_val);
}
// Handles multi-dimensional registers
for (auto &item : fst_array_handles) {
if (register_wires.count(item.first) == 0) continue;
did_something |= setStateFromArrayHandles(item.first, item.second);
}
// Apply to all child modules
for (auto child : children)
did_something |= child.second->setRegisters(time);
@ -1344,10 +1301,6 @@ 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) {
did_something |= setStateFromArrayHandles(item.first, item.second);
}
for (auto child : children)
did_something |= child.second->setInputs();
@ -1646,16 +1599,10 @@ struct SimWorker : SimShared
for (auto wire : topmod->wires()) {
// Populate fst_inputs and fst_array_inputs for input ports
// Populate fst_inputs for input ports
if (wire->port_input) {
fstHandle id = fst->getHandle(scope + "." + RTLIL::unescape_id(wire->name));
// Try to get array element handles if this is a multi-dimensional array
dict<std::vector<int>, fstHandle> 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;
} else if (id != 0) {
if (id != 0) {
// Case of a regular wire/reg
top->fst_inputs[wire] = id;
} else {

View File

@ -25,6 +25,12 @@
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
// Struct stores the width and index offset of a register in VCD file.
struct RegInfo {
int width = 0;
int offset = 0;
};
struct RegRenameInstance {
std::string vcd_scope;
Module *module;
@ -58,7 +64,7 @@ struct RegRenameInstance {
// Processes registers in a given module hierarchy
// and renames to allow for correct register annotation
void process_registers(dict<std::pair<std::string, std::string>, int> &vcd_reg_widths)
void process_registers(dict<std::pair<std::string, std::string>, RegInfo> &vcd_reg_widths)
{
if (debug)
log("Processing registers in scope: %s (module: %s)\n",
@ -69,6 +75,7 @@ struct RegRenameInstance {
// Map of old bits to new bits of a renamed reg wire
dict<SigBit, SigBit> bit_map;
pool<SigBit> claimed_bits;
// Caches of target wires and wires to remove
dict<IdString, Wire*> targetWireCache;
@ -115,9 +122,12 @@ struct RegRenameInstance {
Wire *oldWire = conn.second.as_wire();
if (oldWire->port_input || oldWire->port_output) continue;
// Lookup wire width from VCD
// Lookup wire information from VCD
std::string regName = RTLIL::unescape_id(wireName);
int wireWidth = vcd_reg_widths[{vcd_scope, regName}];
RegInfo regInfo = vcd_reg_widths[{vcd_scope, regName}];
int wireWidth = regInfo.width;
int wireOffset = regInfo.offset;
if (wireWidth == 0) {
log_warning("Unable to find matching register %s in VCD for cell %s in scope %s\n",
regName.c_str(), cellName.c_str(), vcd_scope.c_str());
@ -125,9 +135,11 @@ struct RegRenameInstance {
}
// Validate bit index
if (bitIndex >= wireWidth) {
log_warning("Bit index %d exceeds wire width %d for '%s'\n",
bitIndex, wireWidth, wireName.c_str());
int maxIndex = wireOffset + wireWidth - 1;
int minIndex = wireOffset;
if (bitIndex < minIndex || bitIndex > maxIndex) {
log_warning("Bit index %d is invalid for wire indices [%d:%d] for '%s'\n",
bitIndex, maxIndex, minIndex, wireName.c_str());
continue;
}
@ -146,9 +158,10 @@ struct RegRenameInstance {
targetWire = module->wire(wireId);
if (!targetWire) {
if (debug)
log("Creating wire %s[%d:0] in scope %s\n",
wireName.c_str(), wireWidth - 1, vcd_scope.c_str());
log("Creating wire %s[%d:%d] in scope %s\n",
wireName.c_str(), maxIndex, minIndex, vcd_scope.c_str());
targetWire = module->addWire(wireId, wireWidth);
targetWire->start_offset = wireOffset;
}
targetWireCache[wireId] = targetWire;
}
@ -157,14 +170,33 @@ struct RegRenameInstance {
if (targetWire == oldWire)
continue;
int normalizedIndex = bitIndex - wireOffset;
// Check for conflicts with other cells (multiple drivers guard)
bool conflict = false;
for (int i = 0; i < GetSize(oldWire); i++) {
if (claimed_bits.count(SigBit(targetWire, normalizedIndex + i))) {
conflict = true;
break;
}
}
if (conflict) {
log_warning("Skipping cell %s: target %s[%d] already driven by another cell\n",
log_id(cell->name), wireName.c_str(), bitIndex);
continue;
}
// Create the new connection.
if (debug)
log("Connecting %s to %s[%d]\n",
log_id(oldWire), wireName.c_str(), bitIndex);
// Record the mapping for each bit of the old wire to the target wire
for (int i = 0; i < GetSize(oldWire); i++)
bit_map[SigBit(oldWire, i)] = SigBit(targetWire, bitIndex + i);
// Record the mapping for each bit of the old wire to the target wire.
for (int i = 0; i < GetSize(oldWire); i++) {
SigBit target(targetWire, normalizedIndex + i);
bit_map[SigBit(oldWire, i)] = target;
claimed_bits.insert(target);
}
wireRemoveCache.insert(oldWire);
}
}
@ -186,7 +218,7 @@ struct RegRenameInstance {
module->remove(wireRemoveCache);
}
void process_all(dict<std::pair<std::string, std::string>, int> &vcd_reg_widths)
void process_all(dict<std::pair<std::string, std::string>, RegInfo> &vcd_reg_widths)
{
process_registers(vcd_reg_widths);
for (auto &it : children)
@ -248,7 +280,7 @@ struct RegRenamePass : public Pass {
log_error("No top module found!\n");
// Extract pre-optimization signal widths from VCD file
dict<std::pair<std::string, std::string>, int> vcd_reg_widths;
dict<std::pair<std::string, std::string>, RegInfo> vcd_reg_widths;
if (!vcd_filename.empty()) {
log("Reading VCD file: %s\n", vcd_filename.c_str());
try {
@ -266,18 +298,36 @@ struct RegRenamePass : public Pass {
for (auto &var : fst.getVars()) {
std::string vcd_scope = var.scope;
std::string signal_name = var.name;
std::string signal_bits = "";
// Remove bracket notation if present to preserve register name
if (auto pos = signal_name.rfind('['); pos != std::string::npos)
signal_name.erase(pos);
// Use the bracket notation to extract the bit range and construct true reg name.
size_t bit_pos = signal_name.rfind('[');
if (bit_pos != std::string::npos) {
signal_bits = signal_name.substr(bit_pos);
signal_name.erase(bit_pos);
}
// Extract the LSB and MSB indices if present.
int msb = 0;
int lsb = 0;
size_t colon_pos = signal_bits.find(':');
if (colon_pos != std::string::npos) { // range case
msb = std::stoi(signal_bits.substr(1, colon_pos - 1));
lsb = std::stoi(signal_bits.substr(colon_pos + 1));
} else if (!signal_bits.empty()) { // single index case
msb = lsb = std::stoi(signal_bits.substr(1));
}
int width = var.width;
int offset = std::min(msb, lsb);
// Map the register's vcd scope and name to
// its original width for later lookup.
// its original width and offset for later lookup.
signal_name = RTLIL::unescape_id(signal_name);
vcd_reg_widths[{vcd_scope, signal_name}] = var.width;
vcd_reg_widths[{vcd_scope, signal_name}] = {width, offset};
if (debug)
log("Found signal '%s' in scope '%s' with width %d\n",
signal_name.c_str(), vcd_scope.c_str(), var.width);
log("Found signal '%s' in scope '%s' with range [%d:%d] (width %d)\n",
signal_name.c_str(), vcd_scope.c_str(),
offset + width - 1, offset, width);
}
log("Extracted %d signal widths from VCD\n", GetSize(vcd_reg_widths));
} catch (const std::exception &e) {