Start restoring memory state from VCD/FST
[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_error("Error parsing memory address in : %s\n", clean_name.c_str());
145 }
146 memory_to_handle[var.scope+"."+mem_cell][mem_addr] = var.id;
147 }
148 pos = clean_name.find_last_of("[");
149 if (pos != std::string::npos) {
150 std::string mem_cell = clean_name.substr(0, pos);
151 std::string addr = clean_name.substr(pos+1);
152 addr.pop_back(); // remove closing bracket
153 char *endptr;
154 int mem_addr = strtol(addr.c_str(), &endptr, 10);
155 if (*endptr) {
156 log_error("Error parsing memory address in : %s\n", clean_name.c_str());
157 }
158 memory_to_handle[var.scope+"."+mem_cell][mem_addr] = var.id;
159 }
160 name_to_handle[var.scope+"."+clean_name] = h->u.var.handle;
161 break;
162 }
163 }
164 }
165 }
166
167
168 static void reconstruct_clb_varlen_attimes(void *user_data, uint64_t pnt_time, fstHandle pnt_facidx, const unsigned char *pnt_value, uint32_t plen)
169 {
170 FstData *ptr = (FstData*)user_data;
171 ptr->reconstruct_callback_attimes(pnt_time, pnt_facidx, pnt_value, plen);
172 }
173
174 static void reconstruct_clb_attimes(void *user_data, uint64_t pnt_time, fstHandle pnt_facidx, const unsigned char *pnt_value)
175 {
176 FstData *ptr = (FstData*)user_data;
177 uint32_t plen = (pnt_value) ? strlen((const char *)pnt_value) : 0;
178 ptr->reconstruct_callback_attimes(pnt_time, pnt_facidx, pnt_value, plen);
179 }
180
181 void FstData::reconstruct_callback_attimes(uint64_t pnt_time, fstHandle pnt_facidx, const unsigned char *pnt_value, uint32_t /* plen */)
182 {
183 if (pnt_time > end_time) return;
184 // if we are past the timestamp
185 bool is_clock = false;
186 if (!all_samples) {
187 for(auto &s : clk_signals) {
188 if (s==pnt_facidx) {
189 is_clock=true;
190 break;
191 }
192 }
193 }
194
195 if (pnt_time > past_time) {
196 past_data = last_data;
197 past_time = pnt_time;
198 }
199
200 if (pnt_time > last_time) {
201 if (all_samples) {
202 callback(last_time);
203 last_time = pnt_time;
204 } else {
205 if (is_clock) {
206 std::string val = std::string((const char *)pnt_value);
207 std::string prev = past_data[pnt_facidx];
208 if ((prev!="1" && val=="1") || (prev!="0" && val=="0")) {
209 callback(last_time);
210 last_time = pnt_time;
211 }
212 }
213 }
214 }
215 // always update last_data
216 last_data[pnt_facidx] = std::string((const char *)pnt_value);
217 }
218
219 void FstData::reconstructAllAtTimes(std::vector<fstHandle> &signal, uint64_t start, uint64_t end, CallbackFunction cb)
220 {
221 clk_signals = signal;
222 callback = cb;
223 start_time = start;
224 end_time = end;
225 last_data.clear();
226 last_time = start_time;
227 past_data.clear();
228 past_time = start_time;
229 all_samples = clk_signals.empty();
230
231 fstReaderSetUnlimitedTimeRange(ctx);
232 fstReaderSetFacProcessMaskAll(ctx);
233 fstReaderIterBlocks2(ctx, reconstruct_clb_attimes, reconstruct_clb_varlen_attimes, this, nullptr);
234 if (last_time!=end_time) {
235 past_data = last_data;
236 callback(last_time);
237 }
238 callback(end_time);
239 }
240
241 std::string FstData::valueOf(fstHandle signal)
242 {
243 if (past_data.find(signal) == past_data.end())
244 log_error("Signal id %d not found\n", (int)signal);
245 return past_data[signal];
246 }