/* * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2022 Miodrag Milanovic * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ #include "kernel/fstdata.h" USING_YOSYS_NAMESPACE static std::string file_base_name(std::string const & path) { return path.substr(path.find_last_of("/\\") + 1); } FstData::FstData(std::string filename) : ctx(nullptr) { #if !defined(YOSYS_DISABLE_SPAWN) std::string filename_trim = file_base_name(filename); if (filename_trim.size() > 4 && filename_trim.compare(filename_trim.size()-4, std::string::npos, ".vcd") == 0) { filename_trim.erase(filename_trim.size()-4); tmp_file = stringf("%s/converted_%s.fst", get_base_tmpdir(), filename_trim); std::string cmd = stringf("vcd2fst %s %s", filename, tmp_file); log("Exec: %s\n", cmd); if (run_command(cmd) != 0) log_cmd_error("Shell command failed!\n"); filename = tmp_file; } #endif const std::vector g_units = { "s", "ms", "us", "ns", "ps", "fs", "as", "zs" }; ctx = (fstReaderContext *)fstReaderOpen(filename.c_str()); if (!ctx) log_error("Error opening '%s' as FST file\n", filename); int scale = (int)fstReaderGetTimescale(ctx); timescale = pow(10.0, scale); timescale_str = ""; int unit = 0; int zeros = 0; if (scale > 0) { zeros = scale; } else { if ((scale % 3) == 0) { zeros = (-scale % 3); unit = (-scale / 3); } else { zeros = 3 - (-scale % 3); unit = (-scale / 3) + 1; } } for (int i=0;i') c = ']'; } } fstHandle FstData::getHandle(std::string name) { normalize_brackets(name); if (name_to_handle.find(name) != name_to_handle.end()) return name_to_handle[name]; else return 0; }; dict FstData::getMemoryHandles(std::string name) { if (memory_to_handle.find(name) != memory_to_handle.end()) return memory_to_handle[name]; else return dict(); }; static std::string remove_spaces(std::string str) { str.erase(std::remove(str.begin(), str.end(), ' '), str.end()); return str; } void FstData::extractVarNames() { struct fstHier *h; std::string fst_scope_name; // Track current fork scope std::string current_fork_scope; // 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))) { 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 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; } 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; // 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(); } fst_scope_name = fstReaderPopScope(ctx); break; } case FST_HT_VAR: { FstVar var; var.id = h->u.var.handle; var.is_alias = h->u.var.is_alias; var.is_reg = (fstVarType)h->u.var.typ == FST_VT_VCD_REG; var.name = remove_spaces(h->u.var.name); 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; // 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++) { 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); if (!has_space) { size_t pos = clean_name.find_last_of("["); std::string index_or_range = clean_name.substr(pos+1); if (index_or_range.find(":") != std::string::npos) { clean_name = clean_name.substr(0,pos); } } size_t pos = clean_name.find_last_of("<"); if (pos != std::string::npos && clean_name.back() == '>') { std::string mem_cell = clean_name.substr(0, pos); normalize_brackets(mem_cell); std::string addr = clean_name.substr(pos+1); addr.pop_back(); // remove closing bracket char *endptr; int mem_addr = strtol(addr.c_str(), &endptr, 16); if (*endptr) { log_debug("Error parsing memory address in : %s\n", clean_name); } else { memory_to_handle[var.scope+"."+mem_cell][mem_addr] = var.id; } } pos = clean_name.find_last_of("["); if (pos != std::string::npos && clean_name.back() == ']') { std::string mem_cell = clean_name.substr(0, pos); normalize_brackets(mem_cell); std::string addr = clean_name.substr(pos+1); addr.pop_back(); // remove closing bracket char *endptr; int mem_addr = strtol(addr.c_str(), &endptr, 10); if (*endptr) { log_debug("Error parsing memory address in : %s\n", clean_name); } else { memory_to_handle[var.scope+"."+mem_cell][mem_addr] = var.id; } } normalize_brackets(clean_name); name_to_handle[var.scope+"."+clean_name] = h->u.var.handle; break; } } } } static void reconstruct_clb_varlen_attimes(void *user_data, uint64_t pnt_time, fstHandle pnt_facidx, const unsigned char *pnt_value, uint32_t plen) { FstData *ptr = (FstData*)user_data; ptr->reconstruct_callback_attimes(pnt_time, pnt_facidx, pnt_value, plen); } static void reconstruct_clb_attimes(void *user_data, uint64_t pnt_time, fstHandle pnt_facidx, const unsigned char *pnt_value) { FstData *ptr = (FstData*)user_data; uint32_t plen = (pnt_value) ? strlen((const char *)pnt_value) : 0; ptr->reconstruct_callback_attimes(pnt_time, pnt_facidx, pnt_value, plen); } void FstData::reconstruct_callback_attimes(uint64_t pnt_time, fstHandle pnt_facidx, const unsigned char *pnt_value, uint32_t /* plen */) { if (pnt_time > end_time || !pnt_value) return; if (curr_cycle > last_cycle) return; // if we are past the timestamp bool is_clock = false; if (!all_samples) { for(auto &s : clk_signals) { if (s==pnt_facidx) { is_clock=true; break; } } } if (pnt_time > past_time) { past_data = last_data; past_time = pnt_time; } if (pnt_time > last_time) { if (all_samples) { callback(last_time); curr_cycle++; last_time = pnt_time; } else { if (is_clock) { std::string val = std::string((const char *)pnt_value); std::string prev = past_data[pnt_facidx]; if ((prev!="1" && val=="1") || (prev!="0" && val=="0")) { callback(last_time); curr_cycle++; last_time = pnt_time; } } } } // always update last_data last_data[pnt_facidx] = std::string((const char *)pnt_value); } void FstData::reconstructAllAtTimes(std::vector &signal, uint64_t start, uint64_t end, unsigned int end_cycle, CallbackFunction cb) { clk_signals = signal; callback = cb; start_time = start; end_time = end; curr_cycle = 0; last_cycle = end_cycle; last_data.clear(); last_time = start_time; past_data.clear(); past_time = start_time; all_samples = clk_signals.empty(); fstReaderSetUnlimitedTimeRange(ctx); fstReaderSetFacProcessMaskAll(ctx); fstReaderIterBlocks2(ctx, reconstruct_clb_attimes, reconstruct_clb_varlen_attimes, this, nullptr); if (last_time!=end_time && curr_cycle <= last_cycle) { past_data = last_data; callback(last_time); curr_cycle++; } if (curr_cycle <= last_cycle) { past_data = last_data; callback(end_time); curr_cycle++; } } 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& 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.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 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 (handle_to_var.find(signal) != handle_to_var.end()) { return std::string(handle_to_var[signal].width, 'x'); } return past_data[signal]; }