89a403e63612ae787059a81a746c6ffcae1b8f42
[yosys.git] / kernel / log.cc
1 /*
2 * yosys -- Yosys Open SYnthesis Suite
3 *
4 * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
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/yosys.h"
21 #include "libs/sha1/sha1.h"
22 #include "backends/ilang/ilang_backend.h"
23
24 #if !defined(_WIN32) || defined(__MINGW32__)
25 # include <sys/time.h>
26 #endif
27
28 #if defined(__linux__) || defined(__FreeBSD__)
29 # include <dlfcn.h>
30 #endif
31
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <stdarg.h>
36 #include <vector>
37 #include <list>
38
39 YOSYS_NAMESPACE_BEGIN
40
41 std::vector<FILE*> log_files;
42 std::vector<std::ostream*> log_streams;
43 std::map<std::string, std::set<std::string>> log_hdump;
44 std::vector<std::regex> log_warn_regexes, log_nowarn_regexes, log_werror_regexes;
45 std::vector<std::pair<std::regex,LogExpectedItem>> log_expect_log, log_expect_warning, log_expect_error;
46 std::set<std::string> log_warnings, log_experimentals, log_experimentals_ignored;
47 int log_warnings_count = 0;
48 bool log_hdump_all = false;
49 FILE *log_errfile = NULL;
50 SHA1 *log_hasher = NULL;
51
52 bool log_time = false;
53 bool log_error_stderr = false;
54 bool log_cmd_error_throw = false;
55 bool log_quiet_warnings = false;
56 int log_verbose_level;
57 string log_last_error;
58 void (*log_error_atexit)() = NULL;
59
60 int log_make_debug = 0;
61 int log_force_debug = 0;
62 int log_debug_suppressed = 0;
63
64 vector<int> header_count;
65 vector<char*> log_id_cache;
66 vector<shared_str> string_buf;
67 int string_buf_index = -1;
68
69 static struct timeval initial_tv = { 0, 0 };
70 static bool next_print_log = false;
71 static int log_newline_count = 0;
72 static bool check_expected_logs = true;
73
74 static void log_id_cache_clear()
75 {
76 for (auto p : log_id_cache)
77 free(p);
78 log_id_cache.clear();
79 }
80
81 #if defined(_WIN32) && !defined(__MINGW32__)
82 // this will get time information and return it in timeval, simulating gettimeofday()
83 int gettimeofday(struct timeval *tv, struct timezone *tz)
84 {
85 LARGE_INTEGER counter;
86 LARGE_INTEGER freq;
87
88 QueryPerformanceFrequency(&freq);
89 QueryPerformanceCounter(&counter);
90
91 counter.QuadPart *= 1000000;
92 counter.QuadPart /= freq.QuadPart;
93
94 tv->tv_sec = long(counter.QuadPart / 1000000);
95 tv->tv_usec = counter.QuadPart % 1000000;
96
97 return 0;
98 }
99 #endif
100
101 void logv(const char *format, va_list ap)
102 {
103 while (format[0] == '\n' && format[1] != 0) {
104 log("\n");
105 format++;
106 }
107
108 if (log_make_debug && !ys_debug(1))
109 return;
110
111 std::string str = vstringf(format, ap);
112
113 if (str.empty())
114 return;
115
116 size_t nnl_pos = str.find_last_not_of('\n');
117 if (nnl_pos == std::string::npos)
118 log_newline_count += GetSize(str);
119 else
120 log_newline_count = GetSize(str) - nnl_pos - 1;
121
122 if (log_hasher)
123 log_hasher->update(str);
124
125 if (log_time)
126 {
127 std::string time_str;
128
129 if (next_print_log || initial_tv.tv_sec == 0) {
130 next_print_log = false;
131 struct timeval tv;
132 gettimeofday(&tv, NULL);
133 if (initial_tv.tv_sec == 0)
134 initial_tv = tv;
135 if (tv.tv_usec < initial_tv.tv_usec) {
136 tv.tv_sec--;
137 tv.tv_usec += 1000000;
138 }
139 tv.tv_sec -= initial_tv.tv_sec;
140 tv.tv_usec -= initial_tv.tv_usec;
141 time_str += stringf("[%05d.%06d] ", int(tv.tv_sec), int(tv.tv_usec));
142 }
143
144 if (format[0] && format[strlen(format)-1] == '\n')
145 next_print_log = true;
146
147 for (auto f : log_files)
148 fputs(time_str.c_str(), f);
149
150 for (auto f : log_streams)
151 *f << time_str;
152 }
153
154 for (auto f : log_files)
155 fputs(str.c_str(), f);
156
157 for (auto f : log_streams)
158 *f << str;
159
160 static std::string linebuffer;
161 static bool log_warn_regex_recusion_guard = false;
162
163 if (!log_warn_regex_recusion_guard)
164 {
165 log_warn_regex_recusion_guard = true;
166
167 if (log_warn_regexes.empty() && log_expect_log.empty())
168 {
169 linebuffer.clear();
170 }
171 else
172 {
173 linebuffer += str;
174
175 if (!linebuffer.empty() && linebuffer.back() == '\n') {
176 for (auto &re : log_warn_regexes)
177 if (std::regex_search(linebuffer, re))
178 log_warning("Found log message matching -W regex:\n%s", str.c_str());
179
180 for (auto &item : log_expect_log)
181 if (std::regex_search(linebuffer, item.first))
182 item.second.current_count++;
183
184 linebuffer.clear();
185 }
186 }
187
188 log_warn_regex_recusion_guard = false;
189 }
190 }
191
192 void logv_header(RTLIL::Design *design, const char *format, va_list ap)
193 {
194 bool pop_errfile = false;
195
196 log_spacer();
197 if (header_count.size() > 0)
198 header_count.back()++;
199
200 if (int(header_count.size()) <= log_verbose_level && log_errfile != NULL) {
201 log_files.push_back(log_errfile);
202 pop_errfile = true;
203 }
204
205 std::string header_id;
206
207 for (int c : header_count)
208 header_id += stringf("%s%d", header_id.empty() ? "" : ".", c);
209
210 log("%s. ", header_id.c_str());
211 logv(format, ap);
212 log_flush();
213
214 if (log_hdump_all)
215 log_hdump[header_id].insert("yosys_dump_" + header_id + ".il");
216
217 if (log_hdump.count(header_id) && design != nullptr)
218 for (auto &filename : log_hdump.at(header_id)) {
219 log("Dumping current design to '%s'.\n", filename.c_str());
220 if (yosys_xtrace)
221 IdString::xtrace_db_dump();
222 Pass::call(design, {"dump", "-o", filename});
223 if (yosys_xtrace)
224 log("#X# -- end of dump --\n");
225 }
226
227 if (pop_errfile)
228 log_files.pop_back();
229 }
230
231 static void logv_warning_with_prefix(const char *prefix,
232 const char *format, va_list ap)
233 {
234 std::string message = vstringf(format, ap);
235 bool suppressed = false;
236
237 for (auto &re : log_nowarn_regexes)
238 if (std::regex_search(message, re))
239 suppressed = true;
240
241 if (suppressed)
242 {
243 log("Suppressed %s%s", prefix, message.c_str());
244 }
245 else
246 {
247 int bak_log_make_debug = log_make_debug;
248 log_make_debug = 0;
249
250 for (auto &re : log_werror_regexes)
251 if (std::regex_search(message, re))
252 log_error("%s", message.c_str());
253
254 for (auto &item : log_expect_warning)
255 if (std::regex_search(message, item.first))
256 item.second.current_count++;
257
258 if (log_warnings.count(message))
259 {
260 log("%s%s", prefix, message.c_str());
261 log_flush();
262 }
263 else
264 {
265 if (log_errfile != NULL && !log_quiet_warnings)
266 log_files.push_back(log_errfile);
267
268 log("%s%s", prefix, message.c_str());
269 log_flush();
270
271 if (log_errfile != NULL && !log_quiet_warnings)
272 log_files.pop_back();
273
274 log_warnings.insert(message);
275 }
276
277 log_warnings_count++;
278 log_make_debug = bak_log_make_debug;
279 }
280 }
281
282 void logv_warning(const char *format, va_list ap)
283 {
284 logv_warning_with_prefix("Warning: ", format, ap);
285 }
286
287 void logv_warning_noprefix(const char *format, va_list ap)
288 {
289 logv_warning_with_prefix("", format, ap);
290 }
291
292 void log_file_warning(const std::string &filename, int lineno,
293 const char *format, ...)
294 {
295 va_list ap;
296 va_start(ap, format);
297 std::string prefix = stringf("%s:%d: Warning: ",
298 filename.c_str(), lineno);
299 logv_warning_with_prefix(prefix.c_str(), format, ap);
300 va_end(ap);
301 }
302
303 void log_file_info(const std::string &filename, int lineno,
304 const char *format, ...)
305 {
306 va_list ap;
307 va_start(ap, format);
308 std::string fmt = stringf("%s:%d: Info: %s",
309 filename.c_str(), lineno, format);
310 logv(fmt.c_str(), ap);
311 va_end(ap);
312 }
313
314 YS_ATTRIBUTE(noreturn)
315 static void logv_error_with_prefix(const char *prefix,
316 const char *format, va_list ap)
317 {
318 #ifdef EMSCRIPTEN
319 auto backup_log_files = log_files;
320 #endif
321 int bak_log_make_debug = log_make_debug;
322 log_make_debug = 0;
323 log_suppressed();
324
325 if (log_errfile != NULL)
326 log_files.push_back(log_errfile);
327
328 if (log_error_stderr)
329 for (auto &f : log_files)
330 if (f == stdout)
331 f = stderr;
332
333 log_last_error = vstringf(format, ap);
334 if (check_expected_logs)
335 log("%s%s", prefix, log_last_error.c_str());
336 log_flush();
337
338 log_make_debug = bak_log_make_debug;
339
340 if (log_error_atexit)
341 log_error_atexit();
342
343 for (auto &item : log_expect_error)
344 if (std::regex_search(log_last_error, item.first))
345 item.second.current_count++;
346
347 if (check_expected_logs)
348 log_check_expected();
349 #ifdef EMSCRIPTEN
350 log_files = backup_log_files;
351 throw 0;
352 #elif defined(_MSC_VER)
353 _exit(1);
354 #else
355 _Exit(1);
356 #endif
357 }
358
359 void logv_error(const char *format, va_list ap)
360 {
361 logv_error_with_prefix("ERROR: ", format, ap);
362 }
363
364 void log_file_error(const string &filename, int lineno,
365 const char *format, ...)
366 {
367 va_list ap;
368 va_start(ap, format);
369 std::string prefix = stringf("%s:%d: ERROR: ",
370 filename.c_str(), lineno);
371 logv_error_with_prefix(prefix.c_str(), format, ap);
372 }
373
374 void log(const char *format, ...)
375 {
376 va_list ap;
377 va_start(ap, format);
378 logv(format, ap);
379 va_end(ap);
380 }
381
382 void log_header(RTLIL::Design *design, const char *format, ...)
383 {
384 va_list ap;
385 va_start(ap, format);
386 logv_header(design, format, ap);
387 va_end(ap);
388 }
389
390 void log_warning(const char *format, ...)
391 {
392 va_list ap;
393 va_start(ap, format);
394 logv_warning(format, ap);
395 va_end(ap);
396 }
397
398 void log_experimental(const char *format, ...)
399 {
400 va_list ap;
401 va_start(ap, format);
402 string s = vstringf(format, ap);
403 va_end(ap);
404
405 if (log_experimentals_ignored.count(s) == 0 && log_experimentals.count(s) == 0) {
406 log_warning("Feature '%s' is experimental.\n", s.c_str());
407 log_experimentals.insert(s);
408 }
409 }
410
411 void log_warning_noprefix(const char *format, ...)
412 {
413 va_list ap;
414 va_start(ap, format);
415 logv_warning_noprefix(format, ap);
416 va_end(ap);
417 }
418
419 void log_error(const char *format, ...)
420 {
421 va_list ap;
422 va_start(ap, format);
423 logv_error(format, ap);
424 }
425
426 void log_cmd_error(const char *format, ...)
427 {
428 va_list ap;
429 va_start(ap, format);
430
431 if (log_cmd_error_throw) {
432 log_last_error = vstringf(format, ap);
433 log("ERROR: %s", log_last_error.c_str());
434 log_flush();
435 throw log_cmd_error_exception();
436 }
437
438 logv_error(format, ap);
439 }
440
441 void log_spacer()
442 {
443 if (log_newline_count < 2) log("\n");
444 if (log_newline_count < 2) log("\n");
445 }
446
447 void log_push()
448 {
449 header_count.push_back(0);
450 }
451
452 void log_pop()
453 {
454 header_count.pop_back();
455 log_id_cache_clear();
456 string_buf.clear();
457 string_buf_index = -1;
458 log_flush();
459 }
460
461 #if (defined(__linux__) || defined(__FreeBSD__)) && defined(YOSYS_ENABLE_PLUGINS)
462 void log_backtrace(const char *prefix, int levels)
463 {
464 if (levels <= 0) return;
465
466 Dl_info dli;
467 void *p;
468
469 if ((p = __builtin_extract_return_addr(__builtin_return_address(0))) && dladdr(p, &dli)) {
470 log("%sframe #1: %p %s(%p) %s(%p)\n", prefix, p, dli.dli_fname, dli.dli_fbase, dli.dli_sname, dli.dli_saddr);
471 } else {
472 log("%sframe #1: ---\n", prefix);
473 return;
474 }
475
476 if (levels <= 1) return;
477
478 #ifndef DEBUG
479 log("%sframe #2: [build Yosys with ENABLE_DEBUG for deeper backtraces]\n", prefix);
480 #else
481 if ((p = __builtin_extract_return_addr(__builtin_return_address(1))) && dladdr(p, &dli)) {
482 log("%sframe #2: %p %s(%p) %s(%p)\n", prefix, p, dli.dli_fname, dli.dli_fbase, dli.dli_sname, dli.dli_saddr);
483 } else {
484 log("%sframe #2: ---\n", prefix);
485 return;
486 }
487
488 if (levels <= 2) return;
489
490 if ((p = __builtin_extract_return_addr(__builtin_return_address(2))) && dladdr(p, &dli)) {
491 log("%sframe #3: %p %s(%p) %s(%p)\n", prefix, p, dli.dli_fname, dli.dli_fbase, dli.dli_sname, dli.dli_saddr);
492 } else {
493 log("%sframe #3: ---\n", prefix);
494 return;
495 }
496
497 if (levels <= 3) return;
498
499 if ((p = __builtin_extract_return_addr(__builtin_return_address(3))) && dladdr(p, &dli)) {
500 log("%sframe #4: %p %s(%p) %s(%p)\n", prefix, p, dli.dli_fname, dli.dli_fbase, dli.dli_sname, dli.dli_saddr);
501 } else {
502 log("%sframe #4: ---\n", prefix);
503 return;
504 }
505
506 if (levels <= 4) return;
507
508 if ((p = __builtin_extract_return_addr(__builtin_return_address(4))) && dladdr(p, &dli)) {
509 log("%sframe #5: %p %s(%p) %s(%p)\n", prefix, p, dli.dli_fname, dli.dli_fbase, dli.dli_sname, dli.dli_saddr);
510 } else {
511 log("%sframe #5: ---\n", prefix);
512 return;
513 }
514
515 if (levels <= 5) return;
516
517 if ((p = __builtin_extract_return_addr(__builtin_return_address(5))) && dladdr(p, &dli)) {
518 log("%sframe #6: %p %s(%p) %s(%p)\n", prefix, p, dli.dli_fname, dli.dli_fbase, dli.dli_sname, dli.dli_saddr);
519 } else {
520 log("%sframe #6: ---\n", prefix);
521 return;
522 }
523
524 if (levels <= 6) return;
525
526 if ((p = __builtin_extract_return_addr(__builtin_return_address(6))) && dladdr(p, &dli)) {
527 log("%sframe #7: %p %s(%p) %s(%p)\n", prefix, p, dli.dli_fname, dli.dli_fbase, dli.dli_sname, dli.dli_saddr);
528 } else {
529 log("%sframe #7: ---\n", prefix);
530 return;
531 }
532
533 if (levels <= 7) return;
534
535 if ((p = __builtin_extract_return_addr(__builtin_return_address(7))) && dladdr(p, &dli)) {
536 log("%sframe #8: %p %s(%p) %s(%p)\n", prefix, p, dli.dli_fname, dli.dli_fbase, dli.dli_sname, dli.dli_saddr);
537 } else {
538 log("%sframe #8: ---\n", prefix);
539 return;
540 }
541
542 if (levels <= 8) return;
543
544 if ((p = __builtin_extract_return_addr(__builtin_return_address(8))) && dladdr(p, &dli)) {
545 log("%sframe #9: %p %s(%p) %s(%p)\n", prefix, p, dli.dli_fname, dli.dli_fbase, dli.dli_sname, dli.dli_saddr);
546 } else {
547 log("%sframe #9: ---\n", prefix);
548 return;
549 }
550
551 if (levels <= 9) return;
552 #endif
553 }
554 #else
555 void log_backtrace(const char*, int) { }
556 #endif
557
558 void log_reset_stack()
559 {
560 while (header_count.size() > 1)
561 header_count.pop_back();
562 log_id_cache_clear();
563 string_buf.clear();
564 string_buf_index = -1;
565 log_flush();
566 }
567
568 void log_flush()
569 {
570 for (auto f : log_files)
571 fflush(f);
572
573 for (auto f : log_streams)
574 f->flush();
575 }
576
577 void log_dump_val_worker(RTLIL::IdString v) {
578 log("%s", log_id(v));
579 }
580
581 void log_dump_val_worker(RTLIL::SigSpec v) {
582 log("%s", log_signal(v));
583 }
584
585 void log_dump_val_worker(RTLIL::State v) {
586 log("%s", log_signal(v));
587 }
588
589 const char *log_signal(const RTLIL::SigSpec &sig, bool autoint)
590 {
591 std::stringstream buf;
592 ILANG_BACKEND::dump_sigspec(buf, sig, autoint);
593
594 if (string_buf.size() < 100) {
595 string_buf.push_back(buf.str());
596 return string_buf.back().c_str();
597 } else {
598 if (++string_buf_index == 100)
599 string_buf_index = 0;
600 string_buf[string_buf_index] = buf.str();
601 return string_buf[string_buf_index].c_str();
602 }
603 }
604
605 const char *log_const(const RTLIL::Const &value, bool autoint)
606 {
607 if ((value.flags & RTLIL::CONST_FLAG_STRING) == 0)
608 return log_signal(value, autoint);
609
610 std::string str = "\"" + value.decode_string() + "\"";
611
612 if (string_buf.size() < 100) {
613 string_buf.push_back(str);
614 return string_buf.back().c_str();
615 } else {
616 if (++string_buf_index == 100)
617 string_buf_index = 0;
618 string_buf[string_buf_index] = str;
619 return string_buf[string_buf_index].c_str();
620 }
621 }
622
623 const char *log_id(RTLIL::IdString str)
624 {
625 log_id_cache.push_back(strdup(str.c_str()));
626 const char *p = log_id_cache.back();
627 if (p[0] != '\\')
628 return p;
629 if (p[1] == '$' || p[1] == '\\' || p[1] == 0)
630 return p;
631 if (p[1] >= '0' && p[1] <= '9')
632 return p;
633 return p+1;
634 }
635
636 void log_module(RTLIL::Module *module, std::string indent)
637 {
638 std::stringstream buf;
639 ILANG_BACKEND::dump_module(buf, indent, module, module->design, false);
640 log("%s", buf.str().c_str());
641 }
642
643 void log_cell(RTLIL::Cell *cell, std::string indent)
644 {
645 std::stringstream buf;
646 ILANG_BACKEND::dump_cell(buf, indent, cell);
647 log("%s", buf.str().c_str());
648 }
649
650 void log_wire(RTLIL::Wire *wire, std::string indent)
651 {
652 std::stringstream buf;
653 ILANG_BACKEND::dump_wire(buf, indent, wire);
654 log("%s", buf.str().c_str());
655 }
656
657 void log_check_expected()
658 {
659 check_expected_logs = false;
660
661 for (auto &item : log_expect_warning) {
662 if (item.second.current_count != item.second.expected_count) {
663 log_error("Expected warning pattern '%s' not found !\n", item.second.pattern.c_str());
664 }
665 if (item.second.current_count != item.second.expected_count) {
666 log_error("Expected warning pattern '%s' found %d time(s), instead of %d time(s) !\n",
667 item.second.pattern.c_str(), item.second.current_count, item.second.expected_count);
668 }
669 }
670
671 for (auto &item : log_expect_log) {
672 if (item.second.current_count == 0) {
673 log_error("Expected log pattern '%s' not found !\n", item.second.pattern.c_str());
674 }
675 if (item.second.current_count != item.second.expected_count) {
676 log_error("Expected log pattern '%s' found %d time(s), instead of %d time(s) !\n",
677 item.second.pattern.c_str(), item.second.current_count, item.second.expected_count);
678 }
679 }
680
681 for (auto &item : log_expect_error)
682 if (item.second.current_count == item.second.expected_count) {
683 log("Expected error pattern '%s' found !!!\n", item.second.pattern.c_str());
684 #ifdef EMSCRIPTEN
685 log_files = backup_log_files;
686 throw 0;
687 #elif defined(_MSC_VER)
688 _exit(0);
689 #else
690 _Exit(0);
691 #endif
692 } else {
693 log_error("Expected error pattern '%s' not found !\n", item.second.pattern.c_str());
694 }
695 }
696
697 // ---------------------------------------------------
698 // This is the magic behind the code coverage counters
699 // ---------------------------------------------------
700 #if defined(YOSYS_ENABLE_COVER) && (defined(__linux__) || defined(__FreeBSD__))
701
702 dict<std::string, std::pair<std::string, int>> extra_coverage_data;
703
704 void cover_extra(std::string parent, std::string id, bool increment) {
705 if (extra_coverage_data.count(id) == 0) {
706 for (CoverData *p = __start_yosys_cover_list; p != __stop_yosys_cover_list; p++)
707 if (p->id == parent)
708 extra_coverage_data[id].first = stringf("%s:%d:%s", p->file, p->line, p->func);
709 log_assert(extra_coverage_data.count(id));
710 }
711 if (increment)
712 extra_coverage_data[id].second++;
713 }
714
715 dict<std::string, std::pair<std::string, int>> get_coverage_data()
716 {
717 dict<std::string, std::pair<std::string, int>> coverage_data;
718
719 for (auto &it : pass_register) {
720 std::string key = stringf("passes.%s", it.first.c_str());
721 coverage_data[key].first = stringf("%s:%d:%s", __FILE__, __LINE__, __FUNCTION__);
722 coverage_data[key].second += it.second->call_counter;
723 }
724
725 for (auto &it : extra_coverage_data) {
726 if (coverage_data.count(it.first))
727 log_warning("found duplicate coverage id \"%s\".\n", it.first.c_str());
728 coverage_data[it.first].first = it.second.first;
729 coverage_data[it.first].second += it.second.second;
730 }
731
732 for (CoverData *p = __start_yosys_cover_list; p != __stop_yosys_cover_list; p++) {
733 if (coverage_data.count(p->id))
734 log_warning("found duplicate coverage id \"%s\".\n", p->id);
735 coverage_data[p->id].first = stringf("%s:%d:%s", p->file, p->line, p->func);
736 coverage_data[p->id].second += p->counter;
737 }
738
739 for (auto &it : coverage_data)
740 if (!it.second.first.compare(0, strlen(YOSYS_SRC "/"), YOSYS_SRC "/"))
741 it.second.first = it.second.first.substr(strlen(YOSYS_SRC "/"));
742
743 return coverage_data;
744 }
745
746 #endif
747
748 YOSYS_NAMESPACE_END