mirror of https://github.com/YosysHQ/yosys.git
Merge pull request #155 from Silimate/fork-scope
[CUS-486] Strip $fork scope and N-dim register flattening
This commit is contained in:
commit
c05efa055b
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Reference in New Issue