Add a mark at simulation end for vcd/lxt/lxt2 files.
[iverilog.git] / vpi / sys_lxt.c
blobad4b38a7e3014a724a5cbafb02a0c2df53cf1bcb
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 VCD 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 struct lxt_scope *t = lxt_scope_head;
113 /* Figure out how long the combined string will be. */
114 while(t) {
115 len+=t->len+1;
116 t=t->next;
119 len += strlen(name) + 1;
121 /* Allocate a string buffer. */
122 n = n2 = malloc(len);
124 t = lxt_scope_head;
125 while(t) {
126 strcpy(n2, t->name);
127 n2 += t->len;
128 *n2 = '.';
129 n2++;
130 t=t->next;
133 strcpy(n2, name);
134 n2 += strlen(n2);
135 assert( (n2 - n + 1) == len );
137 return n;
141 static struct lt_trace *dump_file = 0;
143 struct vcd_info {
144 vpiHandle item;
145 vpiHandle cb;
146 struct t_vpi_time time;
147 struct lt_symbol *sym;
148 struct vcd_info *next;
149 struct vcd_info *dmp_next;
150 int scheduled;
154 static struct vcd_info*vcd_list = 0;
155 static struct vcd_info*vcd_dmp_list = 0;
156 static PLI_UINT64 vcd_cur_time = 0;
157 static int dump_is_off = 0;
158 static long dump_limit = 0;
159 static int dump_is_full = 0;
160 static int finish_status = 0;
163 static void show_this_item(struct vcd_info*info)
165 s_vpi_value value;
167 if (vpi_get(vpiType,info->item) == vpiRealVar) {
168 value.format = vpiRealVal;
169 vpi_get_value(info->item, &value);
170 lt_emit_value_double(dump_file, info->sym, 0, value.value.real);
172 } else {
173 value.format = vpiBinStrVal;
174 vpi_get_value(info->item, &value);
175 lt_emit_value_bit_string(dump_file, info->sym,
176 0 /* array row */,
177 value.value.str);
182 static void show_this_item_x(struct vcd_info*info)
184 if (vpi_get(vpiType,info->item) == vpiRealVar) {
185 /* Should write a NaN here? */
186 } else {
187 lt_emit_value_bit_string(dump_file, info->sym, 0, "x");
193 * managed qsorted list of scope names for duplicates bsearching
196 struct vcd_names_list_s lxt_tab;
199 static int dumpvars_status = 0; /* 0:fresh 1:cb installed, 2:callback done */
200 static PLI_UINT64 dumpvars_time;
201 inline static int dump_header_pending(void)
203 return dumpvars_status != 2;
207 * This function writes out all the traced variables, whether they
208 * changed or not.
210 static void vcd_checkpoint()
212 struct vcd_info*cur;
214 for (cur = vcd_list ; cur ; cur = cur->next)
215 show_this_item(cur);
218 static void vcd_checkpoint_x()
220 struct vcd_info*cur;
222 for (cur = vcd_list ; cur ; cur = cur->next)
223 show_this_item_x(cur);
226 static PLI_INT32 variable_cb_2(p_cb_data cause)
228 struct vcd_info* info = vcd_dmp_list;
229 PLI_UINT64 now = timerec_to_time64(cause->time);
231 if (now != vcd_cur_time) {
232 lt_set_time64(dump_file, now);
233 vcd_cur_time = now;
236 do {
237 show_this_item(info);
238 info->scheduled = 0;
239 } while ((info = info->dmp_next) != 0);
241 vcd_dmp_list = 0;
243 return 0;
246 static PLI_INT32 variable_cb_1(p_cb_data cause)
248 struct t_cb_data cb;
249 struct vcd_info*info = (struct vcd_info*)cause->user_data;
251 if (dump_is_full) return 0;
252 if (dump_is_off) return 0;
253 if (dump_header_pending()) return 0;
254 if (info->scheduled) return 0;
256 if ((dump_limit > 0) && (ftell(dump_file->handle) > dump_limit)) {
257 dump_is_full = 1;
258 vpi_printf("WARNING: Dump file limit (%ld bytes) "
259 "exceeded.\n", dump_limit);
260 return 0;
263 if (!vcd_dmp_list) {
264 cb = *cause;
265 cb.reason = cbReadOnlySynch;
266 cb.cb_rtn = variable_cb_2;
267 vpi_register_cb(&cb);
270 info->scheduled = 1;
271 info->dmp_next = vcd_dmp_list;
272 vcd_dmp_list = info;
274 return 0;
277 static PLI_INT32 dumpvars_cb(p_cb_data cause)
279 if (dumpvars_status != 1)
280 return 0;
282 dumpvars_status = 2;
284 dumpvars_time = timerec_to_time64(cause->time);
285 vcd_cur_time = dumpvars_time;
287 if (!dump_is_off) {
288 lt_set_time64(dump_file, dumpvars_time);
289 vcd_checkpoint();
292 return 0;
295 static PLI_INT32 finish_cb(p_cb_data cause)
297 if (finish_status != 0)
298 return 0;
300 finish_status = 1;
302 dumpvars_time = timerec_to_time64(cause->time);
304 if (!dump_is_off && !dump_is_full && dumpvars_time != vcd_cur_time) {
305 lt_set_time64(dump_file, dumpvars_time);
308 return 0;
311 inline static int install_dumpvars_callback(void)
313 struct t_cb_data cb;
314 static struct t_vpi_time time;
316 if (dumpvars_status == 1)
317 return 0;
319 if (dumpvars_status == 2) {
320 vpi_mcd_printf(1, "VCD Error:"
321 " $dumpvars ignored,"
322 " previously called at simtime %lu\n",
323 dumpvars_time);
324 return 1;
327 time.type = vpiSimTime;
328 cb.time = &time;
329 cb.reason = cbReadOnlySynch;
330 cb.cb_rtn = dumpvars_cb;
331 cb.user_data = 0x0;
332 cb.obj = 0x0;
334 vpi_register_cb(&cb);
336 cb.reason = cbEndOfSimulation;
337 cb.cb_rtn = finish_cb;
339 vpi_register_cb(&cb);
341 dumpvars_status = 1;
342 return 0;
345 static PLI_INT32 sys_dumpoff_calltf(PLI_BYTE8*name)
347 s_vpi_time now;
348 PLI_UINT64 now64;
350 if (dump_is_off)
351 return 0;
353 dump_is_off = 1;
355 if (dump_file == 0)
356 return 0;
358 if (dump_header_pending())
359 return 0;
361 now.type = vpiSimTime;
362 vpi_get_time(0, &now);
363 now64 = timerec_to_time64(&now);
365 if (now64 > vcd_cur_time)
366 lt_set_time64(dump_file, now64);
367 vcd_cur_time = now64;
369 lt_set_dumpoff(dump_file);
370 vcd_checkpoint_x();
372 return 0;
375 static PLI_INT32 sys_dumpon_calltf(PLI_BYTE8*name)
377 s_vpi_time now;
378 PLI_UINT64 now64;
380 if (!dump_is_off)
381 return 0;
383 dump_is_off = 0;
385 if (dump_file == 0)
386 return 0;
388 if (dump_header_pending())
389 return 0;
391 now.type = vpiSimTime;
392 vpi_get_time(0, &now);
393 now64 = timerec_to_time64(&now);
395 if (now64 > vcd_cur_time)
396 lt_set_time64(dump_file, now64);
397 vcd_cur_time = now64;
399 lt_set_dumpon(dump_file);
400 vcd_checkpoint();
402 return 0;
405 static PLI_INT32 sys_dumpall_calltf(PLI_BYTE8*name)
407 s_vpi_time now;
408 PLI_UINT64 now64;
410 if (dump_file == 0)
411 return 0;
413 if (dump_header_pending())
414 return 0;
416 now.type = vpiSimTime;
417 vpi_get_time(0, &now);
418 now64 = timerec_to_time64(&now);
420 if (now64> vcd_cur_time)
421 lt_set_time64(dump_file, now64);
422 vcd_cur_time = now64;
424 vcd_checkpoint();
426 return 0;
429 static void *close_dumpfile(void)
431 lt_close(dump_file);
432 return(dump_file = NULL);
435 static void open_dumpfile(const char*path)
437 dump_file = lt_init(path);
439 if (dump_file == 0) {
440 vpi_mcd_printf(1,
441 "LXT Error: Unable to open %s for output.\n",
442 path);
443 return;
444 } else {
445 int prec = vpi_get(vpiTimePrecision, 0);
447 vpi_mcd_printf(1,
448 "LXT info: dumpfile %s opened for output.\n",
449 path);
451 assert(prec >= -15);
452 lt_set_timescale(dump_file, prec);
454 lt_set_initial_value(dump_file, 'x');
455 lt_set_clock_compress(dump_file);
457 atexit((void(*)(void))close_dumpfile);
461 static PLI_INT32 sys_dumpfile_calltf(PLI_BYTE8*name)
463 char*path;
465 vpiHandle sys = vpi_handle(vpiSysTfCall, 0);
466 vpiHandle argv = vpi_iterate(vpiArgument, sys);
467 vpiHandle item;
469 if (argv && (item = vpi_scan(argv))) {
470 s_vpi_value value;
472 if (vpi_get(vpiType, item) != vpiConstant
473 || vpi_get(vpiConstType, item) != vpiStringConst) {
474 vpi_mcd_printf(1,
475 "LXT Error:"
476 " %s parameter must be a string constant\n",
477 name);
478 return 0;
481 value.format = vpiStringVal;
482 vpi_get_value(item, &value);
483 path = strdup(value.value.str);
485 vpi_free_object(argv);
487 } else {
488 path = strdup("dump.lxt");
491 if (dump_file)
492 close_dumpfile();
494 assert(dump_file == 0);
495 open_dumpfile(path);
497 free(path);
499 return 0;
503 * The LXT1 format has no concept of file flushing.
505 static PLI_INT32 sys_dumpflush_calltf(PLI_BYTE8*name)
507 return 0;
510 static PLI_INT32 sys_dumplimit_compiletf(PLI_BYTE8 *name)
512 vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
513 vpiHandle argv = vpi_iterate(vpiArgument, callh);
514 vpiHandle limit;
516 /* Check that there is a argument and get it. */
517 if (argv == 0) {
518 vpi_printf("ERROR: %s requires an argument.\n", name);
519 vpi_control(vpiFinish, 1);
520 return 0;
522 limit = vpi_scan(argv);
524 /* Check that we are not given a string. */
525 switch (vpi_get(vpiType, limit)) {
526 case vpiConstant:
527 case vpiParameter:
528 if (vpi_get(vpiConstType, limit) == vpiStringConst) {
529 vpi_printf("ERROR: %s's argument must be a number.\n", name);
533 /* Check that there is only a single argument. */
534 limit = vpi_scan(argv);
535 if (limit != 0) {
536 vpi_printf("ERROR: %s takes a single argument.\n", name);
537 vpi_control(vpiFinish, 1);
538 return 0;
541 return 0;
544 static PLI_INT32 sys_dumplimit_calltf(PLI_BYTE8 *name)
546 vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
547 vpiHandle argv = vpi_iterate(vpiArgument, callh);
548 vpiHandle limit = vpi_scan(argv);
549 s_vpi_value val;
551 /* Get the value and set the dump limit. */
552 val.format = vpiIntVal;
553 vpi_get_value(limit, &val);
554 dump_limit = val.value.integer;
556 vpi_free_object(argv);
557 return 0;
560 static void scan_item(unsigned depth, vpiHandle item, int skip)
562 struct t_cb_data cb;
563 struct vcd_info* info;
565 const char* type;
566 const char* name;
567 const char* ident;
568 int nexus_id;
570 /* list of types to iterate upon */
571 int i;
572 static int types[] = {
573 /* Value */
574 vpiNet,
575 vpiReg,
576 vpiVariables,
577 /* Scope */
578 vpiFunction,
579 vpiModule,
580 vpiNamedBegin,
581 vpiNamedFork,
582 vpiTask,
586 switch (vpi_get(vpiType, item)) {
588 case vpiMemory:
589 /* don't know how to watch memories. */
590 break;
592 case vpiNamedEvent:
593 /* There is nothing in named events to dump. */
594 break;
596 case vpiNet: type = "wire"; if(0){
597 case vpiIntegerVar:
598 case vpiTimeVar:
599 case vpiReg: type = "reg"; }
601 if (skip)
602 break;
604 name = vpi_get_str(vpiName, item);
605 nexus_id = vpi_get(_vpiNexusId, item);
606 if (nexus_id) {
607 ident = find_nexus_ident(nexus_id);
608 } else {
609 ident = 0;
612 if (!ident) {
613 char*tmp = create_full_name(name);
614 ident = strdup_sh(&name_heap, tmp);
615 free(tmp);
617 if (nexus_id)
618 set_nexus_ident(nexus_id, ident);
620 info = malloc(sizeof(*info));
622 info->time.type = vpiSimTime;
623 info->item = item;
624 info->sym = lt_symbol_add(dump_file, ident, 0 /* array rows */, vpi_get(vpiLeftRange, item), vpi_get(vpiRightRange, item), LT_SYM_F_BITS);
625 info->scheduled = 0;
627 cb.time = &info->time;
628 cb.user_data = (char*)info;
629 cb.value = NULL;
630 cb.obj = item;
631 cb.reason = cbValueChange;
632 cb.cb_rtn = variable_cb_1;
634 info->next = vcd_list;
635 vcd_list = info;
637 info->cb = vpi_register_cb(&cb);
639 } else {
640 char *n = create_full_name(name);
641 lt_symbol_alias(dump_file, ident, n,
642 vpi_get(vpiSize, item)-1, 0);
643 free(n);
646 break;
648 case vpiRealVar:
650 if (skip)
651 break;
653 name = vpi_get_str(vpiName, item);
654 { char*tmp = create_full_name(name);
655 ident = strdup_sh(&name_heap, tmp);
656 free(tmp);
658 info = malloc(sizeof(*info));
660 info->time.type = vpiSimTime;
661 info->item = item;
662 info->sym = lt_symbol_add(dump_file, ident, 0 /* array rows */, vpi_get(vpiSize, item)-1, 0, LT_SYM_F_DOUBLE);
663 info->scheduled = 0;
665 cb.time = &info->time;
666 cb.user_data = (char*)info;
667 cb.value = NULL;
668 cb.obj = item;
669 cb.reason = cbValueChange;
670 cb.cb_rtn = variable_cb_1;
672 info->next = vcd_list;
673 vcd_list = info;
675 info->cb = vpi_register_cb(&cb);
677 break;
679 case vpiModule: type = "module"; if(0){
680 case vpiNamedBegin: type = "begin"; }if(0){
681 case vpiTask: type = "task"; }if(0){
682 case vpiFunction: type = "function"; }if(0){
683 case vpiNamedFork: type = "fork"; }
685 if (depth > 0) {
686 int nskip;
687 vpiHandle argv;
689 const char* fullname =
690 vpi_get_str(vpiFullName, item);
692 #if 0
693 vpi_mcd_printf(1,
694 "LXT info:"
695 " scanning scope %s, %u levels\n",
696 fullname, depth);
697 #endif
698 nskip = 0 != vcd_names_search(&lxt_tab, fullname);
700 if (!nskip)
701 vcd_names_add(&lxt_tab, fullname);
702 else
703 vpi_mcd_printf(1,
704 "LXT warning:"
705 " ignoring signals"
706 " in previously scanned scope %s\n",
707 fullname);
709 name = vpi_get_str(vpiName, item);
711 push_scope(name); /* keep in type info determination for possible future usage */
713 for (i=0; types[i]>0; i++) {
714 vpiHandle hand;
715 argv = vpi_iterate(types[i], item);
716 while (argv && (hand = vpi_scan(argv))) {
717 scan_item(depth-1, hand, nskip);
721 pop_scope();
723 break;
725 default:
726 vpi_mcd_printf(1,
727 "LXT Error: $lxtdumpvars: Unsupported parameter "
728 "type (%d)\n", vpi_get(vpiType, item));
733 static int draw_scope(vpiHandle item)
735 int depth;
736 const char *name;
737 char *type;
739 vpiHandle scope = vpi_handle(vpiScope, item);
740 if (!scope)
741 return 0;
743 depth = 1 + draw_scope(scope);
744 name = vpi_get_str(vpiName, scope);
746 switch (vpi_get(vpiType, item)) {
747 case vpiNamedBegin: type = "begin"; break;
748 case vpiTask: type = "task"; break;
749 case vpiFunction: type = "function"; break;
750 case vpiNamedFork: type = "fork"; break;
751 default: type = "module"; break;
754 push_scope(name); /* keep in type info determination for possible future usage */
756 return depth;
759 static PLI_INT32 sys_dumpvars_calltf(PLI_BYTE8*name)
761 unsigned depth;
762 s_vpi_value value;
763 vpiHandle item = 0;
764 vpiHandle sys = vpi_handle(vpiSysTfCall, 0);
765 vpiHandle argv;
767 if (dump_file == 0) {
768 open_dumpfile("dump.lxt");
769 if (dump_file == 0)
770 return 0;
773 if (install_dumpvars_callback()) {
774 return 0;
777 argv = vpi_iterate(vpiArgument, sys);
779 depth = 0;
780 if (argv && (item = vpi_scan(argv)))
781 switch (vpi_get(vpiType, item)) {
782 case vpiConstant:
783 case vpiNet:
784 case vpiIntegerVar:
785 case vpiReg:
786 case vpiMemoryWord:
787 value.format = vpiIntVal;
788 vpi_get_value(item, &value);
789 depth = value.value.integer;
790 break;
793 if (!depth)
794 depth = 10000;
796 if (!argv) {
797 // $dumpvars;
798 // search for the toplevel module
799 vpiHandle parent = vpi_handle(vpiScope, sys);
800 while (parent) {
801 item = parent;
802 parent = vpi_handle(vpiScope, item);
805 } else if (!item || !(item = vpi_scan(argv))) {
806 // $dumpvars(level);
807 // $dumpvars();
808 // dump the current scope
809 item = vpi_handle(vpiScope, sys);
810 argv = 0x0;
813 for ( ; item; item = argv ? vpi_scan(argv) : 0x0) {
815 int dep = draw_scope(item);
817 vcd_names_sort(&lxt_tab);
818 scan_item(depth, item, 0);
820 while (dep--) {
821 pop_scope();
825 /* Most effective compression. */
826 if (lxm_optimum_mode == LXM_SPACE)
827 lt_set_no_interlace(dump_file);
829 return 0;
832 void sys_lxt_register()
834 int idx;
835 struct t_vpi_vlog_info vlog_info;
836 s_vpi_systf_data tf_data;
839 /* Scan the extended arguments, looking for lxt optimization
840 flags. */
841 vpi_get_vlog_info(&vlog_info);
843 for (idx = 0 ; idx < vlog_info.argc ; idx += 1) {
844 if (strcmp(vlog_info.argv[idx],"-lxt-space") == 0) {
845 lxm_optimum_mode = LXM_SPACE;
847 } else if (strcmp(vlog_info.argv[idx],"-lxt-speed") == 0) {
848 lxm_optimum_mode = LXM_SPEED;
853 tf_data.type = vpiSysTask;
854 tf_data.tfname = "$dumpall";
855 tf_data.calltf = sys_dumpall_calltf;
856 tf_data.compiletf = 0;
857 tf_data.sizetf = 0;
858 tf_data.user_data = "$dumpall";
859 vpi_register_systf(&tf_data);
861 tf_data.type = vpiSysTask;
862 tf_data.tfname = "$dumpoff";
863 tf_data.calltf = sys_dumpoff_calltf;
864 tf_data.compiletf = 0;
865 tf_data.sizetf = 0;
866 tf_data.user_data = "$dumpoff";
867 vpi_register_systf(&tf_data);
869 tf_data.type = vpiSysTask;
870 tf_data.tfname = "$dumpon";
871 tf_data.calltf = sys_dumpon_calltf;
872 tf_data.compiletf = 0;
873 tf_data.sizetf = 0;
874 tf_data.user_data = "$dumpon";
875 vpi_register_systf(&tf_data);
877 tf_data.type = vpiSysTask;
878 tf_data.tfname = "$dumpfile";
879 tf_data.calltf = sys_dumpfile_calltf;
880 tf_data.compiletf = 0;
881 tf_data.sizetf = 0;
882 tf_data.user_data = "$dumpfile";
883 vpi_register_systf(&tf_data);
885 tf_data.type = vpiSysTask;
886 tf_data.tfname = "$dumpflush";
887 tf_data.calltf = sys_dumpflush_calltf;
888 tf_data.compiletf = 0;
889 tf_data.sizetf = 0;
890 tf_data.user_data = "$dumpflush";
891 vpi_register_systf(&tf_data);
893 tf_data.type = vpiSysTask;
894 tf_data.tfname = "$dumplimit";
895 tf_data.calltf = sys_dumplimit_calltf;
896 tf_data.compiletf = sys_dumplimit_compiletf;
897 tf_data.sizetf = 0;
898 tf_data.user_data = "$dumplimit";
899 vpi_register_systf(&tf_data);
901 tf_data.type = vpiSysTask;
902 tf_data.tfname = "$dumpvars";
903 tf_data.calltf = sys_dumpvars_calltf;
904 tf_data.compiletf = sys_vcd_dumpvars_compiletf;
905 tf_data.sizetf = 0;
906 tf_data.user_data = "$dumpvars";
907 vpi_register_systf(&tf_data);