Fix for assertion error when expanding macro.
[iverilog.git] / vpi / sys_lxt.c
blob91fe77d923c35cf0d999d56e918f7e62a1c51968
1 /*
2 * Copyright (c) 2002 Stephen Williams (steve@icarus.com)
4 * This source code is free software; you can redistribute it
5 * and/or modify it in source code form under the terms of the GNU
6 * General Public License as published by the Free Software
7 * Foundation; either version 2 of the License, or (at your option)
8 * any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19 #ifdef HAVE_CVS_IDENT
20 #ident "$Id: sys_lxt.c,v 1.28 2007/03/14 04:05:51 steve Exp $"
21 #endif
23 # include "sys_priv.h"
24 # include "lxt_write.h"
25 # include "vcd_priv.h"
26 # include "sys_priv.h"
29 * This file contains the implementations of the LXT related
30 * funcitons.
33 # include "vpi_user.h"
34 # include <stdio.h>
35 # include <stdlib.h>
36 # include <string.h>
37 # include <assert.h>
38 # include <time.h>
39 #ifdef HAVE_MALLOC_H
40 # include <malloc.h>
41 #endif
42 # include "stringheap.h"
45 static enum lxm_optimum_mode_e {
46 LXM_NONE = 0,
47 LXM_SPACE = 1,
48 LXM_SPEED = 2
49 } lxm_optimum_mode = LXM_SPEED;
53 * The lxt_scope head and current pointers are used to keep a scope
54 * stack that can be accessed from the bottom. The lxt_scope_head
55 * points to the first (bottom) item in the stack and
56 * lxt_scope_current points to the last (top) item in the stack. The
57 * push_scope and pop_scope methods manipulate the stack.
59 struct lxt_scope
61 struct lxt_scope *next, *prev;
62 char *name;
63 int len;
66 static struct lxt_scope *lxt_scope_head=NULL, *lxt_scope_current=NULL;
68 static void push_scope(const char *name)
70 struct lxt_scope *t = (struct lxt_scope *)
71 calloc(1, sizeof(struct lxt_scope));
73 t->name = strdup(name);
74 t->len = strlen(name);
76 if(!lxt_scope_head) {
77 lxt_scope_head = lxt_scope_current = t;
78 } else {
79 lxt_scope_current->next = t;
80 t->prev = lxt_scope_current;
81 lxt_scope_current = t;
85 static void pop_scope(void)
87 struct lxt_scope *t;
89 assert(lxt_scope_current);
91 t=lxt_scope_current->prev;
92 free(lxt_scope_current->name);
93 free(lxt_scope_current);
94 lxt_scope_current = t;
95 if (lxt_scope_current) {
96 lxt_scope_current->next = 0;
97 } else {
98 lxt_scope_head = 0;
103 * This function uses the scope stack to generate a hierarchical
104 * name. Scan the scope stack from the bottom up to construct the
105 * name.
107 static char *create_full_name(const char *name)
109 char *n, *n2;
110 int len = 0;
111 int is_esc_id = is_escaped_id(name);
112 struct lxt_scope *t = lxt_scope_head;
114 /* Figure out how long the combined string will be. */
115 while(t) {
116 len+=t->len+1;
117 t=t->next;
120 len += strlen(name) + 1;
121 if (is_esc_id) len += 1;
123 /* Allocate a string buffer. */
124 n = n2 = malloc(len);
126 t = lxt_scope_head;
127 while(t) {
128 strcpy(n2, t->name);
129 n2 += t->len;
130 *n2 = '.';
131 n2++;
132 t=t->next;
135 if (is_esc_id) {
136 *n2 = '\\';
137 n2++;
139 strcpy(n2, name);
140 n2 += strlen(n2);
141 assert( (n2 - n + 1) == len );
143 return n;
147 static struct lt_trace *dump_file = 0;
149 struct vcd_info {
150 vpiHandle item;
151 vpiHandle cb;
152 struct t_vpi_time time;
153 struct lt_symbol *sym;
154 struct vcd_info *next;
155 struct vcd_info *dmp_next;
156 int scheduled;
160 static struct vcd_info*vcd_list = 0;
161 static struct vcd_info*vcd_dmp_list = 0;
162 static PLI_UINT64 vcd_cur_time = 0;
163 static int dump_is_off = 0;
164 static long dump_limit = 0;
165 static int dump_is_full = 0;
166 static int finish_status = 0;
169 static void show_this_item(struct vcd_info*info)
171 s_vpi_value value;
173 if (vpi_get(vpiType,info->item) == vpiRealVar) {
174 value.format = vpiRealVal;
175 vpi_get_value(info->item, &value);
176 lt_emit_value_double(dump_file, info->sym, 0, value.value.real);
178 } else {
179 value.format = vpiBinStrVal;
180 vpi_get_value(info->item, &value);
181 lt_emit_value_bit_string(dump_file, info->sym,
182 0 /* array row */,
183 value.value.str);
188 static void show_this_item_x(struct vcd_info*info)
190 if (vpi_get(vpiType,info->item) == vpiRealVar) {
191 /* Should write a NaN here? */
192 } else {
193 lt_emit_value_bit_string(dump_file, info->sym, 0, "x");
199 * managed qsorted list of scope names for duplicates bsearching
202 struct vcd_names_list_s lxt_tab;
205 static int dumpvars_status = 0; /* 0:fresh 1:cb installed, 2:callback done */
206 static PLI_UINT64 dumpvars_time;
207 inline static int dump_header_pending(void)
209 return dumpvars_status != 2;
213 * This function writes out all the traced variables, whether they
214 * changed or not.
216 static void vcd_checkpoint()
218 struct vcd_info*cur;
220 for (cur = vcd_list ; cur ; cur = cur->next)
221 show_this_item(cur);
224 static void vcd_checkpoint_x()
226 struct vcd_info*cur;
228 for (cur = vcd_list ; cur ; cur = cur->next)
229 show_this_item_x(cur);
232 static PLI_INT32 variable_cb_2(p_cb_data cause)
234 struct vcd_info* info = vcd_dmp_list;
235 PLI_UINT64 now = timerec_to_time64(cause->time);
237 if (now != vcd_cur_time) {
238 lt_set_time64(dump_file, now);
239 vcd_cur_time = now;
242 do {
243 show_this_item(info);
244 info->scheduled = 0;
245 } while ((info = info->dmp_next) != 0);
247 vcd_dmp_list = 0;
249 return 0;
252 static PLI_INT32 variable_cb_1(p_cb_data cause)
254 struct t_cb_data cb;
255 struct vcd_info*info = (struct vcd_info*)cause->user_data;
257 if (dump_is_full) return 0;
258 if (dump_is_off) return 0;
259 if (dump_header_pending()) return 0;
260 if (info->scheduled) return 0;
262 if ((dump_limit > 0) && (ftell(dump_file->handle) > dump_limit)) {
263 dump_is_full = 1;
264 vpi_printf("WARNING: Dump file limit (%ld bytes) "
265 "exceeded.\n", dump_limit);
266 return 0;
269 if (!vcd_dmp_list) {
270 cb = *cause;
271 cb.reason = cbReadOnlySynch;
272 cb.cb_rtn = variable_cb_2;
273 vpi_register_cb(&cb);
276 info->scheduled = 1;
277 info->dmp_next = vcd_dmp_list;
278 vcd_dmp_list = info;
280 return 0;
283 static PLI_INT32 dumpvars_cb(p_cb_data cause)
285 if (dumpvars_status != 1)
286 return 0;
288 dumpvars_status = 2;
290 dumpvars_time = timerec_to_time64(cause->time);
291 vcd_cur_time = dumpvars_time;
293 if (!dump_is_off) {
294 lt_set_time64(dump_file, dumpvars_time);
295 vcd_checkpoint();
298 return 0;
301 static PLI_INT32 finish_cb(p_cb_data cause)
303 if (finish_status != 0)
304 return 0;
306 finish_status = 1;
308 dumpvars_time = timerec_to_time64(cause->time);
310 if (!dump_is_off && !dump_is_full && dumpvars_time != vcd_cur_time) {
311 lt_set_time64(dump_file, dumpvars_time);
314 return 0;
317 inline static int install_dumpvars_callback(void)
319 struct t_cb_data cb;
320 static struct t_vpi_time time;
322 if (dumpvars_status == 1)
323 return 0;
325 if (dumpvars_status == 2) {
326 vpi_mcd_printf(1, "LXT warning:"
327 " $dumpvars ignored,"
328 " previously called at simtime %lu\n",
329 dumpvars_time);
330 return 1;
333 time.type = vpiSimTime;
334 cb.time = &time;
335 cb.reason = cbReadOnlySynch;
336 cb.cb_rtn = dumpvars_cb;
337 cb.user_data = 0x0;
338 cb.obj = 0x0;
340 vpi_register_cb(&cb);
342 cb.reason = cbEndOfSimulation;
343 cb.cb_rtn = finish_cb;
345 vpi_register_cb(&cb);
347 dumpvars_status = 1;
348 return 0;
351 static PLI_INT32 sys_dumpoff_calltf(PLI_BYTE8*name)
353 s_vpi_time now;
354 PLI_UINT64 now64;
356 if (dump_is_off)
357 return 0;
359 dump_is_off = 1;
361 if (dump_file == 0)
362 return 0;
364 if (dump_header_pending())
365 return 0;
367 now.type = vpiSimTime;
368 vpi_get_time(0, &now);
369 now64 = timerec_to_time64(&now);
371 if (now64 > vcd_cur_time)
372 lt_set_time64(dump_file, now64);
373 vcd_cur_time = now64;
375 lt_set_dumpoff(dump_file);
376 vcd_checkpoint_x();
378 return 0;
381 static PLI_INT32 sys_dumpon_calltf(PLI_BYTE8*name)
383 s_vpi_time now;
384 PLI_UINT64 now64;
386 if (!dump_is_off)
387 return 0;
389 dump_is_off = 0;
391 if (dump_file == 0)
392 return 0;
394 if (dump_header_pending())
395 return 0;
397 now.type = vpiSimTime;
398 vpi_get_time(0, &now);
399 now64 = timerec_to_time64(&now);
401 if (now64 > vcd_cur_time)
402 lt_set_time64(dump_file, now64);
403 vcd_cur_time = now64;
405 lt_set_dumpon(dump_file);
406 vcd_checkpoint();
408 return 0;
411 static PLI_INT32 sys_dumpall_calltf(PLI_BYTE8*name)
413 s_vpi_time now;
414 PLI_UINT64 now64;
416 if (dump_file == 0)
417 return 0;
419 if (dump_header_pending())
420 return 0;
422 now.type = vpiSimTime;
423 vpi_get_time(0, &now);
424 now64 = timerec_to_time64(&now);
426 if (now64> vcd_cur_time)
427 lt_set_time64(dump_file, now64);
428 vcd_cur_time = now64;
430 vcd_checkpoint();
432 return 0;
435 static void *close_dumpfile(void)
437 lt_close(dump_file);
438 return(dump_file = NULL);
441 static void open_dumpfile(const char*path)
443 dump_file = lt_init(path);
445 if (dump_file == 0) {
446 vpi_mcd_printf(1,
447 "LXT Error: Unable to open %s for output.\n",
448 path);
449 return;
450 } else {
451 int prec = vpi_get(vpiTimePrecision, 0);
453 vpi_mcd_printf(1,
454 "LXT info: dumpfile %s opened for output.\n",
455 path);
457 assert(prec >= -15);
458 lt_set_timescale(dump_file, prec);
460 lt_set_initial_value(dump_file, 'x');
461 lt_set_clock_compress(dump_file);
463 atexit((void(*)(void))close_dumpfile);
467 static PLI_INT32 sys_dumpfile_calltf(PLI_BYTE8*name)
469 char*path;
471 vpiHandle sys = vpi_handle(vpiSysTfCall, 0);
472 vpiHandle argv = vpi_iterate(vpiArgument, sys);
473 vpiHandle item;
475 if (argv && (item = vpi_scan(argv))) {
476 s_vpi_value value;
478 if (vpi_get(vpiType, item) != vpiConstant
479 || vpi_get(vpiConstType, item) != vpiStringConst) {
480 vpi_mcd_printf(1,
481 "LXT Error:"
482 " %s parameter must be a string constant\n",
483 name);
484 return 0;
487 value.format = vpiStringVal;
488 vpi_get_value(item, &value);
489 path = strdup(value.value.str);
491 vpi_free_object(argv);
493 } else {
494 path = strdup("dump.lxt");
497 if (dump_file)
498 close_dumpfile();
500 assert(dump_file == 0);
501 open_dumpfile(path);
503 free(path);
505 return 0;
509 * The LXT1 format has no concept of file flushing.
511 static PLI_INT32 sys_dumpflush_calltf(PLI_BYTE8*name)
513 return 0;
516 static PLI_INT32 sys_dumplimit_compiletf(PLI_BYTE8 *name)
518 vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
519 vpiHandle argv = vpi_iterate(vpiArgument, callh);
520 vpiHandle limit;
522 /* Check that there is a argument and get it. */
523 if (argv == 0) {
524 vpi_printf("ERROR: %s requires an argument.\n", name);
525 vpi_control(vpiFinish, 1);
526 return 0;
528 limit = vpi_scan(argv);
530 /* Check that we are not given a string. */
531 switch (vpi_get(vpiType, limit)) {
532 case vpiConstant:
533 case vpiParameter:
534 if (vpi_get(vpiConstType, limit) == vpiStringConst) {
535 vpi_printf("ERROR: %s's argument must be a number.\n", name);
539 /* Check that there is only a single argument. */
540 limit = vpi_scan(argv);
541 if (limit != 0) {
542 vpi_printf("ERROR: %s takes a single argument.\n", name);
543 vpi_control(vpiFinish, 1);
544 return 0;
547 return 0;
550 static PLI_INT32 sys_dumplimit_calltf(PLI_BYTE8 *name)
552 vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
553 vpiHandle argv = vpi_iterate(vpiArgument, callh);
554 vpiHandle limit = vpi_scan(argv);
555 s_vpi_value val;
557 /* Get the value and set the dump limit. */
558 val.format = vpiIntVal;
559 vpi_get_value(limit, &val);
560 dump_limit = val.value.integer;
562 vpi_free_object(argv);
563 return 0;
566 static void scan_item(unsigned depth, vpiHandle item, int skip)
568 struct t_cb_data cb;
569 struct vcd_info* info;
571 const char* type;
572 const char* name;
573 const char* ident;
574 int nexus_id;
576 /* list of types to iterate upon */
577 int i;
578 static int types[] = {
579 /* Value */
580 vpiNet,
581 vpiReg,
582 vpiVariables,
583 /* Scope */
584 vpiFunction,
585 vpiModule,
586 vpiNamedBegin,
587 vpiNamedFork,
588 vpiTask,
592 switch (vpi_get(vpiType, item)) {
594 case vpiMemory:
595 /* don't know how to watch memories. */
596 break;
598 case vpiNamedEvent:
599 /* There is nothing in named events to dump. */
600 break;
602 case vpiNet: type = "wire"; if(0){
603 case vpiIntegerVar:
604 case vpiTimeVar:
605 case vpiReg: type = "reg"; }
607 if (skip)
608 break;
610 name = vpi_get_str(vpiName, item);
611 nexus_id = vpi_get(_vpiNexusId, item);
612 if (nexus_id) {
613 ident = find_nexus_ident(nexus_id);
614 } else {
615 ident = 0;
618 if (!ident) {
619 char*tmp = create_full_name(name);
620 ident = strdup_sh(&name_heap, tmp);
621 free(tmp);
623 if (nexus_id)
624 set_nexus_ident(nexus_id, ident);
626 info = malloc(sizeof(*info));
628 info->time.type = vpiSimTime;
629 info->item = item;
630 info->sym = lt_symbol_add(dump_file, ident, 0 /* array rows */, vpi_get(vpiLeftRange, item), vpi_get(vpiRightRange, item), LT_SYM_F_BITS);
631 info->scheduled = 0;
633 cb.time = &info->time;
634 cb.user_data = (char*)info;
635 cb.value = NULL;
636 cb.obj = item;
637 cb.reason = cbValueChange;
638 cb.cb_rtn = variable_cb_1;
640 info->next = vcd_list;
641 vcd_list = info;
643 info->cb = vpi_register_cb(&cb);
645 } else {
646 char *n = create_full_name(name);
647 lt_symbol_alias(dump_file, ident, n,
648 vpi_get(vpiSize, item)-1, 0);
649 free(n);
652 break;
654 case vpiRealVar:
656 if (skip)
657 break;
659 name = vpi_get_str(vpiName, item);
660 { char*tmp = create_full_name(name);
661 ident = strdup_sh(&name_heap, tmp);
662 free(tmp);
664 info = malloc(sizeof(*info));
666 info->time.type = vpiSimTime;
667 info->item = item;
668 info->sym = lt_symbol_add(dump_file, ident, 0 /* array rows */, vpi_get(vpiSize, item)-1, 0, LT_SYM_F_DOUBLE);
669 info->scheduled = 0;
671 cb.time = &info->time;
672 cb.user_data = (char*)info;
673 cb.value = NULL;
674 cb.obj = item;
675 cb.reason = cbValueChange;
676 cb.cb_rtn = variable_cb_1;
678 info->next = vcd_list;
679 vcd_list = info;
681 info->cb = vpi_register_cb(&cb);
683 break;
685 case vpiModule: type = "module"; if(0){
686 case vpiNamedBegin: type = "begin"; }if(0){
687 case vpiTask: type = "task"; }if(0){
688 case vpiFunction: type = "function"; }if(0){
689 case vpiNamedFork: type = "fork"; }
691 if (depth > 0) {
692 int nskip;
693 vpiHandle argv;
695 const char* fullname =
696 vpi_get_str(vpiFullName, item);
698 #if 0
699 vpi_mcd_printf(1,
700 "LXT info:"
701 " scanning scope %s, %u levels\n",
702 fullname, depth);
703 #endif
704 nskip = 0 != vcd_names_search(&lxt_tab, fullname);
706 if (!nskip)
707 vcd_names_add(&lxt_tab, fullname);
708 else
709 vpi_mcd_printf(1,
710 "LXT warning:"
711 " ignoring signals"
712 " in previously scanned scope %s\n",
713 fullname);
715 name = vpi_get_str(vpiName, item);
717 push_scope(name); /* keep in type info determination for possible future usage */
719 for (i=0; types[i]>0; i++) {
720 vpiHandle hand;
721 argv = vpi_iterate(types[i], item);
722 while (argv && (hand = vpi_scan(argv))) {
723 scan_item(depth-1, hand, nskip);
727 pop_scope();
729 break;
731 default:
732 vpi_mcd_printf(1,
733 "LXT Error: $lxtdumpvars: Unsupported parameter "
734 "type (%d)\n", vpi_get(vpiType, item));
739 static int draw_scope(vpiHandle item)
741 int depth;
742 const char *name;
743 char *type;
745 vpiHandle scope = vpi_handle(vpiScope, item);
746 if (!scope)
747 return 0;
749 depth = 1 + draw_scope(scope);
750 name = vpi_get_str(vpiName, scope);
752 switch (vpi_get(vpiType, item)) {
753 case vpiNamedBegin: type = "begin"; break;
754 case vpiTask: type = "task"; break;
755 case vpiFunction: type = "function"; break;
756 case vpiNamedFork: type = "fork"; break;
757 default: type = "module"; break;
760 push_scope(name); /* keep in type info determination for possible future usage */
762 return depth;
765 static PLI_INT32 sys_dumpvars_calltf(PLI_BYTE8*name)
767 unsigned depth;
768 s_vpi_value value;
769 vpiHandle item = 0;
770 vpiHandle sys = vpi_handle(vpiSysTfCall, 0);
771 vpiHandle argv;
773 if (dump_file == 0) {
774 open_dumpfile("dump.lxt");
775 if (dump_file == 0)
776 return 0;
779 if (install_dumpvars_callback()) {
780 return 0;
783 argv = vpi_iterate(vpiArgument, sys);
785 depth = 0;
786 if (argv && (item = vpi_scan(argv)))
787 switch (vpi_get(vpiType, item)) {
788 case vpiConstant:
789 case vpiNet:
790 case vpiIntegerVar:
791 case vpiReg:
792 case vpiMemoryWord:
793 value.format = vpiIntVal;
794 vpi_get_value(item, &value);
795 depth = value.value.integer;
796 break;
799 if (!depth)
800 depth = 10000;
802 if (!argv) {
803 // $dumpvars;
804 // search for the toplevel module
805 vpiHandle parent = vpi_handle(vpiScope, sys);
806 while (parent) {
807 item = parent;
808 parent = vpi_handle(vpiScope, item);
811 } else if (!item || !(item = vpi_scan(argv))) {
812 // $dumpvars(level);
813 // $dumpvars();
814 // dump the current scope
815 item = vpi_handle(vpiScope, sys);
816 argv = 0x0;
819 for ( ; item; item = argv ? vpi_scan(argv) : 0x0) {
821 int dep = draw_scope(item);
823 vcd_names_sort(&lxt_tab);
824 scan_item(depth, item, 0);
826 while (dep--) {
827 pop_scope();
831 /* Most effective compression. */
832 if (lxm_optimum_mode == LXM_SPACE)
833 lt_set_no_interlace(dump_file);
835 return 0;
838 void sys_lxt_register()
840 int idx;
841 struct t_vpi_vlog_info vlog_info;
842 s_vpi_systf_data tf_data;
845 /* Scan the extended arguments, looking for lxt optimization
846 flags. */
847 vpi_get_vlog_info(&vlog_info);
849 for (idx = 0 ; idx < vlog_info.argc ; idx += 1) {
850 if (strcmp(vlog_info.argv[idx],"-lxt-space") == 0) {
851 lxm_optimum_mode = LXM_SPACE;
853 } else if (strcmp(vlog_info.argv[idx],"-lxt-speed") == 0) {
854 lxm_optimum_mode = LXM_SPEED;
859 tf_data.type = vpiSysTask;
860 tf_data.tfname = "$dumpall";
861 tf_data.calltf = sys_dumpall_calltf;
862 tf_data.compiletf = 0;
863 tf_data.sizetf = 0;
864 tf_data.user_data = "$dumpall";
865 vpi_register_systf(&tf_data);
867 tf_data.type = vpiSysTask;
868 tf_data.tfname = "$dumpoff";
869 tf_data.calltf = sys_dumpoff_calltf;
870 tf_data.compiletf = 0;
871 tf_data.sizetf = 0;
872 tf_data.user_data = "$dumpoff";
873 vpi_register_systf(&tf_data);
875 tf_data.type = vpiSysTask;
876 tf_data.tfname = "$dumpon";
877 tf_data.calltf = sys_dumpon_calltf;
878 tf_data.compiletf = 0;
879 tf_data.sizetf = 0;
880 tf_data.user_data = "$dumpon";
881 vpi_register_systf(&tf_data);
883 tf_data.type = vpiSysTask;
884 tf_data.tfname = "$dumpfile";
885 tf_data.calltf = sys_dumpfile_calltf;
886 tf_data.compiletf = 0;
887 tf_data.sizetf = 0;
888 tf_data.user_data = "$dumpfile";
889 vpi_register_systf(&tf_data);
891 tf_data.type = vpiSysTask;
892 tf_data.tfname = "$dumpflush";
893 tf_data.calltf = sys_dumpflush_calltf;
894 tf_data.compiletf = 0;
895 tf_data.sizetf = 0;
896 tf_data.user_data = "$dumpflush";
897 vpi_register_systf(&tf_data);
899 tf_data.type = vpiSysTask;
900 tf_data.tfname = "$dumplimit";
901 tf_data.calltf = sys_dumplimit_calltf;
902 tf_data.compiletf = sys_dumplimit_compiletf;
903 tf_data.sizetf = 0;
904 tf_data.user_data = "$dumplimit";
905 vpi_register_systf(&tf_data);
907 tf_data.type = vpiSysTask;
908 tf_data.tfname = "$dumpvars";
909 tf_data.calltf = sys_dumpvars_calltf;
910 tf_data.compiletf = sys_vcd_dumpvars_compiletf;
911 tf_data.sizetf = 0;
912 tf_data.user_data = "$dumpvars";
913 vpi_register_systf(&tf_data);