Handle possible non-memory indexed data
[yosys.git] / kernel / fstdata.cc
1 /*
2 * yosys -- Yosys Open SYnthesis Suite
3 *
4 * Copyright (C) 2022 Miodrag Milanovic <micko@yosyshq.com>
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 *
18 */
19
20 #include "kernel/fstdata.h"
21
22 USING_YOSYS_NAMESPACE
23
24
25 static std::string file_base_name(std::string const & path)
26 {
27 return path.substr(path.find_last_of("/\\") + 1);
28 }
29
30 FstData::FstData(std::string filename) : ctx(nullptr)
31 {
32 #if !defined(YOSYS_DISABLE_SPAWN)
33 std::string filename_trim = file_base_name(filename);
34 if (filename_trim.size() > 4 && filename_trim.compare(filename_trim.size()-4, std::string::npos, ".vcd") == 0) {
35 filename_trim.erase(filename_trim.size()-4);
36 tmp_file = stringf("/tmp/converted_%s.fst", filename_trim.c_str());
37 std::string cmd = stringf("vcd2fst %s %s", filename.c_str(), tmp_file.c_str());
38 log("Exec: %s\n", cmd.c_str());
39 if (run_command(cmd) != 0)
40 log_cmd_error("Shell command failed!\n");
41 filename = tmp_file;
42 }
43 #endif
44 const std::vector<std::string> g_units = { "s", "ms", "us", "ns", "ps", "fs", "as", "zs" };
45 ctx = (fstReaderContext *)fstReaderOpen(filename.c_str());
46 if (!ctx)
47 log_error("Error opening '%s' as FST file\n", filename.c_str());
48 int scale = (int)fstReaderGetTimescale(ctx);
49 timescale = pow(10.0, scale);
50 timescale_str = "";
51 int unit = 0;
52 int zeros = 0;
53 if (scale > 0) {
54 zeros = scale;
55 } else {
56 if ((scale % 3) == 0) {
57 zeros = (-scale % 3);
58 unit = (-scale / 3);
59 } else {
60 zeros = 3 - (-scale % 3);
61 unit = (-scale / 3) + 1;
62 }
63 }
64 for (int i=0;i<zeros; i++) timescale_str += "0";
65 timescale_str += g_units[unit];
66 extractVarNames();
67 }
68
69 FstData::~FstData()
70 {
71 if (ctx)
72 fstReaderClose(ctx);
73 if (!tmp_file.empty())
74 remove(tmp_file.c_str());
75 }
76
77 uint64_t FstData::getStartTime() { return fstReaderGetStartTime(ctx); }
78
79 uint64_t FstData::getEndTime() { return fstReaderGetEndTime(ctx); }
80
81 fstHandle FstData::getHandle(std::string name) {
82 if (name_to_handle.find(name) != name_to_handle.end())
83 return name_to_handle[name];
84 else
85 return 0;
86 };
87
88 dict<int,fstHandle> FstData::getMemoryHandles(std::string name) {
89 if (memory_to_handle.find(name) != memory_to_handle.end())
90 return memory_to_handle[name];
91 else
92 return dict<int,fstHandle>();
93 };
94
95 static std::string remove_spaces(std::string str)
96 {
97 str.erase(std::remove(str.begin(), str.end(), ' '), str.end());
98 return str;
99 }
100
101 void FstData::extractVarNames()
102 {
103 struct fstHier *h;
104 std::string fst_scope_name;
105
106 while ((h = fstReaderIterateHier(ctx))) {
107 switch (h->htyp) {
108 case FST_HT_SCOPE: {
109 fst_scope_name = fstReaderPushScope(ctx, h->u.scope.name, NULL);
110 break;
111 }
112 case FST_HT_UPSCOPE: {
113 fst_scope_name = fstReaderPopScope(ctx);
114 break;
115 }
116 case FST_HT_VAR: {
117 FstVar var;
118 var.id = h->u.var.handle;
119 var.is_alias = h->u.var.is_alias;
120 var.is_reg = (fstVarType)h->u.var.typ == FST_VT_VCD_REG;
121 var.name = remove_spaces(h->u.var.name);
122 var.scope = fst_scope_name;
123 var.width = h->u.var.length;
124 vars.push_back(var);
125 if (!var.is_alias)
126 handle_to_var[h->u.var.handle] = var;
127 std::string clean_name;
128 for(size_t i=0;i<strlen(h->u.var.name);i++)
129 {
130 char c = h->u.var.name[i];
131 if(c==' ') break;
132 clean_name += c;
133 }
134 if (clean_name[0]=='\\')
135 clean_name = clean_name.substr(1);
136 size_t pos = clean_name.find_last_of("<");
137 if (pos != std::string::npos) {
138 std::string mem_cell = clean_name.substr(0, pos);
139 std::string addr = clean_name.substr(pos+1);
140 addr.pop_back(); // remove closing bracket
141 char *endptr;
142 int mem_addr = strtol(addr.c_str(), &endptr, 16);
143 if (*endptr) {
144 log_warning("Error parsing memory address in : %s\n", clean_name.c_str());
145 } else {
146 memory_to_handle[var.scope+"."+mem_cell][mem_addr] = var.id;
147 name_to_handle[stringf("%s.%s[%d]",var.scope.c_str(),mem_cell.c_str(),mem_addr)] = h->u.var.handle;
148 continue;
149 }
150 }
151 pos = clean_name.find_last_of("[");
152 if (pos != std::string::npos) {
153 std::string mem_cell = clean_name.substr(0, pos);
154 std::string addr = clean_name.substr(pos+1);
155 addr.pop_back(); // remove closing bracket
156 char *endptr;
157 int mem_addr = strtol(addr.c_str(), &endptr, 10);
158 if (*endptr) {
159 log_warning("Error parsing memory address in : %s\n", clean_name.c_str());
160 } else {
161 memory_to_handle[var.scope+"."+mem_cell][mem_addr] = var.id;
162 name_to_handle[stringf("%s.%s[%d]",var.scope.c_str(),mem_cell.c_str(),mem_addr)] = h->u.var.handle;
163 continue;
164 }
165 }
166 name_to_handle[var.scope+"."+clean_name] = h->u.var.handle;
167 break;
168 }
169 }
170 }
171 }
172
173
174 static void reconstruct_clb_varlen_attimes(void *user_data, uint64_t pnt_time, fstHandle pnt_facidx, const unsigned char *pnt_value, uint32_t plen)
175 {
176 FstData *ptr = (FstData*)user_data;
177 ptr->reconstruct_callback_attimes(pnt_time, pnt_facidx, pnt_value, plen);
178 }
179
180 static void reconstruct_clb_attimes(void *user_data, uint64_t pnt_time, fstHandle pnt_facidx, const unsigned char *pnt_value)
181 {
182 FstData *ptr = (FstData*)user_data;
183 uint32_t plen = (pnt_value) ? strlen((const char *)pnt_value) : 0;
184 ptr->reconstruct_callback_attimes(pnt_time, pnt_facidx, pnt_value, plen);
185 }
186
187 void FstData::reconstruct_callback_attimes(uint64_t pnt_time, fstHandle pnt_facidx, const unsigned char *pnt_value, uint32_t /* plen */)
188 {
189 if (pnt_time > end_time) return;
190 // if we are past the timestamp
191 bool is_clock = false;
192 if (!all_samples) {
193 for(auto &s : clk_signals) {
194 if (s==pnt_facidx) {
195 is_clock=true;
196 break;
197 }
198 }
199 }
200
201 if (pnt_time > past_time) {
202 past_data = last_data;
203 past_time = pnt_time;
204 }
205
206 if (pnt_time > last_time) {
207 if (all_samples) {
208 callback(last_time);
209 last_time = pnt_time;
210 } else {
211 if (is_clock) {
212 std::string val = std::string((const char *)pnt_value);
213 std::string prev = past_data[pnt_facidx];
214 if ((prev!="1" && val=="1") || (prev!="0" && val=="0")) {
215 callback(last_time);
216 last_time = pnt_time;
217 }
218 }
219 }
220 }
221 // always update last_data
222 last_data[pnt_facidx] = std::string((const char *)pnt_value);
223 }
224
225 void FstData::reconstructAllAtTimes(std::vector<fstHandle> &signal, uint64_t start, uint64_t end, CallbackFunction cb)
226 {
227 clk_signals = signal;
228 callback = cb;
229 start_time = start;
230 end_time = end;
231 last_data.clear();
232 last_time = start_time;
233 past_data.clear();
234 past_time = start_time;
235 all_samples = clk_signals.empty();
236
237 fstReaderSetUnlimitedTimeRange(ctx);
238 fstReaderSetFacProcessMaskAll(ctx);
239 fstReaderIterBlocks2(ctx, reconstruct_clb_attimes, reconstruct_clb_varlen_attimes, this, nullptr);
240 if (last_time!=end_time) {
241 past_data = last_data;
242 callback(last_time);
243 }
244 callback(end_time);
245 }
246
247 std::string FstData::valueOf(fstHandle signal)
248 {
249 if (past_data.find(signal) == past_data.end())
250 log_error("Signal id %d not found\n", (int)signal);
251 return past_data[signal];
252 }